X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Flibrustc%2Fmiddle%2Ftrans%2F_match.rs;h=d8cbf5a2341e57c3e550afa2792a9c523f0421f1;hb=d7c0f7d1c07a060b6d06bdd60b24c78bd2c9a6c3;hp=958d2cd3774315fa4da1a53a44b2e2e7c00a6e86;hpb=b74562b0aeedd912acf2df7c2e8879f0a9b1cb2c;p=rust.git diff --git a/src/librustc/middle/trans/_match.rs b/src/librustc/middle/trans/_match.rs index 958d2cd3774..d8cbf5a2341 100644 --- a/src/librustc/middle/trans/_match.rs +++ b/src/librustc/middle/trans/_match.rs @@ -189,24 +189,27 @@ #![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; 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; @@ -219,19 +222,12 @@ 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), - ConstLit(ast::DefId), // the def ID of the constant -} +use syntax::fold::Folder; #[deriving(PartialEq)] pub enum VecLenOpt { @@ -242,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), var(ty::Disr, Rc, ast::DefId), range(Gc, Gc), vec_len(/* length */ uint, VecLenOpt, /*range of matches*/(uint, uint)) } -fn lit_to_expr(tcx: &ty::ctxt, a: &Lit) -> Gc { - 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"), @@ -286,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)); @@ -321,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), @@ -491,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 @@ -546,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) @@ -647,36 +614,17 @@ fn add_veclen_to_set(set: &mut Vec , 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)); } _ => {} } @@ -822,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>, -} - -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>), + Unreachable } impl<'a> FailureHandler<'a> { fn is_infallible(&self) -> bool { match *self { Infallible => true, - _ => false, + _ => false } } @@ -863,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) } } } @@ -969,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 }, @@ -983,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); + } _ => {} } @@ -1022,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 { @@ -1032,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); @@ -1057,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; } @@ -1065,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 { @@ -1328,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, @@ -1352,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; @@ -1370,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())); } @@ -1382,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; @@ -1422,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 = 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(), })); @@ -1462,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); @@ -1613,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, @@ -1629,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, @@ -1752,8 +1752,6 @@ fn bind_irrefutable_pat<'a>( } } } - Some(def::DefStatic(_, false)) => { - } _ => { // Nothing to do here. } @@ -1808,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; }