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:
dianne
2025-04-17 02:33:24 -07:00
parent 669c1ab967
commit b41d8bde00
8 changed files with 47 additions and 24 deletions

View File

@@ -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);

View File

@@ -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),

View File

@@ -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 {