Auto merge of #123080 - Jules-Bertholet:mut-ref-mut, r=Nadrieril

Match ergonomics 2024: implement mutable by-reference bindings

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

r? `@Nadrieril`

`@rustbot` label A-patterns A-edition-2024
This commit is contained in:
bors 2024-03-29 11:08:11 +00:00
commit 45796d1c24
58 changed files with 529 additions and 378 deletions

View File

@ -702,19 +702,10 @@ pub struct PatField {
#[derive(Clone, Copy, Debug, Eq, PartialEq)] #[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[derive(Encodable, Decodable, HashStable_Generic)] #[derive(Encodable, Decodable, HashStable_Generic)]
pub enum ByRef { pub enum ByRef {
Yes, Yes(Mutability),
No, 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 /// Explicit binding annotations given in the HIR for a binding. Note
/// that this is not the final binding *mode* that we infer after type /// that this is not the final binding *mode* that we infer after type
/// inference. /// inference.
@ -724,9 +715,11 @@ pub struct BindingAnnotation(pub ByRef, pub Mutability);
impl BindingAnnotation { impl BindingAnnotation {
pub const NONE: Self = Self(ByRef::No, Mutability::Not); 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 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 { pub fn prefix_str(self) -> &'static str {
match self { match self {
@ -734,6 +727,8 @@ impl BindingAnnotation {
Self::REF => "ref ", Self::REF => "ref ",
Self::MUT => "mut ", Self::MUT => "mut ",
Self::REF_MUT => "ref mut ", Self::REF_MUT => "ref mut ",
Self::MUT_REF => "mut ref ",
Self::MUT_REF_MUT => "mut ref mut ",
} }
} }
} }

View File

@ -1847,8 +1847,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
// the case where we have a mutable pattern to a reference as that would // the case where we have a mutable pattern to a reference as that would
// no longer be an `ImplicitSelf`. // no longer be an `ImplicitSelf`.
TyKind::Ref(_, mt) if mt.ty.kind.is_implicit_self() => match mt.mutbl { TyKind::Ref(_, mt) if mt.ty.kind.is_implicit_self() => match mt.mutbl {
hir::Mutability::Not => hir::ImplicitSelfKind::ImmRef, hir::Mutability::Not => hir::ImplicitSelfKind::RefImm,
hir::Mutability::Mut => hir::ImplicitSelfKind::MutRef, hir::Mutability::Mut => hir::ImplicitSelfKind::RefMut,
}, },
_ => hir::ImplicitSelfKind::None, _ => hir::ImplicitSelfKind::None,
} }

View File

@ -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!(unnamed_fields, "unnamed fields are not yet fully implemented");
gate_all!(fn_delegation, "functions delegation is 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!(postfix_match, "postfix match is experimental");
gate_all!(mut_ref, "mutable by-reference bindings are experimental");
if !visitor.features.never_patterns { if !visitor.features.never_patterns {
if let Some(spans) = spans.get(&sym::never_patterns) { if let Some(spans) = spans.get(&sym::never_patterns) {

View File

@ -1545,12 +1545,15 @@ impl<'a> State<'a> {
PatKind::Wild => self.word("_"), PatKind::Wild => self.word("_"),
PatKind::Never => self.word("!"), PatKind::Never => self.word("!"),
PatKind::Ident(BindingAnnotation(by_ref, mutbl), ident, sub) => { PatKind::Ident(BindingAnnotation(by_ref, mutbl), ident, sub) => {
if *by_ref == ByRef::Yes {
self.word_nbsp("ref");
}
if mutbl.is_mut() { if mutbl.is_mut() {
self.word_nbsp("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); self.print_ident(*ident);
if let Some(p) = sub { if let Some(p) = sub {
self.space(); self.space();

View File

@ -4,9 +4,8 @@
use core::ops::ControlFlow; use core::ops::ControlFlow;
use hir::{ExprKind, Param}; use hir::{ExprKind, Param};
use rustc_errors::{Applicability, Diag}; use rustc_errors::{Applicability, Diag};
use rustc_hir as hir;
use rustc_hir::intravisit::Visitor; use rustc_hir::intravisit::Visitor;
use rustc_hir::Node; use rustc_hir::{self as hir, BindingAnnotation, ByRef, Node};
use rustc_infer::traits; use rustc_infer::traits;
use rustc_middle::mir::{Mutability, Place, PlaceRef, ProjectionElem}; use rustc_middle::mir::{Mutability, Place, PlaceRef, ProjectionElem};
use rustc_middle::ty::{self, InstanceDef, ToPredicate, Ty, TyCtxt}; use rustc_middle::ty::{self, InstanceDef, ToPredicate, Ty, TyCtxt};
@ -304,7 +303,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
{ {
match *decl.local_info() { match *decl.local_info() {
LocalInfo::User(BindingForm::Var(mir::VarBindingForm { 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_ty_info: Some(sp),
opt_match_place: _, opt_match_place: _,
pat_span: _, pat_span: _,
@ -342,7 +341,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
} else if decl.mutability.is_not() { } else if decl.mutability.is_not() {
if matches!( if matches!(
decl.local_info(), decl.local_info(),
LocalInfo::User(BindingForm::ImplicitSelf(hir::ImplicitSelfKind::MutRef)) LocalInfo::User(BindingForm::ImplicitSelf(hir::ImplicitSelfKind::RefMut))
) { ) {
err.note( err.note(
"as `Self` may be unsized, this call attempts to take `&mut &mut self`", "as `Self` may be unsized, this call attempts to take `&mut &mut self`",
@ -407,7 +406,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
if let Some(fn_decl) = node.fn_decl() { if let Some(fn_decl) = node.fn_decl() {
if !matches!( if !matches!(
fn_decl.implicit_self, fn_decl.implicit_self,
hir::ImplicitSelfKind::ImmRef | hir::ImplicitSelfKind::MutRef hir::ImplicitSelfKind::RefImm | hir::ImplicitSelfKind::RefMut
) { ) {
err.span_suggestion( err.span_suggestion(
upvar_ident.span, upvar_ident.span,
@ -717,7 +716,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
debug!("local_decl: {:?}", local_decl); debug!("local_decl: {:?}", local_decl);
let pat_span = match *local_decl.local_info() { let pat_span = match *local_decl.local_info() {
LocalInfo::User(BindingForm::Var(mir::VarBindingForm { LocalInfo::User(BindingForm::Var(mir::VarBindingForm {
binding_mode: ty::BindingMode::BindByValue(Mutability::Not), binding_mode: BindingAnnotation(ByRef::No, Mutability::Not),
opt_ty_info: _, opt_ty_info: _,
opt_match_place: _, opt_match_place: _,
pat_span, pat_span,
@ -1070,7 +1069,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
} }
LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm { LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm {
binding_mode: ty::BindingMode::BindByValue(_), binding_mode: BindingAnnotation(ByRef::No, _),
opt_ty_info, opt_ty_info,
.. ..
})) => { })) => {
@ -1138,7 +1137,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
} }
LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm { 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; let pattern_span: Span = local_decl.source_info.span;
@ -1329,7 +1328,7 @@ pub fn mut_borrow_of_mutable_ref(local_decl: &LocalDecl<'_>, local_name: Option<
match *local_decl.local_info() { match *local_decl.local_info() {
// Check if mutably borrowing a mutable reference. // Check if mutably borrowing a mutable reference.
LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm { 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)), })) => matches!(local_decl.ty.kind(), ty::Ref(_, _, hir::Mutability::Mut)),
LocalInfo::User(mir::BindingForm::ImplicitSelf(kind)) => { LocalInfo::User(mir::BindingForm::ImplicitSelf(kind)) => {
@ -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, // Deliberately fall into this case for all implicit self types,
// so that we don't fall into the next case with them. // 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 => { _ if Some(kw::SelfLower) == local_name => {
// Otherwise, check if the name is the `self` keyword - in which case // Otherwise, check if the name is the `self` keyword - in which case

View File

@ -529,6 +529,8 @@ declare_features! (
(unstable, more_qualified_paths, "1.54.0", Some(86935)), (unstable, more_qualified_paths, "1.54.0", Some(86935)),
/// Allows the `#[must_not_suspend]` attribute. /// Allows the `#[must_not_suspend]` attribute.
(unstable, must_not_suspend, "1.57.0", Some(83310)), (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. /// Allows using `#[naked]` on functions.
(unstable, naked_functions, "1.9.0", Some(90957)), (unstable, naked_functions, "1.9.0", Some(90957)),
/// Allows specifying the as-needed link modifier /// Allows specifying the as-needed link modifier

View File

@ -2733,9 +2733,9 @@ pub enum ImplicitSelfKind {
/// Represents a `fn x(mut self);`. /// Represents a `fn x(mut self);`.
Mut, Mut,
/// Represents a `fn x(&self);`. /// Represents a `fn x(&self);`.
ImmRef, RefImm,
/// Represents a `fn x(&mut self);`. /// Represents a `fn x(&mut self);`.
MutRef, RefMut,
/// Represents when a function does not have a self argument or /// Represents when a function does not have a self argument or
/// when a function has a `self: X` argument. /// when a function has a `self: X` argument.
None, None,

View File

@ -14,14 +14,7 @@ pub fn maybe_expr_static_mut(tcx: TyCtxt<'_>, expr: hir::Expr<'_>) {
&& matches!(borrow_kind, hir::BorrowKind::Ref) && matches!(borrow_kind, hir::BorrowKind::Ref)
&& let Some(var) = is_path_static_mut(*expr) && let Some(var) = is_path_static_mut(*expr)
{ {
handle_static_mut_ref( handle_static_mut_ref(tcx, span, var, span.edition().at_least_rust_2024(), m, hir_id);
tcx,
span,
var,
span.edition().at_least_rust_2024(),
matches!(m, Mutability::Mut),
hir_id,
);
} }
} }
@ -29,7 +22,7 @@ pub fn maybe_expr_static_mut(tcx: TyCtxt<'_>, expr: hir::Expr<'_>) {
pub fn maybe_stmt_static_mut(tcx: TyCtxt<'_>, stmt: hir::Stmt<'_>) { pub fn maybe_stmt_static_mut(tcx: TyCtxt<'_>, stmt: hir::Stmt<'_>) {
if let hir::StmtKind::Let(loc) = stmt.kind if let hir::StmtKind::Let(loc) = stmt.kind
&& let hir::PatKind::Binding(ba, _, _, _) = loc.pat.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(init) = loc.init
&& let Some(var) = is_path_static_mut(*init) && let Some(var) = is_path_static_mut(*init)
{ {
@ -38,7 +31,7 @@ pub fn maybe_stmt_static_mut(tcx: TyCtxt<'_>, stmt: hir::Stmt<'_>) {
init.span, init.span,
var, var,
loc.span.edition().at_least_rust_2024(), loc.span.edition().at_least_rust_2024(),
matches!(ba.1, Mutability::Mut), rmutbl,
stmt.hir_id, stmt.hir_id,
); );
} }
@ -60,28 +53,27 @@ fn handle_static_mut_ref(
span: Span, span: Span,
var: String, var: String,
e2024: bool, e2024: bool,
mutable: bool, mutable: Mutability,
hir_id: hir::HirId, hir_id: hir::HirId,
) { ) {
if e2024 { if e2024 {
let (sugg, shared) = if mutable { let (sugg, shared) = if mutable == Mutability::Mut {
(errors::StaticMutRefSugg::Mut { span, var }, "mutable") (errors::StaticMutRefSugg::Mut { span, var }, "mutable")
} else { } else {
(errors::StaticMutRefSugg::Shared { span, var }, "shared") (errors::StaticMutRefSugg::Shared { span, var }, "shared")
}; };
tcx.sess.psess.dcx.emit_err(errors::StaticMutRef { span, sugg, 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 { } else {
(errors::RefOfMutStaticSugg::Shared { span, var }, "shared") let (sugg, shared) = if mutable == Mutability::Mut {
}; (errors::RefOfMutStaticSugg::Mut { span, var }, "mutable")
tcx.emit_node_span_lint( } else {
STATIC_MUT_REFS, (errors::RefOfMutStaticSugg::Shared { span, var }, "shared")
hir_id, };
span, tcx.emit_node_span_lint(
errors::RefOfMutStatic { span, sugg, shared }, STATIC_MUT_REFS,
); hir_id,
span,
errors::RefOfMutStatic { span, sugg, shared },
);
}
} }

View File

@ -654,7 +654,7 @@ fn resolve_local<'tcx>(
// & expression, and its lifetime would be extended to the end of the block (due // & expression, and its lifetime would be extended to the end of the block (due
// to a different rule, not the below code). // to a different rule, not the below code).
match pat.kind { 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)), PatKind::Struct(_, field_pats, _) => field_pats.iter().any(|fp| is_binding_pat(fp.pat)),

View File

@ -1721,12 +1721,15 @@ impl<'a> State<'a> {
PatKind::Wild => self.word("_"), PatKind::Wild => self.word("_"),
PatKind::Never => self.word("!"), PatKind::Never => self.word("!"),
PatKind::Binding(BindingAnnotation(by_ref, mutbl), _, ident, sub) => { PatKind::Binding(BindingAnnotation(by_ref, mutbl), _, ident, sub) => {
if by_ref == ByRef::Yes {
self.word_nbsp("ref");
}
if mutbl.is_mut() { if mutbl.is_mut() {
self.word_nbsp("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); self.print_ident(ident);
if let Some(p) = sub { if let Some(p) = sub {
self.word("@"); self.word("@");

View File

@ -739,12 +739,12 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
// In a cases of pattern like `let pat = upvar`, don't use the span // 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 pattern, as this just looks confusing, instead use the span
// of the discriminant. // of the discriminant.
match bm { match bm.0 {
ty::BindByReference(m) => { hir::ByRef::Yes(m) => {
let bk = ty::BorrowKind::from_mutbl(m); let bk = ty::BorrowKind::from_mutbl(m);
delegate.borrow(place, discr_place.hir_id, bk); delegate.borrow(place, discr_place.hir_id, bk);
} }
ty::BindByValue(..) => { hir::ByRef::No => {
debug!("walk_pat binding consuming pat"); debug!("walk_pat binding consuming pat");
delegate_consume(mc, *delegate, place, discr_place.hir_id); delegate_consume(mc, *delegate, place, discr_place.hir_id);
} }

View File

@ -206,7 +206,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
.get(pat.hir_id) .get(pat.hir_id)
.expect("missing binding mode"); .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, // 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. // but what we want here is the type of the underlying value being borrowed.
// So peel off one-level, turning the &T into T. // So peel off one-level, turning the &T into T.

View File

@ -5,14 +5,13 @@ use rustc_data_structures::fx::FxHashMap;
use rustc_errors::{ use rustc_errors::{
codes::*, pluralize, struct_span_code_err, Applicability, Diag, ErrorGuaranteed, MultiSpan, codes::*, pluralize, struct_span_code_err, Applicability, Diag, ErrorGuaranteed, MultiSpan,
}; };
use rustc_hir as hir;
use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def::{CtorKind, DefKind, Res};
use rustc_hir::pat_util::EnumerateAndAdjustIterator; use rustc_hir::pat_util::EnumerateAndAdjustIterator;
use rustc_hir::{HirId, Pat, PatKind}; use rustc_hir::{self as hir, BindingAnnotation, ByRef, HirId, Mutability, Pat, PatKind};
use rustc_infer::infer; use rustc_infer::infer;
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use rustc_middle::mir::interpret::ErrorHandled; use rustc_middle::mir::interpret::ErrorHandled;
use rustc_middle::ty::{self, Adt, BindingMode, Ty, TypeVisitableExt}; use rustc_middle::ty::{self, Adt, Ty, TypeVisitableExt};
use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS; use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS;
use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::edit_distance::find_best_match_for_name;
use rustc_span::hygiene::DesugaringKind; use rustc_span::hygiene::DesugaringKind;
@ -79,7 +78,7 @@ struct TopInfo<'tcx> {
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
struct PatInfo<'tcx, 'a> { struct PatInfo<'tcx, 'a> {
binding_mode: BindingMode, binding_mode: BindingAnnotation,
top_info: TopInfo<'tcx>, top_info: TopInfo<'tcx>,
decl_origin: Option<DeclOrigin<'a>>, decl_origin: Option<DeclOrigin<'a>>,
@ -124,7 +123,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
} }
} }
const INITIAL_BM: BindingMode = BindingMode::BindByValue(hir::Mutability::Not); const INITIAL_BM: BindingAnnotation = BindingAnnotation(ByRef::No, Mutability::Not);
/// Mode for adjusting the expected type and binding mode. /// Mode for adjusting the expected type and binding mode.
enum AdjustMode { enum AdjustMode {
@ -269,9 +268,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
&self, &self,
pat: &'tcx Pat<'tcx>, pat: &'tcx Pat<'tcx>,
expected: Ty<'tcx>, expected: Ty<'tcx>,
def_bm: BindingMode, def_bm: BindingAnnotation,
adjust_mode: AdjustMode, adjust_mode: AdjustMode,
) -> (Ty<'tcx>, BindingMode) { ) -> (Ty<'tcx>, BindingAnnotation) {
match adjust_mode { match adjust_mode {
AdjustMode::Pass => (expected, def_bm), AdjustMode::Pass => (expected, def_bm),
AdjustMode::Reset => (expected, INITIAL_BM), AdjustMode::Reset => (expected, INITIAL_BM),
@ -354,8 +353,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
&self, &self,
pat: &'tcx Pat<'tcx>, pat: &'tcx Pat<'tcx>,
expected: Ty<'tcx>, expected: Ty<'tcx>,
mut def_bm: BindingMode, mut def_bm: BindingAnnotation,
) -> (Ty<'tcx>, BindingMode) { ) -> (Ty<'tcx>, BindingAnnotation) {
let mut expected = self.try_structurally_resolve_type(pat.span, expected); let mut expected = self.try_structurally_resolve_type(pat.span, expected);
// Peel off as many `&` or `&mut` from the scrutinee type as possible. For example, // Peel off as many `&` or `&mut` from the scrutinee type as possible. For example,
// for `match &&&mut Some(5)` the loop runs three times, aborting when it reaches // for `match &&&mut Some(5)` the loop runs three times, aborting when it reaches
@ -374,15 +373,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
pat_adjustments.push(expected); pat_adjustments.push(expected);
expected = self.try_structurally_resolve_type(pat.span, inner_ty); expected = self.try_structurally_resolve_type(pat.span, inner_ty);
def_bm = ty::BindByReference(match def_bm { def_bm.0 = ByRef::Yes(match def_bm.0 {
// If default binding mode is by value, make it `ref` or `ref mut` // If default binding mode is by value, make it `ref` or `ref mut`
// (depending on whether we observe `&` or `&mut`). // (depending on whether we observe `&` or `&mut`).
ty::BindByValue(_) | ByRef::No |
// When `ref mut`, stay a `ref mut` (on `&mut`) or downgrade to `ref` (on `&`). // When `ref mut`, stay a `ref mut` (on `&mut`) or downgrade to `ref` (on `&`).
ty::BindByReference(hir::Mutability::Mut) => inner_mutability, ByRef::Yes(Mutability::Mut) => inner_mutability,
// Once a `ref`, always a `ref`. // Once a `ref`, always a `ref`.
// This is because a `& &mut` cannot mutate the underlying value. // This is because a `& &mut` cannot mutate the underlying value.
ty::BindByReference(m @ hir::Mutability::Not) => m, ByRef::Yes(Mutability::Not) => Mutability::Not,
}); });
} }
@ -599,7 +598,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
fn check_pat_ident( fn check_pat_ident(
&self, &self,
pat: &'tcx Pat<'tcx>, pat: &'tcx Pat<'tcx>,
ba: hir::BindingAnnotation, ba: BindingAnnotation,
var_id: HirId, var_id: HirId,
sub: Option<&'tcx Pat<'tcx>>, sub: Option<&'tcx Pat<'tcx>>,
expected: Ty<'tcx>, expected: Ty<'tcx>,
@ -609,8 +608,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// Determine the binding mode... // Determine the binding mode...
let bm = match ba { let bm = match ba {
hir::BindingAnnotation::NONE => def_bm, BindingAnnotation(ByRef::No, Mutability::Not) => def_bm,
_ => BindingMode::convert(ba), _ => ba,
}; };
// ...and store it in a side table: // ...and store it in a side table:
self.typeck_results.borrow_mut().pat_binding_modes_mut().insert(pat.hir_id, bm); self.typeck_results.borrow_mut().pat_binding_modes_mut().insert(pat.hir_id, bm);
@ -618,8 +617,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
debug!("check_pat_ident: pat.hir_id={:?} bm={:?}", pat.hir_id, bm); debug!("check_pat_ident: pat.hir_id={:?} bm={:?}", pat.hir_id, bm);
let local_ty = self.local_ty(pat.span, pat.hir_id); let local_ty = self.local_ty(pat.span, pat.hir_id);
let eq_ty = match bm { let eq_ty = match bm.0 {
ty::BindByReference(mutbl) => { ByRef::Yes(mutbl) => {
// If the binding is like `ref x | ref mut x`, // If the binding is like `ref x | ref mut x`,
// then `x` is assigned a value of type `&M T` where M is the // then `x` is assigned a value of type `&M T` where M is the
// mutability and T is the expected type. // mutability and T is the expected type.
@ -630,10 +629,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.new_ref_ty(pat.span, mutbl, expected) self.new_ref_ty(pat.span, mutbl, expected)
} }
// Otherwise, the type of x is the expected type `T`. // Otherwise, the type of x is the expected type `T`.
ty::BindByValue(_) => { ByRef::No => expected, // As above, `T <: typeof(x)` is required, but we use equality, see (note_1).
// As above, `T <: typeof(x)` is required, but we use equality, see (note_1).
expected
}
}; };
self.demand_eqtype_pat(pat.span, eq_ty, local_ty, ti); self.demand_eqtype_pat(pat.span, eq_ty, local_ty, ti);
@ -655,7 +651,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
/// bindings have the same type by comparing them all against the type of that first pat. /// bindings have the same type by comparing them all against the type of that first pat.
fn check_binding_alt_eq_ty( fn check_binding_alt_eq_ty(
&self, &self,
ba: hir::BindingAnnotation, ba: BindingAnnotation,
span: Span, span: Span,
var_id: HirId, var_id: HirId,
ty: Ty<'tcx>, ty: Ty<'tcx>,
@ -695,10 +691,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
span: Span, span: Span,
expected: Ty<'tcx>, expected: Ty<'tcx>,
actual: Ty<'tcx>, actual: Ty<'tcx>,
ba: hir::BindingAnnotation, ba: BindingAnnotation,
) { ) {
match (expected.kind(), actual.kind(), ba) { match (expected.kind(), actual.kind(), ba) {
(ty::Ref(_, inner_ty, _), _, hir::BindingAnnotation::NONE) (ty::Ref(_, inner_ty, _), _, BindingAnnotation::NONE)
if self.can_eq(self.param_env, *inner_ty, actual) => if self.can_eq(self.param_env, *inner_ty, actual) =>
{ {
err.span_suggestion_verbose( err.span_suggestion_verbose(
@ -708,7 +704,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
Applicability::MaybeIncorrect, Applicability::MaybeIncorrect,
); );
} }
(_, ty::Ref(_, inner_ty, _), hir::BindingAnnotation::REF) (_, ty::Ref(_, inner_ty, _), BindingAnnotation::REF)
if self.can_eq(self.param_env, expected, *inner_ty) => if self.can_eq(self.param_env, expected, *inner_ty) =>
{ {
err.span_suggestion_verbose( err.span_suggestion_verbose(
@ -800,7 +796,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
if let PatKind::Ref(the_ref, _) = i.kind if let PatKind::Ref(the_ref, _) = i.kind
&& let PatKind::Binding(mt, _, ident, _) = the_ref.kind && let PatKind::Binding(mt, _, ident, _) = the_ref.kind
{ {
let hir::BindingAnnotation(_, mtblty) = mt; let BindingAnnotation(_, mtblty) = mt;
err.span_suggestion_verbose( err.span_suggestion_verbose(
i.span, i.span,
format!("consider removing `&{mutability}` from the pattern"), format!("consider removing `&{mutability}` from the pattern"),
@ -2037,7 +2033,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
&self, &self,
pat: &'tcx Pat<'tcx>, pat: &'tcx Pat<'tcx>,
inner: &'tcx Pat<'tcx>, inner: &'tcx Pat<'tcx>,
mutbl: hir::Mutability, mutbl: Mutability,
expected: Ty<'tcx>, expected: Ty<'tcx>,
pat_info: PatInfo<'tcx, '_>, pat_info: PatInfo<'tcx, '_>,
) -> Ty<'tcx> { ) -> Ty<'tcx> {
@ -2088,7 +2084,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
} }
/// Create a reference type with a fresh region variable. /// Create a reference type with a fresh region variable.
fn new_ref_ty(&self, span: Span, mutbl: hir::Mutability, ty: Ty<'tcx>) -> Ty<'tcx> { fn new_ref_ty(&self, span: Span, mutbl: Mutability, ty: Ty<'tcx>) -> Ty<'tcx> {
let region = self.next_region_var(infer::PatternRegion(span)); let region = self.next_region_var(infer::PatternRegion(span));
Ty::new_ref(self.tcx, region, ty, mutbl) Ty::new_ref(self.tcx, region, ty, mutbl)
} }

View File

@ -1711,10 +1711,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let bm = *typeck_results.pat_binding_modes().get(var_hir_id).expect("missing binding mode"); let bm = *typeck_results.pat_binding_modes().get(var_hir_id).expect("missing binding mode");
let mut is_mutbl = match bm { let mut is_mutbl = bm.1;
ty::BindByValue(mutability) => mutability,
ty::BindByReference(_) => hir::Mutability::Not,
};
for pointer_ty in place.deref_tys() { for pointer_ty in place.deref_tys() {
match pointer_ty.kind() { match pointer_ty.kind() {

View File

@ -17,8 +17,10 @@ use rustc_data_structures::captures::Captures;
use rustc_errors::{DiagArgName, DiagArgValue, DiagMessage, ErrorGuaranteed, IntoDiagArg}; use rustc_errors::{DiagArgName, DiagArgValue, DiagMessage, ErrorGuaranteed, IntoDiagArg};
use rustc_hir::def::{CtorKind, Namespace}; use rustc_hir::def::{CtorKind, Namespace};
use rustc_hir::def_id::{DefId, CRATE_DEF_ID}; use rustc_hir::def_id::{DefId, CRATE_DEF_ID};
use rustc_hir::{self, CoroutineDesugaring, CoroutineKind, ImplicitSelfKind}; use rustc_hir::{
use rustc_hir::{self as hir, HirId}; self as hir, BindingAnnotation, ByRef, CoroutineDesugaring, CoroutineKind, HirId,
ImplicitSelfKind,
};
use rustc_session::Session; use rustc_session::Session;
use rustc_span::source_map::Spanned; use rustc_span::source_map::Spanned;
use rustc_target::abi::{FieldIdx, VariantIdx}; use rustc_target::abi::{FieldIdx, VariantIdx};
@ -992,8 +994,8 @@ pub enum LocalKind {
#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable)] #[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable)]
pub struct VarBindingForm<'tcx> { pub struct VarBindingForm<'tcx> {
/// Is variable bound via `x`, `mut x`, `ref x`, or `ref mut x`? /// Is variable bound via `x`, `mut x`, `ref x`, `ref mut x`, `mut ref x`, or `mut ref mut x`?
pub binding_mode: ty::BindingMode, pub binding_mode: BindingAnnotation,
/// If an explicit type was provided for this variable binding, /// If an explicit type was provided for this variable binding,
/// this holds the source Span of that type. /// this holds the source Span of that type.
/// ///
@ -1218,7 +1220,7 @@ impl<'tcx> LocalDecl<'tcx> {
self.local_info(), self.local_info(),
LocalInfo::User( LocalInfo::User(
BindingForm::Var(VarBindingForm { BindingForm::Var(VarBindingForm {
binding_mode: ty::BindingMode::BindByValue(_), binding_mode: BindingAnnotation(ByRef::No, _),
opt_ty_info: _, opt_ty_info: _,
opt_match_place: _, opt_match_place: _,
pat_span: _, pat_span: _,
@ -1235,7 +1237,7 @@ impl<'tcx> LocalDecl<'tcx> {
self.local_info(), self.local_info(),
LocalInfo::User( LocalInfo::User(
BindingForm::Var(VarBindingForm { BindingForm::Var(VarBindingForm {
binding_mode: ty::BindingMode::BindByValue(_), binding_mode: BindingAnnotation(ByRef::No, _),
opt_ty_info: _, opt_ty_info: _,
opt_match_place: _, opt_match_place: _,
pat_span: _, pat_span: _,

View File

@ -12,12 +12,12 @@ use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece};
use rustc_errors::{DiagArgValue, IntoDiagArg}; use rustc_errors::{DiagArgValue, IntoDiagArg};
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::def_id::DefId; use rustc_hir::def_id::DefId;
use rustc_hir::RangeEnd; use rustc_hir::{BindingAnnotation, ByRef, RangeEnd};
use rustc_index::newtype_index; use rustc_index::newtype_index;
use rustc_index::IndexVec; use rustc_index::IndexVec;
use rustc_middle::middle::region; use rustc_middle::middle::region;
use rustc_middle::mir::interpret::{AllocId, Scalar}; use rustc_middle::mir::interpret::{AllocId, Scalar};
use rustc_middle::mir::{self, BinOp, BorrowKind, FakeReadCause, Mutability, UnOp}; use rustc_middle::mir::{self, BinOp, BorrowKind, FakeReadCause, UnOp};
use rustc_middle::ty::adjustment::PointerCoercion; use rustc_middle::ty::adjustment::PointerCoercion;
use rustc_middle::ty::layout::IntegerExt; use rustc_middle::ty::layout::IntegerExt;
use rustc_middle::ty::{ use rustc_middle::ty::{
@ -581,12 +581,6 @@ pub enum InlineAsmOperand<'tcx> {
}, },
} }
#[derive(Copy, Clone, Debug, PartialEq, HashStable)]
pub enum BindingMode {
ByValue,
ByRef(BorrowKind),
}
#[derive(Clone, Debug, HashStable, TypeVisitable)] #[derive(Clone, Debug, HashStable, TypeVisitable)]
pub struct FieldPat<'tcx> { pub struct FieldPat<'tcx> {
pub field: FieldIdx, pub field: FieldIdx,
@ -607,19 +601,22 @@ impl<'tcx> Pat<'tcx> {
pub fn simple_ident(&self) -> Option<Symbol> { pub fn simple_ident(&self) -> Option<Symbol> {
match self.kind { match self.kind {
PatKind::Binding { name, mode: BindingMode::ByValue, subpattern: None, .. } => { PatKind::Binding {
Some(name) name,
} mode: BindingAnnotation(ByRef::No, _),
subpattern: None,
..
} => Some(name),
_ => None, _ => None,
} }
} }
/// Call `f` on every "binding" in a pattern, e.g., on `a` in /// Call `f` on every "binding" in a pattern, e.g., on `a` in
/// `match foo() { Some(a) => (), None => () }` /// `match foo() { Some(a) => (), None => () }`
pub fn each_binding(&self, mut f: impl FnMut(Symbol, BindingMode, Ty<'tcx>, Span)) { pub fn each_binding(&self, mut f: impl FnMut(Symbol, ByRef, Ty<'tcx>, Span)) {
self.walk_always(|p| { self.walk_always(|p| {
if let PatKind::Binding { name, mode, ty, .. } = p.kind { if let PatKind::Binding { name, mode, ty, .. } = p.kind {
f(name, mode, ty, p.span); f(name, mode.0, ty, p.span);
} }
}); });
} }
@ -730,10 +727,9 @@ pub enum PatKind<'tcx> {
/// `x`, `ref x`, `x @ P`, etc. /// `x`, `ref x`, `x @ P`, etc.
Binding { Binding {
mutability: Mutability,
name: Symbol, name: Symbol,
#[type_visitable(ignore)] #[type_visitable(ignore)]
mode: BindingMode, mode: BindingAnnotation,
#[type_visitable(ignore)] #[type_visitable(ignore)]
var: LocalVarId, var: LocalVarId,
ty: Ty<'tcx>, ty: Ty<'tcx>,
@ -1073,17 +1069,8 @@ impl<'tcx> fmt::Display for Pat<'tcx> {
PatKind::Wild => write!(f, "_"), PatKind::Wild => write!(f, "_"),
PatKind::Never => write!(f, "!"), PatKind::Never => write!(f, "!"),
PatKind::AscribeUserType { ref subpattern, .. } => write!(f, "{subpattern}: _"), PatKind::AscribeUserType { ref subpattern, .. } => write!(f, "{subpattern}: _"),
PatKind::Binding { mutability, name, mode, ref subpattern, .. } => { PatKind::Binding { name, mode, ref subpattern, .. } => {
let is_mut = match mode { f.write_str(mode.prefix_str())?;
BindingMode::ByValue => mutability == Mutability::Mut,
BindingMode::ByRef(bk) => {
write!(f, "ref ")?;
matches!(bk, BorrowKind::Mut { .. })
}
};
if is_mut {
write!(f, "mut ")?;
}
write!(f, "{name}")?; write!(f, "{name}")?;
if let Some(ref subpattern) = *subpattern { if let Some(ref subpattern) = *subpattern {
write!(f, " @ {subpattern}")?; write!(f, " @ {subpattern}")?;

View File

@ -230,15 +230,7 @@ pub fn walk_pat<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>(
AscribeUserType { subpattern, ascription: _ } AscribeUserType { subpattern, ascription: _ }
| Deref { subpattern } | Deref { subpattern }
| DerefPattern { subpattern } | DerefPattern { subpattern }
| Binding { | Binding { subpattern: Some(subpattern), .. } => visitor.visit_pat(subpattern),
subpattern: Some(subpattern),
mutability: _,
mode: _,
var: _,
ty: _,
is_primary: _,
name: _,
} => visitor.visit_pat(subpattern),
Binding { .. } | Wild | Never | Error(_) => {} Binding { .. } | Wild | Never | Error(_) => {}
Variant { subpatterns, adt_def: _, args: _, variant_index: _ } | Leaf { subpatterns } => { Variant { subpatterns, adt_def: _, args: _, variant_index: _ } | Leaf { subpatterns } => {
for subpattern in subpatterns { for subpattern in subpatterns {

View File

@ -1,18 +0,0 @@
use rustc_hir::{BindingAnnotation, ByRef, Mutability};
#[derive(Clone, PartialEq, TyEncodable, TyDecodable, Debug, Copy, HashStable)]
pub enum BindingMode {
BindByReference(Mutability),
BindByValue(Mutability),
}
TrivialTypeTraversalImpls! { BindingMode }
impl BindingMode {
pub fn convert(BindingAnnotation(by_ref, mutbl): BindingAnnotation) -> BindingMode {
match by_ref {
ByRef::No => BindingMode::BindByValue(mutbl),
ByRef::Yes => BindingMode::BindByReference(mutbl),
}
}
}

View File

@ -76,8 +76,6 @@ pub use rustc_type_ir::ConstKind::{
}; };
pub use rustc_type_ir::*; pub use rustc_type_ir::*;
pub use self::binding::BindingMode;
pub use self::binding::BindingMode::*;
pub use self::closure::{ pub use self::closure::{
is_ancestor_or_same_capture, place_to_string_for_capture, BorrowKind, CaptureInfo, is_ancestor_or_same_capture, place_to_string_for_capture, BorrowKind, CaptureInfo,
CapturedPlace, ClosureTypeInfo, MinCaptureInformationMap, MinCaptureList, CapturedPlace, ClosureTypeInfo, MinCaptureInformationMap, MinCaptureList,
@ -123,7 +121,6 @@ pub use self::typeck_results::{
pub mod _match; pub mod _match;
pub mod abstract_const; pub mod abstract_const;
pub mod adjustment; pub mod adjustment;
pub mod binding;
pub mod cast; pub mod cast;
pub mod codec; pub mod codec;
pub mod error; pub mod error;

View File

@ -402,6 +402,7 @@ TrivialTypeTraversalImpls! {
::rustc_span::symbol::Symbol, ::rustc_span::symbol::Symbol,
::rustc_hir::def::Res, ::rustc_hir::def::Res,
::rustc_hir::def_id::LocalDefId, ::rustc_hir::def_id::LocalDefId,
::rustc_hir::ByRef,
::rustc_hir::HirId, ::rustc_hir::HirId,
::rustc_hir::MatchSource, ::rustc_hir::MatchSource,
::rustc_target::asm::InlineAsmRegOrRegClass, ::rustc_target::asm::InlineAsmRegOrRegClass,

View File

@ -3,8 +3,8 @@ use crate::{
infer::canonical::Canonical, infer::canonical::Canonical,
traits::ObligationCause, traits::ObligationCause,
ty::{ ty::{
self, tls, BindingMode, BoundVar, CanonicalPolyFnSig, ClosureSizeProfileData, self, tls, BoundVar, CanonicalPolyFnSig, ClosureSizeProfileData, GenericArgKind,
GenericArgKind, GenericArgs, GenericArgsRef, Ty, UserArgs, GenericArgs, GenericArgsRef, Ty, UserArgs,
}, },
}; };
use rustc_data_structures::{ use rustc_data_structures::{
@ -12,12 +12,12 @@ use rustc_data_structures::{
unord::{ExtendUnord, UnordItems, UnordSet}, unord::{ExtendUnord, UnordItems, UnordSet},
}; };
use rustc_errors::ErrorGuaranteed; use rustc_errors::ErrorGuaranteed;
use rustc_hir as hir;
use rustc_hir::{ use rustc_hir::{
self as hir,
def::{DefKind, Res}, def::{DefKind, Res},
def_id::{DefId, LocalDefId, LocalDefIdMap}, def_id::{DefId, LocalDefId, LocalDefIdMap},
hir_id::OwnerId, hir_id::OwnerId,
HirId, ItemLocalId, ItemLocalMap, ItemLocalSet, BindingAnnotation, ByRef, HirId, ItemLocalId, ItemLocalMap, ItemLocalSet, Mutability,
}; };
use rustc_index::{Idx, IndexVec}; use rustc_index::{Idx, IndexVec};
use rustc_macros::HashStable; use rustc_macros::HashStable;
@ -78,8 +78,8 @@ pub struct TypeckResults<'tcx> {
adjustments: ItemLocalMap<Vec<ty::adjustment::Adjustment<'tcx>>>, adjustments: ItemLocalMap<Vec<ty::adjustment::Adjustment<'tcx>>>,
/// Stores the actual binding mode for all instances of hir::BindingAnnotation. /// Stores the actual binding mode for all instances of [`BindingAnnotation`].
pat_binding_modes: ItemLocalMap<BindingMode>, pat_binding_modes: ItemLocalMap<BindingAnnotation>,
/// Stores the types which were implicitly dereferenced in pattern binding modes /// Stores the types which were implicitly dereferenced in pattern binding modes
/// for later usage in THIR lowering. For example, /// for later usage in THIR lowering. For example,
@ -408,17 +408,22 @@ impl<'tcx> TypeckResults<'tcx> {
matches!(self.type_dependent_defs().get(expr.hir_id), Some(Ok((DefKind::AssocFn, _)))) matches!(self.type_dependent_defs().get(expr.hir_id), Some(Ok((DefKind::AssocFn, _))))
} }
pub fn extract_binding_mode(&self, s: &Session, id: HirId, sp: Span) -> Option<BindingMode> { pub fn extract_binding_mode(
&self,
s: &Session,
id: HirId,
sp: Span,
) -> Option<BindingAnnotation> {
self.pat_binding_modes().get(id).copied().or_else(|| { self.pat_binding_modes().get(id).copied().or_else(|| {
s.dcx().span_bug(sp, "missing binding mode"); s.dcx().span_bug(sp, "missing binding mode");
}) })
} }
pub fn pat_binding_modes(&self) -> LocalTableInContext<'_, BindingMode> { pub fn pat_binding_modes(&self) -> LocalTableInContext<'_, BindingAnnotation> {
LocalTableInContext { hir_owner: self.hir_owner, data: &self.pat_binding_modes } LocalTableInContext { hir_owner: self.hir_owner, data: &self.pat_binding_modes }
} }
pub fn pat_binding_modes_mut(&mut self) -> LocalTableInContextMut<'_, BindingMode> { pub fn pat_binding_modes_mut(&mut self) -> LocalTableInContextMut<'_, BindingAnnotation> {
LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.pat_binding_modes } LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.pat_binding_modes }
} }
@ -442,7 +447,7 @@ impl<'tcx> TypeckResults<'tcx> {
let mut has_ref_mut = false; let mut has_ref_mut = false;
pat.walk(|pat| { pat.walk(|pat| {
if let hir::PatKind::Binding(_, id, _, _) = pat.kind if let hir::PatKind::Binding(_, id, _, _) = pat.kind
&& let Some(ty::BindByReference(ty::Mutability::Mut)) = && let Some(BindingAnnotation(ByRef::Yes(Mutability::Mut), _)) =
self.pat_binding_modes().get(id) self.pat_binding_modes().get(id)
{ {
has_ref_mut = true; has_ref_mut = true;

View File

@ -218,7 +218,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
this.visit_primary_bindings( this.visit_primary_bindings(
pattern, pattern,
UserTypeProjections::none(), UserTypeProjections::none(),
&mut |this, _, _, _, node, span, _, _| { &mut |this, _, _, node, span, _, _| {
this.storage_live_binding( this.storage_live_binding(
block, block,
node, node,
@ -308,7 +308,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
this.visit_primary_bindings( this.visit_primary_bindings(
pattern, pattern,
UserTypeProjections::none(), UserTypeProjections::none(),
&mut |this, _, _, _, node, span, _, _| { &mut |this, _, _, node, span, _, _| {
this.storage_live_binding(block, node, span, OutsideGuard, true); this.storage_live_binding(block, node, span, OutsideGuard, true);
this.schedule_drop_for_binding(node, span, OutsideGuard); this.schedule_drop_for_binding(node, span, OutsideGuard);
}, },

View File

@ -14,6 +14,7 @@ use rustc_data_structures::{
fx::{FxHashSet, FxIndexMap, FxIndexSet}, fx::{FxHashSet, FxIndexMap, FxIndexSet},
stack::ensure_sufficient_stack, stack::ensure_sufficient_stack,
}; };
use rustc_hir::{BindingAnnotation, ByRef};
use rustc_middle::middle::region; use rustc_middle::middle::region;
use rustc_middle::mir::{self, *}; use rustc_middle::mir::{self, *};
use rustc_middle::thir::{self, *}; use rustc_middle::thir::{self, *};
@ -554,7 +555,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
) -> BlockAnd<()> { ) -> BlockAnd<()> {
match irrefutable_pat.kind { match irrefutable_pat.kind {
// Optimize the case of `let x = ...` to write directly into `x` // Optimize the case of `let x = ...` to write directly into `x`
PatKind::Binding { mode: BindingMode::ByValue, var, subpattern: None, .. } => { PatKind::Binding {
mode: BindingAnnotation(ByRef::No, _),
var,
subpattern: None,
..
} => {
let place = let place =
self.storage_live_binding(block, var, irrefutable_pat.span, OutsideGuard, true); self.storage_live_binding(block, var, irrefutable_pat.span, OutsideGuard, true);
unpack!(block = self.expr_into_dest(place, block, initializer_id)); unpack!(block = self.expr_into_dest(place, block, initializer_id));
@ -580,7 +586,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
box Pat { box Pat {
kind: kind:
PatKind::Binding { PatKind::Binding {
mode: BindingMode::ByValue, var, subpattern: None, .. mode: BindingAnnotation(ByRef::No, _),
var,
subpattern: None,
..
}, },
.. ..
}, },
@ -720,7 +729,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
self.visit_primary_bindings( self.visit_primary_bindings(
pattern, pattern,
UserTypeProjections::none(), UserTypeProjections::none(),
&mut |this, mutability, name, mode, var, span, ty, user_ty| { &mut |this, name, mode, var, span, ty, user_ty| {
if visibility_scope.is_none() { if visibility_scope.is_none() {
visibility_scope = visibility_scope =
Some(this.new_source_scope(scope_span, LintLevel::Inherited, None)); Some(this.new_source_scope(scope_span, LintLevel::Inherited, None));
@ -730,7 +739,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
this.declare_binding( this.declare_binding(
source_info, source_info,
visibility_scope, visibility_scope,
mutability,
name, name,
mode, mode,
var, var,
@ -818,9 +826,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
pattern_user_ty: UserTypeProjections, pattern_user_ty: UserTypeProjections,
f: &mut impl FnMut( f: &mut impl FnMut(
&mut Self, &mut Self,
Mutability,
Symbol, Symbol,
BindingMode, BindingAnnotation,
LocalVarId, LocalVarId,
Span, Span,
Ty<'tcx>, Ty<'tcx>,
@ -832,18 +839,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
pattern, pattern_user_ty pattern, pattern_user_ty
); );
match pattern.kind { match pattern.kind {
PatKind::Binding { PatKind::Binding { name, mode, var, ty, ref subpattern, is_primary, .. } => {
mutability,
name,
mode,
var,
ty,
ref subpattern,
is_primary,
..
} => {
if is_primary { if is_primary {
f(self, mutability, name, mode, var, pattern.span, ty, pattern_user_ty.clone()); f(self, name, mode, var, pattern.span, ty, pattern_user_ty.clone());
} }
if let Some(subpattern) = subpattern.as_ref() { if let Some(subpattern) = subpattern.as_ref() {
self.visit_primary_bindings(subpattern, pattern_user_ty, f); self.visit_primary_bindings(subpattern, pattern_user_ty, f);
@ -1079,7 +1077,7 @@ struct Binding<'tcx> {
span: Span, span: Span,
source: Place<'tcx>, source: Place<'tcx>,
var_id: LocalVarId, var_id: LocalVarId,
binding_mode: BindingMode, binding_mode: BindingAnnotation,
} }
/// Indicates that the type of `source` must be a subtype of the /// Indicates that the type of `source` must be a subtype of the
@ -2097,9 +2095,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
parent_data.iter().flat_map(|d| &d.bindings).chain(&candidate.extra_data.bindings); parent_data.iter().flat_map(|d| &d.bindings).chain(&candidate.extra_data.bindings);
self.bind_matched_candidate_for_guard(block, schedule_drops, bindings.clone()); self.bind_matched_candidate_for_guard(block, schedule_drops, bindings.clone());
let guard_frame = GuardFrame { let guard_frame =
locals: bindings.map(|b| GuardFrameLocal::new(b.var_id, b.binding_mode)).collect(), GuardFrame { locals: bindings.map(|b| GuardFrameLocal::new(b.var_id)).collect() };
};
debug!("entering guard building context: {:?}", guard_frame); debug!("entering guard building context: {:?}", guard_frame);
self.guard_context.push(guard_frame); self.guard_context.push(guard_frame);
@ -2176,7 +2173,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
.iter() .iter()
.flat_map(|d| &d.bindings) .flat_map(|d| &d.bindings)
.chain(&candidate.extra_data.bindings) .chain(&candidate.extra_data.bindings)
.filter(|binding| matches!(binding.binding_mode, BindingMode::ByValue)); .filter(|binding| matches!(binding.binding_mode.0, ByRef::No));
// Read all of the by reference bindings to ensure that the // Read all of the by reference bindings to ensure that the
// place they refer to can't be modified by the guard. // place they refer to can't be modified by the guard.
for binding in by_value_bindings.clone() { for binding in by_value_bindings.clone() {
@ -2263,12 +2260,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
RefWithinGuard, RefWithinGuard,
schedule_drops, schedule_drops,
); );
match binding.binding_mode { match binding.binding_mode.0 {
BindingMode::ByValue => { ByRef::No => {
let rvalue = Rvalue::Ref(re_erased, BorrowKind::Shared, binding.source); let rvalue = Rvalue::Ref(re_erased, BorrowKind::Shared, binding.source);
self.cfg.push_assign(block, source_info, ref_for_guard, rvalue); self.cfg.push_assign(block, source_info, ref_for_guard, rvalue);
} }
BindingMode::ByRef(borrow_kind) => { ByRef::Yes(mutbl) => {
let value_for_arm = self.storage_live_binding( let value_for_arm = self.storage_live_binding(
block, block,
binding.var_id, binding.var_id,
@ -2277,7 +2274,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
schedule_drops, schedule_drops,
); );
let rvalue = Rvalue::Ref(re_erased, borrow_kind, binding.source); let rvalue =
Rvalue::Ref(re_erased, util::ref_pat_borrow_kind(mutbl), binding.source);
self.cfg.push_assign(block, source_info, value_for_arm, rvalue); self.cfg.push_assign(block, source_info, value_for_arm, rvalue);
let rvalue = Rvalue::Ref(re_erased, BorrowKind::Shared, value_for_arm); let rvalue = Rvalue::Ref(re_erased, BorrowKind::Shared, value_for_arm);
self.cfg.push_assign(block, source_info, ref_for_guard, rvalue); self.cfg.push_assign(block, source_info, ref_for_guard, rvalue);
@ -2318,10 +2316,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
if schedule_drops { if schedule_drops {
self.schedule_drop_for_binding(binding.var_id, binding.span, OutsideGuard); self.schedule_drop_for_binding(binding.var_id, binding.span, OutsideGuard);
} }
let rvalue = match binding.binding_mode { let rvalue = match binding.binding_mode.0 {
BindingMode::ByValue => Rvalue::Use(self.consume_by_copy_or_move(binding.source)), ByRef::No => Rvalue::Use(self.consume_by_copy_or_move(binding.source)),
BindingMode::ByRef(borrow_kind) => { ByRef::Yes(mutbl) => {
Rvalue::Ref(re_erased, borrow_kind, binding.source) Rvalue::Ref(re_erased, util::ref_pat_borrow_kind(mutbl), binding.source)
} }
}; };
self.cfg.push_assign(block, source_info, local, rvalue); self.cfg.push_assign(block, source_info, local, rvalue);
@ -2338,9 +2336,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
&mut self, &mut self,
source_info: SourceInfo, source_info: SourceInfo,
visibility_scope: SourceScope, visibility_scope: SourceScope,
mutability: Mutability,
name: Symbol, name: Symbol,
mode: BindingMode, mode: BindingAnnotation,
var_id: LocalVarId, var_id: LocalVarId,
var_ty: Ty<'tcx>, var_ty: Ty<'tcx>,
user_ty: UserTypeProjections, user_ty: UserTypeProjections,
@ -2350,18 +2347,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
) { ) {
let tcx = self.tcx; let tcx = self.tcx;
let debug_source_info = SourceInfo { span: source_info.span, scope: visibility_scope }; let debug_source_info = SourceInfo { span: source_info.span, scope: visibility_scope };
let binding_mode = match mode {
BindingMode::ByValue => ty::BindingMode::BindByValue(mutability),
BindingMode::ByRef(_) => ty::BindingMode::BindByReference(mutability),
};
let local = LocalDecl { let local = LocalDecl {
mutability, mutability: mode.1,
ty: var_ty, ty: var_ty,
user_ty: if user_ty.is_empty() { None } else { Some(Box::new(user_ty)) }, user_ty: if user_ty.is_empty() { None } else { Some(Box::new(user_ty)) },
source_info, source_info,
local_info: ClearCrossCrate::Set(Box::new(LocalInfo::User(BindingForm::Var( local_info: ClearCrossCrate::Set(Box::new(LocalInfo::User(BindingForm::Var(
VarBindingForm { VarBindingForm {
binding_mode, binding_mode: mode,
// hypothetically, `visit_primary_bindings` could try to unzip // hypothetically, `visit_primary_bindings` could try to unzip
// an outermost hir::Ty as we descend, matching up // an outermost hir::Ty as we descend, matching up
// idents in pat; but complex w/ unclear UI payoff. // idents in pat; but complex w/ unclear UI payoff.

View File

@ -154,15 +154,7 @@ impl<'pat, 'tcx> MatchPair<'pat, 'tcx> {
TestCase::Irrefutable { ascription, binding: None } TestCase::Irrefutable { ascription, binding: None }
} }
PatKind::Binding { PatKind::Binding { mode, var, ref subpattern, .. } => {
name: _,
mutability: _,
mode,
var,
ty: _,
ref subpattern,
is_primary: _,
} => {
let binding = place.map(|source| super::Binding { let binding = place.map(|source| super::Binding {
span: pattern.span, span: pattern.span,
source, source,
@ -347,3 +339,11 @@ impl<'a, 'b, 'tcx> FakeBorrowCollector<'a, 'b, 'tcx> {
} }
} }
} }
#[must_use]
pub fn ref_pat_borrow_kind(ref_mutability: Mutability) -> BorrowKind {
match ref_mutability {
Mutability::Mut => BorrowKind::Mut { kind: MutBorrowKind::Default },
Mutability::Not => BorrowKind::Shared,
}
}

View File

@ -7,10 +7,9 @@ use rustc_ast::attr;
use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::sorted_map::SortedIndexMultiMap; use rustc_data_structures::sorted_map::SortedIndexMultiMap;
use rustc_errors::ErrorGuaranteed; use rustc_errors::ErrorGuaranteed;
use rustc_hir as hir;
use rustc_hir::def::DefKind; use rustc_hir::def::DefKind;
use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::Node; use rustc_hir::{self as hir, BindingAnnotation, ByRef, Node};
use rustc_index::bit_set::GrowableBitSet; use rustc_index::bit_set::GrowableBitSet;
use rustc_index::{Idx, IndexSlice, IndexVec}; use rustc_index::{Idx, IndexSlice, IndexVec};
use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
@ -19,9 +18,7 @@ use rustc_middle::middle::region;
use rustc_middle::mir::interpret::Scalar; use rustc_middle::mir::interpret::Scalar;
use rustc_middle::mir::*; use rustc_middle::mir::*;
use rustc_middle::query::TyCtxtAt; use rustc_middle::query::TyCtxtAt;
use rustc_middle::thir::{ use rustc_middle::thir::{self, ExprId, LintLevel, LocalVarId, Param, ParamId, PatKind, Thir};
self, BindingMode, ExprId, LintLevel, LocalVarId, Param, ParamId, PatKind, Thir,
};
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt};
use rustc_span::symbol::sym; use rustc_span::symbol::sym;
use rustc_span::Span; use rustc_span::Span;
@ -337,7 +334,7 @@ struct GuardFrameLocal {
} }
impl GuardFrameLocal { impl GuardFrameLocal {
fn new(id: LocalVarId, _binding_mode: BindingMode) -> Self { fn new(id: LocalVarId) -> Self {
GuardFrameLocal { id } GuardFrameLocal { id }
} }
} }
@ -967,9 +964,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
match pat.kind { match pat.kind {
// Don't introduce extra copies for simple bindings // Don't introduce extra copies for simple bindings
PatKind::Binding { PatKind::Binding {
mutability,
var, var,
mode: BindingMode::ByValue, mode: BindingAnnotation(ByRef::No, mutability),
subpattern: None, subpattern: None,
.. ..
} => { } => {
@ -979,7 +975,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
if let Some(kind) = param.self_kind { if let Some(kind) = param.self_kind {
LocalInfo::User(BindingForm::ImplicitSelf(kind)) LocalInfo::User(BindingForm::ImplicitSelf(kind))
} else { } else {
let binding_mode = ty::BindingMode::BindByValue(mutability); let binding_mode = BindingAnnotation(ByRef::No, mutability);
LocalInfo::User(BindingForm::Var(VarBindingForm { LocalInfo::User(BindingForm::Var(VarBindingForm {
binding_mode, binding_mode,
opt_ty_info: param.ty_span, opt_ty_info: param.ty_span,

View File

@ -2,11 +2,11 @@ use std::borrow::Cow;
use crate::build::ExprCategory; use crate::build::ExprCategory;
use crate::errors::*; use crate::errors::*;
use rustc_middle::thir::visit::Visitor;
use rustc_errors::DiagArgValue; use rustc_errors::DiagArgValue;
use rustc_hir as hir; use rustc_hir::{self as hir, BindingAnnotation, ByRef, Mutability};
use rustc_middle::mir::BorrowKind; use rustc_middle::mir::BorrowKind;
use rustc_middle::thir::visit::Visitor;
use rustc_middle::thir::*; use rustc_middle::thir::*;
use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::{self, ParamEnv, Ty, TyCtxt}; use rustc_middle::ty::{self, ParamEnv, Ty, TyCtxt};
@ -289,22 +289,22 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
visit::walk_pat(self, pat); visit::walk_pat(self, pat);
} }
} }
PatKind::Binding { mode: BindingMode::ByRef(borrow_kind), ty, .. } => { PatKind::Binding { mode: BindingAnnotation(ByRef::Yes(rm), _), ty, .. } => {
if self.inside_adt { if self.inside_adt {
let ty::Ref(_, ty, _) = ty.kind() else { let ty::Ref(_, ty, _) = ty.kind() else {
span_bug!( span_bug!(
pat.span, pat.span,
"BindingMode::ByRef in pattern, but found non-reference type {}", "ByRef::Yes in pattern, but found non-reference type {}",
ty ty
); );
}; };
match borrow_kind { match rm {
BorrowKind::Fake | BorrowKind::Shared => { Mutability::Not => {
if !ty.is_freeze(self.tcx, self.param_env) { if !ty.is_freeze(self.tcx, self.param_env) {
self.requires_unsafe(pat.span, BorrowOfLayoutConstrainedField); self.requires_unsafe(pat.span, BorrowOfLayoutConstrainedField);
} }
} }
BorrowKind::Mut { .. } => { Mutability::Mut { .. } => {
self.requires_unsafe(pat.span, MutationOfLayoutConstrainedField); self.requires_unsafe(pat.span, MutationOfLayoutConstrainedField);
} }
} }

View File

@ -13,10 +13,9 @@ use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_errors::{ use rustc_errors::{
codes::*, struct_span_code_err, Applicability, Diag, ErrorGuaranteed, MultiSpan, codes::*, struct_span_code_err, Applicability, Diag, ErrorGuaranteed, MultiSpan,
}; };
use rustc_hir as hir;
use rustc_hir::def::*; use rustc_hir::def::*;
use rustc_hir::def_id::LocalDefId; use rustc_hir::def_id::LocalDefId;
use rustc_hir::HirId; use rustc_hir::{self as hir, BindingAnnotation, ByRef, HirId};
use rustc_middle::middle::limits::get_limit_size; use rustc_middle::middle::limits::get_limit_size;
use rustc_middle::thir::visit::Visitor; use rustc_middle::thir::visit::Visitor;
use rustc_middle::thir::*; use rustc_middle::thir::*;
@ -723,13 +722,14 @@ fn check_borrow_conflicts_in_at_patterns<'tcx>(cx: &MatchVisitor<'_, 'tcx>, pat:
let sess = cx.tcx.sess; let sess = cx.tcx.sess;
// Get the binding move, extract the mutability if by-ref. // Get the binding move, extract the mutability if by-ref.
let mut_outer = match mode { let mut_outer = match mode.0 {
BindingMode::ByValue if is_binding_by_move(ty) => { ByRef::No if is_binding_by_move(ty) => {
// We have `x @ pat` where `x` is by-move. Reject all borrows in `pat`. // We have `x @ pat` where `x` is by-move. Reject all borrows in `pat`.
let mut conflicts_ref = Vec::new(); let mut conflicts_ref = Vec::new();
sub.each_binding(|_, mode, _, span| match mode { sub.each_binding(|_, mode, _, span| {
BindingMode::ByValue => {} if matches!(mode, ByRef::Yes(_)) {
BindingMode::ByRef(_) => conflicts_ref.push(span), conflicts_ref.push(span)
}
}); });
if !conflicts_ref.is_empty() { if !conflicts_ref.is_empty() {
sess.dcx().emit_err(BorrowOfMovedValue { sess.dcx().emit_err(BorrowOfMovedValue {
@ -742,8 +742,8 @@ fn check_borrow_conflicts_in_at_patterns<'tcx>(cx: &MatchVisitor<'_, 'tcx>, pat:
} }
return; return;
} }
BindingMode::ByValue => return, ByRef::No => return,
BindingMode::ByRef(m) => m.mutability(), ByRef::Yes(m) => m,
}; };
// We now have `ref $mut_outer binding @ sub` (semantically). // We now have `ref $mut_outer binding @ sub` (semantically).
@ -753,7 +753,7 @@ fn check_borrow_conflicts_in_at_patterns<'tcx>(cx: &MatchVisitor<'_, 'tcx>, pat:
let mut conflicts_mut_ref = Vec::new(); let mut conflicts_mut_ref = Vec::new();
sub.each_binding(|name, mode, ty, span| { sub.each_binding(|name, mode, ty, span| {
match mode { match mode {
BindingMode::ByRef(mut_inner) => match (mut_outer, mut_inner.mutability()) { ByRef::Yes(mut_inner) => match (mut_outer, mut_inner) {
// Both sides are `ref`. // Both sides are `ref`.
(Mutability::Not, Mutability::Not) => {} (Mutability::Not, Mutability::Not) => {}
// 2x `ref mut`. // 2x `ref mut`.
@ -767,10 +767,10 @@ fn check_borrow_conflicts_in_at_patterns<'tcx>(cx: &MatchVisitor<'_, 'tcx>, pat:
conflicts_mut_ref.push(Conflict::Ref { span, name }) conflicts_mut_ref.push(Conflict::Ref { span, name })
} }
}, },
BindingMode::ByValue if is_binding_by_move(ty) => { ByRef::No if is_binding_by_move(ty) => {
conflicts_move.push(Conflict::Moved { span, name }) // `ref mut?` + by-move conflict. conflicts_move.push(Conflict::Moved { span, name }) // `ref mut?` + by-move conflict.
} }
BindingMode::ByValue => {} // `ref mut?` + by-copy is fine. ByRef::No => {} // `ref mut?` + by-copy is fine.
} }
}); });
@ -813,8 +813,7 @@ fn check_for_bindings_named_same_as_variants(
) { ) {
if let PatKind::Binding { if let PatKind::Binding {
name, name,
mode: BindingMode::ByValue, mode: BindingAnnotation(ByRef::No, Mutability::Not),
mutability: Mutability::Not,
subpattern: None, subpattern: None,
ty, ty,
.. ..

View File

@ -9,15 +9,14 @@ use crate::errors::*;
use crate::thir::util::UserAnnotatedTyHelpers; use crate::thir::util::UserAnnotatedTyHelpers;
use rustc_errors::codes::*; use rustc_errors::codes::*;
use rustc_hir as hir;
use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def::{CtorOf, DefKind, Res};
use rustc_hir::pat_util::EnumerateAndAdjustIterator; use rustc_hir::pat_util::EnumerateAndAdjustIterator;
use rustc_hir::RangeEnd; use rustc_hir::{self as hir, RangeEnd};
use rustc_index::Idx; use rustc_index::Idx;
use rustc_middle::mir::interpret::{ErrorHandled, GlobalId, LitToConstError, LitToConstInput}; use rustc_middle::mir::interpret::{ErrorHandled, GlobalId, LitToConstError, LitToConstInput};
use rustc_middle::mir::{self, BorrowKind, Const, Mutability}; use rustc_middle::mir::{self, Const};
use rustc_middle::thir::{ use rustc_middle::thir::{
Ascription, BindingMode, FieldPat, LocalVarId, Pat, PatKind, PatRange, PatRangeBoundary, Ascription, FieldPat, LocalVarId, Pat, PatKind, PatRange, PatRangeBoundary,
}; };
use rustc_middle::ty::layout::IntegerExt; use rustc_middle::ty::layout::IntegerExt;
use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty, TyCtxt, TypeVisitableExt}; use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty, TyCtxt, TypeVisitableExt};
@ -281,26 +280,16 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
span = span.with_hi(ident_span.hi()); span = span.with_hi(ident_span.hi());
} }
let bm = *self let mode = *self
.typeck_results .typeck_results
.pat_binding_modes() .pat_binding_modes()
.get(pat.hir_id) .get(pat.hir_id)
.expect("missing binding mode"); .expect("missing binding mode");
let (mutability, mode) = match bm {
ty::BindByValue(mutbl) => (mutbl, BindingMode::ByValue),
ty::BindByReference(hir::Mutability::Mut) => (
Mutability::Not,
BindingMode::ByRef(BorrowKind::Mut { kind: mir::MutBorrowKind::Default }),
),
ty::BindByReference(hir::Mutability::Not) => {
(Mutability::Not, BindingMode::ByRef(BorrowKind::Shared))
}
};
// A ref x pattern is the same node used for x, and as such it has // A ref x pattern is the same node used for x, and as such it has
// x's type, which is &T, where we want T (the type being matched). // x's type, which is &T, where we want T (the type being matched).
let var_ty = ty; let var_ty = ty;
if let ty::BindByReference(_) = bm { if let hir::ByRef::Yes(_) = mode.0 {
if let ty::Ref(_, rty, _) = ty.kind() { if let ty::Ref(_, rty, _) = ty.kind() {
ty = *rty; ty = *rty;
} else { } else {
@ -309,7 +298,6 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
}; };
PatKind::Binding { PatKind::Binding {
mutability,
mode, mode,
name: ident.name, name: ident.name,
var: LocalVarId(id), var: LocalVarId(id),

View File

@ -635,9 +635,8 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> {
self.print_pat(subpattern, depth_lvl + 3); self.print_pat(subpattern, depth_lvl + 3);
print_indented!(self, "}", depth_lvl + 1); print_indented!(self, "}", depth_lvl + 1);
} }
PatKind::Binding { mutability, name, mode, var, ty, subpattern, is_primary } => { PatKind::Binding { name, mode, var, ty, subpattern, is_primary } => {
print_indented!(self, "Binding {", depth_lvl + 1); print_indented!(self, "Binding {", depth_lvl + 1);
print_indented!(self, format!("mutability: {:?}", mutability), depth_lvl + 2);
print_indented!(self, format!("name: {:?}", name), depth_lvl + 2); print_indented!(self, format!("name: {:?}", name), depth_lvl + 2);
print_indented!(self, format!("mode: {:?}", mode), depth_lvl + 2); print_indented!(self, format!("mode: {:?}", mode), depth_lvl + 2);
print_indented!(self, format!("var: {:?}", var), depth_lvl + 2); print_indented!(self, format!("var: {:?}", var), depth_lvl + 2);

View File

@ -655,9 +655,6 @@ parse_question_mark_in_type = invalid `?` in type
parse_recover_import_as_use = expected item, found {$token_name} parse_recover_import_as_use = expected item, found {$token_name}
.suggestion = items are imported using the `use` keyword .suggestion = items are imported using the `use` keyword
parse_ref_mut_order_incorrect = the order of `mut` and `ref` is incorrect
.suggestion = try switching the order
parse_remove_let = expected pattern, found `let` parse_remove_let = expected pattern, found `let`
.suggestion = remove the unnecessary `let` keyword .suggestion = remove the unnecessary `let` keyword

View File

@ -2364,14 +2364,6 @@ pub(crate) struct UnexpectedLifetimeInPattern {
pub symbol: Symbol, pub symbol: Symbol,
} }
#[derive(Diagnostic)]
#[diag(parse_ref_mut_order_incorrect)]
pub(crate) struct RefMutOrderIncorrect {
#[primary_span]
#[suggestion(code = "ref mut", applicability = "machine-applicable")]
pub span: Span,
}
#[derive(Diagnostic)] #[derive(Diagnostic)]
pub(crate) enum InvalidMutInPattern { pub(crate) enum InvalidMutInPattern {
#[diag(parse_mut_on_nested_ident_pattern)] #[diag(parse_mut_on_nested_ident_pattern)]

View File

@ -24,12 +24,11 @@ use rustc_ast::token::{self, Delimiter, Token, TokenKind};
use rustc_ast::tokenstream::{AttributesData, DelimSpacing, DelimSpan, Spacing}; use rustc_ast::tokenstream::{AttributesData, DelimSpacing, DelimSpan, Spacing};
use rustc_ast::tokenstream::{TokenStream, TokenTree, TokenTreeCursor}; use rustc_ast::tokenstream::{TokenStream, TokenTree, TokenTreeCursor};
use rustc_ast::util::case::Case; use rustc_ast::util::case::Case;
use rustc_ast::AttrId; use rustc_ast::{
use rustc_ast::CoroutineKind; self as ast, AnonConst, AttrArgs, AttrArgsEq, AttrId, ByRef, Const, CoroutineKind, DelimArgs,
use rustc_ast::DUMMY_NODE_ID; Expr, ExprKind, Extern, HasAttrs, HasTokens, Mutability, StrLit, Unsafe, Visibility,
use rustc_ast::{self as ast, AnonConst, Const, DelimArgs, Extern}; VisibilityKind, DUMMY_NODE_ID,
use rustc_ast::{AttrArgs, AttrArgsEq, Expr, ExprKind, Mutability, StrLit}; };
use rustc_ast::{HasAttrs, HasTokens, Unsafe, Visibility, VisibilityKind};
use rustc_ast_pretty::pprust; use rustc_ast_pretty::pprust;
use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::fx::FxHashMap;
use rustc_errors::PResult; use rustc_errors::PResult;
@ -1273,6 +1272,11 @@ impl<'a> Parser<'a> {
if self.eat_keyword(kw::Mut) { Mutability::Mut } else { Mutability::Not } if self.eat_keyword(kw::Mut) { Mutability::Mut } else { Mutability::Not }
} }
/// Parses reference binding mode (`ref`, `ref mut`, or nothing).
fn parse_byref(&mut self) -> ByRef {
if self.eat_keyword(kw::Ref) { ByRef::Yes(self.parse_mutability()) } else { ByRef::No }
}
/// Possibly parses mutability (`const` or `mut`). /// Possibly parses mutability (`const` or `mut`).
fn parse_const_or_mut(&mut self) -> Option<Mutability> { fn parse_const_or_mut(&mut self) -> Option<Mutability> {
if self.eat_keyword(kw::Mut) { if self.eat_keyword(kw::Mut) {

View File

@ -4,11 +4,11 @@ use crate::errors::{
DotDotDotRestPattern, EnumPatternInsteadOfIdentifier, ExpectedBindingLeftOfAt, DotDotDotRestPattern, EnumPatternInsteadOfIdentifier, ExpectedBindingLeftOfAt,
ExpectedCommaAfterPatternField, GenericArgsInPatRequireTurbofishSyntax, ExpectedCommaAfterPatternField, GenericArgsInPatRequireTurbofishSyntax,
InclusiveRangeExtraEquals, InclusiveRangeMatchArrow, InclusiveRangeNoEnd, InvalidMutInPattern, InclusiveRangeExtraEquals, InclusiveRangeMatchArrow, InclusiveRangeNoEnd, InvalidMutInPattern,
PatternOnWrongSideOfAt, RefMutOrderIncorrect, RemoveLet, RepeatedMutInPattern, PatternOnWrongSideOfAt, RemoveLet, RepeatedMutInPattern, SwitchRefBoxOrder,
SwitchRefBoxOrder, TopLevelOrPatternNotAllowed, TopLevelOrPatternNotAllowedSugg, TopLevelOrPatternNotAllowed, TopLevelOrPatternNotAllowedSugg, TrailingVertNotAllowed,
TrailingVertNotAllowed, UnexpectedExpressionInPattern, UnexpectedLifetimeInPattern, UnexpectedExpressionInPattern, UnexpectedLifetimeInPattern, UnexpectedParenInRangePat,
UnexpectedParenInRangePat, UnexpectedParenInRangePatSugg, UnexpectedParenInRangePatSugg, UnexpectedVertVertBeforeFunctionParam,
UnexpectedVertVertBeforeFunctionParam, UnexpectedVertVertInPattern, UnexpectedVertVertInPattern,
}; };
use crate::parser::expr::could_be_unclosed_char_literal; use crate::parser::expr::could_be_unclosed_char_literal;
use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole}; use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole};
@ -476,7 +476,7 @@ impl<'a> Parser<'a> {
// Parse `_` // Parse `_`
PatKind::Wild PatKind::Wild
} else if self.eat_keyword(kw::Mut) { } else if self.eat_keyword(kw::Mut) {
self.parse_pat_ident_mut(syntax_loc)? self.parse_pat_ident_mut()?
} else if self.eat_keyword(kw::Ref) { } else if self.eat_keyword(kw::Ref) {
if self.check_keyword(kw::Box) { if self.check_keyword(kw::Box) {
// Suggest `box ref`. // Suggest `box ref`.
@ -486,7 +486,7 @@ impl<'a> Parser<'a> {
} }
// Parse ref ident @ pat / ref mut ident @ pat // Parse ref ident @ pat / ref mut ident @ pat
let mutbl = self.parse_mutability(); let mutbl = self.parse_mutability();
self.parse_pat_ident(BindingAnnotation(ByRef::Yes, mutbl), syntax_loc)? self.parse_pat_ident(BindingAnnotation(ByRef::Yes(mutbl), Mutability::Not), syntax_loc)?
} else if self.eat_keyword(kw::Box) { } else if self.eat_keyword(kw::Box) {
self.parse_pat_box()? self.parse_pat_box()?
} else if self.check_inline_const(0) { } else if self.check_inline_const(0) {
@ -746,13 +746,12 @@ impl<'a> Parser<'a> {
} }
/// Parse a mutable binding with the `mut` token already eaten. /// Parse a mutable binding with the `mut` token already eaten.
fn parse_pat_ident_mut(&mut self, syntax_loc: Option<PatternLocation>) -> PResult<'a, PatKind> { fn parse_pat_ident_mut(&mut self) -> PResult<'a, PatKind> {
let mut_span = self.prev_token.span; let mut_span = self.prev_token.span;
if self.eat_keyword(kw::Ref) { self.recover_additional_muts();
self.dcx().emit_err(RefMutOrderIncorrect { span: mut_span.to(self.prev_token.span) });
return self.parse_pat_ident(BindingAnnotation::REF_MUT, syntax_loc); let byref = self.parse_byref();
}
self.recover_additional_muts(); self.recover_additional_muts();
@ -767,10 +766,12 @@ impl<'a> Parser<'a> {
let mut pat = self.parse_pat_no_top_alt(Some(Expected::Identifier), None)?; let mut pat = self.parse_pat_no_top_alt(Some(Expected::Identifier), None)?;
// If we don't have `mut $ident (@ pat)?`, error. // If we don't have `mut $ident (@ pat)?`, error.
if let PatKind::Ident(BindingAnnotation(ByRef::No, m @ Mutability::Not), ..) = &mut pat.kind if let PatKind::Ident(BindingAnnotation(br @ ByRef::No, m @ Mutability::Not), ..) =
&mut pat.kind
{ {
// Don't recurse into the subpattern. // Don't recurse into the subpattern.
// `mut` on the outer binding doesn't affect the inner bindings. // `mut` on the outer binding doesn't affect the inner bindings.
*br = byref;
*m = Mutability::Mut; *m = Mutability::Mut;
} else { } else {
// Add `mut` to any binding in the parsed pattern. // Add `mut` to any binding in the parsed pattern.
@ -778,6 +779,10 @@ impl<'a> Parser<'a> {
self.ban_mut_general_pat(mut_span, &pat, changed_any_binding); self.ban_mut_general_pat(mut_span, &pat, changed_any_binding);
} }
if matches!(pat.kind, PatKind::Ident(BindingAnnotation(ByRef::Yes(_), Mutability::Mut), ..))
{
self.psess.gated_spans.gate(sym::mut_ref, pat.span);
}
Ok(pat.into_inner().kind) Ok(pat.into_inner().kind)
} }
@ -1390,16 +1395,12 @@ impl<'a> Parser<'a> {
// Parsing a pattern of the form `(box) (ref) (mut) fieldname`. // Parsing a pattern of the form `(box) (ref) (mut) fieldname`.
let is_box = self.eat_keyword(kw::Box); let is_box = self.eat_keyword(kw::Box);
let boxed_span = self.token.span; let boxed_span = self.token.span;
let is_ref = self.eat_keyword(kw::Ref); let mutability = self.parse_mutability();
let is_mut = self.eat_keyword(kw::Mut); let by_ref = self.parse_byref();
let fieldname = self.parse_field_name()?; let fieldname = self.parse_field_name()?;
hi = self.prev_token.span; hi = self.prev_token.span;
let ann = BindingAnnotation(by_ref, mutability);
let mutability = match is_mut {
false => Mutability::Not,
true => Mutability::Mut,
};
let ann = BindingAnnotation(ByRef::from(is_ref), mutability);
let fieldpat = self.mk_pat_ident(boxed_span.to(hi), ann, fieldname); let fieldpat = self.mk_pat_ident(boxed_span.to(hi), ann, fieldname);
let subpat = let subpat =
if is_box { self.mk_pat(lo.to(hi), PatKind::Box(fieldpat)) } else { fieldpat }; if is_box { self.mk_pat(lo.to(hi), PatKind::Box(fieldpat)) } else { fieldpat };

View File

@ -1185,6 +1185,7 @@ symbols! {
multiple_supertrait_upcastable, multiple_supertrait_upcastable,
must_not_suspend, must_not_suspend,
must_use, must_use,
mut_ref,
naked, naked,
naked_functions, naked_functions,
name, name,

View File

@ -24,13 +24,13 @@ pub fn check_fn(cx: &LateContext<'_>, kind: FnKind<'_>, decl: &FnDecl<'_>, body:
let name = ident.name.as_str(); let name = ident.name.as_str();
let name = match decl.implicit_self { let name = match decl.implicit_self {
ImplicitSelfKind::MutRef => { ImplicitSelfKind::RefMut => {
let Some(name) = name.strip_suffix("_mut") else { let Some(name) = name.strip_suffix("_mut") else {
return; return;
}; };
name name
}, },
ImplicitSelfKind::Imm | ImplicitSelfKind::Mut | ImplicitSelfKind::ImmRef => name, ImplicitSelfKind::Imm | ImplicitSelfKind::Mut | ImplicitSelfKind::RefImm => name,
ImplicitSelfKind::None => return, ImplicitSelfKind::None => return,
}; };

View File

@ -97,7 +97,7 @@ fn find_slice_values(cx: &LateContext<'_>, pat: &hir::Pat<'_>) -> FxIndexMap<hir
value_hir_id, value_hir_id,
ident, ident,
sub_pat, sub_pat,
) = pat.kind ) = pat.kind && by_ref != hir::ByRef::Yes(hir::Mutability::Mut)
{ {
// This block catches bindings with sub patterns. It would be hard to build a correct suggestion // This block catches bindings with sub patterns. It would be hard to build a correct suggestion
// for them and it's likely that the user knows what they are doing in such a case. // for them and it's likely that the user knows what they are doing in such a case.
@ -115,7 +115,7 @@ fn find_slice_values(cx: &LateContext<'_>, pat: &hir::Pat<'_>) -> FxIndexMap<hir
if let ty::Slice(inner_ty) | ty::Array(inner_ty, _) = bound_ty.peel_refs().kind() { if let ty::Slice(inner_ty) | ty::Array(inner_ty, _) = bound_ty.peel_refs().kind() {
// The values need to use the `ref` keyword if they can't be copied. // The values need to use the `ref` keyword if they can't be copied.
// This will need to be adjusted if the lint want to support mutable access in the future // This will need to be adjusted if the lint want to support mutable access in the future
let src_is_ref = bound_ty.is_ref() && by_ref != hir::ByRef::Yes; let src_is_ref = bound_ty.is_ref() && by_ref == hir::ByRef::No;
let needs_ref = !(src_is_ref || is_copy(cx, *inner_ty)); let needs_ref = !(src_is_ref || is_copy(cx, *inner_ty));
let slice_info = slices let slice_info = slices

View File

@ -216,8 +216,8 @@ impl {self_ty_without_ref} {{
fn check_impl_item(&mut self, cx: &LateContext<'_>, item: &rustc_hir::ImplItem<'_>) { fn check_impl_item(&mut self, cx: &LateContext<'_>, item: &rustc_hir::ImplItem<'_>) {
let item_did = item.owner_id.to_def_id(); let item_did = item.owner_id.to_def_id();
let (borrow_prefix, expected_implicit_self) = match item.ident.name { let (borrow_prefix, expected_implicit_self) = match item.ident.name {
sym::iter => ("&", ImplicitSelfKind::ImmRef), sym::iter => ("&", ImplicitSelfKind::RefImm),
sym::iter_mut => ("&mut ", ImplicitSelfKind::MutRef), sym::iter_mut => ("&mut ", ImplicitSelfKind::RefMut),
_ => return, _ => return,
}; };

View File

@ -384,8 +384,8 @@ impl LenOutput {
fn expected_sig(self, self_kind: ImplicitSelfKind) -> String { fn expected_sig(self, self_kind: ImplicitSelfKind) -> String {
let self_ref = match self_kind { let self_ref = match self_kind {
ImplicitSelfKind::ImmRef => "&", ImplicitSelfKind::RefImm => "&",
ImplicitSelfKind::MutRef => "&mut ", ImplicitSelfKind::RefMut => "&mut ",
_ => "", _ => "",
}; };
match self { match self {
@ -411,8 +411,8 @@ fn check_is_empty_sig<'tcx>(
[arg, res] if len_output.matches_is_empty_output(cx, *res) => { [arg, res] if len_output.matches_is_empty_output(cx, *res) => {
matches!( matches!(
(arg.kind(), self_kind), (arg.kind(), self_kind),
(ty::Ref(_, _, Mutability::Not), ImplicitSelfKind::ImmRef) (ty::Ref(_, _, Mutability::Not), ImplicitSelfKind::RefImm)
| (ty::Ref(_, _, Mutability::Mut), ImplicitSelfKind::MutRef) | (ty::Ref(_, _, Mutability::Mut), ImplicitSelfKind::RefMut)
) || (!arg.is_ref() && matches!(self_kind, ImplicitSelfKind::Imm | ImplicitSelfKind::Mut)) ) || (!arg.is_ref() && matches!(self_kind, ImplicitSelfKind::Imm | ImplicitSelfKind::Mut))
}, },
_ => false, _ => false,

View File

@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability; use clippy_utils::source::snippet_with_applicability;
use clippy_utils::{path_to_local_id, peel_blocks, strip_pat_refs}; use clippy_utils::{path_to_local_id, peel_blocks, strip_pat_refs};
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{ByRef, ExprKind, LetStmt, MatchSource, PatKind, QPath}; use rustc_hir::{ExprKind, LetStmt, MatchSource, PatKind, QPath};
use rustc_lint::LateContext; use rustc_lint::LateContext;
use super::INFALLIBLE_DESTRUCTURING_MATCH; use super::INFALLIBLE_DESTRUCTURING_MATCH;
@ -30,7 +30,7 @@ pub(crate) fn check(cx: &LateContext<'_>, local: &LetStmt<'_>) -> bool {
format!( format!(
"let {}({}{}) = {};", "let {}({}{}) = {};",
snippet_with_applicability(cx, variant_name.span, "..", &mut applicability), snippet_with_applicability(cx, variant_name.span, "..", &mut applicability),
if binding.0 == ByRef::Yes { "ref " } else { "" }, binding.prefix_str(),
snippet_with_applicability(cx, local.pat.span, "..", &mut applicability), snippet_with_applicability(cx, local.pat.span, "..", &mut applicability),
snippet_with_applicability(cx, target.span, "..", &mut applicability), snippet_with_applicability(cx, target.span, "..", &mut applicability),
), ),

View File

@ -67,7 +67,7 @@ fn is_none_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
fn is_ref_some_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> Option<Mutability> { fn is_ref_some_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> Option<Mutability> {
if let PatKind::TupleStruct(ref qpath, [first_pat, ..], _) = arm.pat.kind if let PatKind::TupleStruct(ref qpath, [first_pat, ..], _) = arm.pat.kind
&& is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), LangItem::OptionSome) && is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), LangItem::OptionSome)
&& let PatKind::Binding(BindingAnnotation(ByRef::Yes, mutabl), .., ident, _) = first_pat.kind && let PatKind::Binding(BindingAnnotation(ByRef::Yes(mutabl), _), .., ident, _) = first_pat.kind
&& let ExprKind::Call(e, [arg]) = peel_blocks(arm.body).kind && let ExprKind::Call(e, [arg]) = peel_blocks(arm.body).kind
&& is_res_lang_ctor(cx, path_res(cx, e), LangItem::OptionSome) && is_res_lang_ctor(cx, path_res(cx, e), LangItem::OptionSome)
&& let ExprKind::Path(QPath::Resolved(_, path2)) = arg.kind && let ExprKind::Path(QPath::Resolved(_, path2)) = arg.kind

View File

@ -178,7 +178,7 @@ fn pat_same_as_expr(pat: &Pat<'_>, expr: &Expr<'_>) -> bool {
}, },
)), )),
) => { ) => {
return !matches!(annot, BindingAnnotation(ByRef::Yes, _)) && pat_ident.name == first_seg.ident.name; return !matches!(annot, BindingAnnotation(ByRef::Yes(_), _)) && pat_ident.name == first_seg.ident.name;
}, },
// Example: `Custom::TypeA => Custom::TypeB`, or `None => None` // Example: `Custom::TypeA => Custom::TypeB`, or `None => None`
(PatKind::Path(QPath::Resolved(_, p_path)), ExprKind::Path(QPath::Resolved(_, e_path))) => { (PatKind::Path(QPath::Resolved(_, p_path)), ExprKind::Path(QPath::Resolved(_, e_path))) => {

View File

@ -182,7 +182,7 @@ fn get_pat_binding<'tcx>(
if let PatKind::Binding(bind_annot, hir_id, ident, _) = pat.kind if let PatKind::Binding(bind_annot, hir_id, ident, _) = pat.kind
&& hir_id == local && hir_id == local
{ {
if matches!(bind_annot.0, rustc_ast::ByRef::Yes) { if matches!(bind_annot.0, rustc_ast::ByRef::Yes(_)) {
let _ = byref_ident.insert(ident); let _ = byref_ident.insert(ident);
} }
// the second call of `replace()` returns a `Some(span)`, meaning a multi-binding pattern // the second call of `replace()` returns a `Some(span)`, meaning a multi-binding pattern

View File

@ -69,7 +69,7 @@ pub(super) fn check(
_ => false, _ => false,
}, },
// local binding capturing a reference // local binding capturing a reference
Node::LetStmt(l) if matches!(l.pat.kind, PatKind::Binding(BindingAnnotation(ByRef::Yes, _), ..)) => { Node::LetStmt(l) if matches!(l.pat.kind, PatKind::Binding(BindingAnnotation(ByRef::Yes(_), _), ..)) => {
return; return;
}, },
_ => false, _ => false,

View File

@ -60,8 +60,6 @@ pub(super) fn check<'tcx>(
applicability, applicability,
); );
} else { } else {
let ref_annotation = if annotation.0 == ByRef::Yes { "ref " } else { "" };
let mut_annotation = if annotation.1 == Mutability::Mut { "mut " } else { "" };
span_lint_and_sugg( span_lint_and_sugg(
cx, cx,
ITER_KV_MAP, ITER_KV_MAP,
@ -69,7 +67,8 @@ pub(super) fn check<'tcx>(
&format!("iterating on a map's {replacement_kind}s"), &format!("iterating on a map's {replacement_kind}s"),
"try", "try",
format!( format!(
"{recv_snippet}.{into_prefix}{replacement_kind}s().map(|{ref_annotation}{mut_annotation}{bound_ident}| {})", "{recv_snippet}.{into_prefix}{replacement_kind}s().map(|{}{bound_ident}| {})",
annotation.prefix_str(),
snippet_with_applicability(cx, body_expr.span, "/* body */", &mut applicability) snippet_with_applicability(cx, body_expr.span, "/* body */", &mut applicability)
), ),
applicability, applicability,

View File

@ -129,7 +129,7 @@ impl<'tcx> LateLintPass<'tcx> for LintPass {
if !is_lint_allowed(cx, REF_PATTERNS, arg.pat.hir_id) { if !is_lint_allowed(cx, REF_PATTERNS, arg.pat.hir_id) {
return; return;
} }
if let PatKind::Binding(BindingAnnotation(ByRef::Yes, _), ..) = arg.pat.kind { if let PatKind::Binding(BindingAnnotation(ByRef::Yes(_), _), ..) = arg.pat.kind {
span_lint( span_lint(
cx, cx,
TOPLEVEL_REF_ARG, TOPLEVEL_REF_ARG,
@ -144,7 +144,7 @@ impl<'tcx> LateLintPass<'tcx> for LintPass {
fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
if !in_external_macro(cx.tcx.sess, stmt.span) if !in_external_macro(cx.tcx.sess, stmt.span)
&& let StmtKind::Let(local) = stmt.kind && let StmtKind::Let(local) = stmt.kind
&& let PatKind::Binding(BindingAnnotation(ByRef::Yes, mutabl), .., name, None) = local.pat.kind && let PatKind::Binding(BindingAnnotation(ByRef::Yes(mutabl), _), .., name, None) = local.pat.kind
&& let Some(init) = local.init && let Some(init) = local.init
// Do not emit if clippy::ref_patterns is not allowed to avoid having two lints for the same issue. // Do not emit if clippy::ref_patterns is not allowed to avoid having two lints for the same issue.
&& is_lint_allowed(cx, REF_PATTERNS, local.pat.hir_id) && is_lint_allowed(cx, REF_PATTERNS, local.pat.hir_id)

View File

@ -14,7 +14,7 @@ use rustc_errors::Applicability;
use rustc_hir::def::Res; use rustc_hir::def::Res;
use rustc_hir::LangItem::{self, OptionNone, OptionSome, ResultErr, ResultOk}; use rustc_hir::LangItem::{self, OptionNone, OptionSome, ResultErr, ResultOk};
use rustc_hir::{ use rustc_hir::{
BindingAnnotation, Block, ByRef, Expr, ExprKind, LetStmt, Node, PatKind, PathSegment, QPath, Stmt, StmtKind, BindingAnnotation, Block, ByRef, Expr, ExprKind, LetStmt, Mutability, Node, PatKind, PathSegment, QPath, Stmt, StmtKind,
}; };
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::Ty; use rustc_middle::ty::Ty;
@ -283,9 +283,13 @@ fn check_if_let_some_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr:
let mut applicability = Applicability::MachineApplicable; let mut applicability = Applicability::MachineApplicable;
let receiver_str = snippet_with_applicability(cx, let_expr.span, "..", &mut applicability); let receiver_str = snippet_with_applicability(cx, let_expr.span, "..", &mut applicability);
let requires_semi = matches!(cx.tcx.parent_hir_node(expr.hir_id), Node::Stmt(_)); let requires_semi = matches!(cx.tcx.parent_hir_node(expr.hir_id), Node::Stmt(_));
let method_call_str = match by_ref {
ByRef::Yes(Mutability::Mut) => ".as_mut()",
ByRef::Yes(Mutability::Not) => ".as_ref()",
ByRef::No => "",
};
let sugg = format!( let sugg = format!(
"{receiver_str}{}?{}", "{receiver_str}{method_call_str}?{}",
if by_ref == ByRef::Yes { ".as_ref()" } else { "" },
if requires_semi { ";" } else { "" } if requires_semi { ";" } else { "" }
); );
span_lint_and_sugg( span_lint_and_sugg(

View File

@ -649,6 +649,8 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
BindingAnnotation::REF => "REF", BindingAnnotation::REF => "REF",
BindingAnnotation::MUT => "MUT", BindingAnnotation::MUT => "MUT",
BindingAnnotation::REF_MUT => "REF_MUT", BindingAnnotation::REF_MUT => "REF_MUT",
BindingAnnotation::MUT_REF => "MUT_REF",
BindingAnnotation::MUT_REF_MUT => "MUT_REF_MUT",
}; };
kind!("Binding(BindingAnnotation::{ann}, _, {name}, {sub})"); kind!("Binding(BindingAnnotation::{ann}, _, {name}, {sub})");
self.ident(name); self.ident(name);

View File

@ -97,7 +97,7 @@ use rustc_hir::hir_id::{HirIdMap, HirIdSet};
use rustc_hir::intravisit::{walk_expr, FnKind, Visitor}; use rustc_hir::intravisit::{walk_expr, FnKind, Visitor};
use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk}; use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk};
use rustc_hir::{ use rustc_hir::{
self as hir, def, Arm, ArrayLen, BindingAnnotation, Block, BlockCheckMode, Body, Closure, Destination, Expr, self as hir, def, Arm, ArrayLen, BindingAnnotation, Block, BlockCheckMode, Body, ByRef, Closure, Destination, Expr,
ExprField, ExprKind, FnDecl, FnRetTy, GenericArgs, HirId, Impl, ImplItem, ImplItemKind, ImplItemRef, Item, ExprField, ExprKind, FnDecl, FnRetTy, GenericArgs, HirId, Impl, ImplItem, ImplItemKind, ImplItemRef, Item,
ItemKind, LangItem, LetStmt, MatchSource, Mutability, Node, OwnerId, Param, Pat, PatKind, Path, PathSegment, PrimTy, ItemKind, LangItem, LetStmt, MatchSource, Mutability, Node, OwnerId, Param, Pat, PatKind, Path, PathSegment, PrimTy,
QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitItemRef, TraitRef, TyKind, UnOp, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitItemRef, TraitRef, TyKind, UnOp,
@ -107,7 +107,6 @@ use rustc_lint::{LateContext, Level, Lint, LintContext};
use rustc_middle::hir::place::PlaceBase; use rustc_middle::hir::place::PlaceBase;
use rustc_middle::mir::Const; use rustc_middle::mir::Const;
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow}; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
use rustc_middle::ty::binding::BindingMode;
use rustc_middle::ty::fast_reject::SimplifiedType; use rustc_middle::ty::fast_reject::SimplifiedType;
use rustc_middle::ty::layout::IntegerExt; use rustc_middle::ty::layout::IntegerExt;
use rustc_middle::ty::{ use rustc_middle::ty::{
@ -1006,11 +1005,12 @@ pub fn capture_local_usage(cx: &LateContext<'_>, e: &Expr<'_>) -> CaptureKind {
.typeck_results() .typeck_results()
.extract_binding_mode(cx.sess(), id, span) .extract_binding_mode(cx.sess(), id, span)
.unwrap() .unwrap()
.0
{ {
BindingMode::BindByValue(_) if !is_copy(cx, cx.typeck_results().node_type(id)) => { ByRef::No if !is_copy(cx, cx.typeck_results().node_type(id)) => {
capture = CaptureKind::Value; capture = CaptureKind::Value;
}, },
BindingMode::BindByReference(Mutability::Mut) if capture != CaptureKind::Value => { ByRef::Yes(Mutability::Mut) if capture != CaptureKind::Value => {
capture = CaptureKind::Ref(Mutability::Mut); capture = CaptureKind::Ref(Mutability::Mut);
}, },
_ => (), _ => (),
@ -2035,7 +2035,7 @@ fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool {
.typeck_results() .typeck_results()
.pat_binding_modes() .pat_binding_modes()
.get(pat.hir_id) .get(pat.hir_id)
.is_some_and(|mode| matches!(mode, BindingMode::BindByReference(_))) .is_some_and(|mode| matches!(mode.0, ByRef::Yes(_)))
{ {
// If a tuple `(x, y)` is of type `&(i32, i32)`, then due to match ergonomics, // If a tuple `(x, y)` is of type `&(i32, i32)`, then due to match ergonomics,
// the inner patterns become references. Don't consider this the identity function // the inner patterns become references. Don't consider this the identity function

View File

@ -107,18 +107,19 @@ impl Rewrite for Pat {
} }
PatKind::Box(ref pat) => rewrite_unary_prefix(context, "box ", &**pat, shape), PatKind::Box(ref pat) => rewrite_unary_prefix(context, "box ", &**pat, shape),
PatKind::Ident(BindingAnnotation(by_ref, mutability), ident, ref sub_pat) => { PatKind::Ident(BindingAnnotation(by_ref, mutability), ident, ref sub_pat) => {
let prefix = match by_ref { let mut_prefix = format_mutability(mutability).trim();
ByRef::Yes => "ref",
ByRef::No => "", let (ref_kw, mut_infix) = match by_ref {
ByRef::Yes(rmutbl) => ("ref", format_mutability(rmutbl).trim()),
ByRef::No => ("", ""),
}; };
let mut_infix = format_mutability(mutability).trim();
let id_str = rewrite_ident(context, ident); let id_str = rewrite_ident(context, ident);
let sub_pat = match *sub_pat { let sub_pat = match *sub_pat {
Some(ref p) => { Some(ref p) => {
// 2 - `@ `. // 2 - `@ `.
let width = shape let width = shape.width.checked_sub(
.width mut_prefix.len() + ref_kw.len() + mut_infix.len() + id_str.len() + 2,
.checked_sub(prefix.len() + mut_infix.len() + id_str.len() + 2)?; )?;
let lo = context.snippet_provider.span_after(self.span, "@"); let lo = context.snippet_provider.span_after(self.span, "@");
combine_strs_with_missing_comments( combine_strs_with_missing_comments(
context, context,
@ -132,33 +133,55 @@ impl Rewrite for Pat {
None => "".to_owned(), None => "".to_owned(),
}; };
// combine prefix and mut // combine prefix and ref
let (first_lo, first) = if !prefix.is_empty() && !mut_infix.is_empty() { let (first_lo, first) = match (mut_prefix.is_empty(), ref_kw.is_empty()) {
let hi = context.snippet_provider.span_before(self.span, "mut"); (false, false) => {
let lo = context.snippet_provider.span_after(self.span, "ref"); let lo = context.snippet_provider.span_after(self.span, "mut");
( let hi = context.snippet_provider.span_before(self.span, "ref");
(
context.snippet_provider.span_after(self.span, "ref"),
combine_strs_with_missing_comments(
context,
mut_prefix,
ref_kw,
mk_sp(lo, hi),
shape,
true,
)?,
)
}
(false, true) => (
context.snippet_provider.span_after(self.span, "mut"), context.snippet_provider.span_after(self.span, "mut"),
combine_strs_with_missing_comments( mut_prefix.to_owned(),
context, ),
prefix, (true, false) => (
mut_infix,
mk_sp(lo, hi),
shape,
true,
)?,
)
} else if !prefix.is_empty() {
(
context.snippet_provider.span_after(self.span, "ref"), context.snippet_provider.span_after(self.span, "ref"),
prefix.to_owned(), ref_kw.to_owned(),
) ),
} else if !mut_infix.is_empty() { (true, true) => (self.span.lo(), "".to_owned()),
( };
context.snippet_provider.span_after(self.span, "mut"),
mut_infix.to_owned(), // combine result of above and mut
) let (second_lo, second) = match (first.is_empty(), mut_infix.is_empty()) {
} else { (false, false) => {
(self.span.lo(), "".to_owned()) let lo = context.snippet_provider.span_after(self.span, "ref");
let end_span = mk_sp(first_lo, self.span.hi());
let hi = context.snippet_provider.span_before(end_span, "mut");
(
context.snippet_provider.span_after(end_span, "mut"),
combine_strs_with_missing_comments(
context,
&first,
mut_infix,
mk_sp(lo, hi),
shape,
true,
)?,
)
}
(false, true) => (first_lo, first),
(true, false) => unreachable!("mut_infix necessarily follows a ref"),
(true, true) => (self.span.lo(), "".to_owned()),
}; };
let next = if !sub_pat.is_empty() { let next = if !sub_pat.is_empty() {
@ -177,9 +200,9 @@ impl Rewrite for Pat {
combine_strs_with_missing_comments( combine_strs_with_missing_comments(
context, context,
&first, &second,
&next, &next,
mk_sp(first_lo, ident.span.lo()), mk_sp(second_lo, ident.span.lo()),
shape, shape,
true, true,
) )

View File

@ -0,0 +1,10 @@
#![feature(mut_ref)]
fn mut_ref() {
if let Some(mut /*a*/ ref /*def*/ mut /*abc*/ state)= /*abc*/foo{
println!(
"asdfasdfasdf"); }
if let Some(mut /*a*/ ref /*def*/ /*mut*/ state)= /*abc*/foo{
println!(
"asdfasdfasdf"); }
}

View File

@ -0,0 +1,10 @@
#![feature(mut_ref)]
fn mut_ref() {
if let Some(mut /*a*/ ref /*def*/ mut /*abc*/ state) = /*abc*/ foo {
println!("asdfasdfasdf");
}
if let Some(mut /*a*/ ref /*def*/ /*mut*/ state) = /*abc*/ foo {
println!("asdfasdfasdf");
}
}

View File

@ -0,0 +1,13 @@
fn main() {
let mut ref x = 10; //~ ERROR [E0658]
x = &11;
let ref mut y = 12;
*y = 13;
let mut ref mut z = 14; //~ ERROR [E0658]
z = &mut 15;
#[cfg(FALSE)]
let mut ref x = 10; //~ ERROR [E0658]
#[cfg(FALSE)]
let mut ref mut y = 10; //~ ERROR [E0658]
}

View File

@ -0,0 +1,43 @@
error[E0658]: mutable by-reference bindings are experimental
--> $DIR/feature-gate-mut-ref.rs:2:17
|
LL | let mut ref x = 10;
| ^
|
= note: see issue #123076 <https://github.com/rust-lang/rust/issues/123076> for more information
= help: add `#![feature(mut_ref)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: mutable by-reference bindings are experimental
--> $DIR/feature-gate-mut-ref.rs:6:21
|
LL | let mut ref mut z = 14;
| ^
|
= note: see issue #123076 <https://github.com/rust-lang/rust/issues/123076> for more information
= help: add `#![feature(mut_ref)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: mutable by-reference bindings are experimental
--> $DIR/feature-gate-mut-ref.rs:10:17
|
LL | let mut ref x = 10;
| ^
|
= note: see issue #123076 <https://github.com/rust-lang/rust/issues/123076> for more information
= help: add `#![feature(mut_ref)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: mutable by-reference bindings are experimental
--> $DIR/feature-gate-mut-ref.rs:12:21
|
LL | let mut ref mut y = 10;
| ^
|
= note: see issue #123076 <https://github.com/rust-lang/rust/issues/123076> for more information
= help: add `#![feature(mut_ref)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error: aborting due to 4 previous errors
For more information about this error, try `rustc --explain E0658`.

View File

@ -1,4 +1,11 @@
//@ check-pass
#![allow(incomplete_features)]
#![feature(mut_ref)]
fn main() { fn main() {
let mut ref x = 10; //~ ERROR the order of `mut` and `ref` is incorrect let mut ref x = 10;
let ref mut y = 11; x = &11;
let ref mut y = 12;
*y = 13;
let mut ref mut z = 14;
z = &mut 15;
} }

View File

@ -1,8 +0,0 @@
error: the order of `mut` and `ref` is incorrect
--> $DIR/mut-ref.rs:2:9
|
LL | let mut ref x = 10;
| ^^^^^^^ help: try switching the order: `ref mut`
error: aborting due to 1 previous error

View File

@ -0,0 +1,55 @@
//@ edition: 2021
#![allow(incomplete_features)]
#![feature(mut_ref)]
struct Foo(u8);
fn main() {
let Foo(a) = Foo(0);
a = 42; //~ ERROR [E0384]
let Foo(mut a) = Foo(0);
a = 42;
let Foo(ref a) = Foo(0);
a = &42; //~ ERROR [E0384]
let Foo(mut ref a) = Foo(0);
a = &42;
let Foo(ref mut a) = Foo(0);
a = &mut 42; //~ ERROR [E0384]
let Foo(mut ref mut a) = Foo(0);
a = &mut 42;
let Foo(a) = &Foo(0);
a = &42; //~ ERROR [E0384]
let Foo(mut a) = &Foo(0);
a = 42;
let Foo(ref a) = &Foo(0);
a = &42; //~ ERROR [E0384]
let Foo(mut ref a) = &Foo(0);
a = &42;
let Foo(a) = &mut Foo(0);
a = &mut 42; //~ ERROR [E0384]
let Foo(mut a) = &mut Foo(0);
a = 42;
let Foo(ref a) = &mut Foo(0);
a = &42; //~ ERROR [E0384]
let Foo(mut ref a) = &mut Foo(0);
a = &42;
let Foo(ref mut a) = &mut Foo(0);
a = &mut 42; //~ ERROR [E0384]
let Foo(mut ref mut a) = &mut Foo(0);
a = &mut 42;
}

View File

@ -0,0 +1,70 @@
error[E0384]: cannot assign twice to immutable variable `a`
--> $DIR/mut-ref-mut-2021.rs:9:5
|
LL | let Foo(a) = Foo(0);
| -
| |
| first assignment to `a`
| help: consider making this binding mutable: `mut a`
LL | a = 42;
| ^^^^^^ cannot assign twice to immutable variable
error[E0384]: cannot assign twice to immutable variable `a`
--> $DIR/mut-ref-mut-2021.rs:15:5
|
LL | let Foo(ref a) = Foo(0);
| ----- first assignment to `a`
LL | a = &42;
| ^^^^^^^ cannot assign twice to immutable variable
error[E0384]: cannot assign twice to immutable variable `a`
--> $DIR/mut-ref-mut-2021.rs:21:5
|
LL | let Foo(ref mut a) = Foo(0);
| --------- first assignment to `a`
LL | a = &mut 42;
| ^^^^^^^^^^^ cannot assign twice to immutable variable
error[E0384]: cannot assign twice to immutable variable `a`
--> $DIR/mut-ref-mut-2021.rs:27:5
|
LL | let Foo(a) = &Foo(0);
| - first assignment to `a`
LL | a = &42;
| ^^^^^^^ cannot assign twice to immutable variable
error[E0384]: cannot assign twice to immutable variable `a`
--> $DIR/mut-ref-mut-2021.rs:33:5
|
LL | let Foo(ref a) = &Foo(0);
| ----- first assignment to `a`
LL | a = &42;
| ^^^^^^^ cannot assign twice to immutable variable
error[E0384]: cannot assign twice to immutable variable `a`
--> $DIR/mut-ref-mut-2021.rs:39:5
|
LL | let Foo(a) = &mut Foo(0);
| - first assignment to `a`
LL | a = &mut 42;
| ^^^^^^^^^^^ cannot assign twice to immutable variable
error[E0384]: cannot assign twice to immutable variable `a`
--> $DIR/mut-ref-mut-2021.rs:45:5
|
LL | let Foo(ref a) = &mut Foo(0);
| ----- first assignment to `a`
LL | a = &42;
| ^^^^^^^ cannot assign twice to immutable variable
error[E0384]: cannot assign twice to immutable variable `a`
--> $DIR/mut-ref-mut-2021.rs:51:5
|
LL | let Foo(ref mut a) = &mut Foo(0);
| --------- first assignment to `a`
LL | a = &mut 42;
| ^^^^^^^^^^^ cannot assign twice to immutable variable
error: aborting due to 8 previous errors
For more information about this error, try `rustc --explain E0384`.

View File

@ -11,9 +11,8 @@ params: [
span: $DIR/thir-tree-match.rs:15:14: 15:17 (#0) span: $DIR/thir-tree-match.rs:15:14: 15:17 (#0)
kind: PatKind { kind: PatKind {
Binding { Binding {
mutability: Not
name: "foo" name: "foo"
mode: ByValue mode: BindingAnnotation(No, Not)
var: LocalVarId(HirId(DefId(0:16 ~ thir_tree_match[fcf8]::has_match).2)) var: LocalVarId(HirId(DefId(0:16 ~ thir_tree_match[fcf8]::has_match).2))
ty: Foo ty: Foo
is_primary: true is_primary: true