From 93544d5db3d8f0d0e5d2ea344154689008fa2ff8 Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Sun, 31 Mar 2024 22:27:19 -0500 Subject: [PATCH 1/6] Match ergonomics 2024: Implement eat-one-layer --- compiler/rustc_feature/src/unstable.rs | 2 + compiler/rustc_hir_typeck/src/pat.rs | 120 +++++++++------- compiler/rustc_span/src/symbol.rs | 1 + ...feature-gate-ref_pat_eat_one_layer_2024.rs | 37 +++++ ...ure-gate-ref_pat_eat_one_layer_2024.stderr | 128 ++++++++++++++++++ .../ref_pat_eat_one_layer_2021.rs | 37 +++++ .../ref_pat_eat_one_layer_2021.stderr | 128 ++++++++++++++++++ .../ref_pat_eat_one_layer_2024.rs | 32 +++++ .../ref_pat_eat_one_layer_2024_fail.rs | 13 ++ .../ref_pat_eat_one_layer_2024_fail.stderr | 25 ++++ 10 files changed, 472 insertions(+), 51 deletions(-) create mode 100644 tests/ui/match/ref_pat_eat_one_layer_2024/feature-gate-ref_pat_eat_one_layer_2024.rs create mode 100644 tests/ui/match/ref_pat_eat_one_layer_2024/feature-gate-ref_pat_eat_one_layer_2024.stderr create mode 100644 tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2021.rs create mode 100644 tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2021.stderr create mode 100644 tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024.rs create mode 100644 tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.rs create mode 100644 tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.stderr diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index e6b19817de385..5d17675a5325c 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -571,6 +571,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)), + /// Makes `&` and `&mut` patterns eat only one layer of references in Rust 2024. + (incomplete, ref_pat_eat_one_layer_2024, "CURRENT_RUSTC_VERSION", Some(123076)), /// 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. diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index db4bd132b7e30..ff7716ce7eae0 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -294,7 +294,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { AdjustMode::Pass => (expected, def_bm, false), AdjustMode::Reset => (expected, INITIAL_BM, false), AdjustMode::ResetAndConsumeRef(mutbl) => { - (expected, INITIAL_BM, def_bm.0 == ByRef::Yes(mutbl)) + let mutbls_match = def_bm.0 == ByRef::Yes(mutbl); + if pat.span.at_least_rust_2024() && self.tcx.features().ref_pat_eat_one_layer_2024 { + if mutbls_match { + (expected, INITIAL_BM, true) + } else { + (expected, def_bm, false) + } + } else { + (expected, INITIAL_BM, mutbls_match) + } } AdjustMode::Peel => { let peeled = self.peel_off_references(pat, expected, def_bm); @@ -2056,61 +2065,70 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pat_info: PatInfo<'tcx, '_>, consumed_inherited_ref: bool, ) -> Ty<'tcx> { - let tcx = self.tcx; - let expected = self.shallow_resolve(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. - - // 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 => (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(TypeVariableOrigin { - param_def_id: None, - 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 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) = 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. + + // 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 => (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(TypeVariableOrigin { + param_def_id: None, + 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) } } } - } - Err(guar) => { - let err = Ty::new_error(tcx, guar); - (err, err) - } - }; - 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. diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index bfd0f77c237b2..baf7fab3ad852 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1460,6 +1460,7 @@ symbols! { receiver, recursion_limit, reexport_test_harness_main, + ref_pat_eat_one_layer_2024, ref_pat_everywhere, ref_unwind_safe_trait, reference, diff --git a/tests/ui/match/ref_pat_eat_one_layer_2024/feature-gate-ref_pat_eat_one_layer_2024.rs b/tests/ui/match/ref_pat_eat_one_layer_2024/feature-gate-ref_pat_eat_one_layer_2024.rs new file mode 100644 index 0000000000000..83f1ee6a77e89 --- /dev/null +++ b/tests/ui/match/ref_pat_eat_one_layer_2024/feature-gate-ref_pat_eat_one_layer_2024.rs @@ -0,0 +1,37 @@ +//@ edition: 2024 +//@ compile-flags: -Zunstable-options + +pub fn main() { + if let Some(Some(&x)) = &Some(&Some(0)) { + //~^ ERROR: mismatched types + let _: u32 = x; + } + if let Some(Some(&x)) = &Some(Some(&0)) { + let _: &u32 = x; + //~^ ERROR: mismatched types + } + if let Some(Some(&&x)) = &Some(Some(&0)) { + //~^ ERROR: mismatched types + let _: u32 = x; + } + if let Some(&Some(x)) = &Some(Some(0)) { + //~^ ERROR: mismatched types + let _: u32 = x; + } + if let Some(Some(&mut x)) = &mut Some(&mut Some(0)) { + //~^ ERROR: mismatched types + let _: u32 = x; + } + if let Some(Some(&x)) = &Some(&Some(0)) { + //~^ ERROR: mismatched types + let _: u32 = x; + } + if let Some(&mut Some(&x)) = &Some(&mut Some(0)) { + //~^ ERROR: mismatched types + let _: u32 = x; + } + if let Some(&Some(&mut x)) = &mut Some(&Some(0)) { + //~^ ERROR: mismatched types + let _: u32 = x; + } +} diff --git a/tests/ui/match/ref_pat_eat_one_layer_2024/feature-gate-ref_pat_eat_one_layer_2024.stderr b/tests/ui/match/ref_pat_eat_one_layer_2024/feature-gate-ref_pat_eat_one_layer_2024.stderr new file mode 100644 index 0000000000000..132fe421a18d8 --- /dev/null +++ b/tests/ui/match/ref_pat_eat_one_layer_2024/feature-gate-ref_pat_eat_one_layer_2024.stderr @@ -0,0 +1,128 @@ +error[E0308]: mismatched types + --> $DIR/feature-gate-ref_pat_eat_one_layer_2024.rs:5: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-ref_pat_eat_one_layer_2024.rs:10:23 + | +LL | let _: &u32 = x; + | ---- ^ expected `&u32`, found integer + | | + | expected due to this + | +help: consider borrowing here + | +LL | let _: &u32 = &x; + | + + +error[E0308]: mismatched types + --> $DIR/feature-gate-ref_pat_eat_one_layer_2024.rs:13:23 + | +LL | if let Some(Some(&&x)) = &Some(Some(&0)) { + | ^^ --------------- this expression has type `&Option>` + | | + | expected integer, found `&_` + | + = note: expected type `{integer}` + found reference `&_` +help: consider removing `&` from the pattern + | +LL - if let Some(Some(&&x)) = &Some(Some(&0)) { +LL + if let Some(Some(&x)) = &Some(Some(&0)) { + | + +error[E0308]: mismatched types + --> $DIR/feature-gate-ref_pat_eat_one_layer_2024.rs:17: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-ref_pat_eat_one_layer_2024.rs:21: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-ref_pat_eat_one_layer_2024.rs:21: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[E0308]: mismatched types + --> $DIR/feature-gate-ref_pat_eat_one_layer_2024.rs:25: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-ref_pat_eat_one_layer_2024.rs:29:27 + | +LL | if let Some(&mut Some(&x)) = &Some(&mut Some(0)) { + | ^^ ------------------- this expression has type `&Option<&mut Option<{integer}>>` + | | + | expected integer, found `&_` + | + = note: expected type `{integer}` + found reference `&_` +help: consider removing `&` from the pattern + | +LL | if let Some(&mut Some(x)) = &Some(&mut Some(0)) { + | ~ + +error[E0308]: mismatched types + --> $DIR/feature-gate-ref_pat_eat_one_layer_2024.rs:33:23 + | +LL | if let Some(&Some(&mut x)) = &mut Some(&Some(0)) { + | ^^^^^^ ------------------- this expression has type `&mut Option<&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-ref_pat_eat_one_layer_2024.rs:33:23 + | +LL | if let Some(&Some(&mut x)) = &mut Some(&Some(0)) { + | ^^^^^^ +help: consider removing `&mut` from the pattern + | +LL | if let Some(&Some(x)) = &mut Some(&Some(0)) { + | ~ + +error: aborting due to 8 previous errors + +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_2021.rs b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2021.rs new file mode 100644 index 0000000000000..d28567f2859a3 --- /dev/null +++ b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2021.rs @@ -0,0 +1,37 @@ +//@ edition: 2021 +#![allow(incomplete_features)] +#![feature(ref_pat_eat_one_layer_2024)] +pub fn main() { + if let Some(Some(&x)) = &Some(&Some(0)) { + //~^ ERROR: mismatched types + let _: u32 = x; + } + if let Some(Some(&x)) = &Some(Some(&0)) { + let _: &u32 = x; + //~^ ERROR: mismatched types + } + if let Some(Some(&&x)) = &Some(Some(&0)) { + //~^ ERROR: mismatched types + let _: u32 = x; + } + if let Some(&Some(x)) = &Some(Some(0)) { + //~^ ERROR: mismatched types + let _: u32 = x; + } + if let Some(Some(&mut x)) = &mut Some(&mut Some(0)) { + //~^ ERROR: mismatched types + let _: u32 = x; + } + if let Some(Some(&x)) = &Some(&Some(0)) { + //~^ ERROR: mismatched types + let _: u32 = x; + } + if let Some(&mut Some(&x)) = &Some(&mut Some(0)) { + //~^ ERROR: mismatched types + let _: u32 = x; + } + if let Some(&Some(&mut x)) = &mut Some(&Some(0)) { + //~^ ERROR: mismatched types + let _: u32 = x; + } +} diff --git a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2021.stderr b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2021.stderr new file mode 100644 index 0000000000000..28706f89c066b --- /dev/null +++ b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2021.stderr @@ -0,0 +1,128 @@ +error[E0308]: mismatched types + --> $DIR/ref_pat_eat_one_layer_2021.rs:5: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/ref_pat_eat_one_layer_2021.rs:10:23 + | +LL | let _: &u32 = x; + | ---- ^ expected `&u32`, found integer + | | + | expected due to this + | +help: consider borrowing here + | +LL | let _: &u32 = &x; + | + + +error[E0308]: mismatched types + --> $DIR/ref_pat_eat_one_layer_2021.rs:13:23 + | +LL | if let Some(Some(&&x)) = &Some(Some(&0)) { + | ^^ --------------- this expression has type `&Option>` + | | + | expected integer, found `&_` + | + = note: expected type `{integer}` + found reference `&_` +help: consider removing `&` from the pattern + | +LL - if let Some(Some(&&x)) = &Some(Some(&0)) { +LL + if let Some(Some(&x)) = &Some(Some(&0)) { + | + +error[E0308]: mismatched types + --> $DIR/ref_pat_eat_one_layer_2021.rs:17: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/ref_pat_eat_one_layer_2021.rs:21: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/ref_pat_eat_one_layer_2021.rs:21: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[E0308]: mismatched types + --> $DIR/ref_pat_eat_one_layer_2021.rs:25: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/ref_pat_eat_one_layer_2021.rs:29:27 + | +LL | if let Some(&mut Some(&x)) = &Some(&mut Some(0)) { + | ^^ ------------------- this expression has type `&Option<&mut Option<{integer}>>` + | | + | expected integer, found `&_` + | + = note: expected type `{integer}` + found reference `&_` +help: consider removing `&` from the pattern + | +LL | if let Some(&mut Some(x)) = &Some(&mut Some(0)) { + | ~ + +error[E0308]: mismatched types + --> $DIR/ref_pat_eat_one_layer_2021.rs:33:23 + | +LL | if let Some(&Some(&mut x)) = &mut Some(&Some(0)) { + | ^^^^^^ ------------------- this expression has type `&mut Option<&Option<{integer}>>` + | | + | expected integer, found `&mut _` + | + = note: expected type `{integer}` + found mutable reference `&mut _` +note: to declare a mutable binding use: `mut x` + --> $DIR/ref_pat_eat_one_layer_2021.rs:33:23 + | +LL | if let Some(&Some(&mut x)) = &mut Some(&Some(0)) { + | ^^^^^^ +help: consider removing `&mut` from the pattern + | +LL | if let Some(&Some(x)) = &mut Some(&Some(0)) { + | ~ + +error: aborting due to 8 previous errors + +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.rs b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024.rs new file mode 100644 index 0000000000000..f83784273836f --- /dev/null +++ b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024.rs @@ -0,0 +1,32 @@ +//@ run-pass +//@ edition: 2024 +//@ compile-flags: -Zunstable-options +#![allow(incomplete_features)] +#![feature(ref_pat_eat_one_layer_2024)] + +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(&&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; + } + if let Some(Some(&x)) = &Some(&Some(0)) { + let _: u32 = x; + } + if let Some(&mut Some(&x)) = &Some(&mut Some(0)) { + let _: u32 = x; + } + if let Some(&Some(&mut x)) = &mut Some(& Some(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 new file mode 100644 index 0000000000000..566196c1a323e --- /dev/null +++ b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.rs @@ -0,0 +1,13 @@ +//@ edition: 2024 +//@ compile-flags: -Zunstable-options +#![allow(incomplete_features)] +#![feature(ref_pat_eat_one_layer_2024)] + +pub fn main() { + if let Some(&mut Some(&_)) = &Some(&Some(0)) { + //~^ ERROR: mismatched types + } + if let Some(&Some(&_)) = &Some(&mut Some(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 new file mode 100644 index 0000000000000..96a107b5c508d --- /dev/null +++ b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.stderr @@ -0,0 +1,25 @@ +error[E0308]: mismatched types + --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:7:17 + | +LL | if let Some(&mut Some(&_)) = &Some(&Some(0)) { + | ^^^^^^^^^^^^^ --------------- this expression has type `&Option<&Option<{integer}>>` + | | + | types differ in mutability + | + = note: expected reference `&Option<{integer}>` + found mutable reference `&mut _` + +error[E0308]: mismatched types + --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:10:23 + | +LL | if let Some(&Some(&_)) = &Some(&mut Some(0)) { + | ^^ ------------------- this expression has type `&Option<&mut Option<{integer}>>` + | | + | expected integer, found `&_` + | + = note: expected type `{integer}` + found reference `&_` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. From 4cd87c463c2a58379b2efd45cdc92027f2965611 Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Fri, 5 Apr 2024 21:18:05 -0400 Subject: [PATCH 2/6] Properly downgrade inherited mutability --- compiler/rustc_hir_typeck/src/pat.rs | 6 +++- .../ref_pat_eat_one_layer_2024.rs | 11 +++++++- .../ref_pat_eat_one_layer_2024_fail.rs | 7 +++++ .../ref_pat_eat_one_layer_2024_fail.stderr | 28 ++++++++++++++++++- .../ref_pat_eat_one_layer_2024_fail2.rs | 11 ++++++++ .../ref_pat_eat_one_layer_2024_fail2.stderr | 17 +++++++++++ 6 files changed, 77 insertions(+), 3 deletions(-) create mode 100644 tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail2.rs create mode 100644 tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail2.stderr diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index ff7716ce7eae0..bec177a2845a6 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -299,7 +299,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if mutbls_match { (expected, INITIAL_BM, true) } else { - (expected, def_bm, false) + let mut new_bm = def_bm; + if new_bm.0 == ByRef::Yes(Mutability::Mut) && mutbl == Mutability::Not { + new_bm.0 = ByRef::Yes(Mutability::Not); + } + (expected, new_bm, false) } } else { (expected, INITIAL_BM, mutbls_match) 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 f83784273836f..3cf6008609ded 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 @@ -26,7 +26,16 @@ pub fn main() { if let Some(&mut Some(&x)) = &Some(&mut Some(0)) { let _: u32 = x; } - if let Some(&Some(&mut x)) = &mut Some(& Some(0)) { + if let Some(&Some(&x)) = &mut Some(&Some(0)) { let _: u32 = x; } + if let Some(&Some(x)) = &mut Some(&Some(0)) { + let _: &u32 = x; + } + if let Some(&Some(&mut ref x)) = Some(&Some(&mut 0)) { + let _: &u32 = x; + } + if let Some(Some(&mut x)) = &Some(Some(&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 566196c1a323e..f8385d2e0494d 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 @@ -10,4 +10,11 @@ pub fn main() { if let Some(&Some(&_)) = &Some(&mut Some(0)) { //~^ ERROR: mismatched types } + if let Some(&Some(x)) = &mut Some(&Some(0)) { + let _: &mut u32 = x; + //~^ ERROR: mismatched types + } + if let Some(&Some(&x)) = Some(&Some(&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 96a107b5c508d..8b714cfcc3ecd 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 @@ -20,6 +20,32 @@ LL | if let Some(&Some(&_)) = &Some(&mut Some(0)) { = note: expected type `{integer}` found reference `&_` -error: aborting due to 2 previous errors +error[E0308]: mismatched types + --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:14:27 + | +LL | let _: &mut u32 = x; + | -------- ^ types differ in mutability + | | + | expected due to this + | + = note: expected mutable reference `&mut u32` + found reference `&{integer}` + +error[E0308]: mismatched types + --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:17:23 + | +LL | if let Some(&Some(&x)) = Some(&Some(&mut 0)) { + | ^^ ------------------- this expression has type `Option<&Option<&mut {integer}>>` + | | + | types differ in mutability + | + = note: expected mutable reference `&mut {integer}` + found reference `&_` +help: consider removing `&` from the pattern + | +LL | if let Some(&Some(x)) = Some(&Some(&mut 0)) { + | ~ + +error: aborting due to 4 previous errors 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_fail2.rs b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail2.rs new file mode 100644 index 0000000000000..364554884073a --- /dev/null +++ b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail2.rs @@ -0,0 +1,11 @@ +//@ edition: 2024 +//@ compile-flags: -Zunstable-options +#![allow(incomplete_features)] +#![feature(ref_pat_eat_one_layer_2024)] + +pub fn main() { + if let Some(&Some(x)) = Some(&Some(&mut 0)) { + //~^ ERROR: cannot move out of a shared reference [E0507] + let _: &u32 = x; + } +} 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 new file mode 100644 index 0000000000000..ccfb5c7a0c076 --- /dev/null +++ b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail2.stderr @@ -0,0 +1,17 @@ +error[E0507]: cannot move out of a shared reference + --> $DIR/ref_pat_eat_one_layer_2024_fail2.rs:7:29 + | +LL | if let Some(&Some(x)) = Some(&Some(&mut 0)) { + | - ^^^^^^^^^^^^^^^^^^^ + | | + | data moved here + | move occurs because `x` has type `&mut u32`, which does not implement the `Copy` trait + | +help: consider borrowing the pattern binding + | +LL | if let Some(&Some(ref x)) = Some(&Some(&mut 0)) { + | +++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0507`. From b6c409723b6438a10db53f12ae6e79f530df8a1b Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Fri, 5 Apr 2024 22:07:57 -0400 Subject: [PATCH 3/6] Support `let &mut x = &&mut 0;` --- compiler/rustc_hir_typeck/src/pat.rs | 23 +++++++++----- .../ref_pat_eat_one_layer_2024.rs | 9 ++++++ .../ref_pat_eat_one_layer_2024_fail.rs | 8 +++++ .../ref_pat_eat_one_layer_2024_fail.stderr | 30 +++++++++++++++++-- 4 files changed, 60 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index bec177a2845a6..5930cc0698abf 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -299,18 +299,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if mutbls_match { (expected, INITIAL_BM, true) } else { - let mut new_bm = def_bm; - if new_bm.0 == ByRef::Yes(Mutability::Mut) && mutbl == Mutability::Not { - new_bm.0 = ByRef::Yes(Mutability::Not); - } - (expected, new_bm, false) + let (new_ty, new_bm) = if mutbl == Mutability::Mut { + self.peel_off_references(pat, expected, def_bm, Mutability::Not) + } else { + let new_byref = if def_bm.0 == ByRef::Yes(Mutability::Mut) { + ByRef::Yes(Mutability::Not) + } else { + def_bm.0 + }; + (expected, BindingAnnotation(new_byref, def_bm.1)) + }; + (new_ty, new_bm, false) } } else { (expected, INITIAL_BM, mutbls_match) } } AdjustMode::Peel => { - let peeled = self.peel_off_references(pat, expected, def_bm); + let peeled = self.peel_off_references(pat, expected, def_bm, Mutability::Mut); (peeled.0, peeled.1, false) } } @@ -392,6 +398,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pat: &'tcx Pat<'tcx>, expected: Ty<'tcx>, mut def_bm: BindingAnnotation, + max_mutability: Mutability, ) -> (Ty<'tcx>, BindingAnnotation) { 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, @@ -403,7 +410,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // // See the examples in `ui/match-defbm*.rs`. let mut pat_adjustments = vec![]; - while let ty::Ref(_, inner_ty, inner_mutability) = *expected.kind() { + while let ty::Ref(_, inner_ty, inner_mutability) = *expected.kind() + && inner_mutability <= max_mutability + { debug!("inspecting {:?}", expected); debug!("current discriminant is Ref, inserting implicit deref"); 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 3cf6008609ded..2c426f469d84a 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 @@ -38,4 +38,13 @@ pub fn main() { if let Some(Some(&mut x)) = &Some(Some(&mut 0)) { let _: &u32 = x; } + + let &mut x = &&mut 0; + let _: &u32 = x; + + let &mut x = &&&&&&&&&&&&&&&&&&&&&&&&&&&&mut 0; + let _: &u32 = x; + + let &mut &mut &mut &mut x = &mut &&&&mut &&&mut &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 f8385d2e0494d..9e14a524e2b71 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 @@ -17,4 +17,12 @@ pub fn main() { if let Some(&Some(&x)) = Some(&Some(&mut 0)) { //~^ ERROR: mismatched types } + + let &mut x = &&0; + //~^ ERROR: mismatched types + let _: &u32 = x; + + let &mut x = &&&&&&&&&&&&&&&&&&&&&&&&&&&&0; + //~^ ERROR: mismatched types + let _: &u32 = x; } 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 8b714cfcc3ecd..074c5d298a553 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 @@ -4,9 +4,9 @@ error[E0308]: mismatched types LL | if let Some(&mut Some(&_)) = &Some(&Some(0)) { | ^^^^^^^^^^^^^ --------------- this expression has type `&Option<&Option<{integer}>>` | | - | types differ in mutability + | expected `Option<{integer}>`, found `&mut _` | - = note: expected reference `&Option<{integer}>` + = note: expected enum `Option<{integer}>` found mutable reference `&mut _` error[E0308]: mismatched types @@ -46,6 +46,30 @@ help: consider removing `&` from the pattern LL | if let Some(&Some(x)) = Some(&Some(&mut 0)) { | ~ -error: aborting due to 4 previous errors +error[E0308]: mismatched types + --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:21:9 + | +LL | let &mut x = &&0; + | ^^^^^^ --- this expression has type `&&{integer}` + | | + | expected integer, found `&mut _` + | help: to declare a mutable variable use: `mut x` + | + = note: expected type `{integer}` + found mutable reference `&mut _` + +error[E0308]: mismatched types + --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:25:9 + | +LL | let &mut x = &&&&&&&&&&&&&&&&&&&&&&&&&&&&0; + | ^^^^^^ ----------------------------- this expression has type `&&&&&&&&&&&&&&&&&&&&&&&&&&&&{integer}` + | | + | expected integer, found `&mut _` + | help: to declare a mutable variable use: `mut x` + | + = note: expected type `{integer}` + found mutable reference `&mut _` + +error: aborting due to 6 previous errors For more information about this error, try `rustc --explain E0308`. From e3945bd3a8236ea6e1f6e1e04842c56a3c689cc5 Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Sat, 6 Apr 2024 17:07:09 -0400 Subject: [PATCH 4/6] Ensure inherited reference is never set to `&mut` behind an `&` --- compiler/rustc_ast/src/ast.rs | 8 ++ compiler/rustc_hir_typeck/src/pat.rs | 91 +++++++++++-------- .../rustc_mir_build/src/thir/pattern/mod.rs | 2 +- .../ref_pat_eat_one_layer_2024.rs | 15 +++ .../ref_pat_eat_one_layer_2024_fail.rs | 17 ++-- .../ref_pat_eat_one_layer_2024_fail.stderr | 50 ++++++---- 6 files changed, 120 insertions(+), 63 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 5b708cf4e1a55..fc7f64c739268 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -36,6 +36,7 @@ use rustc_macros::HashStable_Generic; use rustc_span::source_map::{respan, Spanned}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{ErrorGuaranteed, Span, DUMMY_SP}; +use std::cmp; use std::fmt; use std::mem; use thin_vec::{thin_vec, ThinVec}; @@ -731,6 +732,13 @@ impl BindingAnnotation { Self::MUT_REF_MUT => "mut ref mut ", } } + + pub fn cap_ref_mutability(mut self, mutbl: Mutability) -> Self { + if let ByRef::Yes(old_mutbl) = self.0 { + self.0 = ByRef::Yes(cmp::min(old_mutbl, mutbl)); + } + self + } } #[derive(Clone, Encodable, Decodable, Debug)] diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 5930cc0698abf..801c735ade38a 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -79,6 +79,7 @@ struct TopInfo<'tcx> { #[derive(Copy, Clone)] struct PatInfo<'tcx, 'a> { binding_mode: BindingAnnotation, + max_ref_mutbl: Mutability, top_info: TopInfo<'tcx>, decl_origin: Option>, @@ -160,8 +161,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { decl_origin: Option>, ) { let info = TopInfo { expected, origin_expr, span }; - let pat_info = - PatInfo { binding_mode: INITIAL_BM, top_info: info, decl_origin, current_depth: 0 }; + let pat_info = PatInfo { + binding_mode: INITIAL_BM, + max_ref_mutbl: Mutability::Mut, + top_info: info, + decl_origin, + current_depth: 0, + }; self.check_pat(pat, expected, pat_info); } @@ -172,7 +178,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// 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, top_info: ti, current_depth, .. } = pat_info; + let PatInfo { binding_mode: def_bm, max_ref_mutbl, top_info: ti, current_depth, .. } = + pat_info; let path_res = match &pat.kind { PatKind::Path(qpath) => Some( @@ -181,10 +188,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => None, }; let adjust_mode = self.calc_adjust_mode(pat, path_res.map(|(res, ..)| res)); - let (expected, def_bm, ref_pattern_already_consumed) = - self.calc_default_binding_mode(pat, expected, def_bm, adjust_mode); + 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 pat_info = PatInfo { binding_mode: def_bm, + max_ref_mutbl, top_info: ti, decl_origin: pat_info.decl_origin, current_depth: current_depth + 1, @@ -289,35 +297,43 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected: Ty<'tcx>, def_bm: BindingAnnotation, adjust_mode: AdjustMode, - ) -> (Ty<'tcx>, BindingAnnotation, bool) { + max_ref_mutbl: Mutability, + ) -> (Ty<'tcx>, BindingAnnotation, Mutability, bool) { + if let ByRef::Yes(mutbl) = def_bm.0 { + debug_assert!(mutbl <= max_ref_mutbl); + } match adjust_mode { - AdjustMode::Pass => (expected, def_bm, false), - AdjustMode::Reset => (expected, INITIAL_BM, false), - AdjustMode::ResetAndConsumeRef(mutbl) => { - let mutbls_match = def_bm.0 == ByRef::Yes(mutbl); + AdjustMode::Pass => (expected, def_bm, max_ref_mutbl, false), + AdjustMode::Reset => (expected, INITIAL_BM, Mutability::Mut, false), + AdjustMode::ResetAndConsumeRef(ref_pat_mutbl) => { + let mutbls_match = def_bm.0 == ByRef::Yes(ref_pat_mutbl); if pat.span.at_least_rust_2024() && self.tcx.features().ref_pat_eat_one_layer_2024 { + let max_ref_mutbl = cmp::min(max_ref_mutbl, ref_pat_mutbl); if mutbls_match { - (expected, INITIAL_BM, true) + debug!("consuming inherited reference"); + (expected, INITIAL_BM, max_ref_mutbl, true) } else { - let (new_ty, new_bm) = if mutbl == Mutability::Mut { - self.peel_off_references(pat, expected, def_bm, Mutability::Not) + let (new_ty, new_bm, max_ref_mutbl) = if ref_pat_mutbl == Mutability::Mut { + self.peel_off_references( + pat, + expected, + def_bm, + Mutability::Not, + max_ref_mutbl, + ) } else { - let new_byref = if def_bm.0 == ByRef::Yes(Mutability::Mut) { - ByRef::Yes(Mutability::Not) - } else { - def_bm.0 - }; - (expected, BindingAnnotation(new_byref, def_bm.1)) + (expected, def_bm.cap_ref_mutability(Mutability::Not), Mutability::Not) }; - (new_ty, new_bm, false) + (new_ty, new_bm, max_ref_mutbl, false) } } else { - (expected, INITIAL_BM, mutbls_match) + (expected, INITIAL_BM, max_ref_mutbl, mutbls_match) } } AdjustMode::Peel => { - let peeled = self.peel_off_references(pat, expected, def_bm, Mutability::Mut); - (peeled.0, peeled.1, false) + let peeled = + self.peel_off_references(pat, expected, def_bm, Mutability::Mut, max_ref_mutbl); + (peeled.0, peeled.1, peeled.2, false) } } } @@ -398,8 +414,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pat: &'tcx Pat<'tcx>, expected: Ty<'tcx>, mut def_bm: BindingAnnotation, - max_mutability: Mutability, - ) -> (Ty<'tcx>, BindingAnnotation) { + max_peelable_mutability: Mutability, + mut max_ref_mutability: Mutability, + ) -> (Ty<'tcx>, BindingAnnotation, Mutability) { 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 @@ -411,7 +428,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // See the examples in `ui/match-defbm*.rs`. let mut pat_adjustments = vec![]; while let ty::Ref(_, inner_ty, inner_mutability) = *expected.kind() - && inner_mutability <= max_mutability + && inner_mutability <= max_peelable_mutability { debug!("inspecting {:?}", expected); @@ -430,6 +447,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // This is because a `& &mut` cannot mutate the underlying value. ByRef::Yes(Mutability::Not) => Mutability::Not, }); + + if pat.span.at_least_rust_2024() && self.tcx.features().ref_pat_eat_one_layer_2024 { + max_ref_mutability = cmp::min(max_ref_mutability, inner_mutability); + def_bm = def_bm.cap_ref_mutability(max_ref_mutability); + } } if !pat_adjustments.is_empty() { @@ -440,7 +462,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .insert(pat.hir_id, pat_adjustments); } - (expected, def_bm) + (expected, def_bm, max_ref_mutability) } fn check_pat_lit( @@ -1116,15 +1138,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected: Ty<'tcx>, pat_info: PatInfo<'tcx, '_>, ) -> Ty<'tcx> { - let PatInfo { binding_mode: def_bm, top_info: ti, decl_origin, current_depth } = pat_info; let tcx = self.tcx; let on_error = |e| { for pat in subpats { - self.check_pat( - pat, - Ty::new_error(tcx, e), - PatInfo { binding_mode: def_bm, top_info: ti, decl_origin, current_depth }, - ); + self.check_pat(pat, Ty::new_error(tcx, e), pat_info); } }; let report_unexpected_res = |res: Res| { @@ -1169,7 +1186,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let pat_ty = pat_ty.no_bound_vars().expect("expected fn type"); // Type-check the tuple struct pattern against the expected type. - let diag = self.demand_eqtype_pat_diag(pat.span, expected, pat_ty, ti); + let diag = self.demand_eqtype_pat_diag(pat.span, expected, pat_ty, pat_info.top_info); let had_err = if let Some(err) = diag { err.emit(); true @@ -1187,11 +1204,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { for (i, subpat) in subpats.iter().enumerate_and_adjust(variant.fields.len(), ddpos) { let field = &variant.fields[FieldIdx::from_usize(i)]; let field_ty = self.field_ty(subpat.span, field, args); - self.check_pat( - subpat, - field_ty, - PatInfo { binding_mode: def_bm, top_info: ti, decl_origin, current_depth }, - ); + self.check_pat(subpat, field_ty, pat_info); self.tcx.check_stability( variant.fields[FieldIdx::from_usize(i)].did, diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index 133cf8e334929..bcb43a0054709 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -69,7 +69,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { hir::PatKind::Ref(inner, _) if self.typeck_results.skipped_ref_pats().contains(pat.hir_id) => { - self.lower_pattern_unadjusted(inner) + self.lower_pattern(inner) } _ => self.lower_pattern_unadjusted(pat), }; 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 2c426f469d84a..f1ac3e340e915 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 @@ -38,6 +38,21 @@ pub fn main() { if let Some(Some(&mut x)) = &Some(Some(&mut 0)) { let _: &u32 = x; } + if let &Some(Some(x)) = &Some(&mut Some(0)) { + let _: &u32 = x; + } + if let Some(&Some(&x)) = &Some(&mut Some(0)) { + let _: u32 = x; + } + if let Some(&Some(&x)) = &Some(&Some(0)) { + let _: u32 = x; + } + if let Some(&Some(&x)) = &Some(&mut Some(0)) { + let _: u32 = x; + } + if let Some(&Some(Some(&x))) = &Some(Some(&mut 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 9e14a524e2b71..ec091bb17467d 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 @@ -7,22 +7,27 @@ pub fn main() { if let Some(&mut Some(&_)) = &Some(&Some(0)) { //~^ ERROR: mismatched types } - if let Some(&Some(&_)) = &Some(&mut Some(0)) { + if let Some(&Some(&mut _)) = &Some(&mut Some(0)) { //~^ ERROR: mismatched types } if let Some(&Some(x)) = &mut Some(&Some(0)) { let _: &mut u32 = x; //~^ ERROR: mismatched types } - if let Some(&Some(&x)) = Some(&Some(&mut 0)) { + 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 + } + - let &mut x = &&0; + let &mut _= &&0; //~^ ERROR: mismatched types - let _: &u32 = x; - let &mut x = &&&&&&&&&&&&&&&&&&&&&&&&&&&&0; + let &mut _ = &&&&&&&&&&&&&&&&&&&&&&&&&&&&0; //~^ ERROR: mismatched types - let _: &u32 = x; } 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 074c5d298a553..be71ee606c767 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 @@ -12,13 +12,13 @@ LL | if let Some(&mut Some(&_)) = &Some(&Some(0)) { error[E0308]: mismatched types --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:10:23 | -LL | if let Some(&Some(&_)) = &Some(&mut Some(0)) { - | ^^ ------------------- this expression has type `&Option<&mut Option<{integer}>>` +LL | if let Some(&Some(&mut _)) = &Some(&mut Some(0)) { + | ^^^^^^ ------------------- this expression has type `&Option<&mut Option<{integer}>>` | | - | expected integer, found `&_` + | expected integer, found `&mut _` | - = note: expected type `{integer}` - found reference `&_` + = note: expected type `{integer}` + found mutable reference `&mut _` error[E0308]: mismatched types --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:14:27 @@ -34,42 +34,58 @@ 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(&x)) = Some(&Some(&mut 0)) { +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 `&_` -help: consider removing `&` from the pattern + +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}>>` + | | + | expected integer, found `&mut _` + | + = note: expected type `{integer}` + found mutable reference `&mut _` + +error[E0308]: mismatched types + --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:23:29 | -LL | if let Some(&Some(x)) = Some(&Some(&mut 0)) { - | ~ +LL | if let Some(&Some(Some((&mut _)))) = &Some(Some(&mut Some(0))) { + | ^^^^^^ ------------------------- this expression has type `&Option>>` + | | + | expected integer, found `&mut _` + | + = note: expected type `{integer}` + found mutable reference `&mut _` error[E0308]: mismatched types - --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:21:9 + --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:28:9 | -LL | let &mut x = &&0; - | ^^^^^^ --- this expression has type `&&{integer}` +LL | let &mut _= &&0; + | ^^^^^^ --- this expression has type `&&{integer}` | | | expected integer, found `&mut _` - | help: to declare a mutable variable use: `mut x` | = note: expected type `{integer}` found mutable reference `&mut _` error[E0308]: mismatched types - --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:25:9 + --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:31:9 | -LL | let &mut x = &&&&&&&&&&&&&&&&&&&&&&&&&&&&0; +LL | let &mut _ = &&&&&&&&&&&&&&&&&&&&&&&&&&&&0; | ^^^^^^ ----------------------------- this expression has type `&&&&&&&&&&&&&&&&&&&&&&&&&&&&{integer}` | | | expected integer, found `&mut _` - | help: to declare a mutable variable use: `mut x` | = note: expected type `{integer}` found mutable reference `&mut _` -error: aborting due to 6 previous errors +error: aborting due to 8 previous errors For more information about this error, try `rustc --explain E0308`. From 88cd821e6275ed18e493fd22201ee313154ea6a7 Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Sun, 14 Apr 2024 10:52:37 -0400 Subject: [PATCH 5/6] Address review comments --- compiler/rustc_ast/src/ast.rs | 4 ++-- compiler/rustc_hir_typeck/src/pat.rs | 8 +++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index fc7f64c739268..334ce5e060b8d 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -734,8 +734,8 @@ impl BindingAnnotation { } pub fn cap_ref_mutability(mut self, mutbl: Mutability) -> Self { - if let ByRef::Yes(old_mutbl) = self.0 { - self.0 = ByRef::Yes(cmp::min(old_mutbl, mutbl)); + if let ByRef::Yes(old_mutbl) = &mut self.0 { + *old_mutbl = cmp::min(*old_mutbl, mutbl); } self } diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 801c735ade38a..28681365af547 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -447,10 +447,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // This is because a `& &mut` cannot mutate the underlying value. ByRef::Yes(Mutability::Not) => Mutability::Not, }); + } - if pat.span.at_least_rust_2024() && self.tcx.features().ref_pat_eat_one_layer_2024 { - max_ref_mutability = cmp::min(max_ref_mutability, inner_mutability); - def_bm = def_bm.cap_ref_mutability(max_ref_mutability); + if pat.span.at_least_rust_2024() && self.tcx.features().ref_pat_eat_one_layer_2024 { + def_bm = def_bm.cap_ref_mutability(max_ref_mutability); + if def_bm.0 == ByRef::Yes(Mutability::Not) { + max_ref_mutability = Mutability::Not; } } From 3efbe3e70ceb9825bcced29422e5f4a695bb254b Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Sun, 14 Apr 2024 11:01:00 -0400 Subject: [PATCH 6/6] Simplify `calc_default_binding_mode` --- compiler/rustc_hir_typeck/src/pat.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 28681365af547..d66d327c49a4c 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -308,10 +308,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { AdjustMode::ResetAndConsumeRef(ref_pat_mutbl) => { let mutbls_match = def_bm.0 == ByRef::Yes(ref_pat_mutbl); if pat.span.at_least_rust_2024() && self.tcx.features().ref_pat_eat_one_layer_2024 { - let max_ref_mutbl = cmp::min(max_ref_mutbl, ref_pat_mutbl); if mutbls_match { debug!("consuming inherited reference"); - (expected, INITIAL_BM, max_ref_mutbl, true) + (expected, INITIAL_BM, cmp::min(max_ref_mutbl, ref_pat_mutbl), true) } else { let (new_ty, new_bm, max_ref_mutbl) = if ref_pat_mutbl == Mutability::Mut { self.peel_off_references(