1
Fork 0

The only remaining constant patterns are opaque

This commit is contained in:
Nadrieril 2020-10-18 13:48:54 +01:00
parent d1a784e7b9
commit da0ba2f645
4 changed files with 50 additions and 37 deletions

View file

@ -394,9 +394,15 @@ impl<'p, 'tcx> PatStack<'p, 'tcx> {
cx: &mut MatchCheckCtxt<'p, 'tcx>, cx: &mut MatchCheckCtxt<'p, 'tcx>,
constructor: &Constructor<'tcx>, constructor: &Constructor<'tcx>,
ctor_wild_subpatterns: &Fields<'p, 'tcx>, ctor_wild_subpatterns: &Fields<'p, 'tcx>,
is_my_head_ctor: bool,
) -> Option<PatStack<'p, 'tcx>> { ) -> Option<PatStack<'p, 'tcx>> {
let new_fields = let new_fields = specialize_one_pattern(
specialize_one_pattern(cx, self.head(), constructor, ctor_wild_subpatterns)?; cx,
self.head(),
constructor,
ctor_wild_subpatterns,
is_my_head_ctor,
)?;
Some(new_fields.push_on_patstack(&self.0[1..])) Some(new_fields.push_on_patstack(&self.0[1..]))
} }
} }
@ -574,6 +580,7 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> {
cx, cx,
constructor, constructor,
ctor_wild_subpatterns, ctor_wild_subpatterns,
false,
) )
}) })
.collect() .collect()
@ -599,7 +606,9 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> {
SpecializationCache::Incompatible => self SpecializationCache::Incompatible => self
.patterns .patterns
.iter() .iter()
.filter_map(|r| r.specialize_constructor(cx, constructor, ctor_wild_subpatterns)) .filter_map(|r| {
r.specialize_constructor(cx, constructor, ctor_wild_subpatterns, false)
})
.collect(), .collect(),
} }
} }
@ -821,8 +830,6 @@ enum Constructor<'tcx> {
Single, Single,
/// Enum variants. /// Enum variants.
Variant(DefId), Variant(DefId),
/// Literal values.
ConstantValue(&'tcx ty::Const<'tcx>),
/// Ranges of integer literal values (`2`, `2..=5` or `2..5`). /// Ranges of integer literal values (`2`, `2..=5` or `2..5`).
IntRange(IntRange<'tcx>), IntRange(IntRange<'tcx>),
/// Ranges of floating-point literal values (`2.0..=5.2`). /// Ranges of floating-point literal values (`2.0..=5.2`).
@ -831,27 +838,22 @@ enum Constructor<'tcx> {
Str(&'tcx ty::Const<'tcx>), Str(&'tcx ty::Const<'tcx>),
/// Array and slice patterns. /// Array and slice patterns.
Slice(Slice), Slice(Slice),
/// Constants that must not be matched structurally. They are treated as black
/// boxes for the purposes of exhaustiveness: we must not inspect them, and they
/// don't count towards making a match exhaustive.
Opaque,
/// Fake extra constructor for enums that aren't allowed to be matched exhaustively. /// Fake extra constructor for enums that aren't allowed to be matched exhaustively.
NonExhaustive, NonExhaustive,
} }
impl<'tcx> Constructor<'tcx> { impl<'tcx> Constructor<'tcx> {
fn variant_index_for_adt<'a>( fn variant_index_for_adt(&self, adt: &'tcx ty::AdtDef) -> VariantIdx {
&self,
cx: &MatchCheckCtxt<'a, 'tcx>,
adt: &'tcx ty::AdtDef,
) -> VariantIdx {
match *self { match *self {
Variant(id) => adt.variant_index_with_id(id), Variant(id) => adt.variant_index_with_id(id),
Single => { Single => {
assert!(!adt.is_enum()); assert!(!adt.is_enum());
VariantIdx::new(0) VariantIdx::new(0)
} }
ConstantValue(c) => cx
.tcx
.destructure_const(cx.param_env.and(c))
.variant
.expect("destructed const of adt without variant id"),
_ => bug!("bad constructor {:?} for adt {:?}", self, adt), _ => bug!("bad constructor {:?} for adt {:?}", self, adt),
} }
} }
@ -865,7 +867,7 @@ impl<'tcx> Constructor<'tcx> {
match self { match self {
// Those constructors can only match themselves. // Those constructors can only match themselves.
Single | Variant(_) | ConstantValue(..) | Str(..) | FloatRange(..) => { Single | Variant(_) | Str(..) | FloatRange(..) => {
if other_ctors.iter().any(|c| c == self) { vec![] } else { vec![self.clone()] } if other_ctors.iter().any(|c| c == self) { vec![] } else { vec![self.clone()] }
} }
&Slice(slice) => { &Slice(slice) => {
@ -936,6 +938,7 @@ impl<'tcx> Constructor<'tcx> {
} }
// This constructor is never covered by anything else // This constructor is never covered by anything else
NonExhaustive => vec![NonExhaustive], NonExhaustive => vec![NonExhaustive],
Opaque => bug!("unexpected opaque ctor {:?} found in all_ctors", self),
} }
} }
@ -975,7 +978,7 @@ impl<'tcx> Constructor<'tcx> {
PatKind::Variant { PatKind::Variant {
adt_def: adt, adt_def: adt,
substs, substs,
variant_index: self.variant_index_for_adt(cx, adt), variant_index: self.variant_index_for_adt(adt),
subpatterns, subpatterns,
} }
} else { } else {
@ -1014,11 +1017,11 @@ impl<'tcx> Constructor<'tcx> {
PatKind::Slice { prefix, slice: Some(wild), suffix } PatKind::Slice { prefix, slice: Some(wild), suffix }
} }
}, },
&ConstantValue(value) => PatKind::Constant { value },
&Str(value) => PatKind::Constant { value }, &Str(value) => PatKind::Constant { value },
&FloatRange(lo, hi, end) => PatKind::Range(PatRange { lo, hi, end }), &FloatRange(lo, hi, end) => PatKind::Range(PatRange { lo, hi, end }),
IntRange(range) => return range.to_pat(cx.tcx), IntRange(range) => return range.to_pat(cx.tcx),
NonExhaustive => PatKind::Wild, NonExhaustive => PatKind::Wild,
Opaque => bug!("we should not try to apply an opaque constructor {:?}", self),
}; };
Pat { ty, span: DUMMY_SP, kind: Box::new(pat) } Pat { ty, span: DUMMY_SP, kind: Box::new(pat) }
@ -1122,7 +1125,7 @@ impl<'p, 'tcx> Fields<'p, 'tcx> {
// Use T as the sub pattern type of Box<T>. // Use T as the sub pattern type of Box<T>.
Fields::from_single_pattern(wildcard_from_ty(substs.type_at(0))) Fields::from_single_pattern(wildcard_from_ty(substs.type_at(0)))
} else { } else {
let variant = &adt.variants[constructor.variant_index_for_adt(cx, adt)]; let variant = &adt.variants[constructor.variant_index_for_adt(adt)];
// Whether we must not match the fields of this variant exhaustively. // Whether we must not match the fields of this variant exhaustively.
let is_non_exhaustive = let is_non_exhaustive =
variant.is_field_list_non_exhaustive() && !adt.did.is_local(); variant.is_field_list_non_exhaustive() && !adt.did.is_local();
@ -1170,9 +1173,7 @@ impl<'p, 'tcx> Fields<'p, 'tcx> {
} }
_ => bug!("bad slice pattern {:?} {:?}", constructor, ty), _ => bug!("bad slice pattern {:?} {:?}", constructor, ty),
}, },
ConstantValue(..) | Str(..) | FloatRange(..) | IntRange(..) | NonExhaustive => { Str(..) | FloatRange(..) | IntRange(..) | NonExhaustive | Opaque => Fields::empty(),
Fields::empty()
}
}; };
debug!("Fields::wildcards({:?}, {:?}) = {:#?}", constructor, ty, ret); debug!("Fields::wildcards({:?}, {:?}) = {:#?}", constructor, ty, ret);
ret ret
@ -2085,7 +2086,7 @@ fn is_useful_specialized<'p, 'tcx>(
// We cache the result of `Fields::wildcards` because it is used a lot. // We cache the result of `Fields::wildcards` because it is used a lot.
let ctor_wild_subpatterns = Fields::wildcards(cx, &ctor, ty); let ctor_wild_subpatterns = Fields::wildcards(cx, &ctor, ty);
let matrix = matrix.specialize_constructor(cx, &ctor, &ctor_wild_subpatterns); let matrix = matrix.specialize_constructor(cx, &ctor, &ctor_wild_subpatterns);
v.specialize_constructor(cx, &ctor, &ctor_wild_subpatterns) v.specialize_constructor(cx, &ctor, &ctor_wild_subpatterns, true)
.map(|v| is_useful(cx, &matrix, &v, witness_preference, hir_id, is_under_guard, false)) .map(|v| is_useful(cx, &matrix, &v, witness_preference, hir_id, is_under_guard, false))
.map(|u| u.apply_constructor(cx, &ctor, ty, &ctor_wild_subpatterns)) .map(|u| u.apply_constructor(cx, &ctor, ty, &ctor_wild_subpatterns))
.unwrap_or(NotUseful) .unwrap_or(NotUseful)
@ -2112,7 +2113,7 @@ fn pat_constructor<'tcx>(
match value.ty.kind() { match value.ty.kind() {
ty::Float(_) => Some(FloatRange(value, value, RangeEnd::Included)), ty::Float(_) => Some(FloatRange(value, value, RangeEnd::Included)),
ty::Ref(_, t, _) if t.is_str() => Some(Str(value)), ty::Ref(_, t, _) if t.is_str() => Some(Str(value)),
_ => Some(ConstantValue(value)), _ => Some(Opaque),
} }
} }
} }
@ -2461,15 +2462,26 @@ fn specialize_one_pattern<'p, 'tcx>(
pat: &'p Pat<'tcx>, pat: &'p Pat<'tcx>,
constructor: &Constructor<'tcx>, constructor: &Constructor<'tcx>,
ctor_wild_subpatterns: &Fields<'p, 'tcx>, ctor_wild_subpatterns: &Fields<'p, 'tcx>,
is_its_own_ctor: bool, // Whether `ctor` is known to be derived from `pat`
) -> Option<Fields<'p, 'tcx>> { ) -> Option<Fields<'p, 'tcx>> {
if let NonExhaustive = constructor { if let NonExhaustive = constructor {
// Only a wildcard pattern can match the special extra constructor // Only a wildcard pattern can match the special extra constructor.
if !pat.is_wildcard() { if !pat.is_wildcard() {
return None; return None;
} }
return Some(Fields::empty()); return Some(Fields::empty());
} }
if let Opaque = constructor {
// Only a wildcard pattern can match an opaque constant, unless we're specializing the
// value against its own constructor.
if is_its_own_ctor || pat.is_wildcard() {
return Some(Fields::empty());
} else {
return None;
}
}
let result = match *pat.kind { let result = match *pat.kind {
PatKind::AscribeUserType { .. } => bug!(), // Handled by `expand_pattern` PatKind::AscribeUserType { .. } => bug!(), // Handled by `expand_pattern`
@ -2491,7 +2503,6 @@ fn specialize_one_pattern<'p, 'tcx>(
PatKind::Constant { .. } | PatKind::Range { .. } => { PatKind::Constant { .. } | PatKind::Range { .. } => {
match constructor { match constructor {
Single => {}
IntRange(ctor) => { IntRange(ctor) => {
let pat = IntRange::from_pat(cx.tcx, cx.param_env, pat)?; let pat = IntRange::from_pat(cx.tcx, cx.param_env, pat)?;
ctor.intersection(cx.tcx, &pat)?; ctor.intersection(cx.tcx, &pat)?;
@ -2514,7 +2525,7 @@ fn specialize_one_pattern<'p, 'tcx>(
return None; return None;
} }
} }
ConstantValue(ctor_value) | Str(ctor_value) => { Str(ctor_value) => {
let pat_value = match *pat.kind { let pat_value = match *pat.kind {
PatKind::Constant { value } => value, PatKind::Constant { value } => value,
_ => span_bug!( _ => span_bug!(
@ -2532,7 +2543,9 @@ fn specialize_one_pattern<'p, 'tcx>(
} }
} }
_ => { _ => {
span_bug!(pat.span, "unexpected pattern {:?} with ctor {:?}", pat, constructor) // If we reach here, we must be trying to inspect an opaque constant. Thus we skip
// the row.
return None;
} }
} }
Some(Fields::empty()) Some(Fields::empty())

View file

@ -158,6 +158,13 @@ crate enum PatKind<'tcx> {
subpattern: Pat<'tcx>, subpattern: Pat<'tcx>,
}, },
/// One of the following:
/// * `&str`, which will be handled as a string pattern and thus exhaustiveness
/// checking will detect if you use the same string twice in different patterns.
/// * integer, bool, char or float, which will be handled by exhaustivenes to cover exactly
/// its own value, similar to `&str`, but these values are much simpler.
/// * Opaque constants, that must not be matched structurally. So anything that does not derive
/// `PartialEq` and `Eq`.
Constant { Constant {
value: &'tcx ty::Const<'tcx>, value: &'tcx ty::Const<'tcx>,
}, },

View file

@ -106,8 +106,7 @@ fn main() {
match QUUX { match QUUX {
QUUX => {} QUUX => {}
QUUX => {} // should not be emitting unreachable warning QUUX => {}
//~^ ERROR unreachable pattern
_ => {} _ => {}
} }
} }

View file

@ -144,11 +144,5 @@ error: unreachable pattern
LL | _ => {} // should not be emitting unreachable warning LL | _ => {} // should not be emitting unreachable warning
| ^ | ^
error: unreachable pattern error: aborting due to 22 previous errors
--> $DIR/consts-opaque.rs:109:9
|
LL | QUUX => {} // should not be emitting unreachable warning
| ^^^^
error: aborting due to 23 previous errors