Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Match ergonomics 2024: implement mutable by-reference bindings #123080

Merged
merged 3 commits into from
Mar 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 7 additions & 12 deletions compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Loading
Loading