X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Flibrustc%2Fmiddle%2Ftrans%2F_match.rs;h=d8cbf5a2341e57c3e550afa2792a9c523f0421f1;hb=d7c0f7d1c07a060b6d06bdd60b24c78bd2c9a6c3;hp=fe5252c423cce9838ba3abbf4a4e616372709284;hpb=826b8358134f909f0b8aeb4c1d67a3fdda50b4b0;p=rust.git diff --git a/src/librustc/middle/trans/_match.rs b/src/librustc/middle/trans/_match.rs index fe5252c423c..d8cbf5a2341 100644 --- a/src/librustc/middle/trans/_match.rs +++ b/src/librustc/middle/trans/_match.rs @@ -189,7 +189,9 @@ #![allow(non_camel_case_types)] use back::abi; +use mc = middle::mem_categorization; use driver::config::FullDebugInfo; +use euv = middle::expr_use_visitor; use llvm; use llvm::{ValueRef, BasicBlockRef}; use middle::const_eval; @@ -892,7 +894,12 @@ fn insert_lllocals<'a>(mut bcx: &'a Block<'a>, bindings_map: &BindingsMap, TrByCopy(llbinding) => { let llval = Load(bcx, binding_info.llmatch); let datum = Datum::new(llval, binding_info.ty, Lvalue); + call_lifetime_start(bcx, llbinding); bcx = datum.store_to(bcx, llbinding); + match cs { + Some(cs) => bcx.fcx.schedule_lifetime_end(cs, llbinding), + _ => {} + } llbinding }, @@ -906,7 +913,10 @@ fn insert_lllocals<'a>(mut bcx: &'a Block<'a>, bindings_map: &BindingsMap, let datum = Datum::new(llval, binding_info.ty, Lvalue); match cs { - Some(cs) => bcx.fcx.schedule_drop_and_zero_mem(cs, llval, binding_info.ty), + Some(cs) => { + bcx.fcx.schedule_drop_and_zero_mem(cs, llval, binding_info.ty); + bcx.fcx.schedule_lifetime_end(cs, binding_info.llmatch); + } _ => {} } @@ -945,9 +955,17 @@ fn compile_guard<'a, 'b>( let val = unpack_datum!(bcx, expr::trans(bcx, guard_expr)); let val = val.to_llbool(bcx); + for (_, &binding_info) in data.bindings_map.iter() { + match binding_info.trmode { + TrByCopy(llbinding) => call_lifetime_end(bcx, llbinding), + _ => {} + } + } + return with_cond(bcx, Not(bcx, val), |bcx| { // Guard does not match: remove all bindings from the lllocals table for (_, &binding_info) in data.bindings_map.iter() { + call_lifetime_end(bcx, binding_info.llmatch); bcx.fcx.lllocals.borrow_mut().remove(&binding_info.id); } match chk { @@ -988,6 +1006,7 @@ fn compile_submatch<'a, 'b>( let data = &m[0].data; for &(ref ident, ref value_ptr) in m[0].bound_ptrs.iter() { let llmatch = data.bindings_map.get(ident).llmatch; + call_lifetime_start(bcx, llmatch); Store(bcx, *value_ptr, llmatch); } match data.arm.guard { @@ -1275,13 +1294,55 @@ pub fn trans_match<'a>( trans_match_inner(bcx, match_expr.id, discr_expr, arms, dest) } -fn create_bindings_map(bcx: &Block, pat: Gc) -> BindingsMap { +/// Checks whether the binding in `discr` is assigned to anywhere in the expression `body` +fn is_discr_reassigned(bcx: &Block, discr: &ast::Expr, body: &ast::Expr) -> bool { + match discr.node { + ast::ExprPath(..) => match bcx.def(discr.id) { + def::DefLocal(vid, _) | def::DefBinding(vid, _) => { + let mut rc = ReassignmentChecker { + node: vid, + reassigned: false + }; + { + let mut visitor = euv::ExprUseVisitor::new(&mut rc, bcx); + visitor.walk_expr(body); + } + rc.reassigned + } + _ => false + }, + _ => false + } +} + +struct ReassignmentChecker { + node: ast::NodeId, + reassigned: bool +} + +impl euv::Delegate for ReassignmentChecker { + fn consume(&mut self, _: ast::NodeId, _: Span, _: mc::cmt, _: euv::ConsumeMode) {} + fn consume_pat(&mut self, _: &ast::Pat, _: mc::cmt, _: euv::ConsumeMode) {} + fn borrow(&mut self, _: ast::NodeId, _: Span, _: mc::cmt, _: ty::Region, + _: ty::BorrowKind, _: euv::LoanCause) {} + fn decl_without_init(&mut self, _: ast::NodeId, _: Span) {} + fn mutate(&mut self, _: ast::NodeId, _: Span, cmt: mc::cmt, _: euv::MutateMode) { + match cmt.cat { + mc::cat_local(vid) => self.reassigned = self.node == vid, + _ => {} + } + } +} + +fn create_bindings_map(bcx: &Block, pat: Gc, + discr: &ast::Expr, body: &ast::Expr) -> BindingsMap { // Create the bindings map, which is a mapping from each binding name // to an alloca() that will be the value for that local variable. // Note that we use the names because each binding will have many ids // from the various alternatives. let ccx = bcx.ccx(); let tcx = bcx.tcx(); + let reassigned = is_discr_reassigned(bcx, discr, body); let mut bindings_map = HashMap::new(); pat_bindings(&tcx.def_map, &*pat, |bm, p_id, span, path1| { let ident = path1.node; @@ -1293,11 +1354,11 @@ fn create_bindings_map(bcx: &Block, pat: Gc) -> BindingsMap { let trmode; match bm { ast::BindByValue(_) - if !ty::type_moves_by_default(tcx, variable_ty) => { - llmatch = alloca(bcx, + if !ty::type_moves_by_default(tcx, variable_ty) || reassigned => { + llmatch = alloca_no_lifetime(bcx, llvariable_ty.ptr_to(), "__llmatch"); - trmode = TrByCopy(alloca(bcx, + trmode = TrByCopy(alloca_no_lifetime(bcx, llvariable_ty, bcx.ident(ident).as_slice())); } @@ -1305,13 +1366,13 @@ fn create_bindings_map(bcx: &Block, pat: Gc) -> BindingsMap { // in this case, the final type of the variable will be T, // but during matching we need to store a *T as explained // above - llmatch = alloca(bcx, + llmatch = alloca_no_lifetime(bcx, llvariable_ty.ptr_to(), bcx.ident(ident).as_slice()); trmode = TrByMove; } ast::BindByRef(_) => { - llmatch = alloca(bcx, + llmatch = alloca_no_lifetime(bcx, llvariable_ty, bcx.ident(ident).as_slice()); trmode = TrByRef; @@ -1354,7 +1415,7 @@ fn trans_match_inner<'a>(scope_cx: &'a Block<'a>, let arm_datas: Vec = arms.iter().map(|arm| ArmData { bodycx: fcx.new_id_block("case_body", arm.body.id), arm: arm, - bindings_map: create_bindings_map(bcx, *arm.pats.get(0)) + bindings_map: create_bindings_map(bcx, *arm.pats.get(0), discr_expr, &*arm.body) }).collect(); let mut static_inliner = StaticInliner { tcx: scope_cx.tcx() }; @@ -1375,7 +1436,7 @@ fn trans_match_inner<'a>(scope_cx: &'a Block<'a>, // to the default arm. let has_default = arms.last().map_or(false, |arm| { arm.pats.len() == 1 - && arm.pats.last().unwrap().node == ast::PatWild + && arm.pats.last().unwrap().node == ast::PatWild(ast::PatWildSingle) }); compile_submatch(bcx, matches.as_slice(), [discr_datum.val], &chk, has_default); @@ -1526,6 +1587,31 @@ pub fn store_arg<'a>(mut bcx: &'a Block<'a>, } } +/// Generates code for the pattern binding in a `for` loop like +/// `for in { ... }`. +pub fn store_for_loop_binding<'a>( + bcx: &'a Block<'a>, + pat: Gc, + llvalue: ValueRef, + body_scope: cleanup::ScopeId) + -> &'a Block<'a> { + let _icx = push_ctxt("match::store_for_loop_binding"); + + if simple_identifier(&*pat).is_some() { + // Generate nicer LLVM for the common case of a `for` loop pattern + // like `for x in blahblah { ... }`. + let binding_type = node_id_type(bcx, pat.id); + bcx.fcx.lllocals.borrow_mut().insert(pat.id, + Datum::new(llvalue, + binding_type, + Lvalue)); + return bcx + } + + // General path. Copy out the values that are used in the pattern. + bind_irrefutable_pat(bcx, pat, llvalue, BindLocal, body_scope) +} + fn mk_binding_alloca<'a,A>(bcx: &'a Block<'a>, p_id: ast::NodeId, ident: &ast::Ident, @@ -1720,7 +1806,7 @@ fn bind_irrefutable_pat<'a>( ast::PatMac(..) => { bcx.sess().span_bug(pat.span, "unexpanded macro"); } - ast::PatWild | ast::PatWildMulti | ast::PatLit(_) | ast::PatRange(_, _) => () + ast::PatWild(_) | ast::PatLit(_) | ast::PatRange(_, _) => () } return bcx; }