about summary refs log tree commit diff
path: root/compiler/rustc_pattern_analysis
diff options
context:
space:
mode:
authordianne <diannes.gm@gmail.com>2025-04-20 23:57:09 -0700
committerdianne <diannes.gm@gmail.com>2025-05-06 18:53:55 -0700
commitfb261a179d2c210785b6e9005201e262dac801b5 (patch)
tree17a709e679f327bb95f013ed4c2f5a825637bc1a /compiler/rustc_pattern_analysis
parentcf43bba1e5ec87838fca248f36d940f2923576c2 (diff)
downloadrust-fb261a179d2c210785b6e9005201e262dac801b5.tar.gz
rust-fb261a179d2c210785b6e9005201e262dac801b5.zip
error early when mixing deref patterns with normal constructors
Without adding proper support for mixed exhaustiveness, mixing deref
patterns with normal constructors would either violate
`ConstructorSet::split`'s invariant 4 or 7. We'd either be ignoring rows
with normal constructors or we'd have problems in unspecialization from
non-disjoint constructors. Checking mixed exhaustivenss similarly to how
unions are currently checked should work, but the diagnostics for unions
are confusing. Since mixing deref patterns with normal constructors is
pretty niche (currently it only makes sense for `Cow`), emitting an
error lets us avoid committing to supporting mixed exhaustiveness
without a good answer for the diagnostics.
Diffstat (limited to 'compiler/rustc_pattern_analysis')
-rw-r--r--compiler/rustc_pattern_analysis/messages.ftl4
-rw-r--r--compiler/rustc_pattern_analysis/src/errors.rs14
-rw-r--r--compiler/rustc_pattern_analysis/src/rustc.rs53
3 files changed, 70 insertions, 1 deletions
diff --git a/compiler/rustc_pattern_analysis/messages.ftl b/compiler/rustc_pattern_analysis/messages.ftl
index 41a1d958f10..d3a3107f8e8 100644
--- a/compiler/rustc_pattern_analysis/messages.ftl
+++ b/compiler/rustc_pattern_analysis/messages.ftl
@@ -6,6 +6,10 @@ pattern_analysis_excluside_range_missing_max = exclusive range missing `{$max}`
     .label = this range doesn't match `{$max}` because `..` is an exclusive range
     .suggestion = use an inclusive range instead
 
+pattern_analysis_mixed_deref_pattern_constructors = mix of deref patterns and normal constructors
+    .deref_pattern_label = matches on the result of dereferencing `{$smart_pointer_ty}`
+    .normal_constructor_label = matches directly on `{$smart_pointer_ty}`
+
 pattern_analysis_non_exhaustive_omitted_pattern = some variants are not matched explicitly
     .help = ensure that all variants are matched explicitly by adding the suggested match arms
     .note = the matched value is of type `{$scrut_ty}` and the `non_exhaustive_omitted_patterns` attribute was found
diff --git a/compiler/rustc_pattern_analysis/src/errors.rs b/compiler/rustc_pattern_analysis/src/errors.rs
index e60930d6cd2..156ba973767 100644
--- a/compiler/rustc_pattern_analysis/src/errors.rs
+++ b/compiler/rustc_pattern_analysis/src/errors.rs
@@ -1,5 +1,5 @@
 use rustc_errors::{Diag, EmissionGuarantee, Subdiagnostic};
-use rustc_macros::{LintDiagnostic, Subdiagnostic};
+use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
 use rustc_middle::ty::Ty;
 use rustc_span::Span;
 
@@ -133,3 +133,15 @@ pub(crate) struct NonExhaustiveOmittedPatternLintOnArm {
     pub lint_level: &'static str,
     pub lint_name: &'static str,
 }
+
+#[derive(Diagnostic)]
+#[diag(pattern_analysis_mixed_deref_pattern_constructors)]
+pub(crate) struct MixedDerefPatternConstructors<'tcx> {
+    #[primary_span]
+    pub spans: Vec<Span>,
+    pub smart_pointer_ty: Ty<'tcx>,
+    #[label(pattern_analysis_deref_pattern_label)]
+    pub deref_pattern_label: Span,
+    #[label(pattern_analysis_normal_constructor_label)]
+    pub normal_constructor_label: Span,
+}
diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs
index 050b48d082b..a89d01dcbbe 100644
--- a/compiler/rustc_pattern_analysis/src/rustc.rs
+++ b/compiler/rustc_pattern_analysis/src/rustc.rs
@@ -1106,6 +1106,14 @@ pub fn analyze_match<'p, 'tcx>(
     scrut_ty: Ty<'tcx>,
 ) -> Result<UsefulnessReport<'p, 'tcx>, ErrorGuaranteed> {
     let scrut_ty = tycx.reveal_opaque_ty(scrut_ty);
+
+    // The analysis doesn't support deref patterns mixed with normal constructors; error if present.
+    // FIXME(deref_patterns): This only needs to run when a deref pattern was found during lowering.
+    if tycx.tcx.features().deref_patterns() {
+        let pat_column = PatternColumn::new(arms);
+        detect_mixed_deref_pat_ctors(tycx, &pat_column)?;
+    }
+
     let scrut_validity = PlaceValidity::from_bool(tycx.known_valid_scrutinee);
     let report = compute_match_usefulness(
         tycx,
@@ -1125,6 +1133,51 @@ pub fn analyze_match<'p, 'tcx>(
     Ok(report)
 }
 
+// FIXME(deref_patterns): Currently it's the responsibility of the frontend (rustc or rust-analyzer)
+// to ensure that deref patterns don't appear in the same column as normal constructors. Deref
+// patterns aren't currently implemented in rust-analyzer, but should they be, the columnwise check
+// here could be made generic and shared between frontends.
+fn detect_mixed_deref_pat_ctors<'p, 'tcx>(
+    cx: &RustcPatCtxt<'p, 'tcx>,
+    column: &PatternColumn<'p, RustcPatCtxt<'p, 'tcx>>,
+) -> Result<(), ErrorGuaranteed> {
+    let Some(&ty) = column.head_ty() else {
+        return Ok(());
+    };
+
+    // Check for a mix of deref patterns and normal constructors.
+    let mut normal_ctor_span = None;
+    let mut deref_pat_span = None;
+    for pat in column.iter() {
+        match pat.ctor() {
+            // The analysis can handle mixing deref patterns with wildcards and opaque patterns.
+            Wildcard | Opaque(_) => {}
+            DerefPattern(_) => deref_pat_span = Some(pat.data().span),
+            // Nothing else can be compared to deref patterns in `Constructor::is_covered_by`.
+            _ => normal_ctor_span = Some(pat.data().span),
+        }
+    }
+    if let Some(normal_constructor_label) = normal_ctor_span
+        && let Some(deref_pattern_label) = deref_pat_span
+    {
+        return Err(cx.tcx.dcx().emit_err(errors::MixedDerefPatternConstructors {
+            spans: vec![deref_pattern_label, normal_constructor_label],
+            smart_pointer_ty: ty.inner(),
+            deref_pattern_label,
+            normal_constructor_label,
+        }));
+    }
+
+    // Specialize and recurse into the patterns' fields.
+    let set = column.analyze_ctors(cx, &ty)?;
+    for ctor in set.present {
+        for specialized_column in column.specialize(cx, &ty, &ctor).iter() {
+            detect_mixed_deref_pat_ctors(cx, specialized_column)?;
+        }
+    }
+    Ok(())
+}
+
 struct RecursiveOpaque {
     def_id: DefId,
 }