about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMatthias Krüger <matthias.krueger@famsik.de>2024-04-16 17:54:42 +0200
committerGitHub <noreply@github.com>2024-04-16 17:54:42 +0200
commitdc40da83e3326ca72f7f34f0a3bf33e3c50fb3c7 (patch)
tree83489a9efc831bf32cd5d07d6219e83b24e2a155
parentf54219c4736a831e091f3c35ad8cf3326a4da715 (diff)
parent7a3211726bb101ab0c0138b2dcff44a175ff74c4 (diff)
downloadrust-dc40da83e3326ca72f7f34f0a3bf33e3c50fb3c7.tar.gz
rust-dc40da83e3326ca72f7f34f0a3bf33e3c50fb3c7.zip
Rollup merge of #123535 - Jules-Bertholet:mut_dont_reset_binding_mode_2024, r=Nadrieril
Match ergonomics 2024: `mut` doesn't reset binding mode

r? ``@Nadrieril``

cc https://github.com/rust-lang/rust/issues/123076

``@rustbot`` label A-edition-2024 A-patterns
-rw-r--r--compiler/rustc_feature/src/unstable.rs2
-rw-r--r--compiler/rustc_hir_typeck/messages.ftl4
-rw-r--r--compiler/rustc_hir_typeck/src/errors.rs8
-rw-r--r--compiler/rustc_hir_typeck/src/pat.rs23
-rw-r--r--compiler/rustc_lint_defs/src/builtin.rs37
-rw-r--r--compiler/rustc_span/src/symbol.rs1
-rw-r--r--tests/ui/pattern/feature-gate-mut_preserve_binding_mode_2024.rs14
-rw-r--r--tests/ui/pattern/feature-gate-mut_preserve_binding_mode_2024.stderr31
-rw-r--r--tests/ui/pattern/mut_preserve_binding_mode_2021.rs16
-rw-r--r--tests/ui/pattern/mut_preserve_binding_mode_2021.stderr31
-rw-r--r--tests/ui/pattern/mut_preserve_binding_mode_2024.rs15
-rw-r--r--tests/ui/pattern/mut_preserve_binding_mode_2024_lint.rs16
-rw-r--r--tests/ui/pattern/mut_preserve_binding_mode_2024_lint.stderr31
13 files changed, 225 insertions, 4 deletions
diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs
index 7986add68b7..71a1113a649 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 18d9d739dd6..07b4948872d 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 d399730bf3d..3dc9c7b86f7 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<Span>,
     },
 }
+
+#[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 db4bd132b7e..cdc6c4d809d 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 2713690f812..e74cc388cab 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,
@@ -1628,6 +1629,42 @@ declare_lint! {
 }
 
 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 19c3fc58943..be5b0b663c1 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 00000000000..15c542e6bf1
--- /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 00000000000..6d0a034be21
--- /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 00000000000..befa49fdc24
--- /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 00000000000..b800cc4a0f4
--- /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 00000000000..5454962e16c
--- /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 00000000000..249f251d2cd
--- /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 00000000000..e8d11acd83e
--- /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
+