Auto merge of #32202 - arielb1:slice-patterns, r=nikomatsakis

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.

[RFC 495 text](https://github.com/rust-lang/rfcs/blob/master/text/0495-array-pattern-changes.md)
This commit is contained in:
bors 2016-06-08 19:30:33 -07:00
commit bb4b3fb7f9
77 changed files with 940 additions and 738 deletions

View file

@ -109,7 +109,7 @@ DEPS_rustc := syntax fmt_macros flate arena serialize getopts rbml \
log graphviz rustc_llvm rustc_back rustc_data_structures\
rustc_const_math
DEPS_rustc_back := std syntax flate log libc
DEPS_rustc_borrowck := rustc rustc_mir log graphviz syntax
DEPS_rustc_borrowck := rustc log graphviz syntax rustc_mir
DEPS_rustc_data_structures := std log serialize
DEPS_rustc_driver := arena flate getopts graphviz libc rustc rustc_back rustc_borrowck \
rustc_typeck rustc_mir rustc_resolve log syntax serialize rustc_llvm \
@ -123,9 +123,9 @@ DEPS_rustc_passes := syntax rustc core rustc_const_eval
DEPS_rustc_mir := rustc syntax rustc_const_math rustc_const_eval rustc_bitflags
DEPS_rustc_resolve := arena rustc log syntax
DEPS_rustc_platform_intrinsics := std
DEPS_rustc_plugin := rustc rustc_metadata syntax rustc_mir
DEPS_rustc_plugin := rustc rustc_metadata syntax
DEPS_rustc_privacy := rustc log syntax
DEPS_rustc_trans := arena flate getopts graphviz libc rustc rustc_back rustc_mir \
DEPS_rustc_trans := arena flate getopts graphviz libc rustc rustc_back \
log syntax serialize rustc_llvm rustc_platform_intrinsics \
rustc_const_math rustc_const_eval rustc_incremental
DEPS_rustc_incremental := rbml rustc serialize rustc_data_structures

View file

@ -10,7 +10,7 @@ fn main() {
let v = vec!["match_this", "1"];
match &v[..] {
["match_this", second] => println!("The second element is {}", second),
&["match_this", second] => println!("The second element is {}", second),
_ => {},
}
}
@ -26,8 +26,8 @@ slice will be bound to that name. For example:
fn is_symmetric(list: &[u32]) -> bool {
match list {
[] | [_] => true,
[x, inside.., y] if x == y => is_symmetric(inside),
&[] | &[_] => true,
&[x, ref inside.., y] if x == y => is_symmetric(inside),
_ => false
}
}

View file

@ -106,6 +106,7 @@ pub mod mir {
pub mod tcx;
pub mod visit;
pub mod transform;
pub mod traversal;
pub mod mir_map;
}

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

@ -228,10 +228,10 @@ fn deref_kind(t: Ty, context: DerefKindContext) -> McResult<deref_kind> {
Ok(deref_interior(InteriorField(PositionalField(0))))
}
ty::TyArray(_, _) | ty::TySlice(_) | ty::TyStr => {
ty::TyArray(_, _) | ty::TySlice(_) => {
// no deref of indexed content without supplying InteriorOffsetKind
if let Some(context) = context {
Ok(deref_interior(InteriorElement(context, element_kind(t))))
Ok(deref_interior(InteriorElement(context, ElementKind::VecElement)))
} else {
Err(())
}
@ -981,18 +981,19 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
let method_call = ty::MethodCall::expr(elt.id());
let method_ty = self.infcx.node_method_ty(method_call);
let element_ty = match method_ty {
let (element_ty, element_kind) = match method_ty {
Some(method_ty) => {
let ref_ty = self.overloaded_method_return_ty(method_ty);
base_cmt = self.cat_rvalue_node(elt.id(), elt.span(), ref_ty);
// FIXME(#20649) -- why are we using the `self_ty` as the element type...?
let self_ty = method_ty.fn_sig().input(0);
self.tcx().no_late_bound_regions(&self_ty).unwrap()
(self.tcx().no_late_bound_regions(&self_ty).unwrap(),
ElementKind::OtherElement)
}
None => {
match base_cmt.ty.builtin_index() {
Some(ty) => ty,
Some(ty) => (ty, ElementKind::VecElement),
None => {
return Err(());
}
@ -1000,102 +1001,11 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
}
};
let m = base_cmt.mutbl.inherit();
let ret = interior(elt, base_cmt.clone(), base_cmt.ty,
m, context, element_ty);
let interior_elem = InteriorElement(context, element_kind);
let ret =
self.cat_imm_interior(elt, base_cmt.clone(), element_ty, interior_elem);
debug!("cat_index ret {:?}", ret);
return Ok(ret);
fn interior<'tcx, N: ast_node>(elt: &N,
of_cmt: cmt<'tcx>,
vec_ty: Ty<'tcx>,
mutbl: MutabilityCategory,
context: InteriorOffsetKind,
element_ty: Ty<'tcx>) -> cmt<'tcx>
{
let interior_elem = InteriorElement(context, element_kind(vec_ty));
Rc::new(cmt_ {
id:elt.id(),
span:elt.span(),
cat:Categorization::Interior(of_cmt, interior_elem),
mutbl:mutbl,
ty:element_ty,
note: NoteNone
})
}
}
// Takes either a vec or a reference to a vec and returns the cmt for the
// underlying vec.
fn deref_vec<N:ast_node>(&self,
elt: &N,
base_cmt: cmt<'tcx>,
context: InteriorOffsetKind)
-> McResult<cmt<'tcx>>
{
let ret = match deref_kind(base_cmt.ty, Some(context))? {
deref_ptr(ptr) => {
// for unique ptrs, we inherit mutability from the
// owning reference.
let m = MutabilityCategory::from_pointer_kind(base_cmt.mutbl, ptr);
// the deref is explicit in the resulting cmt
Rc::new(cmt_ {
id:elt.id(),
span:elt.span(),
cat:Categorization::Deref(base_cmt.clone(), 0, ptr),
mutbl:m,
ty: match base_cmt.ty.builtin_deref(false, ty::NoPreference) {
Some(mt) => mt.ty,
None => bug!("Found non-derefable type")
},
note: NoteNone
})
}
deref_interior(_) => {
base_cmt
}
};
debug!("deref_vec ret {:?}", ret);
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,
@ -1319,15 +1229,12 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
PatKind::Vec(ref before, ref slice, ref after) => {
let context = InteriorOffsetKind::Pattern;
let vec_cmt = self.deref_vec(pat, cmt, context)?;
let elt_cmt = self.cat_index(pat, vec_cmt, context)?;
let elt_cmt = self.cat_index(pat, cmt, context)?;
for before_pat in before {
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)?;
@ -1620,18 +1527,6 @@ impl fmt::Debug for InteriorKind {
}
}
fn element_kind(t: Ty) -> ElementKind {
match t.sty {
ty::TyRef(_, ty::TypeAndMut{ty, ..}) |
ty::TyBox(ty) => match ty.sty {
ty::TySlice(_) => VecElement,
_ => OtherElement
},
ty::TyArray(..) | ty::TySlice(_) => VecElement,
_ => OtherElement
}
}
impl fmt::Debug for Upvar {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}/{:?}", self.id, self.kind)

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

@ -12,7 +12,7 @@ use std::vec;
use rustc_data_structures::bitvec::BitVector;
use rustc::mir::repr::*;
use super::repr::*;
/// Preorder traversal of a graph.
///

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

@ -17,6 +17,8 @@ use session::Session;
use traits;
use ty::{self, Ty, TyCtxt, TypeFoldable};
use util::common::slice_pat;
use syntax::ast::{FloatTy, IntTy, UintTy};
use syntax::attr;
use syntax::codemap::DUMMY_SP;
@ -98,17 +100,17 @@ impl TargetDataLayout {
let mut dl = TargetDataLayout::default();
for spec in sess.target.target.data_layout.split("-") {
match &spec.split(":").collect::<Vec<_>>()[..] {
["e"] => dl.endian = Endian::Little,
["E"] => dl.endian = Endian::Big,
["a", a..] => dl.aggregate_align = align(a, "a"),
["f32", a..] => dl.f32_align = align(a, "f32"),
["f64", a..] => dl.f64_align = align(a, "f64"),
[p @ "p", s, a..] | [p @ "p0", s, a..] => {
match slice_pat(&&spec.split(":").collect::<Vec<_>>()[..]) {
&["e"] => dl.endian = Endian::Little,
&["E"] => dl.endian = Endian::Big,
&["a", ref a..] => dl.aggregate_align = align(a, "a"),
&["f32", ref a..] => dl.f32_align = align(a, "f32"),
&["f64", ref a..] => dl.f64_align = align(a, "f64"),
&[p @ "p", s, ref a..] | &[p @ "p0", s, ref a..] => {
dl.pointer_size = size(s, p);
dl.pointer_align = align(a, p);
}
[s, a..] if s.starts_with("i") => {
&[s, ref a..] if s.starts_with("i") => {
let ty_align = match s[1..].parse::<u64>() {
Ok(1) => &mut dl.i8_align,
Ok(8) => &mut dl.i8_align,
@ -123,7 +125,7 @@ impl TargetDataLayout {
};
*ty_align = align(a, s);
}
[s, a..] if s.starts_with("v") => {
&[s, ref a..] if s.starts_with("v") => {
let v_size = size(&s[1..], "v");
let a = align(a, s);
if let Some(v) = dl.vector_align.iter_mut().find(|v| v.0 == v_size) {

View file

@ -247,3 +247,15 @@ pub fn path2cstr(p: &Path) -> CString {
pub fn path2cstr(p: &Path) -> CString {
CString::new(p.to_str().unwrap()).unwrap()
}
// FIXME(stage0): remove this
// HACK: this is needed because the interpretation of slice
// patterns changed between stage0 and now.
#[cfg(stage0)]
pub fn slice_pat<'a, 'b, T>(t: &'a &'b [T]) -> &'a &'b [T] {
t
}
#[cfg(not(stage0))]
pub fn slice_pat<'a, 'b, T>(t: &'a &'b [T]) -> &'b [T] {
*t
}

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

@ -42,6 +42,7 @@ use rustc::hir::fold::{Folder, noop_fold_pat};
use rustc::hir::print::pat_to_string;
use syntax::ptr::P;
use rustc::util::nodemap::FnvHashMap;
use rustc::util::common::slice_pat;
pub const DUMMY_WILD_PAT: &'static Pat = &Pat {
id: DUMMY_NODE_ID,
@ -49,7 +50,7 @@ pub const DUMMY_WILD_PAT: &'static Pat = &Pat {
span: DUMMY_SP
};
struct Matrix<'a>(Vec<Vec<&'a Pat>>);
struct Matrix<'a, 'tcx>(Vec<Vec<(&'a Pat, Option<Ty<'tcx>>)>>);
/// Pretty-printer for matrices of patterns, example:
/// ++++++++++++++++++++++++++
@ -63,14 +64,14 @@ struct Matrix<'a>(Vec<Vec<&'a Pat>>);
/// ++++++++++++++++++++++++++
/// + _ + [_, _, ..tail] +
/// ++++++++++++++++++++++++++
impl<'a> fmt::Debug for Matrix<'a> {
impl<'a, 'tcx> fmt::Debug for Matrix<'a, 'tcx> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "\n")?;
let &Matrix(ref m) = self;
let pretty_printed_matrix: Vec<Vec<String>> = m.iter().map(|row| {
row.iter()
.map(|&pat| pat_to_string(&pat))
.map(|&(pat,ty)| format!("{}: {:?}", pat_to_string(&pat), ty))
.collect::<Vec<String>>()
}).collect();
@ -97,8 +98,10 @@ impl<'a> fmt::Debug for Matrix<'a> {
}
}
impl<'a> FromIterator<Vec<&'a Pat>> for Matrix<'a> {
fn from_iter<T: IntoIterator<Item=Vec<&'a Pat>>>(iter: T) -> Matrix<'a> {
impl<'a, 'tcx> FromIterator<Vec<(&'a Pat, Option<Ty<'tcx>>)>> for Matrix<'a, 'tcx> {
fn from_iter<T: IntoIterator<Item=Vec<(&'a Pat, Option<Ty<'tcx>>)>>>(iter: T)
-> Self
{
Matrix(iter.into_iter().collect())
}
}
@ -109,7 +112,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.
@ -229,7 +232,7 @@ fn check_expr(cx: &mut MatchCheckCtxt, ex: &hir::Expr) {
.iter()
.filter(|&&(_, guard)| guard.is_none())
.flat_map(|arm| &arm.0)
.map(|pat| vec![&**pat])
.map(|pat| vec![wrap_pat(cx, &pat)])
.collect();
check_exhaustive(cx, ex.span, &matrix, source);
},
@ -301,7 +304,7 @@ fn check_arms(cx: &MatchCheckCtxt,
let mut printed_if_let_err = false;
for &(ref pats, guard) in arms {
for pat in pats {
let v = vec![&**pat];
let v = vec![wrap_pat(cx, &pat)];
match is_useful(cx, &seen, &v[..], LeaveOutWitness) {
NotUseful => {
@ -341,8 +344,9 @@ fn check_arms(cx: &MatchCheckCtxt,
"unreachable pattern");
// if we had a catchall pattern, hint at that
for row in &seen.0 {
if pat_is_catchall(&cx.tcx.def_map.borrow(), row[0]) {
span_note!(err, row[0].span, "this pattern matches any value");
if pat_is_catchall(&cx.tcx.def_map.borrow(), row[0].0) {
span_note!(err, row[0].0.span,
"this pattern matches any value");
}
}
err.emit();
@ -383,20 +387,23 @@ fn raw_pat(p: &Pat) -> &Pat {
}
}
fn check_exhaustive(cx: &MatchCheckCtxt, sp: Span, matrix: &Matrix, source: hir::MatchSource) {
match is_useful(cx, matrix, &[DUMMY_WILD_PAT], ConstructWitness) {
fn check_exhaustive<'a, 'tcx>(cx: &MatchCheckCtxt<'a, 'tcx>,
sp: Span,
matrix: &Matrix<'a, 'tcx>,
source: hir::MatchSource) {
match is_useful(cx, matrix, &[(DUMMY_WILD_PAT, None)], ConstructWitness) {
UsefulWithWitness(pats) => {
let witnesses = if pats.is_empty() {
vec![DUMMY_WILD_PAT]
} else {
pats.iter().map(|w| &**w ).collect()
pats.iter().map(|w| &**w).collect()
};
match source {
hir::MatchSource::ForLoopDesugar => {
// `witnesses[0]` has the form `Some(<head>)`, peel off the `Some`
let witness = match witnesses[0].node {
PatKind::TupleStruct(_, ref pats, _) => match &pats[..] {
[ref pat] => &**pat,
PatKind::TupleStruct(_, ref pats, _) => match slice_pat(&&pats[..]) {
&[ref pat] => &**pat,
_ => bug!(),
},
_ => bug!(),
@ -584,31 +591,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())
@ -643,7 +638,7 @@ impl Constructor {
fn missing_constructors(cx: &MatchCheckCtxt, &Matrix(ref rows): &Matrix,
left_ty: Ty, max_slice_length: usize) -> Vec<Constructor> {
let used_constructors: Vec<Constructor> = rows.iter()
.flat_map(|row| pat_constructors(cx, row[0], left_ty, max_slice_length))
.flat_map(|row| pat_constructors(cx, row[0].0, left_ty, max_slice_length))
.collect();
all_constructors(cx, left_ty, max_slice_length)
.into_iter()
@ -660,13 +655,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]
}
@ -685,13 +675,13 @@ fn all_constructors(_cx: &MatchCheckCtxt, left_ty: Ty,
// Note: is_useful doesn't work on empty types, as the paper notes.
// So it assumes that v is non-empty.
fn is_useful(cx: &MatchCheckCtxt,
matrix: &Matrix,
v: &[&Pat],
witness: WitnessPreference)
-> Usefulness {
fn is_useful<'a, 'tcx>(cx: &MatchCheckCtxt<'a, 'tcx>,
matrix: &Matrix<'a, 'tcx>,
v: &[(&Pat, Option<Ty<'tcx>>)],
witness: WitnessPreference)
-> Usefulness {
let &Matrix(ref rows) = matrix;
debug!("{:?}", matrix);
debug!("is_useful({:?}, {:?})", matrix, v);
if rows.is_empty() {
return match witness {
ConstructWitness => UsefulWithWitness(vec!()),
@ -702,32 +692,25 @@ fn is_useful(cx: &MatchCheckCtxt,
return NotUseful;
}
assert!(rows.iter().all(|r| r.len() == v.len()));
let real_pat = match rows.iter().find(|r| (*r)[0].id != DUMMY_NODE_ID) {
Some(r) => raw_pat(r[0]),
None if v.is_empty() => return NotUseful,
None => v[0]
};
let left_ty = if real_pat.id == DUMMY_NODE_ID {
cx.tcx.mk_nil()
} else {
let left_ty = cx.tcx.pat_ty(&real_pat);
match real_pat.node {
PatKind::Binding(hir::BindByRef(..), _, _) => {
left_ty.builtin_deref(false, NoPreference).unwrap().ty
}
_ => left_ty,
let left_ty = match rows.iter().filter_map(|r| r[0].1).next().or_else(|| v[0].1) {
Some(ty) => ty,
None => {
// all patterns are wildcards - we can pick any type we want
cx.tcx.types.bool
}
};
let max_slice_length = rows.iter().filter_map(|row| match row[0].node {
let max_slice_length = rows.iter().filter_map(|row| match row[0].0.node {
PatKind::Vec(ref before, _, ref after) => Some(before.len() + after.len()),
_ => None
}).max().map_or(0, |v| v + 1);
let constructors = pat_constructors(cx, v[0], left_ty, max_slice_length);
let constructors = pat_constructors(cx, v[0].0, left_ty, max_slice_length);
debug!("is_useful - pat_constructors = {:?} left_ty = {:?}", constructors,
left_ty);
if constructors.is_empty() {
let constructors = missing_constructors(cx, matrix, left_ty, max_slice_length);
debug!("is_useful - missing_constructors = {:?}", constructors);
if constructors.is_empty() {
all_constructors(cx, left_ty, max_slice_length).into_iter().map(|c| {
match is_useful_specialized(cx, matrix, v, c.clone(), left_ty, witness) {
@ -748,7 +731,7 @@ fn is_useful(cx: &MatchCheckCtxt,
}).find(|result| result != &NotUseful).unwrap_or(NotUseful)
} else {
let matrix = rows.iter().filter_map(|r| {
match raw_pat(r[0]).node {
match raw_pat(r[0].0).node {
PatKind::Binding(..) | PatKind::Wild => Some(r[1..].to_vec()),
_ => None,
}
@ -773,9 +756,14 @@ fn is_useful(cx: &MatchCheckCtxt,
}
}
fn is_useful_specialized(cx: &MatchCheckCtxt, &Matrix(ref m): &Matrix,
v: &[&Pat], ctor: Constructor, lty: Ty,
witness: WitnessPreference) -> Usefulness {
fn is_useful_specialized<'a, 'tcx>(
cx: &MatchCheckCtxt<'a, 'tcx>,
&Matrix(ref m): &Matrix<'a, 'tcx>,
v: &[(&Pat, Option<Ty<'tcx>>)],
ctor: Constructor,
lty: Ty<'tcx>,
witness: WitnessPreference) -> Usefulness
{
let arity = constructor_arity(cx, &ctor, lty);
let matrix = Matrix(m.iter().filter_map(|r| {
specialize(cx, &r[..], &ctor, 0, arity)
@ -818,13 +806,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 +828,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()
}
@ -877,6 +864,19 @@ fn range_covered_by_constructor(ctor: &Constructor,
}
}
fn wrap_pat<'a, 'b, 'tcx>(cx: &MatchCheckCtxt<'b, 'tcx>,
pat: &'a Pat)
-> (&'a Pat, Option<Ty<'tcx>>)
{
let pat_ty = cx.tcx.pat_ty(pat);
(pat, Some(match pat.node {
PatKind::Binding(hir::BindByRef(..), _, _) => {
pat_ty.builtin_deref(false, NoPreference).unwrap().ty
}
_ => pat_ty
}))
}
/// This is the main specialization step. It expands the first pattern in the given row
/// into `arity` patterns based on the constructor. For most patterns, the step is trivial,
/// for instance tuple patterns are flattened and box patterns expand into their inner pattern.
@ -885,14 +885,22 @@ fn range_covered_by_constructor(ctor: &Constructor,
/// different patterns.
/// Structure patterns with a partial wild pattern (Foo { a: 42, .. }) have their missing
/// fields filled with wild patterns.
pub fn specialize<'a>(cx: &MatchCheckCtxt, r: &[&'a Pat],
constructor: &Constructor, col: usize, arity: usize) -> Option<Vec<&'a Pat>> {
pub fn specialize<'a, 'b, 'tcx>(
cx: &MatchCheckCtxt<'b, 'tcx>,
r: &[(&'a Pat, Option<Ty<'tcx>>)],
constructor: &Constructor, col: usize, arity: usize)
-> Option<Vec<(&'a Pat, Option<Ty<'tcx>>)>>
{
let pat = raw_pat(r[col].0);
let &Pat {
id: pat_id, ref node, span: pat_span
} = raw_pat(r[col]);
let head: Option<Vec<&Pat>> = match *node {
} = pat;
let wpat = |pat: &'a Pat| wrap_pat(cx, pat);
let dummy_pat = (DUMMY_WILD_PAT, None);
let head: Option<Vec<(&Pat, Option<Ty>)>> = match *node {
PatKind::Binding(..) | PatKind::Wild =>
Some(vec![DUMMY_WILD_PAT; arity]),
Some(vec![dummy_pat; arity]),
PatKind::Path(..) => {
let def = cx.tcx.def_map.borrow().get(&pat_id).unwrap().full_def();
@ -917,12 +925,14 @@ pub fn specialize<'a>(cx: &MatchCheckCtxt, r: &[&'a Pat],
Def::Variant(..) | Def::Struct(..) => {
match ddpos {
Some(ddpos) => {
let mut pats: Vec<_> = args[..ddpos].iter().map(|p| &**p).collect();
pats.extend(repeat(DUMMY_WILD_PAT).take(arity - args.len()));
pats.extend(args[ddpos..].iter().map(|p| &**p));
let mut pats: Vec<_> = args[..ddpos].iter().map(|p| {
wpat(p)
}).collect();
pats.extend(repeat((DUMMY_WILD_PAT, None)).take(arity - args.len()));
pats.extend(args[ddpos..].iter().map(|p| wpat(p)));
Some(pats)
}
None => Some(args.iter().map(|p| &**p).collect())
None => Some(args.iter().map(|p| wpat(p)).collect())
}
}
_ => None
@ -941,8 +951,8 @@ pub fn specialize<'a>(cx: &MatchCheckCtxt, r: &[&'a Pat],
if variant.did == def_variant.did {
Some(variant.fields.iter().map(|sf| {
match pattern_fields.iter().find(|f| f.node.name == sf.name) {
Some(ref f) => &*f.node.pat,
_ => DUMMY_WILD_PAT
Some(ref f) => wpat(&f.node.pat),
_ => dummy_pat
}
}).collect())
} else {
@ -951,25 +961,32 @@ pub fn specialize<'a>(cx: &MatchCheckCtxt, r: &[&'a Pat],
}
PatKind::Tuple(ref args, Some(ddpos)) => {
let mut pats: Vec<_> = args[..ddpos].iter().map(|p| &**p).collect();
pats.extend(repeat(DUMMY_WILD_PAT).take(arity - args.len()));
pats.extend(args[ddpos..].iter().map(|p| &**p));
let mut pats: Vec<_> = args[..ddpos].iter().map(|p| wpat(p)).collect();
pats.extend(repeat(dummy_pat).take(arity - args.len()));
pats.extend(args[ddpos..].iter().map(|p| wpat(p)));
Some(pats)
}
PatKind::Tuple(ref args, None) =>
Some(args.iter().map(|p| &**p).collect()),
Some(args.iter().map(|p| wpat(&**p)).collect()),
PatKind::Box(ref inner) | PatKind::Ref(ref inner, _) =>
Some(vec![&**inner]),
Some(vec![wpat(&**inner)]),
PatKind::Lit(ref expr) => {
let expr_value = eval_const_expr(cx.tcx, &expr);
match range_covered_by_constructor(constructor, &expr_value, &expr_value) {
Some(true) => Some(vec![]),
Some(false) => None,
None => {
span_err!(cx.tcx.sess, pat_span, E0298, "mismatched types between arms");
None
if let Some(&ty::TyS { sty: ty::TyRef(_, mt), .. }) = r[col].1 {
// HACK: handle string literals. A string literal pattern
// serves both as an unary reference pattern and as a
// nullary value pattern, depending on the type.
Some(vec![(pat, Some(mt.ty))])
} else {
let expr_value = eval_const_expr(cx.tcx, &expr);
match range_covered_by_constructor(constructor, &expr_value, &expr_value) {
Some(true) => Some(vec![]),
Some(false) => None,
None => {
span_err!(cx.tcx.sess, pat_span, E0298, "mismatched types between arms");
None
}
}
}
}
@ -988,37 +1005,45 @@ 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| wpat(p)).chain(
repeat(dummy_pat).take(arity - pat_len).chain(
after.iter().map(|p| wpat(p))
)).collect())
},
Slice(length) if pat_len <= length && slice.is_some() => {
Some(
before.iter().map(|p| wpat(p)).chain(
repeat(dummy_pat).take(arity - pat_len).chain(
after.iter().map(|p| wpat(p))
)).collect())
}
Slice(length) if pat_len == length => {
Some(
before.iter().map(|p| wpat(p)).chain(
after.iter().map(|p| wpat(p))
).collect())
}
SliceWithSubslice(prefix, suffix)
if before.len() == prefix
&& after.len() == suffix
&& slice.is_some() => {
let mut pats: Vec<&Pat> = before.iter().map(|p| &**p).collect();
pats.extend(after.iter().map(|p| &**p));
// this is used by trans::_match only
let mut pats: Vec<_> = before.iter()
.map(|p| (&**p, None)).collect();
pats.extend(after.iter().map(|p| (&**p, None)));
Some(pats)
}
_ => None
}
}
};
debug!("specialize({:?}, {:?}) = {:?}", r[col], arity, head);
head.map(|mut head| {
head.extend_from_slice(&r[..col]);
head.extend_from_slice(&r[col + 1..]);
@ -1076,8 +1101,8 @@ fn check_irrefutable(cx: &MatchCheckCtxt, pat: &Pat, is_fn_arg: bool) {
fn is_refutable<A, F>(cx: &MatchCheckCtxt, pat: &Pat, refutable: F) -> Option<A> where
F: FnOnce(&Pat) -> A,
{
let pats = Matrix(vec!(vec!(pat)));
match is_useful(cx, &pats, &[DUMMY_WILD_PAT], ConstructWitness) {
let pats = Matrix(vec!(vec!(wrap_pat(cx, pat))));
match is_useful(cx, &pats, &[(DUMMY_WILD_PAT, None)], ConstructWitness) {
UsefulWithWitness(pats) => Some(refutable(&pats[0])),
NotUseful => None,
Useful => bug!()

View file

@ -16,6 +16,7 @@ use rustc::ty::{self, Ty, TyCtxt};
use middle::const_val::ConstVal;
use rustc_const_eval::eval_const_expr_partial;
use rustc_const_eval::EvalHint::ExprTypeChecked;
use util::common::slice_pat;
use util::nodemap::{FnvHashSet};
use lint::{LateContext, LintContext, LintArray};
use lint::{LintPass, LateLintPass};
@ -459,8 +460,8 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
// Check for a repr() attribute to specify the size of the
// discriminant.
let repr_hints = cx.lookup_repr_hints(def.did);
match &**repr_hints {
[] => {
match slice_pat(&&**repr_hints) {
&[] => {
// Special-case types like `Option<extern fn()>`.
if !is_repr_nullable_ptr(cx, def, substs) {
return FfiUnsafe(
@ -470,7 +471,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
the type")
}
}
[ref hint] => {
&[ref hint] => {
if !hint.is_ffi_safe() {
// FIXME: This shouldn't be reachable: we should check
// this earlier.

View file

@ -429,42 +429,87 @@ 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, z) {
/// (true, _, true) => true, // (0)
/// (_, true, _) => true, // (1)
/// (false, false, _) => false, // (2)
/// (true, _, false) => false, // (3)
/// }
/// ```
///
/// 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, 1, and 3
/// - 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 the 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

@ -49,4 +49,3 @@ mod hair;
pub mod mir_map;
pub mod pretty;
pub mod transform;
pub mod traversal;

View file

@ -11,11 +11,9 @@
use rustc::ty::TyCtxt;
use rustc::mir::repr::*;
use rustc::mir::transform::{MirPass, MirSource, Pass};
use rustc::mir::traversal;
use pretty;
use traversal;
pub struct AddCallGuards;
/**

View file

@ -24,11 +24,11 @@
use rustc::mir::repr::*;
use rustc::mir::visit::{LvalueContext, MutVisitor, Visitor};
use rustc::mir::traversal::ReversePostorder;
use rustc::ty::{self, TyCtxt};
use syntax::codemap::Span;
use build::Location;
use traversal::ReversePostorder;
use std::mem;

View file

@ -25,6 +25,7 @@ use rustc::ty::cast::CastTy;
use rustc::mir::repr::*;
use rustc::mir::mir_map::MirMap;
use rustc::mir::transform::{Pass, MirMapPass, MirSource};
use rustc::mir::traversal::{self, ReversePostorder};
use rustc::mir::visit::{LvalueContext, Visitor};
use rustc::util::nodemap::DefIdMap;
use syntax::abi::Abi;
@ -35,7 +36,6 @@ use std::collections::hash_map::Entry;
use std::fmt;
use build::Location;
use traversal::{self, ReversePostorder};
use super::promote_consts::{self, Candidate, TempState};
@ -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

@ -13,13 +13,12 @@ use rustc::middle::const_val::ConstVal;
use rustc::ty::TyCtxt;
use rustc::mir::repr::*;
use rustc::mir::transform::{MirPass, MirSource, Pass};
use rustc::mir::traversal;
use pretty;
use std::mem;
use super::remove_dead_blocks::RemoveDeadBlocks;
use traversal;
pub struct SimplifyCfg;
impl SimplifyCfg {

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

@ -14,5 +14,4 @@ rustc = { path = "../librustc" }
rustc_back = { path = "../librustc_back" }
rustc_bitflags = { path = "../librustc_bitflags" }
rustc_metadata = { path = "../librustc_metadata" }
rustc_mir = { path = "../librustc_mir" }
syntax = { path = "../libsyntax" }

View file

@ -70,7 +70,6 @@
extern crate rustc;
extern crate rustc_back;
extern crate rustc_metadata;
extern crate rustc_mir;
pub use self::registry::Registry;

View file

@ -21,7 +21,6 @@ rustc_const_math = { path = "../librustc_const_math" }
rustc_data_structures = { path = "../librustc_data_structures" }
rustc_incremental = { path = "../librustc_incremental" }
rustc_llvm = { path = "../librustc_llvm" }
rustc_mir = { path = "../librustc_mir" }
rustc_platform_intrinsics = { path = "../librustc_platform_intrinsics" }
serialize = { path = "../libserialize" }
syntax = { path = "../libsyntax" }

View file

@ -505,14 +505,16 @@ fn enter_match<'a, 'b, 'p, 'blk, 'tcx, F>(bcx: Block<'blk, 'tcx>,
val: MatchInput,
mut e: F)
-> Vec<Match<'a, 'p, 'blk, 'tcx>> where
F: FnMut(&[&'p hir::Pat]) -> Option<Vec<&'p hir::Pat>>,
F: FnMut(&[(&'p hir::Pat, Option<Ty<'tcx>>)])
-> Option<Vec<(&'p hir::Pat, Option<Ty<'tcx>>)>>,
{
debug!("enter_match(bcx={}, m={:?}, col={}, val={:?})",
bcx.to_str(), m, col, val);
let _indenter = indenter();
m.iter().filter_map(|br| {
e(&br.pats).map(|pats| {
let pats : Vec<_> = br.pats.iter().map(|p| (*p, None)).collect();
e(&pats).map(|pats| {
let this = br.pats[col];
let mut bound_ptrs = br.bound_ptrs.clone();
match this.node {
@ -530,7 +532,7 @@ fn enter_match<'a, 'b, 'p, 'blk, 'tcx, F>(bcx: Block<'blk, 'tcx>,
_ => {}
}
Match {
pats: pats,
pats: pats.into_iter().map(|p| p.0).collect(),
data: br.data,
bound_ptrs: bound_ptrs,
pat_renaming_map: br.pat_renaming_map,
@ -550,7 +552,7 @@ fn enter_default<'a, 'p, 'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
// Collect all of the matches that can match against anything.
enter_match(bcx, m, col, val, |pats| {
match pats[col].node {
match pats[col].0.node {
PatKind::Binding(..) | PatKind::Wild => {
let mut r = pats[..col].to_vec();
r.extend_from_slice(&pats[col + 1..]);
@ -729,7 +731,14 @@ fn bind_subslice_pat(bcx: Block,
let (base, len) = vec_datum.get_vec_base_and_len(bcx);
let slice_begin = InBoundsGEP(bcx, base, &[C_uint(bcx.ccx(), offset_left)]);
let slice_len_offset = C_uint(bcx.ccx(), offset_left + offset_right);
let diff = offset_left + offset_right;
if let ty::TyArray(ty, n) = vec_ty_contents.sty {
let array_ty = bcx.tcx().mk_array(ty, n-diff);
let llty_array = type_of::type_of(bcx.ccx(), array_ty);
return PointerCast(bcx, slice_begin, llty_array.ptr_to());
}
let slice_len_offset = C_uint(bcx.ccx(), diff);
let slice_len = Sub(bcx, len, slice_len_offset, DebugLoc::None);
let slice_ty = bcx.tcx().mk_imm_ref(bcx.tcx().mk_region(ty::ReErased),
bcx.tcx().mk_slice(unit_ty));
@ -1205,7 +1214,12 @@ fn compile_submatch_continue<'a, 'p, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
}
Some(field_vals)
} else if any_uniq_pat(m, col) || any_region_pat(m, col) {
Some(vec!(Load(bcx, val.val)))
let ptr = if type_is_fat_ptr(bcx.tcx(), left_ty) {
val.val
} else {
Load(bcx, val.val)
};
Some(vec!(ptr))
} else {
match left_ty.sty {
ty::TyArray(_, n) => {

View file

@ -48,7 +48,6 @@ extern crate rustc_back;
extern crate rustc_data_structures;
extern crate rustc_incremental;
pub extern crate rustc_llvm as llvm;
extern crate rustc_mir;
extern crate rustc_platform_intrinsics as intrinsics;
extern crate serialize;
extern crate rustc_const_math;

View file

@ -15,7 +15,7 @@ use rustc_data_structures::bitvec::BitVector;
use rustc::mir::repr as mir;
use rustc::mir::repr::TerminatorKind;
use rustc::mir::visit::{Visitor, LvalueContext};
use rustc_mir::traversal;
use rustc::mir::traversal;
use common::{self, Block, BlockAndBuilder};
use super::rvalue;

View file

@ -775,9 +775,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
}
let dest = match *dest {
mir::Lvalue::Temp(idx) => {
let lvalue_ty = self.mir.lvalue_ty(bcx.tcx(), dest);
let lvalue_ty = bcx.monomorphize(&lvalue_ty);
let ret_ty = lvalue_ty.to_ty(bcx.tcx());
let ret_ty = self.lvalue_ty(dest);
match self.temps[idx as usize] {
TempRef::Lvalue(dest) => dest,
TempRef::Operand(None) => {

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) =>
@ -98,8 +99,9 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
},
mir::Lvalue::Arg(index) => self.args[index as usize],
mir::Lvalue::Static(def_id) => {
let const_ty = self.mir.lvalue_ty(tcx, lvalue);
LvalueRef::new_sized(consts::get_static(ccx, def_id).val, const_ty)
let const_ty = self.lvalue_ty(lvalue);
LvalueRef::new_sized(consts::get_static(ccx, def_id).val,
LvalueTy::from_ty(const_ty))
},
mir::Lvalue::ReturnPointer => {
let llval = if !fcx.fn_ty.ret.is_ignore() {
@ -131,7 +133,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 +171,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 +185,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.lvalue_ty(lvalue);
let llbasety = type_of::type_of(bcx.ccx(), base_ty).ptr_to();
let llbase = bcx.pointercast(llbase, llbasety);
(llbase, 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 +220,9 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
ty: projected_ty,
}
}
}
};
debug!("trans_lvalue(lvalue={:?}) => {:?}", lvalue, result);
result
}
// Perform an action using the given Lvalue.
@ -210,27 +237,23 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
match self.temps[idx as usize] {
TempRef::Lvalue(lvalue) => f(self, lvalue),
TempRef::Operand(None) => {
let lvalue_ty = self.mir.lvalue_ty(bcx.tcx(), lvalue);
let lvalue_ty = bcx.monomorphize(&lvalue_ty);
let lvalue_ty = self.lvalue_ty(lvalue);
let lvalue = LvalueRef::alloca(bcx,
lvalue_ty.to_ty(bcx.tcx()),
lvalue_ty,
"lvalue_temp");
let ret = f(self, lvalue);
let op = self.trans_load(bcx, lvalue.llval, lvalue_ty.to_ty(bcx.tcx()));
let op = self.trans_load(bcx, lvalue.llval, lvalue_ty);
self.temps[idx as usize] = TempRef::Operand(Some(op));
ret
}
TempRef::Operand(Some(_)) => {
let lvalue_ty = self.mir.lvalue_ty(bcx.tcx(), lvalue);
let lvalue_ty = bcx.monomorphize(&lvalue_ty);
// See comments in TempRef::new_operand as to why
// we always have Some in a ZST TempRef::Operand.
let ty = lvalue_ty.to_ty(bcx.tcx());
let ty = self.lvalue_ty(lvalue);
if common::type_is_zero_size(bcx.ccx(), ty) {
// Pass an undef pointer as no stores can actually occur.
let llptr = C_undef(type_of(bcx.ccx(), ty).ptr_to());
f(self, LvalueRef::new_sized(llptr, lvalue_ty))
f(self, LvalueRef::new_sized(llptr, LvalueTy::from_ty(ty)))
} else {
bug!("Lvalue temp already set");
}
@ -264,4 +287,10 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
llindex
}
}
pub fn lvalue_ty(&self, lvalue: &mir::Lvalue<'tcx>) -> Ty<'tcx> {
let tcx = self.fcx.ccx.tcx();
let lvalue_ty = self.mir.lvalue_ty(tcx, lvalue);
self.fcx.monomorphize(&lvalue_ty.to_ty(tcx))
}
}

View file

@ -34,7 +34,7 @@ use rustc_data_structures::bitvec::BitVector;
pub use self::constant::trans_static_initializer;
use self::lvalue::{LvalueRef, get_dataptr, get_meta};
use rustc_mir::traversal;
use rustc::mir::traversal;
use self::operand::{OperandRef, OperandValue};

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

@ -40,8 +40,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
bcx
}
TempRef::Operand(Some(_)) => {
let ty = self.mir.lvalue_ty(bcx.tcx(), lvalue);
let ty = bcx.monomorphize(&ty.to_ty(bcx.tcx()));
let ty = self.lvalue_ty(lvalue);
if !common::type_is_zero_size(bcx.ccx(), ty) {
span_bug!(statement.source_info.span,

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,53 @@ 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() {
let mut err = struct_span_err!(
tcx.sess, pat.span, E0529,
"expected an array or slice, found `{}`",
expected_ty);
if let ty::TyRef(_, ty::TypeAndMut { mutbl: _, ty }) = expected_ty.sty {
match ty.sty {
ty::TyArray(..) | ty::TySlice(..) => {
err.help("the semantics of slice patterns changed \
recently; see issue #23121");
}
_ => {}
}
}
err.emit();
}
(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 +378,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

@ -22,6 +22,7 @@ use rustc::hir::def_id::DefId;
use rustc::hir::print as pprust;
use rustc::ty::{self, TyCtxt};
use rustc::ty::subst;
use rustc::util::common::slice_pat;
use rustc_const_eval::lookup_const_by_id;
@ -197,10 +198,10 @@ fn build_struct<'a, 'tcx>(cx: &DocContext, tcx: TyCtxt<'a, 'tcx, 'tcx>,
let variant = tcx.lookup_adt_def(did).struct_variant();
clean::Struct {
struct_type: match &*variant.fields {
[] => doctree::Unit,
[_] if variant.kind == ty::VariantKind::Tuple => doctree::Newtype,
[..] if variant.kind == ty::VariantKind::Tuple => doctree::Tuple,
struct_type: match slice_pat(&&*variant.fields) {
&[] => doctree::Unit,
&[_] if variant.kind == ty::VariantKind::Tuple => doctree::Newtype,
&[..] if variant.kind == ty::VariantKind::Tuple => doctree::Tuple,
_ => doctree::Plain,
},
generics: (&t.generics, &predicates, subst::TypeSpace).clean(cx),

View file

@ -20,6 +20,7 @@ use std::iter::repeat;
use rustc::middle::cstore::LOCAL_CRATE;
use rustc::hir::def_id::{CRATE_DEF_INDEX, DefId};
use rustc::util::common::slice_pat;
use syntax::abi::Abi;
use rustc::hir;
@ -474,9 +475,9 @@ impl fmt::Display for clean::Type {
decl.decl)
}
clean::Tuple(ref typs) => {
match &**typs {
[] => primitive_link(f, clean::PrimitiveTuple, "()"),
[ref one] => {
match slice_pat(&&**typs) {
&[] => primitive_link(f, clean::PrimitiveTuple, "()"),
&[ref one] => {
primitive_link(f, clean::PrimitiveTuple, "(")?;
write!(f, "{},", one)?;
primitive_link(f, clean::PrimitiveTuple, ")")

View file

@ -467,3 +467,15 @@ pub mod __rand {
// the rustdoc documentation for primitive types. Using `include!`
// because rustdoc only looks for these modules at the crate level.
include!("primitive_docs.rs");
// FIXME(stage0): remove this after a snapshot
// HACK: this is needed because the interpretation of slice
// patterns changed between stage0 and now.
#[cfg(stage0)]
fn slice_pat<'a, 'b, T>(t: &'a &'b [T]) -> &'a &'b [T] {
t
}
#[cfg(not(stage0))]
fn slice_pat<'a, 'b, T>(t: &'a &'b [T]) -> &'b [T] {
*t
}

View file

@ -265,15 +265,18 @@ mod tests {
// Ensure the borrowchecker works
match queue.peek() {
Some(vec) => match &**vec {
// Note that `pop` is not allowed here due to borrow
[1] => {}
_ => return
Some(vec) => {
assert_eq!(&*vec, &[1]);
},
None => unreachable!()
}
queue.pop();
match queue.pop() {
Some(vec) => {
assert_eq!(&*vec, &[1]);
},
None => unreachable!()
}
}
}

View file

@ -566,8 +566,8 @@ impl Wtf8 {
if len < 3 {
return None
}
match &self.bytes[(len - 3)..] {
[0xED, b2 @ 0xA0...0xAF, b3] => Some(decode_surrogate(b2, b3)),
match ::slice_pat(&&self.bytes[(len - 3)..]) {
&[0xED, b2 @ 0xA0...0xAF, b3] => Some(decode_surrogate(b2, b3)),
_ => None
}
}
@ -578,8 +578,8 @@ impl Wtf8 {
if len < 3 {
return None
}
match &self.bytes[..3] {
[0xED, b2 @ 0xB0...0xBF, b3] => Some(decode_surrogate(b2, b3)),
match ::slice_pat(&&self.bytes[..3]) {
&[0xED, b2 @ 0xB0...0xBF, b3] => Some(decode_surrogate(b2, b3)),
_ => None
}
}

View file

@ -117,10 +117,10 @@ impl Drop for FindNextFileHandle {
impl DirEntry {
fn new(root: &Arc<PathBuf>, wfd: &c::WIN32_FIND_DATAW) -> Option<DirEntry> {
match &wfd.cFileName[0..3] {
match ::slice_pat(&&wfd.cFileName[0..3]) {
// check for '.' and '..'
[46, 0, ..] |
[46, 46, 0, ..] => return None,
&[46, 0, ..] |
&[46, 46, 0, ..] => return None,
_ => {}
}

2
src/rustc/Cargo.lock generated
View file

@ -238,7 +238,6 @@ dependencies = [
"rustc_back 0.0.0",
"rustc_bitflags 0.0.0",
"rustc_metadata 0.0.0",
"rustc_mir 0.0.0",
"syntax 0.0.0",
]
@ -285,7 +284,6 @@ dependencies = [
"rustc_data_structures 0.0.0",
"rustc_incremental 0.0.0",
"rustc_llvm 0.0.0",
"rustc_mir 0.0.0",
"rustc_platform_intrinsics 0.0.0",
"serialize 0.0.0",
"syntax 0.0.0",

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

@ -0,0 +1,21 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
fn main() {
match "world" { //~ ERROR non-exhaustive patterns: `&_`
"hello" => {}
}
match "world" { //~ ERROR non-exhaustive patterns: `&_`
ref _x if false => {}
"hello" => {}
"hello" => {} //~ ERROR unreachable pattern
}
}

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

@ -0,0 +1,22 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(slice_patterns)]
fn slice_pat(x: &[u8]) {
// OLD!
match x {
[a, b..] => {}
//~^ ERROR expected an array or slice, found `&[u8]`
//~| HELP the semantics of slice patterns changed recently; see issue #23121
}
}
fn main() {}

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

@ -0,0 +1,23 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
fn main() {
let &ref a = &[0i32] as &[_];
assert_eq!(a, &[0i32] as &[_]);
let &ref a = "hello";
assert_eq!(a, "hello");
match "foo" {
"fool" => unreachable!(),
"foo" => {},
ref _x => unreachable!()
}
}

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

@ -0,0 +1,18 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
fn main() {
let data: &'static str = "Hello, World!";
match data {
&ref xs => {
assert_eq!(data, xs);
}
}
}

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