From f37a4d55ee6a15a4d240de07a4a33766973866c7 Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Sat, 30 Mar 2024 12:57:54 -0500 Subject: [PATCH 1/3] Implement "& everywhere" The original proposal allows reference patterns with "compatible" mutability, however it's not clear what that means so for now we require an exact match. I don't know the type system code well, so if something seems to not make sense it's probably because I made a mistake --- compiler/rustc_feature/src/unstable.rs | 2 + compiler/rustc_hir_typeck/src/pat.rs | 88 +++++++++++++------ compiler/rustc_hir_typeck/src/writeback.rs | 9 ++ .../rustc_middle/src/ty/typeck_results.rs | 56 ++++++++++++ .../rustc_mir_build/src/thir/pattern/mod.rs | 11 ++- compiler/rustc_span/src/symbol.rs | 1 + .../and_pat_everywhere-mutability-mismatch.rs | 12 +++ ..._pat_everywhere-mutability-mismatch.stderr | 29 ++++++ tests/ui/match/and_pat_everywhere.rs | 15 ++++ .../match/feature-gate-and_pat_everywhere.rs | 14 +++ .../feature-gate-and_pat_everywhere.stderr | 49 +++++++++++ 11 files changed, 257 insertions(+), 29 deletions(-) create mode 100644 tests/ui/match/and_pat_everywhere-mutability-mismatch.rs create mode 100644 tests/ui/match/and_pat_everywhere-mutability-mismatch.stderr create mode 100644 tests/ui/match/and_pat_everywhere.rs create mode 100644 tests/ui/match/feature-gate-and_pat_everywhere.rs create mode 100644 tests/ui/match/feature-gate-and_pat_everywhere.stderr diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 4c975c7b9e052..d1a67f4f96ec9 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -339,6 +339,8 @@ declare_features! ( (incomplete, adt_const_params, "1.56.0", Some(95174)), /// Allows defining an `#[alloc_error_handler]`. (unstable, alloc_error_handler, "1.29.0", Some(51540)), + /// Allows `&` and `&mut` patterns to consume match-ergonomics-inserted references. + (incomplete, and_pat_everywhere, "CURRENT_RUSTC_VERSION", Some(123076)), /// Allows trait methods with arbitrary self types. (unstable, arbitrary_self_types, "1.23.0", Some(44874)), /// Allows using `const` operands in inline assembly. diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 9d247c46bab4b..f428c536da038 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -131,6 +131,12 @@ enum AdjustMode { Peel, /// Reset binding mode to the initial mode. Reset, + /// Produced by ref patterns. + /// Reset the binding mode to the initial mode, + /// and if the old biding mode was by-reference + /// with mutability matching the pattern, + /// mark the pattern as having consumed this reference. + RefReset(Mutability), /// Pass on the input binding mode and expected type. Pass, } @@ -174,7 +180,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => None, }; let adjust_mode = self.calc_adjust_mode(pat, path_res.map(|(res, ..)| res)); - let (expected, def_bm) = self.calc_default_binding_mode(pat, expected, def_bm, adjust_mode); + let (expected, def_bm, ref_pattern_already_consumed) = + self.calc_default_binding_mode(pat, expected, def_bm, adjust_mode); let pat_info = PatInfo { binding_mode: def_bm, top_info: ti, @@ -211,7 +218,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } PatKind::Box(inner) => self.check_pat_box(pat.span, inner, expected, pat_info), PatKind::Deref(inner) => self.check_pat_deref(pat.span, inner, expected, pat_info), - PatKind::Ref(inner, mutbl) => self.check_pat_ref(pat, inner, mutbl, expected, pat_info), + PatKind::Ref(inner, mutbl) => self.check_pat_ref( + pat, + inner, + mutbl, + expected, + pat_info, + ref_pattern_already_consumed, + ), PatKind::Slice(before, slice, after) => { self.check_pat_slice(pat.span, before, slice, after, expected, pat_info) } @@ -264,17 +278,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Compute the new expected type and default binding mode from the old ones /// as well as the pattern form we are currently checking. + /// + /// Last entry is only relevant for ref patterns (`&` and `&mut`); + /// if `true`, then the ref pattern consumed a match ergonomics inserted reference + /// and so does no need to match against a reference in the scrutinee type. fn calc_default_binding_mode( &self, pat: &'tcx Pat<'tcx>, expected: Ty<'tcx>, def_bm: BindingAnnotation, adjust_mode: AdjustMode, - ) -> (Ty<'tcx>, BindingAnnotation) { + ) -> (Ty<'tcx>, BindingAnnotation, bool) { match adjust_mode { - AdjustMode::Pass => (expected, def_bm), - AdjustMode::Reset => (expected, INITIAL_BM), - AdjustMode::Peel => self.peel_off_references(pat, expected, def_bm), + AdjustMode::Pass => (expected, def_bm, false), + AdjustMode::Reset => (expected, INITIAL_BM, false), + AdjustMode::RefReset(mutbl) => (expected, INITIAL_BM, def_bm.0 == ByRef::Yes(mutbl)), + AdjustMode::Peel => { + let peeled = self.peel_off_references(pat, expected, def_bm); + (peeled.0, peeled.1, false) + } } } @@ -329,7 +351,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // ``` // // See issue #46688. - PatKind::Ref(..) => AdjustMode::Reset, + PatKind::Ref(_, mutbl) => AdjustMode::RefReset(*mutbl), // A `_` pattern works with any expected type, so there's no need to do anything. PatKind::Wild // A malformed pattern doesn't have an expected type, so let's just accept any type. @@ -840,8 +862,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { && let Some(mt) = self.shallow_resolve(expected).builtin_deref(true) && let ty::Dynamic(..) = mt.ty.kind() { - // This is "x = SomeTrait" being reduced from - // "let &x = &SomeTrait" or "let box x = Box", an error. + // This is "x = dyn SomeTrait" being reduced from + // "let &x = &dyn SomeTrait" or "let box x = Box", an error. let type_str = self.ty_to_string(expected); let mut err = struct_span_code_err!( self.dcx(), @@ -2036,6 +2058,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { mutbl: Mutability, expected: Ty<'tcx>, pat_info: PatInfo<'tcx, '_>, + already_consumed: bool, ) -> Ty<'tcx> { let tcx = self.tcx; let expected = self.shallow_resolve(expected); @@ -2051,26 +2074,37 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match *expected.kind() { ty::Ref(_, r_ty, r_mutbl) if r_mutbl == mutbl => (expected, r_ty), _ => { - let inner_ty = self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::TypeInference, - span: inner.span, - }); - let ref_ty = self.new_ref_ty(pat.span, mutbl, inner_ty); - debug!("check_pat_ref: demanding {:?} = {:?}", expected, ref_ty); - let err = self.demand_eqtype_pat_diag( - pat.span, - expected, - ref_ty, - pat_info.top_info, - ); + if already_consumed && self.tcx.features().and_pat_everywhere { + // We already matched against a match-ergonmics inserted reference, + // so we don't need to match against a reference from the original type. + // Save this infor for use in lowering later + self.typeck_results + .borrow_mut() + .ref_pats_that_dont_deref_mut() + .insert(pat.hir_id); + (expected, expected) + } else { + let inner_ty = self.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::TypeInference, + span: inner.span, + }); + let ref_ty = self.new_ref_ty(pat.span, mutbl, inner_ty); + debug!("check_pat_ref: demanding {:?} = {:?}", expected, ref_ty); + let err = self.demand_eqtype_pat_diag( + pat.span, + expected, + ref_ty, + pat_info.top_info, + ); - // Look for a case like `fn foo(&foo: u32)` and suggest - // `fn foo(foo: &u32)` - if let Some(mut err) = err { - self.borrow_pat_suggestion(&mut err, pat); - err.emit(); + // Look for a case like `fn foo(&foo: u32)` and suggest + // `fn foo(foo: &u32)` + if let Some(mut err) = err { + self.borrow_pat_suggestion(&mut err, pat); + err.emit(); + } + (ref_ty, inner_ty) } - (ref_ty, inner_ty) } } } diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index f4516b684c386..142a13f88767c 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -345,6 +345,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> { _ => {} }; + self.visit_ref_pats_that_dont_deref(p.hir_id); self.visit_pat_adjustments(p.span, p.hir_id); self.visit_node_id(p.span, p.hir_id); @@ -674,6 +675,14 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { } } + #[instrument(skip(self), level = "debug")] + fn visit_ref_pats_that_dont_deref(&mut self, hir_id: hir::HirId) { + if self.fcx.typeck_results.borrow_mut().ref_pats_that_dont_deref_mut().remove(hir_id) { + debug!("node is a ref pat that doesn't deref"); + self.typeck_results.ref_pats_that_dont_deref_mut().insert(hir_id); + } + } + fn visit_liberated_fn_sigs(&mut self) { let fcx_typeck_results = self.fcx.typeck_results.borrow(); assert_eq!(fcx_typeck_results.hir_owner, self.typeck_results.hir_owner); diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs index d60926bf796d9..6abcab0699a61 100644 --- a/compiler/rustc_middle/src/ty/typeck_results.rs +++ b/compiler/rustc_middle/src/ty/typeck_results.rs @@ -96,6 +96,10 @@ pub struct TypeckResults<'tcx> { /// pat_adjustments: ItemLocalMap>>, + /// Set of reference patterns that match against a match-ergonomics inserted reference + /// (as opposed to against a reference in the scrutinee type). + ref_pats_that_dont_deref: ItemLocalSet, + /// Records the reasons that we picked the kind of each closure; /// not all closures are present in the map. closure_kind_origins: ItemLocalMap<(Span, HirPlace<'tcx>)>, @@ -228,6 +232,7 @@ impl<'tcx> TypeckResults<'tcx> { adjustments: Default::default(), pat_binding_modes: Default::default(), pat_adjustments: Default::default(), + ref_pats_that_dont_deref: Default::default(), closure_kind_origins: Default::default(), liberated_fn_sigs: Default::default(), fru_field_types: Default::default(), @@ -435,6 +440,14 @@ impl<'tcx> TypeckResults<'tcx> { LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.pat_adjustments } } + pub fn ref_pats_that_dont_deref(&self) -> LocalSetInContext<'_> { + LocalSetInContext { hir_owner: self.hir_owner, data: &self.ref_pats_that_dont_deref } + } + + pub fn ref_pats_that_dont_deref_mut(&mut self) -> LocalSetInContextMut<'_> { + LocalSetInContextMut { hir_owner: self.hir_owner, data: &mut self.ref_pats_that_dont_deref } + } + /// Does the pattern recursively contain a `ref mut` binding in it? /// /// This is used to determined whether a `deref` pattern should emit a `Deref` @@ -629,6 +642,49 @@ impl<'a, V> LocalTableInContextMut<'a, V> { } } +#[derive(Clone, Copy, Debug)] +pub struct LocalSetInContext<'a> { + hir_owner: OwnerId, + data: &'a ItemLocalSet, +} + +impl<'a> LocalSetInContext<'a> { + pub fn is_empty(&self) -> bool { + self.data.is_empty() + } + + pub fn contains(&self, id: hir::HirId) -> bool { + validate_hir_id_for_typeck_results(self.hir_owner, id); + self.data.contains(&id.local_id) + } +} + +#[derive(Debug)] +pub struct LocalSetInContextMut<'a> { + hir_owner: OwnerId, + data: &'a mut ItemLocalSet, +} + +impl<'a> LocalSetInContextMut<'a> { + pub fn is_empty(&self) -> bool { + self.data.is_empty() + } + + pub fn contains(&self, id: hir::HirId) -> bool { + validate_hir_id_for_typeck_results(self.hir_owner, id); + self.data.contains(&id.local_id) + } + pub fn insert(&mut self, id: hir::HirId) -> bool { + validate_hir_id_for_typeck_results(self.hir_owner, id); + self.data.insert(id.local_id) + } + + pub fn remove(&mut self, id: hir::HirId) -> bool { + validate_hir_id_for_typeck_results(self.hir_owner, id); + self.data.remove(&id.local_id) + } +} + rustc_index::newtype_index! { #[derive(HashStable)] #[encodable] diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index a4992da679e8a..4b4a54a85c0b0 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -65,9 +65,16 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { // we wrap the unadjusted pattern in `PatKind::Deref` repeatedly, consuming the // adjustments in *reverse order* (last-in-first-out, so that the last `Deref` inserted // gets the least-dereferenced type). - let unadjusted_pat = self.lower_pattern_unadjusted(pat); + let unadjusted = if self.typeck_results.ref_pats_that_dont_deref().contains(pat.hir_id) { + match pat.kind { + hir::PatKind::Ref(inner, _) => self.lower_pattern_unadjusted(inner), + _ => span_bug!(pat.span, "non ref pattern marked as non-deref ref pattern"), + } + } else { + self.lower_pattern_unadjusted(pat) + }; self.typeck_results.pat_adjustments().get(pat.hir_id).unwrap_or(&vec![]).iter().rev().fold( - unadjusted_pat, + unadjusted, |pat: Box<_>, ref_ty| { debug!("{:?}: wrapping pattern with type {:?}", pat, ref_ty); Box::new(Pat { diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 998b1a5c7eaa9..1a20ed1caa3ed 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -380,6 +380,7 @@ symbols! { alu32, always, and, + and_pat_everywhere, and_then, anon, anon_adt, diff --git a/tests/ui/match/and_pat_everywhere-mutability-mismatch.rs b/tests/ui/match/and_pat_everywhere-mutability-mismatch.rs new file mode 100644 index 0000000000000..2135f0e2e5057 --- /dev/null +++ b/tests/ui/match/and_pat_everywhere-mutability-mismatch.rs @@ -0,0 +1,12 @@ +#![allow(incomplete_features)] +#![feature(and_pat_everywhere)] +pub fn main() { + if let Some(&x) = Some(0) { + //~^ ERROR: mismatched types [E0308] + let _: u32 = x; + } + if let &Some(x) = &mut Some(0) { + //~^ ERROR: mismatched types [E0308] + let _: u32 = x; + } +} diff --git a/tests/ui/match/and_pat_everywhere-mutability-mismatch.stderr b/tests/ui/match/and_pat_everywhere-mutability-mismatch.stderr new file mode 100644 index 0000000000000..6d1317a9dfdbc --- /dev/null +++ b/tests/ui/match/and_pat_everywhere-mutability-mismatch.stderr @@ -0,0 +1,29 @@ +error[E0308]: mismatched types + --> $DIR/and_pat_everywhere-mutability-mismatch.rs:4:17 + | +LL | if let Some(&x) = Some(0) { + | ^^ ------- this expression has type `Option<{integer}>` + | | + | expected integer, found `&_` + | + = note: expected type `{integer}` + found reference `&_` +help: consider removing `&` from the pattern + | +LL | if let Some(x) = Some(0) { + | ~ + +error[E0308]: mismatched types + --> $DIR/and_pat_everywhere-mutability-mismatch.rs:8:12 + | +LL | if let &Some(x) = &mut Some(0) { + | ^^^^^^^^ ------------ this expression has type `&mut Option<{integer}>` + | | + | types differ in mutability + | + = note: expected mutable reference `&mut Option<{integer}>` + found reference `&_` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/match/and_pat_everywhere.rs b/tests/ui/match/and_pat_everywhere.rs new file mode 100644 index 0000000000000..00938a212ab3b --- /dev/null +++ b/tests/ui/match/and_pat_everywhere.rs @@ -0,0 +1,15 @@ +//@ run-pass +#![allow(incomplete_features)] +#![feature(and_pat_everywhere)] + +pub fn main() { + if let Some(Some(&x)) = &Some(&Some(0)) { + let _: u32 = x; + } + if let Some(&Some(x)) = &Some(Some(0)) { + let _: u32 = x; + } + if let Some(Some(&mut x)) = &mut Some(&mut Some(0)) { + let _: u32 = x; + } +} diff --git a/tests/ui/match/feature-gate-and_pat_everywhere.rs b/tests/ui/match/feature-gate-and_pat_everywhere.rs new file mode 100644 index 0000000000000..ed5db56e0e83d --- /dev/null +++ b/tests/ui/match/feature-gate-and_pat_everywhere.rs @@ -0,0 +1,14 @@ +pub fn main() { + if let Some(Some(&x)) = &Some(&Some(0)) { + //~^ ERROR: mismatched types [E0308] + let _: u32 = x; + } + if let Some(&Some(x)) = &Some(Some(0)) { + //~^ ERROR: mismatched types [E0308] + let _: u32 = x; + } + if let Some(Some(&mut x)) = &mut Some(&mut Some(0)) { + //~^ ERROR: mismatched types [E0308] + let _: u32 = x; + } +} diff --git a/tests/ui/match/feature-gate-and_pat_everywhere.stderr b/tests/ui/match/feature-gate-and_pat_everywhere.stderr new file mode 100644 index 0000000000000..3c6b7752a0aca --- /dev/null +++ b/tests/ui/match/feature-gate-and_pat_everywhere.stderr @@ -0,0 +1,49 @@ +error[E0308]: mismatched types + --> $DIR/feature-gate-and_pat_everywhere.rs:2:22 + | +LL | if let Some(Some(&x)) = &Some(&Some(0)) { + | ^^ --------------- this expression has type `&Option<&Option<{integer}>>` + | | + | expected integer, found `&_` + | + = note: expected type `{integer}` + found reference `&_` +help: consider removing `&` from the pattern + | +LL | if let Some(Some(x)) = &Some(&Some(0)) { + | ~ + +error[E0308]: mismatched types + --> $DIR/feature-gate-and_pat_everywhere.rs:6:17 + | +LL | if let Some(&Some(x)) = &Some(Some(0)) { + | ^^^^^^^^ -------------- this expression has type `&Option>` + | | + | expected `Option<{integer}>`, found `&_` + | + = note: expected enum `Option<{integer}>` + found reference `&_` + +error[E0308]: mismatched types + --> $DIR/feature-gate-and_pat_everywhere.rs:10:22 + | +LL | if let Some(Some(&mut x)) = &mut Some(&mut Some(0)) { + | ^^^^^^ ----------------------- this expression has type `&mut Option<&mut Option<{integer}>>` + | | + | expected integer, found `&mut _` + | + = note: expected type `{integer}` + found mutable reference `&mut _` +note: to declare a mutable binding use: `mut x` + --> $DIR/feature-gate-and_pat_everywhere.rs:10:22 + | +LL | if let Some(Some(&mut x)) = &mut Some(&mut Some(0)) { + | ^^^^^^ +help: consider removing `&mut` from the pattern + | +LL | if let Some(Some(x)) = &mut Some(&mut Some(0)) { + | ~ + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0308`. From 9d200f2d88600c685e4d2945308b5116c29849e7 Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Tue, 2 Apr 2024 10:54:29 -0500 Subject: [PATCH 2/3] Address review comments --- compiler/rustc_feature/src/unstable.rs | 4 ++-- compiler/rustc_hir_typeck/src/pat.rs | 6 +++--- compiler/rustc_hir_typeck/src/writeback.rs | 10 ++++----- .../rustc_middle/src/ty/typeck_results.rs | 12 +++++------ .../rustc_mir_build/src/thir/pattern/mod.rs | 14 ++++++------- compiler/rustc_span/src/symbol.rs | 2 +- .../feature-gate-ref_pat_everywhere.rs} | 0 .../feature-gate-ref_pat_everywhere.stderr} | 8 +++---- ...ref_pat_everywhere-mutability-mismatch.rs} | 6 +++++- ...pat_everywhere-mutability-mismatch.stderr} | 21 ++++++++++++++++--- ...at_everywhere.rs => ref_pat_everywhere.rs} | 5 ++++- 11 files changed, 55 insertions(+), 33 deletions(-) rename tests/ui/{match/feature-gate-and_pat_everywhere.rs => feature-gates/feature-gate-ref_pat_everywhere.rs} (100%) rename tests/ui/{match/feature-gate-and_pat_everywhere.stderr => feature-gates/feature-gate-ref_pat_everywhere.stderr} (88%) rename tests/ui/match/{and_pat_everywhere-mutability-mismatch.rs => ref_pat_everywhere-mutability-mismatch.rs} (65%) rename tests/ui/match/{and_pat_everywhere-mutability-mismatch.stderr => ref_pat_everywhere-mutability-mismatch.stderr} (55%) rename tests/ui/match/{and_pat_everywhere.rs => ref_pat_everywhere.rs} (72%) diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index d1a67f4f96ec9..36db377f7e0a8 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -339,8 +339,6 @@ declare_features! ( (incomplete, adt_const_params, "1.56.0", Some(95174)), /// Allows defining an `#[alloc_error_handler]`. (unstable, alloc_error_handler, "1.29.0", Some(51540)), - /// Allows `&` and `&mut` patterns to consume match-ergonomics-inserted references. - (incomplete, and_pat_everywhere, "CURRENT_RUSTC_VERSION", Some(123076)), /// Allows trait methods with arbitrary self types. (unstable, arbitrary_self_types, "1.23.0", Some(44874)), /// Allows using `const` operands in inline assembly. @@ -569,6 +567,8 @@ declare_features! ( (unstable, proc_macro_hygiene, "1.30.0", Some(54727)), /// Allows `&raw const $place_expr` and `&raw mut $place_expr` expressions. (unstable, raw_ref_op, "1.41.0", Some(64490)), + /// Allows `&` and `&mut` patterns to consume match-ergonomics-inserted references. + (incomplete, ref_pat_everywhere, "CURRENT_RUSTC_VERSION", Some(123076)), /// Allows using the `#[register_tool]` attribute. (unstable, register_tool, "1.41.0", Some(66079)), /// Allows the `#[repr(i128)]` attribute for enums. diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index f428c536da038..298047427cc0b 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -2058,7 +2058,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { mutbl: Mutability, expected: Ty<'tcx>, pat_info: PatInfo<'tcx, '_>, - already_consumed: bool, + consumed_inherited_ref: bool, ) -> Ty<'tcx> { let tcx = self.tcx; let expected = self.shallow_resolve(expected); @@ -2074,13 +2074,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match *expected.kind() { ty::Ref(_, r_ty, r_mutbl) if r_mutbl == mutbl => (expected, r_ty), _ => { - if already_consumed && self.tcx.features().and_pat_everywhere { + if consumed_inherited_ref && self.tcx.features().ref_pat_everywhere { // We already matched against a match-ergonmics inserted reference, // so we don't need to match against a reference from the original type. // Save this infor for use in lowering later self.typeck_results .borrow_mut() - .ref_pats_that_dont_deref_mut() + .skipped_ref_pats_mut() .insert(pat.hir_id); (expected, expected) } else { diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index 142a13f88767c..6604bf094c14a 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -345,7 +345,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> { _ => {} }; - self.visit_ref_pats_that_dont_deref(p.hir_id); + self.visit_skipped_ref_pats(p.hir_id); self.visit_pat_adjustments(p.span, p.hir_id); self.visit_node_id(p.span, p.hir_id); @@ -676,10 +676,10 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { } #[instrument(skip(self), level = "debug")] - fn visit_ref_pats_that_dont_deref(&mut self, hir_id: hir::HirId) { - if self.fcx.typeck_results.borrow_mut().ref_pats_that_dont_deref_mut().remove(hir_id) { - debug!("node is a ref pat that doesn't deref"); - self.typeck_results.ref_pats_that_dont_deref_mut().insert(hir_id); + fn visit_skipped_ref_pats(&mut self, hir_id: hir::HirId) { + if self.fcx.typeck_results.borrow_mut().skipped_ref_pats_mut().remove(hir_id) { + debug!("node is a skipped ref pat"); + self.typeck_results.skipped_ref_pats_mut().insert(hir_id); } } diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs index 6abcab0699a61..175b53686a176 100644 --- a/compiler/rustc_middle/src/ty/typeck_results.rs +++ b/compiler/rustc_middle/src/ty/typeck_results.rs @@ -98,7 +98,7 @@ pub struct TypeckResults<'tcx> { /// Set of reference patterns that match against a match-ergonomics inserted reference /// (as opposed to against a reference in the scrutinee type). - ref_pats_that_dont_deref: ItemLocalSet, + skipped_ref_pats: ItemLocalSet, /// Records the reasons that we picked the kind of each closure; /// not all closures are present in the map. @@ -232,7 +232,7 @@ impl<'tcx> TypeckResults<'tcx> { adjustments: Default::default(), pat_binding_modes: Default::default(), pat_adjustments: Default::default(), - ref_pats_that_dont_deref: Default::default(), + skipped_ref_pats: Default::default(), closure_kind_origins: Default::default(), liberated_fn_sigs: Default::default(), fru_field_types: Default::default(), @@ -440,12 +440,12 @@ impl<'tcx> TypeckResults<'tcx> { LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.pat_adjustments } } - pub fn ref_pats_that_dont_deref(&self) -> LocalSetInContext<'_> { - LocalSetInContext { hir_owner: self.hir_owner, data: &self.ref_pats_that_dont_deref } + pub fn skipped_ref_pats(&self) -> LocalSetInContext<'_> { + LocalSetInContext { hir_owner: self.hir_owner, data: &self.skipped_ref_pats } } - pub fn ref_pats_that_dont_deref_mut(&mut self) -> LocalSetInContextMut<'_> { - LocalSetInContextMut { hir_owner: self.hir_owner, data: &mut self.ref_pats_that_dont_deref } + pub fn skipped_ref_pats_mut(&mut self) -> LocalSetInContextMut<'_> { + LocalSetInContextMut { hir_owner: self.hir_owner, data: &mut self.skipped_ref_pats } } /// Does the pattern recursively contain a `ref mut` binding in it? diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index 4b4a54a85c0b0..133cf8e334929 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -65,16 +65,16 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { // we wrap the unadjusted pattern in `PatKind::Deref` repeatedly, consuming the // adjustments in *reverse order* (last-in-first-out, so that the last `Deref` inserted // gets the least-dereferenced type). - let unadjusted = if self.typeck_results.ref_pats_that_dont_deref().contains(pat.hir_id) { - match pat.kind { - hir::PatKind::Ref(inner, _) => self.lower_pattern_unadjusted(inner), - _ => span_bug!(pat.span, "non ref pattern marked as non-deref ref pattern"), + let unadjusted_pat = match pat.kind { + hir::PatKind::Ref(inner, _) + if self.typeck_results.skipped_ref_pats().contains(pat.hir_id) => + { + self.lower_pattern_unadjusted(inner) } - } else { - self.lower_pattern_unadjusted(pat) + _ => self.lower_pattern_unadjusted(pat), }; self.typeck_results.pat_adjustments().get(pat.hir_id).unwrap_or(&vec![]).iter().rev().fold( - unadjusted, + unadjusted_pat, |pat: Box<_>, ref_ty| { debug!("{:?}: wrapping pattern with type {:?}", pat, ref_ty); Box::new(Pat { diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 1a20ed1caa3ed..1b407539d3c1e 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -380,7 +380,6 @@ symbols! { alu32, always, and, - and_pat_everywhere, and_then, anon, anon_adt, @@ -1457,6 +1456,7 @@ symbols! { receiver, recursion_limit, reexport_test_harness_main, + ref_pat_everywhere, ref_unwind_safe_trait, reference, reflect, diff --git a/tests/ui/match/feature-gate-and_pat_everywhere.rs b/tests/ui/feature-gates/feature-gate-ref_pat_everywhere.rs similarity index 100% rename from tests/ui/match/feature-gate-and_pat_everywhere.rs rename to tests/ui/feature-gates/feature-gate-ref_pat_everywhere.rs diff --git a/tests/ui/match/feature-gate-and_pat_everywhere.stderr b/tests/ui/feature-gates/feature-gate-ref_pat_everywhere.stderr similarity index 88% rename from tests/ui/match/feature-gate-and_pat_everywhere.stderr rename to tests/ui/feature-gates/feature-gate-ref_pat_everywhere.stderr index 3c6b7752a0aca..0f0051325cdf0 100644 --- a/tests/ui/match/feature-gate-and_pat_everywhere.stderr +++ b/tests/ui/feature-gates/feature-gate-ref_pat_everywhere.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/feature-gate-and_pat_everywhere.rs:2:22 + --> $DIR/feature-gate-ref_pat_everywhere.rs:2:22 | LL | if let Some(Some(&x)) = &Some(&Some(0)) { | ^^ --------------- this expression has type `&Option<&Option<{integer}>>` @@ -14,7 +14,7 @@ LL | if let Some(Some(x)) = &Some(&Some(0)) { | ~ error[E0308]: mismatched types - --> $DIR/feature-gate-and_pat_everywhere.rs:6:17 + --> $DIR/feature-gate-ref_pat_everywhere.rs:6:17 | LL | if let Some(&Some(x)) = &Some(Some(0)) { | ^^^^^^^^ -------------- this expression has type `&Option>` @@ -25,7 +25,7 @@ LL | if let Some(&Some(x)) = &Some(Some(0)) { found reference `&_` error[E0308]: mismatched types - --> $DIR/feature-gate-and_pat_everywhere.rs:10:22 + --> $DIR/feature-gate-ref_pat_everywhere.rs:10:22 | LL | if let Some(Some(&mut x)) = &mut Some(&mut Some(0)) { | ^^^^^^ ----------------------- this expression has type `&mut Option<&mut Option<{integer}>>` @@ -35,7 +35,7 @@ LL | if let Some(Some(&mut x)) = &mut Some(&mut Some(0)) { = note: expected type `{integer}` found mutable reference `&mut _` note: to declare a mutable binding use: `mut x` - --> $DIR/feature-gate-and_pat_everywhere.rs:10:22 + --> $DIR/feature-gate-ref_pat_everywhere.rs:10:22 | LL | if let Some(Some(&mut x)) = &mut Some(&mut Some(0)) { | ^^^^^^ diff --git a/tests/ui/match/and_pat_everywhere-mutability-mismatch.rs b/tests/ui/match/ref_pat_everywhere-mutability-mismatch.rs similarity index 65% rename from tests/ui/match/and_pat_everywhere-mutability-mismatch.rs rename to tests/ui/match/ref_pat_everywhere-mutability-mismatch.rs index 2135f0e2e5057..9dd7a7893ec71 100644 --- a/tests/ui/match/and_pat_everywhere-mutability-mismatch.rs +++ b/tests/ui/match/ref_pat_everywhere-mutability-mismatch.rs @@ -1,5 +1,5 @@ #![allow(incomplete_features)] -#![feature(and_pat_everywhere)] +#![feature(ref_pat_everywhere)] pub fn main() { if let Some(&x) = Some(0) { //~^ ERROR: mismatched types [E0308] @@ -9,4 +9,8 @@ pub fn main() { //~^ ERROR: mismatched types [E0308] let _: u32 = x; } + if let Some(&x) = &mut Some(0) { + //~^ ERROR: mismatched types [E0308] + let _: u32 = x; + } } diff --git a/tests/ui/match/and_pat_everywhere-mutability-mismatch.stderr b/tests/ui/match/ref_pat_everywhere-mutability-mismatch.stderr similarity index 55% rename from tests/ui/match/and_pat_everywhere-mutability-mismatch.stderr rename to tests/ui/match/ref_pat_everywhere-mutability-mismatch.stderr index 6d1317a9dfdbc..d512ea5f957da 100644 --- a/tests/ui/match/and_pat_everywhere-mutability-mismatch.stderr +++ b/tests/ui/match/ref_pat_everywhere-mutability-mismatch.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/and_pat_everywhere-mutability-mismatch.rs:4:17 + --> $DIR/ref_pat_everywhere-mutability-mismatch.rs:4:17 | LL | if let Some(&x) = Some(0) { | ^^ ------- this expression has type `Option<{integer}>` @@ -14,7 +14,7 @@ LL | if let Some(x) = Some(0) { | ~ error[E0308]: mismatched types - --> $DIR/and_pat_everywhere-mutability-mismatch.rs:8:12 + --> $DIR/ref_pat_everywhere-mutability-mismatch.rs:8:12 | LL | if let &Some(x) = &mut Some(0) { | ^^^^^^^^ ------------ this expression has type `&mut Option<{integer}>` @@ -24,6 +24,21 @@ LL | if let &Some(x) = &mut Some(0) { = note: expected mutable reference `&mut Option<{integer}>` found reference `&_` -error: aborting due to 2 previous errors +error[E0308]: mismatched types + --> $DIR/ref_pat_everywhere-mutability-mismatch.rs:12:17 + | +LL | if let Some(&x) = &mut Some(0) { + | ^^ ------------ this expression has type `&mut Option<{integer}>` + | | + | expected integer, found `&_` + | + = note: expected type `{integer}` + found reference `&_` +help: consider removing `&` from the pattern + | +LL | if let Some(x) = &mut Some(0) { + | ~ + +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/match/and_pat_everywhere.rs b/tests/ui/match/ref_pat_everywhere.rs similarity index 72% rename from tests/ui/match/and_pat_everywhere.rs rename to tests/ui/match/ref_pat_everywhere.rs index 00938a212ab3b..b3daca484092b 100644 --- a/tests/ui/match/and_pat_everywhere.rs +++ b/tests/ui/match/ref_pat_everywhere.rs @@ -1,6 +1,6 @@ //@ run-pass #![allow(incomplete_features)] -#![feature(and_pat_everywhere)] +#![feature(ref_pat_everywhere)] pub fn main() { if let Some(Some(&x)) = &Some(&Some(0)) { @@ -12,4 +12,7 @@ pub fn main() { if let Some(Some(&mut x)) = &mut Some(&mut Some(0)) { let _: u32 = x; } + if let Some(Some(&x)) = &Some(&mut Some(0)) { + let _: u32 = x; + } } From e5376f3947ba8faf0f7c3a9543366060d662357d Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Wed, 3 Apr 2024 20:35:02 -0400 Subject: [PATCH 3/3] Address final nits --- compiler/rustc_hir_typeck/src/pat.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 298047427cc0b..bb47f8dfba46c 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -130,13 +130,14 @@ enum AdjustMode { /// Peel off all immediate reference types. Peel, /// Reset binding mode to the initial mode. + /// Used for destructuring assignment, where we don't want any match ergonomics. Reset, /// Produced by ref patterns. /// Reset the binding mode to the initial mode, /// and if the old biding mode was by-reference /// with mutability matching the pattern, /// mark the pattern as having consumed this reference. - RefReset(Mutability), + ResetAndConsumeRef(Mutability), /// Pass on the input binding mode and expected type. Pass, } @@ -292,7 +293,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match adjust_mode { AdjustMode::Pass => (expected, def_bm, false), AdjustMode::Reset => (expected, INITIAL_BM, false), - AdjustMode::RefReset(mutbl) => (expected, INITIAL_BM, def_bm.0 == ByRef::Yes(mutbl)), + AdjustMode::ResetAndConsumeRef(mutbl) => { + (expected, INITIAL_BM, def_bm.0 == ByRef::Yes(mutbl)) + } AdjustMode::Peel => { let peeled = self.peel_off_references(pat, expected, def_bm); (peeled.0, peeled.1, false) @@ -351,7 +354,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // ``` // // See issue #46688. - PatKind::Ref(_, mutbl) => AdjustMode::RefReset(*mutbl), + PatKind::Ref(_, mutbl) => AdjustMode::ResetAndConsumeRef(*mutbl), // A `_` pattern works with any expected type, so there's no need to do anything. PatKind::Wild // A malformed pattern doesn't have an expected type, so let's just accept any type.