mirror of
https://github.com/rust-lang/rust.git
synced 2025-01-10 14:57:14 +00:00
get preservation of boxes working, at least in simple cases
This commit is contained in:
parent
4c7be32606
commit
adb61e3e99
@ -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;
|
||||
|
@ -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",
|
||||
|
@ -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,
|
||||
|
@ -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<ast::node_id, ast::node_id>;
|
||||
type root_map = hashmap<root_map_key, ast::node_id>;
|
||||
|
||||
// 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<T> = result<T, bckerr>;
|
||||
|
||||
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:copy,U>(&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<N:ast_node>(node: N, base_cmt: cmt,
|
||||
fn cat_deref<N:ast_node>(node: N, base_cmt: cmt, derefs: uint,
|
||||
expl: bool) -> option<cmt> {
|
||||
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,
|
||||
|
@ -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<BasicBlockRef>,
|
||||
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}];
|
||||
|
@ -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");
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
22
src/test/run-pass/borrowck-preserve-box-in-field.rs
Normal file
22
src/test/run-pass/borrowck-preserve-box-in-field.rs
Normal file
@ -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);
|
||||
}
|
||||
}
|
22
src/test/run-pass/borrowck-preserve-box-in-uniq.rs
Normal file
22
src/test/run-pass/borrowck-preserve-box-in-uniq.rs
Normal file
@ -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);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user