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(Encodable, Decodable, HashStable_Generic)]
pub enum ByRef {
Yes,
Yes(Mutability),
No,
}
impl From<bool> for ByRef {
fn from(b: bool) -> ByRef {
match b {
false => ByRef::No,
true => ByRef::Yes,
}
}
}
/// Explicit binding annotations given in the HIR for a binding. Note
/// that this is not the final binding *mode* that we infer after type
/// inference.
@ -724,9 +715,11 @@ pub struct BindingAnnotation(pub ByRef, pub Mutability);
impl BindingAnnotation {
pub const NONE: Self = Self(ByRef::No, Mutability::Not);
pub const REF: Self = Self(ByRef::Yes, Mutability::Not);
pub const REF: Self = Self(ByRef::Yes(Mutability::Not), Mutability::Not);
pub const MUT: Self = Self(ByRef::No, Mutability::Mut);
pub const REF_MUT: Self = Self(ByRef::Yes, Mutability::Mut);
pub const REF_MUT: Self = Self(ByRef::Yes(Mutability::Mut), Mutability::Not);
pub const MUT_REF: Self = Self(ByRef::Yes(Mutability::Not), Mutability::Mut);
pub const MUT_REF_MUT: Self = Self(ByRef::Yes(Mutability::Mut), Mutability::Mut);
pub fn prefix_str(self) -> &'static str {
match self {
@ -734,6 +727,8 @@ impl BindingAnnotation {
Self::REF => "ref ",
Self::MUT => "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
// no longer be an `ImplicitSelf`.
TyKind::Ref(_, mt) if mt.ty.kind.is_implicit_self() => match mt.mutbl {
hir::Mutability::Not => hir::ImplicitSelfKind::ImmRef,
hir::Mutability::Mut => hir::ImplicitSelfKind::MutRef,
hir::Mutability::Not => hir::ImplicitSelfKind::RefImm,
hir::Mutability::Mut => hir::ImplicitSelfKind::RefMut,
},
_ => hir::ImplicitSelfKind::None,
}

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

View File

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

View File

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

View File

@ -529,6 +529,8 @@ declare_features! (
(unstable, more_qualified_paths, "1.54.0", Some(86935)),
/// Allows the `#[must_not_suspend]` attribute.
(unstable, must_not_suspend, "1.57.0", Some(83310)),
/// Allows `mut ref` and `mut ref mut` identifier patterns.
(incomplete, mut_ref, "CURRENT_RUSTC_VERSION", Some(123076)),
/// Allows using `#[naked]` on functions.
(unstable, naked_functions, "1.9.0", Some(90957)),
/// Allows specifying the as-needed link modifier

View File

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

View File

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

View File

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

View File

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

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
// of the pattern, as this just looks confusing, instead use the span
// of the discriminant.
match bm {
ty::BindByReference(m) => {
match bm.0 {
hir::ByRef::Yes(m) => {
let bk = ty::BorrowKind::from_mutbl(m);
delegate.borrow(place, discr_place.hir_id, bk);
}
ty::BindByValue(..) => {
hir::ByRef::No => {
debug!("walk_pat binding consuming pat");
delegate_consume(mc, *delegate, place, discr_place.hir_id);
}

View File

@ -206,7 +206,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
.get(pat.hir_id)
.expect("missing binding mode");
if let ty::BindByReference(_) = bm {
if matches!(bm.0, hir::ByRef::Yes(_)) {
// a bind-by-ref means that the base_ty will be the type of the ident itself,
// but what we want here is the type of the underlying value being borrowed.
// So peel off one-level, turning the &T into T.

View File

@ -5,14 +5,13 @@ use rustc_data_structures::fx::FxHashMap;
use rustc_errors::{
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::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::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
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_span::edit_distance::find_best_match_for_name;
use rustc_span::hygiene::DesugaringKind;
@ -79,7 +78,7 @@ struct TopInfo<'tcx> {
#[derive(Copy, Clone)]
struct PatInfo<'tcx, 'a> {
binding_mode: BindingMode,
binding_mode: BindingAnnotation,
top_info: TopInfo<'tcx>,
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.
enum AdjustMode {
@ -269,9 +268,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
&self,
pat: &'tcx Pat<'tcx>,
expected: Ty<'tcx>,
def_bm: BindingMode,
def_bm: BindingAnnotation,
adjust_mode: AdjustMode,
) -> (Ty<'tcx>, BindingMode) {
) -> (Ty<'tcx>, BindingAnnotation) {
match adjust_mode {
AdjustMode::Pass => (expected, def_bm),
AdjustMode::Reset => (expected, INITIAL_BM),
@ -354,8 +353,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
&self,
pat: &'tcx Pat<'tcx>,
expected: Ty<'tcx>,
mut def_bm: BindingMode,
) -> (Ty<'tcx>, BindingMode) {
mut def_bm: BindingAnnotation,
) -> (Ty<'tcx>, BindingAnnotation) {
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,
// 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);
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`
// (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 `&`).
ty::BindByReference(hir::Mutability::Mut) => inner_mutability,
ByRef::Yes(Mutability::Mut) => inner_mutability,
// Once a `ref`, always a `ref`.
// 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(
&self,
pat: &'tcx Pat<'tcx>,
ba: hir::BindingAnnotation,
ba: BindingAnnotation,
var_id: HirId,
sub: Option<&'tcx Pat<'tcx>>,
expected: Ty<'tcx>,
@ -609,8 +608,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// Determine the binding mode...
let bm = match ba {
hir::BindingAnnotation::NONE => def_bm,
_ => BindingMode::convert(ba),
BindingAnnotation(ByRef::No, Mutability::Not) => def_bm,
_ => ba,
};
// ...and store it in a side table:
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);
let local_ty = self.local_ty(pat.span, pat.hir_id);
let eq_ty = match bm {
ty::BindByReference(mutbl) => {
let eq_ty = match bm.0 {
ByRef::Yes(mutbl) => {
// If the binding is like `ref x | ref mut x`,
// then `x` is assigned a value of type `&M T` where M is the
// 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)
}
// Otherwise, the type of x is the expected type `T`.
ty::BindByValue(_) => {
// As above, `T <: typeof(x)` is required, but we use equality, see (note_1).
expected
}
ByRef::No => expected, // As above, `T <: typeof(x)` is required, but we use equality, see (note_1).
};
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.
fn check_binding_alt_eq_ty(
&self,
ba: hir::BindingAnnotation,
ba: BindingAnnotation,
span: Span,
var_id: HirId,
ty: Ty<'tcx>,
@ -695,10 +691,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
span: Span,
expected: Ty<'tcx>,
actual: Ty<'tcx>,
ba: hir::BindingAnnotation,
ba: BindingAnnotation,
) {
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) =>
{
err.span_suggestion_verbose(
@ -708,7 +704,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
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) =>
{
err.span_suggestion_verbose(
@ -800,7 +796,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
if let PatKind::Ref(the_ref, _) = i.kind
&& let PatKind::Binding(mt, _, ident, _) = the_ref.kind
{
let hir::BindingAnnotation(_, mtblty) = mt;
let BindingAnnotation(_, mtblty) = mt;
err.span_suggestion_verbose(
i.span,
format!("consider removing `&{mutability}` from the pattern"),
@ -2037,7 +2033,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
&self,
pat: &'tcx Pat<'tcx>,
inner: &'tcx Pat<'tcx>,
mutbl: hir::Mutability,
mutbl: Mutability,
expected: Ty<'tcx>,
pat_info: PatInfo<'tcx, '_>,
) -> Ty<'tcx> {
@ -2088,7 +2084,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
/// 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));
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 mut is_mutbl = match bm {
ty::BindByValue(mutability) => mutability,
ty::BindByReference(_) => hir::Mutability::Not,
};
let mut is_mutbl = bm.1;
for pointer_ty in place.deref_tys() {
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_hir::def::{CtorKind, Namespace};
use rustc_hir::def_id::{DefId, CRATE_DEF_ID};
use rustc_hir::{self, CoroutineDesugaring, CoroutineKind, ImplicitSelfKind};
use rustc_hir::{self as hir, HirId};
use rustc_hir::{
self as hir, BindingAnnotation, ByRef, CoroutineDesugaring, CoroutineKind, HirId,
ImplicitSelfKind,
};
use rustc_session::Session;
use rustc_span::source_map::Spanned;
use rustc_target::abi::{FieldIdx, VariantIdx};
@ -992,8 +994,8 @@ pub enum LocalKind {
#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable)]
pub struct VarBindingForm<'tcx> {
/// Is variable bound via `x`, `mut x`, `ref x`, or `ref mut x`?
pub binding_mode: ty::BindingMode,
/// Is variable bound via `x`, `mut x`, `ref x`, `ref mut x`, `mut ref x`, or `mut ref mut x`?
pub binding_mode: BindingAnnotation,
/// If an explicit type was provided for this variable binding,
/// this holds the source Span of that type.
///
@ -1218,7 +1220,7 @@ impl<'tcx> LocalDecl<'tcx> {
self.local_info(),
LocalInfo::User(
BindingForm::Var(VarBindingForm {
binding_mode: ty::BindingMode::BindByValue(_),
binding_mode: BindingAnnotation(ByRef::No, _),
opt_ty_info: _,
opt_match_place: _,
pat_span: _,
@ -1235,7 +1237,7 @@ impl<'tcx> LocalDecl<'tcx> {
self.local_info(),
LocalInfo::User(
BindingForm::Var(VarBindingForm {
binding_mode: ty::BindingMode::BindByValue(_),
binding_mode: BindingAnnotation(ByRef::No, _),
opt_ty_info: _,
opt_match_place: _,
pat_span: _,

View File

@ -12,12 +12,12 @@ use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece};
use rustc_errors::{DiagArgValue, IntoDiagArg};
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_hir::RangeEnd;
use rustc_hir::{BindingAnnotation, ByRef, RangeEnd};
use rustc_index::newtype_index;
use rustc_index::IndexVec;
use rustc_middle::middle::region;
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::layout::IntegerExt;
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)]
pub struct FieldPat<'tcx> {
pub field: FieldIdx,
@ -607,19 +601,22 @@ impl<'tcx> Pat<'tcx> {
pub fn simple_ident(&self) -> Option<Symbol> {
match self.kind {
PatKind::Binding { name, mode: BindingMode::ByValue, subpattern: None, .. } => {
Some(name)
}
PatKind::Binding {
name,
mode: BindingAnnotation(ByRef::No, _),
subpattern: None,
..
} => Some(name),
_ => None,
}
}
/// Call `f` on every "binding" in a pattern, e.g., on `a` in
/// `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| {
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.
Binding {
mutability: Mutability,
name: Symbol,
#[type_visitable(ignore)]
mode: BindingMode,
mode: BindingAnnotation,
#[type_visitable(ignore)]
var: LocalVarId,
ty: Ty<'tcx>,
@ -1073,17 +1069,8 @@ impl<'tcx> fmt::Display for Pat<'tcx> {
PatKind::Wild => write!(f, "_"),
PatKind::Never => write!(f, "!"),
PatKind::AscribeUserType { ref subpattern, .. } => write!(f, "{subpattern}: _"),
PatKind::Binding { mutability, name, mode, ref subpattern, .. } => {
let is_mut = match mode {
BindingMode::ByValue => mutability == Mutability::Mut,
BindingMode::ByRef(bk) => {
write!(f, "ref ")?;
matches!(bk, BorrowKind::Mut { .. })
}
};
if is_mut {
write!(f, "mut ")?;
}
PatKind::Binding { name, mode, ref subpattern, .. } => {
f.write_str(mode.prefix_str())?;
write!(f, "{name}")?;
if let Some(ref subpattern) = *subpattern {
write!(f, " @ {subpattern}")?;

View File

@ -230,15 +230,7 @@ pub fn walk_pat<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>(
AscribeUserType { subpattern, ascription: _ }
| Deref { subpattern }
| DerefPattern { subpattern }
| Binding {
subpattern: Some(subpattern),
mutability: _,
mode: _,
var: _,
ty: _,
is_primary: _,
name: _,
} => visitor.visit_pat(subpattern),
| Binding { subpattern: Some(subpattern), .. } => visitor.visit_pat(subpattern),
Binding { .. } | Wild | Never | Error(_) => {}
Variant { subpatterns, adt_def: _, args: _, variant_index: _ } | Leaf { 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 self::binding::BindingMode;
pub use self::binding::BindingMode::*;
pub use self::closure::{
is_ancestor_or_same_capture, place_to_string_for_capture, BorrowKind, CaptureInfo,
CapturedPlace, ClosureTypeInfo, MinCaptureInformationMap, MinCaptureList,
@ -123,7 +121,6 @@ pub use self::typeck_results::{
pub mod _match;
pub mod abstract_const;
pub mod adjustment;
pub mod binding;
pub mod cast;
pub mod codec;
pub mod error;

View File

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

View File

@ -3,8 +3,8 @@ use crate::{
infer::canonical::Canonical,
traits::ObligationCause,
ty::{
self, tls, BindingMode, BoundVar, CanonicalPolyFnSig, ClosureSizeProfileData,
GenericArgKind, GenericArgs, GenericArgsRef, Ty, UserArgs,
self, tls, BoundVar, CanonicalPolyFnSig, ClosureSizeProfileData, GenericArgKind,
GenericArgs, GenericArgsRef, Ty, UserArgs,
},
};
use rustc_data_structures::{
@ -12,12 +12,12 @@ use rustc_data_structures::{
unord::{ExtendUnord, UnordItems, UnordSet},
};
use rustc_errors::ErrorGuaranteed;
use rustc_hir as hir;
use rustc_hir::{
self as hir,
def::{DefKind, Res},
def_id::{DefId, LocalDefId, LocalDefIdMap},
hir_id::OwnerId,
HirId, ItemLocalId, ItemLocalMap, ItemLocalSet,
BindingAnnotation, ByRef, HirId, ItemLocalId, ItemLocalMap, ItemLocalSet, Mutability,
};
use rustc_index::{Idx, IndexVec};
use rustc_macros::HashStable;
@ -78,8 +78,8 @@ pub struct TypeckResults<'tcx> {
adjustments: ItemLocalMap<Vec<ty::adjustment::Adjustment<'tcx>>>,
/// Stores the actual binding mode for all instances of hir::BindingAnnotation.
pat_binding_modes: ItemLocalMap<BindingMode>,
/// Stores the actual binding mode for all instances of [`BindingAnnotation`].
pat_binding_modes: ItemLocalMap<BindingAnnotation>,
/// Stores the types which were implicitly dereferenced in pattern binding modes
/// 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, _))))
}
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(|| {
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 }
}
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 }
}
@ -442,7 +447,7 @@ impl<'tcx> TypeckResults<'tcx> {
let mut has_ref_mut = false;
pat.walk(|pat| {
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)
{
has_ref_mut = true;

View File

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

View File

@ -14,6 +14,7 @@ use rustc_data_structures::{
fx::{FxHashSet, FxIndexMap, FxIndexSet},
stack::ensure_sufficient_stack,
};
use rustc_hir::{BindingAnnotation, ByRef};
use rustc_middle::middle::region;
use rustc_middle::mir::{self, *};
use rustc_middle::thir::{self, *};
@ -554,7 +555,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
) -> BlockAnd<()> {
match irrefutable_pat.kind {
// 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 =
self.storage_live_binding(block, var, irrefutable_pat.span, OutsideGuard, true);
unpack!(block = self.expr_into_dest(place, block, initializer_id));
@ -580,7 +586,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
box Pat {
kind:
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(
pattern,
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() {
visibility_scope =
Some(this.new_source_scope(scope_span, LintLevel::Inherited, None));
@ -730,7 +739,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
this.declare_binding(
source_info,
visibility_scope,
mutability,
name,
mode,
var,
@ -818,9 +826,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
pattern_user_ty: UserTypeProjections,
f: &mut impl FnMut(
&mut Self,
Mutability,
Symbol,
BindingMode,
BindingAnnotation,
LocalVarId,
Span,
Ty<'tcx>,
@ -832,18 +839,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
pattern, pattern_user_ty
);
match pattern.kind {
PatKind::Binding {
mutability,
name,
mode,
var,
ty,
ref subpattern,
is_primary,
..
} => {
PatKind::Binding { name, mode, var, ty, ref subpattern, 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() {
self.visit_primary_bindings(subpattern, pattern_user_ty, f);
@ -1079,7 +1077,7 @@ struct Binding<'tcx> {
span: Span,
source: Place<'tcx>,
var_id: LocalVarId,
binding_mode: BindingMode,
binding_mode: BindingAnnotation,
}
/// 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);
self.bind_matched_candidate_for_guard(block, schedule_drops, bindings.clone());
let guard_frame = GuardFrame {
locals: bindings.map(|b| GuardFrameLocal::new(b.var_id, b.binding_mode)).collect(),
};
let guard_frame =
GuardFrame { locals: bindings.map(|b| GuardFrameLocal::new(b.var_id)).collect() };
debug!("entering guard building context: {:?}", guard_frame);
self.guard_context.push(guard_frame);
@ -2176,7 +2173,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
.iter()
.flat_map(|d| &d.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
// place they refer to can't be modified by the guard.
for binding in by_value_bindings.clone() {
@ -2263,12 +2260,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
RefWithinGuard,
schedule_drops,
);
match binding.binding_mode {
BindingMode::ByValue => {
match binding.binding_mode.0 {
ByRef::No => {
let rvalue = Rvalue::Ref(re_erased, BorrowKind::Shared, binding.source);
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(
block,
binding.var_id,
@ -2277,7 +2274,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
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);
let rvalue = Rvalue::Ref(re_erased, BorrowKind::Shared, value_for_arm);
self.cfg.push_assign(block, source_info, ref_for_guard, rvalue);
@ -2318,10 +2316,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
if schedule_drops {
self.schedule_drop_for_binding(binding.var_id, binding.span, OutsideGuard);
}
let rvalue = match binding.binding_mode {
BindingMode::ByValue => Rvalue::Use(self.consume_by_copy_or_move(binding.source)),
BindingMode::ByRef(borrow_kind) => {
Rvalue::Ref(re_erased, borrow_kind, binding.source)
let rvalue = match binding.binding_mode.0 {
ByRef::No => Rvalue::Use(self.consume_by_copy_or_move(binding.source)),
ByRef::Yes(mutbl) => {
Rvalue::Ref(re_erased, util::ref_pat_borrow_kind(mutbl), binding.source)
}
};
self.cfg.push_assign(block, source_info, local, rvalue);
@ -2338,9 +2336,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
&mut self,
source_info: SourceInfo,
visibility_scope: SourceScope,
mutability: Mutability,
name: Symbol,
mode: BindingMode,
mode: BindingAnnotation,
var_id: LocalVarId,
var_ty: Ty<'tcx>,
user_ty: UserTypeProjections,
@ -2350,18 +2347,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
) {
let tcx = self.tcx;
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 {
mutability,
mutability: mode.1,
ty: var_ty,
user_ty: if user_ty.is_empty() { None } else { Some(Box::new(user_ty)) },
source_info,
local_info: ClearCrossCrate::Set(Box::new(LocalInfo::User(BindingForm::Var(
VarBindingForm {
binding_mode,
binding_mode: mode,
// hypothetically, `visit_primary_bindings` could try to unzip
// an outermost hir::Ty as we descend, matching up
// 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 }
}
PatKind::Binding {
name: _,
mutability: _,
mode,
var,
ty: _,
ref subpattern,
is_primary: _,
} => {
PatKind::Binding { mode, var, ref subpattern, .. } => {
let binding = place.map(|source| super::Binding {
span: pattern.span,
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::sorted_map::SortedIndexMultiMap;
use rustc_errors::ErrorGuaranteed;
use rustc_hir as hir;
use rustc_hir::def::DefKind;
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::{Idx, IndexSlice, IndexVec};
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::*;
use rustc_middle::query::TyCtxtAt;
use rustc_middle::thir::{
self, BindingMode, ExprId, LintLevel, LocalVarId, Param, ParamId, PatKind, Thir,
};
use rustc_middle::thir::{self, ExprId, LintLevel, LocalVarId, Param, ParamId, PatKind, Thir};
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt};
use rustc_span::symbol::sym;
use rustc_span::Span;
@ -337,7 +334,7 @@ struct GuardFrameLocal {
}
impl GuardFrameLocal {
fn new(id: LocalVarId, _binding_mode: BindingMode) -> Self {
fn new(id: LocalVarId) -> Self {
GuardFrameLocal { id }
}
}
@ -967,9 +964,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
match pat.kind {
// Don't introduce extra copies for simple bindings
PatKind::Binding {
mutability,
var,
mode: BindingMode::ByValue,
mode: BindingAnnotation(ByRef::No, mutability),
subpattern: None,
..
} => {
@ -979,7 +975,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
if let Some(kind) = param.self_kind {
LocalInfo::User(BindingForm::ImplicitSelf(kind))
} else {
let binding_mode = ty::BindingMode::BindByValue(mutability);
let binding_mode = BindingAnnotation(ByRef::No, mutability);
LocalInfo::User(BindingForm::Var(VarBindingForm {
binding_mode,
opt_ty_info: param.ty_span,

View File

@ -2,11 +2,11 @@ use std::borrow::Cow;
use crate::build::ExprCategory;
use crate::errors::*;
use rustc_middle::thir::visit::Visitor;
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::thir::visit::Visitor;
use rustc_middle::thir::*;
use rustc_middle::ty::print::with_no_trimmed_paths;
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);
}
}
PatKind::Binding { mode: BindingMode::ByRef(borrow_kind), ty, .. } => {
PatKind::Binding { mode: BindingAnnotation(ByRef::Yes(rm), _), ty, .. } => {
if self.inside_adt {
let ty::Ref(_, ty, _) = ty.kind() else {
span_bug!(
pat.span,
"BindingMode::ByRef in pattern, but found non-reference type {}",
"ByRef::Yes in pattern, but found non-reference type {}",
ty
);
};
match borrow_kind {
BorrowKind::Fake | BorrowKind::Shared => {
match rm {
Mutability::Not => {
if !ty.is_freeze(self.tcx, self.param_env) {
self.requires_unsafe(pat.span, BorrowOfLayoutConstrainedField);
}
}
BorrowKind::Mut { .. } => {
Mutability::Mut { .. } => {
self.requires_unsafe(pat.span, MutationOfLayoutConstrainedField);
}
}

View File

@ -13,10 +13,9 @@ use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_errors::{
codes::*, struct_span_code_err, Applicability, Diag, ErrorGuaranteed, MultiSpan,
};
use rustc_hir as hir;
use rustc_hir::def::*;
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::thir::visit::Visitor;
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;
// Get the binding move, extract the mutability if by-ref.
let mut_outer = match mode {
BindingMode::ByValue if is_binding_by_move(ty) => {
let mut_outer = match mode.0 {
ByRef::No if is_binding_by_move(ty) => {
// We have `x @ pat` where `x` is by-move. Reject all borrows in `pat`.
let mut conflicts_ref = Vec::new();
sub.each_binding(|_, mode, _, span| match mode {
BindingMode::ByValue => {}
BindingMode::ByRef(_) => conflicts_ref.push(span),
sub.each_binding(|_, mode, _, span| {
if matches!(mode, ByRef::Yes(_)) {
conflicts_ref.push(span)
}
});
if !conflicts_ref.is_empty() {
sess.dcx().emit_err(BorrowOfMovedValue {
@ -742,8 +742,8 @@ fn check_borrow_conflicts_in_at_patterns<'tcx>(cx: &MatchVisitor<'_, 'tcx>, pat:
}
return;
}
BindingMode::ByValue => return,
BindingMode::ByRef(m) => m.mutability(),
ByRef::No => return,
ByRef::Yes(m) => m,
};
// 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();
sub.each_binding(|name, mode, ty, span| {
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`.
(Mutability::Not, Mutability::Not) => {}
// 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 })
}
},
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.
}
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 {
name,
mode: BindingMode::ByValue,
mutability: Mutability::Not,
mode: BindingAnnotation(ByRef::No, Mutability::Not),
subpattern: None,
ty,
..

View File

@ -9,15 +9,14 @@ use crate::errors::*;
use crate::thir::util::UserAnnotatedTyHelpers;
use rustc_errors::codes::*;
use rustc_hir as hir;
use rustc_hir::def::{CtorOf, DefKind, Res};
use rustc_hir::pat_util::EnumerateAndAdjustIterator;
use rustc_hir::RangeEnd;
use rustc_hir::{self as hir, RangeEnd};
use rustc_index::Idx;
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::{
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::{self, CanonicalUserTypeAnnotation, Ty, TyCtxt, TypeVisitableExt};
@ -281,26 +280,16 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
span = span.with_hi(ident_span.hi());
}
let bm = *self
let mode = *self
.typeck_results
.pat_binding_modes()
.get(pat.hir_id)
.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
// x's type, which is &T, where we want T (the type being matched).
let var_ty = ty;
if let ty::BindByReference(_) = bm {
if let hir::ByRef::Yes(_) = mode.0 {
if let ty::Ref(_, rty, _) = ty.kind() {
ty = *rty;
} else {
@ -309,7 +298,6 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
};
PatKind::Binding {
mutability,
mode,
name: ident.name,
var: LocalVarId(id),

View File

@ -635,9 +635,8 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> {
self.print_pat(subpattern, depth_lvl + 3);
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, format!("mutability: {:?}", mutability), 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!("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}
.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`
.suggestion = remove the unnecessary `let` keyword

View File

@ -2364,14 +2364,6 @@ pub(crate) struct UnexpectedLifetimeInPattern {
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)]
pub(crate) enum InvalidMutInPattern {
#[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::{TokenStream, TokenTree, TokenTreeCursor};
use rustc_ast::util::case::Case;
use rustc_ast::AttrId;
use rustc_ast::CoroutineKind;
use rustc_ast::DUMMY_NODE_ID;
use rustc_ast::{self as ast, AnonConst, Const, DelimArgs, Extern};
use rustc_ast::{AttrArgs, AttrArgsEq, Expr, ExprKind, Mutability, StrLit};
use rustc_ast::{HasAttrs, HasTokens, Unsafe, Visibility, VisibilityKind};
use rustc_ast::{
self as ast, AnonConst, AttrArgs, AttrArgsEq, AttrId, ByRef, Const, CoroutineKind, DelimArgs,
Expr, ExprKind, Extern, HasAttrs, HasTokens, Mutability, StrLit, Unsafe, Visibility,
VisibilityKind, DUMMY_NODE_ID,
};
use rustc_ast_pretty::pprust;
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::PResult;
@ -1273,6 +1272,11 @@ impl<'a> Parser<'a> {
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`).
fn parse_const_or_mut(&mut self) -> Option<Mutability> {
if self.eat_keyword(kw::Mut) {

View File

@ -4,11 +4,11 @@ use crate::errors::{
DotDotDotRestPattern, EnumPatternInsteadOfIdentifier, ExpectedBindingLeftOfAt,
ExpectedCommaAfterPatternField, GenericArgsInPatRequireTurbofishSyntax,
InclusiveRangeExtraEquals, InclusiveRangeMatchArrow, InclusiveRangeNoEnd, InvalidMutInPattern,
PatternOnWrongSideOfAt, RefMutOrderIncorrect, RemoveLet, RepeatedMutInPattern,
SwitchRefBoxOrder, TopLevelOrPatternNotAllowed, TopLevelOrPatternNotAllowedSugg,
TrailingVertNotAllowed, UnexpectedExpressionInPattern, UnexpectedLifetimeInPattern,
UnexpectedParenInRangePat, UnexpectedParenInRangePatSugg,
UnexpectedVertVertBeforeFunctionParam, UnexpectedVertVertInPattern,
PatternOnWrongSideOfAt, RemoveLet, RepeatedMutInPattern, SwitchRefBoxOrder,
TopLevelOrPatternNotAllowed, TopLevelOrPatternNotAllowedSugg, TrailingVertNotAllowed,
UnexpectedExpressionInPattern, UnexpectedLifetimeInPattern, UnexpectedParenInRangePat,
UnexpectedParenInRangePatSugg, UnexpectedVertVertBeforeFunctionParam,
UnexpectedVertVertInPattern,
};
use crate::parser::expr::could_be_unclosed_char_literal;
use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole};
@ -476,7 +476,7 @@ impl<'a> Parser<'a> {
// Parse `_`
PatKind::Wild
} 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) {
if self.check_keyword(kw::Box) {
// Suggest `box ref`.
@ -486,7 +486,7 @@ impl<'a> Parser<'a> {
}
// Parse ref ident @ pat / ref mut ident @ pat
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) {
self.parse_pat_box()?
} 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.
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;
if self.eat_keyword(kw::Ref) {
self.dcx().emit_err(RefMutOrderIncorrect { span: mut_span.to(self.prev_token.span) });
return self.parse_pat_ident(BindingAnnotation::REF_MUT, syntax_loc);
}
self.recover_additional_muts();
let byref = self.parse_byref();
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)?;
// 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.
// `mut` on the outer binding doesn't affect the inner bindings.
*br = byref;
*m = Mutability::Mut;
} else {
// 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);
}
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)
}
@ -1390,16 +1395,12 @@ impl<'a> Parser<'a> {
// Parsing a pattern of the form `(box) (ref) (mut) fieldname`.
let is_box = self.eat_keyword(kw::Box);
let boxed_span = self.token.span;
let is_ref = self.eat_keyword(kw::Ref);
let is_mut = self.eat_keyword(kw::Mut);
let mutability = self.parse_mutability();
let by_ref = self.parse_byref();
let fieldname = self.parse_field_name()?;
hi = self.prev_token.span;
let mutability = match is_mut {
false => Mutability::Not,
true => Mutability::Mut,
};
let ann = BindingAnnotation(ByRef::from(is_ref), mutability);
let ann = BindingAnnotation(by_ref, mutability);
let fieldpat = self.mk_pat_ident(boxed_span.to(hi), ann, fieldname);
let subpat =
if is_box { self.mk_pat(lo.to(hi), PatKind::Box(fieldpat)) } else { fieldpat };

View File

@ -1185,6 +1185,7 @@ symbols! {
multiple_supertrait_upcastable,
must_not_suspend,
must_use,
mut_ref,
naked,
naked_functions,
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 = match decl.implicit_self {
ImplicitSelfKind::MutRef => {
ImplicitSelfKind::RefMut => {
let Some(name) = name.strip_suffix("_mut") else {
return;
};
name
},
ImplicitSelfKind::Imm | ImplicitSelfKind::Mut | ImplicitSelfKind::ImmRef => name,
ImplicitSelfKind::Imm | ImplicitSelfKind::Mut | ImplicitSelfKind::RefImm => name,
ImplicitSelfKind::None => return,
};

View File

@ -97,7 +97,7 @@ fn find_slice_values(cx: &LateContext<'_>, pat: &hir::Pat<'_>) -> FxIndexMap<hir
value_hir_id,
ident,
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
// 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() {
// 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
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 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<'_>) {
let item_did = item.owner_id.to_def_id();
let (borrow_prefix, expected_implicit_self) = match item.ident.name {
sym::iter => ("&", ImplicitSelfKind::ImmRef),
sym::iter_mut => ("&mut ", ImplicitSelfKind::MutRef),
sym::iter => ("&", ImplicitSelfKind::RefImm),
sym::iter_mut => ("&mut ", ImplicitSelfKind::RefMut),
_ => return,
};

View File

@ -384,8 +384,8 @@ impl LenOutput {
fn expected_sig(self, self_kind: ImplicitSelfKind) -> String {
let self_ref = match self_kind {
ImplicitSelfKind::ImmRef => "&",
ImplicitSelfKind::MutRef => "&mut ",
ImplicitSelfKind::RefImm => "&",
ImplicitSelfKind::RefMut => "&mut ",
_ => "",
};
match self {
@ -411,8 +411,8 @@ fn check_is_empty_sig<'tcx>(
[arg, res] if len_output.matches_is_empty_output(cx, *res) => {
matches!(
(arg.kind(), self_kind),
(ty::Ref(_, _, Mutability::Not), ImplicitSelfKind::ImmRef)
| (ty::Ref(_, _, Mutability::Mut), ImplicitSelfKind::MutRef)
(ty::Ref(_, _, Mutability::Not), ImplicitSelfKind::RefImm)
| (ty::Ref(_, _, Mutability::Mut), ImplicitSelfKind::RefMut)
) || (!arg.is_ref() && matches!(self_kind, ImplicitSelfKind::Imm | ImplicitSelfKind::Mut))
},
_ => 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::{path_to_local_id, peel_blocks, strip_pat_refs};
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 super::INFALLIBLE_DESTRUCTURING_MATCH;
@ -30,7 +30,7 @@ pub(crate) fn check(cx: &LateContext<'_>, local: &LetStmt<'_>) -> bool {
format!(
"let {}({}{}) = {};",
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, 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> {
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)
&& 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
&& is_res_lang_ctor(cx, path_res(cx, e), LangItem::OptionSome)
&& 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`
(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
&& 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);
}
// 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,
},
// 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;
},
_ => false,

View File

@ -60,8 +60,6 @@ pub(super) fn check<'tcx>(
applicability,
);
} 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(
cx,
ITER_KV_MAP,
@ -69,7 +67,8 @@ pub(super) fn check<'tcx>(
&format!("iterating on a map's {replacement_kind}s"),
"try",
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)
),
applicability,

View File

@ -129,7 +129,7 @@ impl<'tcx> LateLintPass<'tcx> for LintPass {
if !is_lint_allowed(cx, REF_PATTERNS, arg.pat.hir_id) {
return;
}
if let PatKind::Binding(BindingAnnotation(ByRef::Yes, _), ..) = arg.pat.kind {
if let PatKind::Binding(BindingAnnotation(ByRef::Yes(_), _), ..) = arg.pat.kind {
span_lint(
cx,
TOPLEVEL_REF_ARG,
@ -144,7 +144,7 @@ impl<'tcx> LateLintPass<'tcx> for LintPass {
fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
if !in_external_macro(cx.tcx.sess, stmt.span)
&& 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
// 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)

View File

@ -14,7 +14,7 @@ use rustc_errors::Applicability;
use rustc_hir::def::Res;
use rustc_hir::LangItem::{self, OptionNone, OptionSome, ResultErr, ResultOk};
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_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 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 method_call_str = match by_ref {
ByRef::Yes(Mutability::Mut) => ".as_mut()",
ByRef::Yes(Mutability::Not) => ".as_ref()",
ByRef::No => "",
};
let sugg = format!(
"{receiver_str}{}?{}",
if by_ref == ByRef::Yes { ".as_ref()" } else { "" },
"{receiver_str}{method_call_str}?{}",
if requires_semi { ";" } else { "" }
);
span_lint_and_sugg(

View File

@ -649,6 +649,8 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
BindingAnnotation::REF => "REF",
BindingAnnotation::MUT => "MUT",
BindingAnnotation::REF_MUT => "REF_MUT",
BindingAnnotation::MUT_REF => "MUT_REF",
BindingAnnotation::MUT_REF_MUT => "MUT_REF_MUT",
};
kind!("Binding(BindingAnnotation::{ann}, _, {name}, {sub})");
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::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk};
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,
ItemKind, LangItem, LetStmt, MatchSource, Mutability, Node, OwnerId, Param, Pat, PatKind, Path, PathSegment, PrimTy,
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::mir::Const;
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::layout::IntegerExt;
use rustc_middle::ty::{
@ -1006,11 +1005,12 @@ pub fn capture_local_usage(cx: &LateContext<'_>, e: &Expr<'_>) -> CaptureKind {
.typeck_results()
.extract_binding_mode(cx.sess(), id, span)
.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;
},
BindingMode::BindByReference(Mutability::Mut) if capture != CaptureKind::Value => {
ByRef::Yes(Mutability::Mut) if capture != CaptureKind::Value => {
capture = CaptureKind::Ref(Mutability::Mut);
},
_ => (),
@ -2035,7 +2035,7 @@ fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool {
.typeck_results()
.pat_binding_modes()
.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,
// 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::Ident(BindingAnnotation(by_ref, mutability), ident, ref sub_pat) => {
let prefix = match by_ref {
ByRef::Yes => "ref",
ByRef::No => "",
let mut_prefix = format_mutability(mutability).trim();
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 sub_pat = match *sub_pat {
Some(ref p) => {
// 2 - `@ `.
let width = shape
.width
.checked_sub(prefix.len() + mut_infix.len() + id_str.len() + 2)?;
let width = shape.width.checked_sub(
mut_prefix.len() + ref_kw.len() + mut_infix.len() + id_str.len() + 2,
)?;
let lo = context.snippet_provider.span_after(self.span, "@");
combine_strs_with_missing_comments(
context,
@ -132,33 +133,55 @@ impl Rewrite for Pat {
None => "".to_owned(),
};
// combine prefix and mut
let (first_lo, first) = if !prefix.is_empty() && !mut_infix.is_empty() {
let hi = context.snippet_provider.span_before(self.span, "mut");
let lo = context.snippet_provider.span_after(self.span, "ref");
(
// combine prefix and ref
let (first_lo, first) = match (mut_prefix.is_empty(), ref_kw.is_empty()) {
(false, false) => {
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"),
combine_strs_with_missing_comments(
context,
prefix,
mut_infix,
mk_sp(lo, hi),
shape,
true,
)?,
)
} else if !prefix.is_empty() {
(
mut_prefix.to_owned(),
),
(true, false) => (
context.snippet_provider.span_after(self.span, "ref"),
prefix.to_owned(),
)
} else if !mut_infix.is_empty() {
(
context.snippet_provider.span_after(self.span, "mut"),
mut_infix.to_owned(),
)
} else {
(self.span.lo(), "".to_owned())
ref_kw.to_owned(),
),
(true, true) => (self.span.lo(), "".to_owned()),
};
// combine result of above and mut
let (second_lo, second) = match (first.is_empty(), mut_infix.is_empty()) {
(false, false) => {
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() {
@ -177,9 +200,9 @@ impl Rewrite for Pat {
combine_strs_with_missing_comments(
context,
&first,
&second,
&next,
mk_sp(first_lo, ident.span.lo()),
mk_sp(second_lo, ident.span.lo()),
shape,
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() {
let mut ref x = 10; //~ ERROR the order of `mut` and `ref` is incorrect
let ref mut y = 11;
let mut ref x = 10;
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)
kind: PatKind {
Binding {
mutability: Not
name: "foo"
mode: ByValue
mode: BindingAnnotation(No, Not)
var: LocalVarId(HirId(DefId(0:16 ~ thir_tree_match[fcf8]::has_match).2))
ty: Foo
is_primary: true