Skip to content

Commit 9c5e86d

Browse files
committed
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.
1 parent bcdbe94 commit 9c5e86d

File tree

8 files changed

+237
-96
lines changed

8 files changed

+237
-96
lines changed

src/librustc/mir/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -888,6 +888,10 @@ impl<'tcx> Lvalue<'tcx> {
888888
self.elem(ProjectionElem::Deref)
889889
}
890890

891+
pub fn downcast(self, adt_def: AdtDef<'tcx>, variant_index: usize) -> Lvalue<'tcx> {
892+
self.elem(ProjectionElem::Downcast(adt_def, variant_index))
893+
}
894+
891895
pub fn index(self, index: Operand<'tcx>) -> Lvalue<'tcx> {
892896
self.elem(ProjectionElem::Index(index))
893897
}

src/librustc/ty/mod.rs

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1416,7 +1416,7 @@ impl<'a, 'gcx, 'tcx> AdtDef {
14161416
return false;
14171417
};
14181418
self.variants.iter().all(|v| {
1419-
v.is_uninhabited_recurse(visited, block, tcx, substs, self.is_union())
1419+
v.is_uninhabited_recurse(visited, block, tcx, substs, self.adt_kind())
14201420
})
14211421
}
14221422

@@ -1761,11 +1761,23 @@ impl<'a, 'gcx, 'tcx> VariantDef {
17611761
block: Option<NodeId>,
17621762
tcx: TyCtxt<'a, 'gcx, 'tcx>,
17631763
substs: &'tcx Substs<'tcx>,
1764-
is_union: bool) -> bool {
1765-
if is_union {
1766-
self.fields.iter().all(|f| f.is_uninhabited_recurse(visited, block, tcx, substs))
1767-
} else {
1768-
self.fields.iter().any(|f| f.is_uninhabited_recurse(visited, block, tcx, substs))
1764+
adt_kind: AdtKind) -> bool {
1765+
match adt_kind {
1766+
AdtKind::Union => {
1767+
self.fields.iter().all(|f| {
1768+
f.is_uninhabited_recurse(visited, block, tcx, substs, false)
1769+
})
1770+
},
1771+
AdtKind::Struct => {
1772+
self.fields.iter().any(|f| {
1773+
f.is_uninhabited_recurse(visited, block, tcx, substs, false)
1774+
})
1775+
},
1776+
AdtKind::Enum => {
1777+
self.fields.iter().any(|f| {
1778+
f.is_uninhabited_recurse(visited, block, tcx, substs, true)
1779+
})
1780+
},
17691781
}
17701782
}
17711783
}
@@ -1780,9 +1792,12 @@ impl<'a, 'gcx, 'tcx> FieldDef {
17801792
visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>,
17811793
block: Option<NodeId>,
17821794
tcx: TyCtxt<'a, 'gcx, 'tcx>,
1783-
substs: &'tcx Substs<'tcx>) -> bool {
1784-
block.map_or(true, |b| tcx.vis_is_accessible_from(self.vis, b)) &&
1785-
self.ty(tcx, substs).is_uninhabited_recurse(visited, block, tcx)
1795+
substs: &'tcx Substs<'tcx>,
1796+
is_enum: bool) -> bool {
1797+
let visible = is_enum || block.map_or(true, |b| {
1798+
tcx.vis_is_accessible_from(self.vis, b)
1799+
});
1800+
visible && self.ty(tcx, substs).is_uninhabited_recurse(visited, block, tcx)
17861801
}
17871802
}
17881803

src/librustc_const_eval/_match.rs

Lines changed: 92 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ use pattern::{FieldPattern, Pattern, PatternKind};
2424
use pattern::{PatternFoldable, PatternFolder};
2525

2626
use rustc::hir::def_id::DefId;
27-
use rustc::ty::{self, Ty, TyCtxt, TypeFoldable};
27+
use rustc::ty::{self, AdtKind, Ty, TyCtxt, TypeFoldable};
2828

2929
use rustc::mir::Field;
3030
use rustc::util::common::ErrorReported;
@@ -153,9 +153,6 @@ pub struct MatchCheckCtxt<'a, 'tcx: 'a> {
153153
/// can not be seen to be empty outside it's module and should not
154154
/// be matchable with an empty match statement.
155155
pub node: NodeId,
156-
/// A wild pattern with an error type - it exists to avoid having to normalize
157-
/// associated types to get field types.
158-
pub wild_pattern: &'a Pattern<'tcx>,
159156
pub pattern_arena: &'a TypedArena<Pattern<'tcx>>,
160157
pub byte_array_map: FxHashMap<*const Pattern<'tcx>, Vec<&'a Pattern<'tcx>>>,
161158
}
@@ -167,25 +164,20 @@ impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> {
167164
f: F) -> R
168165
where F: for<'b> FnOnce(MatchCheckCtxt<'b, 'tcx>) -> R
169166
{
170-
let wild_pattern = Pattern {
171-
ty: tcx.types.err,
172-
span: DUMMY_SP,
173-
kind: box PatternKind::Wild
174-
};
175-
176167
let pattern_arena = TypedArena::new();
177168

178169
f(MatchCheckCtxt {
179170
tcx: tcx,
180171
node: node,
181-
wild_pattern: &wild_pattern,
182172
pattern_arena: &pattern_arena,
183173
byte_array_map: FxHashMap(),
184174
})
185175
}
186176

187177
// convert a byte-string pattern to a list of u8 patterns.
188-
fn lower_byte_str_pattern(&mut self, pat: &'a Pattern<'tcx>) -> Vec<&'a Pattern<'tcx>> {
178+
fn lower_byte_str_pattern<'p>(&mut self, pat: &'p Pattern<'tcx>) -> Vec<&'p Pattern<'tcx>>
179+
where 'a: 'p
180+
{
189181
let pattern_arena = &*self.pattern_arena;
190182
let tcx = self.tcx;
191183
self.byte_array_map.entry(pat).or_insert_with(|| {
@@ -401,6 +393,7 @@ fn missing_constructors<'a, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
401393
fn all_constructors<'a, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
402394
pcx: PatternContext<'tcx>) -> Vec<Constructor>
403395
{
396+
debug!("all_constructors({:?})", pcx.ty);
404397
match pcx.ty.sty {
405398
ty::TyBool =>
406399
[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>,
421414
ty::TyAdt(def, substs) if def.is_enum() && def.variants.len() != 1 => {
422415
def.variants.iter().filter_map(|v| {
423416
let mut visited = FxHashSet::default();
424-
if v.is_uninhabited_recurse(&mut visited, Some(cx.node), cx.tcx, substs, false) {
417+
if v.is_uninhabited_recurse(&mut visited,
418+
Some(cx.node),
419+
cx.tcx, substs,
420+
AdtKind::Enum) {
425421
None
426422
} else {
427423
Some(Variant(v.did))
@@ -438,10 +434,10 @@ fn all_constructors<'a, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
438434
}
439435
}
440436

441-
fn max_slice_length<'a, 'tcx, I>(
437+
fn max_slice_length<'p, 'a: 'p, 'tcx: 'a, I>(
442438
_cx: &mut MatchCheckCtxt<'a, 'tcx>,
443439
patterns: I) -> usize
444-
where I: Iterator<Item=&'a Pattern<'tcx>>
440+
where I: Iterator<Item=&'p Pattern<'tcx>>
445441
{
446442
// The exhaustiveness-checking paper does not include any details on
447443
// checking variable-length slice patterns. However, they are matched
@@ -532,6 +528,12 @@ fn max_slice_length<'a, 'tcx, I>(
532528
}
533529

534530
/// Algorithm from http://moscova.inria.fr/~maranget/papers/warn/index.html
531+
/// The algorithm from the paper has been modified to correctly handle empty
532+
/// types. The changes are:
533+
/// (0) We don't exit early if the pattern matrix has zero rows. We just
534+
/// continue to recurse over columns.
535+
/// (1) all_constructors will only return constructors that are statically
536+
/// possible. eg. it will only return Ok for Result<T, !>
535537
///
536538
/// Whether a vector `v` of patterns is 'useful' in relation to a set of such
537539
/// 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>(
541543
/// relation to preceding patterns, it is not reachable) and exhaustiveness
542544
/// checking (if a wildcard pattern is useful in relation to a matrix, the
543545
/// matrix isn't exhaustive).
544-
///
545-
/// Note: is_useful doesn't work on empty types, as the paper notes.
546-
/// So it assumes that v is non-empty.
547-
pub fn is_useful<'a, 'tcx>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
548-
matrix: &Matrix<'a, 'tcx>,
549-
v: &[&'a Pattern<'tcx>],
546+
pub fn is_useful<'p, 'a: 'p, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
547+
matrix: &Matrix<'p, 'tcx>,
548+
v: &[&'p Pattern<'tcx>],
550549
witness: WitnessPreference)
551550
-> Usefulness<'tcx> {
552551
let &Matrix(ref rows) = matrix;
@@ -616,19 +615,27 @@ pub fn is_useful<'a, 'tcx>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
616615
}
617616
}
618617

619-
fn is_useful_specialized<'a, 'tcx>(
618+
fn is_useful_specialized<'p, 'a:'p, 'tcx: 'a>(
620619
cx: &mut MatchCheckCtxt<'a, 'tcx>,
621-
&Matrix(ref m): &Matrix<'a, 'tcx>,
622-
v: &[&'a Pattern<'tcx>],
620+
&Matrix(ref m): &Matrix<'p, 'tcx>,
621+
v: &[&'p Pattern<'tcx>],
623622
ctor: Constructor,
624623
lty: Ty<'tcx>,
625624
witness: WitnessPreference) -> Usefulness<'tcx>
626625
{
627-
let arity = constructor_arity(cx, &ctor, lty);
626+
let sub_pat_tys = constructor_sub_pattern_tys(cx, &ctor, lty);
627+
let wild_patterns_owned: Vec<_> = sub_pat_tys.iter().map(|ty| {
628+
Pattern {
629+
ty: ty,
630+
span: DUMMY_SP,
631+
kind: box PatternKind::Wild,
632+
}
633+
}).collect();
634+
let wild_patterns: Vec<_> = wild_patterns_owned.iter().collect();
628635
let matrix = Matrix(m.iter().flat_map(|r| {
629-
specialize(cx, &r[..], &ctor, 0, arity)
636+
specialize(cx, &r[..], &ctor, &wild_patterns)
630637
}).collect());
631-
match specialize(cx, v, &ctor, 0, arity) {
638+
match specialize(cx, v, &ctor, &wild_patterns) {
632639
Some(v) => match is_useful(cx, &matrix, &v[..], witness) {
633640
UsefulWithWitness(witnesses) => UsefulWithWitness(
634641
witnesses.into_iter()
@@ -703,6 +710,33 @@ fn constructor_arity(_cx: &MatchCheckCtxt, ctor: &Constructor, ty: Ty) -> usize
703710
}
704711
}
705712

713+
/// This computes the types of the sub patterns that a constructor should be
714+
/// expanded to.
715+
///
716+
/// For instance, a tuple pattern (43u32, 'a') has sub pattern types [u32, char].
717+
fn constructor_sub_pattern_tys<'a, 'tcx: 'a>(cx: &MatchCheckCtxt<'a, 'tcx>,
718+
ctor: &Constructor,
719+
ty: Ty<'tcx>) -> Vec<Ty<'tcx>>
720+
{
721+
debug!("constructor_sub_pattern_tys({:?}, {:?})", ctor, ty);
722+
match ty.sty {
723+
ty::TyTuple(ref fs) => fs.into_iter().map(|t| *t).collect(),
724+
ty::TyBox(ty) => vec![ty],
725+
ty::TySlice(ty) | ty::TyArray(ty, _) => match *ctor {
726+
Slice(length) => repeat(ty).take(length).collect(),
727+
ConstantValue(_) => vec![],
728+
_ => bug!("bad slice pattern {:?} {:?}", ctor, ty)
729+
},
730+
ty::TyRef(_, ref ty_and_mut) => vec![ty_and_mut.ty],
731+
ty::TyAdt(adt, substs) => {
732+
ctor.variant_for_adt(adt).fields.iter().map(|field| {
733+
field.ty(cx.tcx, substs)
734+
}).collect()
735+
}
736+
_ => vec![],
737+
}
738+
}
739+
706740
fn slice_pat_covered_by_constructor(_tcx: TyCtxt, _span: Span,
707741
ctor: &Constructor,
708742
prefix: &[Pattern],
@@ -754,19 +788,18 @@ fn range_covered_by_constructor(tcx: TyCtxt, span: Span,
754788
Ok(cmp_from != Ordering::Less && cmp_to != Ordering::Greater)
755789
}
756790

757-
fn patterns_for_variant<'a, 'tcx>(
758-
cx: &mut MatchCheckCtxt<'a, 'tcx>,
759-
subpatterns: &'a [FieldPattern<'tcx>],
760-
arity: usize)
761-
-> Vec<&'a Pattern<'tcx>>
791+
fn patterns_for_variant<'p, 'a: 'p, 'tcx: 'a>(
792+
subpatterns: &'p [FieldPattern<'tcx>],
793+
wild_patterns: &[&'p Pattern<'tcx>])
794+
-> Vec<&'p Pattern<'tcx>>
762795
{
763-
let mut result = vec![cx.wild_pattern; arity];
796+
let mut result = wild_patterns.to_owned();
764797

765798
for subpat in subpatterns {
766799
result[subpat.field.index()] = &subpat.pattern;
767800
}
768801

769-
debug!("patterns_for_variant({:?}, {:?}) = {:?}", subpatterns, arity, result);
802+
debug!("patterns_for_variant({:?}, {:?}) = {:?}", subpatterns, wild_patterns, result);
770803
result
771804
}
772805

@@ -778,35 +811,41 @@ fn patterns_for_variant<'a, 'tcx>(
778811
/// different patterns.
779812
/// Structure patterns with a partial wild pattern (Foo { a: 42, .. }) have their missing
780813
/// fields filled with wild patterns.
781-
fn specialize<'a, 'tcx>(
814+
fn specialize<'p, 'a: 'p, 'tcx: 'a>(
782815
cx: &mut MatchCheckCtxt<'a, 'tcx>,
783-
r: &[&'a Pattern<'tcx>],
784-
constructor: &Constructor, col: usize, arity: usize)
785-
-> Option<Vec<&'a Pattern<'tcx>>>
816+
r: &[&'p Pattern<'tcx>],
817+
constructor: &Constructor,
818+
wild_patterns: &[&'p Pattern<'tcx>])
819+
-> Option<Vec<&'p Pattern<'tcx>>>
786820
{
787-
let pat = &r[col];
821+
let pat = &r[0];
788822

789823
let head: Option<Vec<&Pattern>> = match *pat.kind {
790-
PatternKind::Binding { .. } | PatternKind::Wild =>
791-
Some(vec![cx.wild_pattern; arity]),
824+
PatternKind::Binding { .. } | PatternKind::Wild => {
825+
Some(wild_patterns.to_owned())
826+
},
792827

793-
PatternKind::Variant { adt_def, variant_index, ref subpatterns } => {
828+
PatternKind::Variant { adt_def, variant_index, ref subpatterns, .. } => {
794829
let ref variant = adt_def.variants[variant_index];
795830
if *constructor == Variant(variant.did) {
796-
Some(patterns_for_variant(cx, subpatterns, arity))
831+
Some(patterns_for_variant(subpatterns, wild_patterns))
797832
} else {
798833
None
799834
}
800835
}
801836

802-
PatternKind::Leaf { ref subpatterns } => Some(patterns_for_variant(cx, subpatterns, arity)),
803-
PatternKind::Deref { ref subpattern } => Some(vec![subpattern]),
837+
PatternKind::Leaf { ref subpatterns } => {
838+
Some(patterns_for_variant(subpatterns, wild_patterns))
839+
}
840+
PatternKind::Deref { ref subpattern } => {
841+
Some(vec![subpattern])
842+
}
804843

805844
PatternKind::Constant { ref value } => {
806845
match *constructor {
807846
Slice(..) => match *value {
808847
ConstVal::ByteStr(ref data) => {
809-
if arity == data.len() {
848+
if wild_patterns.len() == data.len() {
810849
Some(cx.lower_byte_str_pattern(pat))
811850
} else {
812851
None
@@ -842,11 +881,14 @@ fn specialize<'a, 'tcx>(
842881
match *constructor {
843882
Slice(..) => {
844883
let pat_len = prefix.len() + suffix.len();
845-
if let Some(slice_count) = arity.checked_sub(pat_len) {
884+
if let Some(slice_count) = wild_patterns.len().checked_sub(pat_len) {
846885
if slice_count == 0 || slice.is_some() {
847886
Some(
848887
prefix.iter().chain(
849-
repeat(cx.wild_pattern).take(slice_count).chain(
888+
wild_patterns.iter().map(|p| *p)
889+
.skip(prefix.len())
890+
.take(slice_count)
891+
.chain(
850892
suffix.iter()
851893
)).collect())
852894
} else {
@@ -870,11 +912,10 @@ fn specialize<'a, 'tcx>(
870912
}
871913
}
872914
};
873-
debug!("specialize({:?}, {:?}) = {:?}", r[col], arity, head);
915+
debug!("specialize({:?}, {:?}) = {:?}", r[0], wild_patterns, head);
874916

875917
head.map(|mut head| {
876-
head.extend_from_slice(&r[..col]);
877-
head.extend_from_slice(&r[col + 1..]);
918+
head.extend_from_slice(&r[1 ..]);
878919
head
879920
})
880921
}

0 commit comments

Comments
 (0)