#![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;
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 {
// 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"),
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));
}
}
-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),
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)
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(..)) => {
+ 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,
- variant_opt(bcx, cur.id));
- }
- Some(def::DefStatic(const_did, false)) => {
- 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));
}
_ => {}
}
})
}
-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
}
}
!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)
}
}
}
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
},
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);
+ }
_ => {}
}
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 {
// 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);
let mut bcx = bcx;
if m.len() == 0u {
if chk.is_fallible() {
- Br(bcx, chk.handle_fail());
+ chk.handle_fail(bcx);
}
return;
}
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 {
// 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,
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;
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()));
}
// 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;
}
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(),
}));
// 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);
}
}
+/// 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,
// 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,
}
}
}
- Some(def::DefStatic(_, false)) => {
- }
_ => {
// Nothing to do here.
}
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;
}