Skip to content

Commit

Permalink
Auto merge of rust-lang#123080 - Jules-Bertholet:mut-ref-mut, r=Nadri…
Browse files Browse the repository at this point in the history
…eril

Match ergonomics 2024: implement mutable by-reference bindings

Implements the mutable by-reference bindings portion of match ergonomics 2024 (rust-lang#123076), with the `mut ref`/`mut ref mut` syntax, under feature gate `mut_ref`.

r? `@Nadrieril`

`@rustbot` label A-patterns A-edition-2024
  • Loading branch information
bors committed Mar 28, 2024
2 parents ba52720 + e931595 commit 170598b
Show file tree
Hide file tree
Showing 58 changed files with 529 additions and 378 deletions.
19 changes: 7 additions & 12 deletions compiler/rustc_ast/src/ast.rs
Expand Up @@ -702,19 +702,10 @@ pub struct PatField {
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[derive(Encodable, Decodable, HashStable_Generic)]
pub enum ByRef {
Yes,
Yes(Mutability),
No,
}

impl From<bool> for ByRef {
fn from(b: bool) -> ByRef {
match b {
false => ByRef::No,
true => ByRef::Yes,
}
}
}

/// Explicit binding annotations given in the HIR for a binding. Note
/// that this is not the final binding *mode* that we infer after type
/// inference.
Expand All @@ -724,16 +715,20 @@ pub struct BindingAnnotation(pub ByRef, pub Mutability);

impl BindingAnnotation {
pub const NONE: Self = Self(ByRef::No, Mutability::Not);
pub const REF: Self = Self(ByRef::Yes, Mutability::Not);
pub const REF: Self = Self(ByRef::Yes(Mutability::Not), Mutability::Not);
pub const MUT: Self = Self(ByRef::No, Mutability::Mut);
pub const REF_MUT: Self = Self(ByRef::Yes, Mutability::Mut);
pub const REF_MUT: Self = Self(ByRef::Yes(Mutability::Mut), Mutability::Not);
pub const MUT_REF: Self = Self(ByRef::Yes(Mutability::Not), Mutability::Mut);
pub const MUT_REF_MUT: Self = Self(ByRef::Yes(Mutability::Mut), Mutability::Mut);

pub fn prefix_str(self) -> &'static str {
match self {
Self::NONE => "",
Self::REF => "ref ",
Self::MUT => "mut ",
Self::REF_MUT => "ref mut ",
Self::MUT_REF => "mut ref ",
Self::MUT_REF_MUT => "mut ref mut ",
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_ast_lowering/src/lib.rs
Expand Up @@ -1847,8 +1847,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
// the case where we have a mutable pattern to a reference as that would
// no longer be an `ImplicitSelf`.
TyKind::Ref(_, mt) if mt.ty.kind.is_implicit_self() => match mt.mutbl {
hir::Mutability::Not => hir::ImplicitSelfKind::ImmRef,
hir::Mutability::Mut => hir::ImplicitSelfKind::MutRef,
hir::Mutability::Not => hir::ImplicitSelfKind::RefImm,
hir::Mutability::Mut => hir::ImplicitSelfKind::RefMut,
},
_ => hir::ImplicitSelfKind::None,
}
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_ast_passes/src/feature_gate.rs
Expand Up @@ -565,6 +565,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
gate_all!(unnamed_fields, "unnamed fields are not yet fully implemented");
gate_all!(fn_delegation, "functions delegation is not yet fully implemented");
gate_all!(postfix_match, "postfix match is experimental");
gate_all!(mut_ref, "mutable by-reference bindings are experimental");

if !visitor.features.never_patterns {
if let Some(spans) = spans.get(&sym::never_patterns) {
Expand Down
9 changes: 6 additions & 3 deletions compiler/rustc_ast_pretty/src/pprust/state.rs
Expand Up @@ -1545,12 +1545,15 @@ impl<'a> State<'a> {
PatKind::Wild => self.word("_"),
PatKind::Never => self.word("!"),
PatKind::Ident(BindingAnnotation(by_ref, mutbl), ident, sub) => {
if *by_ref == ByRef::Yes {
self.word_nbsp("ref");
}
if mutbl.is_mut() {
self.word_nbsp("mut");
}
if let ByRef::Yes(rmutbl) = by_ref {
self.word_nbsp("ref");
if rmutbl.is_mut() {
self.word_nbsp("mut");
}
}
self.print_ident(*ident);
if let Some(p) = sub {
self.space();
Expand Down
19 changes: 9 additions & 10 deletions compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
Expand Up @@ -4,9 +4,8 @@
use core::ops::ControlFlow;
use hir::{ExprKind, Param};
use rustc_errors::{Applicability, Diag};
use rustc_hir as hir;
use rustc_hir::intravisit::Visitor;
use rustc_hir::Node;
use rustc_hir::{self as hir, BindingAnnotation, ByRef, Node};
use rustc_infer::traits;
use rustc_middle::mir::{Mutability, Place, PlaceRef, ProjectionElem};
use rustc_middle::ty::{self, InstanceDef, ToPredicate, Ty, TyCtxt};
Expand Down Expand Up @@ -304,7 +303,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
{
match *decl.local_info() {
LocalInfo::User(BindingForm::Var(mir::VarBindingForm {
binding_mode: ty::BindingMode::BindByValue(Mutability::Not),
binding_mode: BindingAnnotation(ByRef::No, Mutability::Not),
opt_ty_info: Some(sp),
opt_match_place: _,
pat_span: _,
Expand Down Expand Up @@ -342,7 +341,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
} else if decl.mutability.is_not() {
if matches!(
decl.local_info(),
LocalInfo::User(BindingForm::ImplicitSelf(hir::ImplicitSelfKind::MutRef))
LocalInfo::User(BindingForm::ImplicitSelf(hir::ImplicitSelfKind::RefMut))
) {
err.note(
"as `Self` may be unsized, this call attempts to take `&mut &mut self`",
Expand Down Expand Up @@ -407,7 +406,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
if let Some(fn_decl) = node.fn_decl() {
if !matches!(
fn_decl.implicit_self,
hir::ImplicitSelfKind::ImmRef | hir::ImplicitSelfKind::MutRef
hir::ImplicitSelfKind::RefImm | hir::ImplicitSelfKind::RefMut
) {
err.span_suggestion(
upvar_ident.span,
Expand Down Expand Up @@ -717,7 +716,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
debug!("local_decl: {:?}", local_decl);
let pat_span = match *local_decl.local_info() {
LocalInfo::User(BindingForm::Var(mir::VarBindingForm {
binding_mode: ty::BindingMode::BindByValue(Mutability::Not),
binding_mode: BindingAnnotation(ByRef::No, Mutability::Not),
opt_ty_info: _,
opt_match_place: _,
pat_span,
Expand Down Expand Up @@ -1070,7 +1069,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
}

LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm {
binding_mode: ty::BindingMode::BindByValue(_),
binding_mode: BindingAnnotation(ByRef::No, _),
opt_ty_info,
..
})) => {
Expand Down Expand Up @@ -1138,7 +1137,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
}

LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm {
binding_mode: ty::BindingMode::BindByReference(_),
binding_mode: BindingAnnotation(ByRef::Yes(_), _),
..
})) => {
let pattern_span: Span = local_decl.source_info.span;
Expand Down Expand Up @@ -1329,7 +1328,7 @@ pub fn mut_borrow_of_mutable_ref(local_decl: &LocalDecl<'_>, local_name: Option<
match *local_decl.local_info() {
// Check if mutably borrowing a mutable reference.
LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm {
binding_mode: ty::BindingMode::BindByValue(Mutability::Not),
binding_mode: BindingAnnotation(ByRef::No, Mutability::Not),
..
})) => matches!(local_decl.ty.kind(), ty::Ref(_, _, hir::Mutability::Mut)),
LocalInfo::User(mir::BindingForm::ImplicitSelf(kind)) => {
Expand All @@ -1338,7 +1337,7 @@ pub fn mut_borrow_of_mutable_ref(local_decl: &LocalDecl<'_>, local_name: Option<
//
// Deliberately fall into this case for all implicit self types,
// so that we don't fall into the next case with them.
kind == hir::ImplicitSelfKind::MutRef
kind == hir::ImplicitSelfKind::RefMut
}
_ if Some(kw::SelfLower) == local_name => {
// Otherwise, check if the name is the `self` keyword - in which case
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_feature/src/unstable.rs
Expand Up @@ -529,6 +529,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)),
/// Allows `mut ref` and `mut ref mut` identifier patterns.
(incomplete, mut_ref, "CURRENT_RUSTC_VERSION", Some(123076)),
/// Allows using `#[naked]` on functions.
(unstable, naked_functions, "1.9.0", Some(90957)),
/// Allows specifying the as-needed link modifier
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_hir/src/hir.rs
Expand Up @@ -2733,9 +2733,9 @@ pub enum ImplicitSelfKind {
/// Represents a `fn x(mut self);`.
Mut,
/// Represents a `fn x(&self);`.
ImmRef,
RefImm,
/// Represents a `fn x(&mut self);`.
MutRef,
RefMut,
/// Represents when a function does not have a self argument or
/// when a function has a `self: X` argument.
None,
Expand Down
42 changes: 17 additions & 25 deletions compiler/rustc_hir_analysis/src/check/errs.rs
Expand Up @@ -14,22 +14,15 @@ pub fn maybe_expr_static_mut(tcx: TyCtxt<'_>, expr: hir::Expr<'_>) {
&& matches!(borrow_kind, hir::BorrowKind::Ref)
&& let Some(var) = is_path_static_mut(*expr)
{
handle_static_mut_ref(
tcx,
span,
var,
span.edition().at_least_rust_2024(),
matches!(m, Mutability::Mut),
hir_id,
);
handle_static_mut_ref(tcx, span, var, span.edition().at_least_rust_2024(), m, hir_id);
}
}

/// Check for shared or mutable references of `static mut` inside statement
pub fn maybe_stmt_static_mut(tcx: TyCtxt<'_>, stmt: hir::Stmt<'_>) {
if let hir::StmtKind::Let(loc) = stmt.kind
&& let hir::PatKind::Binding(ba, _, _, _) = loc.pat.kind
&& matches!(ba.0, rustc_ast::ByRef::Yes)
&& let hir::ByRef::Yes(rmutbl) = ba.0
&& let Some(init) = loc.init
&& let Some(var) = is_path_static_mut(*init)
{
Expand All @@ -38,7 +31,7 @@ pub fn maybe_stmt_static_mut(tcx: TyCtxt<'_>, stmt: hir::Stmt<'_>) {
init.span,
var,
loc.span.edition().at_least_rust_2024(),
matches!(ba.1, Mutability::Mut),
rmutbl,
stmt.hir_id,
);
}
Expand All @@ -60,28 +53,27 @@ fn handle_static_mut_ref(
span: Span,
var: String,
e2024: bool,
mutable: bool,
mutable: Mutability,
hir_id: hir::HirId,
) {
if e2024 {
let (sugg, shared) = if mutable {
let (sugg, shared) = if mutable == Mutability::Mut {
(errors::StaticMutRefSugg::Mut { span, var }, "mutable")
} else {
(errors::StaticMutRefSugg::Shared { span, var }, "shared")
};
tcx.sess.psess.dcx.emit_err(errors::StaticMutRef { span, sugg, shared });
return;
}

let (sugg, shared) = if mutable {
(errors::RefOfMutStaticSugg::Mut { span, var }, "mutable")
} else {
(errors::RefOfMutStaticSugg::Shared { span, var }, "shared")
};
tcx.emit_node_span_lint(
STATIC_MUT_REFS,
hir_id,
span,
errors::RefOfMutStatic { span, sugg, shared },
);
let (sugg, shared) = if mutable == Mutability::Mut {
(errors::RefOfMutStaticSugg::Mut { span, var }, "mutable")
} else {
(errors::RefOfMutStaticSugg::Shared { span, var }, "shared")
};
tcx.emit_node_span_lint(
STATIC_MUT_REFS,
hir_id,
span,
errors::RefOfMutStatic { span, sugg, shared },
);
}
}
2 changes: 1 addition & 1 deletion compiler/rustc_hir_analysis/src/check/region.rs
Expand Up @@ -654,7 +654,7 @@ fn resolve_local<'tcx>(
// & expression, and its lifetime would be extended to the end of the block (due
// to a different rule, not the below code).
match pat.kind {
PatKind::Binding(hir::BindingAnnotation(hir::ByRef::Yes, _), ..) => true,
PatKind::Binding(hir::BindingAnnotation(hir::ByRef::Yes(_), _), ..) => true,

PatKind::Struct(_, field_pats, _) => field_pats.iter().any(|fp| is_binding_pat(fp.pat)),

Expand Down
9 changes: 6 additions & 3 deletions compiler/rustc_hir_pretty/src/lib.rs
Expand Up @@ -1721,12 +1721,15 @@ impl<'a> State<'a> {
PatKind::Wild => self.word("_"),
PatKind::Never => self.word("!"),
PatKind::Binding(BindingAnnotation(by_ref, mutbl), _, ident, sub) => {
if by_ref == ByRef::Yes {
self.word_nbsp("ref");
}
if mutbl.is_mut() {
self.word_nbsp("mut");
}
if let ByRef::Yes(rmutbl) = by_ref {
self.word_nbsp("ref");
if rmutbl.is_mut() {
self.word_nbsp("mut");
}
}
self.print_ident(ident);
if let Some(p) = sub {
self.word("@");
Expand Down
6 changes: 3 additions & 3 deletions compiler/rustc_hir_typeck/src/expr_use_visitor.rs
Expand Up @@ -739,12 +739,12 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
// In a cases of pattern like `let pat = upvar`, don't use the span
// of the pattern, as this just looks confusing, instead use the span
// of the discriminant.
match bm {
ty::BindByReference(m) => {
match bm.0 {
hir::ByRef::Yes(m) => {
let bk = ty::BorrowKind::from_mutbl(m);
delegate.borrow(place, discr_place.hir_id, bk);
}
ty::BindByValue(..) => {
hir::ByRef::No => {
debug!("walk_pat binding consuming pat");
delegate_consume(mc, *delegate, place, discr_place.hir_id);
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_hir_typeck/src/mem_categorization.rs
Expand Up @@ -206,7 +206,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
.get(pat.hir_id)
.expect("missing binding mode");

if let ty::BindByReference(_) = bm {
if matches!(bm.0, hir::ByRef::Yes(_)) {
// a bind-by-ref means that the base_ty will be the type of the ident itself,
// but what we want here is the type of the underlying value being borrowed.
// So peel off one-level, turning the &T into T.
Expand Down

0 comments on commit 170598b

Please sign in to comment.