Add a non-shallow fake borrow

This commit is contained in:
Nadrieril 2024-04-07 00:30:28 +02:00
parent 511bd78863
commit 50531806ee
32 changed files with 188 additions and 92 deletions

View File

@ -69,7 +69,8 @@ impl<'tcx> fmt::Display for BorrowData<'tcx> {
fn fmt(&self, w: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, w: &mut fmt::Formatter<'_>) -> fmt::Result {
let kind = match self.kind { let kind = match self.kind {
mir::BorrowKind::Shared => "", mir::BorrowKind::Shared => "",
mir::BorrowKind::Fake => "fake ", mir::BorrowKind::Fake(mir::FakeBorrowKind::Deep) => "fake ",
mir::BorrowKind::Fake(mir::FakeBorrowKind::Shallow) => "fake shallow ",
mir::BorrowKind::Mut { kind: mir::MutBorrowKind::ClosureCapture } => "uniq ", mir::BorrowKind::Mut { kind: mir::MutBorrowKind::ClosureCapture } => "uniq ",
// FIXME: differentiate `TwoPhaseBorrow` // FIXME: differentiate `TwoPhaseBorrow`
mir::BorrowKind::Mut { mir::BorrowKind::Mut {

View File

@ -17,9 +17,9 @@ use rustc_middle::hir::nested_filter::OnlyBodies;
use rustc_middle::mir::tcx::PlaceTy; use rustc_middle::mir::tcx::PlaceTy;
use rustc_middle::mir::{ use rustc_middle::mir::{
self, AggregateKind, BindingForm, BorrowKind, CallSource, ClearCrossCrate, ConstraintCategory, self, AggregateKind, BindingForm, BorrowKind, CallSource, ClearCrossCrate, ConstraintCategory,
FakeReadCause, LocalDecl, LocalInfo, LocalKind, Location, MutBorrowKind, Operand, Place, FakeBorrowKind, FakeReadCause, LocalDecl, LocalInfo, LocalKind, Location, MutBorrowKind,
PlaceRef, ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, Operand, Place, PlaceRef, ProjectionElem, Rvalue, Statement, StatementKind, Terminator,
VarBindingForm, TerminatorKind, VarBindingForm,
}; };
use rustc_middle::ty::{ use rustc_middle::ty::{
self, suggest_constraining_type_params, PredicateKind, ToPredicate, Ty, TyCtxt, self, suggest_constraining_type_params, PredicateKind, ToPredicate, Ty, TyCtxt,
@ -1486,7 +1486,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
let first_borrow_desc; let first_borrow_desc;
let mut err = match (gen_borrow_kind, issued_borrow.kind) { let mut err = match (gen_borrow_kind, issued_borrow.kind) {
( (
BorrowKind::Shared, BorrowKind::Shared | BorrowKind::Fake(FakeBorrowKind::Deep),
BorrowKind::Mut { kind: MutBorrowKind::Default | MutBorrowKind::TwoPhaseBorrow }, BorrowKind::Mut { kind: MutBorrowKind::Default | MutBorrowKind::TwoPhaseBorrow },
) => { ) => {
first_borrow_desc = "mutable "; first_borrow_desc = "mutable ";
@ -1504,7 +1504,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
} }
( (
BorrowKind::Mut { kind: MutBorrowKind::Default | MutBorrowKind::TwoPhaseBorrow }, BorrowKind::Mut { kind: MutBorrowKind::Default | MutBorrowKind::TwoPhaseBorrow },
BorrowKind::Shared, BorrowKind::Shared | BorrowKind::Fake(FakeBorrowKind::Deep),
) => { ) => {
first_borrow_desc = "immutable "; first_borrow_desc = "immutable ";
let mut err = self.cannot_reborrow_already_borrowed( let mut err = self.cannot_reborrow_already_borrowed(
@ -1566,7 +1566,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
self.cannot_uniquely_borrow_by_two_closures(span, &desc_place, issued_span, None) self.cannot_uniquely_borrow_by_two_closures(span, &desc_place, issued_span, None)
} }
(BorrowKind::Mut { .. }, BorrowKind::Fake) => { (BorrowKind::Mut { .. }, BorrowKind::Fake(FakeBorrowKind::Shallow)) => {
if let Some(immutable_section_description) = if let Some(immutable_section_description) =
self.classify_immutable_section(issued_borrow.assigned_place) self.classify_immutable_section(issued_borrow.assigned_place)
{ {
@ -1629,7 +1629,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
) )
} }
(BorrowKind::Shared, BorrowKind::Mut { kind: MutBorrowKind::ClosureCapture }) => { (
BorrowKind::Shared | BorrowKind::Fake(FakeBorrowKind::Deep),
BorrowKind::Mut { kind: MutBorrowKind::ClosureCapture },
) => {
first_borrow_desc = "first "; first_borrow_desc = "first ";
self.cannot_reborrow_already_uniquely_borrowed( self.cannot_reborrow_already_uniquely_borrowed(
span, span,
@ -1659,8 +1662,14 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
) )
} }
(BorrowKind::Shared, BorrowKind::Shared | BorrowKind::Fake) (
| (BorrowKind::Fake, BorrowKind::Mut { .. } | BorrowKind::Shared | BorrowKind::Fake) => { BorrowKind::Shared | BorrowKind::Fake(FakeBorrowKind::Deep),
BorrowKind::Shared | BorrowKind::Fake(_),
)
| (
BorrowKind::Fake(FakeBorrowKind::Shallow),
BorrowKind::Mut { .. } | BorrowKind::Shared | BorrowKind::Fake(_),
) => {
unreachable!() unreachable!()
} }
}; };
@ -3572,7 +3581,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
let loan_span = loan_spans.args_or_use(); let loan_span = loan_spans.args_or_use();
let descr_place = self.describe_any_place(place.as_ref()); let descr_place = self.describe_any_place(place.as_ref());
if loan.kind == BorrowKind::Fake { if let BorrowKind::Fake(_) = loan.kind {
if let Some(section) = self.classify_immutable_section(loan.assigned_place) { if let Some(section) = self.classify_immutable_section(loan.assigned_place) {
let mut err = self.cannot_mutate_in_immutable_section( let mut err = self.cannot_mutate_in_immutable_section(
span, span,

View File

@ -654,7 +654,7 @@ impl UseSpans<'_> {
match kind { match kind {
Some(kd) => match kd { Some(kd) => match kd {
rustc_middle::mir::BorrowKind::Shared rustc_middle::mir::BorrowKind::Shared
| rustc_middle::mir::BorrowKind::Fake => { | rustc_middle::mir::BorrowKind::Fake(_) => {
CaptureVarKind::Immut { kind_span: capture_kind_span } CaptureVarKind::Immut { kind_span: capture_kind_span }
} }

View File

@ -1056,18 +1056,19 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
Control::Continue Control::Continue
} }
(Read(_), BorrowKind::Shared | BorrowKind::Fake) (Read(_), BorrowKind::Shared | BorrowKind::Fake(_))
| (Read(ReadKind::Borrow(BorrowKind::Fake)), BorrowKind::Mut { .. }) => { | (
Control::Continue Read(ReadKind::Borrow(BorrowKind::Fake(FakeBorrowKind::Shallow))),
} BorrowKind::Mut { .. },
) => Control::Continue,
(Reservation(_), BorrowKind::Fake | BorrowKind::Shared) => { (Reservation(_), BorrowKind::Fake(_) | BorrowKind::Shared) => {
// This used to be a future compatibility warning (to be // This used to be a future compatibility warning (to be
// disallowed on NLL). See rust-lang/rust#56254 // disallowed on NLL). See rust-lang/rust#56254
Control::Continue Control::Continue
} }
(Write(WriteKind::Move), BorrowKind::Fake) => { (Write(WriteKind::Move), BorrowKind::Fake(FakeBorrowKind::Shallow)) => {
// Handled by initialization checks. // Handled by initialization checks.
Control::Continue Control::Continue
} }
@ -1175,10 +1176,12 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
match rvalue { match rvalue {
&Rvalue::Ref(_ /*rgn*/, bk, place) => { &Rvalue::Ref(_ /*rgn*/, bk, place) => {
let access_kind = match bk { let access_kind = match bk {
BorrowKind::Fake => { BorrowKind::Fake(FakeBorrowKind::Shallow) => {
(Shallow(Some(ArtificialField::FakeBorrow)), Read(ReadKind::Borrow(bk))) (Shallow(Some(ArtificialField::FakeBorrow)), Read(ReadKind::Borrow(bk)))
} }
BorrowKind::Shared => (Deep, Read(ReadKind::Borrow(bk))), BorrowKind::Shared | BorrowKind::Fake(FakeBorrowKind::Deep) => {
(Deep, Read(ReadKind::Borrow(bk)))
}
BorrowKind::Mut { .. } => { BorrowKind::Mut { .. } => {
let wk = WriteKind::MutableBorrow(bk); let wk = WriteKind::MutableBorrow(bk);
if allow_two_phase_borrow(bk) { if allow_two_phase_borrow(bk) {
@ -1197,7 +1200,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
flow_state, flow_state,
); );
let action = if bk == BorrowKind::Fake { let action = if bk == BorrowKind::Fake(FakeBorrowKind::Shallow) {
InitializationRequiringAction::MatchOn InitializationRequiringAction::MatchOn
} else { } else {
InitializationRequiringAction::Borrow InitializationRequiringAction::Borrow
@ -1556,7 +1559,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
// only mutable borrows should be 2-phase // only mutable borrows should be 2-phase
assert!(match borrow.kind { assert!(match borrow.kind {
BorrowKind::Shared | BorrowKind::Fake => false, BorrowKind::Shared | BorrowKind::Fake(_) => false,
BorrowKind::Mut { .. } => true, BorrowKind::Mut { .. } => true,
}); });
@ -2121,14 +2124,14 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
| WriteKind::Replace | WriteKind::Replace
| WriteKind::StorageDeadOrDrop | WriteKind::StorageDeadOrDrop
| WriteKind::MutableBorrow(BorrowKind::Shared) | WriteKind::MutableBorrow(BorrowKind::Shared)
| WriteKind::MutableBorrow(BorrowKind::Fake), | WriteKind::MutableBorrow(BorrowKind::Fake(_)),
) )
| Write( | Write(
WriteKind::Move WriteKind::Move
| WriteKind::Replace | WriteKind::Replace
| WriteKind::StorageDeadOrDrop | WriteKind::StorageDeadOrDrop
| WriteKind::MutableBorrow(BorrowKind::Shared) | WriteKind::MutableBorrow(BorrowKind::Shared)
| WriteKind::MutableBorrow(BorrowKind::Fake), | WriteKind::MutableBorrow(BorrowKind::Fake(_)),
) => { ) => {
if self.is_mutable(place.as_ref(), is_local_mutation_allowed).is_err() if self.is_mutable(place.as_ref(), is_local_mutation_allowed).is_err()
&& !self.has_buffered_diags() && !self.has_buffered_diags()
@ -2152,7 +2155,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
return false; return false;
} }
Read( Read(
ReadKind::Borrow(BorrowKind::Mut { .. } | BorrowKind::Shared | BorrowKind::Fake) ReadKind::Borrow(BorrowKind::Mut { .. } | BorrowKind::Shared | BorrowKind::Fake(_))
| ReadKind::Copy, | ReadKind::Copy,
) => { ) => {
// Access authorized // Access authorized

View File

@ -55,7 +55,7 @@ use crate::Overlap;
use crate::{AccessDepth, Deep, Shallow}; use crate::{AccessDepth, Deep, Shallow};
use rustc_hir as hir; use rustc_hir as hir;
use rustc_middle::mir::{ use rustc_middle::mir::{
Body, BorrowKind, MutBorrowKind, Place, PlaceElem, PlaceRef, ProjectionElem, Body, BorrowKind, FakeBorrowKind, MutBorrowKind, Place, PlaceElem, PlaceRef, ProjectionElem,
}; };
use rustc_middle::ty::{self, TyCtxt}; use rustc_middle::ty::{self, TyCtxt};
use std::cmp::max; use std::cmp::max;
@ -271,10 +271,10 @@ fn place_components_conflict<'tcx>(
// If the second example, where we did, then we still know // If the second example, where we did, then we still know
// that the borrow can access a *part* of our place that // that the borrow can access a *part* of our place that
// our access cares about, so we still have a conflict. // our access cares about, so we still have a conflict.
if borrow_kind == BorrowKind::Fake if borrow_kind == BorrowKind::Fake(FakeBorrowKind::Shallow)
&& borrow_place.projection.len() < access_place.projection.len() && borrow_place.projection.len() < access_place.projection.len()
{ {
debug!("borrow_conflicts_with_place: fake borrow"); debug!("borrow_conflicts_with_place: shallow borrow");
false false
} else { } else {
debug!("borrow_conflicts_with_place: full borrow, CONFLICT"); debug!("borrow_conflicts_with_place: full borrow, CONFLICT");

View File

@ -1,6 +1,8 @@
use rustc_data_structures::graph::dominators::Dominators; use rustc_data_structures::graph::dominators::Dominators;
use rustc_middle::mir::visit::Visitor; use rustc_middle::mir::visit::Visitor;
use rustc_middle::mir::{self, BasicBlock, Body, Location, NonDivergingIntrinsic, Place, Rvalue}; use rustc_middle::mir::{
self, BasicBlock, Body, FakeBorrowKind, Location, NonDivergingIntrinsic, Place, Rvalue,
};
use rustc_middle::mir::{BorrowKind, Mutability, Operand}; use rustc_middle::mir::{BorrowKind, Mutability, Operand};
use rustc_middle::mir::{InlineAsmOperand, Terminator, TerminatorKind}; use rustc_middle::mir::{InlineAsmOperand, Terminator, TerminatorKind};
use rustc_middle::mir::{Statement, StatementKind}; use rustc_middle::mir::{Statement, StatementKind};
@ -239,10 +241,12 @@ impl<'cx, 'tcx> LoanInvalidationsGenerator<'cx, 'tcx> {
match rvalue { match rvalue {
&Rvalue::Ref(_ /*rgn*/, bk, place) => { &Rvalue::Ref(_ /*rgn*/, bk, place) => {
let access_kind = match bk { let access_kind = match bk {
BorrowKind::Fake => { BorrowKind::Fake(FakeBorrowKind::Shallow) => {
(Shallow(Some(ArtificialField::FakeBorrow)), Read(ReadKind::Borrow(bk))) (Shallow(Some(ArtificialField::FakeBorrow)), Read(ReadKind::Borrow(bk)))
} }
BorrowKind::Shared => (Deep, Read(ReadKind::Borrow(bk))), BorrowKind::Shared | BorrowKind::Fake(FakeBorrowKind::Deep) => {
(Deep, Read(ReadKind::Borrow(bk)))
}
BorrowKind::Mut { .. } => { BorrowKind::Mut { .. } => {
let wk = WriteKind::MutableBorrow(bk); let wk = WriteKind::MutableBorrow(bk);
if allow_two_phase_borrow(bk) { if allow_two_phase_borrow(bk) {
@ -357,8 +361,11 @@ impl<'cx, 'tcx> LoanInvalidationsGenerator<'cx, 'tcx> {
// have already taken the reservation // have already taken the reservation
} }
(Read(_), BorrowKind::Fake | BorrowKind::Shared) (Read(_), BorrowKind::Fake(_) | BorrowKind::Shared)
| (Read(ReadKind::Borrow(BorrowKind::Fake)), BorrowKind::Mut { .. }) => { | (
Read(ReadKind::Borrow(BorrowKind::Fake(FakeBorrowKind::Shallow))),
BorrowKind::Mut { .. },
) => {
// Reads don't invalidate shared or shallow borrows // Reads don't invalidate shared or shallow borrows
} }
@ -403,7 +410,7 @@ impl<'cx, 'tcx> LoanInvalidationsGenerator<'cx, 'tcx> {
// only mutable borrows should be 2-phase // only mutable borrows should be 2-phase
assert!(match borrow.kind { assert!(match borrow.kind {
BorrowKind::Shared | BorrowKind::Fake => false, BorrowKind::Shared | BorrowKind::Fake(_) => false,
BorrowKind::Mut { .. } => true, BorrowKind::Mut { .. } => true,
}); });

View File

@ -414,7 +414,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
BorrowKind::Shared => { BorrowKind::Shared => {
PlaceContext::NonMutatingUse(NonMutatingUseContext::SharedBorrow) PlaceContext::NonMutatingUse(NonMutatingUseContext::SharedBorrow)
} }
BorrowKind::Fake => { BorrowKind::Fake(_) => {
PlaceContext::NonMutatingUse(NonMutatingUseContext::FakeBorrow) PlaceContext::NonMutatingUse(NonMutatingUseContext::FakeBorrow)
} }
BorrowKind::Mut { .. } => { BorrowKind::Mut { .. } => {
@ -487,7 +487,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
} }
} }
Rvalue::Ref(_, BorrowKind::Shared | BorrowKind::Fake, place) Rvalue::Ref(_, BorrowKind::Shared | BorrowKind::Fake(_), place)
| Rvalue::AddressOf(Mutability::Not, place) => { | Rvalue::AddressOf(Mutability::Not, place) => {
let borrowed_place_has_mut_interior = qualifs::in_place::<HasMutInterior, _>( let borrowed_place_has_mut_interior = qualifs::in_place::<HasMutInterior, _>(
self.ccx, self.ccx,

View File

@ -105,7 +105,7 @@ where
fn ref_allows_mutation(&self, kind: mir::BorrowKind, place: mir::Place<'tcx>) -> bool { fn ref_allows_mutation(&self, kind: mir::BorrowKind, place: mir::Place<'tcx>) -> bool {
match kind { match kind {
mir::BorrowKind::Mut { .. } => true, mir::BorrowKind::Mut { .. } => true,
mir::BorrowKind::Shared | mir::BorrowKind::Fake => { mir::BorrowKind::Shared | mir::BorrowKind::Fake(_) => {
self.shared_borrow_allows_mutation(place) self.shared_borrow_allows_mutation(place)
} }
} }

View File

@ -924,7 +924,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
} }
} }
}, },
Rvalue::Ref(_, BorrowKind::Fake, _) => { Rvalue::Ref(_, BorrowKind::Fake(_), _) => {
if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) { if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
self.fail( self.fail(
location, location,

View File

@ -954,7 +954,8 @@ impl<'tcx> Debug for Rvalue<'tcx> {
Ref(region, borrow_kind, ref place) => { Ref(region, borrow_kind, ref place) => {
let kind_str = match borrow_kind { let kind_str = match borrow_kind {
BorrowKind::Shared => "", BorrowKind::Shared => "",
BorrowKind::Fake => "fake ", BorrowKind::Fake(FakeBorrowKind::Deep) => "fake ",
BorrowKind::Fake(FakeBorrowKind::Shallow) => "fake shallow ",
BorrowKind::Mut { .. } => "mut ", BorrowKind::Mut { .. } => "mut ",
}; };

View File

@ -451,7 +451,7 @@ impl<'tcx> Rvalue<'tcx> {
impl BorrowKind { impl BorrowKind {
pub fn mutability(&self) -> Mutability { pub fn mutability(&self) -> Mutability {
match *self { match *self {
BorrowKind::Shared | BorrowKind::Fake => Mutability::Not, BorrowKind::Shared | BorrowKind::Fake(_) => Mutability::Not,
BorrowKind::Mut { .. } => Mutability::Mut, BorrowKind::Mut { .. } => Mutability::Mut,
} }
} }
@ -459,7 +459,7 @@ impl BorrowKind {
pub fn allows_two_phase_borrow(&self) -> bool { pub fn allows_two_phase_borrow(&self) -> bool {
match *self { match *self {
BorrowKind::Shared BorrowKind::Shared
| BorrowKind::Fake | BorrowKind::Fake(_)
| BorrowKind::Mut { kind: MutBorrowKind::Default | MutBorrowKind::ClosureCapture } => { | BorrowKind::Mut { kind: MutBorrowKind::Default | MutBorrowKind::ClosureCapture } => {
false false
} }

View File

@ -165,13 +165,16 @@ pub enum BorrowKind {
/// Data must be immutable and is aliasable. /// Data must be immutable and is aliasable.
Shared, Shared,
/// The immediately borrowed place must be immutable, but projections from /// An immutable, aliasable borrow that is discarded after borrow-checking. Can behave either
/// it don't need to be. For example, a shallow borrow of `a.b` doesn't /// like a normal shared borrow or like a special shallow borrow (see [`FakeBorrowKind`]).
/// conflict with a mutable borrow of `a.b.c`.
/// ///
/// This is used when lowering matches: when matching on a place we want to /// This is used when lowering index expressions and matches. This is used to prevent code like
/// ensure that place have the same value from the start of the match until /// the following from compiling:
/// an arm is selected. This prevents this code from compiling: /// ```compile_fail,E0506
/// let mut x = vec![vec![0, 1]];
/// let y = vec![];
/// let _ = x[0][{x = y; 1}];
/// ```
/// ```compile_fail,E0510 /// ```compile_fail,E0510
/// let mut x = &Some(0); /// let mut x = &Some(0);
/// match *x { /// match *x {
@ -180,11 +183,8 @@ pub enum BorrowKind {
/// Some(_) => (), /// Some(_) => (),
/// } /// }
/// ``` /// ```
/// This can't be a shared borrow because mutably borrowing (*x as Some).0
/// should not prevent `if let None = x { ... }`, for example, because the
/// mutating `(*x as Some).0` can't affect the discriminant of `x`.
/// We can also report errors with this kind of borrow differently. /// We can also report errors with this kind of borrow differently.
Fake, Fake(FakeBorrowKind),
/// Data is mutable and not aliasable. /// Data is mutable and not aliasable.
Mut { kind: MutBorrowKind }, Mut { kind: MutBorrowKind },
@ -240,6 +240,57 @@ pub enum MutBorrowKind {
ClosureCapture, ClosureCapture,
} }
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, TyEncodable, TyDecodable)]
#[derive(Hash, HashStable)]
pub enum FakeBorrowKind {
/// A shared shallow borrow. The immediately borrowed place must be immutable, but projections
/// from it don't need to be. For example, a shallow borrow of `a.b` doesn't conflict with a
/// mutable borrow of `a.b.c`.
///
/// This is used when lowering matches: when matching on a place we want to ensure that place
/// have the same value from the start of the match until an arm is selected. This prevents this
/// code from compiling:
/// ```compile_fail,E0510
/// let mut x = &Some(0);
/// match *x {
/// None => (),
/// Some(_) if { x = &None; false } => (),
/// Some(_) => (),
/// }
/// ```
/// This can't be a shared borrow because mutably borrowing `(*x as Some).0` should not checking
/// the discriminant or accessing other variants, because the mutating `(*x as Some).0` can't
/// affect the discriminant of `x`. E.g. the following is allowed:
/// ```rust
/// let mut x = Some(0);
/// match x {
/// Some(_)
/// if {
/// if let Some(ref mut y) = x {
/// *y += 1;
/// };
/// true
/// } => {}
/// _ => {}
/// }
/// ```
Shallow,
/// A shared (deep) borrow. Data must be immutable and is aliasable.
///
/// This is used when lowering deref patterns, where shallow borrows wouldn't prevent something
/// like:
// ```compile_fail
// let mut b = Box::new(false);
// match b {
// deref!(true) => {} // not reached because `*b == false`
// _ if { *b = true; false } => {} // not reached because the guard is `false`
// deref!(false) => {} // not reached because the guard changed it
// // UB because we reached the unreachable.
// }
// ```
Deep,
}
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
// Statements // Statements

View File

@ -293,7 +293,7 @@ impl BorrowKind {
// We have no type corresponding to a shallow borrow, so use // We have no type corresponding to a shallow borrow, so use
// `&` as an approximation. // `&` as an approximation.
BorrowKind::Fake => hir::Mutability::Not, BorrowKind::Fake(_) => hir::Mutability::Not,
} }
} }
} }

View File

@ -653,7 +653,7 @@ macro_rules! make_mir_visitor {
BorrowKind::Shared => PlaceContext::NonMutatingUse( BorrowKind::Shared => PlaceContext::NonMutatingUse(
NonMutatingUseContext::SharedBorrow NonMutatingUseContext::SharedBorrow
), ),
BorrowKind::Fake => PlaceContext::NonMutatingUse( BorrowKind::Fake(_) => PlaceContext::NonMutatingUse(
NonMutatingUseContext::FakeBorrow NonMutatingUseContext::FakeBorrow
), ),
BorrowKind::Mut { .. } => BorrowKind::Mut { .. } =>
@ -1279,6 +1279,8 @@ pub enum NonMutatingUseContext {
/// Shared borrow. /// Shared borrow.
SharedBorrow, SharedBorrow,
/// A fake borrow. /// A fake borrow.
/// FIXME: do we need to distinguish shallow and deep fake borrows? In fact, do we need to
/// distinguish fake and normal deep borrows?
FakeBorrow, FakeBorrow,
/// AddressOf for *const pointer. /// AddressOf for *const pointer.
AddressOf, AddressOf,

View File

@ -685,7 +685,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
fake_borrow_temp.into(), fake_borrow_temp.into(),
Rvalue::Ref( Rvalue::Ref(
tcx.lifetimes.re_erased, tcx.lifetimes.re_erased,
BorrowKind::Fake, BorrowKind::Fake(FakeBorrowKind::Shallow),
Place { local: base_place.local, projection }, Place { local: base_place.local, projection },
), ),
); );

View File

@ -2106,7 +2106,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let re_erased = tcx.lifetimes.re_erased; let re_erased = tcx.lifetimes.re_erased;
let scrutinee_source_info = self.source_info(scrutinee_span); let scrutinee_source_info = self.source_info(scrutinee_span);
for &(place, temp) in fake_borrows { for &(place, temp) in fake_borrows {
let borrow = Rvalue::Ref(re_erased, BorrowKind::Fake, place); let borrow =
Rvalue::Ref(re_erased, BorrowKind::Fake(FakeBorrowKind::Shallow), place);
self.cfg.push_assign(block, scrutinee_source_info, Place::from(temp), borrow); self.cfg.push_assign(block, scrutinee_source_info, Place::from(temp), borrow);
} }

View File

@ -513,7 +513,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
visit::walk_expr(&mut visitor, expr); visit::walk_expr(&mut visitor, expr);
if visitor.found { if visitor.found {
match borrow_kind { match borrow_kind {
BorrowKind::Fake | BorrowKind::Shared BorrowKind::Fake(_) | BorrowKind::Shared
if !self.thir[arg].ty.is_freeze(self.tcx, self.param_env) => if !self.thir[arg].ty.is_freeze(self.tcx, self.param_env) =>
{ {
self.requires_unsafe(expr.span, BorrowOfLayoutConstrainedField) self.requires_unsafe(expr.span, BorrowOfLayoutConstrainedField)
@ -521,7 +521,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
BorrowKind::Mut { .. } => { BorrowKind::Mut { .. } => {
self.requires_unsafe(expr.span, MutationOfLayoutConstrainedField) self.requires_unsafe(expr.span, MutationOfLayoutConstrainedField)
} }
BorrowKind::Fake | BorrowKind::Shared => {} BorrowKind::Fake(_) | BorrowKind::Shared => {}
} }
} }
} }

View File

@ -102,7 +102,7 @@ where
} }
Rvalue::Cast(..) Rvalue::Cast(..)
| Rvalue::Ref(_, BorrowKind::Fake, _) | Rvalue::Ref(_, BorrowKind::Fake(_), _)
| Rvalue::ShallowInitBox(..) | Rvalue::ShallowInitBox(..)
| Rvalue::Use(..) | Rvalue::Use(..)
| Rvalue::ThreadLocalRef(..) | Rvalue::ThreadLocalRef(..)

View File

@ -29,7 +29,7 @@ impl<'tcx> MirPass<'tcx> for CleanupPostBorrowck {
for statement in basic_block.statements.iter_mut() { for statement in basic_block.statements.iter_mut() {
match statement.kind { match statement.kind {
StatementKind::AscribeUserType(..) StatementKind::AscribeUserType(..)
| StatementKind::Assign(box (_, Rvalue::Ref(_, BorrowKind::Fake, _))) | StatementKind::Assign(box (_, Rvalue::Ref(_, BorrowKind::Fake(_), _)))
| StatementKind::Coverage( | StatementKind::Coverage(
// These kinds of coverage statements are markers inserted during // These kinds of coverage statements are markers inserted during
// MIR building, and are not needed after InstrumentCoverage. // MIR building, and are not needed after InstrumentCoverage.

View File

@ -384,7 +384,7 @@ impl<'tcx> Validator<'_, 'tcx> {
match kind { match kind {
// Reject these borrow types just to be safe. // Reject these borrow types just to be safe.
// FIXME(RalfJung): could we allow them? Should we? No point in it until we have a usecase. // FIXME(RalfJung): could we allow them? Should we? No point in it until we have a usecase.
BorrowKind::Fake | BorrowKind::Mut { kind: MutBorrowKind::ClosureCapture } => { BorrowKind::Fake(_) | BorrowKind::Mut { kind: MutBorrowKind::ClosureCapture } => {
return Err(Unpromotable); return Err(Unpromotable);
} }

View File

@ -229,7 +229,7 @@ impl<'tcx> Stable<'tcx> for mir::BorrowKind {
use rustc_middle::mir::BorrowKind::*; use rustc_middle::mir::BorrowKind::*;
match *self { match *self {
Shared => stable_mir::mir::BorrowKind::Shared, Shared => stable_mir::mir::BorrowKind::Shared,
Fake => stable_mir::mir::BorrowKind::Fake, Fake(kind) => stable_mir::mir::BorrowKind::Fake(kind.stable(tables)),
Mut { kind } => stable_mir::mir::BorrowKind::Mut { kind: kind.stable(tables) }, Mut { kind } => stable_mir::mir::BorrowKind::Mut { kind: kind.stable(tables) },
} }
} }
@ -247,6 +247,17 @@ impl<'tcx> Stable<'tcx> for mir::MutBorrowKind {
} }
} }
impl<'tcx> Stable<'tcx> for mir::FakeBorrowKind {
type T = stable_mir::mir::FakeBorrowKind;
fn stable(&self, _: &mut Tables<'_>) -> Self::T {
use rustc_middle::mir::FakeBorrowKind::*;
match *self {
Deep => stable_mir::mir::FakeBorrowKind::Deep,
Shallow => stable_mir::mir::FakeBorrowKind::Shallow,
}
}
}
impl<'tcx> Stable<'tcx> for mir::NullOp<'tcx> { impl<'tcx> Stable<'tcx> for mir::NullOp<'tcx> {
type T = stable_mir::mir::NullOp; type T = stable_mir::mir::NullOp;
fn stable(&self, tables: &mut Tables<'_>) -> Self::T { fn stable(&self, tables: &mut Tables<'_>) -> Self::T {

View File

@ -865,11 +865,9 @@ pub enum BorrowKind {
/// Data must be immutable and is aliasable. /// Data must be immutable and is aliasable.
Shared, Shared,
/// The immediately borrowed place must be immutable, but projections from /// An immutable, aliasable borrow that is discarded after borrow-checking. Can behave either
/// it don't need to be. This is used to prevent match guards from replacing /// like a normal shared borrow or like a special shallow borrow (see [`FakeBorrowKind`]).
/// the scrutinee. For example, a fake borrow of `a.b` doesn't Fake(FakeBorrowKind),
/// conflict with a mutable borrow of `a.b.c`.
Fake,
/// Data is mutable and not aliasable. /// Data is mutable and not aliasable.
Mut { Mut {
@ -884,7 +882,7 @@ impl BorrowKind {
BorrowKind::Mut { .. } => Mutability::Mut, BorrowKind::Mut { .. } => Mutability::Mut,
BorrowKind::Shared => Mutability::Not, BorrowKind::Shared => Mutability::Not,
// FIXME: There's no type corresponding to a shallow borrow, so use `&` as an approximation. // FIXME: There's no type corresponding to a shallow borrow, so use `&` as an approximation.
BorrowKind::Fake => Mutability::Not, BorrowKind::Fake(_) => Mutability::Not,
} }
} }
} }
@ -896,6 +894,17 @@ pub enum MutBorrowKind {
ClosureCapture, ClosureCapture,
} }
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum FakeBorrowKind {
/// A shared (deep) borrow. Data must be immutable and is aliasable.
Deep,
/// The immediately borrowed place must be immutable, but projections from
/// it don't need to be. This is used to prevent match guards from replacing
/// the scrutinee. For example, a fake borrow of `a.b` doesn't
/// conflict with a mutable borrow of `a.b.c`.
Shallow,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum Mutability { pub enum Mutability {
Not, Not,

View File

@ -8,7 +8,7 @@ use std::{fmt, io, iter};
use super::{AssertMessage, BinOp, TerminatorKind}; use super::{AssertMessage, BinOp, TerminatorKind};
use super::BorrowKind; use super::{BorrowKind, FakeBorrowKind};
impl Display for Ty { impl Display for Ty {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
@ -352,7 +352,8 @@ fn pretty_rvalue<W: Write>(writer: &mut W, rval: &Rvalue) -> io::Result<()> {
Rvalue::Ref(_, borrowkind, place) => { Rvalue::Ref(_, borrowkind, place) => {
let kind = match borrowkind { let kind = match borrowkind {
BorrowKind::Shared => "&", BorrowKind::Shared => "&",
BorrowKind::Fake => "&fake ", BorrowKind::Fake(FakeBorrowKind::Deep) => "&fake ",
BorrowKind::Fake(FakeBorrowKind::Shallow) => "&fake shallow ",
BorrowKind::Mut { .. } => "&mut ", BorrowKind::Mut { .. } => "&mut ",
}; };
write!(writer, "{kind}{:?}", place) write!(writer, "{kind}{:?}", place)

View File

@ -60,7 +60,7 @@ fn full_tested_match() -> () {
bb7: { bb7: {
StorageLive(_6); StorageLive(_6);
_6 = &((_2 as Some).0: i32); _6 = &((_2 as Some).0: i32);
_3 = &fake _2; _3 = &fake shallow _2;
StorageLive(_7); StorageLive(_7);
_7 = guard() -> [return: bb8, unwind: bb16]; _7 = guard() -> [return: bb8, unwind: bb16];
} }

View File

@ -66,7 +66,7 @@ fn full_tested_match2() -> () {
bb7: { bb7: {
StorageLive(_6); StorageLive(_6);
_6 = &((_2 as Some).0: i32); _6 = &((_2 as Some).0: i32);
_3 = &fake _2; _3 = &fake shallow _2;
StorageLive(_7); StorageLive(_7);
_7 = guard() -> [return: bb8, unwind: bb16]; _7 = guard() -> [return: bb8, unwind: bb16];
} }

View File

@ -87,7 +87,7 @@ fn main() -> () {
bb10: { bb10: {
StorageLive(_7); StorageLive(_7);
_7 = &((_2 as Some).0: i32); _7 = &((_2 as Some).0: i32);
_3 = &fake _2; _3 = &fake shallow _2;
StorageLive(_8); StorageLive(_8);
_8 = guard() -> [return: bb11, unwind: bb24]; _8 = guard() -> [return: bb11, unwind: bb24];
} }
@ -129,7 +129,7 @@ fn main() -> () {
bb16: { bb16: {
StorageLive(_11); StorageLive(_11);
_11 = &((_2 as Some).0: i32); _11 = &((_2 as Some).0: i32);
_3 = &fake _2; _3 = &fake shallow _2;
StorageLive(_12); StorageLive(_12);
StorageLive(_13); StorageLive(_13);
_13 = (*_11); _13 = (*_11);

View File

@ -72,8 +72,8 @@ fn constant_eq(_1: &str, _2: bool) -> u32 {
} }
bb12: { bb12: {
_6 = &fake (_3.0: &str); _6 = &fake shallow (_3.0: &str);
_7 = &fake (_3.1: bool); _7 = &fake shallow (_3.1: bool);
StorageLive(_10); StorageLive(_10);
_10 = const true; _10 = const true;
switchInt(move _10) -> [0: bb14, otherwise: bb13]; switchInt(move _10) -> [0: bb14, otherwise: bb13];

View File

@ -54,7 +54,7 @@ fn disjoint_ranges(_1: i32, _2: bool) -> u32 {
} }
bb9: { bb9: {
_3 = &fake _1; _3 = &fake shallow _1;
StorageLive(_8); StorageLive(_8);
_8 = _2; _8 = _2;
switchInt(move _8) -> [0: bb11, otherwise: bb10]; switchInt(move _8) -> [0: bb11, otherwise: bb10];

View File

@ -80,8 +80,8 @@
_6 = &(_2.1: bool); _6 = &(_2.1: bool);
StorageLive(_8); StorageLive(_8);
_8 = &(_2.2: std::string::String); _8 = &(_2.2: std::string::String);
- _3 = &fake (_2.0: bool); - _3 = &fake shallow (_2.0: bool);
- _4 = &fake (_2.1: bool); - _4 = &fake shallow (_2.1: bool);
StorageLive(_9); StorageLive(_9);
StorageLive(_10); StorageLive(_10);
_10 = _1; _10 = _1;
@ -137,8 +137,8 @@
_6 = &(_2.0: bool); _6 = &(_2.0: bool);
StorageLive(_8); StorageLive(_8);
_8 = &(_2.2: std::string::String); _8 = &(_2.2: std::string::String);
- _3 = &fake (_2.0: bool); - _3 = &fake shallow (_2.0: bool);
- _4 = &fake (_2.1: bool); - _4 = &fake shallow (_2.1: bool);
StorageLive(_12); StorageLive(_12);
StorageLive(_13); StorageLive(_13);
_13 = _1; _13 = _1;

View File

@ -80,8 +80,8 @@
_6 = &(_2.1: bool); _6 = &(_2.1: bool);
StorageLive(_8); StorageLive(_8);
_8 = &(_2.2: std::string::String); _8 = &(_2.2: std::string::String);
- _3 = &fake (_2.0: bool); - _3 = &fake shallow (_2.0: bool);
- _4 = &fake (_2.1: bool); - _4 = &fake shallow (_2.1: bool);
StorageLive(_9); StorageLive(_9);
StorageLive(_10); StorageLive(_10);
_10 = _1; _10 = _1;
@ -137,8 +137,8 @@
_6 = &(_2.0: bool); _6 = &(_2.0: bool);
StorageLive(_8); StorageLive(_8);
_8 = &(_2.2: std::string::String); _8 = &(_2.2: std::string::String);
- _3 = &fake (_2.0: bool); - _3 = &fake shallow (_2.0: bool);
- _4 = &fake (_2.1: bool); - _4 = &fake shallow (_2.1: bool);
StorageLive(_12); StorageLive(_12);
StorageLive(_13); StorageLive(_13);
_13 = _1; _13 = _1;

View File

@ -33,10 +33,10 @@
} }
bb4: { bb4: {
- _3 = &fake _1; - _3 = &fake shallow _1;
- _4 = &fake (*(*((_1 as Some).0: &&i32))); - _4 = &fake shallow (*(*((_1 as Some).0: &&i32)));
- _5 = &fake (*((_1 as Some).0: &&i32)); - _5 = &fake shallow (*((_1 as Some).0: &&i32));
- _6 = &fake ((_1 as Some).0: &&i32); - _6 = &fake shallow ((_1 as Some).0: &&i32);
+ nop; + nop;
+ nop; + nop;
+ nop; + nop;

View File

@ -33,10 +33,10 @@
} }
bb4: { bb4: {
- _3 = &fake _1; - _3 = &fake shallow _1;
- _4 = &fake (*(*((_1 as Some).0: &&i32))); - _4 = &fake shallow (*(*((_1 as Some).0: &&i32)));
- _5 = &fake (*((_1 as Some).0: &&i32)); - _5 = &fake shallow (*((_1 as Some).0: &&i32));
- _6 = &fake ((_1 as Some).0: &&i32); - _6 = &fake shallow ((_1 as Some).0: &&i32);
+ nop; + nop;
+ nop; + nop;
+ nop; + nop;