More pattern matching for empty types changes

Fix is_uninhabited for enum types. It used to assume that an enums variant's
fields were all private.

Fix MIR generation for irrefutable Variant pattern matches. This allows code
like this to work:

    let x: Result<32, !> = Ok(123);
    let Ok(y) = x;

Carry type information on dummy wildcard patterns. Sometimes we need to expand
these patterns into their constructors and we don't want to be expanding a
TyError into a Constructor::Single.
This commit is contained in:
Andrew Cann 2016-12-01 01:12:03 +08:00
parent bcdbe942e1
commit 9c5e86d0cd
8 changed files with 237 additions and 96 deletions

View File

@ -888,6 +888,10 @@ impl<'tcx> Lvalue<'tcx> {
self.elem(ProjectionElem::Deref) self.elem(ProjectionElem::Deref)
} }
pub fn downcast(self, adt_def: AdtDef<'tcx>, variant_index: usize) -> Lvalue<'tcx> {
self.elem(ProjectionElem::Downcast(adt_def, variant_index))
}
pub fn index(self, index: Operand<'tcx>) -> Lvalue<'tcx> { pub fn index(self, index: Operand<'tcx>) -> Lvalue<'tcx> {
self.elem(ProjectionElem::Index(index)) self.elem(ProjectionElem::Index(index))
} }

View File

@ -1416,7 +1416,7 @@ impl<'a, 'gcx, 'tcx> AdtDef {
return false; return false;
}; };
self.variants.iter().all(|v| { self.variants.iter().all(|v| {
v.is_uninhabited_recurse(visited, block, tcx, substs, self.is_union()) v.is_uninhabited_recurse(visited, block, tcx, substs, self.adt_kind())
}) })
} }
@ -1761,11 +1761,23 @@ impl<'a, 'gcx, 'tcx> VariantDef {
block: Option<NodeId>, block: Option<NodeId>,
tcx: TyCtxt<'a, 'gcx, 'tcx>, tcx: TyCtxt<'a, 'gcx, 'tcx>,
substs: &'tcx Substs<'tcx>, substs: &'tcx Substs<'tcx>,
is_union: bool) -> bool { adt_kind: AdtKind) -> bool {
if is_union { match adt_kind {
self.fields.iter().all(|f| f.is_uninhabited_recurse(visited, block, tcx, substs)) AdtKind::Union => {
} else { self.fields.iter().all(|f| {
self.fields.iter().any(|f| f.is_uninhabited_recurse(visited, block, tcx, substs)) f.is_uninhabited_recurse(visited, block, tcx, substs, false)
})
},
AdtKind::Struct => {
self.fields.iter().any(|f| {
f.is_uninhabited_recurse(visited, block, tcx, substs, false)
})
},
AdtKind::Enum => {
self.fields.iter().any(|f| {
f.is_uninhabited_recurse(visited, block, tcx, substs, true)
})
},
} }
} }
} }
@ -1780,9 +1792,12 @@ impl<'a, 'gcx, 'tcx> FieldDef {
visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>, visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>,
block: Option<NodeId>, block: Option<NodeId>,
tcx: TyCtxt<'a, 'gcx, 'tcx>, tcx: TyCtxt<'a, 'gcx, 'tcx>,
substs: &'tcx Substs<'tcx>) -> bool { substs: &'tcx Substs<'tcx>,
block.map_or(true, |b| tcx.vis_is_accessible_from(self.vis, b)) && is_enum: bool) -> bool {
self.ty(tcx, substs).is_uninhabited_recurse(visited, block, tcx) let visible = is_enum || block.map_or(true, |b| {
tcx.vis_is_accessible_from(self.vis, b)
});
visible && self.ty(tcx, substs).is_uninhabited_recurse(visited, block, tcx)
} }
} }

View File

@ -24,7 +24,7 @@ use pattern::{FieldPattern, Pattern, PatternKind};
use pattern::{PatternFoldable, PatternFolder}; use pattern::{PatternFoldable, PatternFolder};
use rustc::hir::def_id::DefId; use rustc::hir::def_id::DefId;
use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; use rustc::ty::{self, AdtKind, Ty, TyCtxt, TypeFoldable};
use rustc::mir::Field; use rustc::mir::Field;
use rustc::util::common::ErrorReported; use rustc::util::common::ErrorReported;
@ -153,9 +153,6 @@ pub struct MatchCheckCtxt<'a, 'tcx: 'a> {
/// can not be seen to be empty outside it's module and should not /// can not be seen to be empty outside it's module and should not
/// be matchable with an empty match statement. /// be matchable with an empty match statement.
pub node: NodeId, pub node: NodeId,
/// A wild pattern with an error type - it exists to avoid having to normalize
/// associated types to get field types.
pub wild_pattern: &'a Pattern<'tcx>,
pub pattern_arena: &'a TypedArena<Pattern<'tcx>>, pub pattern_arena: &'a TypedArena<Pattern<'tcx>>,
pub byte_array_map: FxHashMap<*const Pattern<'tcx>, Vec<&'a Pattern<'tcx>>>, pub byte_array_map: FxHashMap<*const Pattern<'tcx>, Vec<&'a Pattern<'tcx>>>,
} }
@ -167,25 +164,20 @@ impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> {
f: F) -> R f: F) -> R
where F: for<'b> FnOnce(MatchCheckCtxt<'b, 'tcx>) -> R where F: for<'b> FnOnce(MatchCheckCtxt<'b, 'tcx>) -> R
{ {
let wild_pattern = Pattern {
ty: tcx.types.err,
span: DUMMY_SP,
kind: box PatternKind::Wild
};
let pattern_arena = TypedArena::new(); let pattern_arena = TypedArena::new();
f(MatchCheckCtxt { f(MatchCheckCtxt {
tcx: tcx, tcx: tcx,
node: node, node: node,
wild_pattern: &wild_pattern,
pattern_arena: &pattern_arena, pattern_arena: &pattern_arena,
byte_array_map: FxHashMap(), byte_array_map: FxHashMap(),
}) })
} }
// convert a byte-string pattern to a list of u8 patterns. // convert a byte-string pattern to a list of u8 patterns.
fn lower_byte_str_pattern(&mut self, pat: &'a Pattern<'tcx>) -> Vec<&'a Pattern<'tcx>> { fn lower_byte_str_pattern<'p>(&mut self, pat: &'p Pattern<'tcx>) -> Vec<&'p Pattern<'tcx>>
where 'a: 'p
{
let pattern_arena = &*self.pattern_arena; let pattern_arena = &*self.pattern_arena;
let tcx = self.tcx; let tcx = self.tcx;
self.byte_array_map.entry(pat).or_insert_with(|| { self.byte_array_map.entry(pat).or_insert_with(|| {
@ -401,6 +393,7 @@ fn missing_constructors<'a, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
fn all_constructors<'a, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>, fn all_constructors<'a, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
pcx: PatternContext<'tcx>) -> Vec<Constructor> pcx: PatternContext<'tcx>) -> Vec<Constructor>
{ {
debug!("all_constructors({:?})", pcx.ty);
match pcx.ty.sty { match pcx.ty.sty {
ty::TyBool => ty::TyBool =>
[true, false].iter().map(|b| ConstantValue(ConstVal::Bool(*b))).collect(), [true, false].iter().map(|b| ConstantValue(ConstVal::Bool(*b))).collect(),
@ -421,7 +414,10 @@ fn all_constructors<'a, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
ty::TyAdt(def, substs) if def.is_enum() && def.variants.len() != 1 => { ty::TyAdt(def, substs) if def.is_enum() && def.variants.len() != 1 => {
def.variants.iter().filter_map(|v| { def.variants.iter().filter_map(|v| {
let mut visited = FxHashSet::default(); let mut visited = FxHashSet::default();
if v.is_uninhabited_recurse(&mut visited, Some(cx.node), cx.tcx, substs, false) { if v.is_uninhabited_recurse(&mut visited,
Some(cx.node),
cx.tcx, substs,
AdtKind::Enum) {
None None
} else { } else {
Some(Variant(v.did)) Some(Variant(v.did))
@ -438,10 +434,10 @@ fn all_constructors<'a, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
} }
} }
fn max_slice_length<'a, 'tcx, I>( fn max_slice_length<'p, 'a: 'p, 'tcx: 'a, I>(
_cx: &mut MatchCheckCtxt<'a, 'tcx>, _cx: &mut MatchCheckCtxt<'a, 'tcx>,
patterns: I) -> usize patterns: I) -> usize
where I: Iterator<Item=&'a Pattern<'tcx>> where I: Iterator<Item=&'p Pattern<'tcx>>
{ {
// The exhaustiveness-checking paper does not include any details on // The exhaustiveness-checking paper does not include any details on
// checking variable-length slice patterns. However, they are matched // checking variable-length slice patterns. However, they are matched
@ -532,6 +528,12 @@ fn max_slice_length<'a, 'tcx, I>(
} }
/// Algorithm from http://moscova.inria.fr/~maranget/papers/warn/index.html /// Algorithm from http://moscova.inria.fr/~maranget/papers/warn/index.html
/// The algorithm from the paper has been modified to correctly handle empty
/// types. The changes are:
/// (0) We don't exit early if the pattern matrix has zero rows. We just
/// continue to recurse over columns.
/// (1) all_constructors will only return constructors that are statically
/// possible. eg. it will only return Ok for Result<T, !>
/// ///
/// Whether a vector `v` of patterns is 'useful' in relation to a set of such /// 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` /// vectors `m` is defined as there being a set of inputs that will match `v`
@ -541,12 +543,9 @@ fn max_slice_length<'a, 'tcx, I>(
/// relation to preceding patterns, it is not reachable) and exhaustiveness /// relation to preceding patterns, it is not reachable) and exhaustiveness
/// checking (if a wildcard pattern is useful in relation to a matrix, the /// checking (if a wildcard pattern is useful in relation to a matrix, the
/// matrix isn't exhaustive). /// matrix isn't exhaustive).
/// pub fn is_useful<'p, 'a: 'p, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
/// Note: is_useful doesn't work on empty types, as the paper notes. matrix: &Matrix<'p, 'tcx>,
/// So it assumes that v is non-empty. v: &[&'p Pattern<'tcx>],
pub fn is_useful<'a, 'tcx>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
matrix: &Matrix<'a, 'tcx>,
v: &[&'a Pattern<'tcx>],
witness: WitnessPreference) witness: WitnessPreference)
-> Usefulness<'tcx> { -> Usefulness<'tcx> {
let &Matrix(ref rows) = matrix; let &Matrix(ref rows) = matrix;
@ -616,19 +615,27 @@ pub fn is_useful<'a, 'tcx>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
} }
} }
fn is_useful_specialized<'a, 'tcx>( fn is_useful_specialized<'p, 'a:'p, 'tcx: 'a>(
cx: &mut MatchCheckCtxt<'a, 'tcx>, cx: &mut MatchCheckCtxt<'a, 'tcx>,
&Matrix(ref m): &Matrix<'a, 'tcx>, &Matrix(ref m): &Matrix<'p, 'tcx>,
v: &[&'a Pattern<'tcx>], v: &[&'p Pattern<'tcx>],
ctor: Constructor, ctor: Constructor,
lty: Ty<'tcx>, lty: Ty<'tcx>,
witness: WitnessPreference) -> Usefulness<'tcx> witness: WitnessPreference) -> Usefulness<'tcx>
{ {
let arity = constructor_arity(cx, &ctor, lty); let sub_pat_tys = constructor_sub_pattern_tys(cx, &ctor, lty);
let wild_patterns_owned: Vec<_> = sub_pat_tys.iter().map(|ty| {
Pattern {
ty: ty,
span: DUMMY_SP,
kind: box PatternKind::Wild,
}
}).collect();
let wild_patterns: Vec<_> = wild_patterns_owned.iter().collect();
let matrix = Matrix(m.iter().flat_map(|r| { let matrix = Matrix(m.iter().flat_map(|r| {
specialize(cx, &r[..], &ctor, 0, arity) specialize(cx, &r[..], &ctor, &wild_patterns)
}).collect()); }).collect());
match specialize(cx, v, &ctor, 0, arity) { match specialize(cx, v, &ctor, &wild_patterns) {
Some(v) => match is_useful(cx, &matrix, &v[..], witness) { Some(v) => match is_useful(cx, &matrix, &v[..], witness) {
UsefulWithWitness(witnesses) => UsefulWithWitness( UsefulWithWitness(witnesses) => UsefulWithWitness(
witnesses.into_iter() witnesses.into_iter()
@ -703,6 +710,33 @@ fn constructor_arity(_cx: &MatchCheckCtxt, ctor: &Constructor, ty: Ty) -> usize
} }
} }
/// This computes the types of the sub patterns that a constructor should be
/// expanded to.
///
/// For instance, a tuple pattern (43u32, 'a') has sub pattern types [u32, char].
fn constructor_sub_pattern_tys<'a, 'tcx: 'a>(cx: &MatchCheckCtxt<'a, 'tcx>,
ctor: &Constructor,
ty: Ty<'tcx>) -> Vec<Ty<'tcx>>
{
debug!("constructor_sub_pattern_tys({:?}, {:?})", ctor, ty);
match ty.sty {
ty::TyTuple(ref fs) => fs.into_iter().map(|t| *t).collect(),
ty::TyBox(ty) => vec![ty],
ty::TySlice(ty) | ty::TyArray(ty, _) => match *ctor {
Slice(length) => repeat(ty).take(length).collect(),
ConstantValue(_) => vec![],
_ => bug!("bad slice pattern {:?} {:?}", ctor, ty)
},
ty::TyRef(_, ref ty_and_mut) => vec![ty_and_mut.ty],
ty::TyAdt(adt, substs) => {
ctor.variant_for_adt(adt).fields.iter().map(|field| {
field.ty(cx.tcx, substs)
}).collect()
}
_ => vec![],
}
}
fn slice_pat_covered_by_constructor(_tcx: TyCtxt, _span: Span, fn slice_pat_covered_by_constructor(_tcx: TyCtxt, _span: Span,
ctor: &Constructor, ctor: &Constructor,
prefix: &[Pattern], prefix: &[Pattern],
@ -754,19 +788,18 @@ fn range_covered_by_constructor(tcx: TyCtxt, span: Span,
Ok(cmp_from != Ordering::Less && cmp_to != Ordering::Greater) Ok(cmp_from != Ordering::Less && cmp_to != Ordering::Greater)
} }
fn patterns_for_variant<'a, 'tcx>( fn patterns_for_variant<'p, 'a: 'p, 'tcx: 'a>(
cx: &mut MatchCheckCtxt<'a, 'tcx>, subpatterns: &'p [FieldPattern<'tcx>],
subpatterns: &'a [FieldPattern<'tcx>], wild_patterns: &[&'p Pattern<'tcx>])
arity: usize) -> Vec<&'p Pattern<'tcx>>
-> Vec<&'a Pattern<'tcx>>
{ {
let mut result = vec![cx.wild_pattern; arity]; let mut result = wild_patterns.to_owned();
for subpat in subpatterns { for subpat in subpatterns {
result[subpat.field.index()] = &subpat.pattern; result[subpat.field.index()] = &subpat.pattern;
} }
debug!("patterns_for_variant({:?}, {:?}) = {:?}", subpatterns, arity, result); debug!("patterns_for_variant({:?}, {:?}) = {:?}", subpatterns, wild_patterns, result);
result result
} }
@ -778,35 +811,41 @@ fn patterns_for_variant<'a, 'tcx>(
/// different patterns. /// different patterns.
/// Structure patterns with a partial wild pattern (Foo { a: 42, .. }) have their missing /// Structure patterns with a partial wild pattern (Foo { a: 42, .. }) have their missing
/// fields filled with wild patterns. /// fields filled with wild patterns.
fn specialize<'a, 'tcx>( fn specialize<'p, 'a: 'p, 'tcx: 'a>(
cx: &mut MatchCheckCtxt<'a, 'tcx>, cx: &mut MatchCheckCtxt<'a, 'tcx>,
r: &[&'a Pattern<'tcx>], r: &[&'p Pattern<'tcx>],
constructor: &Constructor, col: usize, arity: usize) constructor: &Constructor,
-> Option<Vec<&'a Pattern<'tcx>>> wild_patterns: &[&'p Pattern<'tcx>])
-> Option<Vec<&'p Pattern<'tcx>>>
{ {
let pat = &r[col]; let pat = &r[0];
let head: Option<Vec<&Pattern>> = match *pat.kind { let head: Option<Vec<&Pattern>> = match *pat.kind {
PatternKind::Binding { .. } | PatternKind::Wild => PatternKind::Binding { .. } | PatternKind::Wild => {
Some(vec![cx.wild_pattern; arity]), Some(wild_patterns.to_owned())
},
PatternKind::Variant { adt_def, variant_index, ref subpatterns } => { PatternKind::Variant { adt_def, variant_index, ref subpatterns, .. } => {
let ref variant = adt_def.variants[variant_index]; let ref variant = adt_def.variants[variant_index];
if *constructor == Variant(variant.did) { if *constructor == Variant(variant.did) {
Some(patterns_for_variant(cx, subpatterns, arity)) Some(patterns_for_variant(subpatterns, wild_patterns))
} else { } else {
None None
} }
} }
PatternKind::Leaf { ref subpatterns } => Some(patterns_for_variant(cx, subpatterns, arity)), PatternKind::Leaf { ref subpatterns } => {
PatternKind::Deref { ref subpattern } => Some(vec![subpattern]), Some(patterns_for_variant(subpatterns, wild_patterns))
}
PatternKind::Deref { ref subpattern } => {
Some(vec![subpattern])
}
PatternKind::Constant { ref value } => { PatternKind::Constant { ref value } => {
match *constructor { match *constructor {
Slice(..) => match *value { Slice(..) => match *value {
ConstVal::ByteStr(ref data) => { ConstVal::ByteStr(ref data) => {
if arity == data.len() { if wild_patterns.len() == data.len() {
Some(cx.lower_byte_str_pattern(pat)) Some(cx.lower_byte_str_pattern(pat))
} else { } else {
None None
@ -842,11 +881,14 @@ fn specialize<'a, 'tcx>(
match *constructor { match *constructor {
Slice(..) => { Slice(..) => {
let pat_len = prefix.len() + suffix.len(); let pat_len = prefix.len() + suffix.len();
if let Some(slice_count) = arity.checked_sub(pat_len) { if let Some(slice_count) = wild_patterns.len().checked_sub(pat_len) {
if slice_count == 0 || slice.is_some() { if slice_count == 0 || slice.is_some() {
Some( Some(
prefix.iter().chain( prefix.iter().chain(
repeat(cx.wild_pattern).take(slice_count).chain( wild_patterns.iter().map(|p| *p)
.skip(prefix.len())
.take(slice_count)
.chain(
suffix.iter() suffix.iter()
)).collect()) )).collect())
} else { } else {
@ -870,11 +912,10 @@ fn specialize<'a, 'tcx>(
} }
} }
}; };
debug!("specialize({:?}, {:?}) = {:?}", r[col], arity, head); debug!("specialize({:?}, {:?}) = {:?}", r[0], wild_patterns, head);
head.map(|mut head| { head.map(|mut head| {
head.extend_from_slice(&r[..col]); head.extend_from_slice(&r[1 ..]);
head.extend_from_slice(&r[col + 1..]);
head head
}) })
} }

View File

@ -24,7 +24,7 @@ use rustc::middle::expr_use_visitor as euv;
use rustc::middle::mem_categorization::{cmt}; use rustc::middle::mem_categorization::{cmt};
use rustc::session::Session; use rustc::session::Session;
use rustc::traits::Reveal; use rustc::traits::Reveal;
use rustc::ty::{self, TyCtxt}; use rustc::ty::{self, Ty, TyCtxt};
use rustc::lint; use rustc::lint;
use rustc_errors::DiagnosticBuilder; use rustc_errors::DiagnosticBuilder;
@ -36,7 +36,7 @@ use rustc_back::slice;
use syntax::ast; use syntax::ast;
use syntax::ptr::P; use syntax::ptr::P;
use syntax_pos::Span; use syntax_pos::{Span, DUMMY_SP};
struct OuterVisitor<'a, 'tcx: 'a> { tcx: TyCtxt<'a, 'tcx, 'tcx> } struct OuterVisitor<'a, 'tcx: 'a> { tcx: TyCtxt<'a, 'tcx, 'tcx> }
@ -81,7 +81,7 @@ impl<'a, 'tcx> Visitor<'tcx> for MatchVisitor<'a, 'tcx> {
match ex.node { match ex.node {
hir::ExprMatch(ref scrut, ref arms, source) => { hir::ExprMatch(ref scrut, ref arms, source) => {
self.check_match(scrut, arms, source, ex.span); self.check_match(scrut, arms, source);
} }
_ => {} _ => {}
} }
@ -132,8 +132,7 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> {
&self, &self,
scrut: &hir::Expr, scrut: &hir::Expr,
arms: &[hir::Arm], arms: &[hir::Arm],
source: hir::MatchSource, source: hir::MatchSource)
span: Span)
{ {
for arm in arms { for arm in arms {
// First, check legality of move bindings. // First, check legality of move bindings.
@ -175,32 +174,14 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> {
// Fourth, check for unreachable arms. // Fourth, check for unreachable arms.
check_arms(cx, &inlined_arms, source); check_arms(cx, &inlined_arms, source);
// Finally, check if the whole match expression is exhaustive.
// Check for empty enum, because is_useful only works on inhabited types.
let pat_ty = self.tcx.tables().node_id_to_type(scrut.id);
if inlined_arms.is_empty() {
if !pat_ty.is_uninhabited(Some(scrut.id), self.tcx) {
// We know the type is inhabited, so this must be wrong
let mut err = create_e0004(self.tcx.sess, span,
format!("non-exhaustive patterns: type {} \
is non-empty",
pat_ty));
span_help!(&mut err, span,
"Please ensure that all possible cases are being handled; \
possibly adding wildcards or more match arms.");
err.emit();
}
// If the type *is* uninhabited, it's vacuously exhaustive
return;
}
let matrix: Matrix = inlined_arms let matrix: Matrix = inlined_arms
.iter() .iter()
.filter(|&&(_, guard)| guard.is_none()) .filter(|&&(_, guard)| guard.is_none())
.flat_map(|arm| &arm.0) .flat_map(|arm| &arm.0)
.map(|pat| vec![pat.0]) .map(|pat| vec![pat.0])
.collect(); .collect();
check_exhaustive(cx, scrut.span, &matrix, source); let scrut_ty = cx.tcx.tables().node_id_to_type(scrut.id);
check_exhaustive(cx, scrut_ty, scrut.span, &matrix, source);
}) })
} }
@ -213,11 +194,18 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> {
MatchCheckCtxt::create_and_enter(self.tcx, pat.id, |ref mut cx| { MatchCheckCtxt::create_and_enter(self.tcx, pat.id, |ref mut cx| {
let mut patcx = PatternContext::new(self.tcx); let mut patcx = PatternContext::new(self.tcx);
let pattern = patcx.lower_pattern(pat);
let pattern_ty = pattern.ty;
let pats : Matrix = vec![vec![ let pats : Matrix = vec![vec![
expand_pattern(cx, patcx.lower_pattern(pat)) expand_pattern(cx, pattern)
]].into_iter().collect(); ]].into_iter().collect();
let witness = match is_useful(cx, &pats, &[cx.wild_pattern], ConstructWitness) { let wild_pattern = Pattern {
ty: pattern_ty,
span: DUMMY_SP,
kind: box PatternKind::Wild,
};
let witness = match is_useful(cx, &pats, &[&wild_pattern], ConstructWitness) {
UsefulWithWitness(witness) => witness, UsefulWithWitness(witness) => witness,
NotUseful => return, NotUseful => return,
Useful => bug!() Useful => bug!()
@ -359,10 +347,16 @@ fn check_arms<'a, 'tcx>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
} }
fn check_exhaustive<'a, 'tcx>(cx: &mut MatchCheckCtxt<'a, 'tcx>, fn check_exhaustive<'a, 'tcx>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
scrut_ty: Ty<'tcx>,
sp: Span, sp: Span,
matrix: &Matrix<'a, 'tcx>, matrix: &Matrix<'a, 'tcx>,
source: hir::MatchSource) { source: hir::MatchSource) {
match is_useful(cx, matrix, &[cx.wild_pattern], ConstructWitness) { let wild_pattern = Pattern {
ty: scrut_ty,
span: DUMMY_SP,
kind: box PatternKind::Wild,
};
match is_useful(cx, matrix, &[&wild_pattern], ConstructWitness) {
UsefulWithWitness(pats) => { UsefulWithWitness(pats) => {
let witnesses = if pats.is_empty() { let witnesses = if pats.is_empty() {
vec![cx.wild_pattern] vec![cx.wild_pattern]

View File

@ -13,7 +13,8 @@ use eval;
use rustc::lint; use rustc::lint;
use rustc::middle::const_val::ConstVal; use rustc::middle::const_val::ConstVal;
use rustc::mir::{Field, BorrowKind, Mutability}; use rustc::mir::{Field, BorrowKind, Mutability};
use rustc::ty::{self, TyCtxt, AdtDef, Ty, Region}; use rustc::ty::{self, TyCtxt, AdtDef, Ty, TypeVariants, Region};
use rustc::ty::subst::{Substs, Kind};
use rustc::hir::{self, PatKind}; use rustc::hir::{self, PatKind};
use rustc::hir::def::{Def, CtorKind}; use rustc::hir::def::{Def, CtorKind};
use rustc::hir::pat_util::EnumerateAndAdjustIterator; use rustc::hir::pat_util::EnumerateAndAdjustIterator;
@ -67,6 +68,7 @@ pub enum PatternKind<'tcx> {
/// Foo(...) or Foo{...} or Foo, where `Foo` is a variant name from an adt with >1 variants /// Foo(...) or Foo{...} or Foo, where `Foo` is a variant name from an adt with >1 variants
Variant { Variant {
adt_def: &'tcx AdtDef, adt_def: &'tcx AdtDef,
substs: &'tcx Substs<'tcx>,
variant_index: usize, variant_index: usize,
subpatterns: Vec<FieldPattern<'tcx>>, subpatterns: Vec<FieldPattern<'tcx>>,
}, },
@ -534,11 +536,15 @@ impl<'a, 'gcx, 'tcx> PatternContext<'a, 'gcx, 'tcx> {
{ {
match def { match def {
Def::Variant(variant_id) | Def::VariantCtor(variant_id, ..) => { Def::Variant(variant_id) | Def::VariantCtor(variant_id, ..) => {
let enum_id = self.tcx.parent_def_id(variant_id).unwrap(); let ty = self.tcx.tables().node_id_to_type(pat.id);
let adt_def = self.tcx.lookup_adt_def(enum_id); let (adt_def, substs) = match ty.sty {
TypeVariants::TyAdt(adt_def, substs) => (adt_def, substs),
_ => span_bug!(pat.span, "inappropriate type for def"),
};
if adt_def.variants.len() > 1 { if adt_def.variants.len() > 1 {
PatternKind::Variant { PatternKind::Variant {
adt_def: adt_def, adt_def: adt_def,
substs: substs,
variant_index: adt_def.variant_index_with_id(variant_id), variant_index: adt_def.variant_index_with_id(variant_id),
subpatterns: subpatterns, subpatterns: subpatterns,
} }
@ -776,8 +782,9 @@ macro_rules! CloneImpls {
} }
CloneImpls!{ <'tcx> CloneImpls!{ <'tcx>
Span, Field, Mutability, ast::Name, ast::NodeId, usize, ConstVal, Span, Field, Mutability, ast::Name, ast::NodeId, usize, ConstVal, Region,
Ty<'tcx>, BindingMode<'tcx>, &'tcx AdtDef Ty<'tcx>, BindingMode<'tcx>, &'tcx AdtDef,
&'tcx Substs<'tcx>, &'tcx Kind<'tcx>
} }
impl<'tcx> PatternFoldable<'tcx> for FieldPattern<'tcx> { impl<'tcx> PatternFoldable<'tcx> for FieldPattern<'tcx> {
@ -828,10 +835,12 @@ impl<'tcx> PatternFoldable<'tcx> for PatternKind<'tcx> {
}, },
PatternKind::Variant { PatternKind::Variant {
adt_def, adt_def,
substs,
variant_index, variant_index,
ref subpatterns, ref subpatterns,
} => PatternKind::Variant { } => PatternKind::Variant {
adt_def: adt_def.fold_with(folder), adt_def: adt_def.fold_with(folder),
substs: substs.fold_with(folder),
variant_index: variant_index.fold_with(folder), variant_index: variant_index.fold_with(folder),
subpatterns: subpatterns.fold_with(folder) subpatterns: subpatterns.fold_with(folder)
}, },

View File

@ -26,6 +26,7 @@ use build::{BlockAnd, BlockAndExtension, Builder};
use build::matches::{Binding, MatchPair, Candidate}; use build::matches::{Binding, MatchPair, Candidate};
use hair::*; use hair::*;
use rustc::mir::*; use rustc::mir::*;
use rustc_data_structures::fx::FxHashSet;
use std::mem; use std::mem;
@ -93,11 +94,28 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
} }
PatternKind::Range { .. } | PatternKind::Range { .. } |
PatternKind::Variant { .. } |
PatternKind::Slice { .. } => { PatternKind::Slice { .. } => {
Err(match_pair) Err(match_pair)
} }
PatternKind::Variant { adt_def, substs, variant_index, ref subpatterns } => {
let irrefutable = adt_def.variants.iter().enumerate().all(|(i, v)| {
let mut visited = FxHashSet::default();
i == variant_index || v.is_uninhabited_recurse(&mut visited,
None,
self.hir.tcx(),
substs,
adt_def.adt_kind())
});
if irrefutable {
let lvalue = match_pair.lvalue.downcast(adt_def, variant_index);
candidate.match_pairs.extend(self.field_match_pairs(lvalue, subpatterns));
Ok(())
} else {
Err(match_pair)
}
},
PatternKind::Array { ref prefix, ref slice, ref suffix } => { PatternKind::Array { ref prefix, ref slice, ref suffix } => {
self.prefix_slice_suffix(&mut candidate.match_pairs, self.prefix_slice_suffix(&mut candidate.match_pairs,
&match_pair.lvalue, &match_pair.lvalue,

View File

@ -32,7 +32,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
/// It is a bug to call this with a simplifyable pattern. /// It is a bug to call this with a simplifyable pattern.
pub fn test<'pat>(&mut self, match_pair: &MatchPair<'pat, 'tcx>) -> Test<'tcx> { pub fn test<'pat>(&mut self, match_pair: &MatchPair<'pat, 'tcx>) -> Test<'tcx> {
match *match_pair.pattern.kind { match *match_pair.pattern.kind {
PatternKind::Variant { ref adt_def, variant_index: _, subpatterns: _ } => { PatternKind::Variant { ref adt_def, substs: _, variant_index: _, subpatterns: _ } => {
Test { Test {
span: match_pair.pattern.span, span: match_pair.pattern.span,
kind: TestKind::Switch { kind: TestKind::Switch {
@ -451,7 +451,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
// If we are performing a variant switch, then this // If we are performing a variant switch, then this
// informs variant patterns, but nothing else. // informs variant patterns, but nothing else.
(&TestKind::Switch { adt_def: tested_adt_def, .. }, (&TestKind::Switch { adt_def: tested_adt_def, .. },
&PatternKind::Variant { adt_def, variant_index, ref subpatterns }) => { &PatternKind::Variant { adt_def, variant_index, ref subpatterns, .. }) => {
assert_eq!(adt_def, tested_adt_def); assert_eq!(adt_def, tested_adt_def);
let new_candidate = let new_candidate =
self.candidate_after_variant_switch(match_pair_index, self.candidate_after_variant_switch(match_pair_index,

View File

@ -0,0 +1,60 @@
// Copyright 2012 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(never_type)]
#![feature(slice_patterns)]
#![allow(unreachable_patterns)]
#![allow(unreachable_code)]
#[allow(dead_code)]
fn foo(z: !) {
let x: Result<!, !> = Ok(z);
let Ok(_y) = x;
let Err(_y) = x;
let x = [z; 1];
match x {};
match x {
[q] => q,
};
}
fn bar(nevers: &[!]) {
match nevers {
&[] => (),
};
match nevers {
&[] => (),
&[_] => (),
&[_, _, _, ..] => (),
};
}
fn main() {
let x: Result<u32, !> = Ok(123);
let Ok(y) = x;
assert_eq!(123, y);
match x {
Ok(y) => y,
};
match x {
Ok(y) => y,
Err(e) => match e {},
};
bar(&[]);
}