diff --git a/src/comp/driver/rustc.rs b/src/comp/driver/rustc.rs index 9f8249215e89..3a6f73862920 100644 --- a/src/comp/driver/rustc.rs +++ b/src/comp/driver/rustc.rs @@ -158,14 +158,14 @@ fn compile_input(sess: session::session, cfg: ast::crate_cfg, input: &str, let mut_map = time(time_passes, "mutability checking", bind middle::mut::check_crate(ty_cx, crate)); - time(time_passes, "alias checking", - bind middle::alias::check_crate(ty_cx, crate)); + let copy_map = time(time_passes, "alias checking", + bind middle::alias::check_crate(ty_cx, crate)); time(time_passes, "kind checking", bind kind::check_crate(ty_cx, crate)); if sess.get_opts().no_trans { ret; } let llmod = time(time_passes, "translation", bind trans::trans_crate(sess, crate, ty_cx, output, ast_map, - mut_map)); + mut_map, copy_map)); time(time_passes, "LLVM passes", bind link::write::run_passes(sess, llmod, output)); } diff --git a/src/comp/middle/alias.rs b/src/comp/middle/alias.rs index a808528031d3..e1d96cf9a059 100644 --- a/src/comp/middle/alias.rs +++ b/src/comp/middle/alias.rs @@ -24,28 +24,35 @@ tag valid { valid; overwritten(span, ast::path); val_taken(span, ast::path); } type restrict = @{root_var: option::t, + node_id: node_id, + ty: ty::t, local_id: uint, bindings: [node_id], unsafe_ty: option::t, depends_on: [uint], - mutable ok: valid}; + mutable ok: valid, + mutable given_up: bool}; type scope = @[restrict]; tag local_info { local(uint); } +type copy_map = std::map::hashmap; + type ctx = {tcx: ty::ctxt, local_map: std::map::hashmap, - mutable next_local: uint}; + mutable next_local: uint, + copy_map: copy_map}; -fn check_crate(tcx: ty::ctxt, crate: &@ast::crate) { +fn check_crate(tcx: ty::ctxt, crate: &@ast::crate) -> copy_map { // Stores information about object fields and function // arguments that's otherwise not easily available. let cx = @{tcx: tcx, local_map: std::map::new_int_hash(), - mutable next_local: 0u}; + mutable next_local: 0u, + copy_map: std::map::new_int_hash()}; let v = @{visit_fn: visit_fn, visit_expr: bind visit_expr(cx, _, _, _), @@ -53,6 +60,7 @@ fn check_crate(tcx: ty::ctxt, crate: &@ast::crate) { with *visit::default_visitor::()}; visit::visit_crate(*crate, @[], visit::mk_vt(v)); tcx.sess.abort_if_errors(); + ret cx.copy_map; } fn visit_fn(f: &ast::_fn, _tp: &[ast::ty_param], _sp: &span, _name: &fn_ident, @@ -148,37 +156,53 @@ fn visit_decl(cx: &@ctx, d: &@ast::decl, sc: &scope, v: &vt) { } } -fn check_call(cx: &ctx, f: &@ast::expr, args: &[@ast::expr], sc: &scope) -> - [restrict] { +fn cant_copy(cx: &ctx, r: &restrict) -> bool { + if r.given_up { ret false; } + // FIXME alt contexts copying not supported yet + if r.node_id == 0 { ret true; } + // FIXME warn when copy is expensive + if ty::type_allows_implicit_copy(cx.tcx, r.ty) { + r.given_up = true; + cx.copy_map.insert(r.node_id, ()); + ret false; + } else { + ret true; + } +} + +fn check_call(cx: &ctx, f: &@ast::expr, args: &[@ast::expr], sc: &scope) + -> [restrict] { let fty = ty::type_autoderef(cx.tcx, ty::expr_ty(cx.tcx, f)); let arg_ts = ty::ty_fn_args(cx.tcx, fty); let mut_roots: [{arg: uint, node: node_id}] = []; let restricts = []; let i = 0u; for arg_t: ty::arg in arg_ts { - if arg_t.mode != ty::mo_val { - let arg = args[i]; - let root = expr_root(cx.tcx, arg, false); - if arg_t.mode == ty::mo_alias(true) { - alt path_def(cx, arg) { - some(def) { - let dnum = ast_util::def_id_of_def(def).node; - mut_roots += [{arg: i, node: dnum}]; - } - _ { } - } + let arg = args[i]; + let root = expr_root(cx.tcx, arg, false); + if arg_t.mode == ty::mo_alias(true) { + alt path_def(cx, arg) { + some(def) { + let dnum = ast_util::def_id_of_def(def).node; + mut_roots += [{arg: i, node: dnum}]; + } + _ { } } - let root_var = path_def_id(cx, root.ex); - let unsafe_t = - alt inner_mut(root.ds) { some(t) { some(t) } _ { none } }; - restricts += - [@{root_var: root_var, - local_id: cx.next_local, - bindings: [arg.id], - unsafe_ty: unsafe_t, - depends_on: deps(sc, root_var), - mutable ok: valid}]; } + let root_var = path_def_id(cx, root.ex); + let unsafe_t = + alt inner_mut(root.ds) { some(t) { some(t) } _ { none } }; + restricts += + [@{root_var: root_var, + node_id: arg_t.mode == ast::by_mut_ref ? 0 : arg.id, + ty: arg_t.ty, + local_id: cx.next_local, + bindings: [arg.id], + unsafe_ty: unsafe_t, + depends_on: deps(sc, root_var), + mutable ok: valid, + // FIXME kludge + mutable given_up: arg_t.mode == ty::mo_move}]; i += 1u; } let f_may_close = @@ -189,7 +213,7 @@ fn check_call(cx: &ctx, f: &@ast::expr, args: &[@ast::expr], sc: &scope) -> if f_may_close { let i = 0u; for r in restricts { - if !option::is_none(r.unsafe_ty) { + if !option::is_none(r.unsafe_ty) && cant_copy(cx, r) { cx.tcx.sess.span_err(f.span, #fmt["function may alias with argument \ %u, which is not immutably rooted", @@ -199,19 +223,19 @@ fn check_call(cx: &ctx, f: &@ast::expr, args: &[@ast::expr], sc: &scope) -> } } let j = 0u; - for @{unsafe_ty: unsafe_ty, _} in restricts { - alt unsafe_ty { + for r in restricts { + alt r.unsafe_ty { some(ty) { let i = 0u; for arg_t: ty::arg in arg_ts { let mut_alias = arg_t.mode == ty::mo_alias(true); if i != j && - ty_can_unsafely_include(cx, ty, arg_t.ty, mut_alias) { + ty_can_unsafely_include(cx, ty, arg_t.ty, mut_alias) && + cant_copy(cx, r) { cx.tcx.sess.span_err( args[i].span, #fmt["argument %u may alias with argument %u, \ - which is not immutably rooted", - i, j]); + which is not immutably rooted", i, j]); } i += 1u; } @@ -223,27 +247,22 @@ fn check_call(cx: &ctx, f: &@ast::expr, args: &[@ast::expr], sc: &scope) -> // Ensure we're not passing a root by mutable alias. for {node: node, arg: arg} in mut_roots { - let mut_alias_to_root = false; - let mut_alias_to_root_count = 0u; - for @{root_var: root_var, _} in restricts { - alt root_var { - some(root) { - if node == root { - mut_alias_to_root_count += 1u; - if mut_alias_to_root_count > 1u { - mut_alias_to_root = true; + let i = 0u; + for r in restricts { + if i != arg { + alt r.root_var { + some(root) { + if node == root && cant_copy(cx, r) { + cx.tcx.sess.span_err( + args[arg].span, "passing a mutable alias to a \ + variable that roots another alias"); break; } + } + none. { } } - } - none. { } } - } - - if mut_alias_to_root { - cx.tcx.sess.span_err(args[arg].span, - "passing a mutable alias to a variable \ - that roots another alias"); + i += 1u; } } ret restricts; @@ -261,11 +280,15 @@ fn check_alt(cx: &ctx, input: &@ast::expr, arms: &[ast::arm], sc: &scope, new_sc = @(*sc + [@{root_var: root_var, + // FIXME need to use separate restrict for each binding + node_id: 0, + ty: ty::mk_int(cx.tcx), local_id: cx.next_local, bindings: dnums, unsafe_ty: inner_mut(root.ds), depends_on: deps(sc, root_var), - mutable ok: valid}]); + mutable ok: valid, + mutable given_up: false}]); } register_locals(cx, a.pats[0]); visit::visit_arm(a, new_sc, v); @@ -292,23 +315,28 @@ fn check_for(cx: &ctx, local: &@ast::local, seq: &@ast::expr, blk: &ast::blk, // If this is a mutable vector, don't allow it to be touched. let seq_t = ty::expr_ty(cx.tcx, seq); + let elt_t; alt ty::struct(cx.tcx, seq_t) { - ty::ty_vec(mt) { if mt.mut != ast::imm { unsafe = some(seq_t); } } - ty::ty_str. {/* no-op */ } - _ { - cx.tcx.sess.span_unimpl(seq.span, - "unknown seq type " + - util::ppaux::ty_to_str(cx.tcx, seq_t)); + ty::ty_vec(mt) { + if mt.mut != ast::imm { unsafe = some(seq_t); } + elt_t = mt.ty; + } + ty::ty_str. { + elt_t = ty::mk_mach(cx.tcx, ast::ty_u8); } } let root_var = path_def_id(cx, root.ex); let new_sc = @{root_var: root_var, + // FIXME reenable when trans knows how to copy for vars + node_id: 0, // blk.node.id, + ty: elt_t, local_id: cx.next_local, bindings: ast_util::pat_binding_ids(local.node.pat), unsafe_ty: unsafe, depends_on: deps(sc, root_var), - mutable ok: valid}; + mutable ok: valid, + mutable given_up: false}; register_locals(cx, local.node.pat); visit::visit_block(blk, @(*sc + [new_sc]), v); } @@ -322,7 +350,6 @@ fn check_var(cx: &ctx, ex: &@ast::expr, p: &ast::path, id: ast::node_id, alt cx.local_map.find(my_defnum) { some(local(id)) { id } _ { 0u } }; let var_t = ty::expr_ty(cx.tcx, ex); for r: restrict in *sc { - // excludes variables introduced since the alias was made if my_local_id < r.local_id { alt r.unsafe_ty { @@ -364,17 +391,15 @@ fn test_scope(cx: &ctx, sc: &scope, r: &restrict, p: &ast::path) { if prob != valid { break; } prob = sc[dep].ok; } - if prob != valid { - let msg = - alt prob { - overwritten(sp, wpt) { - {span: sp, msg: "overwriting " + ast_util::path_name(wpt)} - } - val_taken(sp, vpt) { - {span: sp, - msg: "taking the value of " + ast_util::path_name(vpt)} - } - }; + if prob != valid && cant_copy(cx, r) { + let msg = alt prob { + overwritten(sp, wpt) { + {span: sp, msg: "overwriting " + ast_util::path_name(wpt)} + } + val_taken(sp, vpt) { + {span: sp, msg: "taking the value of " + ast_util::path_name(vpt)} + } + }; cx.tcx.sess.span_err(msg.span, msg.msg + " will invalidate alias " + ast_util::path_name(p) + diff --git a/src/comp/middle/mut.rs b/src/comp/middle/mut.rs index 1721a6e130fa..ae1379970541 100644 --- a/src/comp/middle/mut.rs +++ b/src/comp/middle/mut.rs @@ -68,10 +68,14 @@ fn expr_root(tcx: &ty::ctxt, ex: @expr, autoderef: bool) -> let auto_unbox = maybe_auto_unbox(tcx, ty::expr_ty(tcx, base)); alt ty::struct(tcx, auto_unbox.t) { ty::ty_vec(mt) { - ds += - [@{mut: mt.mut != imm, - kind: index, - outer_t: auto_unbox.t}]; + ds += [@{mut: mt.mut != imm, + kind: index, + outer_t: auto_unbox.t}]; + } + ty::ty_str. { + ds += [@{mut: false, + kind: index, + outer_t: auto_unbox.t}]; } } ds += auto_unbox.ds; diff --git a/src/comp/middle/trans.rs b/src/comp/middle/trans.rs index 893614cac9a5..4f702448fd20 100644 --- a/src/comp/middle/trans.rs +++ b/src/comp/middle/trans.rs @@ -79,20 +79,9 @@ fn type_of(cx: &@crate_ctxt, sp: &span, t: ty::t) fn type_of_explicit_args(cx: &@crate_ctxt, sp: &span, inputs: &[ty::arg]) -> [TypeRef] { - let atys: [TypeRef] = []; - for arg: ty::arg in inputs { - let t: TypeRef = type_of_inner(cx, sp, arg.ty); - t = - alt arg.mode { - ty::mo_alias(_) { T_ptr(t) } - ty::mo_move. { T_ptr(t) } - _ { - if ty::type_is_structural(cx.tcx, arg.ty) { - T_ptr(t) - } else { t } - } - }; - atys += [t]; + let atys = []; + for arg in inputs { + atys += [T_ptr(type_of_inner(cx, sp, arg.ty))]; } ret atys; } @@ -2038,12 +2027,6 @@ fn copy_val(cx: &@block_ctxt, action: copy_action, dst: ValueRef, fn copy_val_no_check(cx: &@block_ctxt, action: copy_action, dst: ValueRef, src: ValueRef, t: ty::t) -> @block_ctxt { let ccx = bcx_ccx(cx); - // FIXME this is just a clunky stopgap. we should do proper checking in an - // earlier pass. - if !ty::type_is_copyable(ccx.tcx, t) { - ccx.sess.span_fatal(cx.sp, "Copying a non-copyable type."); - } - if ty::type_is_scalar(ccx.tcx, t) || ty::type_is_native(ccx.tcx, t) { Store(cx, src, dst); ret cx; @@ -3387,14 +3370,12 @@ fn trans_bind_thunk(cx: &@local_ctxt, sp: &span, incoming_fty: ty::t, for arg: option::t<@ast::expr> in args { let out_arg = outgoing_args[outgoing_arg_index]; let llout_arg_ty = llout_arg_tys[outgoing_arg_index]; - let is_val = out_arg.mode == ty::mo_val; alt arg { // Arg provided at binding time; thunk copies it from // closure. some(e) { - let e_ty = ty::expr_ty(cx.ccx.tcx, e); let bound_arg = GEP_tup_like(bcx, closure_ty, llclosure, [0, abi::box_rc_field_body, @@ -3404,20 +3385,12 @@ fn trans_bind_thunk(cx: &@local_ctxt, sp: &span, incoming_fty: ty::t, // If the type is parameterized, then we need to cast the // type we actually have to the parameterized out type. if ty::type_contains_params(cx.ccx.tcx, out_arg.ty) { - let ty = - if is_val { T_ptr(llout_arg_ty) } else { llout_arg_ty }; - val = PointerCast(bcx, val, ty); - } - if is_val && (type_is_immediate(cx.ccx, e_ty) || - ty::type_is_unique(cx.ccx.tcx, e_ty)) { - val = Load(bcx, val); + val = PointerCast(bcx, val, llout_arg_ty); } llargs += [val]; b += 1; } - - // Arg will be provided when the thunk is invoked. none. { let arg: ValueRef = llvm::LLVMGetParam(llthunk, a); @@ -3543,27 +3516,22 @@ fn trans_arg_expr(cx: &@block_ctxt, arg: &ty::arg, lldestty0: TypeRef, // be inspected. It's important for the value // to have type lldestty0 (the callee's expected type). val = llvm::LLVMGetUndef(lldestty0); - } else if arg.mode == ty::mo_val { - if ty::type_is_vec(ccx.tcx, e_ty) { - let r = do_spill(bcx, Load(bcx, val), e_ty); - bcx = r.bcx; - let arg_copy = r.val; - - bcx = take_ty(bcx, arg_copy, e_ty); - val = Load(bcx, arg_copy); - add_clean_temp(bcx, arg_copy, e_ty); - } else if !lv.is_mem { - // Do nothing for non-vector temporaries; just give them to the - // callee. - } else if type_is_structural_or_param(ccx.tcx, e_ty) { - let dst = alloc_ty(bcx, e_ty); - bcx = copy_val(dst.bcx, INIT, dst.val, val, e_ty); - val = dst.val; - add_clean_temp(bcx, val, e_ty); - } else { - bcx = take_ty(bcx, val, e_ty); - val = load_if_immediate(bcx, val, e_ty); - add_clean_temp(bcx, val, e_ty); + } else if arg.mode == ty::mo_val || arg.mode == ty::mo_alias(false) { + let copied = false; + if !lv.is_mem && type_is_immediate(ccx, e_ty) { + val = do_spill_noroot(bcx, val); + copied = true; + } + if ccx.copy_map.contains_key(e.id) && lv.is_mem { + if !copied { + let alloc = alloc_ty(bcx, e_ty); + bcx = copy_val(alloc.bcx, INIT, alloc.val, + load_if_immediate(alloc.bcx, val, e_ty), e_ty); + val = alloc.val; + } else { + bcx = take_ty(bcx, val, e_ty); + } + add_clean(bcx, val, e_ty); } } else if type_is_immediate(ccx, e_ty) && !lv.is_mem { let r = do_spill(bcx, val, e_ty); @@ -4768,7 +4736,7 @@ fn mk_standard_basic_blocks(llfn: ValueRef) -> da: BasicBlockRef, rt: BasicBlockRef} { ret {sa: - str::as_buf("statuc_allocas", + str::as_buf("static_allocas", {|buf| llvm::LLVMAppendBasicBlock(llfn, buf) }), ca: str::as_buf("copy_args", @@ -4892,33 +4860,25 @@ fn create_llargs_for_fn_args(cx: &@fn_ctxt, proto: ast::proto, } fn copy_args_to_allocas(fcx: @fn_ctxt, scope: @block_ctxt, args: &[ast::arg], - arg_tys: &[ty::arg]) { + arg_tys: &[ty::arg], ignore_mut: bool) { let llcopyargs = new_raw_block_ctxt(fcx, fcx.llcopyargs); let bcx = llcopyargs; let arg_n: uint = 0u; for aarg: ast::arg in args { let arg_ty = arg_tys[arg_n].ty; alt aarg.mode { - ast::val. { - - // Structural types are passed by pointer, and we use the - // pointed-to memory for the local. - if !type_is_structural_or_param(fcx_tcx(fcx), arg_ty) { - // Overwrite the llargs entry for this arg with its alloca. - let aval = bcx.fcx.llargs.get(aarg.id); - - let r = do_spill(bcx, aval, arg_ty); - bcx = r.bcx; - let addr = r.val; - - bcx.fcx.llargs.insert(aarg.id, addr); - - // Args that are locally assigned to need to do a local - // take/drop - if fcx.lcx.ccx.mut_map.contains_key(aarg.id) { - bcx = take_ty(bcx, addr, arg_ty); - add_clean(scope, addr, arg_ty); - } + ast::val. | ast::alias(false) { + let mutated = !ignore_mut && + fcx.lcx.ccx.mut_map.contains_key(aarg.id); + // Overwrite the llargs entry for locally mutated params + // with a local alloca. + if mutated { + let aptr = bcx.fcx.llargs.get(aarg.id); + let {bcx, val: alloc} = alloc_ty(bcx, arg_ty); + bcx = copy_val(bcx, INIT, alloc, + load_if_immediate(bcx, aptr, arg_ty), arg_ty); + bcx.fcx.llargs.insert(aarg.id, alloc); + add_clean(scope, alloc, arg_ty); } } ast::move. { @@ -5027,7 +4987,7 @@ fn trans_closure(bcx_maybe: &option::t<@block_ctxt>, let block_ty = node_id_type(cx.ccx, f.body.node.id); let arg_tys = arg_tys_of_fn(fcx.lcx.ccx, id); - copy_args_to_allocas(fcx, bcx, f.decl.inputs, arg_tys); + copy_args_to_allocas(fcx, bcx, f.decl.inputs, arg_tys, false); // Figure out if we need to build a closure and act accordingly let res = @@ -5175,7 +5135,7 @@ fn trans_tag_variant(cx: @local_ctxt, tag_id: ast::node_id, } let arg_tys = arg_tys_of_fn(cx.ccx, variant.node.id); let bcx = new_top_block_ctxt(fcx); - copy_args_to_allocas(fcx, bcx, fn_args, arg_tys); + copy_args_to_allocas(fcx, bcx, fn_args, arg_tys, true); let lltop = bcx.llbb; // Cast the tag to a type we can GEP into. @@ -5389,9 +5349,15 @@ fn create_main_wrapper(ccx: &@crate_ctxt, sp: &span, main_llfn: ValueRef, let lloutputarg = llvm::LLVMGetParam(llfdecl, 0u); let lltaskarg = llvm::LLVMGetParam(llfdecl, 1u); let llenvarg = llvm::LLVMGetParam(llfdecl, 2u); - let llargvarg = llvm::LLVMGetParam(llfdecl, 3u); let args = [lloutputarg, lltaskarg, llenvarg]; - if takes_argv { args += [llargvarg]; } + if takes_argv { + let llargvarg = llvm::LLVMGetParam(llfdecl, 3u); + // The runtime still passes the arg vector by value, this kludge + // makes sure it becomes a pointer (to a pointer to a vec). + let minus_ptr = llvm::LLVMGetElementType(val_ty(llargvarg)); + llargvarg = PointerCast(bcx, llargvarg, minus_ptr); + args += [do_spill_noroot(bcx, llargvarg)]; + } FastCall(bcx, main_llfn, args); build_return(bcx); @@ -5616,6 +5582,9 @@ fn decl_native_fn_and_pair(ccx: &@crate_ctxt, sp: &span, path: &[str], let i = arg_n; for arg: ty::arg in args { let llarg = llvm::LLVMGetParam(fcx.llfn, i); + if arg.mode == ty::mo_val { + llarg = load_if_immediate(bcx, llarg, arg.ty); + } assert (llarg as int != 0); if cast_to_i32 { let llarg_i32 = convert_arg_to_i32(bcx, llarg, arg.ty, arg.mode); @@ -5981,8 +5950,8 @@ fn write_abi_version(ccx: &@crate_ctxt) { } fn trans_crate(sess: &session::session, crate: &@ast::crate, tcx: &ty::ctxt, - output: &str, amap: &ast_map::map, mut_map: mut::mut_map) -> - ModuleRef { + output: &str, amap: &ast_map::map, mut_map: mut::mut_map, + copy_map: alias::copy_map) -> ModuleRef { let llmod = str::as_buf("rust_out", {|buf| llvm::LLVMModuleCreateWithNameInContext( @@ -6039,6 +6008,7 @@ fn trans_crate(sess: &session::session, crate: &@ast::crate, tcx: &ty::ctxt, type_short_names: short_names, tcx: tcx, mut_map: mut_map, + copy_map: copy_map, stats: {mutable n_static_tydescs: 0u, mutable n_derived_tydescs: 0u, diff --git a/src/comp/middle/trans_common.rs b/src/comp/middle/trans_common.rs index cbb11879fd7a..9247135cdec2 100644 --- a/src/comp/middle/trans_common.rs +++ b/src/comp/middle/trans_common.rs @@ -144,6 +144,7 @@ type crate_ctxt = type_short_names: hashmap, tcx: ty::ctxt, mut_map: mut::mut_map, + copy_map: alias::copy_map, stats: stats, upcalls: @upcall::upcalls, rust_object_type: TypeRef, diff --git a/src/comp/middle/trans_objects.rs b/src/comp/middle/trans_objects.rs index ed2ee1ba74dd..ca1740df0349 100644 --- a/src/comp/middle/trans_objects.rs +++ b/src/comp/middle/trans_objects.rs @@ -63,7 +63,7 @@ fn trans_obj(cx: @local_ctxt, sp: &span, ob: &ast::_obj, ty::ret_ty_of_fn(ccx.tcx, ctor_id), fn_args, ty_params); let arg_tys: [ty::arg] = arg_tys_of_fn(ccx, ctor_id); - copy_args_to_allocas(fcx, bcx, fn_args, arg_tys); + copy_args_to_allocas(fcx, bcx, fn_args, arg_tys, true); // Pick up the type of this object by looking at our own output type, that // is, the output type of the object constructor we're building. diff --git a/src/comp/middle/ty.rs b/src/comp/middle/ty.rs index a6cd2a7ec76b..95d4270831e7 100644 --- a/src/comp/middle/ty.rs +++ b/src/comp/middle/ty.rs @@ -155,6 +155,7 @@ export type_is_box; export type_is_boxed; export type_is_vec; export type_is_fp; +export type_allows_implicit_copy; export type_is_integral; export type_is_native; export type_is_nil; @@ -167,6 +168,7 @@ export type_is_copyable; export type_is_tup_like; export type_is_str; export type_is_unique; +export type_structurally_contains_uniques; export type_autoderef; export type_param; export unify; @@ -1161,6 +1163,34 @@ pure fn type_has_dynamic_size(cx: &ctxt, ty: t) -> bool { } } +// Returns true for types where a copy of a value can be distinguished from +// the value itself. I.e. types with mutable content that's not shared through +// a pointer. +fn type_allows_implicit_copy(cx: &ctxt, ty: t) -> bool { + ret !type_structurally_contains(cx, ty, fn(sty: &sty) -> bool { + ret alt sty { + ty_param(_, _) { true } + ty_vec(mt) { mt.mut != ast::imm } + ty_rec(fields) { + for field in fields { if field.mt.mut != ast::imm { ret true; } } + false + } + _ { false } + }; + }); +} + +fn type_structurally_contains_uniques(cx: &ctxt, ty: t) -> bool { + ret type_structurally_contains(cx, ty, fn(sty: &sty) -> bool { + ret alt sty { + ty_uniq(_) { ret true; } + ty_vec(_) { true } + ty_str. { true } + _ { ret false; } + }; + }); +} + fn type_is_integral(cx: &ctxt, ty: t) -> bool { alt struct(cx, ty) { ty_int. { ret true; }