]> git.lizzy.rs Git - rust.git/blobdiff - src/librustc/middle/trans/_match.rs
librustc: Don't use the same alloca for match binding which we reassign to in arm...
[rust.git] / src / librustc / middle / trans / _match.rs
index 7ba49f5a2b90df532ea12ba2e95c52b336cef272..d8cbf5a2341e57c3e550afa2792a9c523f0421f1 100644 (file)
 #![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<ast::Pat>) -> 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<ast::Pat>,
+                      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<ast::Pat>) -> 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<ast::Pat>) -> 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<ArmData> = 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 <pat> in <expr> { ... }`.
+pub fn store_for_loop_binding<'a>(
+                              bcx: &'a Block<'a>,
+                              pat: Gc<ast::Pat>,
+                              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,
@@ -1542,6 +1628,7 @@ fn mk_binding_alloca<'a,A>(bcx: &'a Block<'a>,
     // Subtle: be sure that we *populate* the memory *before*
     // we schedule the cleanup.
     let bcx = populate(arg, bcx, llval, var_ty);
+    bcx.fcx.schedule_lifetime_end(cleanup_scope, llval);
     bcx.fcx.schedule_drop_mem(cleanup_scope, llval, var_ty);
 
     // Now that memory is initialized and has cleanup scheduled,
@@ -1719,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;
 }