split the exhaustiveness-checking logic to its own module

`check_match` is now left with its grab bag of random checks.
This commit is contained in:
Ariel Ben-Yehuda 2016-09-24 18:24:34 +03:00
parent 48387c8bd9
commit abae5e7e25
4 changed files with 726 additions and 682 deletions

View File

@ -0,0 +1,696 @@
// Copyright 2012-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.
use self::Constructor::*;
use self::Usefulness::*;
use self::WitnessPreference::*;
use rustc::middle::const_val::ConstVal;
use eval::{eval_const_expr, compare_const_vals};
use rustc::hir::def::*;
use rustc::hir::def_id::{DefId};
use rustc::hir::pat_util::def_to_path;
use rustc::ty::{self, Ty, TyCtxt};
use std::cmp::Ordering;
use std::fmt;
use std::iter::{FromIterator, IntoIterator, repeat};
use rustc::hir;
use rustc::hir::{Pat, PatKind};
use rustc::hir::print::pat_to_string;
use rustc::util::common::ErrorReported;
use syntax::ast::{self, DUMMY_NODE_ID};
use syntax::codemap::Spanned;
use syntax::ptr::P;
use syntax_pos::{Span, DUMMY_SP};
pub const DUMMY_WILD_PAT: &'static Pat = &Pat {
id: DUMMY_NODE_ID,
node: PatKind::Wild,
span: DUMMY_SP
};
pub const DUMMY_WILD_PATTERN : Pattern<'static, 'static> = Pattern {
pat: DUMMY_WILD_PAT,
pattern_ty: None
};
#[derive(Copy, Clone)]
pub struct Pattern<'a, 'tcx> {
pat: &'a Pat,
pattern_ty: Option<Ty<'tcx>>
}
impl<'a, 'tcx> Pattern<'a, 'tcx> {
fn as_raw(self) -> &'a Pat {
let mut pat = self.pat;
while let PatKind::Binding(.., Some(ref s)) = pat.node {
pat = s;
}
return pat;
}
pub fn span(self) -> Span {
self.pat.span
}
}
impl<'a, 'tcx> fmt::Debug for Pattern<'a, 'tcx> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}: {:?}", pat_to_string(self.pat), self.pattern_ty)
}
}
pub struct Matrix<'a, 'tcx>(Vec<Vec<Pattern<'a, 'tcx>>>);
impl<'a, 'tcx> Matrix<'a, 'tcx> {
pub fn empty() -> Self {
Matrix(vec![])
}
pub fn push(&mut self, row: Vec<Pattern<'a, 'tcx>>) {
self.0.push(row)
}
}
/// Pretty-printer for matrices of patterns, example:
/// ++++++++++++++++++++++++++
/// + _ + [] +
/// ++++++++++++++++++++++++++
/// + true + [First] +
/// ++++++++++++++++++++++++++
/// + true + [Second(true)] +
/// ++++++++++++++++++++++++++
/// + false + [_] +
/// ++++++++++++++++++++++++++
/// + _ + [_, _, ..tail] +
/// ++++++++++++++++++++++++++
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| format!("{:?}", pat)).collect()
}).collect();
let column_count = m.iter().map(|row| row.len()).max().unwrap_or(0);
assert!(m.iter().all(|row| row.len() == column_count));
let column_widths: Vec<usize> = (0..column_count).map(|col| {
pretty_printed_matrix.iter().map(|row| row[col].len()).max().unwrap_or(0)
}).collect();
let total_width = column_widths.iter().cloned().sum::<usize>() + column_count * 3 + 1;
let br = repeat('+').take(total_width).collect::<String>();
write!(f, "{}\n", br)?;
for row in pretty_printed_matrix {
write!(f, "+")?;
for (column, pat_str) in row.into_iter().enumerate() {
write!(f, " ")?;
write!(f, "{:1$}", pat_str, column_widths[column])?;
write!(f, " +")?;
}
write!(f, "\n")?;
write!(f, "{}\n", br)?;
}
Ok(())
}
}
impl<'a, 'tcx> FromIterator<Vec<Pattern<'a, 'tcx>>> for Matrix<'a, 'tcx> {
fn from_iter<T: IntoIterator<Item=Vec<Pattern<'a, 'tcx>>>>(iter: T) -> Self
{
Matrix(iter.into_iter().collect())
}
}
//NOTE: appears to be the only place other then InferCtxt to contain a ParamEnv
pub struct MatchCheckCtxt<'a, 'tcx: 'a> {
pub tcx: TyCtxt<'a, 'tcx, 'tcx>,
pub param_env: ty::ParameterEnvironment<'tcx>,
}
#[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.
Single,
/// Enum variants.
Variant(DefId),
/// Literal values.
ConstantValue(ConstVal),
/// Ranges of literal values (2..5).
ConstantRange(ConstVal, ConstVal),
/// Array patterns of length n.
Slice(usize),
}
impl Constructor {
fn variant_for_adt<'tcx, 'container, 'a>(&self,
adt: &'a ty::AdtDefData<'tcx, 'container>)
-> &'a ty::VariantDefData<'tcx, 'container> {
match self {
&Variant(vid) => adt.variant_with_id(vid),
_ => adt.struct_variant()
}
}
}
#[derive(Clone, PartialEq)]
pub enum Usefulness {
Useful,
UsefulWithWitness(Vec<P<Pat>>),
NotUseful
}
#[derive(Copy, Clone)]
pub enum WitnessPreference {
ConstructWitness,
LeaveOutWitness
}
fn const_val_to_expr(value: &ConstVal) -> P<hir::Expr> {
let node = match value {
&ConstVal::Bool(b) => ast::LitKind::Bool(b),
_ => bug!()
};
P(hir::Expr {
id: DUMMY_NODE_ID,
node: hir::ExprLit(P(Spanned { node: node, span: DUMMY_SP })),
span: DUMMY_SP,
attrs: ast::ThinVec::new(),
})
}
/// Constructs a partial witness for a pattern given a list of
/// patterns expanded by the specialization step.
///
/// When a pattern P is discovered to be useful, this function is used bottom-up
/// to reconstruct a complete witness, e.g. a pattern P' that covers a subset
/// of values, V, where each value in that set is not covered by any previously
/// used patterns and is covered by the pattern P'. Examples:
///
/// left_ty: tuple of 3 elements
/// pats: [10, 20, _] => (10, 20, _)
///
/// left_ty: struct X { a: (bool, &'static str), b: usize}
/// pats: [(false, "foo"), 42] => X { a: (false, "foo"), b: 42 }
fn construct_witness<'a,'tcx>(cx: &MatchCheckCtxt<'a,'tcx>, ctor: &Constructor,
pats: Vec<&Pat>, left_ty: Ty<'tcx>) -> P<Pat> {
let pats_len = pats.len();
let mut pats = pats.into_iter().map(|p| P((*p).clone()));
let pat = match left_ty.sty {
ty::TyTuple(..) => PatKind::Tuple(pats.collect(), None),
ty::TyAdt(adt, _) => {
let v = ctor.variant_for_adt(adt);
match v.ctor_kind {
CtorKind::Fictive => {
let field_pats: hir::HirVec<_> = v.fields.iter()
.zip(pats)
.filter(|&(_, ref pat)| pat.node != PatKind::Wild)
.map(|(field, pat)| Spanned {
span: DUMMY_SP,
node: hir::FieldPat {
name: field.name,
pat: pat,
is_shorthand: false,
}
}).collect();
let has_more_fields = field_pats.len() < pats_len;
PatKind::Struct(def_to_path(cx.tcx, v.did), field_pats, has_more_fields)
}
CtorKind::Fn => {
PatKind::TupleStruct(def_to_path(cx.tcx, v.did), pats.collect(), None)
}
CtorKind::Const => {
PatKind::Path(None, def_to_path(cx.tcx, v.did))
}
}
}
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::Slice(pats.collect(), None, hir::HirVec::new())
},
_ => unreachable!()
},
ty::TyArray(_, len) => {
assert_eq!(pats_len, len);
PatKind::Slice(pats.collect(), None, hir::HirVec::new())
}
_ => {
match *ctor {
ConstantValue(ref v) => PatKind::Lit(const_val_to_expr(v)),
_ => PatKind::Wild,
}
}
};
P(hir::Pat {
id: DUMMY_NODE_ID,
node: pat,
span: DUMMY_SP
})
}
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))
.collect();
all_constructors(cx, left_ty, max_slice_length)
.into_iter()
.filter(|c| !used_constructors.contains(c))
.collect()
}
/// This determines the set of all possible constructors of a pattern matching
/// values of type `left_ty`. For vectors, this would normally be an infinite set
/// but is instead bounded by the maximum fixed length of slice patterns in
/// the column of patterns being analyzed.
fn all_constructors(_cx: &MatchCheckCtxt, left_ty: Ty,
max_slice_length: usize) -> Vec<Constructor> {
match left_ty.sty {
ty::TyBool =>
[true, false].iter().map(|b| ConstantValue(ConstVal::Bool(*b))).collect(),
ty::TySlice(_) =>
(0..max_slice_length+1).map(|length| Slice(length)).collect(),
ty::TyAdt(def, _) if def.is_enum() =>
def.variants.iter().map(|v| Variant(v.did)).collect(),
_ => vec![Single]
}
}
// Algorithm from http://moscova.inria.fr/~maranget/papers/warn/index.html
//
// Whether a vector `v` of patterns is 'useful' in relation to a set of such
// vectors `m` is defined as there being a set of inputs that will match `v`
// but not any of the sets in `m`.
//
// This is used both for reachability checking (if a pattern isn't useful in
// relation to preceding patterns, it is not reachable) and exhaustiveness
// checking (if a wildcard pattern is useful in relation to a matrix, the
// matrix isn't exhaustive).
// Note: is_useful doesn't work on empty types, as the paper notes.
// So it assumes that v is non-empty.
pub fn is_useful<'a, 'tcx>(cx: &MatchCheckCtxt<'a, 'tcx>,
matrix: &Matrix<'a, 'tcx>,
v: &[Pattern<'a, 'tcx>],
witness: WitnessPreference)
-> Usefulness {
let &Matrix(ref rows) = matrix;
debug!("is_useful({:?}, {:?})", matrix, v);
if rows.is_empty() {
return match witness {
ConstructWitness => UsefulWithWitness(vec!()),
LeaveOutWitness => Useful
};
}
if rows[0].is_empty() {
return NotUseful;
}
assert!(rows.iter().all(|r| r.len() == v.len()));
let left_ty = match rows.iter().filter_map(|r| r[0].pattern_ty).next()
.or_else(|| v[0].pattern_ty)
{
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].pat.node {
PatKind::Slice(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);
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) {
UsefulWithWitness(pats) => UsefulWithWitness({
let arity = constructor_arity(cx, &c, left_ty);
let mut result = {
let pat_slice = &pats[..];
let subpats: Vec<_> = (0..arity).map(|i| {
pat_slice.get(i).map_or(DUMMY_WILD_PAT, |p| &**p)
}).collect();
vec![construct_witness(cx, &c, subpats, left_ty)]
};
result.extend(pats.into_iter().skip(arity));
result
}),
result => result
}
}).find(|result| result != &NotUseful).unwrap_or(NotUseful)
} else {
let matrix = rows.iter().filter_map(|r| {
match r[0].as_raw().node {
PatKind::Binding(..) | PatKind::Wild => Some(r[1..].to_vec()),
_ => None,
}
}).collect();
match is_useful(cx, &matrix, &v[1..], witness) {
UsefulWithWitness(pats) => {
let mut new_pats: Vec<_> = constructors.into_iter().map(|constructor| {
let arity = constructor_arity(cx, &constructor, left_ty);
let wild_pats = vec![DUMMY_WILD_PAT; arity];
construct_witness(cx, &constructor, wild_pats, left_ty)
}).collect();
new_pats.extend(pats);
UsefulWithWitness(new_pats)
},
result => result
}
}
} else {
constructors.into_iter().map(|c|
is_useful_specialized(cx, matrix, v, c.clone(), left_ty, witness)
).find(|result| result != &NotUseful).unwrap_or(NotUseful)
}
}
fn is_useful_specialized<'a, 'tcx>(
cx: &MatchCheckCtxt<'a, 'tcx>,
&Matrix(ref m): &Matrix<'a, 'tcx>,
v: &[Pattern<'a, '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)
}).collect());
match specialize(cx, v, &ctor, 0, arity) {
Some(v) => is_useful(cx, &matrix, &v[..], witness),
None => NotUseful
}
}
/// Determines the constructors that the given pattern can be specialized to.
///
/// In most cases, there's only one constructor that a specific pattern
/// represents, such as a specific enum variant or a specific literal value.
/// Slice patterns, however, can match slices of different lengths. For instance,
/// `[a, b, ..tail]` can match a slice of length 2, 3, 4 and so on.
///
/// On the other hand, a wild pattern and an identifier pattern cannot be
/// specialized in any way.
fn pat_constructors(cx: &MatchCheckCtxt, p: Pattern,
left_ty: Ty, max_slice_length: usize) -> Vec<Constructor> {
let pat = p.as_raw();
match pat.node {
PatKind::Struct(..) | PatKind::TupleStruct(..) | PatKind::Path(..) =>
match cx.tcx.expect_def(pat.id) {
Def::Variant(id) | Def::VariantCtor(id, _) => vec![Variant(id)],
Def::Struct(..) | Def::StructCtor(..) | Def::Union(..) |
Def::TyAlias(..) | Def::AssociatedTy(..) => vec![Single],
Def::Const(..) | Def::AssociatedConst(..) =>
span_bug!(p.span(), "const pattern should've been rewritten"),
def => span_bug!(p.span(), "pat_constructors: unexpected definition {:?}", def),
},
PatKind::Lit(ref expr) =>
vec![ConstantValue(eval_const_expr(cx.tcx, &expr))],
PatKind::Range(ref lo, ref hi) =>
vec![ConstantRange(eval_const_expr(cx.tcx, &lo), eval_const_expr(cx.tcx, &hi))],
PatKind::Slice(ref before, ref slice, ref after) =>
match left_ty.sty {
ty::TyArray(..) => vec![Single],
ty::TySlice(_) if slice.is_some() => {
(before.len() + after.len()..max_slice_length+1)
.map(|length| Slice(length))
.collect()
}
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],
PatKind::Binding(..) | PatKind::Wild =>
vec![],
}
}
/// This computes the arity of a constructor. The arity of a constructor
/// is how many subpattern patterns of that constructor should be expanded to.
///
/// 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::TySlice(_) => match *ctor {
Slice(length) => length,
ConstantValue(_) => {
// TODO: this is utterly wrong, but required for byte arrays
0
}
_ => bug!("bad slice pattern {:?} {:?}", ctor, ty)
},
ty::TyRef(..) => 1,
ty::TyAdt(adt, _) => {
ctor.variant_for_adt(adt).fields.len()
}
ty::TyArray(_, n) => n,
_ => 0
}
}
fn range_covered_by_constructor(tcx: TyCtxt, span: Span,
ctor: &Constructor,
from: &ConstVal, to: &ConstVal)
-> Result<bool, ErrorReported> {
let (c_from, c_to) = match *ctor {
ConstantValue(ref value) => (value, value),
ConstantRange(ref from, ref to) => (from, to),
Single => return Ok(true),
_ => bug!()
};
let cmp_from = compare_const_vals(tcx, span, c_from, from)?;
let cmp_to = compare_const_vals(tcx, span, c_to, to)?;
Ok(cmp_from != Ordering::Less && cmp_to != Ordering::Greater)
}
pub fn wrap_pat<'a, 'b, 'tcx>(cx: &MatchCheckCtxt<'b, 'tcx>,
pat: &'a Pat)
-> Pattern<'a, 'tcx>
{
let pat_ty = cx.tcx.pat_ty(pat);
Pattern {
pat: pat,
pattern_ty: Some(match pat.node {
PatKind::Binding(hir::BindByRef(..), ..) => {
pat_ty.builtin_deref(false, ty::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.
///
/// OTOH, slice patterns with a subslice pattern (..tail) can be expanded into multiple
/// different patterns.
/// Structure patterns with a partial wild pattern (Foo { a: 42, .. }) have their missing
/// fields filled with wild patterns.
fn specialize<'a, 'b, 'tcx>(
cx: &MatchCheckCtxt<'b, 'tcx>,
r: &[Pattern<'a, 'tcx>],
constructor: &Constructor, col: usize, arity: usize)
-> Option<Vec<Pattern<'a, 'tcx>>>
{
let pat = r[col].as_raw();
let &Pat {
id: pat_id, ref node, span: pat_span
} = pat;
let wpat = |pat: &'a Pat| wrap_pat(cx, pat);
let head: Option<Vec<Pattern>> = match *node {
PatKind::Binding(..) | PatKind::Wild =>
Some(vec![DUMMY_WILD_PATTERN; arity]),
PatKind::Path(..) => {
match cx.tcx.expect_def(pat_id) {
Def::Const(..) | Def::AssociatedConst(..) =>
span_bug!(pat_span, "const pattern should've \
been rewritten"),
Def::VariantCtor(id, CtorKind::Const) if *constructor != Variant(id) => None,
Def::VariantCtor(_, CtorKind::Const) |
Def::StructCtor(_, CtorKind::Const) => Some(Vec::new()),
def => span_bug!(pat_span, "specialize: unexpected \
definition {:?}", def),
}
}
PatKind::TupleStruct(_, ref args, ddpos) => {
match cx.tcx.expect_def(pat_id) {
Def::Const(..) | Def::AssociatedConst(..) =>
span_bug!(pat_span, "const pattern should've \
been rewritten"),
Def::VariantCtor(id, CtorKind::Fn) if *constructor != Variant(id) => None,
Def::VariantCtor(_, CtorKind::Fn) |
Def::StructCtor(_, CtorKind::Fn) => {
match ddpos {
Some(ddpos) => {
let mut pats: Vec<_> = args[..ddpos].iter().map(|p| {
wpat(p)
}).collect();
pats.extend(repeat(DUMMY_WILD_PATTERN).take(arity - args.len()));
pats.extend(args[ddpos..].iter().map(|p| wpat(p)));
Some(pats)
}
None => Some(args.iter().map(|p| wpat(p)).collect())
}
}
def => span_bug!(pat_span, "specialize: unexpected definition: {:?}", def)
}
}
PatKind::Struct(_, ref pattern_fields, _) => {
let adt = cx.tcx.node_id_to_type(pat_id).ty_adt_def().unwrap();
let variant = constructor.variant_for_adt(adt);
let def_variant = adt.variant_of_def(cx.tcx.expect_def(pat_id));
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) => wpat(&f.node.pat),
_ => DUMMY_WILD_PATTERN
}
}).collect())
} else {
None
}
}
PatKind::Tuple(ref args, Some(ddpos)) => {
let mut pats: Vec<_> = args[..ddpos].iter().map(|p| wpat(p)).collect();
pats.extend(repeat(DUMMY_WILD_PATTERN).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| wpat(&**p)).collect()),
PatKind::Box(ref inner) | PatKind::Ref(ref inner, _) =>
Some(vec![wpat(&**inner)]),
PatKind::Lit(ref expr) => {
match r[col].pattern_ty {
Some(&ty::TyS { sty: ty::TyRef(_, mt), .. }) => {
// 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![Pattern {
pat: pat,
pattern_ty: Some(mt.ty)
}])
}
Some(ty) => {
assert_eq!(constructor_arity(cx, constructor, ty), 0);
let expr_value = eval_const_expr(cx.tcx, &expr);
match range_covered_by_constructor(
cx.tcx, expr.span, constructor, &expr_value, &expr_value
) {
Ok(true) => Some(vec![]),
Ok(false) => None,
Err(ErrorReported) => None,
}
}
None => span_bug!(pat.span, "literal pattern {:?} has no type", pat)
}
}
PatKind::Range(ref from, ref to) => {
let from_value = eval_const_expr(cx.tcx, &from);
let to_value = eval_const_expr(cx.tcx, &to);
match range_covered_by_constructor(
cx.tcx, pat_span, constructor, &from_value, &to_value
) {
Ok(true) => Some(vec![]),
Ok(false) => None,
Err(ErrorReported) => None,
}
}
PatKind::Slice(ref before, ref slice, ref after) => {
let pat_len = before.len() + after.len();
match *constructor {
Single => {
// Fixed-length vectors.
Some(
before.iter().map(|p| wpat(p)).chain(
repeat(DUMMY_WILD_PATTERN).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_WILD_PATTERN).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())
}
_ => None
}
}
};
debug!("specialize({:?}, {:?}) = {:?}", r[col], arity, head);
head.map(|mut head| {
head.extend_from_slice(&r[..col]);
head.extend_from_slice(&r[col + 1..]);
head
})
}
pub fn is_refutable<A, F>(cx: &MatchCheckCtxt, pat: &Pat, refutable: F)
-> Option<A> where
F: FnOnce(&Pat) -> A,
{
let pats = Matrix(vec!(vec!(wrap_pat(cx, pat))));
match is_useful(cx, &pats, &[DUMMY_WILD_PATTERN], ConstructWitness) {
UsefulWithWitness(pats) => Some(refutable(&pats[0])),
NotUseful => None,
Useful => bug!()
}
}

View File

@ -8,169 +8,41 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
use self::Constructor::*; use _match::{MatchCheckCtxt, Matrix, wrap_pat, is_refutable, is_useful};
use self::Usefulness::*; use _match::{DUMMY_WILD_PATTERN, DUMMY_WILD_PAT};
use self::WitnessPreference::*; use _match::Usefulness::*;
use _match::WitnessPreference::*;
use eval::report_const_eval_err;
use eval::{eval_const_expr_partial, const_expr_to_pat, lookup_const_by_id};
use eval::EvalHint::ExprTypeChecked;
use rustc::dep_graph::DepNode; use rustc::dep_graph::DepNode;
use rustc::hir::pat_util::{pat_bindings, pat_contains_bindings};
use rustc::middle::const_val::ConstVal; use rustc::middle::const_val::ConstVal;
use ::{eval_const_expr, eval_const_expr_partial, compare_const_vals};
use ::{const_expr_to_pat, lookup_const_by_id};
use ::EvalHint::ExprTypeChecked;
use eval::report_const_eval_err;
use rustc::hir::def::*;
use rustc::hir::def_id::{DefId};
use rustc::middle::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor}; use rustc::middle::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor};
use rustc::middle::expr_use_visitor::{LoanCause, MutateMode}; use rustc::middle::expr_use_visitor::{LoanCause, MutateMode};
use rustc::middle::expr_use_visitor as euv; use rustc::middle::expr_use_visitor as euv;
use rustc::middle::mem_categorization::{cmt}; use rustc::middle::mem_categorization::{cmt};
use rustc::hir::pat_util::*;
use rustc::session::Session; use rustc::session::Session;
use rustc::traits::Reveal; use rustc::traits::Reveal;
use rustc::ty::{self, Ty, TyCtxt}; use rustc::ty::{self, TyCtxt};
use rustc_errors::DiagnosticBuilder; use rustc_errors::DiagnosticBuilder;
use std::cmp::Ordering;
use std::fmt;
use std::iter::{FromIterator, IntoIterator, repeat};
use rustc::hir; use rustc::hir::def::*;
use rustc::hir::{Pat, PatKind};
use rustc::hir::intravisit::{self, Visitor, FnKind}; use rustc::hir::intravisit::{self, Visitor, FnKind};
use rustc::hir::print::pat_to_string;
use rustc::hir::{self, Pat, PatKind};
use rustc_back::slice; use rustc_back::slice;
use syntax::ast::{self, DUMMY_NODE_ID, NodeId}; use syntax::ast;
use syntax::codemap::Spanned; use syntax::codemap::Spanned;
use syntax_pos::{Span, DUMMY_SP};
use rustc::hir::print::pat_to_string;
use syntax::ptr::P; use syntax::ptr::P;
use syntax::util::move_map::MoveMap; use syntax::util::move_map::MoveMap;
use rustc::util::common::ErrorReported; use syntax_pos::Span;
pub const DUMMY_WILD_PAT: &'static Pat = &Pat {
id: DUMMY_NODE_ID,
node: PatKind::Wild,
span: DUMMY_SP
};
pub const DUMMY_WILD_PATTERN : Pattern<'static, 'static> = Pattern {
pat: DUMMY_WILD_PAT,
pattern_ty: None
};
#[derive(Copy, Clone)]
pub struct Pattern<'a, 'tcx> {
pat: &'a Pat,
pattern_ty: Option<Ty<'tcx>>
}
impl<'a, 'tcx> Pattern<'a, 'tcx> {
fn as_raw(self) -> &'a Pat {
let mut pat = self.pat;
while let PatKind::Binding(.., Some(ref s)) = pat.node {
pat = s;
}
return pat;
}
fn span(self) -> Span {
self.pat.span
}
}
struct Matrix<'a, 'tcx>(Vec<Vec<Pattern<'a, 'tcx>>>);
impl<'a, 'tcx> fmt::Debug for Pattern<'a, 'tcx> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}: {:?}", pat_to_string(self.pat), self.pattern_ty)
}
}
/// Pretty-printer for matrices of patterns, example:
/// ++++++++++++++++++++++++++
/// + _ + [] +
/// ++++++++++++++++++++++++++
/// + true + [First] +
/// ++++++++++++++++++++++++++
/// + true + [Second(true)] +
/// ++++++++++++++++++++++++++
/// + false + [_] +
/// ++++++++++++++++++++++++++
/// + _ + [_, _, ..tail] +
/// ++++++++++++++++++++++++++
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| format!("{:?}", pat)).collect()
}).collect();
let column_count = m.iter().map(|row| row.len()).max().unwrap_or(0);
assert!(m.iter().all(|row| row.len() == column_count));
let column_widths: Vec<usize> = (0..column_count).map(|col| {
pretty_printed_matrix.iter().map(|row| row[col].len()).max().unwrap_or(0)
}).collect();
let total_width = column_widths.iter().cloned().sum::<usize>() + column_count * 3 + 1;
let br = repeat('+').take(total_width).collect::<String>();
write!(f, "{}\n", br)?;
for row in pretty_printed_matrix {
write!(f, "+")?;
for (column, pat_str) in row.into_iter().enumerate() {
write!(f, " ")?;
write!(f, "{:1$}", pat_str, column_widths[column])?;
write!(f, " +")?;
}
write!(f, "\n")?;
write!(f, "{}\n", br)?;
}
Ok(())
}
}
impl<'a, 'tcx> FromIterator<Vec<Pattern<'a, 'tcx>>> for Matrix<'a, 'tcx> {
fn from_iter<T: IntoIterator<Item=Vec<Pattern<'a, 'tcx>>>>(iter: T) -> Self
{
Matrix(iter.into_iter().collect())
}
}
//NOTE: appears to be the only place other then InferCtxt to contain a ParamEnv
pub struct MatchCheckCtxt<'a, 'tcx: 'a> {
pub tcx: TyCtxt<'a, 'tcx, 'tcx>,
pub param_env: ty::ParameterEnvironment<'tcx>,
}
#[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.
Single,
/// Enum variants.
Variant(DefId),
/// Literal values.
ConstantValue(ConstVal),
/// Ranges of literal values (2..5).
ConstantRange(ConstVal, ConstVal),
/// Array patterns of length n.
Slice(usize),
}
#[derive(Clone, PartialEq)]
enum Usefulness {
Useful,
UsefulWithWitness(Vec<P<Pat>>),
NotUseful
}
#[derive(Copy, Clone)]
enum WitnessPreference {
ConstructWitness,
LeaveOutWitness
}
impl<'a, 'tcx, 'v> Visitor<'v> for MatchCheckCtxt<'a, 'tcx> { impl<'a, 'tcx, 'v> Visitor<'v> for MatchCheckCtxt<'a, 'tcx> {
fn visit_expr(&mut self, ex: &hir::Expr) { fn visit_expr(&mut self, ex: &hir::Expr) {
@ -180,7 +52,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for MatchCheckCtxt<'a, 'tcx> {
check_local(self, l); check_local(self, l);
} }
fn visit_fn(&mut self, fk: FnKind<'v>, fd: &'v hir::FnDecl, fn visit_fn(&mut self, fk: FnKind<'v>, fd: &'v hir::FnDecl,
b: &'v hir::Block, s: Span, n: NodeId) { b: &'v hir::Block, s: Span, n: ast::NodeId) {
check_fn(self, fk, fd, b, s, n); check_fn(self, fk, fd, b, s, n);
} }
} }
@ -341,7 +213,7 @@ fn pat_is_catchall(dm: &DefMap, pat: &Pat) -> bool {
fn check_arms(cx: &MatchCheckCtxt, fn check_arms(cx: &MatchCheckCtxt,
arms: &[(Vec<P<Pat>>, Option<&hir::Expr>)], arms: &[(Vec<P<Pat>>, Option<&hir::Expr>)],
source: hir::MatchSource) { source: hir::MatchSource) {
let mut seen = Matrix(vec![]); let mut seen = Matrix::empty();
let mut catchall = None; let mut catchall = None;
let mut printed_if_let_err = false; let mut printed_if_let_err = false;
for &(ref pats, guard) in arms { for &(ref pats, guard) in arms {
@ -390,7 +262,7 @@ fn check_arms(cx: &MatchCheckCtxt,
hir::MatchSource::Normal => { hir::MatchSource::Normal => {
let mut err = struct_span_err!(cx.tcx.sess, pat.span, E0001, let mut err = struct_span_err!(cx.tcx.sess, pat.span, E0001,
"unreachable pattern"); "unreachable pattern");
err.span_label(pat.span, &format!("this is an unreachable pattern")); err.span_label(pat.span, &"this is an unreachable pattern");
// if we had a catchall pattern, hint at that // if we had a catchall pattern, hint at that
if let Some(catchall) = catchall { if let Some(catchall) = catchall {
err.span_note(catchall, "this pattern matches any value"); err.span_note(catchall, "this pattern matches any value");
@ -407,13 +279,10 @@ fn check_arms(cx: &MatchCheckCtxt,
UsefulWithWitness(_) => bug!() UsefulWithWitness(_) => bug!()
} }
if guard.is_none() { if guard.is_none() {
let Matrix(mut rows) = seen; seen.push(v);
rows.push(v);
seen = Matrix(rows);
if catchall.is_none() && pat_is_catchall(&cx.tcx.def_map.borrow(), pat) { if catchall.is_none() && pat_is_catchall(&cx.tcx.def_map.borrow(), pat) {
catchall = Some(pat.span); catchall = Some(pat.span);
} }
} }
} }
} }
@ -485,18 +354,6 @@ fn check_exhaustive<'a, 'tcx>(cx: &MatchCheckCtxt<'a, 'tcx>,
} }
} }
fn const_val_to_expr(value: &ConstVal) -> P<hir::Expr> {
let node = match value {
&ConstVal::Bool(b) => ast::LitKind::Bool(b),
_ => bug!()
};
P(hir::Expr {
id: DUMMY_NODE_ID,
node: hir::ExprLit(P(Spanned { node: node, span: DUMMY_SP })),
span: DUMMY_SP,
attrs: ast::ThinVec::new(),
})
}
struct StaticInliner<'a, 'tcx: 'a> { struct StaticInliner<'a, 'tcx: 'a> {
tcx: TyCtxt<'a, 'tcx, 'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>,
@ -588,505 +445,6 @@ impl<'a, 'tcx> StaticInliner<'a, 'tcx> {
} }
} }
/// Constructs a partial witness for a pattern given a list of
/// patterns expanded by the specialization step.
///
/// When a pattern P is discovered to be useful, this function is used bottom-up
/// to reconstruct a complete witness, e.g. a pattern P' that covers a subset
/// of values, V, where each value in that set is not covered by any previously
/// used patterns and is covered by the pattern P'. Examples:
///
/// left_ty: tuple of 3 elements
/// pats: [10, 20, _] => (10, 20, _)
///
/// left_ty: struct X { a: (bool, &'static str), b: usize}
/// pats: [(false, "foo"), 42] => X { a: (false, "foo"), b: 42 }
fn construct_witness<'a,'tcx>(cx: &MatchCheckCtxt<'a,'tcx>, ctor: &Constructor,
pats: Vec<&Pat>, left_ty: Ty<'tcx>) -> P<Pat> {
let pats_len = pats.len();
let mut pats = pats.into_iter().map(|p| P((*p).clone()));
let pat = match left_ty.sty {
ty::TyTuple(..) => PatKind::Tuple(pats.collect(), None),
ty::TyAdt(adt, _) => {
let v = ctor.variant_for_adt(adt);
match v.ctor_kind {
CtorKind::Fictive => {
let field_pats: hir::HirVec<_> = v.fields.iter()
.zip(pats)
.filter(|&(_, ref pat)| pat.node != PatKind::Wild)
.map(|(field, pat)| Spanned {
span: DUMMY_SP,
node: hir::FieldPat {
name: field.name,
pat: pat,
is_shorthand: false,
}
}).collect();
let has_more_fields = field_pats.len() < pats_len;
PatKind::Struct(def_to_path(cx.tcx, v.did), field_pats, has_more_fields)
}
CtorKind::Fn => {
PatKind::TupleStruct(def_to_path(cx.tcx, v.did), pats.collect(), None)
}
CtorKind::Const => {
PatKind::Path(None, def_to_path(cx.tcx, v.did))
}
}
}
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::Slice(pats.collect(), None, hir::HirVec::new())
},
_ => unreachable!()
},
ty::TyArray(_, len) => {
assert_eq!(pats_len, len);
PatKind::Slice(pats.collect(), None, hir::HirVec::new())
}
_ => {
match *ctor {
ConstantValue(ref v) => PatKind::Lit(const_val_to_expr(v)),
_ => PatKind::Wild,
}
}
};
P(hir::Pat {
id: DUMMY_NODE_ID,
node: pat,
span: DUMMY_SP
})
}
impl Constructor {
fn variant_for_adt<'tcx, 'container, 'a>(&self,
adt: &'a ty::AdtDefData<'tcx, 'container>)
-> &'a ty::VariantDefData<'tcx, 'container> {
match self {
&Variant(vid) => adt.variant_with_id(vid),
_ => adt.struct_variant()
}
}
}
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))
.collect();
all_constructors(cx, left_ty, max_slice_length)
.into_iter()
.filter(|c| !used_constructors.contains(c))
.collect()
}
/// This determines the set of all possible constructors of a pattern matching
/// values of type `left_ty`. For vectors, this would normally be an infinite set
/// but is instead bounded by the maximum fixed length of slice patterns in
/// the column of patterns being analyzed.
fn all_constructors(_cx: &MatchCheckCtxt, left_ty: Ty,
max_slice_length: usize) -> Vec<Constructor> {
match left_ty.sty {
ty::TyBool =>
[true, false].iter().map(|b| ConstantValue(ConstVal::Bool(*b))).collect(),
ty::TySlice(_) =>
(0..max_slice_length+1).map(|length| Slice(length)).collect(),
ty::TyAdt(def, _) if def.is_enum() =>
def.variants.iter().map(|v| Variant(v.did)).collect(),
_ => vec![Single]
}
}
// Algorithm from http://moscova.inria.fr/~maranget/papers/warn/index.html
//
// Whether a vector `v` of patterns is 'useful' in relation to a set of such
// vectors `m` is defined as there being a set of inputs that will match `v`
// but not any of the sets in `m`.
//
// This is used both for reachability checking (if a pattern isn't useful in
// relation to preceding patterns, it is not reachable) and exhaustiveness
// checking (if a wildcard pattern is useful in relation to a matrix, the
// matrix isn't exhaustive).
// Note: is_useful doesn't work on empty types, as the paper notes.
// So it assumes that v is non-empty.
fn is_useful<'a, 'tcx>(cx: &MatchCheckCtxt<'a, 'tcx>,
matrix: &Matrix<'a, 'tcx>,
v: &[Pattern<'a, 'tcx>],
witness: WitnessPreference)
-> Usefulness {
let &Matrix(ref rows) = matrix;
debug!("is_useful({:?}, {:?})", matrix, v);
if rows.is_empty() {
return match witness {
ConstructWitness => UsefulWithWitness(vec!()),
LeaveOutWitness => Useful
};
}
if rows[0].is_empty() {
return NotUseful;
}
assert!(rows.iter().all(|r| r.len() == v.len()));
let left_ty = match rows.iter().filter_map(|r| r[0].pattern_ty).next()
.or_else(|| v[0].pattern_ty)
{
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].pat.node {
PatKind::Slice(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);
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) {
UsefulWithWitness(pats) => UsefulWithWitness({
let arity = constructor_arity(cx, &c, left_ty);
let mut result = {
let pat_slice = &pats[..];
let subpats: Vec<_> = (0..arity).map(|i| {
pat_slice.get(i).map_or(DUMMY_WILD_PAT, |p| &**p)
}).collect();
vec![construct_witness(cx, &c, subpats, left_ty)]
};
result.extend(pats.into_iter().skip(arity));
result
}),
result => result
}
}).find(|result| result != &NotUseful).unwrap_or(NotUseful)
} else {
let matrix = rows.iter().filter_map(|r| {
match r[0].as_raw().node {
PatKind::Binding(..) | PatKind::Wild => Some(r[1..].to_vec()),
_ => None,
}
}).collect();
match is_useful(cx, &matrix, &v[1..], witness) {
UsefulWithWitness(pats) => {
let mut new_pats: Vec<_> = constructors.into_iter().map(|constructor| {
let arity = constructor_arity(cx, &constructor, left_ty);
let wild_pats = vec![DUMMY_WILD_PAT; arity];
construct_witness(cx, &constructor, wild_pats, left_ty)
}).collect();
new_pats.extend(pats);
UsefulWithWitness(new_pats)
},
result => result
}
}
} else {
constructors.into_iter().map(|c|
is_useful_specialized(cx, matrix, v, c.clone(), left_ty, witness)
).find(|result| result != &NotUseful).unwrap_or(NotUseful)
}
}
fn is_useful_specialized<'a, 'tcx>(
cx: &MatchCheckCtxt<'a, 'tcx>,
&Matrix(ref m): &Matrix<'a, 'tcx>,
v: &[Pattern<'a, '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)
}).collect());
match specialize(cx, v, &ctor, 0, arity) {
Some(v) => is_useful(cx, &matrix, &v[..], witness),
None => NotUseful
}
}
/// Determines the constructors that the given pattern can be specialized to.
///
/// In most cases, there's only one constructor that a specific pattern
/// represents, such as a specific enum variant or a specific literal value.
/// Slice patterns, however, can match slices of different lengths. For instance,
/// `[a, b, ..tail]` can match a slice of length 2, 3, 4 and so on.
///
/// On the other hand, a wild pattern and an identifier pattern cannot be
/// specialized in any way.
fn pat_constructors(cx: &MatchCheckCtxt, p: Pattern,
left_ty: Ty, max_slice_length: usize) -> Vec<Constructor> {
let pat = p.as_raw();
match pat.node {
PatKind::Struct(..) | PatKind::TupleStruct(..) | PatKind::Path(..) =>
match cx.tcx.expect_def(pat.id) {
Def::Variant(id) | Def::VariantCtor(id, ..) => vec![Variant(id)],
Def::Struct(..) | Def::StructCtor(..) | Def::Union(..) |
Def::TyAlias(..) | Def::AssociatedTy(..) => vec![Single],
Def::Const(..) | Def::AssociatedConst(..) =>
span_bug!(p.span(), "const pattern should've been rewritten"),
def => span_bug!(p.span(), "pat_constructors: unexpected definition {:?}", def),
},
PatKind::Lit(ref expr) =>
vec![ConstantValue(eval_const_expr(cx.tcx, &expr))],
PatKind::Range(ref lo, ref hi) =>
vec![ConstantRange(eval_const_expr(cx.tcx, &lo), eval_const_expr(cx.tcx, &hi))],
PatKind::Slice(ref before, ref slice, ref after) =>
match left_ty.sty {
ty::TyArray(..) => vec![Single],
ty::TySlice(_) if slice.is_some() => {
(before.len() + after.len()..max_slice_length+1)
.map(|length| Slice(length))
.collect()
}
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],
PatKind::Binding(..) | PatKind::Wild =>
vec![],
}
}
/// This computes the arity of a constructor. The arity of a constructor
/// is how many subpattern patterns of that constructor should be expanded to.
///
/// 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::TySlice(_) => match *ctor {
Slice(length) => length,
ConstantValue(_) => {
// TODO: this is utterly wrong, but required for byte arrays
0
}
_ => bug!("bad slice pattern {:?} {:?}", ctor, ty)
},
ty::TyRef(..) => 1,
ty::TyAdt(adt, _) => {
ctor.variant_for_adt(adt).fields.len()
}
ty::TyArray(_, n) => n,
_ => 0
}
}
fn range_covered_by_constructor(tcx: TyCtxt, span: Span,
ctor: &Constructor,
from: &ConstVal, to: &ConstVal)
-> Result<bool, ErrorReported> {
let (c_from, c_to) = match *ctor {
ConstantValue(ref value) => (value, value),
ConstantRange(ref from, ref to) => (from, to),
Single => return Ok(true),
_ => bug!()
};
let cmp_from = compare_const_vals(tcx, span, c_from, from)?;
let cmp_to = compare_const_vals(tcx, span, c_to, to)?;
Ok(cmp_from != Ordering::Less && cmp_to != Ordering::Greater)
}
fn wrap_pat<'a, 'b, 'tcx>(cx: &MatchCheckCtxt<'b, 'tcx>,
pat: &'a Pat)
-> Pattern<'a, 'tcx>
{
let pat_ty = cx.tcx.pat_ty(pat);
Pattern {
pat: pat,
pattern_ty: Some(match pat.node {
PatKind::Binding(hir::BindByRef(..), ..) => {
pat_ty.builtin_deref(false, ty::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.
///
/// OTOH, slice patterns with a subslice pattern (..tail) can be expanded into multiple
/// different patterns.
/// Structure patterns with a partial wild pattern (Foo { a: 42, .. }) have their missing
/// fields filled with wild patterns.
pub fn specialize<'a, 'b, 'tcx>(
cx: &MatchCheckCtxt<'b, 'tcx>,
r: &[Pattern<'a, 'tcx>],
constructor: &Constructor, col: usize, arity: usize)
-> Option<Vec<Pattern<'a, 'tcx>>>
{
let pat = r[col].as_raw();
let &Pat {
id: pat_id, ref node, span: pat_span
} = pat;
let wpat = |pat: &'a Pat| wrap_pat(cx, pat);
let head: Option<Vec<Pattern>> = match *node {
PatKind::Binding(..) | PatKind::Wild =>
Some(vec![DUMMY_WILD_PATTERN; arity]),
PatKind::Path(..) => {
match cx.tcx.expect_def(pat_id) {
Def::Const(..) | Def::AssociatedConst(..) =>
span_bug!(pat_span, "const pattern should've \
been rewritten"),
Def::VariantCtor(id, CtorKind::Const) if *constructor != Variant(id) => None,
Def::VariantCtor(_, CtorKind::Const) |
Def::StructCtor(_, CtorKind::Const) => Some(Vec::new()),
def => span_bug!(pat_span, "specialize: unexpected definition: {:?}", def),
}
}
PatKind::TupleStruct(_, ref args, ddpos) => {
match cx.tcx.expect_def(pat_id) {
Def::Const(..) | Def::AssociatedConst(..) =>
span_bug!(pat_span, "const pattern should've \
been rewritten"),
Def::VariantCtor(id, CtorKind::Fn) if *constructor != Variant(id) => None,
Def::VariantCtor(_, CtorKind::Fn) |
Def::StructCtor(_, CtorKind::Fn) => {
match ddpos {
Some(ddpos) => {
let mut pats: Vec<_> = args[..ddpos].iter().map(|p| {
wpat(p)
}).collect();
pats.extend(repeat(DUMMY_WILD_PATTERN).take(arity - args.len()));
pats.extend(args[ddpos..].iter().map(|p| wpat(p)));
Some(pats)
}
None => Some(args.iter().map(|p| wpat(p)).collect())
}
}
def => span_bug!(pat_span, "specialize: unexpected definition: {:?}", def),
}
}
PatKind::Struct(_, ref pattern_fields, _) => {
let adt = cx.tcx.node_id_to_type(pat_id).ty_adt_def().unwrap();
let variant = constructor.variant_for_adt(adt);
let def_variant = adt.variant_of_def(cx.tcx.expect_def(pat_id));
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) => wpat(&f.node.pat),
_ => DUMMY_WILD_PATTERN
}
}).collect())
} else {
None
}
}
PatKind::Tuple(ref args, Some(ddpos)) => {
let mut pats: Vec<_> = args[..ddpos].iter().map(|p| wpat(p)).collect();
pats.extend(repeat(DUMMY_WILD_PATTERN).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| wpat(&**p)).collect()),
PatKind::Box(ref inner) | PatKind::Ref(ref inner, _) =>
Some(vec![wpat(&**inner)]),
PatKind::Lit(ref expr) => {
match r[col].pattern_ty {
Some(&ty::TyS { sty: ty::TyRef(_, mt), .. }) => {
// 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![Pattern {
pat: pat,
pattern_ty: Some(mt.ty)
}])
}
Some(ty) => {
assert_eq!(constructor_arity(cx, constructor, ty), 0);
let expr_value = eval_const_expr(cx.tcx, &expr);
match range_covered_by_constructor(
cx.tcx, expr.span, constructor, &expr_value, &expr_value
) {
Ok(true) => Some(vec![]),
Ok(false) => None,
Err(ErrorReported) => None,
}
}
None => span_bug!(pat.span, "literal pattern {:?} has no type", pat)
}
}
PatKind::Range(ref from, ref to) => {
let from_value = eval_const_expr(cx.tcx, &from);
let to_value = eval_const_expr(cx.tcx, &to);
match range_covered_by_constructor(
cx.tcx, pat_span, constructor, &from_value, &to_value
) {
Ok(true) => Some(vec![]),
Ok(false) => None,
Err(ErrorReported) => None,
}
}
PatKind::Slice(ref before, ref slice, ref after) => {
let pat_len = before.len() + after.len();
match *constructor {
Single => {
// Fixed-length vectors.
Some(
before.iter().map(|p| wpat(p)).chain(
repeat(DUMMY_WILD_PATTERN).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_WILD_PATTERN).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())
}
_ => None
}
}
};
debug!("specialize({:?}, {:?}) = {:?}", r[col], arity, head);
head.map(|mut head| {
head.extend_from_slice(&r[..col]);
head.extend_from_slice(&r[col + 1..]);
head
})
}
fn check_local(cx: &mut MatchCheckCtxt, loc: &hir::Local) { fn check_local(cx: &mut MatchCheckCtxt, loc: &hir::Local) {
intravisit::walk_local(cx, loc); intravisit::walk_local(cx, loc);
@ -1103,7 +461,7 @@ fn check_fn(cx: &mut MatchCheckCtxt,
decl: &hir::FnDecl, decl: &hir::FnDecl,
body: &hir::Block, body: &hir::Block,
sp: Span, sp: Span,
fn_id: NodeId) { fn_id: ast::NodeId) {
match kind { match kind {
FnKind::Closure(_) => {} FnKind::Closure(_) => {}
_ => cx.param_env = ty::ParameterEnvironment::for_item(cx.tcx, fn_id), _ => cx.param_env = ty::ParameterEnvironment::for_item(cx.tcx, fn_id),
@ -1135,17 +493,6 @@ 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!(wrap_pat(cx, pat))));
match is_useful(cx, &pats, &[DUMMY_WILD_PATTERN], ConstructWitness) {
UsefulWithWitness(pats) => Some(refutable(&pats[0])),
NotUseful => None,
Useful => bug!()
}
}
// Legality of move bindings checking // Legality of move bindings checking
fn check_legality_of_move_bindings(cx: &MatchCheckCtxt, fn check_legality_of_move_bindings(cx: &MatchCheckCtxt,
has_guard: bool, has_guard: bool,
@ -1219,10 +566,10 @@ struct MutationChecker<'a, 'gcx: 'a> {
impl<'a, 'gcx, 'tcx> Delegate<'tcx> for MutationChecker<'a, 'gcx> { impl<'a, 'gcx, 'tcx> Delegate<'tcx> for MutationChecker<'a, 'gcx> {
fn matched_pat(&mut self, _: &Pat, _: cmt, _: euv::MatchMode) {} fn matched_pat(&mut self, _: &Pat, _: cmt, _: euv::MatchMode) {}
fn consume(&mut self, _: NodeId, _: Span, _: cmt, _: ConsumeMode) {} fn consume(&mut self, _: ast::NodeId, _: Span, _: cmt, _: ConsumeMode) {}
fn consume_pat(&mut self, _: &Pat, _: cmt, _: ConsumeMode) {} fn consume_pat(&mut self, _: &Pat, _: cmt, _: ConsumeMode) {}
fn borrow(&mut self, fn borrow(&mut self,
_: NodeId, _: ast::NodeId,
span: Span, span: Span,
_: cmt, _: cmt,
_: &'tcx ty::Region, _: &'tcx ty::Region,
@ -1238,8 +585,8 @@ impl<'a, 'gcx, 'tcx> Delegate<'tcx> for MutationChecker<'a, 'gcx> {
ty::ImmBorrow | ty::UniqueImmBorrow => {} ty::ImmBorrow | ty::UniqueImmBorrow => {}
} }
} }
fn decl_without_init(&mut self, _: NodeId, _: Span) {} fn decl_without_init(&mut self, _: ast::NodeId, _: Span) {}
fn mutate(&mut self, _: NodeId, span: Span, _: cmt, mode: MutateMode) { fn mutate(&mut self, _: ast::NodeId, span: Span, _: cmt, mode: MutateMode) {
match mode { match mode {
MutateMode::JustWrite | MutateMode::WriteAndRead => { MutateMode::JustWrite | MutateMode::WriteAndRead => {
struct_span_err!(self.cx.tcx.sess, span, E0302, "cannot assign in a pattern guard") struct_span_err!(self.cx.tcx.sess, span, E0302, "cannot assign in a pattern guard")

View File

@ -40,7 +40,7 @@ Ensure the ordering of the match arm is correct and remove any superfluous
arms. arms.
"##, "##,
E0002: r##" /*E0002: r##"
This error indicates that an empty match expression is invalid because the type This error indicates that an empty match expression is invalid because the type
it is matching on is non-empty (there exist values of this type). In safe code it is matching on is non-empty (there exist values of this type). In safe code
it is impossible to create an instance of an empty type, so empty match it is impossible to create an instance of an empty type, so empty match
@ -68,7 +68,7 @@ fn foo(x: Option<String>) {
} }
} }
``` ```
"##, "##,*/
E0003: r##" E0003: r##"

View File

@ -47,6 +47,7 @@ extern crate serialize as rustc_serialize; // used by deriving
pub mod diagnostics; pub mod diagnostics;
mod eval; mod eval;
mod _match;
pub mod check_match; pub mod check_match;
pub mod pattern; pub mod pattern;