From 5c717a6fc21594569d9e64968cdcf2e88e372a07 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Fri, 11 Mar 2016 12:54:59 +0200 Subject: [PATCH] implement RFC495 semantics for slice patterns non-MIR translation is still not supported for these and will happily ICE. This is a [breaking-change] for many uses of slice_patterns. --- src/librustc/middle/expr_use_visitor.rs | 34 ---- src/librustc/middle/mem_categorization.rs | 41 +---- src/librustc/mir/repr.rs | 29 +-- src/librustc/mir/tcx.rs | 15 +- src/librustc/mir/visit.rs | 11 +- .../borrowck/mir/abs_domain.rs | 2 + .../borrowck/mir/gather_moves.rs | 16 -- src/librustc_const_eval/check_match.rs | 103 +++++------ src/librustc_mir/build/matches/mod.rs | 91 +++++++--- src/librustc_mir/build/matches/simplify.rs | 43 ++--- src/librustc_mir/build/matches/test.rs | 165 +++++++++++++----- src/librustc_mir/build/matches/util.rs | 73 ++------ src/librustc_mir/transform/qualify_consts.rs | 2 +- src/librustc_mir/transform/type_check.rs | 20 +++ src/librustc_trans/mir/lvalue.rs | 40 ++++- src/librustc_trans/mir/rvalue.rs | 24 +-- src/librustc_typeck/check/_match.rs | 55 +++--- src/librustc_typeck/check/regionck.rs | 14 +- src/librustc_typeck/diagnostics.rs | 5 +- src/libstd/sys/common/wtf8.rs | 21 ++- .../borrowck/borrowck-move-out-of-vec-tail.rs | 8 +- .../borrowck-vec-pattern-element-loan.rs | 6 +- .../borrowck-vec-pattern-loan-from-mut.rs | 2 +- .../borrowck-vec-pattern-move-tail.rs | 2 +- .../borrowck/borrowck-vec-pattern-nesting.rs | 15 +- .../borrowck-vec-pattern-tail-element-loan.rs | 2 +- src/test/compile-fail/issue-12369.rs | 8 +- src/test/compile-fail/issue-12567.rs | 12 +- src/test/compile-fail/issue-13482-2.rs | 6 +- src/test/compile-fail/issue-13482.rs | 6 +- src/test/compile-fail/issue-15381.rs | 4 +- src/test/compile-fail/match-vec-mismatch-2.rs | 5 +- src/test/compile-fail/match-vec-mismatch.rs | 33 +++- .../compile-fail/match-vec-unreachable.rs | 8 +- .../non-exhaustive-match-nested.rs | 10 +- src/test/compile-fail/non-exhaustive-match.rs | 22 +-- .../non-exhaustive-pattern-witness.rs | 4 +- src/test/run-pass/issue-15080.rs | 6 +- src/test/run-pass/issue-15104.rs | 4 +- src/test/run-pass/issue-16648.rs | 7 +- src/test/run-pass/issue-7784.rs | 2 + src/test/run-pass/match-vec-alternatives.rs | 40 +++-- src/test/run-pass/vec-matching-fold.rs | 24 ++- .../vec-matching-legal-tail-element-borrow.rs | 5 +- src/test/run-pass/vec-matching.rs | 77 +++++++- src/test/run-pass/vec-tail-matching.rs | 22 +-- .../run-pass/zero_sized_subslice_match.rs | 5 +- 47 files changed, 626 insertions(+), 523 deletions(-) diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs index 48b5420dd6b..ba93e897677 100644 --- a/src/librustc/middle/expr_use_visitor.rs +++ b/src/librustc/middle/expr_use_visitor.rs @@ -993,40 +993,6 @@ impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> { } } } - PatKind::Vec(_, Some(ref slice_pat), _) => { - // The `slice_pat` here creates a slice into - // the original vector. This is effectively a - // borrow of the elements of the vector being - // matched. - - let (slice_cmt, slice_mutbl, slice_r) = - return_if_err!(mc.cat_slice_pattern(cmt_pat, &slice_pat)); - - // Note: We declare here that the borrow - // occurs upon entering the `[...]` - // pattern. This implies that something like - // `[a; b]` where `a` is a move is illegal, - // because the borrow is already in effect. - // In fact such a move would be safe-ish, but - // it effectively *requires* that we use the - // nulling out semantics to indicate when a - // value has been moved, which we are trying - // to move away from. Otherwise, how can we - // indicate that the first element in the - // vector has been moved? Eventually, we - // could perhaps modify this rule to permit - // `[..a, b]` where `b` is a move, because in - // that case we can adjust the length of the - // original vec accordingly, but we'd have to - // make trans do the right thing, and it would - // only work for `Box<[T]>`s. It seems simpler - // to just require that people call - // `vec.pop()` or `vec.unshift()`. - let slice_bk = ty::BorrowKind::from_mutbl(slice_mutbl); - delegate.borrow(pat.id, pat.span, - slice_cmt, slice_r, - slice_bk, RefBinding); - } _ => {} } })); diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index 31e3db51f55..fc76c00aaf1 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -1061,43 +1061,6 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> { Ok(ret) } - /// Given a pattern P like: `[_, ..Q, _]`, where `vec_cmt` is the cmt for `P`, `slice_pat` is - /// the pattern `Q`, returns: - /// - /// * a cmt for `Q` - /// * the mutability and region of the slice `Q` - /// - /// These last two bits of info happen to be things that borrowck needs. - pub fn cat_slice_pattern(&self, - vec_cmt: cmt<'tcx>, - slice_pat: &hir::Pat) - -> McResult<(cmt<'tcx>, hir::Mutability, ty::Region)> { - let slice_ty = self.node_ty(slice_pat.id)?; - let (slice_mutbl, slice_r) = vec_slice_info(slice_pat, slice_ty); - let context = InteriorOffsetKind::Pattern; - let cmt_vec = self.deref_vec(slice_pat, vec_cmt, context)?; - let cmt_slice = self.cat_index(slice_pat, cmt_vec, context)?; - return Ok((cmt_slice, slice_mutbl, slice_r)); - - /// In a pattern like [a, b, ..c], normally `c` has slice type, but if you have [a, b, - /// ..ref c], then the type of `ref c` will be `&&[]`, so to extract the slice details we - /// have to recurse through rptrs. - fn vec_slice_info(pat: &hir::Pat, slice_ty: Ty) - -> (hir::Mutability, ty::Region) { - match slice_ty.sty { - ty::TyRef(r, ref mt) => match mt.ty.sty { - ty::TySlice(_) => (mt.mutbl, *r), - _ => vec_slice_info(pat, mt.ty), - }, - - _ => { - span_bug!(pat.span, - "type of slice pattern is not a slice"); - } - } - } - } - pub fn cat_imm_interior(&self, node: &N, base_cmt: cmt<'tcx>, @@ -1325,9 +1288,7 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> { self.cat_pattern_(elt_cmt.clone(), &before_pat, op)?; } if let Some(ref slice_pat) = *slice { - let slice_ty = self.pat_ty(&slice_pat)?; - let slice_cmt = self.cat_rvalue_node(pat.id(), pat.span(), slice_ty); - self.cat_pattern_(slice_cmt, &slice_pat, op)?; + self.cat_pattern_(elt_cmt.clone(), &slice_pat, op)?; } for after_pat in after { self.cat_pattern_(elt_cmt.clone(), &after_pat, op)?; diff --git a/src/librustc/mir/repr.rs b/src/librustc/mir/repr.rs index e6c22d0ea34..7aaaf38400e 100644 --- a/src/librustc/mir/repr.rs +++ b/src/librustc/mir/repr.rs @@ -674,6 +674,14 @@ pub enum ProjectionElem<'tcx, V> { from_end: bool, }, + /// These indices are generated by slice patterns. + /// + /// slice[from:-to] in Python terms. + Subslice { + from: u32, + to: u32, + }, + /// "Downcast" to a variant of an ADT. Currently, we only introduce /// this for ADTs with more than one variant. It may be better to /// just introduce it always, or always for enums. @@ -753,6 +761,14 @@ impl<'tcx> Debug for Lvalue<'tcx> { write!(fmt, "{:?}[{:?} of {:?}]", data.base, offset, min_length), ProjectionElem::ConstantIndex { offset, min_length, from_end: true } => write!(fmt, "{:?}[-{:?} of {:?}]", data.base, offset, min_length), + ProjectionElem::Subslice { from, to } if to == 0 => + write!(fmt, "{:?}[{:?}:", data.base, from), + ProjectionElem::Subslice { from, to } if from == 0 => + write!(fmt, "{:?}[:-{:?}]", data.base, to), + ProjectionElem::Subslice { from, to } => + write!(fmt, "{:?}[{:?}:-{:?}]", data.base, + from, to), + }, } } @@ -856,17 +872,6 @@ pub enum Rvalue<'tcx> { /// away after type-checking and before lowering. Aggregate(AggregateKind<'tcx>, Vec>), - /// Generates a slice of the form `&input[from_start..L-from_end]` - /// where `L` is the length of the slice. This is only created by - /// slice pattern matching, so e.g. a pattern of the form `[x, y, - /// .., z]` might create a slice with `from_start=2` and - /// `from_end=1`. - Slice { - input: Lvalue<'tcx>, - from_start: usize, - from_end: usize, - }, - InlineAsm { asm: InlineAsm, outputs: Vec>, @@ -972,8 +977,6 @@ impl<'tcx> Debug for Rvalue<'tcx> { InlineAsm { ref asm, ref outputs, ref inputs } => { write!(fmt, "asm!({:?} : {:?} : {:?})", asm, outputs, inputs) } - Slice { ref input, from_start, from_end } => - write!(fmt, "{:?}[{:?}..-{:?}]", input, from_start, from_end), Ref(_, borrow_kind, ref lv) => { let kind_str = match borrow_kind { diff --git a/src/librustc/mir/tcx.rs b/src/librustc/mir/tcx.rs index d0ac98a7958..270e33c48f1 100644 --- a/src/librustc/mir/tcx.rs +++ b/src/librustc/mir/tcx.rs @@ -59,6 +59,20 @@ impl<'a, 'gcx, 'tcx> LvalueTy<'tcx> { LvalueTy::Ty { ty: self.to_ty(tcx).builtin_index().unwrap() }, + ProjectionElem::Subslice { from, to } => { + let ty = self.to_ty(tcx); + LvalueTy::Ty { + ty: match ty.sty { + ty::TyArray(inner, size) => { + tcx.mk_array(inner, size-(from as usize)-(to as usize)) + } + ty::TySlice(..) => ty, + _ => { + bug!("cannot subslice non-array type: `{:?}`", self) + } + } + } + } ProjectionElem::Downcast(adt_def1, index) => match self.to_ty(tcx).sty { ty::TyEnum(adt_def, substs) => { @@ -219,7 +233,6 @@ impl<'a, 'gcx, 'tcx> Mir<'tcx> { } } } - Rvalue::Slice { .. } => None, Rvalue::InlineAsm { .. } => None } } diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs index dbe48d6b094..22b3caf6d0c 100644 --- a/src/librustc/mir/visit.rs +++ b/src/librustc/mir/visit.rs @@ -533,15 +533,6 @@ macro_rules! make_mir_visitor { } } - Rvalue::Slice { ref $($mutability)* input, - from_start, - from_end } => { - self.visit_lvalue(input, LvalueContext::Slice { - from_start: from_start, - from_end: from_end, - }); - } - Rvalue::InlineAsm { ref $($mutability)* outputs, ref $($mutability)* inputs, asm: _ } => { @@ -602,6 +593,8 @@ macro_rules! make_mir_visitor { match *proj { ProjectionElem::Deref => { } + ProjectionElem::Subslice { from: _, to: _ } => { + } ProjectionElem::Field(_field, ref $($mutability)* ty) => { self.visit_ty(ty); } diff --git a/src/librustc_borrowck/borrowck/mir/abs_domain.rs b/src/librustc_borrowck/borrowck/mir/abs_domain.rs index aa885eb4742..155b615d83c 100644 --- a/src/librustc_borrowck/borrowck/mir/abs_domain.rs +++ b/src/librustc_borrowck/borrowck/mir/abs_domain.rs @@ -49,6 +49,8 @@ impl<'tcx> Lift for LvalueElem<'tcx> { ProjectionElem::Field(f.clone(), ty.clone()), ProjectionElem::Index(ref i) => ProjectionElem::Index(i.lift()), + ProjectionElem::Subslice {from, to} => + ProjectionElem::Subslice { from: from, to: to }, ProjectionElem::ConstantIndex {offset,min_length,from_end} => ProjectionElem::ConstantIndex { offset: offset, diff --git a/src/librustc_borrowck/borrowck/mir/gather_moves.rs b/src/librustc_borrowck/borrowck/mir/gather_moves.rs index 27d208240ac..1acee8e64a9 100644 --- a/src/librustc_borrowck/borrowck/mir/gather_moves.rs +++ b/src/librustc_borrowck/borrowck/mir/gather_moves.rs @@ -620,22 +620,6 @@ fn gather_moves<'a, 'tcx>(mir: &Mir<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> MoveD Rvalue::Ref(..) | Rvalue::Len(..) | Rvalue::InlineAsm { .. } => {} - - Rvalue::Slice {..} => { - // A slice pattern `x..` binds `x` to a - // reference; thus no move occurs. - // - // FIXME: I recall arielb1 questioning - // whether this is even a legal thing to - // have as an R-value. The particular - // example where I am seeing this arise is - // `TargetDataLayout::parse(&Session)` in - // `rustc::ty::layout`. - // - // this should be removed soon. - debug!("encountered Rvalue::Slice as RHS of Assign, source: {:?}", - source); - } } } } diff --git a/src/librustc_const_eval/check_match.rs b/src/librustc_const_eval/check_match.rs index 49fa1896ff8..c3da2b9e53d 100644 --- a/src/librustc_const_eval/check_match.rs +++ b/src/librustc_const_eval/check_match.rs @@ -109,7 +109,7 @@ pub struct MatchCheckCtxt<'a, 'tcx: 'a> { pub param_env: ParameterEnvironment<'tcx>, } -#[derive(Clone, PartialEq)] +#[derive(Clone, Debug, PartialEq)] pub enum Constructor { /// The constructor of all patterns that don't vary by constructor, /// e.g. struct patterns and fixed-length arrays. @@ -584,31 +584,19 @@ fn construct_witness<'a,'tcx>(cx: &MatchCheckCtxt<'a,'tcx>, ctor: &Constructor, } } - ty::TyRef(_, ty::TypeAndMut { ty, mutbl }) => { - match ty.sty { - ty::TyArray(_, n) => match ctor { - &Single => { - assert_eq!(pats_len, n); - PatKind::Vec(pats.collect(), None, hir::HirVec::new()) - }, - _ => bug!() - }, - ty::TySlice(_) => match ctor { - &Slice(n) => { - assert_eq!(pats_len, n); - PatKind::Vec(pats.collect(), None, hir::HirVec::new()) - }, - _ => bug!() - }, - ty::TyStr => PatKind::Wild, - - _ => { - assert_eq!(pats_len, 1); - PatKind::Ref(pats.nth(0).unwrap(), mutbl) - } - } + ty::TyRef(_, ty::TypeAndMut { mutbl, .. }) => { + assert_eq!(pats_len, 1); + PatKind::Ref(pats.nth(0).unwrap(), mutbl) } + ty::TySlice(_) => match ctor { + &Slice(n) => { + assert_eq!(pats_len, n); + PatKind::Vec(pats.collect(), None, hir::HirVec::new()) + }, + _ => unreachable!() + }, + ty::TyArray(_, len) => { assert_eq!(pats_len, len); PatKind::Vec(pats.collect(), None, hir::HirVec::new()) @@ -660,13 +648,8 @@ fn all_constructors(_cx: &MatchCheckCtxt, left_ty: Ty, match left_ty.sty { ty::TyBool => [true, false].iter().map(|b| ConstantValue(ConstVal::Bool(*b))).collect(), - - ty::TyRef(_, ty::TypeAndMut { ty, .. }) => match ty.sty { - ty::TySlice(_) => - (0..max_slice_length+1).map(|length| Slice(length)).collect(), - _ => vec![Single] - }, - + ty::TySlice(_) => + (0..max_slice_length+1).map(|length| Slice(length)).collect(), ty::TyEnum(def, _) => def.variants.iter().map(|v| Variant(v.did)).collect(), _ => vec![Single] } @@ -818,13 +801,14 @@ fn pat_constructors(cx: &MatchCheckCtxt, p: &Pat, PatKind::Vec(ref before, ref slice, ref after) => match left_ty.sty { ty::TyArray(_, _) => vec![Single], - _ => if slice.is_some() { + ty::TySlice(_) if slice.is_some() => { (before.len() + after.len()..max_slice_length+1) .map(|length| Slice(length)) .collect() - } else { - vec![Slice(before.len() + after.len())] } + ty::TySlice(_) => vec!(Slice(before.len() + after.len())), + _ => span_bug!(pat.span, "pat_constructors: unexpected \ + slice pattern type {:?}", left_ty) }, PatKind::Box(..) | PatKind::Tuple(..) | PatKind::Ref(..) => vec![Single], @@ -839,18 +823,16 @@ fn pat_constructors(cx: &MatchCheckCtxt, p: &Pat, /// For instance, a tuple pattern (_, 42, Some([])) has the arity of 3. /// A struct pattern's arity is the number of fields it contains, etc. pub fn constructor_arity(_cx: &MatchCheckCtxt, ctor: &Constructor, ty: Ty) -> usize { + debug!("constructor_arity({:?}, {:?})", ctor, ty); match ty.sty { ty::TyTuple(ref fs) => fs.len(), ty::TyBox(_) => 1, - ty::TyRef(_, ty::TypeAndMut { ty, .. }) => match ty.sty { - ty::TySlice(_) => match *ctor { - Slice(length) => length, - ConstantValue(_) => 0, - _ => bug!() - }, - ty::TyStr => 0, - _ => 1 + ty::TySlice(_) => match *ctor { + Slice(length) => length, + ConstantValue(_) => 0, + _ => bug!() }, + ty::TyRef(..) => 1, ty::TyEnum(adt, _) | ty::TyStruct(adt, _) => { ctor.variant_for_adt(adt).fields.len() } @@ -988,29 +970,34 @@ pub fn specialize<'a>(cx: &MatchCheckCtxt, r: &[&'a Pat], } PatKind::Vec(ref before, ref slice, ref after) => { + let pat_len = before.len() + after.len(); match *constructor { - // Fixed-length vectors. Single => { - let mut pats: Vec<&Pat> = before.iter().map(|p| &**p).collect(); - pats.extend(repeat(DUMMY_WILD_PAT).take(arity - before.len() - after.len())); - pats.extend(after.iter().map(|p| &**p)); - Some(pats) - }, - Slice(length) if before.len() + after.len() <= length && slice.is_some() => { - let mut pats: Vec<&Pat> = before.iter().map(|p| &**p).collect(); - pats.extend(repeat(DUMMY_WILD_PAT).take(arity - before.len() - after.len())); - pats.extend(after.iter().map(|p| &**p)); - Some(pats) - }, - Slice(length) if before.len() + after.len() == length => { - let mut pats: Vec<&Pat> = before.iter().map(|p| &**p).collect(); - pats.extend(after.iter().map(|p| &**p)); - Some(pats) + // Fixed-length vectors. + Some( + before.iter().map(|p| &**p).chain( + repeat(DUMMY_WILD_PAT).take(arity - pat_len).chain( + after.iter().map(|p| &**p) + )).collect()) }, + Slice(length) if pat_len <= length && slice.is_some() => { + Some( + before.iter().map(|p| &**p).chain( + repeat(DUMMY_WILD_PAT).take(arity - pat_len).chain( + after.iter().map(|p| &**p) + )).collect()) + } + Slice(length) if pat_len == length => { + Some( + before.iter().map(|p| &**p).chain( + after.iter().map(|p| &**p) + ).collect()) + } SliceWithSubslice(prefix, suffix) if before.len() == prefix && after.len() == suffix && slice.is_some() => { + // this is used by trans::_match only let mut pats: Vec<&Pat> = before.iter().map(|p| &**p).collect(); pats.extend(after.iter().map(|p| &**p)); Some(pats) diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs index 6104679d650..ffb13356ba2 100644 --- a/src/librustc_mir/build/matches/mod.rs +++ b/src/librustc_mir/build/matches/mod.rs @@ -429,42 +429,83 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { /// simpler (and, in fact, irrefutable). /// /// But there may also be candidates that the test just doesn't - /// apply to. For example, consider the case of #29740: + /// apply to. The classical example involves wildcards: /// /// ```rust,ignore - /// match x { - /// "foo" => ..., - /// "bar" => ..., - /// "baz" => ..., - /// _ => ..., + /// match (x, y) { + /// (true, _) => true, // (0) + /// (_, true) => true, // (1) + /// (false, false) => false // (2) /// } /// ``` /// - /// Here the match-pair we are testing will be `x @ "foo"`, and we - /// will generate an `Eq` test. Because `"bar"` and `"baz"` are different - /// constants, we will decide that these later candidates are just not - /// informed by the eq test. So we'll wind up with three candidate sets: + /// In that case, after we test on `x`, there are 2 overlapping candidate + /// sets: /// - /// - If outcome is that `x == "foo"` (one candidate, derived from `x @ "foo"`) - /// - If outcome is that `x != "foo"` (empty list of candidates) - /// - Otherwise (three candidates, `x @ "bar"`, `x @ "baz"`, `x @ - /// _`). Here we have the invariant that everything in the - /// otherwise list is of **lower priority** than the stuff in the - /// other lists. + /// - If the outcome is that `x` is true, candidates 0 and 2 + /// - If the outcome is that `x` is false, candidates 1 and 2 /// - /// So we'll compile the test. For each outcome of the test, we - /// recursively call `match_candidates` with the corresponding set - /// of candidates. But note that this set is now inexhaustive: for - /// example, in the case where the test returns false, there are - /// NO candidates, even though there is stll a value to be - /// matched. So we'll collect the return values from - /// `match_candidates`, which are the blocks where control-flow - /// goes if none of the candidates matched. At this point, we can - /// continue with the "otherwise" list. + /// Here, the traditional "decision tree" method would generate 2 + /// separate code-paths for the 2 separate cases. + /// + /// In some cases, this duplication can create an exponential amount of + /// code. This is most easily seen by noticing that this method terminates + /// with precisely the reachable arms being reachable - but that problem + /// is trivially NP-complete: + /// + /// ```rust + /// match (var0, var1, var2, var3, ..) { + /// (true, _, _, false, true, ...) => false, + /// (_, true, true, false, _, ...) => false, + /// (false, _, false, false, _, ...) => false, + /// ... + /// _ => true + /// } + /// ``` + /// + /// Here the last arm is reachable only if there is an assignment to + /// the variables that does not match any of the literals. Therefore, + /// compilation would take an exponential amount of time in some cases. + /// + /// That kind of exponential worst-case might not occur in practice, but + /// our simplistic treatment of constants and guards would make it occur + /// in very common situations - for example #29740: + /// + /// ```rust + /// match x { + /// "foo" if foo_guard => ..., + /// "bar" if bar_guard => ..., + /// "baz" if baz_guard => ..., + /// ... + /// } + /// ``` + /// + /// Here we first test the match-pair `x @ "foo"`, which is an `Eq` test. + /// It might seem that we would end up with 2 disjoint candidate sets, + /// consisting of the first candidate or the other 3, but our algorithm + /// doesn't reason about "foo" being distinct from the other constants, + /// it considers to latter arms to potentially match after both outcomes, + /// which obviously leads to an exponential amount of tests. + /// To avoid these kinds of problems, our algorithm tries to ensure + /// the amount of generated tests is linear. When we do a k-way test, + /// we return an additional "unmatched" set alongside the obvious `k` + /// sets. When we encounter a candidate that would be present in more + /// than one of the sets, we put it and all candidates below it into the + /// "unmatched" set. This ensures these `k+1` sets are disjoint. + /// + /// After we perform our test, we branch into the appropriate candidate + /// set and recurse with `match_candidates`. These sub-matches are + /// obviously inexhaustive - as we discarded our otherwise set - so + /// we set their continuation to do `match_candidates` on the + /// "unmatched" set (which is again inexhaustive). /// /// If you apply this to the above test, you basically wind up /// with an if-else-if chain, testing each candidate in turn, /// which is precisely what we want. + /// + /// In addition to avoiding exponential-time blowups, this algorithm + /// also has nice property that each guard and arm is only generated + /// once. fn test_candidates<'pat>(&mut self, span: Span, arm_blocks: &mut ArmBlocks, diff --git a/src/librustc_mir/build/matches/simplify.rs b/src/librustc_mir/build/matches/simplify.rs index c707bb8a27b..8392248e3f2 100644 --- a/src/librustc_mir/build/matches/simplify.rs +++ b/src/librustc_mir/build/matches/simplify.rs @@ -31,7 +31,7 @@ use std::mem; impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { pub fn simplify_candidate<'pat>(&mut self, - mut block: BasicBlock, + block: BasicBlock, candidate: &mut Candidate<'pat, 'tcx>) -> BlockAnd<()> { // repeatedly simplify match pairs until fixed point is reached @@ -39,10 +39,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { let match_pairs = mem::replace(&mut candidate.match_pairs, vec![]); let mut progress = match_pairs.len(); // count how many were simplified for match_pair in match_pairs { - match self.simplify_match_pair(block, match_pair, candidate) { - Ok(b) => { - block = b; - } + match self.simplify_match_pair(match_pair, candidate) { + Ok(()) => {} Err(match_pair) => { candidate.match_pairs.push(match_pair); progress -= 1; // this one was not simplified @@ -61,14 +59,13 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { /// possible, Err is returned and no changes are made to /// candidate. fn simplify_match_pair<'pat>(&mut self, - mut block: BasicBlock, match_pair: MatchPair<'pat, 'tcx>, candidate: &mut Candidate<'pat, 'tcx>) - -> Result> { + -> Result<(), MatchPair<'pat, 'tcx>> { match *match_pair.pattern.kind { PatternKind::Wild => { // nothing left to do - Ok(block) + Ok(()) } PatternKind::Binding { name, mutability, mode, var, ty, ref subpattern } => { @@ -87,7 +84,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { candidate.match_pairs.push(MatchPair::new(match_pair.lvalue, subpattern)); } - Ok(block) + Ok(()) } PatternKind::Constant { .. } => { @@ -96,37 +93,31 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { } PatternKind::Range { .. } | - PatternKind::Variant { .. } => { - // cannot simplify, test is required + PatternKind::Variant { .. } | + PatternKind::Slice { .. } => { Err(match_pair) } - PatternKind::Slice { .. } if !match_pair.slice_len_checked => { - Err(match_pair) - } - - PatternKind::Array { ref prefix, ref slice, ref suffix } | - PatternKind::Slice { ref prefix, ref slice, ref suffix } => { - unpack!(block = self.prefix_suffix_slice(&mut candidate.match_pairs, - block, - match_pair.lvalue.clone(), - prefix, - slice.as_ref(), - suffix)); - Ok(block) + PatternKind::Array { ref prefix, ref slice, ref suffix } => { + self.prefix_slice_suffix(&mut candidate.match_pairs, + &match_pair.lvalue, + prefix, + slice.as_ref(), + suffix); + Ok(()) } PatternKind::Leaf { ref subpatterns } => { // tuple struct, match subpats (if any) candidate.match_pairs .extend(self.field_match_pairs(match_pair.lvalue, subpatterns)); - Ok(block) + Ok(()) } PatternKind::Deref { ref subpattern } => { let lvalue = match_pair.lvalue.deref(); candidate.match_pairs.push(MatchPair::new(lvalue, subpattern)); - Ok(block) + Ok(()) } } } diff --git a/src/librustc_mir/build/matches/test.rs b/src/librustc_mir/build/matches/test.rs index 1f0c4bd9e0d..668bdcf7358 100644 --- a/src/librustc_mir/build/matches/test.rs +++ b/src/librustc_mir/build/matches/test.rs @@ -24,6 +24,7 @@ use rustc::middle::const_val::ConstVal; use rustc::ty::{self, Ty}; use rustc::mir::repr::*; use syntax::codemap::Span; +use std::cmp::Ordering; impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { /// Identifies what test is needed to decide if `match_pair` is applicable. @@ -446,69 +447,118 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { } }; - match test.kind { + match (&test.kind, &*match_pair.pattern.kind) { // If we are performing a variant switch, then this // informs variant patterns, but nothing else. - TestKind::Switch { adt_def: tested_adt_def , .. } => { - match *match_pair.pattern.kind { - PatternKind::Variant { adt_def, variant_index, ref subpatterns } => { - assert_eq!(adt_def, tested_adt_def); - let new_candidate = - self.candidate_after_variant_switch(match_pair_index, - adt_def, - variant_index, - subpatterns, - candidate); - resulting_candidates[variant_index].push(new_candidate); - true - } - _ => { - false - } - } + (&TestKind::Switch { adt_def: tested_adt_def, .. }, + &PatternKind::Variant { adt_def, variant_index, ref subpatterns }) => { + assert_eq!(adt_def, tested_adt_def); + let new_candidate = + self.candidate_after_variant_switch(match_pair_index, + adt_def, + variant_index, + subpatterns, + candidate); + resulting_candidates[variant_index].push(new_candidate); + true } + (&TestKind::Switch { .. }, _) => false, // If we are performing a switch over integers, then this informs integer // equality, but nothing else. // - // FIXME(#29623) we could use TestKind::Range to rule + // FIXME(#29623) we could use PatternKind::Range to rule // things out here, in some cases. - TestKind::SwitchInt { switch_ty: _, options: _, ref indices } => { - match *match_pair.pattern.kind { - PatternKind::Constant { ref value } - if is_switch_ty(match_pair.pattern.ty) => { - let index = indices[value]; - let new_candidate = self.candidate_without_match_pair(match_pair_index, - candidate); - resulting_candidates[index].push(new_candidate); + (&TestKind::SwitchInt { switch_ty: _, options: _, ref indices }, + &PatternKind::Constant { ref value }) + if is_switch_ty(match_pair.pattern.ty) => { + let index = indices[value]; + let new_candidate = self.candidate_without_match_pair(match_pair_index, + candidate); + resulting_candidates[index].push(new_candidate); + true + } + (&TestKind::SwitchInt { .. }, _) => false, + + + (&TestKind::Len { len: test_len, op: BinOp::Eq }, + &PatternKind::Slice { ref prefix, ref slice, ref suffix }) => { + let pat_len = (prefix.len() + suffix.len()) as u64; + match (test_len.cmp(&pat_len), slice) { + (Ordering::Equal, &None) => { + // on true, min_len = len = $actual_length, + // on false, len != $actual_length + resulting_candidates[0].push( + self.candidate_after_slice_test(match_pair_index, + candidate, + prefix, + slice.as_ref(), + suffix) + ); true } - _ => { + (Ordering::Less, _) => { + // test_len < pat_len. If $actual_len = test_len, + // then $actual_len < pat_len and we don't have + // enough elements. + resulting_candidates[1].push(candidate.clone()); + true + } + (Ordering::Equal, &Some(_)) | (Ordering::Greater, &Some(_)) => { + // This can match both if $actual_len = test_len >= pat_len, + // and if $actual_len > test_len. We can't advance. + false + } + (Ordering::Greater, &None) => { + // test_len != pat_len, so if $actual_len = test_len, then + // $actual_len != pat_len. + resulting_candidates[1].push(candidate.clone()); + true + } + } + } + + (&TestKind::Len { len: test_len, op: BinOp::Ge }, + &PatternKind::Slice { ref prefix, ref slice, ref suffix }) => { + // the test is `$actual_len >= test_len` + let pat_len = (prefix.len() + suffix.len()) as u64; + match (test_len.cmp(&pat_len), slice) { + (Ordering::Equal, &Some(_)) => { + // $actual_len >= test_len = pat_len, + // so we can match. + resulting_candidates[0].push( + self.candidate_after_slice_test(match_pair_index, + candidate, + prefix, + slice.as_ref(), + suffix) + ); + true + } + (Ordering::Less, _) | (Ordering::Equal, &None) => { + // test_len <= pat_len. If $actual_len < test_len, + // then it is also < pat_len, so the test passing is + // necessary (but insufficient). + resulting_candidates[0].push(candidate.clone()); + true + } + (Ordering::Greater, &None) => { + // test_len > pat_len. If $actual_len >= test_len > pat_len, + // then we know we won't have a match. + resulting_candidates[1].push(candidate.clone()); + true + } + (Ordering::Greater, &Some(_)) => { + // test_len < pat_len, and is therefore less + // strict. This can still go both ways. false } } } - // If we are performing a length check, then this - // informs slice patterns, but nothing else. - TestKind::Len { .. } => { - let pattern_test = self.test(&match_pair); - match *match_pair.pattern.kind { - PatternKind::Slice { .. } if pattern_test.kind == test.kind => { - let mut new_candidate = candidate.clone(); - - // Set up the MatchKind to simplify this like an array. - new_candidate.match_pairs[match_pair_index] - .slice_len_checked = true; - resulting_candidates[0].push(new_candidate); - true - } - _ => false - } - } - - TestKind::Eq { .. } | - TestKind::Range { .. } => { + (&TestKind::Eq { .. }, _) | + (&TestKind::Range { .. }, _) | + (&TestKind::Len { .. }, _) => { // These are all binary tests. // // FIXME(#29623) we can be more clever here @@ -544,6 +594,25 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { } } + fn candidate_after_slice_test<'pat>(&mut self, + match_pair_index: usize, + candidate: &Candidate<'pat, 'tcx>, + prefix: &'pat [Pattern<'tcx>], + opt_slice: Option<&'pat Pattern<'tcx>>, + suffix: &'pat [Pattern<'tcx>]) + -> Candidate<'pat, 'tcx> { + let mut new_candidate = + self.candidate_without_match_pair(match_pair_index, candidate); + self.prefix_slice_suffix( + &mut new_candidate.match_pairs, + &candidate.match_pairs[match_pair_index].lvalue, + prefix, + opt_slice, + suffix); + + new_candidate + } + fn candidate_after_variant_switch<'pat>(&mut self, match_pair_index: usize, adt_def: ty::AdtDef<'tcx>, diff --git a/src/librustc_mir/build/matches/util.rs b/src/librustc_mir/build/matches/util.rs index 2e9cc96c046..53ebf6fceb5 100644 --- a/src/librustc_mir/build/matches/util.rs +++ b/src/librustc_mir/build/matches/util.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use build::{BlockAnd, BlockAndExtension, Builder}; +use build::Builder; use build::matches::MatchPair; use hair::*; use rustc::mir::repr::*; @@ -28,64 +28,17 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { .collect() } - /// When processing an array/slice pattern like `lv @ [x, y, ..s, z]`, - /// this function converts the prefix (`x`, `y`) and suffix (`z`) into - /// distinct match pairs: - /// - /// ```rust,ignore - /// lv[0 of 3] @ x // see ProjectionElem::ConstantIndex (and its Debug impl) - /// lv[1 of 3] @ y // to explain the `[x of y]` notation - /// lv[-1 of 3] @ z - /// ``` - /// - /// If a slice like `s` is present, then the function also creates - /// a temporary like: - /// - /// ```rust,ignore - /// tmp0 = lv[2..-1] // using the special Rvalue::Slice - /// ``` - /// - /// and creates a match pair `tmp0 @ s` - pub fn prefix_suffix_slice<'pat>(&mut self, + pub fn prefix_slice_suffix<'pat>(&mut self, match_pairs: &mut Vec>, - block: BasicBlock, - lvalue: Lvalue<'tcx>, + lvalue: &Lvalue<'tcx>, prefix: &'pat [Pattern<'tcx>], opt_slice: Option<&'pat Pattern<'tcx>>, - suffix: &'pat [Pattern<'tcx>]) - -> BlockAnd<()> { - // If there is a `..P` pattern, create a temporary `t0` for - // the slice and then a match pair `t0 @ P`: - if let Some(slice) = opt_slice { - let prefix_len = prefix.len(); - let suffix_len = suffix.len(); - let rvalue = Rvalue::Slice { - input: lvalue.clone(), - from_start: prefix_len, - from_end: suffix_len, - }; - let temp = self.temp(slice.ty.clone()); // no need to schedule drop, temp is always copy - let source_info = self.source_info(slice.span); - self.cfg.push_assign(block, source_info, &temp, rvalue); - match_pairs.push(MatchPair::new(temp, slice)); - } - - self.prefix_suffix(match_pairs, lvalue, prefix, suffix); - - block.unit() - } - - /// Helper for `prefix_suffix_slice` which just processes the prefix and suffix. - fn prefix_suffix<'pat>(&mut self, - match_pairs: &mut Vec>, - lvalue: Lvalue<'tcx>, - prefix: &'pat [Pattern<'tcx>], - suffix: &'pat [Pattern<'tcx>]) { + suffix: &'pat [Pattern<'tcx>]) { let min_length = prefix.len() + suffix.len(); assert!(min_length < u32::MAX as usize); let min_length = min_length as u32; - let prefix_pairs: Vec<_> = + match_pairs.extend( prefix.iter() .enumerate() .map(|(idx, subpattern)| { @@ -97,9 +50,17 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { let lvalue = lvalue.clone().elem(elem); MatchPair::new(lvalue, subpattern) }) - .collect(); + ); - let suffix_pairs: Vec<_> = + if let Some(subslice_pat) = opt_slice { + let subslice = lvalue.clone().elem(ProjectionElem::Subslice { + from: prefix.len() as u32, + to: suffix.len() as u32 + }); + match_pairs.push(MatchPair::new(subslice, subslice_pat)); + } + + match_pairs.extend( suffix.iter() .rev() .enumerate() @@ -112,9 +73,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { let lvalue = lvalue.clone().elem(elem); MatchPair::new(lvalue, subpattern) }) - .collect(); - - match_pairs.extend(prefix_pairs.into_iter().chain(suffix_pairs)); + ); } } diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs index bce096deea1..3fdf492611d 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -509,6 +509,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { } ProjectionElem::ConstantIndex {..} | + ProjectionElem::Subslice {..} | ProjectionElem::Downcast(..) => { this.not_const() } @@ -708,7 +709,6 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { } } - Rvalue::Slice {..} | Rvalue::InlineAsm {..} => { self.not_const(); } diff --git a/src/librustc_mir/transform/type_check.rs b/src/librustc_mir/transform/type_check.rs index 4bfa3b16efa..c38f1f1e6c0 100644 --- a/src/librustc_mir/transform/type_check.rs +++ b/src/librustc_mir/transform/type_check.rs @@ -203,6 +203,26 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { }) } } + ProjectionElem::Subslice { from, to } => { + LvalueTy::Ty { + ty: match base_ty.sty { + ty::TyArray(inner, size) => { + let min_size = (from as usize) + (to as usize); + if let Some(rest_size) = size.checked_sub(min_size) { + tcx.mk_array(inner, rest_size) + } else { + span_mirbug_and_err!( + self, lvalue, "taking too-small slice of {:?}", base_ty) + } + } + ty::TySlice(..) => base_ty, + _ => { + span_mirbug_and_err!( + self, lvalue, "slice of non-array {:?}", base_ty) + } + } + } + } ProjectionElem::Downcast(adt_def1, index) => match base_ty.sty { ty::TyEnum(adt_def, substs) if adt_def == adt_def1 => { diff --git a/src/librustc_trans/mir/lvalue.rs b/src/librustc_trans/mir/lvalue.rs index bc79482666c..841dcf0301c 100644 --- a/src/librustc_trans/mir/lvalue.rs +++ b/src/librustc_trans/mir/lvalue.rs @@ -20,13 +20,14 @@ use common::{self, BlockAndBuilder, CrateContext, C_uint, C_undef}; use consts; use machine; use type_of::type_of; +use type_of; use Disr; use std::ptr; use super::{MirContext, TempRef}; -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Debug)] pub struct LvalueRef<'tcx> { /// Pointer to the contents of the lvalue pub llval: ValueRef, @@ -88,7 +89,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { let fcx = bcx.fcx(); let ccx = bcx.ccx(); let tcx = bcx.tcx(); - match *lvalue { + let result = match *lvalue { mir::Lvalue::Var(index) => self.vars[index as usize], mir::Lvalue::Temp(index) => match self.temps[index as usize] { TempRef::Lvalue(lvalue) => @@ -131,7 +132,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { let zero = common::C_uint(bcx.ccx(), 0u64); bcx.inbounds_gep(tr_base.llval, &[zero, llindex]) }; - (element, ptr::null_mut()) + element }; let (llprojected, llextra) = match projection.elem { @@ -169,13 +170,13 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } mir::ProjectionElem::Index(ref index) => { let index = self.trans_operand(bcx, index); - project_index(self.prepare_index(bcx, index.immediate())) + (project_index(self.prepare_index(bcx, index.immediate())), ptr::null_mut()) } mir::ProjectionElem::ConstantIndex { offset, from_end: false, min_length: _ } => { let lloffset = C_uint(bcx.ccx(), offset); - project_index(self.prepare_index(bcx, lloffset)) + (project_index(lloffset), ptr::null_mut()) } mir::ProjectionElem::ConstantIndex { offset, from_end: true, @@ -183,7 +184,30 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { let lloffset = C_uint(bcx.ccx(), offset); let lllen = tr_base.len(bcx.ccx()); let llindex = bcx.sub(lllen, lloffset); - project_index(self.prepare_index(bcx, llindex)) + (project_index(llindex), ptr::null_mut()) + } + mir::ProjectionElem::Subslice { from, to } => { + let llindex = C_uint(bcx.ccx(), from); + let llbase = project_index(llindex); + + let base_ty = tr_base.ty.to_ty(bcx.tcx()); + match base_ty.sty { + ty::TyArray(..) => { + // must cast the lvalue pointer type to the new + // array type (*[%_; new_len]). + let base_ty = self.mir.lvalue_ty(tcx, lvalue).to_ty(tcx); + let llbasety = type_of::type_of(bcx.ccx(), base_ty).ptr_to(); + let llbase = bcx.pointercast(llbase, llbasety); + (bcx.pointercast(llbase, llbasety), ptr::null_mut()) + } + ty::TySlice(..) => { + assert!(tr_base.llextra != ptr::null_mut()); + let lllen = bcx.sub(tr_base.llextra, + C_uint(bcx.ccx(), from+to)); + (llbase, lllen) + } + _ => bug!("unexpected type {:?} in Subslice", base_ty) + } } mir::ProjectionElem::Downcast(..) => { (tr_base.llval, tr_base.llextra) @@ -195,7 +219,9 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { ty: projected_ty, } } - } + }; + debug!("trans_lvalue(lvalue={:?}) => {:?}", lvalue, result); + result } // Perform an action using the given Lvalue. diff --git a/src/librustc_trans/mir/rvalue.rs b/src/librustc_trans/mir/rvalue.rs index 6779498fe2c..09b07c1440e 100644 --- a/src/librustc_trans/mir/rvalue.rs +++ b/src/librustc_trans/mir/rvalue.rs @@ -29,7 +29,7 @@ use Disr; use super::MirContext; use super::constant::const_scalar_checked_binop; use super::operand::{OperandRef, OperandValue}; -use super::lvalue::{LvalueRef, get_dataptr, get_meta}; +use super::lvalue::{LvalueRef, get_dataptr}; impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { pub fn trans_rvalue(&mut self, @@ -170,26 +170,6 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { bcx } - mir::Rvalue::Slice { ref input, from_start, from_end } => { - let ccx = bcx.ccx(); - let input = self.trans_lvalue(&bcx, input); - let ty = input.ty.to_ty(bcx.tcx()); - let (llbase1, lllen) = match ty.sty { - ty::TyArray(_, n) => { - (bcx.gepi(input.llval, &[0, from_start]), C_uint(ccx, n)) - } - ty::TySlice(_) | ty::TyStr => { - (bcx.gepi(input.llval, &[from_start]), input.llextra) - } - _ => bug!("cannot slice {}", ty) - }; - let adj = C_uint(ccx, from_start + from_end); - let lllen1 = bcx.sub(lllen, adj); - bcx.store(llbase1, get_dataptr(&bcx, dest.llval)); - bcx.store(lllen1, get_meta(&bcx, dest.llval)); - bcx - } - mir::Rvalue::InlineAsm { ref asm, ref outputs, ref inputs } => { let outputs = outputs.iter().map(|output| { let lvalue = self.trans_lvalue(&bcx, output); @@ -498,7 +478,6 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } mir::Rvalue::Repeat(..) | mir::Rvalue::Aggregate(..) | - mir::Rvalue::Slice { .. } | mir::Rvalue::InlineAsm { .. } => { bug!("cannot generate operand from rvalue {:?}", rvalue); @@ -652,7 +631,6 @@ pub fn rvalue_creates_operand<'bcx, 'tcx>(_mir: &mir::Mir<'tcx>, true, mir::Rvalue::Repeat(..) | mir::Rvalue::Aggregate(..) | - mir::Rvalue::Slice { .. } | mir::Rvalue::InlineAsm { .. } => false, } diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs index 99b443e2924..2fbeb43b8a3 100644 --- a/src/librustc_typeck/check/_match.rs +++ b/src/librustc_typeck/check/_match.rs @@ -19,8 +19,8 @@ use lint; use util::nodemap::FnvHashMap; use session::Session; -use std::cmp; use std::collections::hash_map::Entry::{Occupied, Vacant}; +use std::cmp; use std::ops::Deref; use syntax::ast; use syntax::codemap::{Span, Spanned}; @@ -323,44 +323,42 @@ impl<'a, 'gcx, 'tcx> PatCtxt<'a, 'gcx, 'tcx> { } PatKind::Vec(ref before, ref slice, ref after) => { let expected_ty = self.structurally_resolved_type(pat.span, expected); - let inner_ty = self.next_ty_var(); - let pat_ty = match expected_ty.sty { - ty::TyArray(_, size) => tcx.mk_array(inner_ty, { + let (inner_ty, slice_ty) = match expected_ty.sty { + ty::TyArray(inner_ty, size) => { let min_len = before.len() + after.len(); - match *slice { - Some(_) => cmp::max(min_len, size), - None => min_len + if slice.is_none() { + if min_len != size { + span_err!(tcx.sess, pat.span, E0527, + "pattern requires {} elements but array has {}", + min_len, size); + } + (inner_ty, tcx.types.err) + } else if let Some(rest) = size.checked_sub(min_len) { + (inner_ty, tcx.mk_array(inner_ty, rest)) + } else { + span_err!(tcx.sess, pat.span, E0528, + "pattern requires at least {} elements but array has {}", + min_len, size); + (inner_ty, tcx.types.err) } - }), + } + ty::TySlice(inner_ty) => (inner_ty, expected_ty), _ => { - let region = self.next_region_var(infer::PatternRegion(pat.span)); - tcx.mk_ref(tcx.mk_region(region), ty::TypeAndMut { - ty: tcx.mk_slice(inner_ty), - mutbl: expected_ty.builtin_deref(true, ty::NoPreference) - .map_or(hir::MutImmutable, |mt| mt.mutbl) - }) + if !expected_ty.references_error() { + span_err!(tcx.sess, pat.span, E0529, + "expected an array or slice, found `{}`", + expected_ty); + } + (tcx.types.err, tcx.types.err) } }; - self.write_ty(pat.id, pat_ty); - - // `demand::subtype` would be good enough, but using - // `eqtype` turns out to be equally general. See (*) - // below for details. - self.demand_eqtype(pat.span, expected, pat_ty); + self.write_ty(pat.id, expected_ty); for elt in before { self.check_pat(&elt, inner_ty); } if let Some(ref slice) = *slice { - let region = self.next_region_var(infer::PatternRegion(pat.span)); - let mutbl = expected_ty.builtin_deref(true, ty::NoPreference) - .map_or(hir::MutImmutable, |mt| mt.mutbl); - - let slice_ty = tcx.mk_ref(tcx.mk_region(region), ty::TypeAndMut { - ty: tcx.mk_slice(inner_ty), - mutbl: mutbl - }); self.check_pat(&slice, slice_ty); } for elt in after { @@ -369,7 +367,6 @@ impl<'a, 'gcx, 'tcx> PatCtxt<'a, 'gcx, 'tcx> { } } - // (*) In most of the cases above (literals and constants being // the exception), we relate types using strict equality, evewn // though subtyping would be sufficient. There are a few reasons diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs index fd1b6055173..a8cb21126c0 100644 --- a/src/librustc_typeck/check/regionck.rs +++ b/src/librustc_typeck/check/regionck.rs @@ -1157,25 +1157,13 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { debug!("link_pattern(discr_cmt={:?}, root_pat={:?})", discr_cmt, root_pat); - let _ = mc.cat_pattern(discr_cmt, root_pat, |mc, sub_cmt, sub_pat| { + let _ = mc.cat_pattern(discr_cmt, root_pat, |_, sub_cmt, sub_pat| { match sub_pat.node { // `ref x` pattern PatKind::Binding(hir::BindByRef(mutbl), _, _) => { self.link_region_from_node_type(sub_pat.span, sub_pat.id, mutbl, sub_cmt); } - - // `[_, ..slice, _]` pattern - PatKind::Vec(_, Some(ref slice_pat), _) => { - match mc.cat_slice_pattern(sub_cmt, &slice_pat) { - Ok((slice_cmt, slice_mutbl, slice_r)) => { - self.link_region(sub_pat.span, &slice_r, - ty::BorrowKind::from_mutbl(slice_mutbl), - slice_cmt); - } - Err(()) => {} - } - } _ => {} } }); diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs index 67a082ee520..86717da24c5 100644 --- a/src/librustc_typeck/diagnostics.rs +++ b/src/librustc_typeck/diagnostics.rs @@ -4139,5 +4139,8 @@ register_diagnostics! { // type `{}` was overridden E0436, // functional record update requires a struct E0513, // no type for local variable .. - E0521 // redundant default implementations of trait + E0521, // redundant default implementations of trait + E0527, // expected {} elements, found {} + E0528, // expected at least {} elements, found {} + E0529, // slice pattern expects array or slice, not `{}` } diff --git a/src/libstd/sys/common/wtf8.rs b/src/libstd/sys/common/wtf8.rs index 55e485e5811..5519230890c 100644 --- a/src/libstd/sys/common/wtf8.rs +++ b/src/libstd/sys/common/wtf8.rs @@ -560,15 +560,20 @@ impl Wtf8 { } } + // FIXME(stage0): use slice patterns after snapshot #[inline] fn final_lead_surrogate(&self) -> Option { let len = self.len(); if len < 3 { return None } - match &self.bytes[(len - 3)..] { - [0xED, b2 @ 0xA0...0xAF, b3] => Some(decode_surrogate(b2, b3)), - _ => None + if self.bytes[len-3] == 0xed && + self.bytes[len-2] > 0xa0 && + self.bytes[len-2] <= 0xaf + { + Some(decode_surrogate(self.bytes[len-2], self.bytes[len-1])) + } else { + None } } @@ -578,9 +583,13 @@ impl Wtf8 { if len < 3 { return None } - match &self.bytes[..3] { - [0xED, b2 @ 0xB0...0xBF, b3] => Some(decode_surrogate(b2, b3)), - _ => None + if self.bytes[len-3] == 0xed && + self.bytes[len-2] > 0xb0 && + self.bytes[len-2] <= 0xbf + { + Some(decode_surrogate(self.bytes[len-2], self.bytes[len-1])) + } else { + None } } } diff --git a/src/test/compile-fail/borrowck/borrowck-move-out-of-vec-tail.rs b/src/test/compile-fail/borrowck/borrowck-move-out-of-vec-tail.rs index 15771295743..f595d9d81cc 100644 --- a/src/test/compile-fail/borrowck/borrowck-move-out-of-vec-tail.rs +++ b/src/test/compile-fail/borrowck/borrowck-move-out-of-vec-tail.rs @@ -24,14 +24,14 @@ pub fn main() { Foo { string: "baz".to_string() } ); let x: &[Foo] = &x; - match x { - [_, tail..] => { + match *x { + [_, ref tail..] => { match tail { - [Foo { string: a }, + &[Foo { string: a }, //~^ ERROR cannot move out of borrowed content //~| cannot move out //~| to prevent move - Foo { string: b }] => { + Foo { string: b }] => { //~^ NOTE and here } _ => { diff --git a/src/test/compile-fail/borrowck/borrowck-vec-pattern-element-loan.rs b/src/test/compile-fail/borrowck/borrowck-vec-pattern-element-loan.rs index 98052ad31a7..63e80b90ac8 100644 --- a/src/test/compile-fail/borrowck/borrowck-vec-pattern-element-loan.rs +++ b/src/test/compile-fail/borrowck/borrowck-vec-pattern-element-loan.rs @@ -15,7 +15,7 @@ fn a<'a>() -> &'a [isize] { let vec = vec!(1, 2, 3, 4); let vec: &[isize] = &vec; //~ ERROR does not live long enough let tail = match vec { - [_, tail..] => tail, + &[_, ref tail..] => tail, _ => panic!("a") }; tail @@ -25,7 +25,7 @@ fn b<'a>() -> &'a [isize] { let vec = vec!(1, 2, 3, 4); let vec: &[isize] = &vec; //~ ERROR does not live long enough let init = match vec { - [init.., _] => init, + &[ref init.., _] => init, _ => panic!("b") }; init @@ -35,7 +35,7 @@ fn c<'a>() -> &'a [isize] { let vec = vec!(1, 2, 3, 4); let vec: &[isize] = &vec; //~ ERROR does not live long enough let slice = match vec { - [_, slice.., _] => slice, + &[_, ref slice.., _] => slice, _ => panic!("c") }; slice diff --git a/src/test/compile-fail/borrowck/borrowck-vec-pattern-loan-from-mut.rs b/src/test/compile-fail/borrowck/borrowck-vec-pattern-loan-from-mut.rs index db635893c81..9dfd4d77928 100644 --- a/src/test/compile-fail/borrowck/borrowck-vec-pattern-loan-from-mut.rs +++ b/src/test/compile-fail/borrowck/borrowck-vec-pattern-loan-from-mut.rs @@ -14,7 +14,7 @@ fn a() { let mut v = vec!(1, 2, 3); let vb: &mut [isize] = &mut v; match vb { - [_a, tail..] => { + &mut [_a, ref tail..] => { v.push(tail[0] + tail[1]); //~ ERROR cannot borrow } _ => {} diff --git a/src/test/compile-fail/borrowck/borrowck-vec-pattern-move-tail.rs b/src/test/compile-fail/borrowck/borrowck-vec-pattern-move-tail.rs index 97dcaeb0bf1..fddb9838c44 100644 --- a/src/test/compile-fail/borrowck/borrowck-vec-pattern-move-tail.rs +++ b/src/test/compile-fail/borrowck/borrowck-vec-pattern-move-tail.rs @@ -13,7 +13,7 @@ fn main() { let mut a = [1, 2, 3, 4]; let t = match a { - [1, 2, tail..] => tail, + [1, 2, ref tail..] => tail, _ => unreachable!() }; println!("t[0]: {}", t[0]); diff --git a/src/test/compile-fail/borrowck/borrowck-vec-pattern-nesting.rs b/src/test/compile-fail/borrowck/borrowck-vec-pattern-nesting.rs index eec6c8473eb..d89b4100789 100644 --- a/src/test/compile-fail/borrowck/borrowck-vec-pattern-nesting.rs +++ b/src/test/compile-fail/borrowck/borrowck-vec-pattern-nesting.rs @@ -28,7 +28,7 @@ fn b() { let mut vec = vec!(box 1, box 2, box 3); let vec: &mut [Box] = &mut vec; match vec { - [_b..] => { + &mut [ref _b..] => { //~^ borrow of `vec[..]` occurs here vec[0] = box 4; //~ ERROR cannot assign //~^ assignment to borrowed `vec[..]` occurs here @@ -40,10 +40,11 @@ fn c() { let mut vec = vec!(box 1, box 2, box 3); let vec: &mut [Box] = &mut vec; match vec { - [_a, //~ ERROR cannot move out - //~| cannot move out - //~| to prevent move - _b..] => { + &mut [_a, //~ ERROR cannot move out of borrowed content + //~| cannot move out + //~| to prevent move + .. + ] => { // Note: `_a` is *moved* here, but `b` is borrowing, // hence illegal. // @@ -61,7 +62,7 @@ fn d() { let mut vec = vec!(box 1, box 2, box 3); let vec: &mut [Box] = &mut vec; match vec { - [_a.., //~ ERROR cannot move out + &mut [ //~ ERROR cannot move out //~^ cannot move out _b] => {} //~ NOTE to prevent move _ => {} @@ -75,7 +76,7 @@ fn e() { let mut vec = vec!(box 1, box 2, box 3); let vec: &mut [Box] = &mut vec; match vec { - [_a, _b, _c] => {} //~ ERROR cannot move out + &mut [_a, _b, _c] => {} //~ ERROR cannot move out //~| cannot move out //~| NOTE to prevent move //~| NOTE and here diff --git a/src/test/compile-fail/borrowck/borrowck-vec-pattern-tail-element-loan.rs b/src/test/compile-fail/borrowck/borrowck-vec-pattern-tail-element-loan.rs index 82b3490d7d7..a849e4e2faf 100644 --- a/src/test/compile-fail/borrowck/borrowck-vec-pattern-tail-element-loan.rs +++ b/src/test/compile-fail/borrowck/borrowck-vec-pattern-tail-element-loan.rs @@ -14,7 +14,7 @@ fn a<'a>() -> &'a isize { let vec = vec!(1, 2, 3, 4); let vec: &[isize] = &vec; //~ ERROR `vec` does not live long enough let tail = match vec { - [_a, tail..] => &tail[0], + &[_a, ref tail..] => &tail[0], _ => panic!("foo") }; tail diff --git a/src/test/compile-fail/issue-12369.rs b/src/test/compile-fail/issue-12369.rs index 1333bfac64e..978d6f59b2d 100644 --- a/src/test/compile-fail/issue-12369.rs +++ b/src/test/compile-fail/issue-12369.rs @@ -13,9 +13,9 @@ fn main() { let sl = vec![1,2,3]; let v: isize = match &*sl { - [] => 0, - [a,b,c] => 3, - [a, rest..] => a, - [10,a, rest..] => 10 //~ ERROR: unreachable pattern + &[] => 0, + &[a,b,c] => 3, + &[a, ref rest..] => a, + &[10,a, ref rest..] => 10 //~ ERROR: unreachable pattern }; } diff --git a/src/test/compile-fail/issue-12567.rs b/src/test/compile-fail/issue-12567.rs index 1580ec00f94..32a6ea4f062 100644 --- a/src/test/compile-fail/issue-12567.rs +++ b/src/test/compile-fail/issue-12567.rs @@ -12,13 +12,15 @@ fn match_vecs<'a, T>(l1: &'a [T], l2: &'a [T]) { match (l1, l2) { - ([], []) => println!("both empty"), - ([], [hd, tl..]) | ([hd, tl..], []) => println!("one empty"), - //~^ ERROR: cannot move out of borrowed content + (&[], &[]) => println!("both empty"), + (&[], &[hd, ..]) | (&[hd, ..], &[]) + => println!("one empty"), //~^^ ERROR: cannot move out of borrowed content - ([hd1, tl1..], [hd2, tl2..]) => println!("both nonempty"), - //~^ ERROR: cannot move out of borrowed content + //~^^^ ERROR: cannot move out of borrowed content + (&[hd1, ..], &[hd2, ..]) + => println!("both nonempty"), //~^^ ERROR: cannot move out of borrowed content + //~^^^ ERROR: cannot move out of borrowed content } } diff --git a/src/test/compile-fail/issue-13482-2.rs b/src/test/compile-fail/issue-13482-2.rs index fe03373a45d..6885c8d94c6 100644 --- a/src/test/compile-fail/issue-13482-2.rs +++ b/src/test/compile-fail/issue-13482-2.rs @@ -15,11 +15,7 @@ fn main() { let x = [1,2]; let y = match x { - [] => None, -//~^ ERROR mismatched types -//~| expected type `[_#1i; 2]` -//~| found type `[_#7t; 0]` -//~| expected an array with a fixed size of 2 elements, found one with 0 elements + [] => None, //~ ERROR pattern requires 0 elements but array has 2 [a,_] => Some(a) }; } diff --git a/src/test/compile-fail/issue-13482.rs b/src/test/compile-fail/issue-13482.rs index 7ed7f5898b1..82e82df3186 100644 --- a/src/test/compile-fail/issue-13482.rs +++ b/src/test/compile-fail/issue-13482.rs @@ -13,11 +13,7 @@ fn main() { let x = [1,2]; let y = match x { - [] => None, - //~^ ERROR mismatched types - //~| expected type `[_; 2]` - //~| found type `[_; 0]` - //~| expected an array with a fixed size of 2 elements + [] => None, //~ ERROR pattern requires 0 elements but array has 2 [a,_] => Some(a) }; } diff --git a/src/test/compile-fail/issue-15381.rs b/src/test/compile-fail/issue-15381.rs index ec29a84f44e..d0964d2aabe 100644 --- a/src/test/compile-fail/issue-15381.rs +++ b/src/test/compile-fail/issue-15381.rs @@ -13,8 +13,8 @@ fn main() { let values: Vec = vec![1,2,3,4,5,6,7,8]; - for [x,y,z] in values.chunks(3).filter(|&xs| xs.len() == 3) { - //~^ ERROR refutable pattern in `for` loop binding: `[]` not covered + for &[x,y,z] in values.chunks(3).filter(|&xs| xs.len() == 3) { + //~^ ERROR refutable pattern in `for` loop binding: `&[]` not covered println!("y={}", y); } } diff --git a/src/test/compile-fail/match-vec-mismatch-2.rs b/src/test/compile-fail/match-vec-mismatch-2.rs index 2831499c73d..375d855d1fd 100644 --- a/src/test/compile-fail/match-vec-mismatch-2.rs +++ b/src/test/compile-fail/match-vec-mismatch-2.rs @@ -13,9 +13,6 @@ fn main() { match () { [()] => { } - //~^ ERROR mismatched types - //~| expected type `()` - //~| found type `&[_]` - //~| expected (), found &-ptr + //~^ ERROR expected an array or slice, found `()` } } diff --git a/src/test/compile-fail/match-vec-mismatch.rs b/src/test/compile-fail/match-vec-mismatch.rs index ef75213d34b..3ac4958e7db 100644 --- a/src/test/compile-fail/match-vec-mismatch.rs +++ b/src/test/compile-fail/match-vec-mismatch.rs @@ -12,7 +12,36 @@ fn main() { match "foo".to_string() { - ['f', 'o', ..] => {} //~ ERROR mismatched types + ['f', 'o', ..] => {} + //~^ ERROR expected an array or slice, found `std::string::String` _ => { } - } + }; + + match &[0, 1, 2] { + [..] => {} //~ ERROR expected an array or slice, found `&[_; 3]` + }; + + match &[0, 1, 2] { + &[..] => {} // ok + }; + + match [0, 1, 2] { + [0] => {}, //~ ERROR pattern requires + + [0, 1, x..] => { + let a: [_; 1] = x; + } + [0, 1, 2, 3, x..] => {} //~ ERROR pattern requires + }; + + match does_not_exist { //~ ERROR unresolved name + [] => {} + }; +} + +fn another_fn_to_avoid_suppression() { + match Default::default() + { + [] => {} //~ ERROR the type of this value + }; } diff --git a/src/test/compile-fail/match-vec-unreachable.rs b/src/test/compile-fail/match-vec-unreachable.rs index 48b70b4bda0..57e3a58b566 100644 --- a/src/test/compile-fail/match-vec-unreachable.rs +++ b/src/test/compile-fail/match-vec-unreachable.rs @@ -13,7 +13,7 @@ fn main() { let x: Vec<(isize, isize)> = Vec::new(); let x: &[(isize, isize)] = &x; - match x { + match *x { [a, (2, 3), _] => (), [(1, 2), (2, 3), b] => (), //~ ERROR unreachable pattern _ => () @@ -23,7 +23,7 @@ fn main() { "bar".to_string(), "baz".to_string()]; let x: &[String] = &x; - match x { + match *x { [a, _, _, ..] => { println!("{}", a); } [_, _, _, _, _] => { } //~ ERROR unreachable pattern _ => { } @@ -31,8 +31,8 @@ fn main() { let x: Vec = vec!('a', 'b', 'c'); let x: &[char] = &x; - match x { - ['a', 'b', 'c', _tail..] => {} + match *x { + ['a', 'b', 'c', ref _tail..] => {} ['a', 'b', 'c'] => {} //~ ERROR unreachable pattern _ => {} } diff --git a/src/test/compile-fail/non-exhaustive-match-nested.rs b/src/test/compile-fail/non-exhaustive-match-nested.rs index ad2b8c400e5..1d524217a12 100644 --- a/src/test/compile-fail/non-exhaustive-match-nested.rs +++ b/src/test/compile-fail/non-exhaustive-match-nested.rs @@ -14,11 +14,11 @@ enum t { a(u), b } enum u { c, d } fn match_nested_vecs<'a, T>(l1: Option<&'a [T]>, l2: Result<&'a [T], ()>) -> &'static str { - match (l1, l2) { //~ ERROR non-exhaustive patterns: `(Some([]), Err(_))` not covered - (Some([]), Ok([])) => "Some(empty), Ok(empty)", - (Some([_, ..]), Ok(_)) | (Some([_, ..]), Err(())) => "Some(non-empty), any", - (None, Ok([])) | (None, Err(())) | (None, Ok([_])) => "None, Ok(less than one element)", - (None, Ok([_, _, ..])) => "None, Ok(at least two elements)" + match (l1, l2) { //~ ERROR non-exhaustive patterns: `(Some(&[]), Err(_))` not covered + (Some(&[]), Ok(&[])) => "Some(empty), Ok(empty)", + (Some(&[_, ..]), Ok(_)) | (Some(&[_, ..]), Err(())) => "Some(non-empty), any", + (None, Ok(&[])) | (None, Err(())) | (None, Ok(&[_])) => "None, Ok(less than one element)", + (None, Ok(&[_, _, ..])) => "None, Ok(at least two elements)" } } diff --git a/src/test/compile-fail/non-exhaustive-match.rs b/src/test/compile-fail/non-exhaustive-match.rs index b9749c2696e..017baacc9d3 100644 --- a/src/test/compile-fail/non-exhaustive-match.rs +++ b/src/test/compile-fail/non-exhaustive-match.rs @@ -39,20 +39,20 @@ fn main() { } let vec = vec!(Some(42), None, Some(21)); let vec: &[Option] = &vec; - match vec { //~ ERROR non-exhaustive patterns: `[]` not covered - [Some(..), None, tail..] => {} - [Some(..), Some(..), tail..] => {} + match *vec { //~ ERROR non-exhaustive patterns: `[]` not covered + [Some(..), None, ref tail..] => {} + [Some(..), Some(..), ref tail..] => {} [None] => {} } let vec = vec!(1); let vec: &[isize] = &vec; - match vec { - [_, tail..] => (), + match *vec { + [_, ref tail..] => (), [] => () } let vec = vec!(0.5f32); let vec: &[f32] = &vec; - match vec { //~ ERROR non-exhaustive patterns: `[_, _, _, _]` not covered + match *vec { //~ ERROR non-exhaustive patterns: `[_, _, _, _]` not covered [0.1, 0.2, 0.3] => (), [0.1, 0.2] => (), [0.1] => (), @@ -60,11 +60,11 @@ fn main() { } let vec = vec!(Some(42), None, Some(21)); let vec: &[Option] = &vec; - match vec { - [Some(..), None, tail..] => {} - [Some(..), Some(..), tail..] => {} - [None, None, tail..] => {} - [None, Some(..), tail..] => {} + match *vec { + [Some(..), None, ref tail..] => {} + [Some(..), Some(..), ref tail..] => {} + [None, None, ref tail..] => {} + [None, Some(..), ref tail..] => {} [Some(_)] => {} [None] => {} [] => {} diff --git a/src/test/compile-fail/non-exhaustive-pattern-witness.rs b/src/test/compile-fail/non-exhaustive-pattern-witness.rs index b986878f783..0b12a9acbcb 100644 --- a/src/test/compile-fail/non-exhaustive-pattern-witness.rs +++ b/src/test/compile-fail/non-exhaustive-pattern-witness.rs @@ -80,7 +80,7 @@ enum Enum { fn vectors_with_nested_enums() { let x: &'static [Enum] = &[Enum::First, Enum::Second(false)]; - match x { + match *x { //~^ ERROR non-exhaustive patterns: `[Second(true), Second(false)]` not covered [] => (), [_] => (), @@ -88,7 +88,7 @@ fn vectors_with_nested_enums() { [Enum::Second(true), Enum::First] => (), [Enum::Second(true), Enum::Second(true)] => (), [Enum::Second(false), _] => (), - [_, _, tail.., _] => () + [_, _, ref tail.., _] => () } } diff --git a/src/test/run-pass/issue-15080.rs b/src/test/run-pass/issue-15080.rs index 7b00ea4a520..cee0caeb465 100644 --- a/src/test/run-pass/issue-15080.rs +++ b/src/test/run-pass/issue-15080.rs @@ -16,12 +16,12 @@ fn main() { let mut result = vec!(); loop { - x = match x { - [1, n, 3, rest..] => { + x = match *x { + [1, n, 3, ref rest..] => { result.push(n); rest } - [n, rest..] => { + [n, ref rest..] => { result.push(n); rest } diff --git a/src/test/run-pass/issue-15104.rs b/src/test/run-pass/issue-15104.rs index b55754ee59b..508360cb701 100644 --- a/src/test/run-pass/issue-15104.rs +++ b/src/test/run-pass/issue-15104.rs @@ -16,9 +16,9 @@ fn main() { } fn count_members(v: &[usize]) -> usize { - match v { + match *v { [] => 0, [_] => 1, - [_x, xs..] => 1 + count_members(xs) + [_, ref xs..] => 1 + count_members(xs) } } diff --git a/src/test/run-pass/issue-16648.rs b/src/test/run-pass/issue-16648.rs index 384bd9df7cf..e596bee8bfe 100644 --- a/src/test/run-pass/issue-16648.rs +++ b/src/test/run-pass/issue-16648.rs @@ -9,14 +9,15 @@ // except according to those terms. -#![feature(slice_patterns)] +#![feature(slice_patterns, rustc_attrs)] +#[rustc_mir] fn main() { let x: (isize, &[isize]) = (2, &[1, 2]); assert_eq!(match x { - (0, [_, _]) => 0, + (0, &[_, _]) => 0, (1, _) => 1, - (2, [_, _]) => 2, + (2, &[_, _]) => 2, (2, _) => 3, _ => 4 }, 2); diff --git a/src/test/run-pass/issue-7784.rs b/src/test/run-pass/issue-7784.rs index badc013cd62..0008825226b 100644 --- a/src/test/run-pass/issue-7784.rs +++ b/src/test/run-pass/issue-7784.rs @@ -11,6 +11,7 @@ #![feature(advanced_slice_patterns)] #![feature(slice_patterns)] +#![feature(rustc_attrs)] use std::ops::Add; @@ -21,6 +22,7 @@ fn bar(a: &'static str, b: &'static str) -> [&'static str; 4] { [a, b, b, a] } +#[rustc_mir] fn main() { assert_eq!(foo([1, 2, 3]), (1, 3, 6)); diff --git a/src/test/run-pass/match-vec-alternatives.rs b/src/test/run-pass/match-vec-alternatives.rs index 43e0b442251..010c1455210 100644 --- a/src/test/run-pass/match-vec-alternatives.rs +++ b/src/test/run-pass/match-vec-alternatives.rs @@ -11,47 +11,53 @@ #![feature(advanced_slice_patterns)] #![feature(slice_patterns)] +#![feature(rustc_attrs)] +#[rustc_mir] fn match_vecs<'a, T>(l1: &'a [T], l2: &'a [T]) -> &'static str { match (l1, l2) { - ([], []) => "both empty", - ([], [..]) | ([..], []) => "one empty", - ([..], [..]) => "both non-empty" + (&[], &[]) => "both empty", + (&[], &[..]) | (&[..], &[]) => "one empty", + (&[..], &[..]) => "both non-empty" } } +#[rustc_mir] fn match_vecs_cons<'a, T>(l1: &'a [T], l2: &'a [T]) -> &'static str { match (l1, l2) { - ([], []) => "both empty", - ([], [_, ..]) | ([_, ..], []) => "one empty", - ([_, ..], [_, ..]) => "both non-empty" + (&[], &[]) => "both empty", + (&[], &[_, ..]) | (&[_, ..], &[]) => "one empty", + (&[_, ..], &[_, ..]) => "both non-empty" } } +#[rustc_mir] fn match_vecs_snoc<'a, T>(l1: &'a [T], l2: &'a [T]) -> &'static str { match (l1, l2) { - ([], []) => "both empty", - ([], [.., _]) | ([.., _], []) => "one empty", - ([.., _], [.., _]) => "both non-empty" + (&[], &[]) => "both empty", + (&[], &[.., _]) | (&[.., _], &[]) => "one empty", + (&[.., _], &[.., _]) => "both non-empty" } } +#[rustc_mir] fn match_nested_vecs_cons<'a, T>(l1: Option<&'a [T]>, l2: Result<&'a [T], ()>) -> &'static str { match (l1, l2) { - (Some([]), Ok([])) => "Some(empty), Ok(empty)", - (Some([_, ..]), Ok(_)) | (Some([_, ..]), Err(())) => "Some(non-empty), any", - (None, Ok([])) | (None, Err(())) | (None, Ok([_])) => "None, Ok(less than one element)", - (None, Ok([_, _, ..])) => "None, Ok(at least two elements)", + (Some(&[]), Ok(&[])) => "Some(empty), Ok(empty)", + (Some(&[_, ..]), Ok(_)) | (Some(&[_, ..]), Err(())) => "Some(non-empty), any", + (None, Ok(&[])) | (None, Err(())) | (None, Ok(&[_])) => "None, Ok(less than one element)", + (None, Ok(&[_, _, ..])) => "None, Ok(at least two elements)", _ => "other" } } +#[rustc_mir] fn match_nested_vecs_snoc<'a, T>(l1: Option<&'a [T]>, l2: Result<&'a [T], ()>) -> &'static str { match (l1, l2) { - (Some([]), Ok([])) => "Some(empty), Ok(empty)", - (Some([.., _]), Ok(_)) | (Some([.., _]), Err(())) => "Some(non-empty), any", - (None, Ok([])) | (None, Err(())) | (None, Ok([_])) => "None, Ok(less than one element)", - (None, Ok([.., _, _])) => "None, Ok(at least two elements)", + (Some(&[]), Ok(&[])) => "Some(empty), Ok(empty)", + (Some(&[.., _]), Ok(_)) | (Some(&[.., _]), Err(())) => "Some(non-empty), any", + (None, Ok(&[])) | (None, Err(())) | (None, Ok(&[_])) => "None, Ok(less than one element)", + (None, Ok(&[.., _, _])) => "None, Ok(at least two elements)", _ => "other" } } diff --git a/src/test/run-pass/vec-matching-fold.rs b/src/test/run-pass/vec-matching-fold.rs index ee70ea58750..7a6129d311e 100644 --- a/src/test/run-pass/vec-matching-fold.rs +++ b/src/test/run-pass/vec-matching-fold.rs @@ -11,21 +11,28 @@ #![feature(advanced_slice_patterns)] #![feature(slice_patterns)] +#![feature(rustc_attrs)] +use std::fmt::Debug; + +#[rustc_mir(graphviz="mir.gv")] fn foldl(values: &[T], initial: U, mut function: F) -> U where - U: Clone, + U: Clone+Debug, T:Debug, F: FnMut(U, &T) -> U, -{ - match values { - [ref head, tail..] => +{ match values { + &[ref head, ref tail..] => foldl(tail, function(initial, head), function), - [] => initial.clone() + &[] => { + // FIXME: call guards + let res = initial.clone(); res + } } } +#[rustc_mir] fn foldr(values: &[T], initial: U, mut function: F) @@ -34,9 +41,12 @@ fn foldr(values: &[T], F: FnMut(&T, U) -> U, { match values { - [head.., ref tail] => + &[ref head.., ref tail] => foldr(head, function(tail, initial), function), - [] => initial.clone() + &[] => { + // FIXME: call guards + let res = initial.clone(); res + } } } diff --git a/src/test/run-pass/vec-matching-legal-tail-element-borrow.rs b/src/test/run-pass/vec-matching-legal-tail-element-borrow.rs index e7553c8e157..1093bc7c18b 100644 --- a/src/test/run-pass/vec-matching-legal-tail-element-borrow.rs +++ b/src/test/run-pass/vec-matching-legal-tail-element-borrow.rs @@ -8,14 +8,15 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(slice_patterns)] +#![feature(slice_patterns, rustc_attrs)] +#[rustc_mir] pub fn main() { let x = &[1, 2, 3, 4, 5]; let x: &[isize] = &[1, 2, 3, 4, 5]; if !x.is_empty() { let el = match x { - [1, ref tail..] => &tail[0], + &[1, ref tail..] => &tail[0], _ => unreachable!() }; println!("{}", *el); diff --git a/src/test/run-pass/vec-matching.rs b/src/test/run-pass/vec-matching.rs index eedf27f8577..075709a63b5 100644 --- a/src/test/run-pass/vec-matching.rs +++ b/src/test/run-pass/vec-matching.rs @@ -11,7 +11,9 @@ #![feature(advanced_slice_patterns)] #![feature(slice_patterns)] +#![feature(rustc_attrs)] +#[rustc_mir] fn a() { let x = [1]; match x { @@ -21,6 +23,7 @@ fn a() { } } +#[rustc_mir] fn b() { let x = [1, 2, 3]; match x { @@ -56,6 +59,48 @@ fn b() { } } + +#[rustc_mir] +fn b_slice() { + let x : &[_] = &[1, 2, 3]; + match x { + &[a, b, ref c..] => { + assert_eq!(a, 1); + assert_eq!(b, 2); + let expected: &[_] = &[3]; + assert_eq!(c, expected); + } + _ => unreachable!() + } + match x { + &[ref a.., b, c] => { + let expected: &[_] = &[1]; + assert_eq!(a, expected); + assert_eq!(b, 2); + assert_eq!(c, 3); + } + _ => unreachable!() + } + match x { + &[a, ref b.., c] => { + assert_eq!(a, 1); + let expected: &[_] = &[2]; + assert_eq!(b, expected); + assert_eq!(c, 3); + } + _ => unreachable!() + } + match x { + &[a, b, c] => { + assert_eq!(a, 1); + assert_eq!(b, 2); + assert_eq!(c, 3); + } + _ => unreachable!() + } +} + +#[rustc_mir] fn c() { let x = [1]; match x { @@ -64,6 +109,7 @@ fn c() { } } +#[rustc_mir] fn d() { let x = [1, 2, 3]; let branch = match x { @@ -75,17 +121,40 @@ fn d() { assert_eq!(branch, 1); } +#[rustc_mir] fn e() { let x: &[isize] = &[1, 2, 3]; - match x { - [1, 2] => (), - [..] => () - } + let a = match *x { + [1, 2] => 0, + [..] => 1, + }; + + assert_eq!(a, 1); + + let b = match *x { + [2, ..] => 0, + [1, 2, ..] => 1, + [_] => 2, + [..] => 3 + }; + + assert_eq!(b, 1); + + + let c = match *x { + [_, _, _, _, ..] => 0, + [1, 2, ..] => 1, + [_] => 2, + [..] => 3 + }; + + assert_eq!(c, 1); } pub fn main() { a(); b(); + b_slice(); c(); d(); e(); diff --git a/src/test/run-pass/vec-tail-matching.rs b/src/test/run-pass/vec-tail-matching.rs index 6cc7e3a072c..6084a0d07a1 100644 --- a/src/test/run-pass/vec-tail-matching.rs +++ b/src/test/run-pass/vec-tail-matching.rs @@ -11,26 +11,28 @@ #![feature(slice_patterns)] +#![feature(rustc_attrs)] struct Foo { - string: String + string: &'static str } +#[rustc_mir] pub fn main() { let x = [ - Foo { string: "foo".to_string() }, - Foo { string: "bar".to_string() }, - Foo { string: "baz".to_string() } + Foo { string: "foo" }, + Foo { string: "bar" }, + Foo { string: "baz" } ]; match x { - [ref first, tail..] => { - assert_eq!(first.string, "foo".to_string()); + [ref first, ref tail..] => { + assert_eq!(first.string, "foo"); assert_eq!(tail.len(), 2); - assert_eq!(tail[0].string, "bar".to_string()); - assert_eq!(tail[1].string, "baz".to_string()); + assert_eq!(tail[0].string, "bar"); + assert_eq!(tail[1].string, "baz"); - match tail { - [Foo { .. }, _, Foo { .. }, _tail..] => { + match *(tail as &[_]) { + [Foo { .. }, _, Foo { .. }, ref _tail..] => { unreachable!(); } [Foo { string: ref a }, Foo { string: ref b }] => { diff --git a/src/test/run-pass/zero_sized_subslice_match.rs b/src/test/run-pass/zero_sized_subslice_match.rs index 697508ae488..00f4aa98a3e 100644 --- a/src/test/run-pass/zero_sized_subslice_match.rs +++ b/src/test/run-pass/zero_sized_subslice_match.rs @@ -8,15 +8,16 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. - +#![feature(rustc_attrs)] #![feature(slice_patterns)] +#[rustc_mir] fn main() { let x = [(), ()]; // The subslice used to go out of bounds for zero-sized array items, check that this doesn't // happen anymore match x { - [_, y..] => assert_eq!(&x[1] as *const (), &y[0] as *const ()) + [_, ref y..] => assert_eq!(&x[1] as *const (), &y[0] as *const ()) } }