Skip to content

Commit

Permalink
Rollup merge of #123535 - Jules-Bertholet:mut_dont_reset_binding_mode…
Browse files Browse the repository at this point in the history
…_2024, r=Nadrieril

Match ergonomics 2024: `mut` doesn't reset binding mode

r? ``@Nadrieril``

cc #123076

``@rustbot`` label A-edition-2024 A-patterns
  • Loading branch information
matthiaskrgr authored Apr 16, 2024
2 parents f54219c + 7a32117 commit dc40da8
Show file tree
Hide file tree
Showing 13 changed files with 225 additions and 4 deletions.
2 changes: 2 additions & 0 deletions compiler/rustc_feature/src/unstable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_hir_typeck/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 8 additions & 0 deletions compiler/rustc_hir_typeck/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
23 changes: 19 additions & 4 deletions compiler/rustc_hir_typeck/src/pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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
Expand Down
37 changes: 37 additions & 0 deletions compiler/rustc_lint_defs/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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.
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1194,6 +1194,7 @@ symbols! {
multiple_supertrait_upcastable,
must_not_suspend,
must_use,
mut_preserve_binding_mode_2024,
mut_ref,
naked,
naked_functions,
Expand Down
14 changes: 14 additions & 0 deletions tests/ui/pattern/feature-gate-mut_preserve_binding_mode_2024.rs
Original file line number Diff line number Diff line change
@@ -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
}
Original file line number Diff line number Diff line change
@@ -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`.
16 changes: 16 additions & 0 deletions tests/ui/pattern/mut_preserve_binding_mode_2021.rs
Original file line number Diff line number Diff line change
@@ -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
}
31 changes: 31 additions & 0 deletions tests/ui/pattern/mut_preserve_binding_mode_2021.stderr
Original file line number Diff line number Diff line change
@@ -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`.
15 changes: 15 additions & 0 deletions tests/ui/pattern/mut_preserve_binding_mode_2024.rs
Original file line number Diff line number Diff line change
@@ -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;
}
16 changes: 16 additions & 0 deletions tests/ui/pattern/mut_preserve_binding_mode_2024_lint.rs
Original file line number Diff line number Diff line change
@@ -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;
}
31 changes: 31 additions & 0 deletions tests/ui/pattern/mut_preserve_binding_mode_2024_lint.stderr
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit dc40da8

Please sign in to comment.