diff options
| author | dianne <diannes.gm@gmail.com> | 2025-07-04 21:17:40 -0700 |
|---|---|---|
| committer | dianne <diannes.gm@gmail.com> | 2025-07-04 23:47:31 -0700 |
| commit | 50061f3b11f51d7a6e3acd8ce793a1f17f99b597 (patch) | |
| tree | 361e856dd3e167443f287dbf2a45d3e28ac28656 /compiler/rustc_pattern_analysis | |
| parent | d98a5da813da67eb189387b8ccfb73cf481275d8 (diff) | |
| download | rust-50061f3b11f51d7a6e3acd8ce793a1f17f99b597.tar.gz rust-50061f3b11f51d7a6e3acd8ce793a1f17f99b597.zip | |
always check for mixed deref pattern and normal constructors
This makes it work for box patterns and in rust-analyzer.
Diffstat (limited to 'compiler/rustc_pattern_analysis')
| -rw-r--r-- | compiler/rustc_pattern_analysis/src/checks.rs | 50 | ||||
| -rw-r--r-- | compiler/rustc_pattern_analysis/src/lib.rs | 10 | ||||
| -rw-r--r-- | compiler/rustc_pattern_analysis/src/rustc.rs | 67 | ||||
| -rw-r--r-- | compiler/rustc_pattern_analysis/src/usefulness.rs | 5 | ||||
| -rw-r--r-- | compiler/rustc_pattern_analysis/tests/common/mod.rs | 9 |
5 files changed, 88 insertions, 53 deletions
diff --git a/compiler/rustc_pattern_analysis/src/checks.rs b/compiler/rustc_pattern_analysis/src/checks.rs new file mode 100644 index 00000000000..88ccaa1e0e5 --- /dev/null +++ b/compiler/rustc_pattern_analysis/src/checks.rs @@ -0,0 +1,50 @@ +//! Contains checks that must be run to validate matches before performing usefulness analysis. + +use crate::constructor::Constructor::*; +use crate::pat_column::PatternColumn; +use crate::{MatchArm, PatCx}; + +/// Validate that deref patterns and normal constructors aren't used to match on the same place. +pub(crate) fn detect_mixed_deref_pat_ctors<'p, Cx: PatCx>( + cx: &Cx, + arms: &[MatchArm<'p, Cx>], +) -> Result<(), Cx::Error> { + let pat_column = PatternColumn::new(arms); + detect_mixed_deref_pat_ctors_inner(cx, &pat_column) +} + +fn detect_mixed_deref_pat_ctors_inner<'p, Cx: PatCx>( + cx: &Cx, + column: &PatternColumn<'p, Cx>, +) -> Result<(), Cx::Error> { + let Some(ty) = column.head_ty() else { + return Ok(()); + }; + + // Check for a mix of deref patterns and normal constructors. + let mut deref_pat = None; + let mut normal_pat = 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 = Some(pat), + // Nothing else can be compared to deref patterns in `Constructor::is_covered_by`. + _ => normal_pat = Some(pat), + } + } + if let Some(deref_pat) = deref_pat + && let Some(normal_pat) = normal_pat + { + return Err(cx.report_mixed_deref_pat_ctors(deref_pat, normal_pat)); + } + + // 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_inner(cx, specialized_column)?; + } + } + Ok(()) +} diff --git a/compiler/rustc_pattern_analysis/src/lib.rs b/compiler/rustc_pattern_analysis/src/lib.rs index 2b85d7b26ce..129fd38725c 100644 --- a/compiler/rustc_pattern_analysis/src/lib.rs +++ b/compiler/rustc_pattern_analysis/src/lib.rs @@ -8,6 +8,7 @@ #![allow(unused_crate_dependencies)] // tidy-alphabetical-end +pub(crate) mod checks; pub mod constructor; #[cfg(feature = "rustc")] pub mod errors; @@ -107,6 +108,15 @@ pub trait PatCx: Sized + fmt::Debug { _gapped_with: &[&DeconstructedPat<Self>], ) { } + + /// The current implementation of deref patterns requires that they can't match on the same + /// place as a normal constructor. Since this isn't caught by type-checking, we check it in the + /// `PatCx` before running the analysis. This reports an error if the check fails. + fn report_mixed_deref_pat_ctors( + &self, + deref_pat: &DeconstructedPat<Self>, + normal_pat: &DeconstructedPat<Self>, + ) -> Self::Error; } /// The arm of a match expression. diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index e53cebc59ba..e9795126db6 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -1027,6 +1027,21 @@ impl<'p, 'tcx: 'p> PatCx for RustcPatCtxt<'p, 'tcx> { ); } } + + fn report_mixed_deref_pat_ctors( + &self, + deref_pat: &crate::pat::DeconstructedPat<Self>, + normal_pat: &crate::pat::DeconstructedPat<Self>, + ) -> Self::Error { + let deref_pattern_label = deref_pat.data().span; + let normal_constructor_label = normal_pat.data().span; + self.tcx.dcx().emit_err(errors::MixedDerefPatternConstructors { + spans: vec![deref_pattern_label, normal_constructor_label], + smart_pointer_ty: deref_pat.ty().inner(), + deref_pattern_label, + normal_constructor_label, + }) + } } /// Recursively expand this pattern into its subpatterns. Only useful for or-patterns. @@ -1055,13 +1070,6 @@ pub fn analyze_match<'p, '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, @@ -1080,48 +1088,3 @@ 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(()) -} diff --git a/compiler/rustc_pattern_analysis/src/usefulness.rs b/compiler/rustc_pattern_analysis/src/usefulness.rs index c348cd508f9..fb94b4afebb 100644 --- a/compiler/rustc_pattern_analysis/src/usefulness.rs +++ b/compiler/rustc_pattern_analysis/src/usefulness.rs @@ -720,7 +720,7 @@ use tracing::{debug, instrument}; use self::PlaceValidity::*; use crate::constructor::{Constructor, ConstructorSet, IntRange}; use crate::pat::{DeconstructedPat, PatId, PatOrWild, WitnessPat}; -use crate::{MatchArm, PatCx, PrivateUninhabitedField}; +use crate::{MatchArm, PatCx, PrivateUninhabitedField, checks}; #[cfg(not(feature = "rustc"))] pub fn ensure_sufficient_stack<R>(f: impl FnOnce() -> R) -> R { f() @@ -1836,6 +1836,9 @@ pub fn compute_match_usefulness<'p, Cx: PatCx>( scrut_validity: PlaceValidity, complexity_limit: usize, ) -> Result<UsefulnessReport<'p, Cx>, Cx::Error> { + // The analysis doesn't support deref patterns mixed with normal constructors; error if present. + checks::detect_mixed_deref_pat_ctors(tycx, arms)?; + let mut cx = UsefulnessCtxt { tycx, branch_usefulness: FxHashMap::default(), diff --git a/compiler/rustc_pattern_analysis/tests/common/mod.rs b/compiler/rustc_pattern_analysis/tests/common/mod.rs index 8980b644f59..0b939ef7816 100644 --- a/compiler/rustc_pattern_analysis/tests/common/mod.rs +++ b/compiler/rustc_pattern_analysis/tests/common/mod.rs @@ -1,6 +1,7 @@ use rustc_pattern_analysis::constructor::{ Constructor, ConstructorSet, IntRange, MaybeInfiniteInt, RangeEnd, VariantVisibility, }; +use rustc_pattern_analysis::pat::DeconstructedPat; use rustc_pattern_analysis::usefulness::{PlaceValidity, UsefulnessReport}; use rustc_pattern_analysis::{MatchArm, PatCx, PrivateUninhabitedField}; @@ -184,6 +185,14 @@ impl PatCx for Cx { fn complexity_exceeded(&self) -> Result<(), Self::Error> { Err(()) } + + fn report_mixed_deref_pat_ctors( + &self, + _deref_pat: &DeconstructedPat<Self>, + _normal_pat: &DeconstructedPat<Self>, + ) -> Self::Error { + panic!("`rustc_pattern_analysis::tests` currently doesn't test deref pattern errors") + } } /// Construct a single pattern; see `pats!()`. |
