]> 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 3cfd1aaee8ff39a37e0f6c9a9d1dcfccdd01cfcd..d8cbf5a2341e57c3e550afa2792a9c523f0421f1 100644 (file)
 #![allow(non_camel_case_types)]
 
 use back::abi;
+use mc = middle::mem_categorization;
 use driver::config::FullDebugInfo;
-use lib::llvm::{llvm, ValueRef, BasicBlockRef};
+use euv = middle::expr_use_visitor;
+use llvm;
+use llvm::{ValueRef, BasicBlockRef};
 use middle::const_eval;
 use middle::def;
 use middle::check_match;
+use middle::check_match::StaticInliner;
 use middle::lang_items::StrEqFnLangItem;
 use middle::pat_util::*;
 use middle::resolve::DefMap;
 use middle::trans::adt;
 use middle::trans::base::*;
 use middle::trans::build::*;
+use middle::trans::build;
 use middle::trans::callee;
 use middle::trans::cleanup;
 use middle::trans::cleanup::CleanupMethods;
 use middle::trans::common::*;
 use middle::trans::consts;
-use middle::trans::controlflow;
 use middle::trans::datum::*;
 use middle::trans::expr::Dest;
 use middle::trans::expr;
 
 use std;
 use std::collections::HashMap;
-use std::cell::Cell;
 use std::rc::Rc;
 use std::gc::{Gc};
 use syntax::ast;
 use syntax::ast::Ident;
 use syntax::codemap::Span;
-use syntax::parse::token::InternedString;
-
-// An option identifying a literal: either an expression or a DefId of a static expression.
-enum Lit {
-    ExprLit(Gc<ast::Expr>),
-    ConstLit(ast::DefId),              // the def ID of the constant
-}
+use syntax::fold::Folder;
 
 #[deriving(PartialEq)]
 pub enum VecLenOpt {
@@ -241,24 +238,15 @@ pub enum VecLenOpt {
 // An option identifying a branch (either a literal, an enum variant or a
 // range)
 enum Opt {
-    lit(Lit),
+    lit(Gc<ast::Expr>),
     var(ty::Disr, Rc<adt::Repr>, ast::DefId),
     range(Gc<ast::Expr>, Gc<ast::Expr>),
     vec_len(/* length */ uint, VecLenOpt, /*range of matches*/(uint, uint))
 }
 
-fn lit_to_expr(tcx: &ty::ctxt, a: &Lit) -> Gc<ast::Expr> {
-    match *a {
-        ExprLit(existing_a_expr) => existing_a_expr,
-        ConstLit(a_const) => const_eval::lookup_const_by_id(tcx, a_const).unwrap()
-    }
-}
-
 fn opt_eq(tcx: &ty::ctxt, a: &Opt, b: &Opt) -> bool {
     match (a, b) {
-        (&lit(a), &lit(b)) => {
-            let a_expr = lit_to_expr(tcx, &a);
-            let b_expr = lit_to_expr(tcx, &b);
+        (&lit(a_expr), &lit(b_expr)) => {
             match const_eval::compare_lit_exprs(tcx, &*a_expr, &*b_expr) {
                 Some(val1) => val1 == 0,
                 None => fail!("compare_list_exprs: type mismatch"),
@@ -285,20 +273,13 @@ pub enum opt_result<'a> {
     range_result(Result<'a>, Result<'a>),
 }
 
-fn trans_opt<'a>(bcx: &'a Block<'a>, o: &Opt) -> opt_result<'a> {
+fn trans_opt<'a>(mut bcx: &'a Block<'a>, o: &Opt) -> opt_result<'a> {
     let _icx = push_ctxt("match::trans_opt");
     let ccx = bcx.ccx();
-    let mut bcx = bcx;
     match *o {
-        lit(ExprLit(ref lit_expr)) => {
-            let lit_datum = unpack_datum!(bcx, expr::trans(bcx, &**lit_expr));
-            let lit_datum = lit_datum.assert_rvalue(bcx); // literals are rvalues
-            let lit_datum = unpack_datum!(bcx, lit_datum.to_appropriate_datum(bcx));
-            return single_result(Result::new(bcx, lit_datum.val));
-        }
-        lit(l @ ConstLit(ref def_id)) => {
-            let lit_ty = ty::node_id_to_type(bcx.tcx(), lit_to_expr(bcx.tcx(), &l).id);
-            let (llval, _) = consts::get_const_val(bcx.ccx(), *def_id);
+        lit(lit_expr) => {
+            let lit_ty = ty::node_id_to_type(bcx.tcx(), lit_expr.id);
+            let (llval, _) = consts::const_expr(ccx, &*lit_expr, true);
             let lit_datum = immediate_rvalue(llval, lit_ty);
             let lit_datum = unpack_datum!(bcx, lit_datum.to_appropriate_datum(bcx));
             return single_result(Result::new(bcx, lit_datum.val));
@@ -320,20 +301,6 @@ fn trans_opt<'a>(bcx: &'a Block<'a>, o: &Opt) -> opt_result<'a> {
     }
 }
 
-fn variant_opt(bcx: &Block, pat_id: ast::NodeId) -> Opt {
-    let ccx = bcx.ccx();
-    let def = ccx.tcx.def_map.borrow().get_copy(&pat_id);
-    match def {
-        def::DefVariant(enum_id, var_id, _) => {
-            let variant = ty::enum_variant_with_id(ccx.tcx(), enum_id, var_id);
-            var(variant.disr_val, adt::represent_node(bcx, pat_id), var_id)
-        }
-        _ => {
-            ccx.sess().bug("non-variant or struct in variant_opt()");
-        }
-    }
-}
-
 #[deriving(Clone)]
 pub enum TransBindingMode {
     TrByCopy(/* llbinding */ ValueRef),
@@ -490,7 +457,7 @@ fn enter_default<'a, 'b>(
 
     // Collect all of the matches that can match against anything.
     enter_match(bcx, dm, m, col, val, |pats| {
-        if pat_is_binding_or_wild(dm, pats[col]) {
+        if pat_is_binding_or_wild(dm, &*pats[col]) {
             Some(Vec::from_slice(pats.slice_to(col)).append(pats.slice_from(col + 1)))
         } else {
             None
@@ -545,11 +512,12 @@ fn enter_opt<'a, 'b>(
     let _indenter = indenter();
 
     let ctor = match opt {
-        &lit(x) => check_match::ConstantValue(const_eval::eval_const_expr(
-            bcx.tcx(), lit_to_expr(bcx.tcx(), &x))),
-        &range(ref lo, ref hi) => check_match::ConstantRange(
-            const_eval::eval_const_expr(bcx.tcx(), &**lo),
-            const_eval::eval_const_expr(bcx.tcx(), &**hi)
+        &lit(expr) => check_match::ConstantValue(
+            const_eval::eval_const_expr(bcx.tcx(), &*expr)
+        ),
+        &range(lo, hi) => check_match::ConstantRange(
+            const_eval::eval_const_expr(bcx.tcx(), &*lo),
+            const_eval::eval_const_expr(bcx.tcx(), &*hi)
         ),
         &vec_len(len, _, _) => check_match::Slice(len),
         &var(_, _, def_id) => check_match::Variant(def_id)
@@ -646,36 +614,17 @@ fn add_veclen_to_set(set: &mut Vec<Opt> , i: uint,
         let cur = *br.pats.get(col);
         match cur.node {
             ast::PatLit(l) => {
-                add_to_set(ccx.tcx(), &mut found, lit(ExprLit(l)));
+                add_to_set(ccx.tcx(), &mut found, lit(l));
             }
-            ast::PatIdent(..) => {
+            ast::PatIdent(..) | ast::PatEnum(..) | ast::PatStruct(..) => {
                 // This is either an enum variant or a variable binding.
                 let opt_def = ccx.tcx.def_map.borrow().find_copy(&cur.id);
                 match opt_def {
-                    Some(def::DefVariant(..)) => {
-                        add_to_set(ccx.tcx(), &mut found,
-                                   variant_opt(bcx, cur.id));
-                    }
-                    Some(def::DefStatic(const_did, false)) => {
+                    Some(def::DefVariant(enum_id, var_id, _)) => {
+                        let variant = ty::enum_variant_with_id(ccx.tcx(), enum_id, var_id);
                         add_to_set(ccx.tcx(), &mut found,
-                                   lit(ConstLit(const_did)));
-                    }
-                    _ => {}
-                }
-            }
-            ast::PatEnum(..) | ast::PatStruct(..) => {
-                // This could be one of: a tuple-like enum variant, a
-                // struct-like enum variant, or a struct.
-                let opt_def = ccx.tcx.def_map.borrow().find_copy(&cur.id);
-                match opt_def {
-                    Some(def::DefFn(..)) |
-                    Some(def::DefVariant(..)) => {
-                        add_to_set(ccx.tcx(), &mut found,
-                                   variant_opt(bcx, cur.id));
-                    }
-                    Some(def::DefStatic(const_did, false)) => {
-                        add_to_set(ccx.tcx(), &mut found,
-                                   lit(ConstLit(const_did)));
+                                   var(variant.disr_val,
+                                       adt::represent_node(bcx, cur.id), var_id));
                     }
                     _ => {}
                 }
@@ -821,40 +770,18 @@ fn any_irrefutable_adt_pat(bcx: &Block, m: &[Match], col: uint) -> bool {
     })
 }
 
-struct DynamicFailureHandler<'a> {
-    bcx: &'a Block<'a>,
-    sp: Span,
-    msg: InternedString,
-    finished: Cell<Option<BasicBlockRef>>,
-}
-
-impl<'a> DynamicFailureHandler<'a> {
-    fn handle_fail(&self) -> BasicBlockRef {
-        match self.finished.get() {
-            Some(bb) => return bb,
-            _ => (),
-        }
-
-        let fcx = self.bcx.fcx;
-        let fail_cx = fcx.new_block(false, "case_fallthrough", None);
-        controlflow::trans_fail(fail_cx, self.sp, self.msg.clone());
-        self.finished.set(Some(fail_cx.llbb));
-        fail_cx.llbb
-    }
-}
-
 /// What to do when the pattern match fails.
 enum FailureHandler<'a> {
     Infallible,
     JumpToBasicBlock(BasicBlockRef),
-    DynamicFailureHandlerClass(Box<DynamicFailureHandler<'a>>),
+    Unreachable
 }
 
 impl<'a> FailureHandler<'a> {
     fn is_infallible(&self) -> bool {
         match *self {
             Infallible => true,
-            _ => false,
+            _ => false
         }
     }
 
@@ -862,15 +789,14 @@ fn is_fallible(&self) -> bool {
         !self.is_infallible()
     }
 
-    fn handle_fail(&self) -> BasicBlockRef {
+    fn handle_fail(&self, bcx: &Block) {
         match *self {
-            Infallible => {
-                fail!("attempted to fail in infallible failure handler!")
-            }
-            JumpToBasicBlock(basic_block) => basic_block,
-            DynamicFailureHandlerClass(ref dynamic_failure_handler) => {
-                dynamic_failure_handler.handle_fail()
-            }
+            Infallible =>
+                fail!("attempted to fail in infallible failure handler!"),
+            JumpToBasicBlock(basic_block) =>
+                Br(bcx, basic_block),
+            Unreachable =>
+                build::Unreachable(bcx)
         }
     }
 }
@@ -968,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
             },
@@ -982,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);
+            }
             _ => {}
         }
 
@@ -1021,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 {
@@ -1031,7 +973,7 @@ fn compile_guard<'a, 'b>(
             // condition explicitly rather than (possibly) falling back to
             // the default arm.
             &JumpToBasicBlock(_) if m.len() == 1 && has_genuine_default => {
-                Br(bcx, chk.handle_fail());
+                chk.handle_fail(bcx);
             }
             _ => {
                 compile_submatch(bcx, m, vals, chk, has_genuine_default);
@@ -1056,7 +998,7 @@ fn compile_submatch<'a, 'b>(
     let mut bcx = bcx;
     if m.len() == 0u {
         if chk.is_fallible() {
-            Br(bcx, chk.handle_fail());
+            chk.handle_fail(bcx);
         }
         return;
     }
@@ -1064,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 {
@@ -1327,7 +1270,7 @@ fn compile_submatch_continue<'a, 'b>(
             // condition explicitly rather than (eventually) falling back to
             // the last default arm.
             &JumpToBasicBlock(_) if defaults.len() == 1 && has_genuine_default => {
-                Br(else_cx, chk.handle_fail());
+                chk.handle_fail(else_cx);
             }
             _ => {
                 compile_submatch(else_cx,
@@ -1351,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;
@@ -1369,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()));
             }
@@ -1381,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;
@@ -1421,33 +1406,23 @@ fn trans_match_inner<'a>(scope_cx: &'a Block<'a>,
     }
 
     let t = node_id_type(bcx, discr_expr.id);
-    let chk = {
-        if ty::type_is_empty(tcx, t) {
-            // Special case for empty types
-            let fail_cx = Cell::new(None);
-            let fail_handler = box DynamicFailureHandler {
-                bcx: scope_cx,
-                sp: discr_expr.span,
-                msg: InternedString::new("scrutinizing value that can't \
-                                          exist"),
-                finished: fail_cx,
-            };
-            DynamicFailureHandlerClass(fail_handler)
-        } else {
-            Infallible
-        }
+    let chk = if ty::type_is_empty(tcx, t) {
+        Unreachable
+    } else {
+        Infallible
     };
 
     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() };
     let mut matches = Vec::new();
     for arm_data in arm_datas.iter() {
-        matches.extend(arm_data.arm.pats.iter().map(|p| Match {
-            pats: vec!(*p),
+        matches.extend(arm_data.arm.pats.iter().map(|&p| Match {
+            pats: vec![static_inliner.fold_pat(p)],
             data: arm_data,
             bound_ptrs: Vec::new(),
         }));
@@ -1461,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);
@@ -1612,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,
@@ -1628,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,
@@ -1751,8 +1752,6 @@ fn bind_irrefutable_pat<'a>(
                         }
                     }
                 }
-                Some(def::DefStatic(_, false)) => {
-                }
                 _ => {
                     // Nothing to do here.
                 }
@@ -1807,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;
 }