about summary refs log tree commit diff
path: root/compiler/rustc_pattern_analysis/src/constructor.rs
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2025-05-08 02:16:45 +0000
committerbors <bors@rust-lang.org>2025-05-08 02:16:45 +0000
commit7e552b46af72df390ed233b58a7f51650515b2a8 (patch)
treef8aec230f54c32fdc51d778a9856fc753a6ceb1a /compiler/rustc_pattern_analysis/src/constructor.rs
parentae3e8c6191fb2bf9394ea4201adaf7b1ac496120 (diff)
parent09fed2d2f440fff2179ca9373bb16e40fc81d935 (diff)
downloadrust-7e552b46af72df390ed233b58a7f51650515b2a8.tar.gz
rust-7e552b46af72df390ed233b58a7f51650515b2a8.zip
Auto merge of #140106 - dianne:deref-pat-usefulness, r=Nadrieril
allow deref patterns to participate in exhaustiveness analysis

Per [this proposal](https://hackmd.io/4qDDMcvyQ-GDB089IPcHGg#Exhaustiveness), this PR allows deref patterns to participate in exhaustiveness analysis. Currently all deref patterns enforce `DerefPure` bounds on their scrutinees, so this assumes all patterns it's analyzing are well-behaved. This also doesn't support [mixed exhaustiveness](https://hackmd.io/4qDDMcvyQ-GDB089IPcHGg#Mixed-exhaustiveness), and instead emits an error if deref patterns are used together with normal constructors. I think mixed exhaustiveness would be nice to have (especially if we eventually want to support arbitrary `Deref` impls[^1]), but it'd require more work to get reasonable diagnostics[^2].

Tracking issue for deref patterns: #87121

r? `@Nadrieril`

[^1]: Regardless of whether we support limited exhaustiveness checking for untrusted `Deref` or always require other arms to be exhaustive, I think it'd be useful to allow mixed matching for user-defined smart pointers. And it'd be strange if it worked there but not for `Cow`.

[^2]: I think listing out witnesses of non-exhaustiveness can be confusing when they're not necessarily disjoint, and when you only need to cover some of them, so we'd probably want special formatting and/or explanatory subdiagnostics. And if it's implemented similarly to unions, we'd probably also want some way of merging witnesses; the way witnesses for unions can appear duplicated is pretty unfortunate. I'm not sure yet how the diagnostics should look, especially for deeply nested patterns.
Diffstat (limited to 'compiler/rustc_pattern_analysis/src/constructor.rs')
-rw-r--r--compiler/rustc_pattern_analysis/src/constructor.rs22
1 files changed, 22 insertions, 0 deletions
diff --git a/compiler/rustc_pattern_analysis/src/constructor.rs b/compiler/rustc_pattern_analysis/src/constructor.rs
index 4ce868f014f..f7a4931c111 100644
--- a/compiler/rustc_pattern_analysis/src/constructor.rs
+++ b/compiler/rustc_pattern_analysis/src/constructor.rs
@@ -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);