diff --git a/compiler/rustc_feature/src/removed.rs b/compiler/rustc_feature/src/removed.rs
index eaaf7ca34e012..aea447b2aff10 100644
--- a/compiler/rustc_feature/src/removed.rs
+++ b/compiler/rustc_feature/src/removed.rs
@@ -128,6 +128,8 @@ declare_features! (
/// Allows the use of type alias impl trait in function return positions
(removed, min_type_alias_impl_trait, "1.56.0", Some(63063),
Some("removed in favor of full type_alias_impl_trait")),
+ /// Make `mut` not reset the binding mode on edition >= 2024.
+ (removed, mut_preserve_binding_mode_2024, "1.79.0", Some(123076), Some("superseded by `ref_pat_eat_one_layer_2024`")),
(removed, needs_allocator, "1.4.0", Some(27389),
Some("subsumed by `#![feature(allocator_internals)]`")),
/// Allows use of unary negate on unsigned integers, e.g., -e for e: u8
@@ -181,6 +183,7 @@ declare_features! (
(removed, pushpop_unsafe, "1.2.0", None, None),
(removed, quad_precision_float, "1.0.0", None, None),
(removed, quote, "1.33.0", Some(29601), None),
+ (removed, ref_pat_everywhere, "1.79.0", Some(123076), Some("superseded by `ref_pat_eat_one_layer_2024")),
(removed, reflect, "1.0.0", Some(27749), None),
/// Allows using the `#[register_attr]` attribute.
(removed, register_attr, "1.65.0", Some(66080),
diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs
index 60b386acf9106..a4ccb0f1412e9 100644
--- a/compiler/rustc_feature/src/unstable.rs
+++ b/compiler/rustc_feature/src/unstable.rs
@@ -527,8 +527,6 @@ declare_features! (
(unstable, more_qualified_paths, "1.54.0", Some(86935)),
/// Allows the `#[must_not_suspend]` attribute.
(unstable, must_not_suspend, "1.57.0", Some(83310)),
- /// Make `mut` not reset the binding mode on edition >= 2024.
- (incomplete, mut_preserve_binding_mode_2024, "1.79.0", Some(123076)),
/// Allows `mut ref` and `mut ref mut` identifier patterns.
(incomplete, mut_ref, "1.79.0", Some(123076)),
/// Allows using `#[naked]` on functions.
@@ -571,8 +569,6 @@ declare_features! (
(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, "1.79.0", Some(123076)),
- /// Allows `&` and `&mut` patterns to consume match-ergonomics-inserted references.
- (incomplete, ref_pat_everywhere, "1.79.0", Some(123076)),
/// Allows using the `#[register_tool]` attribute.
(unstable, register_tool, "1.41.0", Some(66079)),
/// Allows the `#[repr(i128)]` attribute for enums.
diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs
index b9b220d5af8e4..617c08181fb0d 100644
--- a/compiler/rustc_hir_typeck/src/pat.rs
+++ b/compiler/rustc_hir_typeck/src/pat.rs
@@ -12,7 +12,7 @@ use rustc_infer::infer;
use rustc_middle::mir::interpret::ErrorHandled;
use rustc_middle::ty::{self, Ty, TypeVisitableExt};
use rustc_middle::{bug, span_bug};
-use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS;
+use rustc_session::{lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS, parse::feature_err};
use rustc_span::edit_distance::find_best_match_for_name;
use rustc_span::hygiene::DesugaringKind;
use rustc_span::source_map::Spanned;
@@ -335,9 +335,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
match adjust_mode {
AdjustMode::Pass => (expected, def_br, max_ref_mutbl),
AdjustMode::Reset => (expected, ByRef::No, MutblCap::Mut),
- AdjustMode::Peel => {
- self.peel_off_references(pat, expected, def_br, Mutability::Mut, max_ref_mutbl)
- }
+ AdjustMode::Peel => self.peel_off_references(pat, expected, def_br, max_ref_mutbl),
}
}
@@ -408,8 +406,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
pat: &'tcx Pat<'tcx>,
expected: Ty<'tcx>,
mut def_br: ByRef,
- max_peelable_mutability: Mutability,
- mut max_ref_mutability: MutblCap,
+ mut max_ref_mutbl: 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,
@@ -421,9 +418,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_peelable_mutability
- {
+ while let ty::Ref(_, inner_ty, inner_mutability) = *expected.kind() {
debug!("inspecting {:?}", expected);
debug!("current discriminant is Ref, inserting implicit deref");
@@ -443,10 +438,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
});
}
- 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 self.tcx.features().ref_pat_eat_one_layer_2024 {
+ def_br = def_br.cap_ref_mutability(max_ref_mutbl.as_mutbl());
if def_br == ByRef::Yes(Mutability::Not) {
- max_ref_mutability = MutblCap::Not;
+ max_ref_mutbl = MutblCap::Not;
}
}
@@ -458,7 +453,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
.insert(pat.hir_id, pat_adjustments);
}
- (expected, def_br, max_ref_mutability)
+ (expected, def_br, max_ref_mutbl)
}
fn check_pat_lit(
@@ -674,17 +669,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// Determine the binding mode...
let bm = match user_bind_annot {
- // `mut` resets binding mode on edition <= 2021
- BindingMode(ByRef::No, Mutability::Mut)
- if !(pat.span.at_least_rust_2024()
- && self.tcx.features().mut_preserve_binding_mode_2024)
- && matches!(def_br, ByRef::Yes(_)) =>
- {
- self.typeck_results
- .borrow_mut()
- .rust_2024_migration_desugared_pats_mut()
- .insert(pat_info.top_info.hir_id);
- BindingMode(ByRef::No, Mutability::Mut)
+ BindingMode(ByRef::No, Mutability::Mut) if matches!(def_br, ByRef::Yes(_)) => {
+ if pat.span.at_least_rust_2024() && self.tcx.features().ref_pat_eat_one_layer_2024 {
+ if !self.tcx.features().mut_ref {
+ feature_err(
+ &self.tcx.sess,
+ sym::mut_ref,
+ pat.span.until(ident.span),
+ "binding cannot be both mutable and by-reference",
+ )
+ .emit();
+ }
+
+ BindingMode(def_br, Mutability::Mut)
+ } else {
+ // `mut` resets binding mode on edition <= 2021
+ self.typeck_results
+ .borrow_mut()
+ .rust_2024_migration_desugared_pats_mut()
+ .insert(pat_info.top_info.hir_id);
+ BindingMode(ByRef::No, Mutability::Mut)
+ }
}
BindingMode(ByRef::No, mutbl) => BindingMode(def_br, mutbl),
BindingMode(ByRef::Yes(_), _) => user_bind_annot,
@@ -2125,57 +2130,47 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
mut expected: Ty<'tcx>,
mut pat_info: PatInfo<'tcx, '_>,
) -> Ty<'tcx> {
- // 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 no_ref_mut_behind_and = self.tcx.features().ref_pat_eat_one_layer_2024;
+ let new_match_ergonomics = pat.span.at_least_rust_2024() && no_ref_mut_behind_and;
- 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 pat_prefix_span =
+ inner.span.find_ancestor_inside(pat.span).map(|end| pat.span.until(end));
- let mut inherited_ref_mutbl_match = false;
- if match_ergonomics_mode != MatchErgonomicsMode::Legacy {
+ if no_ref_mut_behind_and {
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)),
- );
+ pat_info.max_ref_mutbl = pat_info.max_ref_mutbl.cap_to_weakly_not(pat_prefix_span);
}
+ } else {
+ pat_info.max_ref_mutbl = MutblCap::Mut;
+ }
+ if new_match_ergonomics {
if let ByRef::Yes(inh_mut) = pat_info.binding_mode {
- inherited_ref_mutbl_match = pat_mutbl <= inh_mut;
- }
+ // ref pattern consumes inherited reference
+
+ if pat_mutbl > inh_mut {
+ // Tried to match inherited `ref` with `&mut`, which is an error
+ let err_msg = "cannot match inherited `&` with `&mut` pattern";
+ let err = if let Some(span) = pat_prefix_span {
+ let mut err = self.dcx().struct_span_err(span, err_msg);
+ err.span_suggestion_verbose(
+ span,
+ "replace this `&mut` pattern with `&`",
+ "&",
+ Applicability::MachineApplicable,
+ );
+ err
+ } else {
+ self.dcx().struct_span_err(pat.span, err_msg)
+ };
+ err.emit();
+ }
- 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
- {
- // `&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;
+ self.typeck_results.borrow_mut().skipped_ref_pats_mut().insert(pat.hir_id);
+ self.check_pat(inner, expected, pat_info);
+ return expected;
}
} else {
// Reset binding mode on old editions
@@ -2188,8 +2183,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
.rust_2024_migration_desugared_pats_mut()
.insert(pat_info.top_info.hir_id);
}
-
- pat_info.max_ref_mutbl = MutblCap::Mut;
}
let tcx = self.tcx;
@@ -2204,34 +2197,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// 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
- {
+ ty::Ref(_, r_ty, r_mutbl)
+ if (new_match_ergonomics && r_mutbl >= pat_mutbl)
+ || r_mutbl == pat_mutbl =>
+ {
+ if no_ref_mut_behind_and && r_mutbl == Mutability::Not {
pat_info.max_ref_mutbl = MutblCap::Not;
}
(expected, r_ty)
}
- // `&` pattern eats `&mut` reference
- ty::Ref(_, r_ty, Mutability::Mut)
- if pat_mutbl == Mutability::Not
- && match_ergonomics_mode != MatchErgonomicsMode::Legacy =>
- {
- (expected, r_ty)
- }
-
- _ 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);
diff --git a/tests/ui/feature-gates/feature-gate-ref_pat_everywhere.rs b/tests/ui/feature-gates/feature-gate-ref_pat_everywhere.rs
deleted file mode 100644
index ed5db56e0e83d..0000000000000
--- a/tests/ui/feature-gates/feature-gate-ref_pat_everywhere.rs
+++ /dev/null
@@ -1,14 +0,0 @@
-pub fn main() {
- if let Some(Some(&x)) = &Some(&Some(0)) {
- //~^ ERROR: mismatched types [E0308]
- let _: u32 = x;
- }
- if let Some(&Some(x)) = &Some(Some(0)) {
- //~^ ERROR: mismatched types [E0308]
- let _: u32 = x;
- }
- if let Some(Some(&mut x)) = &mut Some(&mut Some(0)) {
- //~^ ERROR: mismatched types [E0308]
- let _: u32 = x;
- }
-}
diff --git a/tests/ui/feature-gates/feature-gate-ref_pat_everywhere.stderr b/tests/ui/feature-gates/feature-gate-ref_pat_everywhere.stderr
deleted file mode 100644
index 0f0051325cdf0..0000000000000
--- a/tests/ui/feature-gates/feature-gate-ref_pat_everywhere.stderr
+++ /dev/null
@@ -1,49 +0,0 @@
-error[E0308]: mismatched types
- --> $DIR/feature-gate-ref_pat_everywhere.rs:2:22
- |
-LL | if let Some(Some(&x)) = &Some(&Some(0)) {
- | ^^ --------------- this expression has type `&Option<&Option<{integer}>>`
- | |
- | 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_everywhere.rs:6:17
- |
-LL | if let Some(&Some(x)) = &Some(Some(0)) {
- | ^^^^^^^^ -------------- this expression has type `&Option