From adb61e3e992aa6539dd178f8b2c2f55aca942b16 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 14 May 2012 20:32:29 -0700 Subject: [PATCH] get preservation of boxes working, at least in simple cases --- src/rt/boxed_region.cpp | 1 - src/rustc/driver/driver.rs | 10 +- src/rustc/middle/astencode.rs | 1 + src/rustc/middle/borrowck.rs | 89 ++- src/rustc/middle/trans/base.rs | 654 +++++++++++------- src/rustc/middle/trans/build.rs | 2 +- src/rustc/middle/trans/common.rs | 23 +- src/rustc/middle/trans/native.rs | 5 +- src/rustc/middle/trans/reflect.rs | 2 +- .../borrowck-preserve-box-in-field.rs | 22 + .../run-pass/borrowck-preserve-box-in-uniq.rs | 22 + 11 files changed, 524 insertions(+), 307 deletions(-) create mode 100644 src/test/run-pass/borrowck-preserve-box-in-field.rs create mode 100644 src/test/run-pass/borrowck-preserve-box-in-uniq.rs diff --git a/src/rt/boxed_region.cpp b/src/rt/boxed_region.cpp index 247424088d6..20b608a7060 100644 --- a/src/rt/boxed_region.cpp +++ b/src/rt/boxed_region.cpp @@ -54,7 +54,6 @@ void boxed_region::free(rust_opaque_box *box) { if (env->poison_on_free) { memset(box_body(box), 0xab, box->td->size); - return; } box->prev = NULL; diff --git a/src/rustc/driver/driver.rs b/src/rustc/driver/driver.rs index efa32a5884d..cfd314e1d2c 100644 --- a/src/rustc/driver/driver.rs +++ b/src/rustc/driver/driver.rs @@ -190,7 +190,7 @@ fn compile_upto(sess: session, cfg: ast::crate_cfg, bind middle::check_self::check_crate(ty_cx, crate)); time(time_passes, "typestate checking", bind middle::tstate::ck::check_crate(ty_cx, crate)); - let (_root_map, mutbl_map) = time( + let (root_map, mutbl_map) = time( time_passes, "borrow checking", bind middle::borrowck::check_crate(ty_cx, method_map, crate)); time(time_passes, "region checking", @@ -208,10 +208,10 @@ fn compile_upto(sess: session, cfg: ast::crate_cfg, if upto == cu_no_trans { ret {crate: crate, tcx: some(ty_cx)}; } let outputs = option::get(outputs); - let maps = {mutbl_map: mutbl_map, copy_map: copy_map, - last_uses: last_uses, impl_map: impl_map, - method_map: method_map, vtable_map: vtable_map, - spill_map: spill_map}; + let maps = {mutbl_map: mutbl_map, root_map: root_map, + copy_map: copy_map, last_uses: last_uses, + impl_map: impl_map, method_map: method_map, + vtable_map: vtable_map, spill_map: spill_map}; let (llmod, link_meta) = time(time_passes, "translation", diff --git a/src/rustc/middle/astencode.rs b/src/rustc/middle/astencode.rs index e9c24727f20..6f55cf0ca69 100644 --- a/src/rustc/middle/astencode.rs +++ b/src/rustc/middle/astencode.rs @@ -49,6 +49,7 @@ export decode_inlined_item; // Auxiliary maps of things to be encoded type maps = { mutbl_map: middle::borrowck::mutbl_map, + root_map: middle::borrowck::root_map, copy_map: middle::alias::copy_map, last_uses: middle::last_use::last_uses, impl_map: middle::resolve::impl_map, diff --git a/src/rustc/middle/borrowck.rs b/src/rustc/middle/borrowck.rs index ed310495813..b9076cc435c 100644 --- a/src/rustc/middle/borrowck.rs +++ b/src/rustc/middle/borrowck.rs @@ -27,7 +27,7 @@ fn check_crate(tcx: ty::ctxt, let bccx = @{tcx: tcx, method_map: method_map, msg_level: msg_level, - root_map: int_hash(), + root_map: root_map(), mutbl_map: int_hash()}; let req_loan_map = if msg_level > 0u { @@ -53,7 +53,14 @@ type borrowck_ctxt = @{tcx: ty::ctxt, // a map mapping id's of expressions of task-local type (@T, []/@, etc) where // the box needs to be kept live to the id of the scope for which they must // stay live. -type root_map = hashmap; +type root_map = hashmap; + +// the keys to the root map combine the `id` of the expression with +// the number of types that it is autodereferenced. So, for example, +// if you have an expression `x.f` and x has type ~@T, we could add an +// entry {id:x, derefs:0} to refer to `x` itself, `{id:x, derefs:1}` +// to refer to the deref of the unique pointer, and so on. +type root_map_key = {id: ast::node_id, derefs: uint}; // set of ids of local vars / formal arguments that are modified / moved. // this is used in trans for optimization purposes. @@ -71,13 +78,13 @@ type bckerr = {cmt: cmt, code: bckerr_code}; type bckres = result; enum categorization { - cat_rvalue, // result of eval'ing some misc expr - cat_special(special_kind), // - cat_local(ast::node_id), // local variable - cat_arg(ast::node_id), // formal argument - cat_stack_upvar(cmt), // upvar in stack closure - cat_deref(cmt, ptr_kind), // deref of a ptr - cat_comp(cmt, comp_kind), // adjust to locate an internal component + cat_rvalue, // result of eval'ing some misc expr + cat_special(special_kind), // + cat_local(ast::node_id), // local variable + cat_arg(ast::node_id), // formal argument + cat_stack_upvar(cmt), // upvar in stack closure + cat_deref(cmt, uint, ptr_kind), // deref of a ptr + cat_comp(cmt, comp_kind), // adjust to locate an internal component } // different kinds of pointers: @@ -156,6 +163,18 @@ fn save_and_restore(&t: T, f: fn() -> U) -> U { ret u; } +fn root_map() -> root_map { + ret hashmap(root_map_key_hash, root_map_key_eq); + + fn root_map_key_eq(k1: root_map_key, k2: root_map_key) -> bool { + k1.id == k2.id && k1.derefs == k2.derefs + } + + fn root_map_key_hash(k: root_map_key) -> uint { + (k.id << 4) as uint | k.derefs + } +} + // ---------------------------------------------------------------------- // Gathering loans // @@ -374,7 +393,7 @@ impl methods for gather_loan_ctxt { ast::pat_box(subpat) | ast::pat_uniq(subpat) { // @p1, ~p1 - alt self.bccx.cat_deref(pat, cmt, true) { + alt self.bccx.cat_deref(pat, cmt, 0u, true) { some(subcmt) { self.gather_pat(subcmt, subpat, alt_id); } none { tcx.sess.span_bug(pat.span, "Non derefable type"); } } @@ -915,7 +934,7 @@ impl categorize_methods for borrowck_ctxt { ty::ty_uniq(*) | ty::ty_box(*) | ty::ty_rptr(*) { let cmt = self.cat_expr(expr); - self.cat_deref(expr, cmt, true).get() + self.cat_deref(expr, cmt, 0u, true).get() } _ { @@ -943,7 +962,7 @@ impl categorize_methods for borrowck_ctxt { alt expr.node { ast::expr_unary(ast::deref, e_base) { let base_cmt = self.cat_expr(e_base); - alt self.cat_deref(expr, base_cmt, true) { + alt self.cat_deref(expr, base_cmt, 0u, true) { some(cmt) { ret cmt; } none { tcx.sess.span_bug( @@ -955,7 +974,7 @@ impl categorize_methods for borrowck_ctxt { } ast::expr_field(base, f_name, _) { - let base_cmt = self.cat_autoderef(expr, base); + let base_cmt = self.cat_autoderef(base); self.cat_field(expr, base_cmt, f_name, expr_ty) } @@ -1010,7 +1029,7 @@ impl categorize_methods for borrowck_ctxt { mutbl: m, ty: f_ty} } - fn cat_deref(node: N, base_cmt: cmt, + fn cat_deref(node: N, base_cmt: cmt, derefs: uint, expl: bool) -> option { ty::deref(self.tcx, base_cmt.ty, expl).map { |mt| alt deref_kind(self.tcx, base_cmt.ty) { @@ -1027,7 +1046,7 @@ impl categorize_methods for borrowck_ctxt { } }; @{id:node.id(), span:node.span(), - cat:cat_deref(base_cmt, ptr), lp:lp, + cat:cat_deref(base_cmt, derefs, ptr), lp:lp, mutbl:mt.mutbl, ty:mt.ty} } @@ -1041,7 +1060,7 @@ impl categorize_methods for borrowck_ctxt { } } - fn cat_autoderef(expr: @ast::expr, base: @ast::expr) -> cmt { + fn cat_autoderef(base: @ast::expr) -> cmt { // Creates a string of implicit derefences so long as base is // dereferencable. n.b., it is important that these dereferences are // associated with the field/index that caused the autoderef (expr). @@ -1050,8 +1069,10 @@ impl categorize_methods for borrowck_ctxt { // Given something like base.f where base has type @m1 @m2 T, we want // to yield the equivalent categories to (**base).f. let mut cmt = self.cat_expr(base); + let mut ctr = 0u; loop { - alt self.cat_deref(expr, cmt, false) { + ctr += 1u; + alt self.cat_deref(base, cmt, ctr, false) { none { ret cmt; } some(cmt1) { cmt = cmt1; } } @@ -1059,7 +1080,7 @@ impl categorize_methods for borrowck_ctxt { } fn cat_index(expr: @ast::expr, base: @ast::expr) -> cmt { - let base_cmt = self.cat_autoderef(expr, base); + let base_cmt = self.cat_autoderef(base); let mt = alt ty::index(self.tcx, base_cmt.ty) { some(mt) { mt } @@ -1084,7 +1105,7 @@ impl categorize_methods for borrowck_ctxt { // the head of this section let deref_lp = base_cmt.lp.map { |lp| @lp_deref(lp, ptr) }; let deref_cmt = @{id:expr.id, span:expr.span, - cat:cat_deref(base_cmt, ptr), lp:deref_lp, + cat:cat_deref(base_cmt, 0u, ptr), lp:deref_lp, mutbl:mt.mutbl, ty:mt.ty}; let comp = comp_index(base_cmt.ty); let index_lp = deref_lp.map { |lp| @lp_comp(lp, comp) }; @@ -1211,8 +1232,9 @@ impl categorize_methods for borrowck_ctxt { cat_rvalue { "rvalue" } cat_local(node_id) { #fmt["local(%d)", node_id] } cat_arg(node_id) { #fmt["arg(%d)", node_id] } - cat_deref(cmt, ptr) { - #fmt["%s->(%s)", self.cat_to_repr(cmt.cat), self.ptr_sigil(ptr)] + cat_deref(cmt, derefs, ptr) { + #fmt["%s->(%s, %u)", self.cat_to_repr(cmt.cat), + self.ptr_sigil(ptr), derefs] } cat_comp(cmt, comp) { #fmt["%s.%s", self.cat_to_repr(cmt.cat), self.comp_to_repr(comp)] @@ -1285,7 +1307,7 @@ impl categorize_methods for borrowck_ctxt { cat_rvalue { "non-lvalue" } cat_local(_) { mut_str + " local variable" } cat_arg(_) { mut_str + " argument" } - cat_deref(_, _) { "dereference of " + mut_str + " pointer" } + cat_deref(*) { "dereference of " + mut_str + " pointer" } cat_stack_upvar(_) { mut_str + " upvar" } cat_comp(_, comp_field(_)) { mut_str + " field" } cat_comp(_, comp_tuple) { "tuple content" } @@ -1446,26 +1468,31 @@ impl preserve_methods for preserve_ctxt { cat_comp(cmt1, comp_variant) { self.require_imm(cmt, cmt1, err_mut_variant) } - cat_deref(cmt1, uniq_ptr) { + cat_deref(cmt1, _, uniq_ptr) { self.require_imm(cmt, cmt1, err_mut_uniq) } - cat_deref(_, region_ptr) { + cat_deref(_, _, region_ptr) { // References are always "stable" by induction (when the // reference of type &MT was created, the memory must have // been stable) ok(()) } - cat_deref(_, unsafe_ptr) { + cat_deref(_, _, unsafe_ptr) { // Unsafe pointers are the user's problem ok(()) } - cat_deref(_, gc_ptr) { + cat_deref(_, derefs, gc_ptr) { // GC'd pointers of type @MT: always stable because we can inc // the ref count or keep a GC root as necessary. We need to // insert this id into the root_map, however. alt self.opt_scope_id { some(scope_id) { - self.bccx.root_map.insert(cmt.id, scope_id); + #debug["Inserting root map entry for %s: \ + node %d:%u -> scope %d", + self.bccx.cmt_to_repr(cmt), cmt.id, + derefs, scope_id]; + let rk = {id: cmt.id, derefs: derefs}; + self.bccx.root_map.insert(rk, scope_id); ok(()) } none { @@ -1562,7 +1589,7 @@ impl loan_methods for loan_ctxt { } } cat_comp(cmt1, comp_variant) | - cat_deref(cmt1, uniq_ptr) { + cat_deref(cmt1, _, uniq_ptr) { // Variant components: the base must be immutable, because // if it is overwritten, the types of the embedded data // could change. @@ -1573,9 +1600,9 @@ impl loan_methods for loan_ctxt { self.ok_with_loan_of(cmt, req_mutbl) } } - cat_deref(cmt1, unsafe_ptr) | - cat_deref(cmt1, gc_ptr) | - cat_deref(cmt1, region_ptr) { + cat_deref(cmt1, _, unsafe_ptr) | + cat_deref(cmt1, _, gc_ptr) | + cat_deref(cmt1, _, region_ptr) { // Aliased data is simply not lendable. self.bccx.tcx.sess.span_bug( cmt.span, diff --git a/src/rustc/middle/trans/base.rs b/src/rustc/middle/trans/base.rs index f5b76210574..43dfda53484 100644 --- a/src/rustc/middle/trans/base.rs +++ b/src/rustc/middle/trans/base.rs @@ -1641,12 +1641,34 @@ fn trans_assign_op(bcx: block, ex: @ast::expr, op: ast::binop, save_in(lhs_res.val)); } -fn autoderef(cx: block, v: ValueRef, t: ty::t) -> result_t { +fn autoderef(cx: block, e_id: ast::node_id, + v: ValueRef, t: ty::t) -> result_t { let _icx = cx.insn_ctxt("autoderef"); let mut v1: ValueRef = v; let mut t1: ty::t = t; let ccx = cx.ccx(); + let mut derefs = 0u; loop { + #debug["autoderef(e_id=%d, v1=%s, t1=%s, derefs=%u)", + e_id, val_str(ccx.tn, v1), ty_to_str(ccx.tcx, t1), + derefs]; + + // check if the result of this autoderef must be preserved + derefs += 1u; + alt cx.ccx().maps.root_map.find({id:e_id, derefs:derefs}) { + none {} + some(scope_id) { + if !cx.sess().opts.no_asm_comments { + add_comment(cx, #fmt["preserving until end of scope %d", + scope_id]); + } + let root_loc = alloca(cx, type_of(cx.ccx(), t1)); + Store(cx, v1, root_loc); + take_ty(cx, root_loc, t1); + add_root_cleanup(cx, scope_id, root_loc, t1); + } + } + alt ty::get(t1).struct { ty::ty_box(mt) { let body = GEPi(cx, v1, [0u, abi::box_field_body]); @@ -1677,8 +1699,7 @@ fn autoderef(cx: block, v: ValueRef, t: ty::t) -> result_t { if (*variants).len() != 1u || variants[0].args.len() != 1u { break; } - t1 = - ty::subst(ccx.tcx, substs, variants[0].args[0]); + t1 = ty::subst(ccx.tcx, substs, variants[0].args[0]); v1 = PointerCast(cx, v1, T_ptr(type_of(ccx, t1))); } _ { break; } @@ -2313,7 +2334,7 @@ fn trans_rec_field(bcx: block, base: @ast::expr, field: ast::ident) -> lval_result { let _icx = bcx.insn_ctxt("trans_rec_field"); let {bcx, val} = trans_temp_expr(bcx, base); - let {bcx, val, ty} = autoderef(bcx, val, expr_ty(bcx, base)); + let {bcx, val, ty} = autoderef(bcx, base.id, val, expr_ty(bcx, base)); trans_rec_field_inner(bcx, val, ty, field, base.span) } @@ -2338,7 +2359,7 @@ fn trans_index(cx: block, ex: @ast::expr, base: @ast::expr, let _icx = cx.insn_ctxt("trans_index"); let base_ty = expr_ty(cx, base); let exp = trans_temp_expr(cx, base); - let lv = autoderef(exp.bcx, exp.val, base_ty); + let lv = autoderef(exp.bcx, base.id, exp.val, base_ty); let ix = trans_temp_expr(lv.bcx, idx); let v = lv.val; let bcx = ix.bcx; @@ -2417,40 +2438,64 @@ fn trans_callee(bcx: block, e: @ast::expr) -> lval_maybe_callee { // represented as an alloca or heap, hence needs a 'load' to be used as an // immediate). fn trans_lval(cx: block, e: @ast::expr) -> lval_result { - let _icx = cx.insn_ctxt("trans_lval"); - alt e.node { - ast::expr_path(_) { - let v = trans_path(cx, e.id); - ret lval_maybe_callee_to_lval(v, expr_ty(cx, e)); + ret alt cx.ccx().maps.root_map.find({id:e.id, derefs:0u}) { + // No need to root this lvalue. + none { unrooted(cx, e) } + + // Lvalue must remain rooted until exit of `scope_id`. See + // add_root_cleanup() for comments on why this works the way it does. + some(scope_id) { + let lv = unrooted(cx, e); + + if !cx.sess().opts.no_asm_comments { + add_comment(cx, #fmt["preserving until end of scope %d", + scope_id]); + } + + let ty = expr_ty(lv.bcx, e); + let root_loc = alloca(lv.bcx, type_of(cx.ccx(), ty)); + let bcx = store_temp_expr(lv.bcx, INIT, root_loc, lv, ty, false); + add_root_cleanup(bcx, scope_id, root_loc, ty); + {bcx: bcx with lv} } - ast::expr_field(base, ident, _) { - ret trans_rec_field(cx, base, ident); - } - ast::expr_index(base, idx) { - ret trans_index(cx, e, base, idx); - } - ast::expr_unary(ast::deref, base) { - let ccx = cx.ccx(); - let sub = trans_temp_expr(cx, base); - let t = expr_ty(cx, base); - let val = alt check ty::get(t).struct { - ty::ty_box(_) { - let non_gc_val = non_gc_box_cast(sub.bcx, sub.val, t); - GEPi(sub.bcx, non_gc_val, [0u, abi::box_field_body]) + }; + + fn unrooted(cx: block, e: @ast::expr) -> lval_result { + let _icx = cx.insn_ctxt("trans_lval"); + alt e.node { + ast::expr_path(_) { + let v = trans_path(cx, e.id); + ret lval_maybe_callee_to_lval(v, expr_ty(cx, e)); } - ty::ty_res(_, _, _) { - GEPi(sub.bcx, sub.val, [0u, 1u]) + ast::expr_field(base, ident, _) { + ret trans_rec_field(cx, base, ident); } - ty::ty_enum(_, _) { - let ety = expr_ty(cx, e); - let ellty = T_ptr(type_of(ccx, ety)); - PointerCast(sub.bcx, sub.val, ellty) + ast::expr_index(base, idx) { + ret trans_index(cx, e, base, idx); } - ty::ty_ptr(_) | ty::ty_uniq(_) | ty::ty_rptr(_,_) { sub.val } - }; - ret lval_owned(sub.bcx, val); - } - _ { cx.sess().span_bug(e.span, "non-lval in trans_lval"); } + ast::expr_unary(ast::deref, base) { + let ccx = cx.ccx(); + let sub = trans_temp_expr(cx, base); + let t = expr_ty(cx, base); + let val = alt check ty::get(t).struct { + ty::ty_box(_) { + let non_gc_val = non_gc_box_cast(sub.bcx, sub.val, t); + GEPi(sub.bcx, non_gc_val, [0u, abi::box_field_body]) + } + ty::ty_res(_, _, _) { + GEPi(sub.bcx, sub.val, [0u, 1u]) + } + ty::ty_enum(_, _) { + let ety = expr_ty(cx, e); + let ellty = T_ptr(type_of(ccx, ety)); + PointerCast(sub.bcx, sub.val, ellty) + } + ty::ty_ptr(_) | ty::ty_uniq(_) | ty::ty_rptr(_,_) { sub.val } + }; + ret lval_owned(sub.bcx, val); + } + _ { cx.sess().span_bug(e.span, "non-lval in trans_lval"); } + } } } @@ -2498,12 +2543,12 @@ fn int_cast(bcx: block, lldsttype: TypeRef, llsrctype: TypeRef, let srcsz = llvm::LLVMGetIntTypeWidth(llsrctype); let dstsz = llvm::LLVMGetIntTypeWidth(lldsttype); ret if dstsz == srcsz { - BitCast(bcx, llsrc, lldsttype) - } else if srcsz > dstsz { - TruncOrBitCast(bcx, llsrc, lldsttype) - } else if signed { - SExtOrBitCast(bcx, llsrc, lldsttype) - } else { ZExtOrBitCast(bcx, llsrc, lldsttype) }; + BitCast(bcx, llsrc, lldsttype) + } else if srcsz > dstsz { + TruncOrBitCast(bcx, llsrc, lldsttype) + } else if signed { + SExtOrBitCast(bcx, llsrc, lldsttype) + } else { ZExtOrBitCast(bcx, llsrc, lldsttype) }; } fn float_cast(bcx: block, lldsttype: TypeRef, llsrctype: TypeRef, @@ -2512,14 +2557,14 @@ fn float_cast(bcx: block, lldsttype: TypeRef, llsrctype: TypeRef, let srcsz = lib::llvm::float_width(llsrctype); let dstsz = lib::llvm::float_width(lldsttype); ret if dstsz > srcsz { - FPExt(bcx, llsrc, lldsttype) - } else if srcsz > dstsz { - FPTrunc(bcx, llsrc, lldsttype) - } else { llsrc }; + FPExt(bcx, llsrc, lldsttype) + } else if srcsz > dstsz { + FPTrunc(bcx, llsrc, lldsttype) + } else { llsrc }; } enum cast_kind { cast_pointer, cast_integral, cast_float, - cast_enum, cast_other, } + cast_enum, cast_other, } fn cast_type_kind(t: ty::t) -> cast_kind { if ty::type_is_fp(t) { cast_float } else if ty::type_is_unsafe_ptr(t) { cast_pointer } @@ -2694,6 +2739,14 @@ fn trans_arg_expr(cx: block, arg: ty::arg, lldestty: TypeRef, e: @ast::expr, ret rslt(bcx, val); } +fn load_value_from_lval_result(lv: lval_result) -> ValueRef { + alt lv.kind { + temporary { lv.val } + owned { Load(lv.bcx, lv.val) } + owned_imm { lv.val } + } +} + fn adapt_borrowed_value(lv: lval_result, _arg: ty::arg, e: @ast::expr) -> lval_result { let bcx = lv.bcx; @@ -2702,13 +2755,7 @@ fn adapt_borrowed_value(lv: lval_result, _arg: ty::arg, let e_ty = expr_ty(bcx, e); alt ty::get(e_ty).struct { ty::ty_box(mt) { - let box_ptr = { - alt lv.kind { - temporary { lv.val } - owned { Load(bcx, lv.val) } - owned_imm { lv.val } - } - }; + let box_ptr = load_value_from_lval_result(lv); let body_ptr = GEPi(bcx, box_ptr, [0u, abi::box_field_body]); ret lval_temp(bcx, body_ptr); } @@ -2800,7 +2847,7 @@ fn trans_args(cx: block, llenv: ValueRef, args: call_args, fn_ty: ty::t, vec::iteri(es) {|i, e| let r = trans_arg_expr(bcx, arg_tys[i], llarg_tys[i], e, temp_cleanups, if i == last { ret_flag } - else { none }); + else { none }); bcx = r.bcx; llargs += [r.val]; } @@ -3180,234 +3227,307 @@ fn trans_temp_expr(bcx: block, e: @ast::expr) -> result { ret {bcx: bcx, val: val}; } +// Arranges for the value found in `*root_loc` to be dropped once the scope +// associated with `scope_id` exits. This is used to keep boxes live when +// there are extant region pointers pointing at the interior. +// +// Note that `root_loc` is not the value itself but rather a pointer to the +// value. Generally it in alloca'd value. The reason for this is that the +// value is initialized in an inner block but may be freed in some outer +// block, so an SSA value that is valid in the inner block may not be valid in +// the outer block. In fact, the inner block may not even execute. Rather +// than generate the full SSA form, we just use an alloca'd value. +fn add_root_cleanup(bcx: block, scope_id: ast::node_id, + root_loc: ValueRef, ty: ty::t) { + + #debug["add_root_cleanup(bcx=%s, scope_id=%d, root_loc=%s, ty=%s)", + bcx.to_str(), scope_id, val_str(bcx.ccx().tn, root_loc), + ty_to_str(bcx.ccx().tcx, ty)]; + + let bcx_scope = find_bcx_for_scope(bcx, scope_id); + add_clean_temp_mem(bcx_scope, root_loc, ty); + + fn find_bcx_for_scope(bcx: block, scope_id: ast::node_id) -> block { + let mut bcx_sid = bcx; + loop { + bcx_sid = alt bcx_sid.node_info { + some({id, _}) if id == scope_id { ret bcx_sid; } + _ { + alt bcx_sid.parent { + parent_none { + bcx.tcx().sess.bug( + #fmt["no enclosing scope with id %d", scope_id]); + } + parent_some(bcx_par) { bcx_par } + } + } + } + } + } +} + // Translate an expression, with the dest argument deciding what happens with // the result. Invariants: // - exprs returning nil or bot always get dest=ignore // - exprs with non-immediate type never get dest=by_val fn trans_expr(bcx: block, e: @ast::expr, dest: dest) -> block { let _icx = bcx.insn_ctxt("trans_expr"); - let tcx = bcx.tcx(); debuginfo::update_source_pos(bcx, e.span); if expr_is_lval(bcx, e) { ret lval_to_dps(bcx, e, dest); } - alt e.node { - ast::expr_if(cond, thn, els) | ast::expr_if_check(cond, thn, els) { - ret trans_if(bcx, cond, thn, els, dest); + ret alt bcx.ccx().maps.root_map.find({id:e.id, derefs:0u}) { + none { unrooted(bcx, e, dest) } + some(scope_id) { + #debug["expression %d found in root map with scope %d", + e.id, scope_id]; + + let ty = expr_ty(bcx, e); + let root_loc = alloca(bcx, type_of(bcx.ccx(), ty)); + let bcx = unrooted(bcx, e, save_in(root_loc)); + + if !bcx.sess().opts.no_asm_comments { + add_comment(bcx, #fmt["preserving until end of scope %d", + scope_id]); + } + + add_root_cleanup(bcx, scope_id, root_loc, ty); + let lv = {bcx: bcx, val: root_loc, kind: owned}; + lval_result_to_dps(lv, ty, false, dest) } - ast::expr_alt(expr, arms, mode) { - ret alt::trans_alt(bcx, e, expr, arms, mode, dest); - } - ast::expr_block(blk) { - ret with_scope(bcx, blk.info(), "block-expr body") {|bcx| - trans_block(bcx, blk, dest) - }; - } - ast::expr_rec(args, base) { - ret trans_rec(bcx, args, base, e.id, dest); - } - ast::expr_tup(args) { ret trans_tup(bcx, args, dest); } - ast::expr_vstore(e, v) { ret tvec::trans_vstore(bcx, e, v, dest); } - ast::expr_lit(lit) { ret trans_lit(bcx, *lit, dest); } - ast::expr_vec(args, _) { - ret tvec::trans_evec(bcx, args, ast::vstore_uniq, e.id, dest); - } - ast::expr_binary(op, lhs, rhs) { - ret trans_binary(bcx, op, lhs, rhs, dest, e); - } - ast::expr_unary(op, x) { - assert op != ast::deref; // lvals are handled above - ret trans_unary(bcx, op, x, e, dest); - } - ast::expr_addr_of(_, x) { ret trans_addr_of(bcx, x, dest); } - ast::expr_fn(proto, decl, body, cap_clause) { - ret closure::trans_expr_fn(bcx, proto, decl, body, e.span, e.id, - cap_clause, none, dest); - } - ast::expr_fn_block(decl, body, cap_clause) { - alt check ty::get(expr_ty(bcx, e)).struct { - ty::ty_fn({proto, _}) { - #debug("translating fn_block %s with type %s", - expr_to_str(e), ty_to_str(tcx, expr_ty(bcx, e))); + }; + + fn unrooted(bcx: block, e: @ast::expr, dest: dest) -> block { + let tcx = bcx.tcx(); + alt e.node { + ast::expr_if(cond, thn, els) | ast::expr_if_check(cond, thn, els) { + ret trans_if(bcx, cond, thn, els, dest); + } + ast::expr_alt(expr, arms, mode) { + ret alt::trans_alt(bcx, e, expr, arms, mode, dest); + } + ast::expr_block(blk) { + ret with_scope(bcx, blk.info(), "block-expr body") {|bcx| + trans_block(bcx, blk, dest) + }; + } + ast::expr_rec(args, base) { + ret trans_rec(bcx, args, base, e.id, dest); + } + ast::expr_tup(args) { ret trans_tup(bcx, args, dest); } + ast::expr_vstore(e, v) { ret tvec::trans_vstore(bcx, e, v, dest); } + ast::expr_lit(lit) { ret trans_lit(bcx, *lit, dest); } + ast::expr_vec(args, _) { + ret tvec::trans_evec(bcx, args, ast::vstore_uniq, e.id, dest); + } + ast::expr_binary(op, lhs, rhs) { + ret trans_binary(bcx, op, lhs, rhs, dest, e); + } + ast::expr_unary(op, x) { + assert op != ast::deref; // lvals are handled above + ret trans_unary(bcx, op, x, e, dest); + } + ast::expr_addr_of(_, x) { ret trans_addr_of(bcx, x, dest); } + ast::expr_fn(proto, decl, body, cap_clause) { ret closure::trans_expr_fn(bcx, proto, decl, body, e.span, e.id, cap_clause, none, dest); } + ast::expr_fn_block(decl, body, cap_clause) { + alt check ty::get(expr_ty(bcx, e)).struct { + ty::ty_fn({proto, _}) { + #debug("translating fn_block %s with type %s", + expr_to_str(e), ty_to_str(tcx, expr_ty(bcx, e))); + ret closure::trans_expr_fn(bcx, proto, decl, body, e.span, + e.id, cap_clause, none, dest); + } + } + } + ast::expr_loop_body(blk) { + ret trans_loop_body(bcx, e, none, dest); + } + ast::expr_bind(f, args) { + ret closure::trans_bind( + bcx, f, args, e.id, dest); + } + ast::expr_copy(a) { + if !expr_is_lval(bcx, a) { + ret trans_expr(bcx, a, dest); + } + else { ret lval_to_dps(bcx, a, dest); } + } + ast::expr_cast(val, _) { ret trans_cast(bcx, val, e.id, dest); } + ast::expr_call(f, args, _) { + ret trans_call(bcx, e, f, arg_exprs(args), e.id, dest); + } + ast::expr_field(base, _, _) { + if dest == ignore { ret trans_expr(bcx, base, ignore); } + let callee = trans_callee(bcx, e), ty = expr_ty(bcx, e); + let lv = lval_maybe_callee_to_lval(callee, ty); + revoke_clean(lv.bcx, lv.val); + memmove_ty(lv.bcx, get_dest_addr(dest), lv.val, ty); + ret lv.bcx; + } + ast::expr_index(base, idx) { + // If it is here, it's not an lval, so this is a user-defined + // index op + let origin = bcx.ccx().maps.method_map.get(e.id); + let callee_id = ast_util::op_expr_callee_id(e); + let fty = node_id_type(bcx, callee_id); + ret trans_call_inner( + bcx, e.info(), fty, + expr_ty(bcx, e), + { |bcx| + impl::trans_method_callee(bcx, callee_id, base, origin) + }, + arg_exprs([idx]), dest); + } + + // These return nothing + ast::expr_break { + assert dest == ignore; + ret trans_break(bcx); + } + ast::expr_cont { + assert dest == ignore; + ret trans_cont(bcx); + } + ast::expr_ret(ex) { + assert dest == ignore; + ret trans_ret(bcx, ex); + } + ast::expr_fail(expr) { + assert dest == ignore; + ret trans_fail_expr(bcx, some(e.span), expr); + } + ast::expr_log(_, lvl, a) { + assert dest == ignore; + ret trans_log(e, lvl, bcx, a); + } + ast::expr_assert(a) { + assert dest == ignore; + ret trans_check_expr(bcx, e, a, "Assertion"); + } + ast::expr_check(ast::checked_expr, a) { + assert dest == ignore; + ret trans_check_expr(bcx, e, a, "Predicate"); + } + ast::expr_check(ast::claimed_expr, a) { + assert dest == ignore; + /* Claims are turned on and off by a global variable + that the RTS sets. This case generates code to + check the value of that variable, doing nothing + if it's set to false and acting like a check + otherwise. */ + let c = get_extern_const(bcx.ccx().externs, bcx.ccx().llmod, + "check_claims", T_bool()); + ret with_cond(bcx, Load(bcx, c)) {|bcx| + trans_check_expr(bcx, e, a, "Claim") + }; + } + ast::expr_while(cond, body) { + assert dest == ignore; + ret trans_while(bcx, cond, body); + } + ast::expr_loop(body) { + assert dest == ignore; + ret trans_loop(bcx, body); + } + ast::expr_assign(dst, src) { + assert dest == ignore; + let src_r = trans_temp_lval(bcx, src); + let {bcx, val: addr, kind} = trans_lval(src_r.bcx, dst); + assert kind == owned; + let is_last_use = bcx.ccx().maps.last_uses.contains_key(src.id); + ret store_temp_expr(bcx, DROP_EXISTING, addr, src_r, + expr_ty(bcx, src), is_last_use); + } + ast::expr_move(dst, src) { + // FIXME: calculate copy init-ness in typestate. + assert dest == ignore; + let src_r = trans_temp_lval(bcx, src); + let {bcx, val: addr, kind} = trans_lval(src_r.bcx, dst); + assert kind == owned; + ret move_val(bcx, DROP_EXISTING, addr, src_r, + expr_ty(bcx, src)); + } + ast::expr_swap(dst, src) { + assert dest == ignore; + let lhs_res = trans_lval(bcx, dst); + assert lhs_res.kind == owned; + let rhs_res = trans_lval(lhs_res.bcx, src); + let t = expr_ty(bcx, src); + let tmp_alloc = alloc_ty(rhs_res.bcx, t); + // Swap through a temporary. + let bcx = move_val(rhs_res.bcx, INIT, tmp_alloc, lhs_res, t); + let bcx = move_val(bcx, INIT, lhs_res.val, rhs_res, t); + ret move_val(bcx, INIT, rhs_res.val, + lval_owned(bcx, tmp_alloc), t); + } + ast::expr_assign_op(op, dst, src) { + assert dest == ignore; + ret trans_assign_op(bcx, e, op, dst, src); + } + ast::expr_new(pool, alloc_id, val) { + // First, call pool->alloc(sz, align) to get back a void*. Then, + // cast this memory to the required type and evaluate value into + // it. + let ccx = bcx.ccx(); + + // Allocate space for the ptr that will be returned from + // `pool.alloc()`: + let ptr_ty = expr_ty(bcx, e); + let ptr_ptr_val = alloc_ty(bcx, ptr_ty); + + #debug["ptr_ty = %s", ty_to_str(tcx, ptr_ty)]; + #debug["ptr_ptr_val = %s", val_str(ccx.tn, ptr_ptr_val)]; + + let void_ty = ty::mk_ptr(tcx, {ty: ty::mk_nil(tcx), + mutbl: ast::m_imm}); + let voidval = { + let llvoid_ty = type_of(ccx, void_ty); + PointerCast(bcx, ptr_ptr_val, T_ptr(llvoid_ty)) + }; + + #debug["voidval = %s", val_str(ccx.tn, voidval)]; + + let llval_ty = type_of(ccx, expr_ty(bcx, val)); + let args = [llsize_of(ccx, llval_ty), llalign_of(ccx, llval_ty)]; + let origin = bcx.ccx().maps.method_map.get(alloc_id); + let bcx = trans_call_inner( + bcx, e.info(), node_id_type(bcx, alloc_id), void_ty, + {|bcx| impl::trans_method_callee(bcx, alloc_id, + pool, origin) }, + arg_vals(args), + save_in(voidval)); + + #debug["dest = %s", dest_str(ccx, dest)]; + let ptr_val = Load(bcx, ptr_ptr_val); + #debug["ptr_val = %s", val_str(ccx.tn, ptr_val)]; + let bcx = trans_expr(bcx, val, save_in(ptr_val)); + store_in_dest(bcx, ptr_val, dest) + } + _ { + bcx.tcx().sess.span_bug(e.span, "trans_expr reached \ + fall-through case"); + } } - } - ast::expr_loop_body(blk) { - ret trans_loop_body(bcx, e, none, dest); - } - ast::expr_bind(f, args) { - ret closure::trans_bind( - bcx, f, args, e.id, dest); - } - ast::expr_copy(a) { - if !expr_is_lval(bcx, a) { - ret trans_expr(bcx, a, dest); - } - else { ret lval_to_dps(bcx, a, dest); } - } - ast::expr_cast(val, _) { ret trans_cast(bcx, val, e.id, dest); } - ast::expr_call(f, args, _) { - ret trans_call(bcx, e, f, arg_exprs(args), e.id, dest); - } - ast::expr_field(base, _, _) { - if dest == ignore { ret trans_expr(bcx, base, ignore); } - let callee = trans_callee(bcx, e), ty = expr_ty(bcx, e); - let lv = lval_maybe_callee_to_lval(callee, ty); - revoke_clean(lv.bcx, lv.val); - memmove_ty(lv.bcx, get_dest_addr(dest), lv.val, ty); - ret lv.bcx; - } - ast::expr_index(base, idx) { - // If it is here, it's not an lval, so this is a user-defined index op - let origin = bcx.ccx().maps.method_map.get(e.id); - let callee_id = ast_util::op_expr_callee_id(e); - let fty = node_id_type(bcx, callee_id); - ret trans_call_inner( - bcx, e.info(), fty, - expr_ty(bcx, e), - { |bcx| - impl::trans_method_callee(bcx, callee_id, base, origin) - }, - arg_exprs([idx]), dest); - } - - // These return nothing - ast::expr_break { - assert dest == ignore; - ret trans_break(bcx); - } - ast::expr_cont { - assert dest == ignore; - ret trans_cont(bcx); - } - ast::expr_ret(ex) { - assert dest == ignore; - ret trans_ret(bcx, ex); - } - ast::expr_fail(expr) { - assert dest == ignore; - ret trans_fail_expr(bcx, some(e.span), expr); - } - ast::expr_log(_, lvl, a) { - assert dest == ignore; - ret trans_log(e, lvl, bcx, a); - } - ast::expr_assert(a) { - assert dest == ignore; - ret trans_check_expr(bcx, e, a, "Assertion"); - } - ast::expr_check(ast::checked_expr, a) { - assert dest == ignore; - ret trans_check_expr(bcx, e, a, "Predicate"); - } - ast::expr_check(ast::claimed_expr, a) { - assert dest == ignore; - /* Claims are turned on and off by a global variable - that the RTS sets. This case generates code to - check the value of that variable, doing nothing - if it's set to false and acting like a check - otherwise. */ - let c = get_extern_const(bcx.ccx().externs, bcx.ccx().llmod, - "check_claims", T_bool()); - ret with_cond(bcx, Load(bcx, c)) {|bcx| - trans_check_expr(bcx, e, a, "Claim") - }; - } - ast::expr_while(cond, body) { - assert dest == ignore; - ret trans_while(bcx, cond, body); - } - ast::expr_loop(body) { - assert dest == ignore; - ret trans_loop(bcx, body); - } - ast::expr_assign(dst, src) { - assert dest == ignore; - let src_r = trans_temp_lval(bcx, src); - let {bcx, val: addr, kind} = trans_lval(src_r.bcx, dst); - assert kind == owned; - ret store_temp_expr(bcx, DROP_EXISTING, addr, src_r, - expr_ty(bcx, src), - bcx.ccx().maps.last_uses.contains_key(src.id)); - } - ast::expr_move(dst, src) { - // FIXME: calculate copy init-ness in typestate. - assert dest == ignore; - let src_r = trans_temp_lval(bcx, src); - let {bcx, val: addr, kind} = trans_lval(src_r.bcx, dst); - assert kind == owned; - ret move_val(bcx, DROP_EXISTING, addr, src_r, - expr_ty(bcx, src)); - } - ast::expr_swap(dst, src) { - assert dest == ignore; - let lhs_res = trans_lval(bcx, dst); - assert lhs_res.kind == owned; - let rhs_res = trans_lval(lhs_res.bcx, src); - let t = expr_ty(bcx, src); - let tmp_alloc = alloc_ty(rhs_res.bcx, t); - // Swap through a temporary. - let bcx = move_val(rhs_res.bcx, INIT, tmp_alloc, lhs_res, t); - let bcx = move_val(bcx, INIT, lhs_res.val, rhs_res, t); - ret move_val(bcx, INIT, rhs_res.val, lval_owned(bcx, tmp_alloc), t); - } - ast::expr_assign_op(op, dst, src) { - assert dest == ignore; - ret trans_assign_op(bcx, e, op, dst, src); - } - ast::expr_new(pool, alloc_id, val) { - // First, call pool->alloc(sz, align) to get back a void*. Then, cast - // this memory to the required type and evaluate value into it. - let ccx = bcx.ccx(); - - // Allocate space for the ptr that will be returned from - // `pool.alloc()`: - let ptr_ty = expr_ty(bcx, e); - let ptr_ptr_val = alloc_ty(bcx, ptr_ty); - - #debug["ptr_ty = %s", ty_to_str(tcx, ptr_ty)]; - #debug["ptr_ptr_val = %s", val_str(ccx.tn, ptr_ptr_val)]; - - let void_ty = ty::mk_ptr(tcx, {ty: ty::mk_nil(tcx), - mutbl: ast::m_imm}); - let voidval = { - let llvoid_ty = type_of(ccx, void_ty); - PointerCast(bcx, ptr_ptr_val, T_ptr(llvoid_ty)) - }; - - #debug["voidval = %s", val_str(ccx.tn, voidval)]; - - let llval_ty = type_of(ccx, expr_ty(bcx, val)); - let args = [llsize_of(ccx, llval_ty), llalign_of(ccx, llval_ty)]; - let origin = bcx.ccx().maps.method_map.get(alloc_id); - let bcx = trans_call_inner( - bcx, e.info(), node_id_type(bcx, alloc_id), void_ty, - {|bcx| impl::trans_method_callee(bcx, alloc_id, pool, origin) }, - arg_vals(args), - save_in(voidval)); - - #debug["dest = %s", dest_str(ccx, dest)]; - let ptr_val = Load(bcx, ptr_ptr_val); - #debug["ptr_val = %s", val_str(ccx.tn, ptr_val)]; - let bcx = trans_expr(bcx, val, save_in(ptr_val)); - store_in_dest(bcx, ptr_val, dest) - } - _ { - bcx.tcx().sess.span_bug(e.span, "trans_expr reached \ - fall-through case"); - } } } fn lval_to_dps(bcx: block, e: @ast::expr, dest: dest) -> block { - let lv = trans_lval(bcx, e), ccx = bcx.ccx(); - let mut {bcx, val, kind} = lv; - let last_use = kind == owned && ccx.maps.last_uses.contains_key(e.id); + let last_uses = bcx.ccx().maps.last_uses; let ty = expr_ty(bcx, e); + let lv = trans_lval(bcx, e); + let last_use = (lv.kind == owned && last_uses.contains_key(e.id)); + lval_result_to_dps(lv, ty, last_use, dest) +} + +fn lval_result_to_dps(lv: lval_result, ty: ty::t, + last_use: bool, dest: dest) -> block { + let mut {bcx, val, kind} = lv; + let ccx = bcx.ccx(); alt dest { by_val(cell) { if kind == temporary { @@ -3864,16 +3984,20 @@ fn cleanup_and_leave(bcx: block, upto: option, let _icx = bcx.insn_ctxt("cleanup_and_leave"); let mut cur = bcx, bcx = bcx; let is_lpad = leave == none; - let mut done = false; loop { + #debug["cleanup_and_leave: leaving %s", cur.to_str()]; + + if !bcx.sess().opts.no_asm_comments { + add_comment(bcx, #fmt["cleanup_and_leave(%s)", cur.to_str()]); + } + alt cur.kind { block_scope(inf) if inf.cleanups.len() > 0u { - option::iter(vec::find(inf.cleanup_paths, - {|cp| cp.target == leave})) {|cp| + for vec::find(inf.cleanup_paths, + {|cp| cp.target == leave}).each {|cp| Br(bcx, cp.dest); - done = true; + ret; } - if done { ret; } let sub_cx = sub_block(bcx, "cleanup"); Br(bcx, sub_cx.llbb); inf.cleanup_paths += [{target: leave, dest: sub_cx.llbb}]; diff --git a/src/rustc/middle/trans/build.rs b/src/rustc/middle/trans/build.rs index 83c0dd0d7bc..7bf86b7068a 100644 --- a/src/rustc/middle/trans/build.rs +++ b/src/rustc/middle/trans/build.rs @@ -652,7 +652,7 @@ fn add_comment(bcx: block, text: str) { let ccx = bcx.ccx(); if (!ccx.sess.opts.no_asm_comments) { let sanitized = str::replace(text, "$", ""); - let comment_text = "; " + sanitized; + let comment_text = "# " + sanitized; let asm = str::as_c_str(comment_text, {|c| str::as_c_str("", {|e| count_insn(bcx, "inlineasm"); diff --git a/src/rustc/middle/trans/common.rs b/src/rustc/middle/trans/common.rs index 8e2bad11636..2ea0ff1f692 100644 --- a/src/rustc/middle/trans/common.rs +++ b/src/rustc/middle/trans/common.rs @@ -19,6 +19,7 @@ import lib::llvm::{ModuleRef, ValueRef, TypeRef, BasicBlockRef, BuilderRef}; import lib::llvm::{True, False, Bool}; import metadata::{csearch, encoder}; import ast_map::path; +import util::ppaux::ty_to_str; type namegen = fn@(str) -> str; fn new_namegen() -> namegen { @@ -227,6 +228,9 @@ fn cleanup_type(cx: ty::ctxt, ty: ty::t) -> cleantype { fn add_clean(cx: block, val: ValueRef, ty: ty::t) { if !ty::type_needs_drop(cx.tcx(), ty) { ret; } + #debug["add_clean(%s, %s, %s)", + cx.to_str(), val_str(cx.ccx().tn, val), + ty_to_str(cx.ccx().tcx, ty)]; let cleanup_type = cleanup_type(cx.tcx(), ty); in_scope_cx(cx) {|info| info.cleanups += [clean(bind base::drop_ty(_, val, ty), @@ -236,6 +240,9 @@ fn add_clean(cx: block, val: ValueRef, ty: ty::t) { } fn add_clean_temp(cx: block, val: ValueRef, ty: ty::t) { if !ty::type_needs_drop(cx.tcx(), ty) { ret; } + #debug["add_clean_temp(%s, %s, %s)", + cx.to_str(), val_str(cx.ccx().tn, val), + ty_to_str(cx.ccx().tcx, ty)]; let cleanup_type = cleanup_type(cx.tcx(), ty); fn do_drop(bcx: block, val: ValueRef, ty: ty::t) -> block { @@ -253,6 +260,9 @@ fn add_clean_temp(cx: block, val: ValueRef, ty: ty::t) { } fn add_clean_temp_mem(cx: block, val: ValueRef, ty: ty::t) { if !ty::type_needs_drop(cx.tcx(), ty) { ret; } + #debug["add_clean_temp_mem(%s, %s, %s)", + cx.to_str(), val_str(cx.ccx().tn, val), + ty_to_str(cx.ccx().tcx, ty)]; let cleanup_type = cleanup_type(cx.tcx(), ty); in_scope_cx(cx) {|info| info.cleanups += [clean_temp(val, bind base::drop_ty(_, val, ty), @@ -408,10 +418,21 @@ fn block_parent(cx: block) -> block { // Accessors -impl bxc_cxs for block { +impl bcx_cxs for block { fn ccx() -> @crate_ctxt { self.fcx.ccx } fn tcx() -> ty::ctxt { self.fcx.ccx.tcx } fn sess() -> session { self.fcx.ccx.sess } + + fn to_str() -> str { + alt self.node_info { + some(node_info) { + #fmt["[block %d]", node_info.id] + } + none { + #fmt["[block %x]", ptr::addr_of(*self) as uint] + } + } + } } // LLVM type constructors. diff --git a/src/rustc/middle/trans/native.rs b/src/rustc/middle/trans/native.rs index 7f153c60207..af661454932 100644 --- a/src/rustc/middle/trans/native.rs +++ b/src/rustc/middle/trans/native.rs @@ -864,8 +864,9 @@ fn trans_intrinsic(ccx: @crate_ctxt, decl: ValueRef, item: @ast::native_item, some(sub_origins)); {env: self_env(visitor, vp_ty, none) with lval} }; - bcx = trans_call_inner(bcx, none, mth_ty, ty::mk_bool(ccx.tcx), - get_lval, arg_vals(args), ignore); + bcx = trans_call_inner( + bcx, none, mth_ty, ty::mk_bool(ccx.tcx), + get_lval, arg_vals(args), ignore); } } diff --git a/src/rustc/middle/trans/reflect.rs b/src/rustc/middle/trans/reflect.rs index b1650ef465b..80f6897eca4 100644 --- a/src/rustc/middle/trans/reflect.rs +++ b/src/rustc/middle/trans/reflect.rs @@ -50,7 +50,7 @@ fn emit_calls_to_iface_visit_ty(bcx: block, t: ty::t, let get_lval = {|bcx| impl::trans_iface_callee(bcx, visitor_val, mth_ty, mth_idx) }; - trans_call_inner(bcx, mth_ty, ty::mk_bool(tcx), + trans_call_inner(bcx, none, mth_ty, ty::mk_bool(tcx), get_lval, arg_vals(args), ignore) } } diff --git a/src/test/run-pass/borrowck-preserve-box-in-field.rs b/src/test/run-pass/borrowck-preserve-box-in-field.rs new file mode 100644 index 00000000000..68e8e3b1481 --- /dev/null +++ b/src/test/run-pass/borrowck-preserve-box-in-field.rs @@ -0,0 +1,22 @@ +// compile-flags:--borrowck=err +// exec-env:RUST_POISON_ON_FREE=1 + +fn borrow(x: &int, f: fn(x: &int)) { + let before = *x; + f(x); + let after = *x; + assert before == after; +} + +fn main() { + let mut x = @{f: ~3}; + borrow(x.f) {|b_x| + assert *b_x == 3; + assert ptr::addr_of(*x.f) == ptr::addr_of(*b_x); + x = @{f: ~4}; + + #debug["ptr::addr_of(*b_x) = %x", ptr::addr_of(*b_x) as uint]; + assert *b_x == 3; + assert ptr::addr_of(*x.f) != ptr::addr_of(*b_x); + } +} \ No newline at end of file diff --git a/src/test/run-pass/borrowck-preserve-box-in-uniq.rs b/src/test/run-pass/borrowck-preserve-box-in-uniq.rs new file mode 100644 index 00000000000..f86c9b0c6c1 --- /dev/null +++ b/src/test/run-pass/borrowck-preserve-box-in-uniq.rs @@ -0,0 +1,22 @@ +// compile-flags:--borrowck=err +// exec-env:RUST_POISON_ON_FREE=1 + +fn borrow(x: &int, f: fn(x: &int)) { + let before = *x; + f(x); + let after = *x; + assert before == after; +} + +fn main() { + let mut x = ~mut @{f: ~3}; + borrow(x.f) {|b_x| + assert *b_x == 3; + assert ptr::addr_of(*x.f) == ptr::addr_of(*b_x); + *x = @{f: ~4}; + + #debug["ptr::addr_of(*b_x) = %x", ptr::addr_of(*b_x) as uint]; + assert *b_x == 3; + assert ptr::addr_of(*x.f) != ptr::addr_of(*b_x); + } +} \ No newline at end of file