diff --git a/src/librustc/middle/check_match.rs b/src/librustc/middle/check_match.rs index 5cb7651e99a56..c234682471080 100644 --- a/src/librustc/middle/check_match.rs +++ b/src/librustc/middle/check_match.rs @@ -28,8 +28,7 @@ use syntax::codemap::{Span, Spanned, DUMMY_SP}; use syntax::fold::{Folder, noop_fold_pat}; use syntax::print::pprust::pat_to_string; use syntax::parse::token; -use syntax::visit; -use syntax::visit::{Visitor, FnKind}; +use syntax::visit::{mod, Visitor, FnKind}; use util::ppaux::ty_to_string; struct Matrix(Vec>>); @@ -103,7 +102,9 @@ pub enum Constructor { /// Ranges of literal values (2..5). ConstantRange(const_val, const_val), /// Array patterns of length n. - Slice(uint) + Slice(uint), + /// Array patterns with a subslice. + SliceWithSubslice(uint, uint) } #[deriving(Clone, PartialEq)] @@ -270,13 +271,6 @@ fn check_arms(cx: &MatchCheckCtxt, arms: &[Arm]) { } } -fn raw_pat(p: Gc) -> Gc { - match p.node { - PatIdent(_, _, Some(s)) => { raw_pat(s) } - _ => { p } - } -} - fn check_exhaustive(cx: &MatchCheckCtxt, sp: Span, matrix: &Matrix) { match is_useful(cx, matrix, [wild()], ConstructWitness) { UsefulWithWitness(pats) => { @@ -821,6 +815,14 @@ pub fn specialize(cx: &MatchCheckCtxt, r: &[Gc], pats.push_all(after.as_slice()); Some(pats) }, + SliceWithSubslice(prefix, suffix) + if before.len() == prefix + && after.len() == suffix + && slice.is_some() => { + let mut pats = before.clone(); + pats.push_all(after.as_slice()); + Some(pats) + } _ => None } } diff --git a/src/librustc/middle/pat_util.rs b/src/librustc/middle/pat_util.rs index 2d9e4ee45be95..727f5ad9385a6 100644 --- a/src/librustc/middle/pat_util.rs +++ b/src/librustc/middle/pat_util.rs @@ -119,6 +119,13 @@ pub fn wild() -> Gc { box (GC) Pat { id: 0, node: PatWild(PatWildSingle), span: DUMMY_SP } } +pub fn raw_pat(p: Gc) -> Gc { + match p.node { + PatIdent(_, _, Some(s)) => { raw_pat(s) } + _ => { p } + } +} + pub fn def_to_path(tcx: &ty::ctxt, id: DefId) -> Path { ty::with_path(tcx, id, |mut path| Path { global: false, diff --git a/src/librustc/middle/trans/_match.rs b/src/librustc/middle/trans/_match.rs index bbd6c252849b3..e9a9a75737f8b 100644 --- a/src/librustc/middle/trans/_match.rs +++ b/src/librustc/middle/trans/_match.rs @@ -186,12 +186,9 @@ * */ -#![allow(non_camel_case_types)] - use back::abi; use driver::config::FullDebugInfo; use llvm::{ValueRef, BasicBlockRef}; -use llvm; use middle::check_match::StaticInliner; use middle::check_match; use middle::const_eval; @@ -203,17 +200,15 @@ use middle::pat_util::*; use middle::resolve::DefMap; use middle::trans::adt; use middle::trans::base::*; -use middle::trans::build::{And, BitCast, Br, CondBr, GEPi, InBoundsGEP, Load}; -use middle::trans::build::{Mul, Not, Store, Sub, Switch, add_comment}; +use middle::trans::build::{AddCase, And, BitCast, Br, CondBr, GEPi, InBoundsGEP, Load}; +use middle::trans::build::{Mul, Not, Store, Sub, add_comment}; use middle::trans::build; use middle::trans::callee; -use middle::trans::cleanup; -use middle::trans::cleanup::CleanupMethods; +use middle::trans::cleanup::{mod, CleanupMethods}; use middle::trans::common::*; use middle::trans::consts; use middle::trans::datum::*; -use middle::trans::expr::Dest; -use middle::trans::expr; +use middle::trans::expr::{mod, Dest}; use middle::trans::tvec; use middle::trans::type_of; use middle::trans::debuginfo; @@ -223,83 +218,85 @@ use util::ppaux::{Repr, vec_map_to_string}; use std; use std::collections::HashMap; -use std::rc::Rc; use std::gc::{Gc}; +use std::rc::Rc; use syntax::ast; use syntax::ast::Ident; use syntax::codemap::Span; use syntax::fold::Folder; -#[deriving(PartialEq)] -pub enum VecLenOpt { - vec_len_eq, - vec_len_ge(/* length of prefix */uint) +struct ConstantExpr<'a>(&'a ty::ctxt, Gc); + +impl<'a> Eq for ConstantExpr<'a> { + fn assert_receiver_is_total_eq(&self) {} +} + +impl<'a> PartialEq for ConstantExpr<'a> { + fn eq(&self, other: &ConstantExpr<'a>) -> bool { + let &ConstantExpr(tcx, expr) = self; + let &ConstantExpr(_, other_expr) = other; + match const_eval::compare_lit_exprs(tcx, &*expr, &*other_expr) { + Some(val1) => val1 == 0, + None => fail!("compare_list_exprs: type mismatch"), + } + } } -// An option identifying a branch (either a literal, an enum variant or a -// range) -enum Opt { - lit(Gc), - var(ty::Disr, Rc, ast::DefId), - range(Gc, Gc), - vec_len(/* length */ uint, VecLenOpt, /*range of matches*/(uint, uint)) +// An option identifying a branch (either a literal, an enum variant or a range) +#[deriving(Eq, PartialEq)] +enum Opt<'a> { + ConstantValue(ConstantExpr<'a>), + ConstantRange(ConstantExpr<'a>, ConstantExpr<'a>), + Variant(ty::Disr, Rc, ast::DefId), + SliceLengthEqual(uint), + SliceLengthGreaterOrEqual(/* prefix length */ uint, /* suffix length */ uint), } -fn opt_eq(tcx: &ty::ctxt, a: &Opt, b: &Opt) -> bool { - match (a, 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"), +impl<'a> Opt<'a> { + fn trans(&self, mut bcx: &'a Block<'a>) -> OptResult<'a> { + let _icx = push_ctxt("match::trans_opt"); + let ccx = bcx.ccx(); + match *self { + ConstantValue(ConstantExpr(_, 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)); + SingleResult(Result::new(bcx, lit_datum.val)) } - } - (&range(ref a1, ref a2), &range(ref b1, ref b2)) => { - let m1 = const_eval::compare_lit_exprs(tcx, &**a1, &**b1); - let m2 = const_eval::compare_lit_exprs(tcx, &**a2, &**b2); - match (m1, m2) { - (Some(val1), Some(val2)) => (val1 == 0 && val2 == 0), - _ => fail!("compare_list_exprs: type mismatch"), + ConstantRange( + ConstantExpr(_, ref l1), + ConstantExpr(_, ref l2)) => { + let (l1, _, _) = consts::const_expr(ccx, &**l1, true); + let (l2, _, _) = consts::const_expr(ccx, &**l2, true); + RangeResult(Result::new(bcx, l1), Result::new(bcx, l2)) + } + Variant(disr_val, ref repr, _) => { + adt::trans_case(bcx, &**repr, disr_val) + } + SliceLengthEqual(length) => { + SingleResult(Result::new(bcx, C_uint(ccx, length))) + } + SliceLengthGreaterOrEqual(prefix, suffix) => { + LowerBound(Result::new(bcx, C_uint(ccx, prefix + suffix))) } } - (&var(a, _, _), &var(b, _, _)) => a == b, - (&vec_len(a1, a2, _), &vec_len(b1, b2, _)) => - a1 == b1 && a2 == b2, - _ => false } } -pub enum opt_result<'a> { - single_result(Result<'a>), - lower_bound(Result<'a>), - range_result(Result<'a>, Result<'a>), +#[deriving(PartialEq)] +pub enum BranchKind { + NoBranch, + Single, + Switch, + Compare, + CompareSliceLength } -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(); - match *o { - 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)); - } - var(disr_val, ref repr, _) => { - return adt::trans_case(bcx, &**repr, disr_val); - } - range(ref l1, ref l2) => { - let (l1, _, _) = consts::const_expr(ccx, &**l1, true); - let (l2, _, _) = consts::const_expr(ccx, &**l2, true); - return range_result(Result::new(bcx, l1), Result::new(bcx, l2)); - } - vec_len(n, vec_len_eq, _) => { - return single_result(Result::new(bcx, C_int(ccx, n as int))); - } - vec_len(n, vec_len_ge(_), _) => { - return lower_bound(Result::new(bcx, C_int(ccx, n as int))); - } - } +pub enum OptResult<'a> { + SingleResult(Result<'a>), + RangeResult(Result<'a>, Result<'a>), + LowerBound(Result<'a>) } #[deriving(Clone)] @@ -412,7 +409,7 @@ fn expand_nested_bindings<'a, 'b>( }).collect() } -type enter_pats<'a> = |&[Gc]|: 'a -> Option>>; +type EnterPatterns<'a> = |&[Gc]|: 'a -> Option>>; fn enter_match<'a, 'b>( bcx: &'b Block<'b>, @@ -420,7 +417,7 @@ fn enter_match<'a, 'b>( m: &'a [Match<'a, 'b>], col: uint, val: ValueRef, - e: enter_pats) + e: EnterPatterns) -> Vec> { debug!("enter_match(bcx={}, m={}, col={}, val={})", bcx.to_str(), @@ -434,9 +431,20 @@ fn enter_match<'a, 'b>( let this = *br.pats.get(col); let mut bound_ptrs = br.bound_ptrs.clone(); match this.node { - ast::PatIdent(_, ref path1, None) => { + ast::PatIdent(_, ref path, None) => { if pat_is_binding(dm, &*this) { - bound_ptrs.push((path1.node, val)); + bound_ptrs.push((path.node, val)); + } + } + ast::PatVec(ref before, Some(slice), ref after) => { + match slice.node { + ast::PatIdent(_, ref path, None) => { + let subslice_val = bind_subslice_pat( + bcx, this.id, val, + before.len(), after.len()); + bound_ptrs.push((path.node, subslice_val)); + } + _ => {} } } _ => {} @@ -522,100 +530,36 @@ fn enter_opt<'a, 'b>( let _indenter = indenter(); let ctor = match opt { - &lit(expr) => check_match::ConstantValue( + &ConstantValue(ConstantExpr(_, expr)) => check_match::ConstantValue( const_eval::eval_const_expr(bcx.tcx(), &*expr) ), - &range(lo, hi) => check_match::ConstantRange( + &ConstantRange(ConstantExpr(_, lo), ConstantExpr(_, 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) + &SliceLengthEqual(n) => + check_match::Slice(n), + &SliceLengthGreaterOrEqual(before, after) => + check_match::SliceWithSubslice(before, after), + &Variant(_, _, def_id) => + check_match::Variant(def_id) }; - let mut i = 0; - let tcx = bcx.tcx(); let mcx = check_match::MatchCheckCtxt { tcx: bcx.tcx() }; - enter_match(bcx, dm, m, col, val, |pats| { - let span = pats[col].span; - let specialized = match pats[col].node { - ast::PatVec(ref before, slice, ref after) => { - let (lo, hi) = match *opt { - vec_len(_, _, (lo, hi)) => (lo, hi), - _ => tcx.sess.span_bug(span, - "vec pattern but not vec opt") - }; - - let elems = match slice { - Some(slice) if i >= lo && i <= hi => { - let n = before.len() + after.len(); - let this_opt = vec_len(n, vec_len_ge(before.len()), - (lo, hi)); - if opt_eq(tcx, &this_opt, opt) { - let mut new_before = Vec::new(); - for pat in before.iter() { - new_before.push(*pat); - } - new_before.push(slice); - for pat in after.iter() { - new_before.push(*pat); - } - Some(new_before) - } else { - None - } - } - None if i >= lo && i <= hi => { - let n = before.len(); - if opt_eq(tcx, &vec_len(n, vec_len_eq, (lo,hi)), opt) { - let mut new_before = Vec::new(); - for pat in before.iter() { - new_before.push(*pat); - } - Some(new_before) - } else { - None - } - } - _ => None - }; - elems.map(|head| head.append(pats.slice_to(col)).append(pats.slice_from(col + 1))) - } - _ => { - check_match::specialize(&mcx, pats.as_slice(), &ctor, col, variant_size) - } - }; - i += 1; - specialized - }) + enter_match(bcx, dm, m, col, val, |pats| + check_match::specialize(&mcx, pats.as_slice(), &ctor, col, variant_size) + ) } // Returns the options in one column of matches. An option is something that // needs to be conditionally matched at runtime; for example, the discriminant // on a set of enum variants or a literal. -fn get_options(bcx: &Block, m: &[Match], col: uint) -> Vec { +fn get_branches<'a>(bcx: &'a Block, m: &[Match], col: uint) -> Vec> { let ccx = bcx.ccx(); - fn add_to_set(tcx: &ty::ctxt, set: &mut Vec, val: Opt) { - if set.iter().any(|l| opt_eq(tcx, l, &val)) {return;} - set.push(val); - } - // Vector comparisons are special in that since the actual - // conditions over-match, we need to be careful about them. This - // means that in order to properly handle things in order, we need - // to not always merge conditions. - fn add_veclen_to_set(set: &mut Vec , i: uint, - len: uint, vlo: VecLenOpt) { - match set.last() { - // If the last condition in the list matches the one we want - // to add, then extend its range. Otherwise, make a new - // vec_len with a range just covering the new entry. - Some(&vec_len(len2, vlo2, (start, end))) - if len == len2 && vlo == vlo2 => { - let length = set.len(); - *set.get_mut(length - 1) = - vec_len(len, vlo, (start, end+1)) - } - _ => set.push(vec_len(len, vlo, (i, i))) + + fn add_to_set<'a>(set: &mut Vec>, opt: Opt<'a>) { + if !set.contains(&opt) { + set.push(opt); } } @@ -624,7 +568,7 @@ fn get_options(bcx: &Block, m: &[Match], col: uint) -> Vec { let cur = *br.pats.get(col); match cur.node { ast::PatLit(l) => { - add_to_set(ccx.tcx(), &mut found, lit(l)); + add_to_set(&mut found, ConstantValue(ConstantExpr(ccx.tcx(), l))); } ast::PatIdent(..) | ast::PatEnum(..) | ast::PatStruct(..) => { // This is either an enum variant or a variable binding. @@ -632,28 +576,30 @@ fn get_options(bcx: &Block, m: &[Match], col: uint) -> Vec { match opt_def { 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, - var(variant.disr_val, - adt::represent_node(bcx, cur.id), var_id)); + add_to_set(&mut found, Variant( + variant.disr_val, + adt::represent_node(bcx, cur.id), var_id + )); } _ => {} } } ast::PatRange(l1, l2) => { - add_to_set(ccx.tcx(), &mut found, range(l1, l2)); + add_to_set(&mut found, ConstantRange( + ConstantExpr(ccx.tcx(), l1), + ConstantExpr(ccx.tcx(), l2) + )); } - ast::PatVec(ref before, slice, ref after) => { - let (len, vec_opt) = match slice { - None => (before.len(), vec_len_eq), - Some(_) => (before.len() + after.len(), - vec_len_ge(before.len())) - }; - add_veclen_to_set(&mut found, i, len, vec_opt); + ast::PatVec(ref before, None, ref after) => { + add_to_set(&mut found, SliceLengthEqual(before.len() + after.len())); + } + ast::PatVec(ref before, Some(_), ref after) => { + add_to_set(&mut found, SliceLengthGreaterOrEqual(before.len(), after.len())); } _ => {} } } - return found; + found } struct ExtractedBlock<'a> { @@ -675,62 +621,58 @@ fn extract_variant_args<'a>( ExtractedBlock { vals: args, bcx: bcx } } -fn match_datum(bcx: &Block, - val: ValueRef, - pat_id: ast::NodeId) - -> Datum { +fn match_datum(val: ValueRef, left_ty: ty::t) -> Datum { /*! * Helper for converting from the ValueRef that we pass around in * the match code, which is always an lvalue, into a Datum. Eventually * we should just pass around a Datum and be done with it. */ - - let ty = node_id_type(bcx, pat_id); - Datum::new(val, ty, Lvalue) + Datum::new(val, left_ty, Lvalue) } +fn bind_subslice_pat<'a>( + bcx: &'a Block<'a>, + pat_id: ast::NodeId, + val: ValueRef, + offset_left: uint, + offset_right: uint) -> ValueRef { + let _icx = push_ctxt("match::bind_subslice_pat"); + let vec_ty = node_id_type(bcx, pat_id); + let vt = tvec::vec_types(bcx, ty::sequence_element_type(bcx.tcx(), ty::type_content(vec_ty))); + let vec_datum = match_datum(val, vec_ty); + let (base, len) = vec_datum.get_vec_base_and_len(bcx); + + let slice_byte_offset = Mul(bcx, vt.llunit_size, C_uint(bcx.ccx(), offset_left)); + let slice_begin = tvec::pointer_add_byte(bcx, base, slice_byte_offset); + let slice_len_offset = C_uint(bcx.ccx(), offset_left + offset_right); + let slice_len = Sub(bcx, len, slice_len_offset); + let slice_ty = ty::mk_slice(bcx.tcx(), + ty::ReStatic, + ty::mt {ty: vt.unit_ty, mutbl: ast::MutImmutable}); + let scratch = rvalue_scratch_datum(bcx, slice_ty, ""); + Store(bcx, slice_begin, + GEPi(bcx, scratch.val, [0u, abi::slice_elt_base])); + Store(bcx, slice_len, GEPi(bcx, scratch.val, [0u, abi::slice_elt_len])); + scratch.val +} fn extract_vec_elems<'a>( bcx: &'a Block<'a>, - pat_id: ast::NodeId, - elem_count: uint, - slice: Option, + left_ty: ty::t, + before: uint, + after: uint, val: ValueRef) -> ExtractedBlock<'a> { let _icx = push_ctxt("match::extract_vec_elems"); - let vec_datum = match_datum(bcx, val, pat_id); + let vec_datum = match_datum(val, left_ty); let (base, len) = vec_datum.get_vec_base_and_len(bcx); - let vec_ty = node_id_type(bcx, pat_id); - let vt = tvec::vec_types(bcx, ty::sequence_element_type(bcx.tcx(), ty::type_content(vec_ty))); - - let mut elems = Vec::from_fn(elem_count, |i| { - match slice { - None => GEPi(bcx, base, [i]), - Some(n) if i < n => GEPi(bcx, base, [i]), - Some(n) if i > n => { - InBoundsGEP(bcx, base, [ - Sub(bcx, len, - C_int(bcx.ccx(), (elem_count - i) as int))]) - } - _ => unsafe { llvm::LLVMGetUndef(vt.llunit_ty.to_ref()) } - } - }); - if slice.is_some() { - let n = slice.unwrap(); - let slice_byte_offset = Mul(bcx, vt.llunit_size, C_uint(bcx.ccx(), n)); - let slice_begin = tvec::pointer_add_byte(bcx, base, slice_byte_offset); - let slice_len_offset = C_uint(bcx.ccx(), elem_count - 1u); - let slice_len = Sub(bcx, len, slice_len_offset); - let slice_ty = ty::mk_slice(bcx.tcx(), - ty::ReStatic, - ty::mt {ty: vt.unit_ty, mutbl: ast::MutImmutable}); - let scratch = rvalue_scratch_datum(bcx, slice_ty, ""); - Store(bcx, slice_begin, - GEPi(bcx, scratch.val, [0u, abi::slice_elt_base])); - Store(bcx, slice_len, GEPi(bcx, scratch.val, [0u, abi::slice_elt_len])); - *elems.get_mut(n) = scratch.val; - } - + let mut elems = vec![]; + elems.extend(range(0, before).map(|i| GEPi(bcx, base, [i]))); + elems.extend(range(0, after).rev().map(|i| { + InBoundsGEP(bcx, base, [ + Sub(bcx, len, C_uint(bcx.ccx(), i + 1)) + ]) + })); ExtractedBlock { vals: elems, bcx: bcx } } @@ -757,19 +699,19 @@ fn any_region_pat(m: &[Match], col: uint) -> bool { any_pat!(m, col, ast::PatRegion(_)) } -fn any_irrefutable_adt_pat(bcx: &Block, m: &[Match], col: uint) -> bool { +fn any_irrefutable_adt_pat(tcx: &ty::ctxt, m: &[Match], col: uint) -> bool { m.iter().any(|br| { let pat = *br.pats.get(col); match pat.node { ast::PatTup(_) => true, ast::PatStruct(..) => { - match bcx.tcx().def_map.borrow().find(&pat.id) { + match tcx.def_map.borrow().find(&pat.id) { Some(&def::DefVariant(..)) => false, _ => true, } } ast::PatEnum(..) | ast::PatIdent(_, _, None) => { - match bcx.tcx().def_map.borrow().find(&pat.id) { + match tcx.def_map.borrow().find(&pat.id) { Some(&def::DefFn(..)) | Some(&def::DefStruct(..)) => true, _ => false @@ -788,21 +730,21 @@ enum FailureHandler<'a> { } impl<'a> FailureHandler<'a> { - fn is_infallible(&self) -> bool { + fn is_fallible(&self) -> bool { match *self { - Infallible => true, - _ => false + Infallible => false, + _ => true } } - fn is_fallible(&self) -> bool { - !self.is_infallible() + fn is_infallible(&self) -> bool { + !self.is_fallible() } fn handle_fail(&self, bcx: &Block) { match *self { Infallible => - fail!("attempted to fail in infallible failure handler!"), + fail!("attempted to fail in an infallible failure handler!"), JumpToBasicBlock(basic_block) => Br(bcx, basic_block), Unreachable => @@ -840,9 +782,6 @@ fn pick_col(m: &[Match]) -> uint { return best_col; } -#[deriving(PartialEq)] -pub enum branch_kind { no_branch, single, switch, compare, compare_vec_len } - // Compiles a comparison between two things. fn compare_values<'a>( cx: &'a Block<'a>, @@ -972,7 +911,7 @@ fn compile_guard<'a, 'b>( } } - return with_cond(bcx, Not(bcx, val), |bcx| { + 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); @@ -990,7 +929,7 @@ fn compile_guard<'a, 'b>( } }; bcx - }); + }) } fn compile_submatch<'a, 'b>( @@ -1012,7 +951,9 @@ fn compile_submatch<'a, 'b>( } return; } - if m[0].pats.len() == 0u { + + let col_count = m[0].pats.len(); + if col_count == 0u { 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; @@ -1078,7 +1019,7 @@ fn compile_submatch_continue<'a, 'b>( }; let mcx = check_match::MatchCheckCtxt { tcx: bcx.tcx() }; - let adt_vals = if any_irrefutable_adt_pat(bcx, m, col) { + let adt_vals = if any_irrefutable_adt_pat(bcx.tcx(), m, col) { let repr = adt::represent_type(bcx.ccx(), left_ty); let arg_count = adt::num_args(&*repr, 0); let field_vals: Vec = std::iter::range(0, arg_count).map(|ix| @@ -1088,7 +1029,13 @@ fn compile_submatch_continue<'a, 'b>( } else if any_uniq_pat(m, col) || any_region_pat(m, col) { Some(vec!(Load(bcx, val))) } else { - None + match ty::get(left_ty).sty { + ty::ty_vec(_, Some(n)) => { + let args = extract_vec_elems(bcx, left_ty, n, 0, val); + Some(args.vals) + } + _ => None + } }; match adt_vals { @@ -1104,46 +1051,46 @@ fn compile_submatch_continue<'a, 'b>( } // Decide what kind of branch we need - let opts = get_options(bcx, m, col); + let opts = get_branches(bcx, m, col); debug!("options={:?}", opts); - let mut kind = no_branch; + let mut kind = NoBranch; let mut test_val = val; debug!("test_val={}", bcx.val_to_string(test_val)); if opts.len() > 0u { match *opts.get(0) { - var(_, ref repr, _) => { + ConstantValue(_) | ConstantRange(_, _) => { + test_val = load_if_immediate(bcx, val, left_ty); + kind = if ty::type_is_integral(left_ty) { + Switch + } else { + Compare + }; + } + Variant(_, ref repr, _) => { let (the_kind, val_opt) = adt::trans_switch(bcx, &**repr, val); kind = the_kind; for &tval in val_opt.iter() { test_val = tval; } } - lit(_) => { - test_val = load_if_immediate(bcx, val, left_ty); - kind = if ty::type_is_integral(left_ty) { switch } - else { compare }; - } - range(_, _) => { - test_val = Load(bcx, val); - kind = compare; - }, - vec_len(..) => { + SliceLengthEqual(_) | SliceLengthGreaterOrEqual(_, _) => { let (_, len) = tvec::get_base_and_len(bcx, val, left_ty); test_val = len; - kind = compare_vec_len; + kind = Switch; } } } for o in opts.iter() { match *o { - range(_, _) => { kind = compare; break } + ConstantRange(_, _) => { kind = Compare; break }, + SliceLengthGreaterOrEqual(_, _) => { kind = CompareSliceLength; break }, _ => () } } let else_cx = match kind { - no_branch | single => bcx, + NoBranch | Single => bcx, _ => bcx.fcx.new_temp_block("match_else") }; - let sw = if kind == switch { - Switch(bcx, test_val, else_cx.llbb, opts.len()) + let sw = if kind == Switch { + build::Switch(bcx, test_val, else_cx.llbb, opts.len()) } else { C_int(ccx, 0) // Placeholder for when not using a switch }; @@ -1160,119 +1107,106 @@ fn compile_submatch_continue<'a, 'b>( // for the current conditional branch. let mut branch_chk = None; let mut opt_cx = else_cx; - if !exhaustive || i+1 < len { + if !exhaustive || i + 1 < len { opt_cx = bcx.fcx.new_temp_block("match_case"); match kind { - single => Br(bcx, opt_cx.llbb), - switch => { - match trans_opt(bcx, opt) { - single_result(r) => { - unsafe { - llvm::LLVMAddCase(sw, r.val, opt_cx.llbb); - bcx = r.bcx; + Single => Br(bcx, opt_cx.llbb), + Switch => { + match opt.trans(bcx) { + SingleResult(r) => { + AddCase(sw, r.val, opt_cx.llbb); + bcx = r.bcx; + } + _ => { + bcx.sess().bug( + "in compile_submatch, expected \ + opt.trans() to return a SingleResult") } - } - _ => { - bcx.sess().bug( - "in compile_submatch, expected \ - trans_opt to return a single_result") - } - } - } - compare | compare_vec_len => { - let t = if kind == compare { - left_ty - } else { - ty::mk_uint() // vector length - }; - let Result {bcx: after_cx, val: matches} = { - match trans_opt(bcx, opt) { - single_result(Result {bcx, val}) => { - compare_values(bcx, test_val, val, t) - } - lower_bound(Result {bcx, val}) => { - compare_scalar_types(bcx, test_val, val, t, ast::BiGe) - } - range_result(Result {val: vbegin, ..}, - Result {bcx, val: vend}) => { - let Result {bcx, val: llge} = - compare_scalar_types( - bcx, test_val, - vbegin, t, ast::BiGe); - let Result {bcx, val: llle} = - compare_scalar_types( - bcx, test_val, vend, - t, ast::BiLe); - Result::new(bcx, And(bcx, llge, llle)) - } - } - }; - bcx = fcx.new_temp_block("compare_next"); - - // If none of the sub-cases match, and the current condition - // is guarded or has multiple patterns, move on to the next - // condition, if there is any, rather than falling back to - // the default. - let guarded = m[i].data.arm.guard.is_some(); - let multi_pats = m[i].pats.len() > 1; - if i + 1 < len && (guarded || multi_pats || kind == compare_vec_len) { - branch_chk = Some(JumpToBasicBlock(bcx.llbb)); - } - CondBr(after_cx, matches, opt_cx.llbb, bcx.llbb); - } - _ => () + } + } + Compare | CompareSliceLength => { + let t = if kind == Compare { + left_ty + } else { + ty::mk_uint() // vector length + }; + let Result { bcx: after_cx, val: matches } = { + match opt.trans(bcx) { + SingleResult(Result { bcx, val }) => { + compare_values(bcx, test_val, val, t) + } + RangeResult(Result { val: vbegin, .. }, + Result { bcx, val: vend }) => { + let Result { bcx, val: llge } = + compare_scalar_types( + bcx, test_val, + vbegin, t, ast::BiGe); + let Result { bcx, val: llle } = + compare_scalar_types( + bcx, test_val, vend, + t, ast::BiLe); + Result::new(bcx, And(bcx, llge, llle)) + } + LowerBound(Result { bcx, val }) => { + compare_scalar_types(bcx, test_val, val, t, ast::BiGe) + } + } + }; + bcx = fcx.new_temp_block("compare_next"); + + // If none of the sub-cases match, and the current condition + // is guarded or has multiple patterns, move on to the next + // condition, if there is any, rather than falling back to + // the default. + let guarded = m[i].data.arm.guard.is_some(); + let multi_pats = m[i].pats.len() > 1; + if i + 1 < len && (guarded || multi_pats || kind == CompareSliceLength) { + branch_chk = Some(JumpToBasicBlock(bcx.llbb)); + } + CondBr(after_cx, matches, opt_cx.llbb, bcx.llbb); + } + _ => () } - } else if kind == compare || kind == compare_vec_len { + } else if kind == Compare || kind == CompareSliceLength { Br(bcx, else_cx.llbb); } let mut size = 0u; let mut unpacked = Vec::new(); match *opt { - var(disr_val, ref repr, _) => { + Variant(disr_val, ref repr, _) => { let ExtractedBlock {vals: argvals, bcx: new_bcx} = extract_variant_args(opt_cx, &**repr, disr_val, val); size = argvals.len(); unpacked = argvals; opt_cx = new_bcx; } - vec_len(n, vt, _) => { - let (n, slice) = match vt { - vec_len_ge(i) => (n + 1u, Some(i)), - vec_len_eq => (n, None) - }; - let args = extract_vec_elems(opt_cx, pat_id, n, - slice, val); + SliceLengthEqual(len) => { + let args = extract_vec_elems(opt_cx, left_ty, len, 0, val); size = args.vals.len(); unpacked = args.vals.clone(); opt_cx = args.bcx; } - lit(_) | range(_, _) => () + SliceLengthGreaterOrEqual(before, after) => { + let args = extract_vec_elems(opt_cx, left_ty, before, after, val); + size = args.vals.len(); + unpacked = args.vals.clone(); + opt_cx = args.bcx; + } + ConstantValue(_) | ConstantRange(_, _) => () } let opt_ms = enter_opt(opt_cx, pat_id, dm, m, opt, col, size, val); let opt_vals = unpacked.append(vals_left.as_slice()); - - match branch_chk { - None => { - compile_submatch(opt_cx, - opt_ms.as_slice(), - opt_vals.as_slice(), - chk, - has_genuine_default) - } - Some(branch_chk) => { - compile_submatch(opt_cx, - opt_ms.as_slice(), - opt_vals.as_slice(), - &branch_chk, - has_genuine_default) - } - } + compile_submatch(opt_cx, + opt_ms.as_slice(), + opt_vals.as_slice(), + branch_chk.as_ref().unwrap_or(chk), + has_genuine_default); } // Compile the fall-through case, if any - if !exhaustive && kind != single { - if kind == compare || kind == compare_vec_len { + if !exhaustive && kind != Single { + if kind == Compare || kind == CompareSliceLength { Br(bcx, else_cx.llbb); } match chk { @@ -1801,20 +1735,25 @@ fn bind_irrefutable_pat<'a>( bcx = bind_irrefutable_pat(bcx, inner, loaded_val, binding_mode, cleanup_scope); } ast::PatVec(ref before, ref slice, ref after) => { - let extracted = extract_vec_elems( - bcx, pat.id, before.len() + 1u + after.len(), - slice.map(|_| before.len()), val - ); + let pat_ty = node_id_type(bcx, pat.id); + let mut extracted = extract_vec_elems(bcx, pat_ty, before.len(), after.len(), val); + match slice { + &Some(_) => { + extracted.vals.insert( + before.len(), + bind_subslice_pat(bcx, pat.id, val, before.len(), after.len()) + ); + } + &None => () + } bcx = before - .iter().map(|v| Some(*v)) - .chain(Some(*slice).move_iter()) - .chain(after.iter().map(|v| Some(*v))) - .zip(extracted.vals.iter()) - .fold(bcx, |bcx, (inner, elem)| { - inner.map_or(bcx, |inner| { - bind_irrefutable_pat(bcx, inner, *elem, binding_mode, cleanup_scope) - }) - }); + .iter() + .chain(slice.iter()) + .chain(after.iter()) + .zip(extracted.vals.move_iter()) + .fold(bcx, |bcx, (&inner, elem)| + bind_irrefutable_pat(bcx, inner, elem, binding_mode, cleanup_scope) + ); } ast::PatMac(..) => { bcx.sess().span_bug(pat.span, "unexpanded macro"); diff --git a/src/librustc/middle/trans/adt.rs b/src/librustc/middle/trans/adt.rs index f3f4a88fdee4f..31d21cd707bc3 100644 --- a/src/librustc/middle/trans/adt.rs +++ b/src/librustc/middle/trans/adt.rs @@ -74,6 +74,7 @@ type Hint = attr::ReprAttr; /// Representations. +#[deriving(Eq, PartialEq)] pub enum Repr { /// C-like enums; basically an int. CEnum(IntType, Disr, Disr), // discriminant range (signedness based on the IntType) @@ -126,6 +127,7 @@ pub enum Repr { } /// For structs, and struct-like parts of anything fancier. +#[deriving(Eq, PartialEq)] pub struct Struct { // If the struct is DST, then the size and alignment do not take into // account the unsized fields of the struct. @@ -280,7 +282,7 @@ struct Case { } -#[deriving(Show)] +#[deriving(Eq, PartialEq, Show)] pub enum PointerField { ThinPointer(uint), FatPointer(uint, uint) @@ -572,14 +574,14 @@ fn struct_llfields(cx: &CrateContext, st: &Struct, sizing: bool, dst: bool) -> V * This should ideally be less tightly tied to `_match`. */ pub fn trans_switch(bcx: &Block, r: &Repr, scrutinee: ValueRef) - -> (_match::branch_kind, Option) { + -> (_match::BranchKind, Option) { match *r { CEnum(..) | General(..) | RawNullablePointer { .. } | StructWrappedNullablePointer { .. } => { - (_match::switch, Some(trans_get_discr(bcx, r, scrutinee, None))) + (_match::Switch, Some(trans_get_discr(bcx, r, scrutinee, None))) } Univariant(..) => { - (_match::single, None) + (_match::Single, None) } } } @@ -664,14 +666,14 @@ fn load_discr(bcx: &Block, ity: IntType, ptr: ValueRef, min: Disr, max: Disr) * This should ideally be less tightly tied to `_match`. */ pub fn trans_case<'a>(bcx: &'a Block<'a>, r: &Repr, discr: Disr) - -> _match::opt_result<'a> { + -> _match::OptResult<'a> { match *r { CEnum(ity, _, _) => { - _match::single_result(Result::new(bcx, C_integral(ll_inttype(bcx.ccx(), ity), + _match::SingleResult(Result::new(bcx, C_integral(ll_inttype(bcx.ccx(), ity), discr as u64, true))) } General(ity, _, _) => { - _match::single_result(Result::new(bcx, C_integral(ll_inttype(bcx.ccx(), ity), + _match::SingleResult(Result::new(bcx, C_integral(ll_inttype(bcx.ccx(), ity), discr as u64, true))) } Univariant(..) => { @@ -680,7 +682,7 @@ pub fn trans_case<'a>(bcx: &'a Block<'a>, r: &Repr, discr: Disr) RawNullablePointer { .. } | StructWrappedNullablePointer { .. } => { assert!(discr == 0 || discr == 1); - _match::single_result(Result::new(bcx, C_bool(bcx.ccx(), discr != 0))) + _match::SingleResult(Result::new(bcx, C_bool(bcx.ccx(), discr != 0))) } } } diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index f05602bbb58c8..d22dcb180707d 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -772,11 +772,11 @@ pub fn iter_structural_ty<'r, // comparison know not to proceed when the discriminants differ. match adt::trans_switch(cx, &*repr, av) { - (_match::single, None) => { + (_match::Single, None) => { cx = iter_variant(cx, &*repr, av, &**variants.get(0), substs, f); } - (_match::switch, Some(lldiscrim_a)) => { + (_match::Switch, Some(lldiscrim_a)) => { cx = f(cx, lldiscrim_a, ty::mk_int()); let unr_cx = fcx.new_temp_block("enum-iter-unr"); Unreachable(unr_cx); @@ -791,7 +791,7 @@ pub fn iter_structural_ty<'r, variant.disr_val.to_string().as_slice()) .as_slice()); match adt::trans_case(cx, &*repr, variant.disr_val) { - _match::single_result(r) => { + _match::SingleResult(r) => { AddCase(llswitch, r.val, variant_cx.llbb) } _ => ccx.sess().unimpl("value from adt::trans_case \ diff --git a/src/libsyntax/attr.rs b/src/libsyntax/attr.rs index 4b2a3073755eb..c234bea0a331e 100644 --- a/src/libsyntax/attr.rs +++ b/src/libsyntax/attr.rs @@ -498,7 +498,7 @@ impl ReprAttr { } } -#[deriving(PartialEq, Show)] +#[deriving(Eq, Hash, PartialEq, Show)] pub enum IntType { SignedInt(ast::IntTy), UnsignedInt(ast::UintTy) diff --git a/src/test/run-pass/issue-16648.rs b/src/test/run-pass/issue-16648.rs new file mode 100644 index 0000000000000..7ddb20811a376 --- /dev/null +++ b/src/test/run-pass/issue-16648.rs @@ -0,0 +1,20 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn main() { + let x: (int, &[int]) = (2i, &[1i, 2i]); + assert_eq!(match x { + (0, [_, _]) => 0, + (1, _) => 1, + (2, [_, _]) => 2, + (2, _) => 3, + _ => 4 + }, 2i); +}