]> 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 5334205aa52347b1d9d62fff9ac1477b45966c97..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;
@@ -1292,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;
@@ -1310,7 +1354,7 @@ 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) => {
+                if !ty::type_moves_by_default(tcx, variable_ty) || reassigned => {
                 llmatch = alloca_no_lifetime(bcx,
                                  llvariable_ty.ptr_to(),
                                  "__llmatch");
@@ -1371,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() };