From a0026e6de59d515fdc3ac780f4405e97e6ccb542 Mon Sep 17 00:00:00 2001 From: Andrew Cann Date: Wed, 14 Sep 2016 22:38:25 +0800 Subject: [PATCH] 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. --- src/librustc_const_eval/check_match.rs | 86 ++++++++++++++++++-------- 1 file changed, 60 insertions(+), 26 deletions(-) diff --git a/src/librustc_const_eval/check_match.rs b/src/librustc_const_eval/check_match.rs index cf54f5908ca9e..c2ff5291309ca 100644 --- a/src/librustc_const_eval/check_match.rs +++ b/src/librustc_const_eval/check_match.rs @@ -129,7 +129,7 @@ pub enum Constructor { SliceWithSubslice(usize, usize) } -#[derive(Clone, PartialEq)] +#[derive(Clone, PartialEq, Debug)] enum Usefulness { Useful, UsefulWithWitness(Vec>), @@ -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 { + pats: Vec<&Pat>, left_ty: Ty<'tcx>) -> Option> { + 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, @@ -594,7 +607,7 @@ 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)) @@ -602,22 +615,32 @@ fn construct_witness<'a,'tcx>(cx: &MatchCheckCtxt<'a,'tcx>, ctor: &Constructor, } } - 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()) } _ => { @@ -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 { @@ -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) @@ -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 } @@ -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 }