diff options
| author | Guillaume Gomez <guillaume1.gomez@gmail.com> | 2025-05-06 14:50:46 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-05-06 14:50:46 +0200 |
| commit | ebd12fcc5e440b6d29aec127035aefd6f4894ea5 (patch) | |
| tree | 6f49a48ee801ab30a92028af2f177b4ee2725f7a | |
| parent | 3501842edbce26248b423a9652bb9b5879cd3d91 (diff) | |
| parent | 7e4f6d3a30f22aa86ffbb3a73969646e42952525 (diff) | |
Rollup merge of #140658 - dianne:lit-deref-pats-p2, r=oli-obk
`deref_patterns`: let string and byte string literal patterns peel references and smart pointers before matching This follows up on #140028. Together, they allow using string and byte string literal patterns to match on smart pointers when `deref_patterns` is enabled. In particular, string literals can now match on `String`, subsuming the functionality of the `string_deref_patterns` feature. More generally, this works by letting literals peel references (and smart pointers) before matching, similar to most other patterns, providing an answer to #44849. Though it's only partially implemented at this point: this doesn't yet let named const patterns peel before matching. The peeling logic is general enough to support named consts, but the typing rules for named const patterns would need adjustments to feel consistent (e.g. arrays would need rules to be usable as slices, and `const STR: &'static str` wouldn't be able to match on a `String` unless a rule was added to let it be used where a `str` is expected, similar to what #140028 did for literals). This also allows string and byte string patterns to match on mutable references, following up on https://github.com/rust-lang/rust/pull/140028#discussion_r2053927512. Rather than forward the mutability of the scrutinee to literal patterns, I've opted to peel `&mut`s from the scrutinee. From a design point of view, this makes the behavior consistent with what would be expected from deref coercions using the methodology in the next paragraph. From a diagnostics point of view, this avoids labeling string and byte string patterns as "mutable references", which I think could be confusing. See [`byte-string-type-errors.rs`](https://github.com/rust-lang/rust/compare/master...dianne:rust:lit-deref-pats-p2?expand=1#diff-4a0dd9b164b67c706751f3c0b5762ddab08bcef05a91972beb0190c6c1cd3706) for how the diagnostics look. At a high level, the peeling logic implemented here tries to mimic how deref coercions work for expressions: we peel references (and smart pointers) from the scrutinee until the pattern can match against it, and no more. This is primarily tested by [`const-pats-do-not-mislead-inference.rs`](https://github.com/rust-lang/rust/compare/master...dianne:rust:lit-deref-pats-p2?expand=1#diff-19afc05b8aae9a30fe4a3a8c0bc2ab2c56b58755a45cdf5c12be0d5e83c4739d). To illustrate the connection, I wasn't sure if this made sense to include in the test file, but I've translated those tests to make sure they give the same inference results as deref coercions: [(playground)](https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=1869744cb9cdfed71a686990aadf9fe1). In each case, a reference to the scrutinee is coerced to have the type of the pattern (under a reference). Tracking issue for deref patterns: #87121 r? `@oli-obk` cc `@Nadrieril`
11 files changed, 422 insertions, 88 deletions
diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 7520782930a..f9502153afd 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -177,16 +177,20 @@ enum PeelKind { /// Only peel reference types. This is used for explicit `deref!(_)` patterns, which dereference /// any number of `&`/`&mut` references, plus a single smart pointer. ExplicitDerefPat, - /// Implicitly peel any number of references, and if `deref_patterns` is enabled, smart pointer - /// ADTs. In order to peel only as much as necessary for the pattern to match, the `until_adt` - /// field contains the ADT def that the pattern is a constructor for, if applicable, so that we - /// don't peel it. See [`ResolvedPat`] for more information. - Implicit { until_adt: Option<DefId> }, + /// Implicitly peel references, and if `deref_patterns` is enabled, smart pointer ADTs. + Implicit { + /// The ADT the pattern is a constructor for, if applicable, so that we don't peel it. See + /// [`ResolvedPat`] for more information. + until_adt: Option<DefId>, + /// The number of references at the head of the pattern's type, so we can leave that many + /// untouched. This is `1` for string literals, and `0` for most patterns. + pat_ref_layers: usize, + }, } impl AdjustMode { const fn peel_until_adt(opt_adt_def: Option<DefId>) -> AdjustMode { - AdjustMode::Peel { kind: PeelKind::Implicit { until_adt: opt_adt_def } } + AdjustMode::Peel { kind: PeelKind::Implicit { until_adt: opt_adt_def, pat_ref_layers: 0 } } } const fn peel_all() -> AdjustMode { AdjustMode::peel_until_adt(None) @@ -488,9 +492,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match pat.kind { // Peel off a `&` or `&mut` from the scrutinee type. See the examples in // `tests/ui/rfcs/rfc-2005-default-binding-mode`. - _ if let AdjustMode::Peel { .. } = adjust_mode + _ if let AdjustMode::Peel { kind: peel_kind } = adjust_mode && pat.default_binding_modes - && let ty::Ref(_, inner_ty, inner_mutability) = *expected.kind() => + && let ty::Ref(_, inner_ty, inner_mutability) = *expected.kind() + && self.should_peel_ref(peel_kind, expected) => { debug!("inspecting {:?}", expected); @@ -531,24 +536,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // If `deref_patterns` is enabled, peel a smart pointer from the scrutinee type. See the // examples in `tests/ui/pattern/deref_patterns/`. _ if self.tcx.features().deref_patterns() - && let AdjustMode::Peel { kind: PeelKind::Implicit { until_adt } } = adjust_mode + && let AdjustMode::Peel { kind: peel_kind } = adjust_mode && pat.default_binding_modes - // For simplicity, only apply overloaded derefs if `expected` is a known ADT. - // FIXME(deref_patterns): we'll get better diagnostics for users trying to - // implicitly deref generics if we allow them here, but primitives, tuples, and - // inference vars definitely should be stopped. Figure out what makes most sense. - && let ty::Adt(scrutinee_adt, _) = *expected.kind() - // Don't peel if the pattern type already matches the scrutinee. E.g., stop here if - // matching on a `Cow<'a, T>` scrutinee with a `Cow::Owned(_)` pattern. - && until_adt != Some(scrutinee_adt.did()) - // At this point, the pattern isn't able to match `expected` without peeling. Check - // that it implements `Deref` before assuming it's a smart pointer, to get a normal - // type error instead of a missing impl error if not. This only checks for `Deref`, - // not `DerefPure`: we require that too, but we want a trait error if it's missing. - && let Some(deref_trait) = self.tcx.lang_items().deref_trait() - && self - .type_implements_trait(deref_trait, [expected], self.param_env) - .may_apply() => + && self.should_peel_smart_pointer(peel_kind, expected) => { debug!("scrutinee ty {expected:?} is a smart pointer, inserting overloaded deref"); // The scrutinee is a smart pointer; implicitly dereference it. This adds a @@ -680,21 +670,32 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // String and byte-string literals result in types `&str` and `&[u8]` respectively. // All other literals result in non-reference types. - // As a result, we allow `if let 0 = &&0 {}` but not `if let "foo" = &&"foo" {}`. - // - // Call `resolve_vars_if_possible` here for inline const blocks. - PatKind::Expr(lt) => match self.resolve_vars_if_possible(self.check_pat_expr_unadjusted(lt)).kind() { - ty::Ref(..) => AdjustMode::Pass, - _ => { - // Path patterns have already been handled, and inline const blocks currently - // aren't possible to write, so any handling for them would be untested. - if cfg!(debug_assertions) - && self.tcx.features().deref_patterns() - && !matches!(lt.kind, PatExprKind::Lit { .. }) - { - span_bug!(lt.span, "FIXME(deref_patterns): adjust mode unimplemented for {:?}", lt.kind); + // As a result, we allow `if let 0 = &&0 {}` but not `if let "foo" = &&"foo" {}` unless + // `deref_patterns` is enabled. + PatKind::Expr(lt) => { + // Path patterns have already been handled, and inline const blocks currently + // aren't possible to write, so any handling for them would be untested. + if cfg!(debug_assertions) + && self.tcx.features().deref_patterns() + && !matches!(lt.kind, PatExprKind::Lit { .. }) + { + span_bug!(lt.span, "FIXME(deref_patterns): adjust mode unimplemented for {:?}", lt.kind); + } + // Call `resolve_vars_if_possible` here for inline const blocks. + let lit_ty = self.resolve_vars_if_possible(self.check_pat_expr_unadjusted(lt)); + // If `deref_patterns` is enabled, allow `if let "foo" = &&"foo" {}`. + if self.tcx.features().deref_patterns() { + let mut peeled_ty = lit_ty; + let mut pat_ref_layers = 0; + while let ty::Ref(_, inner_ty, mutbl) = *peeled_ty.kind() { + // We rely on references at the head of constants being immutable. + debug_assert!(mutbl.is_not()); + pat_ref_layers += 1; + peeled_ty = inner_ty; } - AdjustMode::peel_all() + AdjustMode::Peel { kind: PeelKind::Implicit { until_adt: None, pat_ref_layers } } + } else { + if lit_ty.is_ref() { AdjustMode::Pass } else { AdjustMode::peel_all() } } }, @@ -720,6 +721,67 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + /// Assuming `expected` is a reference type, determine whether to peel it before matching. + fn should_peel_ref(&self, peel_kind: PeelKind, mut expected: Ty<'tcx>) -> bool { + debug_assert!(expected.is_ref()); + let pat_ref_layers = match peel_kind { + PeelKind::ExplicitDerefPat => 0, + PeelKind::Implicit { pat_ref_layers, .. } => pat_ref_layers, + }; + + // Most patterns don't have reference types, so we'll want to peel all references from the + // scrutinee before matching. To optimize for the common case, return early. + if pat_ref_layers == 0 { + return true; + } + debug_assert!( + self.tcx.features().deref_patterns(), + "Peeling for patterns with reference types is gated by `deref_patterns`." + ); + + // If the pattern has as many or more layers of reference as the expected type, we can match + // without peeling more, unless we find a smart pointer or `&mut` that we also need to peel. + // We don't treat `&` and `&mut` as interchangeable, but by peeling `&mut`s before matching, + // we can still, e.g., match on a `&mut str` with a string literal pattern. This is because + // string literal patterns may be used where `str` is expected. + let mut expected_ref_layers = 0; + while let ty::Ref(_, inner_ty, mutbl) = *expected.kind() { + if mutbl.is_mut() { + // Mutable references can't be in the final value of constants, thus they can't be + // at the head of their types, thus we should always peel `&mut`. + return true; + } + expected_ref_layers += 1; + expected = inner_ty; + } + pat_ref_layers < expected_ref_layers || self.should_peel_smart_pointer(peel_kind, expected) + } + + /// Determine whether `expected` is a smart pointer type that should be peeled before matching. + fn should_peel_smart_pointer(&self, peel_kind: PeelKind, expected: Ty<'tcx>) -> bool { + // Explicit `deref!(_)` patterns match against smart pointers; don't peel in that case. + if let PeelKind::Implicit { until_adt, .. } = peel_kind + // For simplicity, only apply overloaded derefs if `expected` is a known ADT. + // FIXME(deref_patterns): we'll get better diagnostics for users trying to + // implicitly deref generics if we allow them here, but primitives, tuples, and + // inference vars definitely should be stopped. Figure out what makes most sense. + && let ty::Adt(scrutinee_adt, _) = *expected.kind() + // Don't peel if the pattern type already matches the scrutinee. E.g., stop here if + // matching on a `Cow<'a, T>` scrutinee with a `Cow::Owned(_)` pattern. + && until_adt != Some(scrutinee_adt.did()) + // At this point, the pattern isn't able to match `expected` without peeling. Check + // that it implements `Deref` before assuming it's a smart pointer, to get a normal + // type error instead of a missing impl error if not. This only checks for `Deref`, + // not `DerefPure`: we require that too, but we want a trait error if it's missing. + && let Some(deref_trait) = self.tcx.lang_items().deref_trait() + && self.type_implements_trait(deref_trait, [expected], self.param_env).may_apply() + { + true + } else { + false + } + } + fn check_pat_expr_unadjusted(&self, lt: &'tcx hir::PatExpr<'tcx>) -> Ty<'tcx> { let ty = match <.kind { rustc_hir::PatExprKind::Lit { lit, negated } => { diff --git a/src/doc/unstable-book/src/language-features/deref-patterns.md b/src/doc/unstable-book/src/language-features/deref-patterns.md index 0cc7106da48..fb6df290cc1 100644 --- a/src/doc/unstable-book/src/language-features/deref-patterns.md +++ b/src/doc/unstable-book/src/language-features/deref-patterns.md @@ -65,15 +65,26 @@ let deref!(x) = Box::new(NoCopy) else { unreachable!() }; drop::<NoCopy>(x); ``` -Additionally, when `deref_patterns` is enabled, string literal patterns may be written where `str` -is expected. Likewise, byte string literal patterns may be written where `[u8]` or `[u8; _]` is -expected. This lets them be used in `deref!(_)` patterns: +Additionally, `deref_patterns` implements changes to string and byte string literal patterns, +allowing then to be used in deref patterns: ```rust # #![feature(deref_patterns)] # #![allow(incomplete_features)] -match ("test".to_string(), b"test".to_vec()) { - (deref!("test"), deref!(b"test")) => {} +match ("test".to_string(), Box::from("test"), b"test".to_vec()) { + ("test", "test", b"test") => {} + _ => panic!(), +} + +// This works through multiple layers of reference and smart pointer: +match (&Box::new(&"test".to_string()), &&&"test") { + ("test", "test") => {} + _ => panic!(), +} + +// `deref!("...")` syntax may also be used: +match "test".to_string() { + deref!("test") => {} _ => panic!(), } @@ -82,10 +93,16 @@ match *"test" { "test" => {} _ => panic!(), } +match *b"test" { + b"test" => {} + _ => panic!(), +} +match *(b"test" as &[u8]) { + b"test" => {} + _ => panic!(), +} ``` -Implicit deref pattern syntax is not yet supported for string or byte string literals. - [`box_patterns`]: ./box-patterns.md [`string_deref_patterns`]: ./string-deref-patterns.md [smart pointers in the standard library]: https://doc.rust-lang.org/std/ops/trait.DerefPure.html#implementors diff --git a/tests/ui/pattern/deref-patterns/byte-string-type-errors.rs b/tests/ui/pattern/deref-patterns/byte-string-type-errors.rs index 64acc4748af..fdcc6cb4611 100644 --- a/tests/ui/pattern/deref-patterns/byte-string-type-errors.rs +++ b/tests/ui/pattern/deref-patterns/byte-string-type-errors.rs @@ -33,4 +33,23 @@ fn main() { if let b"test" = *b"this array is too long" {} //~^ ERROR mismatched types //~| NOTE expected an array with a size of 22, found one with a size of 4 + + // Test matching on `&mut T`: we peel the `&mut` before applying the usual special cases. + // No special cases apply to `()`, so the "found" type is the type of the literal. + if let b"test" = &mut () {} + //~^ ERROR mismatched types + //~| NOTE expected `()`, found `&[u8; 4]` + + // If the pointee is an array or slice, the usual special cases will apply to the "found" type: + if let b"test" = &mut [] as &mut [i8] {} + //~^ ERROR mismatched type + //~| NOTE expected `[i8]`, found `[u8]` + + if let b"test" = &mut [()] {} + //~^ ERROR mismatched types + //~| NOTE expected `[(); 1]`, found `[u8; 4]` + + if let b"test" = &mut *b"this array is too long" {} + //~^ ERROR mismatched type + //~| NOTE expected an array with a size of 22, found one with a size of 4 } diff --git a/tests/ui/pattern/deref-patterns/byte-string-type-errors.stderr b/tests/ui/pattern/deref-patterns/byte-string-type-errors.stderr index 0317b7209e1..046682004be 100644 --- a/tests/ui/pattern/deref-patterns/byte-string-type-errors.stderr +++ b/tests/ui/pattern/deref-patterns/byte-string-type-errors.stderr @@ -47,6 +47,44 @@ LL | if let b"test" = *b"this array is too long" {} | | | expected an array with a size of 22, found one with a size of 4 -error: aborting due to 5 previous errors +error[E0308]: mismatched types + --> $DIR/byte-string-type-errors.rs:39:12 + | +LL | if let b"test" = &mut () {} + | ^^^^^^^ ------- this expression has type `&mut ()` + | | + | expected `()`, found `&[u8; 4]` + +error[E0308]: mismatched types + --> $DIR/byte-string-type-errors.rs:44:12 + | +LL | if let b"test" = &mut [] as &mut [i8] {} + | ^^^^^^^ -------------------- this expression has type `&mut [i8]` + | | + | expected `[i8]`, found `[u8]` + | + = note: expected slice `[i8]` + found slice `[u8]` + +error[E0308]: mismatched types + --> $DIR/byte-string-type-errors.rs:48:12 + | +LL | if let b"test" = &mut [()] {} + | ^^^^^^^ --------- this expression has type `&mut [(); 1]` + | | + | expected `[(); 1]`, found `[u8; 4]` + | + = note: expected array `[(); 1]` + found array `[u8; 4]` + +error[E0308]: mismatched types + --> $DIR/byte-string-type-errors.rs:52:12 + | +LL | if let b"test" = &mut *b"this array is too long" {} + | ^^^^^^^ ------------------------------- this expression has type `&mut [u8; 22]` + | | + | expected an array with a size of 22, found one with a size of 4 + +error: aborting due to 9 previous errors For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/pattern/deref-patterns/const-pats-do-not-mislead-inference.rs b/tests/ui/pattern/deref-patterns/const-pats-do-not-mislead-inference.rs new file mode 100644 index 00000000000..3a2531f4b95 --- /dev/null +++ b/tests/ui/pattern/deref-patterns/const-pats-do-not-mislead-inference.rs @@ -0,0 +1,54 @@ +//@ revisions: stable deref_patterns +//@[deref_patterns] check-pass +//! `deref_patterns` allows string and byte string literal patterns to implicitly peel references +//! and smart pointers from the scrutinee before matching. Since strings and byte strings themselves +//! have reference types, we need to make sure we don't peel too much. By leaving the type of the +//! match scrutinee partially uninferred, these tests make sure we only peel as much as needed in +//! order to match. In particular, when peeling isn't needed, the results should be the same was +//! we'd get without `deref_patterns` enabled. + +#![cfg_attr(deref_patterns, feature(deref_patterns))] +#![cfg_attr(deref_patterns, expect(incomplete_features))] + +fn uninferred<T>() -> T { unimplemented!() } + +// Assert type equality without allowing coercions. +trait Is<T> {} +impl<T> Is<T> for T {} +fn has_type<T>(_: impl Is<T>) {} + +fn main() { + // We don't need to peel anything to unify the type of `x` with `&str`, so `x: &str`. + let x = uninferred(); + if let "..." = x {} + has_type::<&str>(x); + + // We don't need to peel anything to unify the type of `&x` with `&[u8; 3]`, so `x: [u8; 3]`. + let x = uninferred(); + if let b"..." = &x {} + has_type::<[u8; 3]>(x); + + // Peeling a single `&` lets us unify the type of `&x` with `&[u8; 3]`, giving `x: [u8; 3]`. + let x = uninferred(); + if let b"..." = &&x {} + //[stable]~^ ERROR: mismatched types + has_type::<[u8; 3]>(x); + + // We have to peel both the `&` and the box before unifying the type of `x` with `&str`. + let x = uninferred(); + if let "..." = &Box::new(x) {} + //[stable]~^ ERROR mismatched types + has_type::<&str>(x); + + // After peeling the box, we can unify the type of `&x` with `&[u8; 3]`, giving `x: [u8; 3]`. + let x = uninferred(); + if let b"..." = Box::new(&x) {} + //[stable]~^ ERROR mismatched types + has_type::<[u8; 3]>(x); + + // `&` and `&mut` aren't interchangeable: `&mut`s need to be peeled before unifying, like boxes: + let mut x = uninferred(); + if let "..." = &mut x {} + //[stable]~^ ERROR mismatched types + has_type::<&str>(x); +} diff --git a/tests/ui/pattern/deref-patterns/const-pats-do-not-mislead-inference.stable.stderr b/tests/ui/pattern/deref-patterns/const-pats-do-not-mislead-inference.stable.stderr new file mode 100644 index 00000000000..61079718c5d --- /dev/null +++ b/tests/ui/pattern/deref-patterns/const-pats-do-not-mislead-inference.stable.stderr @@ -0,0 +1,55 @@ +error[E0308]: mismatched types + --> $DIR/const-pats-do-not-mislead-inference.rs:33:12 + | +LL | if let b"..." = &&x {} + | ^^^^^^ --- this expression has type `&&_` + | | + | expected `&&_`, found `&[u8; 3]` + | + = note: expected reference `&&_` + found reference `&'static [u8; 3]` + +error[E0308]: mismatched types + --> $DIR/const-pats-do-not-mislead-inference.rs:39:12 + | +LL | if let "..." = &Box::new(x) {} + | ^^^^^ ------------ this expression has type `&Box<_>` + | | + | expected `&Box<_>`, found `&str` + | + = note: expected reference `&Box<_>` + found reference `&'static str` +help: consider dereferencing to access the inner value using the Deref trait + | +LL | if let "..." = &*Box::new(x) {} + | + + +error[E0308]: mismatched types + --> $DIR/const-pats-do-not-mislead-inference.rs:45:12 + | +LL | if let b"..." = Box::new(&x) {} + | ^^^^^^ ------------ this expression has type `Box<&_>` + | | + | expected `Box<&_>`, found `&[u8; 3]` + | + = note: expected struct `Box<&_>` + found reference `&'static [u8; 3]` +help: consider dereferencing to access the inner value using the Deref trait + | +LL | if let b"..." = *Box::new(&x) {} + | + + +error[E0308]: mismatched types + --> $DIR/const-pats-do-not-mislead-inference.rs:51:12 + | +LL | if let "..." = &mut x {} + | ^^^^^ ------ this expression has type `&mut _` + | | + | types differ in mutability + | + = note: expected mutable reference `&mut _` + found reference `&'static str` + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/pattern/deref-patterns/needs-gate.rs b/tests/ui/pattern/deref-patterns/needs-gate.rs index 7944744ee83..953051f7b04 100644 --- a/tests/ui/pattern/deref-patterns/needs-gate.rs +++ b/tests/ui/pattern/deref-patterns/needs-gate.rs @@ -29,4 +29,30 @@ fn main() { //~^ ERROR: mismatched types _ => {} } + + // `deref_patterns` allows string and byte string patterns to implicitly peel references. + match &"str" { + "str" => {} + //~^ ERROR: mismatched types + _ => {} + } + match &b"str" { + b"str" => {} + //~^ ERROR: mismatched types + _ => {} + } + match "str".to_owned() { + "str" => {} + //~^ ERROR: mismatched types + _ => {} + } + + // `deref_patterns` allows string and byte string patterns to match on mutable references. + // See also `tests/ui/pattern/byte-string-mutability-mismatch.rs`. + if let "str" = &mut *"str".to_string() {} + //~^ ERROR mismatched types + if let b"str" = &mut b"str".clone() {} + //~^ ERROR mismatched types + if let b"str" = &mut b"str".clone()[..] {} + //~^ ERROR mismatched types } diff --git a/tests/ui/pattern/deref-patterns/needs-gate.stderr b/tests/ui/pattern/deref-patterns/needs-gate.stderr index e886ca98055..3d938a7e23f 100644 --- a/tests/ui/pattern/deref-patterns/needs-gate.stderr +++ b/tests/ui/pattern/deref-patterns/needs-gate.stderr @@ -47,7 +47,70 @@ LL | match *(b"test" as &[u8]) { LL | b"test" => {} | ^^^^^^^ expected `[u8]`, found `&[u8; 4]` -error: aborting due to 5 previous errors +error[E0308]: mismatched types + --> $DIR/needs-gate.rs:35:9 + | +LL | match &"str" { + | ------ this expression has type `&&str` +LL | "str" => {} + | ^^^^^ expected `&&str`, found `&str` + | + = note: expected reference `&&_` + found reference `&'static _` + +error[E0308]: mismatched types + --> $DIR/needs-gate.rs:40:9 + | +LL | match &b"str" { + | ------- this expression has type `&&[u8; 3]` +LL | b"str" => {} + | ^^^^^^ expected `&&[u8; 3]`, found `&[u8; 3]` + | + = note: expected reference `&&_` + found reference `&'static _` + +error[E0308]: mismatched types + --> $DIR/needs-gate.rs:45:9 + | +LL | match "str".to_owned() { + | ---------------- this expression has type `String` +LL | "str" => {} + | ^^^^^ expected `String`, found `&str` + +error[E0308]: mismatched types + --> $DIR/needs-gate.rs:52:12 + | +LL | if let "str" = &mut *"str".to_string() {} + | ^^^^^ ----------------------- this expression has type `&mut str` + | | + | types differ in mutability + | + = note: expected mutable reference `&mut _` + found reference `&'static _` + +error[E0308]: mismatched types + --> $DIR/needs-gate.rs:54:12 + | +LL | if let b"str" = &mut b"str".clone() {} + | ^^^^^^ ------------------- this expression has type `&mut [u8; 3]` + | | + | types differ in mutability + | + = note: expected mutable reference `&mut _` + found reference `&'static _` + +error[E0308]: mismatched types + --> $DIR/needs-gate.rs:56:12 + | +LL | if let b"str" = &mut b"str".clone()[..] {} + | ^^^^^^ ----------------------- this expression has type `&mut [u8]` + | | + | types differ in mutability + | + = note: expected mutable reference `&mut _` + found reference `&'static _` + +error: aborting due to 11 previous errors Some errors have detailed explanations: E0308, E0658. For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/pattern/deref-patterns/strings.rs b/tests/ui/pattern/deref-patterns/strings.rs index 536e943b3f6..fac15a9aee3 100644 --- a/tests/ui/pattern/deref-patterns/strings.rs +++ b/tests/ui/pattern/deref-patterns/strings.rs @@ -14,10 +14,26 @@ fn main() { }; assert_eq!(test_actual, test_expect); - // Test string literals in explicit `deref!(_)` patterns. + // Test matching on `&mut str`. + let test_actual = match &mut *test_in.to_string() { + "zero" => 0, + "one" => 1, + _ => 2, + }; + assert_eq!(test_actual, test_expect); + + // Test string literals in deref patterns. let test_actual = match test_in.to_string() { deref!("zero") => 0, - deref!("one") => 1, + "one" => 1, + _ => 2, + }; + assert_eq!(test_actual, test_expect); + + // Test peeling references in addition to smart pointers. + let test_actual = match &test_in.to_string() { + deref!("zero") => 0, + "one" => 1, _ => 2, }; assert_eq!(test_actual, test_expect); @@ -47,18 +63,34 @@ fn main() { }; assert_eq!(test_actual, test_expect); - // Test byte string literals used as arrays in explicit `deref!(_)` patterns. + // Test matching on `&mut [u8; N]`. + let test_actual = match &mut test_in.clone() { + b"0" => 0, + b"1" => 1, + _ => 2, + }; + assert_eq!(test_actual, test_expect); + + // Test matching on `&mut [u8]`. + let test_actual = match &mut test_in.clone()[..] { + b"0" => 0, + b"1" => 1, + _ => 2, + }; + assert_eq!(test_actual, test_expect); + + // Test byte string literals used as arrays in deref patterns. let test_actual = match Box::new(*test_in) { deref!(b"0") => 0, - deref!(b"1") => 1, + b"1" => 1, _ => 2, }; assert_eq!(test_actual, test_expect); - // Test byte string literals used as slices in explicit `deref!(_)` patterns. + // Test byte string literals used as slices in deref patterns. let test_actual = match test_in.to_vec() { deref!(b"0") => 0, - deref!(b"1") => 1, + b"1" => 1, _ => 2, }; assert_eq!(test_actual, test_expect); diff --git a/tests/ui/pattern/deref-patterns/typeck_fail.rs b/tests/ui/pattern/deref-patterns/typeck_fail.rs index 52d84f7a34d..6ae87bb7bc3 100644 --- a/tests/ui/pattern/deref-patterns/typeck_fail.rs +++ b/tests/ui/pattern/deref-patterns/typeck_fail.rs @@ -2,19 +2,6 @@ #![allow(incomplete_features)] fn main() { - // FIXME(deref_patterns): fails to typecheck because string literal patterns don't peel - // references from the scrutinee. - match "foo".to_string() { - "foo" => {} - //~^ ERROR: mismatched types - _ => {} - } - match &"foo".to_string() { - "foo" => {} - //~^ ERROR: mismatched types - _ => {} - } - // Make sure we don't try implicitly dereferncing any ADT. match Some(0) { Ok(0) => {} diff --git a/tests/ui/pattern/deref-patterns/typeck_fail.stderr b/tests/ui/pattern/deref-patterns/typeck_fail.stderr index e87528c1c51..fc29caac563 100644 --- a/tests/ui/pattern/deref-patterns/typeck_fail.stderr +++ b/tests/ui/pattern/deref-patterns/typeck_fail.stderr @@ -1,24 +1,5 @@ error[E0308]: mismatched types - --> $DIR/typeck_fail.rs:8:9 - | -LL | match "foo".to_string() { - | ----------------- this expression has type `String` -LL | "foo" => {} - | ^^^^^ expected `String`, found `&str` - -error[E0308]: mismatched types - --> $DIR/typeck_fail.rs:13:9 - | -LL | match &"foo".to_string() { - | ------------------ this expression has type `&String` -LL | "foo" => {} - | ^^^^^ expected `&String`, found `&str` - | - = note: expected reference `&String` - found reference `&'static str` - -error[E0308]: mismatched types - --> $DIR/typeck_fail.rs:20:9 + --> $DIR/typeck_fail.rs:7:9 | LL | match Some(0) { | ------- this expression has type `Option<{integer}>` @@ -28,6 +9,6 @@ LL | Ok(0) => {} = note: expected enum `Option<{integer}>` found enum `Result<_, _>` -error: aborting due to 3 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0308`. |
