From 6f35ede5a475c286066d8d88cb9a519be2133d0d Mon Sep 17 00:00:00 2001 From: Jakub Wieczorek Date: Sat, 30 Aug 2014 16:22:19 +0200 Subject: [PATCH] Remove the branch merging optimisations for slice patterns They were only correct in the simplest case. Some of the optimisations are certainly possible but should be introduced carefully and only when the whole pattern codegen infrastructure is in a better shape. Fixes #16648. --- src/librustc/middle/check_match.rs | 22 +- src/librustc/middle/pat_util.rs | 7 + src/librustc/middle/trans/_match.rs | 623 +++++++++++++--------------- src/librustc/middle/trans/adt.rs | 18 +- src/librustc/middle/trans/base.rs | 6 +- src/libsyntax/attr.rs | 2 +- src/test/run-pass/issue-16648.rs | 20 + 7 files changed, 334 insertions(+), 364 deletions(-) create mode 100644 src/test/run-pass/issue-16648.rs 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); +}