fn from_val(val: ValueRef) -> MatchInput {
MatchInput {
val: val,
- lval: Lvalue,
+ lval: Lvalue::new("MatchInput::from_val"),
}
}
cs: Option<cleanup::ScopeId>)
-> Block<'blk, 'tcx> {
for (&ident, &binding_info) in bindings_map {
- let llval = match binding_info.trmode {
+ let (llval, aliases_other_state) = match binding_info.trmode {
// By value mut binding for a copy type: load from the ptr
// into the matched value and copy to our alloca
TrByCopy(llbinding) |
TrByMoveIntoCopy(llbinding) => {
let llval = Load(bcx, binding_info.llmatch);
- let datum = Datum::new(llval, binding_info.ty, Lvalue);
+ let lval = match binding_info.trmode {
+ TrByCopy(..) =>
+ Lvalue::new("_match::insert_lllocals"),
+ TrByMoveIntoCopy(..) =>
+ Lvalue::match_input("_match::insert_lllocals", bcx, binding_info.id),
+ _ => unreachable!(),
+ };
+ let datum = Datum::new(llval, binding_info.ty, lval);
call_lifetime_start(bcx, llbinding);
bcx = datum.store_to(bcx, llbinding);
if let Some(cs) = cs {
bcx.fcx.schedule_lifetime_end(cs, llbinding);
}
- llbinding
+ (llbinding, false)
},
// By value move bindings: load from the ptr into the matched value
- TrByMoveRef => Load(bcx, binding_info.llmatch),
+ TrByMoveRef => (Load(bcx, binding_info.llmatch), true),
// By ref binding: use the ptr into the matched value
- TrByRef => binding_info.llmatch
+ TrByRef => (binding_info.llmatch, true),
};
- let datum = Datum::new(llval, binding_info.ty, Lvalue);
+ let lval = Lvalue::local("_match::insert_lllocals",
+ bcx,
+ binding_info.id,
+ aliases_other_state);
+ let datum = Datum::new(llval, binding_info.ty, lval);
if let Some(cs) = cs {
bcx.fcx.schedule_lifetime_end(cs, binding_info.llmatch);
bcx.fcx.schedule_drop_and_fill_mem(cs, llval, binding_info.ty);
let scope = cleanup::var_scope(tcx, p_id);
bcx = mk_binding_alloca(
bcx, p_id, path1.node.name, scope, (),
+ "_match::store_local::create_dummy_locals",
|(), bcx, llval, ty| { drop_done_fill_mem(bcx, llval, ty); bcx });
});
bcx
let var_scope = cleanup::var_scope(tcx, local.id);
return mk_binding_alloca(
bcx, pat.id, ident.name, var_scope, (),
+ "_match::store_local",
|(), bcx, v, _| expr::trans_into(bcx, &**init_expr,
expr::SaveIn(v)));
}
name: ast::Name,
cleanup_scope: cleanup::ScopeId,
arg: A,
+ caller_name: &'static str,
populate: F)
-> Block<'blk, 'tcx> where
F: FnOnce(A, Block<'blk, 'tcx>, ValueRef, Ty<'tcx>) -> Block<'blk, 'tcx>,
// Now that memory is initialized and has cleanup scheduled,
// create the datum and insert into the local variable map.
- let datum = Datum::new(llval, var_ty, Lvalue);
+ let lval = Lvalue::binding(caller_name, bcx, p_id, name);
+ let datum = Datum::new(llval, var_ty, lval);
bcx.fcx.lllocals.borrow_mut().insert(p_id, datum);
bcx
}
// map.
bcx = mk_binding_alloca(
bcx, pat.id, path1.node.name, cleanup_scope, (),
+ "_match::bind_irrefutable_pat",
|(), bcx, llval, ty| {
match pat_binding_mode {
ast::BindByValue(_) => {
));
bcx = fold_variants(bcx, r, val, |variant_cx, st, value| {
let ptr = struct_field_ptr(variant_cx, st, value, (st.fields.len() - 1), false);
- datum::Datum::new(ptr, ptr_ty, datum::Lvalue)
+ datum::Datum::new(ptr, ptr_ty, datum::Lvalue::new("adt::trans_drop_flag_ptr"))
.store_to(variant_cx, scratch.val)
});
let expr_datum = scratch.to_expr_datum();
use trans::closure;
use trans::common::{Block, C_bool, C_bytes_in_context, C_i32, C_int, C_integral};
use trans::common::{C_null, C_struct_in_context, C_u64, C_u8, C_undef};
-use trans::common::{CrateContext, FunctionContext};
+use trans::common::{CrateContext, DropFlagHintsMap, FunctionContext};
use trans::common::{Result, NodeIdAndSpan};
use trans::common::{node_id_type, return_type_is_void};
use trans::common::{type_is_immediate, type_is_zero_size, val_ty};
caller_expects_out_pointer: uses_outptr,
lllocals: RefCell::new(NodeMap()),
llupvars: RefCell::new(NodeMap()),
+ lldropflag_hints: RefCell::new(DropFlagHintsMap::new()),
id: id,
param_substs: param_substs,
span: sp,
use trans::build;
use trans::common;
use trans::common::{Block, FunctionContext, NodeIdAndSpan};
+use trans::datum::{Datum, Lvalue};
use trans::debuginfo::{DebugLoc, ToDebugLoc};
use trans::glue;
use middle::region;
CustomScope(CustomScopeIndex)
}
+#[derive(Copy, Clone, Debug)]
+pub struct DropHint<K>(pub ast::NodeId, pub K);
+
+pub type DropHintDatum<'tcx> = DropHint<Datum<'tcx, Lvalue>>;
+pub type DropHintValue = DropHint<ValueRef>;
+
impl<'blk, 'tcx> CleanupMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx> {
/// Invoked when we start to trans the code contained within a new cleanup scope.
fn push_ast_cleanup_scope(&self, debug_loc: NodeIdAndSpan) {
type RvalueDatum<'tcx> = datum::Datum<'tcx, datum::Rvalue>;
pub type LvalueDatum<'tcx> = datum::Datum<'tcx, datum::Lvalue>;
+#[derive(Clone, Debug)]
+struct HintEntry<'tcx> {
+ // The datum for the dropflag-hint itself; note that many
+ // source-level Lvalues will be associated with the same
+ // dropflag-hint datum.
+ datum: cleanup::DropHintDatum<'tcx>,
+}
+
+pub struct DropFlagHintsMap<'tcx> {
+ // Maps NodeId for expressions that read/write unfragmented state
+ // to that state's drop-flag "hint." (A stack-local hint
+ // indicates either that (1.) it is certain that no-drop is
+ // needed, or (2.) inline drop-flag must be consulted.)
+ node_map: NodeMap<HintEntry<'tcx>>,
+}
+
+impl<'tcx> DropFlagHintsMap<'tcx> {
+ pub fn new() -> DropFlagHintsMap<'tcx> { DropFlagHintsMap { node_map: NodeMap() } }
+ pub fn has_hint(&self, id: ast::NodeId) -> bool { self.node_map.contains_key(&id) }
+}
+
// Function context. Every LLVM function we create will have one of
// these.
pub struct FunctionContext<'a, 'tcx: 'a> {
// Same as above, but for closure upvars
pub llupvars: RefCell<NodeMap<ValueRef>>,
+ // Carries info about drop-flags for local bindings (longer term,
+ // paths) for the code being compiled.
+ pub lldropflag_hints: RefCell<DropFlagHintsMap<'tcx>>,
+
// The NodeId of the function, or -1 if it doesn't correspond to
// a user-defined function.
pub id: ast::NodeId,
/// `val` is a pointer into memory for which a cleanup is scheduled
/// (and thus has type *T). If you move out of an Lvalue, you must
/// zero out the memory (FIXME #5016).
- LvalueExpr,
+ LvalueExpr(Lvalue),
}
-#[derive(Clone, Copy, Debug)]
-pub struct Lvalue;
+#[derive(Copy, Clone, Debug)]
+pub enum DropFlagInfo {
+ DontZeroJustUse(ast::NodeId),
+ ZeroAndMaintain(ast::NodeId),
+ None,
+}
+
+impl DropFlagInfo {
+ pub fn must_zero(&self) -> bool {
+ match *self {
+ DropFlagInfo::DontZeroJustUse(..) => false,
+ DropFlagInfo::ZeroAndMaintain(..) => true,
+ DropFlagInfo::None => true,
+ }
+ }
+
+ pub fn hint_to_maintain(&self) -> Option<ast::NodeId> {
+ match *self {
+ DropFlagInfo::DontZeroJustUse(id) => Some(id),
+ DropFlagInfo::ZeroAndMaintain(id) => Some(id),
+ DropFlagInfo::None => None,
+ }
+ }
+}
+
+// FIXME: having Lvalue be `Copy` is a bit of a footgun, since clients
+// may not realize that subparts of an Lvalue can have a subset of
+// drop-flags associated with them, while this as written will just
+// memcpy the drop_flag_info. But, it is an easier way to get `_match`
+// off the ground to just let this be `Copy` for now.
+#[derive(Copy, Clone, Debug)]
+pub struct Lvalue {
+ pub source: &'static str,
+ pub drop_flag_info: DropFlagInfo
+}
#[derive(Debug)]
pub struct Rvalue {
pub mode: RvalueMode
}
+impl Lvalue {
+ pub fn new(source: &'static str) -> Lvalue {
+ Lvalue { source: source, drop_flag_info: DropFlagInfo::None }
+ }
+
+ pub fn upvar<'blk, 'tcx>(source: &'static str,
+ bcx: Block<'blk, 'tcx>,
+ id: ast::NodeId) -> Lvalue {
+ let info = if Lvalue::has_dropflag_hint(bcx, id) {
+ DropFlagInfo::ZeroAndMaintain(id)
+ } else {
+ DropFlagInfo::None
+ };
+ let info = if bcx.tcx().sess.nonzeroing_move_hints() { info } else { DropFlagInfo::None };
+ debug!("upvar Lvalue at {}, id: {} info: {:?}", source, id, info);
+ Lvalue { source: source, drop_flag_info: info }
+ }
+
+ pub fn match_input<'blk, 'tcx>(source: &'static str,
+ bcx: Block<'blk, 'tcx>,
+ id: ast::NodeId) -> Lvalue
+ {
+ let info = if Lvalue::has_dropflag_hint(bcx, id) {
+ // match_input is used to move from the input into a
+ // separate stack slot; it must zero (at least until we
+ // improve things to track drop flags for the fragmented
+ // parent match input expression).
+ DropFlagInfo::ZeroAndMaintain(id)
+ } else {
+ DropFlagInfo::None
+ };
+ let info = if bcx.tcx().sess.nonzeroing_move_hints() { info } else { DropFlagInfo::None };
+ debug!("match_input Lvalue at {}, id: {} info: {:?}", source, id, info);
+ Lvalue { source: source, drop_flag_info: info }
+ }
+
+ pub fn local<'blk, 'tcx>(source: &'static str,
+ bcx: Block<'blk, 'tcx>,
+ id: ast::NodeId,
+ aliases_other_state: bool)
+ -> Lvalue
+ {
+ let info = if Lvalue::has_dropflag_hint(bcx, id) {
+ if aliases_other_state {
+ DropFlagInfo::ZeroAndMaintain(id)
+ } else {
+ DropFlagInfo::DontZeroJustUse(id)
+ }
+ } else {
+ DropFlagInfo::None
+ };
+ let info = if bcx.tcx().sess.nonzeroing_move_hints() { info } else { DropFlagInfo::None };
+ debug!("local Lvalue at {}, id: {} info: {:?}", source, id, info);
+ Lvalue { source: source, drop_flag_info: info }
+ }
+
+ pub fn store_arg<'blk, 'tcx>(source: &'static str,
+ bcx: Block<'blk, 'tcx>,
+ id: ast::NodeId) -> Lvalue
+ {
+ let info = if Lvalue::has_dropflag_hint(bcx, id) {
+ DropFlagInfo::ZeroAndMaintain(id)
+ } else {
+ DropFlagInfo::None
+ };
+ let info = if bcx.tcx().sess.nonzeroing_move_hints() { info } else { DropFlagInfo::None };
+ debug!("store_arg Lvalue at {}, id: {} info: {:?}", source, id, info);
+ Lvalue { source: source, drop_flag_info: info }
+ }
+
+ pub fn binding<'blk, 'tcx>(source: &'static str,
+ bcx: Block<'blk, 'tcx>,
+ id: ast::NodeId,
+ name: ast::Name) -> Lvalue {
+ let info = if Lvalue::has_dropflag_hint(bcx, id) {
+ DropFlagInfo::DontZeroJustUse(id)
+ } else {
+ DropFlagInfo::None
+ };
+ let info = if bcx.tcx().sess.nonzeroing_move_hints() { info } else { DropFlagInfo::None };
+ debug!("binding Lvalue at {}, id: {} name: {} info: {:?}",
+ source, id, name, info);
+ Lvalue { source: source, drop_flag_info: info }
+ }
+
+ fn has_dropflag_hint<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
+ id: ast::NodeId) -> bool {
+ bcx.fcx.lldropflag_hints.borrow().has_hint(id)
+ }
+}
+
impl Rvalue {
pub fn new(m: RvalueMode) -> Rvalue {
Rvalue { mode: m }
bcx.fcx.schedule_lifetime_end(scope, scratch);
bcx.fcx.schedule_drop_mem(scope, scratch, ty);
- DatumBlock::new(bcx, Datum::new(scratch, ty, Lvalue))
+ DatumBlock::new(bcx, Datum::new(scratch, ty, Lvalue::new("datum::lvalue_scratch_datum")))
}
/// Allocates temporary space on the stack using alloca() and returns a by-ref Datum pointing to
}
fn to_expr_kind(self) -> Expr {
- LvalueExpr
+ LvalueExpr(self)
}
}
ty: Ty<'tcx>)
-> Block<'blk, 'tcx> {
match *self {
- LvalueExpr => Lvalue.post_store(bcx, val, ty),
+ LvalueExpr(ref l) => l.post_store(bcx, val, ty),
RvalueExpr(ref r) => r.post_store(bcx, val, ty),
}
}
fn is_by_ref(&self) -> bool {
match *self {
- LvalueExpr => Lvalue.is_by_ref(),
+ LvalueExpr(ref l) => l.is_by_ref(),
RvalueExpr(ref r) => r.is_by_ref()
}
}
match self.kind.mode {
ByRef => {
add_rvalue_clean(ByRef, fcx, scope, self.val, self.ty);
- DatumBlock::new(bcx, Datum::new(self.val, self.ty, Lvalue))
+ DatumBlock::new(bcx, Datum::new(
+ self.val,
+ self.ty,
+ Lvalue::new("datum::to_lvalue_datum_in_scope")))
}
ByValue => {
{
let Datum { val, ty, kind } = self;
match kind {
- LvalueExpr => if_lvalue(Datum::new(val, ty, Lvalue)),
+ LvalueExpr(l) => if_lvalue(Datum::new(val, ty, l)),
RvalueExpr(r) => if_rvalue(Datum::new(val, ty, r)),
}
}
};
Datum {
val: val,
- kind: Lvalue,
+ kind: Lvalue::new("Datum::get_element"),
ty: ty,
}
}
let const_ty = expr_ty_adjusted(bcx, expr);
let llty = type_of::type_of(bcx.ccx(), const_ty);
let global = PointerCast(bcx, global, llty.ptr_to());
- let datum = Datum::new(global, const_ty, Lvalue);
+ let datum = Datum::new(global, const_ty, Lvalue::new("expr::trans"));
return DatumBlock::new(bcx, datum.to_expr_datum());
}
// Always generate an lvalue datum, because this pointer doesn't own
// the data and cleanup is scheduled elsewhere.
- DatumBlock::new(bcx, Datum::new(scratch.val, scratch.ty, LvalueExpr))
+ DatumBlock::new(bcx, Datum::new(scratch.val, scratch.ty, LvalueExpr(d.kind)))
}
})
Some(SaveIn(scratch.val)),
false));
let datum = scratch.to_expr_datum();
+ let lval = Lvalue::new("expr::trans_index overload");
if type_is_sized(bcx.tcx(), elt_ty) {
- Datum::new(datum.to_llscalarish(bcx), elt_ty, LvalueExpr)
+ Datum::new(datum.to_llscalarish(bcx), elt_ty, LvalueExpr(lval))
} else {
- Datum::new(datum.val, elt_ty, LvalueExpr)
+ Datum::new(datum.val, elt_ty, LvalueExpr(lval))
}
}
None => {
});
let elt = InBoundsGEP(bcx, base, &[ix_val]);
let elt = PointerCast(bcx, elt, type_of::type_of(ccx, unit_ty).ptr_to());
- Datum::new(elt, unit_ty, LvalueExpr)
+ let lval = Lvalue::new("expr::trans_index fallback");
+ Datum::new(elt, unit_ty, LvalueExpr(lval))
}
};
// Case 2.
base::get_extern_const(bcx.ccx(), did, const_ty)
};
- DatumBlock::new(bcx, Datum::new(val, const_ty, LvalueExpr))
+ let lval = Lvalue::new("expr::trans_def");
+ DatumBlock::new(bcx, Datum::new(val, const_ty, LvalueExpr(lval)))
}
def::DefConst(_) => {
bcx.sess().span_bug(ref_expr.span,
def::DefUpvar(nid, _) => {
// Can't move upvars, so this is never a ZeroMemLastUse.
let local_ty = node_id_type(bcx, nid);
+ let lval = Lvalue::upvar("expr::trans_local_var", bcx, nid);
match bcx.fcx.llupvars.borrow().get(&nid) {
- Some(&val) => Datum::new(val, local_ty, Lvalue),
+ Some(&val) => Datum::new(val, local_ty, lval),
None => {
bcx.sess().bug(&format!(
"trans_local_var: no llval for upvar {} found",
if type_is_sized(bcx.tcx(), content_ty) {
let ptr = load_ty(bcx, datum.val, datum.ty);
- DatumBlock::new(bcx, Datum::new(ptr, content_ty, LvalueExpr))
+ DatumBlock::new(bcx, Datum::new(ptr, content_ty, LvalueExpr(datum.kind)))
} else {
// A fat pointer and a DST lvalue have the same representation
// just different types. Since there is no temporary for `*e`
// object code path for running drop glue and free. Instead,
// we schedule cleanup for `e`, turning it into an lvalue.
- let datum = Datum::new(datum.val, content_ty, LvalueExpr);
+ let lval = Lvalue::new("expr::deref_once ty_uniq");
+ let datum = Datum::new(datum.val, content_ty, LvalueExpr(lval));
DatumBlock::new(bcx, datum)
}
}
ty::TyRawPtr(ty::TypeAndMut { ty: content_ty, .. }) |
ty::TyRef(_, ty::TypeAndMut { ty: content_ty, .. }) => {
+ let lval = Lvalue::new("expr::deref_once ptr");
if type_is_sized(bcx.tcx(), content_ty) {
let ptr = datum.to_llscalarish(bcx);
// rvalue for non-owning pointers like &T or *T, in which
// case cleanup *is* scheduled elsewhere, by the true
// owner (or, in the case of *T, by the user).
- DatumBlock::new(bcx, Datum::new(ptr, content_ty, LvalueExpr))
+ DatumBlock::new(bcx, Datum::new(ptr, content_ty, LvalueExpr(lval)))
} else {
// A fat pointer and a DST lvalue have the same representation
// just different types.
- DatumBlock::new(bcx, Datum::new(datum.val, content_ty, LvalueExpr))
+ DatumBlock::new(bcx, Datum::new(datum.val, content_ty, LvalueExpr(lval)))
}
}