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 {
let kind = match self.kind {
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 ",
// FIXME: differentiate `TwoPhaseBorrow`
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::{
self, AggregateKind, BindingForm, BorrowKind, CallSource, ClearCrossCrate, ConstraintCategory,
FakeReadCause, LocalDecl, LocalInfo, LocalKind, Location, MutBorrowKind, Operand, Place,
PlaceRef, ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind,
VarBindingForm,
FakeBorrowKind, FakeReadCause, LocalDecl, LocalInfo, LocalKind, Location, MutBorrowKind,
Operand, Place, PlaceRef, ProjectionElem, Rvalue, Statement, StatementKind, Terminator,
TerminatorKind, VarBindingForm,
};
use rustc_middle::ty::{
self, suggest_constraining_type_params, PredicateKind, ToPredicate, Ty, TyCtxt,
@ -1486,7 +1486,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
let first_borrow_desc;
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 },
) => {
first_borrow_desc = "mutable ";
@ -1504,7 +1504,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
}
(
BorrowKind::Mut { kind: MutBorrowKind::Default | MutBorrowKind::TwoPhaseBorrow },
BorrowKind::Shared,
BorrowKind::Shared | BorrowKind::Fake(FakeBorrowKind::Deep),
) => {
first_borrow_desc = "immutable ";
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)
}
(BorrowKind::Mut { .. }, BorrowKind::Fake) => {
(BorrowKind::Mut { .. }, BorrowKind::Fake(FakeBorrowKind::Shallow)) => {
if let Some(immutable_section_description) =
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 ";
self.cannot_reborrow_already_uniquely_borrowed(
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!()
}
};
@ -3572,7 +3581,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
let loan_span = loan_spans.args_or_use();
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) {
let mut err = self.cannot_mutate_in_immutable_section(
span,

View File

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

View File

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

View File

@ -55,7 +55,7 @@ use crate::Overlap;
use crate::{AccessDepth, Deep, Shallow};
use rustc_hir as hir;
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 std::cmp::max;
@ -271,10 +271,10 @@ fn place_components_conflict<'tcx>(
// If the second example, where we did, then we still know
// that the borrow can access a *part* of our place that
// 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()
{
debug!("borrow_conflicts_with_place: fake borrow");
debug!("borrow_conflicts_with_place: shallow borrow");
false
} else {
debug!("borrow_conflicts_with_place: full borrow, CONFLICT");

View File

@ -1,6 +1,8 @@
use rustc_data_structures::graph::dominators::Dominators;
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::{InlineAsmOperand, Terminator, TerminatorKind};
use rustc_middle::mir::{Statement, StatementKind};
@ -239,10 +241,12 @@ impl<'cx, 'tcx> LoanInvalidationsGenerator<'cx, 'tcx> {
match rvalue {
&Rvalue::Ref(_ /*rgn*/, bk, place) => {
let access_kind = match bk {
BorrowKind::Fake => {
BorrowKind::Fake(FakeBorrowKind::Shallow) => {
(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 { .. } => {
let wk = WriteKind::MutableBorrow(bk);
if allow_two_phase_borrow(bk) {
@ -357,8 +361,11 @@ impl<'cx, 'tcx> LoanInvalidationsGenerator<'cx, 'tcx> {
// have already taken the reservation
}
(Read(_), BorrowKind::Fake | BorrowKind::Shared)
| (Read(ReadKind::Borrow(BorrowKind::Fake)), BorrowKind::Mut { .. }) => {
(Read(_), BorrowKind::Fake(_) | BorrowKind::Shared)
| (
Read(ReadKind::Borrow(BorrowKind::Fake(FakeBorrowKind::Shallow))),
BorrowKind::Mut { .. },
) => {
// 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
assert!(match borrow.kind {
BorrowKind::Shared | BorrowKind::Fake => false,
BorrowKind::Shared | BorrowKind::Fake(_) => false,
BorrowKind::Mut { .. } => true,
});

View File

@ -414,7 +414,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
BorrowKind::Shared => {
PlaceContext::NonMutatingUse(NonMutatingUseContext::SharedBorrow)
}
BorrowKind::Fake => {
BorrowKind::Fake(_) => {
PlaceContext::NonMutatingUse(NonMutatingUseContext::FakeBorrow)
}
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) => {
let borrowed_place_has_mut_interior = qualifs::in_place::<HasMutInterior, _>(
self.ccx,

View File

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

View File

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

View File

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

View File

@ -165,13 +165,16 @@ pub enum BorrowKind {
/// Data must be immutable and is aliasable.
Shared,
/// 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`.
/// An immutable, aliasable borrow that is discarded after borrow-checking. Can behave either
/// like a normal shared borrow or like a special shallow borrow (see [`FakeBorrowKind`]).
///
/// 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:
/// This is used when lowering index expressions and matches. This is used to prevent code like
/// the following from compiling:
/// ```compile_fail,E0506
/// let mut x = vec![vec![0, 1]];
/// let y = vec![];
/// let _ = x[0][{x = y; 1}];
/// ```
/// ```compile_fail,E0510
/// let mut x = &Some(0);
/// match *x {
@ -180,11 +183,8 @@ pub enum BorrowKind {
/// 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.
Fake,
Fake(FakeBorrowKind),
/// Data is mutable and not aliasable.
Mut { kind: MutBorrowKind },
@ -240,6 +240,57 @@ pub enum MutBorrowKind {
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

View File

@ -293,7 +293,7 @@ impl BorrowKind {
// We have no type corresponding to a shallow borrow, so use
// `&` 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(
NonMutatingUseContext::SharedBorrow
),
BorrowKind::Fake => PlaceContext::NonMutatingUse(
BorrowKind::Fake(_) => PlaceContext::NonMutatingUse(
NonMutatingUseContext::FakeBorrow
),
BorrowKind::Mut { .. } =>
@ -1279,6 +1279,8 @@ pub enum NonMutatingUseContext {
/// Shared borrow.
SharedBorrow,
/// 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,
/// AddressOf for *const pointer.
AddressOf,

View File

@ -685,7 +685,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
fake_borrow_temp.into(),
Rvalue::Ref(
tcx.lifetimes.re_erased,
BorrowKind::Fake,
BorrowKind::Fake(FakeBorrowKind::Shallow),
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 scrutinee_source_info = self.source_info(scrutinee_span);
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);
}

View File

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

View File

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

View File

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

View File

@ -384,7 +384,7 @@ impl<'tcx> Validator<'_, 'tcx> {
match kind {
// 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.
BorrowKind::Fake | BorrowKind::Mut { kind: MutBorrowKind::ClosureCapture } => {
BorrowKind::Fake(_) | BorrowKind::Mut { kind: MutBorrowKind::ClosureCapture } => {
return Err(Unpromotable);
}

View File

@ -229,7 +229,7 @@ impl<'tcx> Stable<'tcx> for mir::BorrowKind {
use rustc_middle::mir::BorrowKind::*;
match *self {
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) },
}
}
@ -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> {
type T = stable_mir::mir::NullOp;
fn stable(&self, tables: &mut Tables<'_>) -> Self::T {

View File

@ -865,11 +865,9 @@ pub enum BorrowKind {
/// Data must be immutable and is aliasable.
Shared,
/// 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`.
Fake,
/// An immutable, aliasable borrow that is discarded after borrow-checking. Can behave either
/// like a normal shared borrow or like a special shallow borrow (see [`FakeBorrowKind`]).
Fake(FakeBorrowKind),
/// Data is mutable and not aliasable.
Mut {
@ -884,7 +882,7 @@ impl BorrowKind {
BorrowKind::Mut { .. } => Mutability::Mut,
BorrowKind::Shared => Mutability::Not,
// 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,
}
#[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)]
pub enum Mutability {
Not,

View File

@ -8,7 +8,7 @@ use std::{fmt, io, iter};
use super::{AssertMessage, BinOp, TerminatorKind};
use super::BorrowKind;
use super::{BorrowKind, FakeBorrowKind};
impl Display for Ty {
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) => {
let kind = match borrowkind {
BorrowKind::Shared => "&",
BorrowKind::Fake => "&fake ",
BorrowKind::Fake(FakeBorrowKind::Deep) => "&fake ",
BorrowKind::Fake(FakeBorrowKind::Shallow) => "&fake shallow ",
BorrowKind::Mut { .. } => "&mut ",
};
write!(writer, "{kind}{:?}", place)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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