1
Fork 0

Implement mut ref/mut ref mut

This commit is contained in:
Jules Bertholet 2024-03-23 21:04:45 -04:00
parent 10a7aa14fe
commit e0da13f25f
No known key found for this signature in database
GPG key ID: 32034DAFC38C1BFC
51 changed files with 442 additions and 378 deletions

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);