From 4f76f1069af505ce72535bda8d10e44dd81e65a5 Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Tue, 30 Apr 2024 16:14:20 -0400 Subject: [PATCH 1/9] Match ergonomics 2024: let `&` patterns eat `&mut` --- compiler/rustc_hir_typeck/src/pat.rs | 186 +++++++++++++----- .../ref_pat_eat_one_layer_2024.rs | 6 + .../ref_pat_eat_one_layer_2024_fail.rs | 16 +- .../ref_pat_eat_one_layer_2024_fail.stderr | 68 +++++-- ...mismatch.rs => ref_pat_everywhere-fail.rs} | 6 +- tests/ui/match/ref_pat_everywhere-fail.stderr | 38 ++++ ..._pat_everywhere-mutability-mismatch.stderr | 44 ----- tests/ui/match/ref_pat_everywhere.rs | 6 + 8 files changed, 248 insertions(+), 122 deletions(-) rename tests/ui/match/{ref_pat_everywhere-mutability-mismatch.rs => ref_pat_everywhere-fail.rs} (63%) create mode 100644 tests/ui/match/ref_pat_everywhere-fail.stderr delete mode 100644 tests/ui/match/ref_pat_everywhere-mutability-mismatch.stderr diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index fe2deee378e..99653814d6f 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -78,7 +78,7 @@ struct TopInfo<'tcx> { #[derive(Copy, Clone)] struct PatInfo<'tcx, 'a> { binding_mode: ByRef, - max_ref_mutbl: Mutability, + max_ref_mutbl: MutblCap, top_info: TopInfo<'tcx>, decl_origin: Option>, @@ -124,6 +124,7 @@ fn demand_eqtype_pat( } /// Mode for adjusting the expected type and binding mode. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] enum AdjustMode { /// Peel off all immediate reference types. Peel, @@ -135,11 +136,44 @@ enum AdjustMode { /// and if the old biding mode was by-reference /// with mutability matching the pattern, /// mark the pattern as having consumed this reference. - ResetAndConsumeRef(Mutability), + /// + /// `Span` is that of the inside of the reference pattern + ResetAndConsumeRef(Mutability, Span), /// Pass on the input binding mode and expected type. Pass, } +/// `ref mut` patterns (explicit or match-ergonomics) +/// are not allowed behind an `&` reference. +/// +/// This includes explicit `ref mut` behind `&` patterns +/// that match against `&mut` references, +/// where the code would have compiled +/// had the pattern been written as `&mut`. +/// However, the borrow checker will not catch +/// this last case, so we need to throw an error ourselves. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +enum MutblCap { + /// Mutability restricted to immutable; + /// contained span, if present, should be shown in diagnostics as the reason. + Not(Option), + /// No restriction on mutability + Mut, +} + +impl MutblCap { + fn cap_mutbl_to_not(self, span: Option) -> Self { + if self == MutblCap::Mut { MutblCap::Not(span) } else { self } + } + + fn as_mutbl(self) -> Mutability { + match self { + MutblCap::Not(_) => Mutability::Not, + MutblCap::Mut => Mutability::Mut, + } + } +} + impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Type check the given top level pattern against the `expected` type. /// @@ -160,7 +194,7 @@ pub(crate) fn check_pat_top( let info = TopInfo { expected, origin_expr, span }; let pat_info = PatInfo { binding_mode: ByRef::No, - max_ref_mutbl: Mutability::Mut, + max_ref_mutbl: MutblCap::Mut, top_info: info, decl_origin, current_depth: 0, @@ -201,8 +235,8 @@ fn check_pat(&self, pat: &'tcx Pat<'tcx>, expected: Ty<'tcx>, pat_info: PatInfo< PatKind::Never => expected, PatKind::Lit(lt) => self.check_pat_lit(pat.span, lt, expected, ti), PatKind::Range(lhs, rhs, _) => self.check_pat_range(pat.span, lhs, rhs, expected, ti), - PatKind::Binding(ba, var_id, _, sub) => { - self.check_pat_ident(pat, ba, var_id, sub, expected, pat_info) + PatKind::Binding(ba, var_id, ident, sub) => { + self.check_pat_ident(pat, ba, var_id, ident, sub, expected, pat_info) } PatKind::TupleStruct(ref qpath, subpats, ddpos) => { self.check_pat_tuple_struct(pat, qpath, subpats, ddpos, expected, pat_info) @@ -294,20 +328,29 @@ fn calc_default_binding_mode( expected: Ty<'tcx>, def_br: ByRef, adjust_mode: AdjustMode, - max_ref_mutbl: Mutability, - ) -> (Ty<'tcx>, ByRef, Mutability, bool) { - if let ByRef::Yes(mutbl) = def_br { - debug_assert!(mutbl <= max_ref_mutbl); + max_ref_mutbl: MutblCap, + ) -> (Ty<'tcx>, ByRef, MutblCap, bool) { + if let ByRef::Yes(Mutability::Mut) = def_br { + debug_assert!(max_ref_mutbl == MutblCap::Mut); } match adjust_mode { AdjustMode::Pass => (expected, def_br, max_ref_mutbl, false), - AdjustMode::Reset => (expected, ByRef::No, Mutability::Mut, false), - AdjustMode::ResetAndConsumeRef(ref_pat_mutbl) => { - let mutbls_match = def_br == ByRef::Yes(ref_pat_mutbl); + AdjustMode::Reset => (expected, ByRef::No, MutblCap::Mut, false), + AdjustMode::ResetAndConsumeRef(ref_pat_mutbl, inner_span) => { + // `&` pattern eats `&mut` + let mutbls_match = + if let ByRef::Yes(def_mut) = def_br { ref_pat_mutbl <= def_mut } else { false }; + if pat.span.at_least_rust_2024() && self.tcx.features().ref_pat_eat_one_layer_2024 { + let max_ref_mutbl = if ref_pat_mutbl == Mutability::Not { + max_ref_mutbl.cap_mutbl_to_not(Some(pat.span.until(inner_span))) + } else { + max_ref_mutbl + }; + if mutbls_match { debug!("consuming inherited reference"); - (expected, ByRef::No, cmp::min(max_ref_mutbl, ref_pat_mutbl), true) + (expected, ByRef::No, max_ref_mutbl, true) } else { let (new_ty, new_bm, max_ref_mutbl) = if ref_pat_mutbl == Mutability::Mut { self.peel_off_references( @@ -318,7 +361,7 @@ fn calc_default_binding_mode( max_ref_mutbl, ) } else { - (expected, def_br.cap_ref_mutability(Mutability::Not), Mutability::Not) + (expected, def_br.cap_ref_mutability(Mutability::Not), max_ref_mutbl) }; (new_ty, new_bm, max_ref_mutbl, false) } @@ -385,7 +428,7 @@ fn calc_adjust_mode(&self, pat: &'tcx Pat<'tcx>, opt_path_res: Option) -> A // ``` // // See issue #46688. - PatKind::Ref(_, mutbl) => AdjustMode::ResetAndConsumeRef(*mutbl), + PatKind::Ref(inner, mutbl) => AdjustMode::ResetAndConsumeRef(*mutbl, inner.span), // 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. @@ -411,8 +454,8 @@ fn peel_off_references( expected: Ty<'tcx>, mut def_br: ByRef, max_peelable_mutability: Mutability, - mut max_ref_mutability: Mutability, - ) -> (Ty<'tcx>, ByRef, Mutability) { + mut max_ref_mutability: MutblCap, + ) -> (Ty<'tcx>, ByRef, MutblCap) { let mut expected = self.try_structurally_resolve_type(pat.span, expected); // Peel off as many `&` or `&mut` from the scrutinee type as possible. For example, // for `match &&&mut Some(5)` the loop runs three times, aborting when it reaches @@ -446,9 +489,9 @@ fn peel_off_references( } if pat.span.at_least_rust_2024() && self.tcx.features().ref_pat_eat_one_layer_2024 { - def_br = def_br.cap_ref_mutability(max_ref_mutability); + def_br = def_br.cap_ref_mutability(max_ref_mutability.as_mutbl()); if def_br == ByRef::Yes(Mutability::Not) { - max_ref_mutability = Mutability::Not; + max_ref_mutability = max_ref_mutability.cap_mutbl_to_not(None); } } @@ -665,8 +708,9 @@ fn emit_err_pat_range( fn check_pat_ident( &self, pat: &'tcx Pat<'tcx>, - ba: BindingMode, + explicit_ba: BindingMode, var_id: HirId, + ident: Ident, sub: Option<&'tcx Pat<'tcx>>, expected: Ty<'tcx>, pat_info: PatInfo<'tcx, '_>, @@ -674,7 +718,7 @@ fn check_pat_ident( let PatInfo { binding_mode: def_br, top_info: ti, .. } = pat_info; // Determine the binding mode... - let bm = match ba { + let bm = match explicit_ba { BindingMode(ByRef::No, Mutability::Mut) if !(pat.span.at_least_rust_2024() && self.tcx.features().mut_preserve_binding_mode_2024) @@ -690,8 +734,22 @@ fn check_pat_ident( BindingMode(ByRef::No, Mutability::Mut) } BindingMode(ByRef::No, mutbl) => BindingMode(def_br, mutbl), - BindingMode(ByRef::Yes(_), _) => ba, + BindingMode(ByRef::Yes(_), _) => explicit_ba, }; + + if bm.0 == ByRef::Yes(Mutability::Mut) + && let MutblCap::Not(Some(and_pat_span)) = pat_info.max_ref_mutbl + { + let mut err = struct_span_code_err!( + self.tcx.dcx(), + ident.span, + E0596, + "cannot bind with `ref mut` behind an `&` pattern" + ); + err.span_help(and_pat_span, "change this `&` pattern to an `&mut`"); + err.emit(); + } + // ...and store it in a side table: self.typeck_results.borrow_mut().pat_binding_modes_mut().insert(pat.hir_id, bm); @@ -717,7 +775,7 @@ fn check_pat_ident( // If there are multiple arms, make sure they all agree on // what the type of the binding `x` ought to be. if var_id != pat.hir_id { - self.check_binding_alt_eq_ty(ba, pat.span, var_id, local_ty, ti); + self.check_binding_alt_eq_ty(explicit_ba, pat.span, var_id, local_ty, ti); } if let Some(p) = sub { @@ -2117,7 +2175,9 @@ fn check_pat_ref( } else { let tcx = self.tcx; let expected = self.shallow_resolve(expected); - let (ref_ty, inner_ty) = match self.check_dereferenceable(pat.span, expected, inner) { + let (ref_ty, inner_ty, pat_info) = match self + .check_dereferenceable(pat.span, expected, inner) + { Ok(()) => { // `demand::subtype` would be good enough, but using `eqtype` turns // out to be equally general. See (note_1) for details. @@ -2127,42 +2187,62 @@ fn check_pat_ref( // the bad interactions of the given hack detailed in (note_1). debug!("check_pat_ref: expected={:?}", expected); match *expected.kind() { - ty::Ref(_, r_ty, r_mutbl) if r_mutbl == mutbl => (expected, r_ty), - _ => { - 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() - .skipped_ref_pats_mut() - .insert(pat.hir_id); - (expected, expected) - } else { - let inner_ty = self.next_ty_var(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, - ); + ty::Ref(_, r_ty, r_mutbl) if r_mutbl == mutbl => (expected, r_ty, pat_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(); - } - (ref_ty, inner_ty) + // `&` pattern eats `&mut` reference + ty::Ref(_, r_ty, Mutability::Mut) + if mutbl == Mutability::Not + && ((pat.span.at_least_rust_2024() + && self.tcx.features().ref_pat_eat_one_layer_2024) + || self.tcx.features().ref_pat_everywhere) => + { + ( + expected, + r_ty, + PatInfo { + max_ref_mutbl: pat_info + .max_ref_mutbl + .cap_mutbl_to_not(Some(pat.span.until(inner.span))), + ..pat_info + }, + ) + } + + _ 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() + .skipped_ref_pats_mut() + .insert(pat.hir_id); + (expected, expected, pat_info) + } + + _ => { + let inner_ty = self.next_ty_var(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(); } + (ref_ty, inner_ty, pat_info) } } } Err(guar) => { let err = Ty::new_error(tcx, guar); - (err, err) + (err, err, pat_info) } }; self.check_pat(inner, inner_ty, pat_info); diff --git a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024.rs b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024.rs index f1ac3e340e9..62e4f82a3ff 100644 --- a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024.rs +++ b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024.rs @@ -53,6 +53,12 @@ pub fn main() { if let Some(&Some(Some(&x))) = &Some(Some(&mut Some(0))) { let _: u32 = x; } + if let Some(&Some(&x)) = Some(&Some(&mut 0)) { + let _: u32 = x; + } + if let Some(&Some(x)) = &mut Some(Some(0)) { + let _: u32 = x; + } let &mut x = &&mut 0; let _: &u32 = x; diff --git a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.rs b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.rs index ec091bb1746..61e61719458 100644 --- a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.rs +++ b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.rs @@ -14,16 +14,24 @@ pub fn main() { let _: &mut u32 = x; //~^ ERROR: mismatched types } - if let Some(&Some(&_)) = Some(&Some(&mut 0)) { - //~^ ERROR: mismatched types - } if let Some(&Some(&mut _)) = &mut Some(&Some(0)) { //~^ ERROR: mismatched types } if let Some(&Some(Some((&mut _)))) = &Some(Some(&mut Some(0))) { //~^ ERROR: mismatched types } - + if let Some(&mut Some(x)) = &Some(Some(0)) { + //~^ ERROR: mismatched types + } + if let Some(&Some(ref mut x)) = &mut Some(Some(0)) { + //~^ ERROR: cannot bind with `ref mut` behind an `&` pattern + } + if let &Some(Some(ref mut x)) = &mut Some(Some(0)) { + //~^ ERROR: cannot bind with `ref mut` behind an `&` pattern + } + if let Some(&mut Some(x)) = &Some(Some(0)) { + //~^ ERROR: mismatched types + } let &mut _= &&0; //~^ ERROR: mismatched types diff --git a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.stderr b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.stderr index be71ee606c7..2aa2e2851ce 100644 --- a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.stderr +++ b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.stderr @@ -34,17 +34,6 @@ LL | let _: &mut u32 = x; error[E0308]: mismatched types --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:17:23 | -LL | if let Some(&Some(&_)) = Some(&Some(&mut 0)) { - | ^^ ------------------- this expression has type `Option<&Option<&mut {integer}>>` - | | - | types differ in mutability - | - = note: expected mutable reference `&mut {integer}` - found reference `&_` - -error[E0308]: mismatched types - --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:20:23 - | LL | if let Some(&Some(&mut _)) = &mut Some(&Some(0)) { | ^^^^^^ ------------------- this expression has type `&mut Option<&Option<{integer}>>` | | @@ -54,7 +43,7 @@ LL | if let Some(&Some(&mut _)) = &mut Some(&Some(0)) { found mutable reference `&mut _` error[E0308]: mismatched types - --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:23:29 + --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:20:29 | LL | if let Some(&Some(Some((&mut _)))) = &Some(Some(&mut Some(0))) { | ^^^^^^ ------------------------- this expression has type `&Option>>` @@ -65,7 +54,53 @@ LL | if let Some(&Some(Some((&mut _)))) = &Some(Some(&mut Some(0))) { found mutable reference `&mut _` error[E0308]: mismatched types - --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:28:9 + --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:23:17 + | +LL | if let Some(&mut Some(x)) = &Some(Some(0)) { + | ^^^^^^^^^^^^ -------------- this expression has type `&Option>` + | | + | expected `Option<{integer}>`, found `&mut _` + | + = note: expected enum `Option<{integer}>` + found mutable reference `&mut _` + +error[E0596]: cannot bind with `ref mut` behind an `&` pattern + --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:26:31 + | +LL | if let Some(&Some(ref mut x)) = &mut Some(Some(0)) { + | ^ + | +help: change this `&` pattern to an `&mut` + --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:26:17 + | +LL | if let Some(&Some(ref mut x)) = &mut Some(Some(0)) { + | ^ + +error[E0596]: cannot bind with `ref mut` behind an `&` pattern + --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:29:31 + | +LL | if let &Some(Some(ref mut x)) = &mut Some(Some(0)) { + | ^ + | +help: change this `&` pattern to an `&mut` + --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:29:12 + | +LL | if let &Some(Some(ref mut x)) = &mut Some(Some(0)) { + | ^ + +error[E0308]: mismatched types + --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:32:17 + | +LL | if let Some(&mut Some(x)) = &Some(Some(0)) { + | ^^^^^^^^^^^^ -------------- this expression has type `&Option>` + | | + | expected `Option<{integer}>`, found `&mut _` + | + = note: expected enum `Option<{integer}>` + found mutable reference `&mut _` + +error[E0308]: mismatched types + --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:36:9 | LL | let &mut _= &&0; | ^^^^^^ --- this expression has type `&&{integer}` @@ -76,7 +111,7 @@ LL | let &mut _= &&0; found mutable reference `&mut _` error[E0308]: mismatched types - --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:31:9 + --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:39:9 | LL | let &mut _ = &&&&&&&&&&&&&&&&&&&&&&&&&&&&0; | ^^^^^^ ----------------------------- this expression has type `&&&&&&&&&&&&&&&&&&&&&&&&&&&&{integer}` @@ -86,6 +121,7 @@ LL | let &mut _ = &&&&&&&&&&&&&&&&&&&&&&&&&&&&0; = note: expected type `{integer}` found mutable reference `&mut _` -error: aborting due to 8 previous errors +error: aborting due to 11 previous errors -For more information about this error, try `rustc --explain E0308`. +Some errors have detailed explanations: E0308, E0596. +For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/match/ref_pat_everywhere-mutability-mismatch.rs b/tests/ui/match/ref_pat_everywhere-fail.rs similarity index 63% rename from tests/ui/match/ref_pat_everywhere-mutability-mismatch.rs rename to tests/ui/match/ref_pat_everywhere-fail.rs index 9dd7a7893ec..d1b1c04730d 100644 --- a/tests/ui/match/ref_pat_everywhere-mutability-mismatch.rs +++ b/tests/ui/match/ref_pat_everywhere-fail.rs @@ -5,11 +5,7 @@ pub fn main() { //~^ ERROR: mismatched types [E0308] let _: u32 = x; } - if let &Some(x) = &mut Some(0) { - //~^ ERROR: mismatched types [E0308] - let _: u32 = x; - } - if let Some(&x) = &mut Some(0) { + if let Some(&mut x) = Some(&0) { //~^ ERROR: mismatched types [E0308] let _: u32 = x; } diff --git a/tests/ui/match/ref_pat_everywhere-fail.stderr b/tests/ui/match/ref_pat_everywhere-fail.stderr new file mode 100644 index 00000000000..25a01129f4a --- /dev/null +++ b/tests/ui/match/ref_pat_everywhere-fail.stderr @@ -0,0 +1,38 @@ +error[E0308]: mismatched types + --> $DIR/ref_pat_everywhere-fail.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/ref_pat_everywhere-fail.rs:8:17 + | +LL | if let Some(&mut x) = Some(&0) { + | ^^^^^^ -------- this expression has type `Option<&{integer}>` + | | + | types differ in mutability + | + = note: expected reference `&{integer}` + found mutable reference `&mut _` +note: to declare a mutable binding use: `mut x` + --> $DIR/ref_pat_everywhere-fail.rs:8:17 + | +LL | if let Some(&mut x) = Some(&0) { + | ^^^^^^ +help: consider removing `&mut` from the pattern + | +LL | if let Some(x) = Some(&0) { + | ~ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/match/ref_pat_everywhere-mutability-mismatch.stderr b/tests/ui/match/ref_pat_everywhere-mutability-mismatch.stderr deleted file mode 100644 index d512ea5f957..00000000000 --- a/tests/ui/match/ref_pat_everywhere-mutability-mismatch.stderr +++ /dev/null @@ -1,44 +0,0 @@ -error[E0308]: mismatched types - --> $DIR/ref_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/ref_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[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/ref_pat_everywhere.rs b/tests/ui/match/ref_pat_everywhere.rs index b3daca48409..9a79c548475 100644 --- a/tests/ui/match/ref_pat_everywhere.rs +++ b/tests/ui/match/ref_pat_everywhere.rs @@ -15,4 +15,10 @@ pub fn main() { if let Some(Some(&x)) = &Some(&mut Some(0)) { let _: u32 = x; } + if let &Some(x) = &mut Some(0) { + let _: u32 = x; + } + if let Some(&x) = &mut Some(0) { + let _: u32 = x; + } } From ed96c655c6e147f5c2ce54e91f71a0874f9f836a Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Sat, 4 May 2024 14:57:27 -0400 Subject: [PATCH 2/9] Various fixes: - Only show error when move-check would not be triggered - Add structured suggestion --- compiler/rustc_hir_typeck/src/pat.rs | 44 +++++++++++++------ .../ref_pat_eat_one_layer_2024_fail.rs | 12 +++-- .../ref_pat_eat_one_layer_2024_fail.stderr | 39 ++++++++-------- .../ref_pat_eat_one_layer_2024_fail2.rs | 3 ++ .../ref_pat_eat_one_layer_2024_fail2.stderr | 11 ++++- 5 files changed, 71 insertions(+), 38 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 99653814d6f..f1eceed0ac8 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -163,7 +163,13 @@ enum MutblCap { impl MutblCap { fn cap_mutbl_to_not(self, span: Option) -> Self { - if self == MutblCap::Mut { MutblCap::Not(span) } else { self } + if let Some(s) = span + && self != MutblCap::Not(None) + { + MutblCap::Not(Some(s)) + } else { + MutblCap::Not(None) + } } fn as_mutbl(self) -> Mutability { @@ -744,9 +750,14 @@ fn check_pat_ident( self.tcx.dcx(), ident.span, E0596, - "cannot bind with `ref mut` behind an `&` pattern" + "cannot borrow as mutable inside an `&` pattern" + ); + err.span_suggestion( + and_pat_span, + "replace this `&` with `&mut`", + "&mut ", + Applicability::MachineApplicable, ); - err.span_help(and_pat_span, "change this `&` pattern to an `&mut`"); err.emit(); } @@ -2187,7 +2198,21 @@ fn check_pat_ref( // the bad interactions of the given hack detailed in (note_1). debug!("check_pat_ref: expected={:?}", expected); match *expected.kind() { - ty::Ref(_, r_ty, r_mutbl) if r_mutbl == mutbl => (expected, r_ty, pat_info), + ty::Ref(_, r_ty, r_mutbl) if r_mutbl == mutbl => { + let pat_info = if r_mutbl == Mutability::Not + && ((pat.span.at_least_rust_2024() + && self.tcx.features().ref_pat_eat_one_layer_2024) + || self.tcx.features().ref_pat_everywhere) + { + PatInfo { + max_ref_mutbl: pat_info.max_ref_mutbl.cap_mutbl_to_not(None), + ..pat_info + } + } else { + pat_info + }; + (expected, r_ty, pat_info) + } // `&` pattern eats `&mut` reference ty::Ref(_, r_ty, Mutability::Mut) @@ -2196,16 +2221,7 @@ fn check_pat_ref( && self.tcx.features().ref_pat_eat_one_layer_2024) || self.tcx.features().ref_pat_everywhere) => { - ( - expected, - r_ty, - PatInfo { - max_ref_mutbl: pat_info - .max_ref_mutbl - .cap_mutbl_to_not(Some(pat.span.until(inner.span))), - ..pat_info - }, - ) + (expected, r_ty, pat_info) } _ if consumed_inherited_ref && self.tcx.features().ref_pat_everywhere => { diff --git a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.rs b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.rs index 61e61719458..376855406a6 100644 --- a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.rs +++ b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.rs @@ -24,18 +24,24 @@ pub fn main() { //~^ ERROR: mismatched types } if let Some(&Some(ref mut x)) = &mut Some(Some(0)) { - //~^ ERROR: cannot bind with `ref mut` behind an `&` pattern + //~^ ERROR: cannot borrow as mutable inside an `&` pattern } if let &Some(Some(ref mut x)) = &mut Some(Some(0)) { - //~^ ERROR: cannot bind with `ref mut` behind an `&` pattern + //~^ ERROR: cannot borrow as mutable inside an `&` pattern } if let Some(&mut Some(x)) = &Some(Some(0)) { //~^ ERROR: mismatched types } - let &mut _= &&0; + let &mut _ = &&0; //~^ ERROR: mismatched types let &mut _ = &&&&&&&&&&&&&&&&&&&&&&&&&&&&0; //~^ ERROR: mismatched types + + macro_rules! pat { + ($var:ident) => { ref mut $var }; + } + let &pat!(x) = &mut 0; + //~^ ERROR: cannot borrow as mutable inside an `&` pattern } diff --git a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.stderr b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.stderr index 2aa2e2851ce..0512a31011d 100644 --- a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.stderr +++ b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.stderr @@ -64,29 +64,21 @@ LL | if let Some(&mut Some(x)) = &Some(Some(0)) { = note: expected enum `Option<{integer}>` found mutable reference `&mut _` -error[E0596]: cannot bind with `ref mut` behind an `&` pattern +error[E0596]: cannot borrow as mutable inside an `&` pattern --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:26:31 | LL | if let Some(&Some(ref mut x)) = &mut Some(Some(0)) { - | ^ - | -help: change this `&` pattern to an `&mut` - --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:26:17 - | -LL | if let Some(&Some(ref mut x)) = &mut Some(Some(0)) { - | ^ + | - ^ + | | + | help: replace this `&` with `&mut`: `&mut` -error[E0596]: cannot bind with `ref mut` behind an `&` pattern +error[E0596]: cannot borrow as mutable inside an `&` pattern --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:29:31 | LL | if let &Some(Some(ref mut x)) = &mut Some(Some(0)) { - | ^ - | -help: change this `&` pattern to an `&mut` - --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:29:12 - | -LL | if let &Some(Some(ref mut x)) = &mut Some(Some(0)) { - | ^ + | - ^ + | | + | help: replace this `&` with `&mut`: `&mut` error[E0308]: mismatched types --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:32:17 @@ -102,8 +94,8 @@ LL | if let Some(&mut Some(x)) = &Some(Some(0)) { error[E0308]: mismatched types --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:36:9 | -LL | let &mut _= &&0; - | ^^^^^^ --- this expression has type `&&{integer}` +LL | let &mut _ = &&0; + | ^^^^^^ --- this expression has type `&&{integer}` | | | expected integer, found `&mut _` | @@ -121,7 +113,16 @@ LL | let &mut _ = &&&&&&&&&&&&&&&&&&&&&&&&&&&&0; = note: expected type `{integer}` found mutable reference `&mut _` -error: aborting due to 11 previous errors +error[E0596]: cannot borrow as mutable inside an `&` pattern + --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:45:15 + | +LL | ($var:ident) => { ref mut $var }; + | ------------ help: replace this `&` with `&mut`: `&mut` +LL | } +LL | let &pat!(x) = &mut 0; + | ^ + +error: aborting due to 12 previous errors Some errors have detailed explanations: E0308, E0596. For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail2.rs b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail2.rs index 36455488407..3cdf47c1dbf 100644 --- a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail2.rs +++ b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail2.rs @@ -8,4 +8,7 @@ pub fn main() { //~^ ERROR: cannot move out of a shared reference [E0507] let _: &u32 = x; } + + let &ref mut x = &0; + //~^ cannot borrow data in a `&` reference as mutable [E0596] } diff --git a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail2.stderr b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail2.stderr index ccfb5c7a0c0..8b86fa65c4d 100644 --- a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail2.stderr +++ b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail2.stderr @@ -12,6 +12,13 @@ help: consider borrowing the pattern binding LL | if let Some(&Some(ref x)) = Some(&Some(&mut 0)) { | +++ -error: aborting due to 1 previous error +error[E0596]: cannot borrow data in a `&` reference as mutable + --> $DIR/ref_pat_eat_one_layer_2024_fail2.rs:12:10 + | +LL | let &ref mut x = &0; + | ^^^^^^^^^ cannot borrow as mutable -For more information about this error, try `rustc --explain E0507`. +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0507, E0596. +For more information about an error, try `rustc --explain E0507`. From 91bbbaa0f75c321d471df6eecdb46fd6c7739fff Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Sat, 4 May 2024 15:20:06 -0400 Subject: [PATCH 3/9] Fix spans when macros are involved --- compiler/rustc_hir_typeck/src/pat.rs | 8 ++++---- .../ref_pat_eat_one_layer_2024_fail.stderr | 7 +++---- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index f1eceed0ac8..d8e21574cf4 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -137,7 +137,7 @@ enum AdjustMode { /// with mutability matching the pattern, /// mark the pattern as having consumed this reference. /// - /// `Span` is that of the inside of the reference pattern + /// `Span` is that of the `&` or `&mut` itself ResetAndConsumeRef(Mutability, Span), /// Pass on the input binding mode and expected type. Pass, @@ -342,14 +342,14 @@ fn calc_default_binding_mode( match adjust_mode { AdjustMode::Pass => (expected, def_br, max_ref_mutbl, false), AdjustMode::Reset => (expected, ByRef::No, MutblCap::Mut, false), - AdjustMode::ResetAndConsumeRef(ref_pat_mutbl, inner_span) => { + AdjustMode::ResetAndConsumeRef(ref_pat_mutbl, ref_span) => { // `&` pattern eats `&mut` let mutbls_match = if let ByRef::Yes(def_mut) = def_br { ref_pat_mutbl <= def_mut } else { false }; if pat.span.at_least_rust_2024() && self.tcx.features().ref_pat_eat_one_layer_2024 { let max_ref_mutbl = if ref_pat_mutbl == Mutability::Not { - max_ref_mutbl.cap_mutbl_to_not(Some(pat.span.until(inner_span))) + max_ref_mutbl.cap_mutbl_to_not(Some(ref_span)) } else { max_ref_mutbl }; @@ -434,7 +434,7 @@ fn calc_adjust_mode(&self, pat: &'tcx Pat<'tcx>, opt_path_res: Option) -> A // ``` // // See issue #46688. - PatKind::Ref(inner, mutbl) => AdjustMode::ResetAndConsumeRef(*mutbl, inner.span), + PatKind::Ref(inner, mutbl) => AdjustMode::ResetAndConsumeRef(*mutbl, pat.span.until(inner.span.find_ancestor_inside(pat.span).unwrap())), // 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. diff --git a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.stderr b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.stderr index 0512a31011d..d51b92230e8 100644 --- a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.stderr +++ b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.stderr @@ -116,11 +116,10 @@ LL | let &mut _ = &&&&&&&&&&&&&&&&&&&&&&&&&&&&0; error[E0596]: cannot borrow as mutable inside an `&` pattern --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:45:15 | -LL | ($var:ident) => { ref mut $var }; - | ------------ help: replace this `&` with `&mut`: `&mut` -LL | } LL | let &pat!(x) = &mut 0; - | ^ + | - ^ + | | + | help: replace this `&` with `&mut`: `&mut` error: aborting due to 12 previous errors From f57b970de8103f1d7cd6307ad18e9f07d21e0e84 Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Sat, 4 May 2024 16:04:08 -0400 Subject: [PATCH 4/9] Comments and fixes --- compiler/rustc_hir_typeck/src/pat.rs | 37 ++++++++++------ .../ref_pat_eat_one_layer_2024_fail.rs | 12 ------ .../ref_pat_eat_one_layer_2024_fail.stderr | 35 +++------------ ...at_one_layer_2024_ref_mut_inside_and.fixed | 30 +++++++++++++ ...t_eat_one_layer_2024_ref_mut_inside_and.rs | 30 +++++++++++++ ...t_one_layer_2024_ref_mut_inside_and.stderr | 43 +++++++++++++++++++ 6 files changed, 132 insertions(+), 55 deletions(-) create mode 100644 tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_ref_mut_inside_and.fixed create mode 100644 tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_ref_mut_inside_and.rs create mode 100644 tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_ref_mut_inside_and.stderr diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index d8e21574cf4..170057d4adf 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -137,8 +137,8 @@ enum AdjustMode { /// with mutability matching the pattern, /// mark the pattern as having consumed this reference. /// - /// `Span` is that of the `&` or `&mut` itself - ResetAndConsumeRef(Mutability, Span), + /// `Span` is that of the `&` or `&mut` itself. + ResetAndConsumeRef(Mutability, Option), /// Pass on the input binding mode and expected type. Pass, } @@ -154,15 +154,23 @@ enum AdjustMode { /// this last case, so we need to throw an error ourselves. #[derive(Clone, Copy, Debug, PartialEq, Eq)] enum MutblCap { - /// Mutability restricted to immutable; - /// contained span, if present, should be shown in diagnostics as the reason. - Not(Option), + /// Mutability restricted to immutable. + /// + /// The contained span, if present, points to an `&` pattern + /// that is the reason for the restriction, + /// and which will be reported in a diagnostic. + /// (Said diagnostic is shown only if + /// replacing the `&` pattern with `&mut` would allow the code to compile.) + /// + /// (Outer [`Option`] is for whether to show the diagnostic, + /// inner [`Option`] is for whether we have a span we can report) + Not(Option>), /// No restriction on mutability Mut, } impl MutblCap { - fn cap_mutbl_to_not(self, span: Option) -> Self { + fn cap_mutbl_to_not(self, span: Option>) -> Self { if let Some(s) = span && self != MutblCap::Not(None) { @@ -434,7 +442,7 @@ fn calc_adjust_mode(&self, pat: &'tcx Pat<'tcx>, opt_path_res: Option) -> A // ``` // // See issue #46688. - PatKind::Ref(inner, mutbl) => AdjustMode::ResetAndConsumeRef(*mutbl, pat.span.until(inner.span.find_ancestor_inside(pat.span).unwrap())), + PatKind::Ref(inner, mutbl) => AdjustMode::ResetAndConsumeRef(*mutbl, inner.span.find_ancestor_inside(pat.span).map(|end| pat.span.until(end))), // 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. @@ -752,12 +760,15 @@ fn check_pat_ident( E0596, "cannot borrow as mutable inside an `&` pattern" ); - err.span_suggestion( - and_pat_span, - "replace this `&` with `&mut`", - "&mut ", - Applicability::MachineApplicable, - ); + + if let Some(span) = and_pat_span { + err.span_suggestion( + span, + "replace this `&` with `&mut`", + "&mut ", + Applicability::MachineApplicable, + ); + } err.emit(); } diff --git a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.rs b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.rs index 376855406a6..96b4ff77ddb 100644 --- a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.rs +++ b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.rs @@ -23,12 +23,6 @@ pub fn main() { if let Some(&mut Some(x)) = &Some(Some(0)) { //~^ ERROR: mismatched types } - if let Some(&Some(ref mut x)) = &mut Some(Some(0)) { - //~^ ERROR: cannot borrow as mutable inside an `&` pattern - } - if let &Some(Some(ref mut x)) = &mut Some(Some(0)) { - //~^ ERROR: cannot borrow as mutable inside an `&` pattern - } if let Some(&mut Some(x)) = &Some(Some(0)) { //~^ ERROR: mismatched types } @@ -38,10 +32,4 @@ pub fn main() { let &mut _ = &&&&&&&&&&&&&&&&&&&&&&&&&&&&0; //~^ ERROR: mismatched types - - macro_rules! pat { - ($var:ident) => { ref mut $var }; - } - let &pat!(x) = &mut 0; - //~^ ERROR: cannot borrow as mutable inside an `&` pattern } diff --git a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.stderr b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.stderr index d51b92230e8..e06a645fc0d 100644 --- a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.stderr +++ b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.stderr @@ -64,24 +64,8 @@ LL | if let Some(&mut Some(x)) = &Some(Some(0)) { = note: expected enum `Option<{integer}>` found mutable reference `&mut _` -error[E0596]: cannot borrow as mutable inside an `&` pattern - --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:26:31 - | -LL | if let Some(&Some(ref mut x)) = &mut Some(Some(0)) { - | - ^ - | | - | help: replace this `&` with `&mut`: `&mut` - -error[E0596]: cannot borrow as mutable inside an `&` pattern - --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:29:31 - | -LL | if let &Some(Some(ref mut x)) = &mut Some(Some(0)) { - | - ^ - | | - | help: replace this `&` with `&mut`: `&mut` - error[E0308]: mismatched types - --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:32:17 + --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:26:17 | LL | if let Some(&mut Some(x)) = &Some(Some(0)) { | ^^^^^^^^^^^^ -------------- this expression has type `&Option>` @@ -92,7 +76,7 @@ LL | if let Some(&mut Some(x)) = &Some(Some(0)) { found mutable reference `&mut _` error[E0308]: mismatched types - --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:36:9 + --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:30:9 | LL | let &mut _ = &&0; | ^^^^^^ --- this expression has type `&&{integer}` @@ -103,7 +87,7 @@ LL | let &mut _ = &&0; found mutable reference `&mut _` error[E0308]: mismatched types - --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:39:9 + --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:33:9 | LL | let &mut _ = &&&&&&&&&&&&&&&&&&&&&&&&&&&&0; | ^^^^^^ ----------------------------- this expression has type `&&&&&&&&&&&&&&&&&&&&&&&&&&&&{integer}` @@ -113,15 +97,6 @@ LL | let &mut _ = &&&&&&&&&&&&&&&&&&&&&&&&&&&&0; = note: expected type `{integer}` found mutable reference `&mut _` -error[E0596]: cannot borrow as mutable inside an `&` pattern - --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:45:15 - | -LL | let &pat!(x) = &mut 0; - | - ^ - | | - | help: replace this `&` with `&mut`: `&mut` +error: aborting due to 9 previous errors -error: aborting due to 12 previous errors - -Some errors have detailed explanations: E0308, E0596. -For more information about an error, try `rustc --explain E0308`. +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_ref_mut_inside_and.fixed b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_ref_mut_inside_and.fixed new file mode 100644 index 00000000000..bc7a58a382d --- /dev/null +++ b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_ref_mut_inside_and.fixed @@ -0,0 +1,30 @@ +//@ edition: 2024 +//@ compile-flags: -Zunstable-options +//@ run-rustfix +#![allow(incomplete_features)] +#![feature(ref_pat_eat_one_layer_2024)] + +pub fn main() { + if let Some(&mut Some(ref mut x)) = &mut Some(Some(0)) { + //~^ ERROR: cannot borrow as mutable inside an `&` pattern + let _: &mut u8 = x; + } + + if let &mut Some(Some(ref mut x)) = &mut Some(Some(0)) { + //~^ ERROR: cannot borrow as mutable inside an `&` pattern + let _: &mut u8 = x; + } + + macro_rules! pat { + ($var:ident) => { ref mut $var }; + } + let &mut pat!(x) = &mut 0; + //~^ ERROR: cannot borrow as mutable inside an `&` pattern + let _: &mut u8 = x; + + let &mut (ref mut a, ref mut b) = &mut (true, false); + //~^ ERROR: cannot borrow as mutable inside an `&` pattern + //~| ERROR: cannot borrow as mutable inside an `&` pattern + let _: &mut bool = a; + let _: &mut bool = b; +} diff --git a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_ref_mut_inside_and.rs b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_ref_mut_inside_and.rs new file mode 100644 index 00000000000..c6d72b0a9d7 --- /dev/null +++ b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_ref_mut_inside_and.rs @@ -0,0 +1,30 @@ +//@ edition: 2024 +//@ compile-flags: -Zunstable-options +//@ run-rustfix +#![allow(incomplete_features)] +#![feature(ref_pat_eat_one_layer_2024)] + +pub fn main() { + if let Some(&Some(ref mut x)) = &mut Some(Some(0)) { + //~^ ERROR: cannot borrow as mutable inside an `&` pattern + let _: &mut u8 = x; + } + + if let &Some(Some(ref mut x)) = &mut Some(Some(0)) { + //~^ ERROR: cannot borrow as mutable inside an `&` pattern + let _: &mut u8 = x; + } + + macro_rules! pat { + ($var:ident) => { ref mut $var }; + } + let &pat!(x) = &mut 0; + //~^ ERROR: cannot borrow as mutable inside an `&` pattern + let _: &mut u8 = x; + + let &(ref mut a, ref mut b) = &mut (true, false); + //~^ ERROR: cannot borrow as mutable inside an `&` pattern + //~| ERROR: cannot borrow as mutable inside an `&` pattern + let _: &mut bool = a; + let _: &mut bool = b; +} diff --git a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_ref_mut_inside_and.stderr b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_ref_mut_inside_and.stderr new file mode 100644 index 00000000000..964e9f36596 --- /dev/null +++ b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_ref_mut_inside_and.stderr @@ -0,0 +1,43 @@ +error[E0596]: cannot borrow as mutable inside an `&` pattern + --> $DIR/ref_pat_eat_one_layer_2024_ref_mut_inside_and.rs:8:31 + | +LL | if let Some(&Some(ref mut x)) = &mut Some(Some(0)) { + | - ^ + | | + | help: replace this `&` with `&mut`: `&mut` + +error[E0596]: cannot borrow as mutable inside an `&` pattern + --> $DIR/ref_pat_eat_one_layer_2024_ref_mut_inside_and.rs:13:31 + | +LL | if let &Some(Some(ref mut x)) = &mut Some(Some(0)) { + | - ^ + | | + | help: replace this `&` with `&mut`: `&mut` + +error[E0596]: cannot borrow as mutable inside an `&` pattern + --> $DIR/ref_pat_eat_one_layer_2024_ref_mut_inside_and.rs:21:15 + | +LL | let &pat!(x) = &mut 0; + | - ^ + | | + | help: replace this `&` with `&mut`: `&mut` + +error[E0596]: cannot borrow as mutable inside an `&` pattern + --> $DIR/ref_pat_eat_one_layer_2024_ref_mut_inside_and.rs:25:19 + | +LL | let &(ref mut a, ref mut b) = &mut (true, false); + | - ^ + | | + | help: replace this `&` with `&mut`: `&mut` + +error[E0596]: cannot borrow as mutable inside an `&` pattern + --> $DIR/ref_pat_eat_one_layer_2024_ref_mut_inside_and.rs:25:30 + | +LL | let &(ref mut a, ref mut b) = &mut (true, false); + | - ^ + | | + | help: replace this `&` with `&mut`: `&mut` + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0596`. From 0f03c2be580548b70d88a809199d840e7d5d5b22 Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Sat, 4 May 2024 16:14:44 -0400 Subject: [PATCH 5/9] Rename `explicit_ba` --- compiler/rustc_hir_typeck/src/pat.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 170057d4adf..482c4ed3af3 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -722,7 +722,7 @@ fn emit_err_pat_range( fn check_pat_ident( &self, pat: &'tcx Pat<'tcx>, - explicit_ba: BindingMode, + user_bind_annot: BindingMode, var_id: HirId, ident: Ident, sub: Option<&'tcx Pat<'tcx>>, @@ -732,7 +732,7 @@ fn check_pat_ident( let PatInfo { binding_mode: def_br, top_info: ti, .. } = pat_info; // Determine the binding mode... - let bm = match explicit_ba { + let bm = match user_bind_annot { BindingMode(ByRef::No, Mutability::Mut) if !(pat.span.at_least_rust_2024() && self.tcx.features().mut_preserve_binding_mode_2024) @@ -748,7 +748,7 @@ fn check_pat_ident( BindingMode(ByRef::No, Mutability::Mut) } BindingMode(ByRef::No, mutbl) => BindingMode(def_br, mutbl), - BindingMode(ByRef::Yes(_), _) => explicit_ba, + BindingMode(ByRef::Yes(_), _) => user_bind_annot, }; if bm.0 == ByRef::Yes(Mutability::Mut) @@ -797,7 +797,7 @@ fn check_pat_ident( // If there are multiple arms, make sure they all agree on // what the type of the binding `x` ought to be. if var_id != pat.hir_id { - self.check_binding_alt_eq_ty(explicit_ba, pat.span, var_id, local_ty, ti); + self.check_binding_alt_eq_ty(user_bind_annot, pat.span, var_id, local_ty, ti); } if let Some(p) = sub { From d8a798b5e93e36a62d3c114f7439cb1e4299279d Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Sun, 5 May 2024 11:35:49 -0400 Subject: [PATCH 6/9] No more `Option>` --- compiler/rustc_hir_typeck/src/pat.rs | 34 ++++++++++++---------------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 482c4ed3af3..5a2fed13d2b 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -155,34 +155,33 @@ enum AdjustMode { #[derive(Clone, Copy, Debug, PartialEq, Eq)] enum MutblCap { /// Mutability restricted to immutable. + Not, + + /// Mutability restricted to immutable, but only because of the pattern + /// (not the scrutinee type). /// /// The contained span, if present, points to an `&` pattern /// that is the reason for the restriction, /// and which will be reported in a diagnostic. /// (Said diagnostic is shown only if /// replacing the `&` pattern with `&mut` would allow the code to compile.) - /// - /// (Outer [`Option`] is for whether to show the diagnostic, - /// inner [`Option`] is for whether we have a span we can report) - Not(Option>), + WeaklyNot(Option), + /// No restriction on mutability Mut, } impl MutblCap { - fn cap_mutbl_to_not(self, span: Option>) -> Self { - if let Some(s) = span - && self != MutblCap::Not(None) - { - MutblCap::Not(Some(s)) - } else { - MutblCap::Not(None) + fn cap_to_weakly_not(self, span: Option) -> Self { + match self { + MutblCap::Not => MutblCap::Not, + _ => MutblCap::WeaklyNot(span), } } fn as_mutbl(self) -> Mutability { match self { - MutblCap::Not(_) => Mutability::Not, + MutblCap::Not | MutblCap::WeaklyNot(_) => Mutability::Not, MutblCap::Mut => Mutability::Mut, } } @@ -357,7 +356,7 @@ fn calc_default_binding_mode( if pat.span.at_least_rust_2024() && self.tcx.features().ref_pat_eat_one_layer_2024 { let max_ref_mutbl = if ref_pat_mutbl == Mutability::Not { - max_ref_mutbl.cap_mutbl_to_not(Some(ref_span)) + max_ref_mutbl.cap_to_weakly_not(ref_span) } else { max_ref_mutbl }; @@ -505,7 +504,7 @@ fn peel_off_references( if pat.span.at_least_rust_2024() && self.tcx.features().ref_pat_eat_one_layer_2024 { def_br = def_br.cap_ref_mutability(max_ref_mutability.as_mutbl()); if def_br == ByRef::Yes(Mutability::Not) { - max_ref_mutability = max_ref_mutability.cap_mutbl_to_not(None); + max_ref_mutability = MutblCap::Not; } } @@ -752,7 +751,7 @@ fn check_pat_ident( }; if bm.0 == ByRef::Yes(Mutability::Mut) - && let MutblCap::Not(Some(and_pat_span)) = pat_info.max_ref_mutbl + && let MutblCap::WeaklyNot(and_pat_span) = pat_info.max_ref_mutbl { let mut err = struct_span_code_err!( self.tcx.dcx(), @@ -2215,10 +2214,7 @@ fn check_pat_ref( && self.tcx.features().ref_pat_eat_one_layer_2024) || self.tcx.features().ref_pat_everywhere) { - PatInfo { - max_ref_mutbl: pat_info.max_ref_mutbl.cap_mutbl_to_not(None), - ..pat_info - } + PatInfo { max_ref_mutbl: MutblCap::Not, ..pat_info } } else { pat_info }; From bff287b4a5c3379db3ffb045fe9eb6eea2eed7d7 Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Sun, 5 May 2024 12:05:18 -0400 Subject: [PATCH 7/9] Remove redundant comment --- compiler/rustc_hir_typeck/src/pat.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 5a2fed13d2b..a4cee5aac8d 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -163,8 +163,6 @@ enum MutblCap { /// The contained span, if present, points to an `&` pattern /// that is the reason for the restriction, /// and which will be reported in a diagnostic. - /// (Said diagnostic is shown only if - /// replacing the `&` pattern with `&mut` would allow the code to compile.) WeaklyNot(Option), /// No restriction on mutability From 7951311c1bbcfc8f3487004f1df7e97a2ac6dc80 Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Sun, 5 May 2024 23:57:52 -0400 Subject: [PATCH 8/9] Move all ref pat logic into `check_pat_ref` --- compiler/rustc_hir_typeck/src/pat.rs | 285 ++++++++++++--------------- 1 file changed, 131 insertions(+), 154 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index a4cee5aac8d..b18da758c1c 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -131,14 +131,6 @@ enum AdjustMode { /// 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. - /// - /// `Span` is that of the `&` or `&mut` itself. - ResetAndConsumeRef(Mutability, Option), /// Pass on the input binding mode and expected type. Pass, } @@ -170,6 +162,7 @@ enum MutblCap { } impl MutblCap { + #[must_use] fn cap_to_weakly_not(self, span: Option) -> Self { match self { MutblCap::Not => MutblCap::Not, @@ -177,6 +170,7 @@ fn cap_to_weakly_not(self, span: Option) -> Self { } } + #[must_use] fn as_mutbl(self) -> Mutability { match self { MutblCap::Not | MutblCap::WeaklyNot(_) => Mutability::Not, @@ -214,14 +208,13 @@ pub(crate) fn check_pat_top( } /// Type check the given `pat` against the `expected` type - /// with the provided `def_bm` (default binding mode). + /// with the provided `binding_mode` (default binding mode). /// /// Outside of this module, `check_pat_top` should always be used. /// Conversely, inside this module, `check_pat_top` should never be used. #[instrument(level = "debug", skip(self, pat_info))] fn check_pat(&self, pat: &'tcx Pat<'tcx>, expected: Ty<'tcx>, pat_info: PatInfo<'tcx, '_>) { - let PatInfo { binding_mode: def_bm, max_ref_mutbl, top_info: ti, current_depth, .. } = - pat_info; + let PatInfo { binding_mode, max_ref_mutbl, top_info: ti, current_depth, .. } = pat_info; let path_res = match &pat.kind { PatKind::Path(qpath) => Some( @@ -230,10 +223,10 @@ fn check_pat(&self, pat: &'tcx Pat<'tcx>, expected: Ty<'tcx>, pat_info: PatInfo< _ => None, }; let adjust_mode = self.calc_adjust_mode(pat, path_res.map(|(res, ..)| res)); - let (expected, def_bm, max_ref_mutbl, ref_pattern_already_consumed) = - self.calc_default_binding_mode(pat, expected, def_bm, adjust_mode, max_ref_mutbl); + let (expected, binding_mode, max_ref_mutbl) = + self.calc_default_binding_mode(pat, expected, binding_mode, adjust_mode, max_ref_mutbl); let pat_info = PatInfo { - binding_mode: def_bm, + binding_mode, max_ref_mutbl, top_info: ti, decl_origin: pat_info.decl_origin, @@ -269,14 +262,7 @@ fn check_pat(&self, pat: &'tcx Pat<'tcx>, expected: Ty<'tcx>, pat_info: PatInfo< } 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, - ref_pattern_already_consumed, - ), + PatKind::Ref(inner, mutbl) => self.check_pat_ref(pat, inner, mutbl, expected, pat_info), PatKind::Slice(before, slice, after) => { self.check_pat_slice(pat.span, before, slice, after, expected, pat_info) } @@ -329,10 +315,6 @@ fn check_pat(&self, pat: &'tcx Pat<'tcx>, expected: Ty<'tcx>, pat_info: PatInfo< /// 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>, @@ -340,50 +322,15 @@ fn calc_default_binding_mode( def_br: ByRef, adjust_mode: AdjustMode, max_ref_mutbl: MutblCap, - ) -> (Ty<'tcx>, ByRef, MutblCap, bool) { + ) -> (Ty<'tcx>, ByRef, MutblCap) { if let ByRef::Yes(Mutability::Mut) = def_br { debug_assert!(max_ref_mutbl == MutblCap::Mut); } match adjust_mode { - AdjustMode::Pass => (expected, def_br, max_ref_mutbl, false), - AdjustMode::Reset => (expected, ByRef::No, MutblCap::Mut, false), - AdjustMode::ResetAndConsumeRef(ref_pat_mutbl, ref_span) => { - // `&` pattern eats `&mut` - let mutbls_match = - if let ByRef::Yes(def_mut) = def_br { ref_pat_mutbl <= def_mut } else { false }; - - if pat.span.at_least_rust_2024() && self.tcx.features().ref_pat_eat_one_layer_2024 { - let max_ref_mutbl = if ref_pat_mutbl == Mutability::Not { - max_ref_mutbl.cap_to_weakly_not(ref_span) - } else { - max_ref_mutbl - }; - - if mutbls_match { - debug!("consuming inherited reference"); - (expected, ByRef::No, max_ref_mutbl, true) - } else { - let (new_ty, new_bm, max_ref_mutbl) = if ref_pat_mutbl == Mutability::Mut { - self.peel_off_references( - pat, - expected, - def_br, - Mutability::Not, - max_ref_mutbl, - ) - } else { - (expected, def_br.cap_ref_mutability(Mutability::Not), max_ref_mutbl) - }; - (new_ty, new_bm, max_ref_mutbl, false) - } - } else { - (expected, ByRef::No, max_ref_mutbl, mutbls_match) - } - } + AdjustMode::Pass => (expected, def_br, max_ref_mutbl), + AdjustMode::Reset => (expected, ByRef::No, MutblCap::Mut), AdjustMode::Peel => { - let peeled = - self.peel_off_references(pat, expected, def_br, Mutability::Mut, max_ref_mutbl); - (peeled.0, peeled.1, peeled.2, false) + self.peel_off_references(pat, expected, def_br, Mutability::Mut, max_ref_mutbl) } } } @@ -429,17 +376,8 @@ fn calc_adjust_mode(&self, pat: &'tcx Pat<'tcx>, opt_path_res: Option) -> A // a reference type wherefore peeling doesn't give up any expressiveness. _ => AdjustMode::Peel, }, - // When encountering a `& mut? pat` pattern, reset to "by value". - // This is so that `x` and `y` here are by value, as they appear to be: - // - // ``` - // match &(&22, &44) { - // (&x, &y) => ... - // } - // ``` - // - // See issue #46688. - PatKind::Ref(inner, mutbl) => AdjustMode::ResetAndConsumeRef(*mutbl, inner.span.find_ancestor_inside(pat.span).map(|end| pat.span.until(end))), + // Ref patterns are complicated, we handle them in `check_pat_ref`. + PatKind::Ref(..) => AdjustMode::Pass, // 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. @@ -2179,96 +2117,135 @@ fn check_pat_ref( &self, pat: &'tcx Pat<'tcx>, inner: &'tcx Pat<'tcx>, - mutbl: Mutability, - expected: Ty<'tcx>, - pat_info: PatInfo<'tcx, '_>, - consumed_inherited_ref: bool, + pat_mutbl: Mutability, + mut expected: Ty<'tcx>, + mut pat_info: PatInfo<'tcx, '_>, ) -> Ty<'tcx> { - if consumed_inherited_ref - && pat.span.at_least_rust_2024() - && self.tcx.features().ref_pat_eat_one_layer_2024 - { - self.typeck_results.borrow_mut().skipped_ref_pats_mut().insert(pat.hir_id); - self.check_pat(inner, expected, pat_info); - expected - } else { - let tcx = self.tcx; - let expected = self.shallow_resolve(expected); - let (ref_ty, inner_ty, pat_info) = match self - .check_dereferenceable(pat.span, expected, inner) + // FIXME: repace with `bool` once final decision on 1 vs 2 layers is made + #[derive(Clone, Copy, Debug, PartialEq, Eq)] + enum MatchErgonomicsMode { + EatOneLayer, + EatTwoLayers, + Legacy, + } + + let match_ergonomics_mode = + if pat.span.at_least_rust_2024() && self.tcx.features().ref_pat_eat_one_layer_2024 { + MatchErgonomicsMode::EatOneLayer + } else if self.tcx.features().ref_pat_everywhere { + MatchErgonomicsMode::EatTwoLayers + } else { + MatchErgonomicsMode::Legacy + }; + + let mut inherited_ref_mutbl_match = false; + if match_ergonomics_mode != MatchErgonomicsMode::Legacy { + if pat_mutbl == Mutability::Not { + pat_info.max_ref_mutbl = pat_info.max_ref_mutbl.cap_to_weakly_not( + inner.span.find_ancestor_inside(pat.span).map(|end| pat.span.until(end)), + ); + } + + if let ByRef::Yes(inh_mut) = pat_info.binding_mode { + inherited_ref_mutbl_match = pat_mutbl <= inh_mut; + } + + if inherited_ref_mutbl_match { + pat_info.binding_mode = ByRef::No; + if match_ergonomics_mode == MatchErgonomicsMode::EatOneLayer { + self.typeck_results.borrow_mut().skipped_ref_pats_mut().insert(pat.hir_id); + self.check_pat(inner, expected, pat_info); + return expected; + } + } else if match_ergonomics_mode == MatchErgonomicsMode::EatOneLayer + && pat_mutbl == Mutability::Mut { - Ok(()) => { - // `demand::subtype` would be good enough, but using `eqtype` turns - // out to be equally general. See (note_1) for details. + // `&mut` patterns pell off `&` references + let (new_expected, new_bm, max_ref_mutbl) = self.peel_off_references( + pat, + expected, + pat_info.binding_mode, + Mutability::Not, + pat_info.max_ref_mutbl, + ); + expected = new_expected; + pat_info.binding_mode = new_bm; + pat_info.max_ref_mutbl = max_ref_mutbl; + } + } else { + // Reset binding mode on old editions + pat_info.binding_mode = ByRef::No; + pat_info.max_ref_mutbl = MutblCap::Mut + } - // Take region, inner-type from expected type if we can, - // to avoid creating needless variables. This also helps with - // the bad interactions of the given hack detailed in (note_1). - debug!("check_pat_ref: expected={:?}", expected); - match *expected.kind() { - ty::Ref(_, r_ty, r_mutbl) if r_mutbl == mutbl => { - let pat_info = if r_mutbl == Mutability::Not - && ((pat.span.at_least_rust_2024() - && self.tcx.features().ref_pat_eat_one_layer_2024) - || self.tcx.features().ref_pat_everywhere) - { - PatInfo { max_ref_mutbl: MutblCap::Not, ..pat_info } - } else { - pat_info - }; - (expected, r_ty, pat_info) - } + let tcx = self.tcx; + expected = self.try_structurally_resolve_type(pat.span, expected); + let (ref_ty, inner_ty) = match self.check_dereferenceable(pat.span, expected, inner) { + Ok(()) => { + // `demand::subtype` would be good enough, but using `eqtype` turns + // out to be equally general. See (note_1) for details. - // `&` pattern eats `&mut` reference - ty::Ref(_, r_ty, Mutability::Mut) - if mutbl == Mutability::Not - && ((pat.span.at_least_rust_2024() - && self.tcx.features().ref_pat_eat_one_layer_2024) - || self.tcx.features().ref_pat_everywhere) => + // Take region, inner-type from expected type if we can, + // to avoid creating needless variables. This also helps with + // the bad interactions of the given hack detailed in (note_1). + debug!("check_pat_ref: expected={:?}", expected); + match *expected.kind() { + ty::Ref(_, r_ty, r_mutbl) if r_mutbl == pat_mutbl => { + if r_mutbl == Mutability::Not + && match_ergonomics_mode != MatchErgonomicsMode::Legacy { - (expected, r_ty, pat_info) + pat_info.max_ref_mutbl = MutblCap::Not; } - _ 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() - .skipped_ref_pats_mut() - .insert(pat.hir_id); - (expected, expected, pat_info) - } + (expected, r_ty) + } - _ => { - let inner_ty = self.next_ty_var(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, - ); + // `&` pattern eats `&mut` reference + ty::Ref(_, r_ty, Mutability::Mut) + if pat_mutbl == Mutability::Not + && match_ergonomics_mode != MatchErgonomicsMode::Legacy => + { + (expected, r_ty) + } - // 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, pat_info) + _ if inherited_ref_mutbl_match + && match_ergonomics_mode == MatchErgonomicsMode::EatTwoLayers => + { + // 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 info for use in lowering later + self.typeck_results.borrow_mut().skipped_ref_pats_mut().insert(pat.hir_id); + (expected, expected) + } + + _ => { + let inner_ty = self.next_ty_var(inner.span); + let ref_ty = self.new_ref_ty(pat.span, pat_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(); } + (ref_ty, inner_ty) } } - Err(guar) => { - let err = Ty::new_error(tcx, guar); - (err, err, pat_info) - } - }; - self.check_pat(inner, inner_ty, pat_info); - ref_ty - } + } + Err(guar) => { + let err = Ty::new_error(tcx, guar); + (err, err) + } + }; + self.check_pat(inner, inner_ty, pat_info); + ref_ty } /// Create a reference type with a fresh region variable. From 6d5c6f541f5dc9f3142c84bbc5203e57c0a4a18c Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Fri, 10 May 2024 13:53:48 -0400 Subject: [PATCH 9/9] Add comment on `cap_to_weakly_not` Co-authored-by: Guillaume Boisseau --- compiler/rustc_hir_typeck/src/pat.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index b18da758c1c..7029f5433d8 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -2141,6 +2141,7 @@ enum MatchErgonomicsMode { let mut inherited_ref_mutbl_match = false; if match_ergonomics_mode != MatchErgonomicsMode::Legacy { if pat_mutbl == Mutability::Not { + // Prevent the inner pattern from binding with `ref mut`. pat_info.max_ref_mutbl = pat_info.max_ref_mutbl.cap_to_weakly_not( inner.span.find_ancestor_inside(pat.span).map(|end| pat.span.until(end)), );