diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index e6b19817de385..eaaf4026cd759 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -533,6 +533,8 @@ 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, "CURRENT_RUSTC_VERSION", Some(123076)), /// Allows `mut ref` and `mut ref mut` identifier patterns. (incomplete, mut_ref, "CURRENT_RUSTC_VERSION", Some(123076)), /// Allows using `#[naked]` on functions. diff --git a/compiler/rustc_hir_typeck/messages.ftl b/compiler/rustc_hir_typeck/messages.ftl index 18d9d739dd696..07b4948872dd8 100644 --- a/compiler/rustc_hir_typeck/messages.ftl +++ b/compiler/rustc_hir_typeck/messages.ftl @@ -46,6 +46,10 @@ hir_typeck_ctor_is_private = tuple struct constructor `{$def}` is private hir_typeck_deref_is_empty = this expression `Deref`s to `{$deref_ty}` which implements `is_empty` +hir_typeck_dereferencing_mut_binding = dereferencing `mut` binding + .label = `mut` dereferences the type of this binding + .help = this will change in edition 2024 + hir_typeck_expected_default_return_type = expected `()` because of default return type hir_typeck_expected_return_type = expected `{$expected}` because of return type diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index d399730bf3df4..3dc9c7b86f713 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -632,3 +632,11 @@ pub enum SuggestBoxingForReturnImplTrait { ends: Vec, }, } + +#[derive(LintDiagnostic)] +#[diag(hir_typeck_dereferencing_mut_binding)] +pub struct DereferencingMutBinding { + #[label] + #[help] + pub span: Span, +} diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index db4bd132b7e30..cdc6c4d809d12 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -10,6 +10,7 @@ use rustc_hir::pat_util::EnumerateAndAdjustIterator; use rustc_hir::{self as hir, BindingAnnotation, ByRef, HirId, Mutability, Pat, PatKind}; use rustc_infer::infer; use rustc_infer::infer::type_variable::TypeVariableOrigin; +use rustc_lint as lint; use rustc_middle::mir::interpret::ErrorHandled; use rustc_middle::ty::{self, Adt, Ty, TypeVisitableExt}; use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS; @@ -629,12 +630,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected: Ty<'tcx>, pat_info: PatInfo<'tcx, '_>, ) -> Ty<'tcx> { - let PatInfo { binding_mode: def_bm, top_info: ti, .. } = pat_info; + let PatInfo { binding_mode: BindingAnnotation(def_br, _), top_info: ti, .. } = pat_info; // Determine the binding mode... let bm = match ba { - BindingAnnotation(ByRef::No, Mutability::Not) => def_bm, - _ => ba, + BindingAnnotation(ByRef::No, Mutability::Mut) + if !(pat.span.at_least_rust_2024() + && self.tcx.features().mut_preserve_binding_mode_2024) + && matches!(def_br, ByRef::Yes(_)) => + { + // `mut x` resets the binding mode in edition <= 2021. + self.tcx.emit_node_span_lint( + lint::builtin::DEREFERENCING_MUT_BINDING, + pat.hir_id, + pat.span, + errors::DereferencingMutBinding { span: pat.span }, + ); + BindingAnnotation(ByRef::No, Mutability::Mut) + } + BindingAnnotation(ByRef::No, mutbl) => BindingAnnotation(def_br, mutbl), + BindingAnnotation(ByRef::Yes(_), _) => ba, }; // ...and store it in a side table: self.typeck_results.borrow_mut().pat_binding_modes_mut().insert(pat.hir_id, bm); @@ -743,7 +758,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - // Precondition: pat is a Ref(_) pattern + /// Precondition: pat is a `Ref(_)` pattern fn borrow_pat_suggestion(&self, err: &mut Diag<'_>, pat: &Pat<'_>) { let tcx = self.tcx; if let PatKind::Ref(inner, mutbl) = pat.kind diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 2713690f8120a..e74cc388cab4c 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -38,6 +38,7 @@ declare_lint_pass! { DEPRECATED_CFG_ATTR_CRATE_TYPE_NAME, DEPRECATED_IN_FUTURE, DEPRECATED_WHERE_CLAUSE_LOCATION, + DEREFERENCING_MUT_BINDING, DUPLICATE_MACRO_ATTRIBUTES, ELIDED_LIFETIMES_IN_ASSOCIATED_CONSTANT, ELIDED_LIFETIMES_IN_PATHS, @@ -1627,6 +1628,42 @@ declare_lint! { "detect mut variables which don't need to be mutable" } +declare_lint! { + /// The `dereferencing_mut_binding` lint detects a `mut x` pattern that resets the binding mode, + /// as this behavior will change in rust 2024. + /// + /// ### Example + /// + /// ```rust + /// # #![warn(dereferencing_mut_binding)] + /// let x = Some(123u32); + /// let _y = match &x { + /// Some(mut x) => { + /// x += 1; + /// x + /// } + /// None => 0, + /// }; + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Without the `mut`, `x` would have type `&u32`. Pre-2024, adding `mut` makes `x` have type + /// `u32`, which was deemed surprising. After edition 2024, adding `mut` will not change the + /// type of `x`. This lint warns users of editions before 2024 to update their code. + pub DEREFERENCING_MUT_BINDING, + Allow, + "detects `mut x` bindings that change the type of `x`", + @feature_gate = sym::mut_preserve_binding_mode_2024; + // FIXME uncomment below upon stabilization + /*@future_incompatible = FutureIncompatibleInfo { + reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2024), + reference: "123076", + };*/ +} + declare_lint! { /// The `unconditional_recursion` lint detects functions that cannot /// return without calling themselves. diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index bfd0f77c237b2..0a95d86ccc891 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1194,6 +1194,7 @@ symbols! { multiple_supertrait_upcastable, must_not_suspend, must_use, + mut_preserve_binding_mode_2024, mut_ref, naked, naked_functions, diff --git a/tests/ui/pattern/feature-gate-mut_preserve_binding_mode_2024.rs b/tests/ui/pattern/feature-gate-mut_preserve_binding_mode_2024.rs new file mode 100644 index 0000000000000..15c542e6bf104 --- /dev/null +++ b/tests/ui/pattern/feature-gate-mut_preserve_binding_mode_2024.rs @@ -0,0 +1,14 @@ +//@ edition: 2024 +//@ compile-flags: -Zunstable-options + +struct Foo(u8); + +fn main() { + let Foo(mut a) = &Foo(0); + a = &42; + //~^ ERROR: mismatched types + + let Foo(mut a) = &mut Foo(0); + a = &mut 42; + //~^ ERROR: mismatched types +} diff --git a/tests/ui/pattern/feature-gate-mut_preserve_binding_mode_2024.stderr b/tests/ui/pattern/feature-gate-mut_preserve_binding_mode_2024.stderr new file mode 100644 index 0000000000000..6d0a034be21c7 --- /dev/null +++ b/tests/ui/pattern/feature-gate-mut_preserve_binding_mode_2024.stderr @@ -0,0 +1,31 @@ +error[E0308]: mismatched types + --> $DIR/feature-gate-mut_preserve_binding_mode_2024.rs:8:9 + | +LL | let Foo(mut a) = &Foo(0); + | ----- expected due to the type of this binding +LL | a = &42; + | ^^^ expected `u8`, found `&{integer}` + | +help: consider removing the borrow + | +LL - a = &42; +LL + a = 42; + | + +error[E0308]: mismatched types + --> $DIR/feature-gate-mut_preserve_binding_mode_2024.rs:12:9 + | +LL | let Foo(mut a) = &mut Foo(0); + | ----- expected due to the type of this binding +LL | a = &mut 42; + | ^^^^^^^ expected `u8`, found `&mut {integer}` + | +help: consider removing the borrow + | +LL - a = &mut 42; +LL + a = 42; + | + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/pattern/mut_preserve_binding_mode_2021.rs b/tests/ui/pattern/mut_preserve_binding_mode_2021.rs new file mode 100644 index 0000000000000..befa49fdc2472 --- /dev/null +++ b/tests/ui/pattern/mut_preserve_binding_mode_2021.rs @@ -0,0 +1,16 @@ +//@ edition: 2021 +//@ compile-flags: -Zunstable-options +#![feature(mut_preserve_binding_mode_2024)] +#![allow(incomplete_features)] + +struct Foo(u8); + +fn main() { + let Foo(mut a) = &Foo(0); + a = &42; + //~^ ERROR: mismatched types + + let Foo(mut a) = &mut Foo(0); + a = &mut 42; + //~^ ERROR: mismatched types +} diff --git a/tests/ui/pattern/mut_preserve_binding_mode_2021.stderr b/tests/ui/pattern/mut_preserve_binding_mode_2021.stderr new file mode 100644 index 0000000000000..b800cc4a0f454 --- /dev/null +++ b/tests/ui/pattern/mut_preserve_binding_mode_2021.stderr @@ -0,0 +1,31 @@ +error[E0308]: mismatched types + --> $DIR/mut_preserve_binding_mode_2021.rs:10:9 + | +LL | let Foo(mut a) = &Foo(0); + | ----- expected due to the type of this binding +LL | a = &42; + | ^^^ expected `u8`, found `&{integer}` + | +help: consider removing the borrow + | +LL - a = &42; +LL + a = 42; + | + +error[E0308]: mismatched types + --> $DIR/mut_preserve_binding_mode_2021.rs:14:9 + | +LL | let Foo(mut a) = &mut Foo(0); + | ----- expected due to the type of this binding +LL | a = &mut 42; + | ^^^^^^^ expected `u8`, found `&mut {integer}` + | +help: consider removing the borrow + | +LL - a = &mut 42; +LL + a = 42; + | + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/pattern/mut_preserve_binding_mode_2024.rs b/tests/ui/pattern/mut_preserve_binding_mode_2024.rs new file mode 100644 index 0000000000000..5454962e16ce1 --- /dev/null +++ b/tests/ui/pattern/mut_preserve_binding_mode_2024.rs @@ -0,0 +1,15 @@ +//@ run-pass +//@ edition: 2024 +//@ compile-flags: -Zunstable-options +#![feature(mut_preserve_binding_mode_2024)] +#![allow(incomplete_features, unused)] + +struct Foo(u8); + +fn main() { + let Foo(mut a) = &Foo(0); + a = &42; + + let Foo(mut a) = &mut Foo(0); + a = &mut 42; +} diff --git a/tests/ui/pattern/mut_preserve_binding_mode_2024_lint.rs b/tests/ui/pattern/mut_preserve_binding_mode_2024_lint.rs new file mode 100644 index 0000000000000..249f251d2cd2e --- /dev/null +++ b/tests/ui/pattern/mut_preserve_binding_mode_2024_lint.rs @@ -0,0 +1,16 @@ +//@ edition: 2021 +#![feature(mut_preserve_binding_mode_2024)] +#![allow(incomplete_features, unused)] +#![forbid(dereferencing_mut_binding)] + +struct Foo(u8); + +fn main() { + let Foo(mut a) = &Foo(0); + //~^ ERROR: dereferencing `mut` binding + a = 42; + + let Foo(mut a) = &mut Foo(0); + //~^ ERROR: dereferencing `mut` binding + a = 42; +} diff --git a/tests/ui/pattern/mut_preserve_binding_mode_2024_lint.stderr b/tests/ui/pattern/mut_preserve_binding_mode_2024_lint.stderr new file mode 100644 index 0000000000000..e8d11acd83e50 --- /dev/null +++ b/tests/ui/pattern/mut_preserve_binding_mode_2024_lint.stderr @@ -0,0 +1,31 @@ +error: dereferencing `mut` binding + --> $DIR/mut_preserve_binding_mode_2024_lint.rs:9:13 + | +LL | let Foo(mut a) = &Foo(0); + | ^^^^^ `mut` dereferences the type of this binding + | +help: this will change in edition 2024 + --> $DIR/mut_preserve_binding_mode_2024_lint.rs:9:13 + | +LL | let Foo(mut a) = &Foo(0); + | ^^^^^ +note: the lint level is defined here + --> $DIR/mut_preserve_binding_mode_2024_lint.rs:4:11 + | +LL | #![forbid(dereferencing_mut_binding)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: dereferencing `mut` binding + --> $DIR/mut_preserve_binding_mode_2024_lint.rs:13:13 + | +LL | let Foo(mut a) = &mut Foo(0); + | ^^^^^ `mut` dereferences the type of this binding + | +help: this will change in edition 2024 + --> $DIR/mut_preserve_binding_mode_2024_lint.rs:13:13 + | +LL | let Foo(mut a) = &mut Foo(0); + | ^^^^^ + +error: aborting due to 2 previous errors +