diff --git a/compiler/rustc_pattern_analysis/messages.ftl b/compiler/rustc_pattern_analysis/messages.ftl index 41a1d958f1094..d3a3107f8e898 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/constructor.rs b/compiler/rustc_pattern_analysis/src/constructor.rs index 4ce868f014f42..f7a4931c11146 100644 --- a/compiler/rustc_pattern_analysis/src/constructor.rs +++ b/compiler/rustc_pattern_analysis/src/constructor.rs @@ -696,6 +696,10 @@ pub enum Constructor { F128Range(IeeeFloat, IeeeFloat, 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 Clone for Constructor { 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 Constructor { } (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 Constructor { 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, "")?, Or => { for pat in fields { @@ -1039,8 +1049,17 @@ impl ConstructorSet { 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 ConstructorSet { } 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); diff --git a/compiler/rustc_pattern_analysis/src/errors.rs b/compiler/rustc_pattern_analysis/src/errors.rs index e60930d6cd21c..156ba9737673e 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, + 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 7c12f69f14c1a..ac9cae00c7e9b 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -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,14 @@ 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, any deref pattern that can + // fail (possibly due to expanding or-patterns inside it) must not influence + // exhaustiveness analysis. + 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 +878,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), @@ -1100,6 +1105,14 @@ pub fn analyze_match<'p, 'tcx>( scrut_ty: Ty<'tcx>, ) -> Result, 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, @@ -1119,6 +1132,47 @@ pub fn analyze_match<'p, 'tcx>( Ok(report) } +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, } diff --git a/compiler/rustc_pattern_analysis/src/usefulness.rs b/compiler/rustc_pattern_analysis/src/usefulness.rs index 11ebbea07fa4e..53638f2a57dd3 100644 --- a/compiler/rustc_pattern_analysis/src/usefulness.rs +++ b/compiler/rustc_pattern_analysis/src/usefulness.rs @@ -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(self, ctor: &Constructor) -> 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 { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs index 91eb59fb3140f..ef23b66b0501d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs @@ -301,6 +301,7 @@ impl<'db> MatchCheckCtx<'db> { // ignore this issue. Ref => PatKind::Deref { subpattern: subpatterns.next().unwrap() }, Slice(_) => unimplemented!(), + DerefPattern(_) => unimplemented!(), &Str(void) => match void {}, Wildcard | NonExhaustive | Hidden | PrivateUninhabited => PatKind::Wild, Never => PatKind::Never, @@ -351,6 +352,7 @@ impl PatCx for MatchCheckCtx<'_> { }, Ref => 1, Slice(..) => unimplemented!(), + DerefPattern(..) => unimplemented!(), Never | Bool(..) | IntRange(..) | F16Range(..) | F32Range(..) | F64Range(..) | F128Range(..) | Str(..) | Opaque(..) | NonExhaustive | PrivateUninhabited | Hidden | Missing | Wildcard => 0, @@ -412,6 +414,7 @@ impl PatCx for MatchCheckCtx<'_> { } }, Slice(_) => unreachable!("Found a `Slice` constructor in match checking"), + DerefPattern(_) => unreachable!("Found a `DerefPattern` constructor in match checking"), Never | Bool(..) | IntRange(..) | F16Range(..) | F32Range(..) | F64Range(..) | F128Range(..) | Str(..) | Opaque(..) | NonExhaustive | PrivateUninhabited | Hidden | Missing | Wildcard => { diff --git a/tests/ui/pattern/deref-patterns/bindings.rs b/tests/ui/pattern/deref-patterns/bindings.rs index c14d57f3f24e6..3a253966e0089 100644 --- a/tests/ui/pattern/deref-patterns/bindings.rs +++ b/tests/ui/pattern/deref-patterns/bindings.rs @@ -11,7 +11,6 @@ fn simple_vec(vec: Vec) -> u32 { deref!([x]) => x, deref!([1, x]) => x + 200, deref!(ref slice) => slice.iter().sum(), - _ => 2000, } } @@ -23,7 +22,6 @@ fn simple_vec(vec: Vec) -> u32 { [x] => x, [1, x] => x + 200, deref!(ref slice) => slice.iter().sum(), - _ => 2000, } } @@ -59,9 +57,8 @@ fn ref_mut(val: u32) -> u32 { deref!(x) => { *x = val; } - _ => unreachable!(), } - let deref!(x) = &b else { unreachable!() }; + let deref!(x) = &b; *x } @@ -73,9 +70,8 @@ fn ref_mut(val: u32) -> u32 { (x,) => { *x = val; } - _ => unreachable!(), } - let (x,) = &b else { unreachable!() }; + let (x,) = &b; *x } diff --git a/tests/ui/pattern/deref-patterns/closure_capture.rs b/tests/ui/pattern/deref-patterns/closure_capture.rs index 08586b6c7abdf..3c237962a5211 100644 --- a/tests/ui/pattern/deref-patterns/closure_capture.rs +++ b/tests/ui/pattern/deref-patterns/closure_capture.rs @@ -5,7 +5,7 @@ fn main() { let b = Box::new("aaa".to_string()); let f = || { - let deref!(ref s) = b else { unreachable!() }; + let deref!(ref s) = b; assert_eq!(s.len(), 3); }; assert_eq!(b.len(), 3); @@ -22,7 +22,7 @@ fn main() { let mut b = Box::new("aaa".to_string()); let mut f = || { - let deref!(ref mut s) = b else { unreachable!() }; + let deref!(ref mut s) = b; s.push_str("aa"); }; f(); diff --git a/tests/ui/pattern/deref-patterns/implicit-cow-deref.rs b/tests/ui/pattern/deref-patterns/implicit-cow-deref.rs index a9b8de8601078..b580a713c60a1 100644 --- a/tests/ui/pattern/deref-patterns/implicit-cow-deref.rs +++ b/tests/ui/pattern/deref-patterns/implicit-cow-deref.rs @@ -10,7 +10,6 @@ fn main() { match cow { [..] => {} - _ => unreachable!(), } match cow { @@ -21,14 +20,12 @@ fn main() { match Box::new(&cow) { Cow::Borrowed { 0: _ } => {} Cow::Owned { 0: _ } => unreachable!(), - _ => unreachable!(), } let cow_of_cow: Cow<'_, Cow<'static, [u8]>> = Cow::Owned(cow); match cow_of_cow { [..] => {} - _ => unreachable!(), } // This matches on the outer `Cow` (the owned one). @@ -40,6 +37,5 @@ fn main() { match Box::new(&cow_of_cow) { Cow::Borrowed { 0: _ } => unreachable!(), Cow::Owned { 0: _ } => {} - _ => unreachable!(), } } diff --git a/tests/ui/pattern/deref-patterns/usefulness/empty-types.rs b/tests/ui/pattern/deref-patterns/usefulness/empty-types.rs new file mode 100644 index 0000000000000..03419030e72fd --- /dev/null +++ b/tests/ui/pattern/deref-patterns/usefulness/empty-types.rs @@ -0,0 +1,47 @@ +//! Test that the place behind a deref pattern is treated as maybe-invalid, and thus empty arms +//! cannot be omitted. This is handled the same as for refs and union fields, so this leaves the +//! bulk of the testing to `tests/ui/pattern/usefulness/empty-types.rs`. +// FIXME(deref_patterns): On stabilization, cases for deref patterns could be worked into that file +// to keep the tests for empty types in one place and test more thoroughly. +#![feature(deref_patterns)] +#![expect(incomplete_features)] +#![deny(unreachable_patterns)] + +enum Void {} + +fn main() { + // Sanity check: matching on an empty type without pointer indirection lets us omit arms. + let opt_void: Option = None; + match opt_void { + None => {} + } + + // But if we hide it behind a smart pointer, we need an arm. + let box_opt_void: Box> = Box::new(None); + match box_opt_void { + //~^ ERROR non-exhaustive patterns: `deref!(Some(_))` not covered + None => {} + } + match box_opt_void { + None => {} + Some(_) => {} + } + match box_opt_void { + None => {} + _ => {} + } + + // For consistency, this behaves the same as if we manually dereferenced the scrutinee. + match *box_opt_void { + //~^ ERROR non-exhaustive patterns: `Some(_)` not covered + None => {} + } + match *box_opt_void { + None => {} + Some(_) => {} + } + match *box_opt_void { + None => {} + _ => {} + } +} diff --git a/tests/ui/pattern/deref-patterns/usefulness/empty-types.stderr b/tests/ui/pattern/deref-patterns/usefulness/empty-types.stderr new file mode 100644 index 0000000000000..e32477085661c --- /dev/null +++ b/tests/ui/pattern/deref-patterns/usefulness/empty-types.stderr @@ -0,0 +1,38 @@ +error[E0004]: non-exhaustive patterns: `deref!(Some(_))` not covered + --> $DIR/empty-types.rs:21:11 + | +LL | match box_opt_void { + | ^^^^^^^^^^^^ pattern `deref!(Some(_))` not covered + | +note: `Box>` defined here + --> $SRC_DIR/alloc/src/boxed.rs:LL:COL + = note: the matched value is of type `Box>` + = note: `Void` is uninhabited but is not being matched by value, so a wildcard `_` is required +help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown + | +LL ~ None => {}, +LL + deref!(Some(_)) => todo!() + | + +error[E0004]: non-exhaustive patterns: `Some(_)` not covered + --> $DIR/empty-types.rs:35:11 + | +LL | match *box_opt_void { + | ^^^^^^^^^^^^^ pattern `Some(_)` not covered + | +note: `Option` defined here + --> $SRC_DIR/core/src/option.rs:LL:COL + ::: $SRC_DIR/core/src/option.rs:LL:COL + | + = note: not covered + = note: the matched value is of type `Option` + = note: `Void` is uninhabited but is not being matched by value, so a wildcard `_` is required +help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown + | +LL ~ None => {}, +LL + Some(_) => todo!() + | + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0004`. diff --git a/tests/ui/pattern/deref-patterns/usefulness/mixed-constructors.rs b/tests/ui/pattern/deref-patterns/usefulness/mixed-constructors.rs new file mode 100644 index 0000000000000..fb1d0dadf4c0a --- /dev/null +++ b/tests/ui/pattern/deref-patterns/usefulness/mixed-constructors.rs @@ -0,0 +1,48 @@ +//! Test matches with a mix of ADT constructors and deref patterns. Currently, usefulness analysis +//! doesn't support this, so make sure we catch it beforehand. As a consequence, it takes priority +//! over non-exhaustive match and unreachable pattern errors. +#![feature(deref_patterns)] +#![expect(incomplete_features)] +#![deny(unreachable_patterns)] + +use std::borrow::Cow; + +fn main() { + let cow: Cow<'static, bool> = Cow::Borrowed(&false); + + match cow { + true => {} + //~v ERROR mix of deref patterns and normal constructors + false => {} + Cow::Borrowed(_) => {} + } + + match cow { + Cow::Owned(_) => {} + Cow::Borrowed(_) => {} + true => {} + //~^ ERROR mix of deref patterns and normal constructors + } + + match cow { + _ => {} + Cow::Owned(_) => {} + false => {} + //~^ ERROR mix of deref patterns and normal constructors + } + + match (cow, 0) { + (Cow::Owned(_), 0) => {} + (Cow::Borrowed(_), 0) => {} + (true, 0) => {} + //~^ ERROR mix of deref patterns and normal constructors + } + + match (0, cow) { + (0, Cow::Owned(_)) => {} + (0, Cow::Borrowed(_)) => {} + _ => {} + (0, true) => {} + //~^ ERROR mix of deref patterns and normal constructors + } +} diff --git a/tests/ui/pattern/deref-patterns/usefulness/mixed-constructors.stderr b/tests/ui/pattern/deref-patterns/usefulness/mixed-constructors.stderr new file mode 100644 index 0000000000000..08658d585495e --- /dev/null +++ b/tests/ui/pattern/deref-patterns/usefulness/mixed-constructors.stderr @@ -0,0 +1,43 @@ +error: mix of deref patterns and normal constructors + --> $DIR/mixed-constructors.rs:16:9 + | +LL | false => {} + | ^^^^^ matches on the result of dereferencing `Cow<'_, bool>` +LL | Cow::Borrowed(_) => {} + | ^^^^^^^^^^^^^^^^ matches directly on `Cow<'_, bool>` + +error: mix of deref patterns and normal constructors + --> $DIR/mixed-constructors.rs:22:9 + | +LL | Cow::Borrowed(_) => {} + | ^^^^^^^^^^^^^^^^ matches directly on `Cow<'_, bool>` +LL | true => {} + | ^^^^ matches on the result of dereferencing `Cow<'_, bool>` + +error: mix of deref patterns and normal constructors + --> $DIR/mixed-constructors.rs:29:9 + | +LL | Cow::Owned(_) => {} + | ^^^^^^^^^^^^^ matches directly on `Cow<'_, bool>` +LL | false => {} + | ^^^^^ matches on the result of dereferencing `Cow<'_, bool>` + +error: mix of deref patterns and normal constructors + --> $DIR/mixed-constructors.rs:36:10 + | +LL | (Cow::Borrowed(_), 0) => {} + | ^^^^^^^^^^^^^^^^ matches directly on `Cow<'_, bool>` +LL | (true, 0) => {} + | ^^^^ matches on the result of dereferencing `Cow<'_, bool>` + +error: mix of deref patterns and normal constructors + --> $DIR/mixed-constructors.rs:43:13 + | +LL | (0, Cow::Borrowed(_)) => {} + | ^^^^^^^^^^^^^^^^ matches directly on `Cow<'_, bool>` +LL | _ => {} +LL | (0, true) => {} + | ^^^^ matches on the result of dereferencing `Cow<'_, bool>` + +error: aborting due to 5 previous errors + diff --git a/tests/ui/pattern/deref-patterns/usefulness/non-exhaustive.rs b/tests/ui/pattern/deref-patterns/usefulness/non-exhaustive.rs new file mode 100644 index 0000000000000..704cae8bdbc4b --- /dev/null +++ b/tests/ui/pattern/deref-patterns/usefulness/non-exhaustive.rs @@ -0,0 +1,28 @@ +//! Test non-exhaustive matches involving deref patterns. +#![feature(deref_patterns)] +#![expect(incomplete_features)] +#![deny(unreachable_patterns)] + +fn main() { + match Box::new(false) { + //~^ ERROR non-exhaustive patterns: `deref!(true)` not covered + false => {} + } + + match Box::new(Box::new(false)) { + //~^ ERROR non-exhaustive patterns: `deref!(deref!(false))` not covered + true => {} + } + + match Box::new((true, Box::new(false))) { + //~^ ERROR non-exhaustive patterns: `deref!((false, deref!(false)))` and `deref!((true, deref!(true)))` not covered + (true, false) => {} + (false, true) => {} + } + + enum T { A, B, C } + match Box::new((Box::new(T::A), Box::new(T::A))) { + //~^ ERROR non-exhaustive patterns: `deref!((deref!(T::C), _))` not covered + (T::A | T::B, T::C) => {} + } +} diff --git a/tests/ui/pattern/deref-patterns/usefulness/non-exhaustive.stderr b/tests/ui/pattern/deref-patterns/usefulness/non-exhaustive.stderr new file mode 100644 index 0000000000000..55fa84bafde26 --- /dev/null +++ b/tests/ui/pattern/deref-patterns/usefulness/non-exhaustive.stderr @@ -0,0 +1,63 @@ +error[E0004]: non-exhaustive patterns: `deref!(true)` not covered + --> $DIR/non-exhaustive.rs:7:11 + | +LL | match Box::new(false) { + | ^^^^^^^^^^^^^^^ pattern `deref!(true)` not covered + | +note: `Box` defined here + --> $SRC_DIR/alloc/src/boxed.rs:LL:COL + = note: the matched value is of type `Box` +help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown + | +LL ~ false => {}, +LL + deref!(true) => todo!() + | + +error[E0004]: non-exhaustive patterns: `deref!(deref!(false))` not covered + --> $DIR/non-exhaustive.rs:12:11 + | +LL | match Box::new(Box::new(false)) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `deref!(deref!(false))` not covered + | +note: `Box>` defined here + --> $SRC_DIR/alloc/src/boxed.rs:LL:COL + = note: the matched value is of type `Box>` +help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown + | +LL ~ true => {}, +LL + deref!(deref!(false)) => todo!() + | + +error[E0004]: non-exhaustive patterns: `deref!((false, deref!(false)))` and `deref!((true, deref!(true)))` not covered + --> $DIR/non-exhaustive.rs:17:11 + | +LL | match Box::new((true, Box::new(false))) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ patterns `deref!((false, deref!(false)))` and `deref!((true, deref!(true)))` not covered + | +note: `Box<(bool, Box)>` defined here + --> $SRC_DIR/alloc/src/boxed.rs:LL:COL + = note: the matched value is of type `Box<(bool, Box)>` +help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms + | +LL ~ (false, true) => {}, +LL + deref!((false, deref!(false))) | deref!((true, deref!(true))) => todo!() + | + +error[E0004]: non-exhaustive patterns: `deref!((deref!(T::C), _))` not covered + --> $DIR/non-exhaustive.rs:24:11 + | +LL | match Box::new((Box::new(T::A), Box::new(T::A))) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `deref!((deref!(T::C), _))` not covered + | +note: `Box<(Box, Box)>` defined here + --> $SRC_DIR/alloc/src/boxed.rs:LL:COL + = note: the matched value is of type `Box<(Box, Box)>` +help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown + | +LL ~ (T::A | T::B, T::C) => {}, +LL + deref!((deref!(T::C), _)) => todo!() + | + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0004`. diff --git a/tests/ui/pattern/deref-patterns/usefulness/unreachable-patterns.rs b/tests/ui/pattern/deref-patterns/usefulness/unreachable-patterns.rs new file mode 100644 index 0000000000000..2677fc54dedc2 --- /dev/null +++ b/tests/ui/pattern/deref-patterns/usefulness/unreachable-patterns.rs @@ -0,0 +1,33 @@ +//! Test unreachable patterns involving deref patterns. +#![feature(deref_patterns)] +#![expect(incomplete_features)] +#![deny(unreachable_patterns)] + +fn main() { + match Box::new(false) { + true => {} + false => {} + false => {} //~ ERROR unreachable pattern + } + + match Box::new(Box::new(false)) { + true => {} + false => {} + true => {} //~ ERROR unreachable pattern + } + + match Box::new((true, Box::new(false))) { + (true, _) => {} + (_, true) => {} + (false, false) => {} + _ => {} //~ ERROR unreachable pattern + } + + enum T { A, B, C } + match Box::new((Box::new(T::A), Box::new(T::A))) { + (T::A | T::B, T::A | T::C) => {} + (T::A, T::C) => {} //~ ERROR unreachable pattern + (T::B, T::A) => {} //~ ERROR unreachable pattern + _ => {} + } +} diff --git a/tests/ui/pattern/deref-patterns/usefulness/unreachable-patterns.stderr b/tests/ui/pattern/deref-patterns/usefulness/unreachable-patterns.stderr new file mode 100644 index 0000000000000..045e11be31966 --- /dev/null +++ b/tests/ui/pattern/deref-patterns/usefulness/unreachable-patterns.stderr @@ -0,0 +1,60 @@ +error: unreachable pattern + --> $DIR/unreachable-patterns.rs:10:9 + | +LL | false => {} + | ----- matches all the relevant values +LL | false => {} + | ^^^^^ no value can reach this + | +note: the lint level is defined here + --> $DIR/unreachable-patterns.rs:4:9 + | +LL | #![deny(unreachable_patterns)] + | ^^^^^^^^^^^^^^^^^^^^ + +error: unreachable pattern + --> $DIR/unreachable-patterns.rs:16:9 + | +LL | true => {} + | ---- matches all the relevant values +LL | false => {} +LL | true => {} + | ^^^^ no value can reach this + +error: unreachable pattern + --> $DIR/unreachable-patterns.rs:23:9 + | +LL | _ => {} + | ^ no value can reach this + | +note: multiple earlier patterns match some of the same values + --> $DIR/unreachable-patterns.rs:23:9 + | +LL | (true, _) => {} + | --------- matches some of the same values +LL | (_, true) => {} + | --------- matches some of the same values +LL | (false, false) => {} + | -------------- matches some of the same values +LL | _ => {} + | ^ collectively making this unreachable + +error: unreachable pattern + --> $DIR/unreachable-patterns.rs:29:9 + | +LL | (T::A | T::B, T::A | T::C) => {} + | -------------------------- matches all the relevant values +LL | (T::A, T::C) => {} + | ^^^^^^^^^^^^ no value can reach this + +error: unreachable pattern + --> $DIR/unreachable-patterns.rs:30:9 + | +LL | (T::A | T::B, T::A | T::C) => {} + | -------------------------- matches all the relevant values +LL | (T::A, T::C) => {} +LL | (T::B, T::A) => {} + | ^^^^^^^^^^^^ no value can reach this + +error: aborting due to 5 previous errors +