Skip to content

Commit a0026e6

Browse files
committed
Check for uninhabited types in sub-patterns (#12609)
This alters the exhaustiveness-checking algorithm for pattern matches to avoid raising spurious errors about cases not being covered for uninhabited types. Specifically, the construct_witness function now returns an Option. If it sees that DUMMY_WILD_PAT is being used to match on an uninhabited type it returns None to indicate that there is no witness. We look for DUMMY_WILD_PAT specifically and not any wildcard pattern so that wildcard patterns explicitly written by the programmer continue to work without raising errors about unreachability.
1 parent 46957f0 commit a0026e6

File tree

1 file changed

+60
-26
lines changed

1 file changed

+60
-26
lines changed

src/librustc_const_eval/check_match.rs

Lines changed: 60 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ pub enum Constructor {
129129
SliceWithSubslice(usize, usize)
130130
}
131131

132-
#[derive(Clone, PartialEq)]
132+
#[derive(Clone, PartialEq, Debug)]
133133
enum Usefulness {
134134
Useful,
135135
UsefulWithWitness(Vec<P<Pat>>),
@@ -569,18 +569,31 @@ impl<'a, 'tcx> StaticInliner<'a, 'tcx> {
569569
/// left_ty: struct X { a: (bool, &'static str), b: usize}
570570
/// pats: [(false, "foo"), 42] => X { a: (false, "foo"), b: 42 }
571571
fn construct_witness<'a,'tcx>(cx: &MatchCheckCtxt<'a,'tcx>, ctor: &Constructor,
572-
pats: Vec<&Pat>, left_ty: Ty<'tcx>) -> P<Pat> {
572+
pats: Vec<&Pat>, left_ty: Ty<'tcx>) -> Option<P<Pat>> {
573+
let tcx = cx.tcx;
573574
let pats_len = pats.len();
574-
let mut pats = pats.into_iter().map(|p| P((*p).clone()));
575+
let mut pats_owned = pats.iter().map(|p| P((**p).clone()));
575576
let pat = match left_ty.sty {
576-
ty::TyTuple(..) => PatKind::Tuple(pats.collect(), None),
577-
578-
ty::TyAdt(adt, _) => {
577+
ty::TyTuple(ref inner_tys) => {
578+
if pats.iter().zip(inner_tys.iter()).any(|(p, t)| {
579+
*p == DUMMY_WILD_PAT && t.is_uninhabited(tcx)
580+
}) {
581+
return None;
582+
}
583+
PatKind::Tuple(pats_owned.collect(), None)
584+
},
585+
ty::TyAdt(adt, substs) => {
579586
let v = ctor.variant_for_adt(adt);
587+
if pats.iter().zip(v.fields.iter()).any(|(p, f)| {
588+
*p == DUMMY_WILD_PAT &&
589+
f.ty(tcx, substs).is_uninhabited(tcx)
590+
}) {
591+
return None;
592+
};
580593
match v.ctor_kind {
581594
CtorKind::Fictive => {
582595
let field_pats: hir::HirVec<_> = v.fields.iter()
583-
.zip(pats)
596+
.zip(pats_owned)
584597
.filter(|&(_, ref pat)| pat.node != PatKind::Wild)
585598
.map(|(field, pat)| Spanned {
586599
span: DUMMY_SP,
@@ -594,30 +607,40 @@ fn construct_witness<'a,'tcx>(cx: &MatchCheckCtxt<'a,'tcx>, ctor: &Constructor,
594607
PatKind::Struct(def_to_path(cx.tcx, v.did), field_pats, has_more_fields)
595608
}
596609
CtorKind::Fn => {
597-
PatKind::TupleStruct(def_to_path(cx.tcx, v.did), pats.collect(), None)
610+
PatKind::TupleStruct(def_to_path(cx.tcx, v.did), pats_owned.collect(), None)
598611
}
599612
CtorKind::Const => {
600613
PatKind::Path(None, def_to_path(cx.tcx, v.did))
601614
}
602615
}
603616
}
604617

605-
ty::TyRef(_, ty::TypeAndMut { mutbl, .. }) => {
618+
ty::TyRef(_, ty::TypeAndMut { mutbl, ty: inner_ty }) => {
606619
assert_eq!(pats_len, 1);
607-
PatKind::Ref(pats.nth(0).unwrap(), mutbl)
620+
let pat = pats_owned.nth(0).unwrap();
621+
if *pat == *DUMMY_WILD_PAT && inner_ty.is_uninhabited(tcx) {
622+
return None;
623+
}
624+
PatKind::Ref(pat, mutbl)
608625
}
609626

610-
ty::TySlice(_) => match ctor {
627+
ty::TySlice(ref inner_ty) => match ctor {
611628
&Slice(n) => {
612629
assert_eq!(pats_len, n);
613-
PatKind::Slice(pats.collect(), None, hir::HirVec::new())
630+
if inner_ty.is_uninhabited(tcx) && pats.iter().any(|p| *p == DUMMY_WILD_PAT) {
631+
return None;
632+
}
633+
PatKind::Slice(pats_owned.collect(), None, hir::HirVec::new())
614634
},
615635
_ => unreachable!()
616636
},
617637

618-
ty::TyArray(_, len) => {
638+
ty::TyArray(ref inner_ty, len) => {
619639
assert_eq!(pats_len, len);
620-
PatKind::Slice(pats.collect(), None, hir::HirVec::new())
640+
if inner_ty.is_uninhabited(tcx) && pats.iter().any(|p| *p == DUMMY_WILD_PAT) {
641+
return None;
642+
}
643+
PatKind::Slice(pats_owned.collect(), None, hir::HirVec::new())
621644
}
622645

623646
_ => {
@@ -628,11 +651,11 @@ fn construct_witness<'a,'tcx>(cx: &MatchCheckCtxt<'a,'tcx>, ctor: &Constructor,
628651
}
629652
};
630653

631-
P(hir::Pat {
654+
Some(P(hir::Pat {
632655
id: DUMMY_NODE_ID,
633656
node: pat,
634657
span: DUMMY_SP
635-
})
658+
}))
636659
}
637660

638661
impl Constructor {
@@ -724,20 +747,26 @@ fn is_useful<'a, 'tcx>(cx: &MatchCheckCtxt<'a, 'tcx>,
724747
let constructors = missing_constructors(cx, matrix, left_ty, max_slice_length);
725748
debug!("is_useful - missing_constructors = {:?}", constructors);
726749
if constructors.is_empty() {
727-
all_constructors(cx, left_ty, max_slice_length).into_iter().map(|c| {
728-
match is_useful_specialized(cx, matrix, v, c.clone(), left_ty, witness) {
729-
UsefulWithWitness(pats) => UsefulWithWitness({
750+
let all_cons = all_constructors(cx, left_ty, max_slice_length);
751+
all_cons.into_iter().map(|c| {
752+
let spec = is_useful_specialized(cx, matrix, v, c.clone(), left_ty, witness);
753+
match spec {
754+
UsefulWithWitness(pats) => {
730755
let arity = constructor_arity(cx, &c, left_ty);
731-
let mut result = {
756+
let mut result: Vec<_> = {
732757
let pat_slice = &pats[..];
733758
let subpats: Vec<_> = (0..arity).map(|i| {
734759
pat_slice.get(i).map_or(DUMMY_WILD_PAT, |p| &**p)
735760
}).collect();
736-
vec![construct_witness(cx, &c, subpats, left_ty)]
761+
construct_witness(cx, &c, subpats, left_ty).into_iter().collect()
737762
};
738763
result.extend(pats.into_iter().skip(arity));
739-
result
740-
}),
764+
if result.is_empty() {
765+
NotUseful
766+
} else {
767+
UsefulWithWitness(result)
768+
}
769+
},
741770
result => result
742771
}
743772
}).find(|result| result != &NotUseful).unwrap_or(NotUseful)
@@ -750,13 +779,17 @@ fn is_useful<'a, 'tcx>(cx: &MatchCheckCtxt<'a, 'tcx>,
750779
}).collect();
751780
match is_useful(cx, &matrix, &v[1..], witness) {
752781
UsefulWithWitness(pats) => {
753-
let mut new_pats: Vec<_> = constructors.into_iter().map(|constructor| {
782+
let mut new_pats: Vec<_> = constructors.into_iter().filter_map(|constructor| {
754783
let arity = constructor_arity(cx, &constructor, left_ty);
755784
let wild_pats = vec![DUMMY_WILD_PAT; arity];
756785
construct_witness(cx, &constructor, wild_pats, left_ty)
757786
}).collect();
758787
new_pats.extend(pats);
759-
UsefulWithWitness(new_pats)
788+
if new_pats.is_empty() {
789+
NotUseful
790+
} else {
791+
UsefulWithWitness(new_pats)
792+
}
760793
},
761794
result => result
762795
}
@@ -780,7 +813,8 @@ fn is_useful_specialized<'a, 'tcx>(
780813
let matrix = Matrix(m.iter().filter_map(|r| {
781814
specialize(cx, &r[..], &ctor, 0, arity)
782815
}).collect());
783-
match specialize(cx, v, &ctor, 0, arity) {
816+
let spec = specialize(cx, v, &ctor, 0, arity);
817+
match spec {
784818
Some(v) => is_useful(cx, &matrix, &v[..], witness),
785819
None => NotUseful
786820
}

0 commit comments

Comments
 (0)