let deref patterns participate in usefulness/exhaustiveness
This does not yet handle the case of mixed deref patterns with normal constructors; it'll ICE in `Constructor::is_covered_by`. That'll be fixed in a later commit.
This commit is contained in:
@@ -696,6 +696,10 @@ pub enum Constructor<Cx: PatCx> {
|
||||
F128Range(IeeeFloat<QuadS>, IeeeFloat<QuadS>, RangeEnd),
|
||||
/// String literals. Strings are not quite the same as `&[u8]` so we treat them separately.
|
||||
Str(Cx::StrLit),
|
||||
/// Deref patterns (enabled by the `deref_patterns` feature) provide a way of matching on a
|
||||
/// smart pointer ADT through its pointee. They don't directly correspond to ADT constructors,
|
||||
/// and currently are not supported alongside them. Carries the type of the pointee.
|
||||
DerefPattern(Cx::Ty),
|
||||
/// 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.
|
||||
@@ -740,6 +744,7 @@ impl<Cx: PatCx> Clone for Constructor<Cx> {
|
||||
Constructor::F64Range(lo, hi, end) => Constructor::F64Range(*lo, *hi, *end),
|
||||
Constructor::F128Range(lo, hi, end) => Constructor::F128Range(*lo, *hi, *end),
|
||||
Constructor::Str(value) => Constructor::Str(value.clone()),
|
||||
Constructor::DerefPattern(ty) => Constructor::DerefPattern(ty.clone()),
|
||||
Constructor::Opaque(inner) => Constructor::Opaque(inner.clone()),
|
||||
Constructor::Or => Constructor::Or,
|
||||
Constructor::Never => Constructor::Never,
|
||||
@@ -856,6 +861,10 @@ impl<Cx: PatCx> Constructor<Cx> {
|
||||
}
|
||||
(Slice(self_slice), Slice(other_slice)) => self_slice.is_covered_by(*other_slice),
|
||||
|
||||
// Deref patterns only interact with other deref patterns. Prior to usefulness analysis,
|
||||
// we ensure they don't appear alongside any other non-wild non-opaque constructors.
|
||||
(DerefPattern(_), DerefPattern(_)) => true,
|
||||
|
||||
// Opaque constructors don't interact with anything unless they come from the
|
||||
// syntactically identical pattern.
|
||||
(Opaque(self_id), Opaque(other_id)) => self_id == other_id,
|
||||
@@ -932,6 +941,7 @@ impl<Cx: PatCx> Constructor<Cx> {
|
||||
F64Range(lo, hi, end) => write!(f, "{lo}{end}{hi}")?,
|
||||
F128Range(lo, hi, end) => write!(f, "{lo}{end}{hi}")?,
|
||||
Str(value) => write!(f, "{value:?}")?,
|
||||
DerefPattern(_) => write!(f, "deref!({:?})", fields.next().unwrap())?,
|
||||
Opaque(..) => write!(f, "<constant pattern>")?,
|
||||
Or => {
|
||||
for pat in fields {
|
||||
@@ -1039,8 +1049,17 @@ impl<Cx: PatCx> ConstructorSet<Cx> {
|
||||
let mut missing = Vec::new();
|
||||
// Constructors in `ctors`, except wildcards and opaques.
|
||||
let mut seen = Vec::new();
|
||||
// If we see a deref pattern, it must be the only non-wildcard non-opaque constructor; we
|
||||
// ensure this prior to analysis.
|
||||
let mut deref_pat_present = false;
|
||||
for ctor in ctors.cloned() {
|
||||
match ctor {
|
||||
DerefPattern(..) => {
|
||||
if !deref_pat_present {
|
||||
deref_pat_present = true;
|
||||
present.push(ctor);
|
||||
}
|
||||
}
|
||||
Opaque(..) => present.push(ctor),
|
||||
Wildcard => {} // discard wildcards
|
||||
_ => seen.push(ctor),
|
||||
@@ -1048,6 +1067,9 @@ impl<Cx: PatCx> ConstructorSet<Cx> {
|
||||
}
|
||||
|
||||
match self {
|
||||
_ if deref_pat_present => {
|
||||
// Deref patterns are the only constructor; nothing is missing.
|
||||
}
|
||||
ConstructorSet::Struct { empty } => {
|
||||
if !seen.is_empty() {
|
||||
present.push(Struct);
|
||||
|
||||
@@ -269,6 +269,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
|
||||
}
|
||||
_ => bug!("bad slice pattern {:?} {:?}", ctor, ty),
|
||||
},
|
||||
DerefPattern(pointee_ty) => reveal_and_alloc(cx, once(pointee_ty.inner())),
|
||||
Bool(..) | IntRange(..) | F16Range(..) | F32Range(..) | F64Range(..)
|
||||
| F128Range(..) | Str(..) | Opaque(..) | Never | NonExhaustive | Hidden | Missing
|
||||
| PrivateUninhabited | Wildcard => &[],
|
||||
@@ -296,7 +297,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
|
||||
}
|
||||
_ => bug!("Unexpected type for constructor `{ctor:?}`: {ty:?}"),
|
||||
},
|
||||
Ref => 1,
|
||||
Ref | DerefPattern(_) => 1,
|
||||
Slice(slice) => slice.arity(),
|
||||
Bool(..) | IntRange(..) | F16Range(..) | F32Range(..) | F64Range(..)
|
||||
| F128Range(..) | Str(..) | Opaque(..) | Never | NonExhaustive | Hidden | Missing
|
||||
@@ -493,11 +494,15 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
|
||||
),
|
||||
};
|
||||
}
|
||||
PatKind::DerefPattern { .. } => {
|
||||
// FIXME(deref_patterns): At least detect that `box _` is irrefutable.
|
||||
fields = vec![];
|
||||
arity = 0;
|
||||
ctor = Opaque(OpaqueId::new());
|
||||
PatKind::DerefPattern { subpattern, .. } => {
|
||||
// NB(deref_patterns): This assumes the deref pattern is matching on a trusted
|
||||
// `DerefPure` type. If the `Deref` impl isn't trusted, exhaustiveness must take
|
||||
// into account that multiple calls to deref may return different results. Hence
|
||||
// multiple deref! patterns cannot be exhaustive together unless each is exhaustive
|
||||
// by itself.
|
||||
fields = vec![self.lower_pat(subpattern).at_index(0)];
|
||||
arity = 1;
|
||||
ctor = DerefPattern(cx.reveal_opaque_ty(subpattern.ty));
|
||||
}
|
||||
PatKind::Leaf { subpatterns } | PatKind::Variant { subpatterns, .. } => {
|
||||
match ty.kind() {
|
||||
@@ -874,6 +879,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
|
||||
print::write_ref_like(&mut s, pat.ty().inner(), &print(&pat.fields[0])).unwrap();
|
||||
s
|
||||
}
|
||||
DerefPattern(_) => format!("deref!({})", print(&pat.fields[0])),
|
||||
Slice(slice) => {
|
||||
let (prefix_len, has_dot_dot) = match slice.kind {
|
||||
SliceKind::FixedLen(len) => (len, false),
|
||||
|
||||
@@ -702,6 +702,7 @@
|
||||
//! - `ui/consts/const_in_pattern`
|
||||
//! - `ui/rfc-2008-non-exhaustive`
|
||||
//! - `ui/half-open-range-patterns`
|
||||
//! - `ui/pattern/deref-patterns`
|
||||
//! - probably many others
|
||||
//!
|
||||
//! I (Nadrieril) prefer to put new tests in `ui/pattern/usefulness` unless there's a specific
|
||||
@@ -866,7 +867,8 @@ impl PlaceValidity {
|
||||
/// inside `&` and union fields where validity is reset to `MaybeInvalid`.
|
||||
fn specialize<Cx: PatCx>(self, ctor: &Constructor<Cx>) -> Self {
|
||||
// We preserve validity except when we go inside a reference or a union field.
|
||||
if matches!(ctor, Constructor::Ref | Constructor::UnionField) {
|
||||
if matches!(ctor, Constructor::Ref | Constructor::DerefPattern(_) | Constructor::UnionField)
|
||||
{
|
||||
// Validity of `x: &T` does not imply validity of `*x: T`.
|
||||
MaybeInvalid
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user