Skip to content

Commit

Permalink
Check for uninhabited types in sub-patterns (rust-lang#12609)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
canndrew committed Oct 6, 2016
1 parent 46957f0 commit a0026e6
Showing 1 changed file with 60 additions and 26 deletions.
86 changes: 60 additions & 26 deletions src/librustc_const_eval/check_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ pub enum Constructor {
SliceWithSubslice(usize, usize)
}

#[derive(Clone, PartialEq)]
#[derive(Clone, PartialEq, Debug)]
enum Usefulness {
Useful,
UsefulWithWitness(Vec<P<Pat>>),
Expand Down Expand Up @@ -569,18 +569,31 @@ impl<'a, 'tcx> StaticInliner<'a, 'tcx> {
/// 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> {
pats: Vec<&Pat>, left_ty: Ty<'tcx>) -> Option<P<Pat>> {
let tcx = cx.tcx;
let pats_len = pats.len();
let mut pats = pats.into_iter().map(|p| P((*p).clone()));
let mut pats_owned = pats.iter().map(|p| P((**p).clone()));
let pat = match left_ty.sty {
ty::TyTuple(..) => PatKind::Tuple(pats.collect(), None),

ty::TyAdt(adt, _) => {
ty::TyTuple(ref inner_tys) => {
if pats.iter().zip(inner_tys.iter()).any(|(p, t)| {
*p == DUMMY_WILD_PAT && t.is_uninhabited(tcx)
}) {
return None;
}
PatKind::Tuple(pats_owned.collect(), None)
},
ty::TyAdt(adt, substs) => {
let v = ctor.variant_for_adt(adt);
if pats.iter().zip(v.fields.iter()).any(|(p, f)| {
*p == DUMMY_WILD_PAT &&
f.ty(tcx, substs).is_uninhabited(tcx)
}) {
return None;
};
match v.ctor_kind {
CtorKind::Fictive => {
let field_pats: hir::HirVec<_> = v.fields.iter()
.zip(pats)
.zip(pats_owned)
.filter(|&(_, ref pat)| pat.node != PatKind::Wild)
.map(|(field, pat)| Spanned {
span: DUMMY_SP,
Expand All @@ -594,30 +607,40 @@ fn construct_witness<'a,'tcx>(cx: &MatchCheckCtxt<'a,'tcx>, ctor: &Constructor,
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)
PatKind::TupleStruct(def_to_path(cx.tcx, v.did), pats_owned.collect(), None)
}
CtorKind::Const => {
PatKind::Path(None, def_to_path(cx.tcx, v.did))
}
}
}

ty::TyRef(_, ty::TypeAndMut { mutbl, .. }) => {
ty::TyRef(_, ty::TypeAndMut { mutbl, ty: inner_ty }) => {
assert_eq!(pats_len, 1);
PatKind::Ref(pats.nth(0).unwrap(), mutbl)
let pat = pats_owned.nth(0).unwrap();
if *pat == *DUMMY_WILD_PAT && inner_ty.is_uninhabited(tcx) {
return None;
}
PatKind::Ref(pat, mutbl)
}

ty::TySlice(_) => match ctor {
ty::TySlice(ref inner_ty) => match ctor {
&Slice(n) => {
assert_eq!(pats_len, n);
PatKind::Slice(pats.collect(), None, hir::HirVec::new())
if inner_ty.is_uninhabited(tcx) && pats.iter().any(|p| *p == DUMMY_WILD_PAT) {
return None;
}
PatKind::Slice(pats_owned.collect(), None, hir::HirVec::new())
},
_ => unreachable!()
},

ty::TyArray(_, len) => {
ty::TyArray(ref inner_ty, len) => {
assert_eq!(pats_len, len);
PatKind::Slice(pats.collect(), None, hir::HirVec::new())
if inner_ty.is_uninhabited(tcx) && pats.iter().any(|p| *p == DUMMY_WILD_PAT) {
return None;
}
PatKind::Slice(pats_owned.collect(), None, hir::HirVec::new())
}

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

P(hir::Pat {
Some(P(hir::Pat {
id: DUMMY_NODE_ID,
node: pat,
span: DUMMY_SP
})
}))
}

impl Constructor {
Expand Down Expand Up @@ -724,20 +747,26 @@ fn is_useful<'a, 'tcx>(cx: &MatchCheckCtxt<'a, 'tcx>,
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 all_cons = all_constructors(cx, left_ty, max_slice_length);
all_cons.into_iter().map(|c| {
let spec = is_useful_specialized(cx, matrix, v, c.clone(), left_ty, witness);
match spec {
UsefulWithWitness(pats) => {
let arity = constructor_arity(cx, &c, left_ty);
let mut result = {
let mut result: Vec<_> = {
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)]
construct_witness(cx, &c, subpats, left_ty).into_iter().collect()
};
result.extend(pats.into_iter().skip(arity));
result
}),
if result.is_empty() {
NotUseful
} else {
UsefulWithWitness(result)
}
},
result => result
}
}).find(|result| result != &NotUseful).unwrap_or(NotUseful)
Expand All @@ -750,13 +779,17 @@ fn is_useful<'a, 'tcx>(cx: &MatchCheckCtxt<'a, 'tcx>,
}).collect();
match is_useful(cx, &matrix, &v[1..], witness) {
UsefulWithWitness(pats) => {
let mut new_pats: Vec<_> = constructors.into_iter().map(|constructor| {
let mut new_pats: Vec<_> = constructors.into_iter().filter_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)
if new_pats.is_empty() {
NotUseful
} else {
UsefulWithWitness(new_pats)
}
},
result => result
}
Expand All @@ -780,7 +813,8 @@ fn is_useful_specialized<'a, 'tcx>(
let matrix = Matrix(m.iter().filter_map(|r| {
specialize(cx, &r[..], &ctor, 0, arity)
}).collect());
match specialize(cx, v, &ctor, 0, arity) {
let spec = specialize(cx, v, &ctor, 0, arity);
match spec {
Some(v) => is_useful(cx, &matrix, &v[..], witness),
None => NotUseful
}
Expand Down

0 comments on commit a0026e6

Please sign in to comment.