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.
This commit is contained in:
Ariel Ben-Yehuda 2016-03-11 12:54:59 +02:00
parent 088b7e2108
commit 5c717a6fc2
47 changed files with 626 additions and 523 deletions

View File

@ -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);
}
_ => {}
}
}));

View File

@ -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<N:ast_node>(&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)?;

View File

@ -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<Operand<'tcx>>),
/// 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<Lvalue<'tcx>>,
@ -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 {

View File

@ -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
}
}

View File

@ -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);
}

View File

@ -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,

View File

@ -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);
}
}
}
}

View File

@ -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)

View File

@ -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,

View File

@ -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<BasicBlock, MatchPair<'pat, 'tcx>> {
-> 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(())
}
}
}

View File

@ -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>,

View File

@ -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<MatchPair<'pat, 'tcx>>,
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<MatchPair<'pat, 'tcx>>,
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));
);
}
}

View File

@ -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();
}

View File

@ -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 => {

View File

@ -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.

View File

@ -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,
}

View File

@ -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

View File

@ -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(()) => {}
}
}
_ => {}
}
});

View File

@ -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 `{}`
}

View File

@ -560,15 +560,20 @@ impl Wtf8 {
}
}
// FIXME(stage0): use slice patterns after snapshot
#[inline]
fn final_lead_surrogate(&self) -> Option<u16> {
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
}
}
}

View File

@ -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
}
_ => {

View File

@ -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

View File

@ -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
}
_ => {}

View File

@ -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]);

View File

@ -28,7 +28,7 @@ fn b() {
let mut vec = vec!(box 1, box 2, box 3);
let vec: &mut [Box<isize>] = &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<isize>] = &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<isize>] = &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<isize>] = &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

View File

@ -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

View File

@ -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
};
}

View File

@ -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
}
}

View File

@ -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)
};
}

View File

@ -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)
};
}

View File

@ -13,8 +13,8 @@
fn main() {
let values: Vec<u8> = 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);
}
}

View File

@ -13,9 +13,6 @@
fn main() {
match () {
[()] => { }
//~^ ERROR mismatched types
//~| expected type `()`
//~| found type `&[_]`
//~| expected (), found &-ptr
//~^ ERROR expected an array or slice, found `()`
}
}

View File

@ -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
};
}

View File

@ -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<char> = 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
_ => {}
}

View File

@ -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)"
}
}

View File

@ -39,20 +39,20 @@ fn main() {
}
let vec = vec!(Some(42), None, Some(21));
let vec: &[Option<isize>] = &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<isize>] = &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] => {}
[] => {}

View File

@ -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.., _] => ()
}
}

View File

@ -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
}

View File

@ -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)
}
}

View File

@ -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);

View File

@ -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));

View File

@ -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"
}
}

View File

@ -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<T, U, F>(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<T, U, F>(values: &[T],
initial: U,
mut function: F)
@ -34,9 +41,12 @@ fn foldr<T, U, F>(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
}
}
}

View File

@ -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);

View File

@ -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();

View File

@ -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 }] => {

View File

@ -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 ())
}
}