Auto merge of #117712 - lcnr:expand-coroutine, r=jackh726

generator layout: ignore fake borrows

fixes #117059

We emit fake shallow borrows in case the scrutinee place uses a `Deref` and there is a match guard. This is necessary to prevent the match guard from mutating the scrutinee: fab1054e17/compiler/rustc_mir_build/src/build/matches/mod.rs (L1250-L1265)

These fake borrows end up impacting the generator witness computation in `mir_generator_witnesses`, which causes the issue in #117059. This PR now completely ignores fake borrows during this computation. This is sound as thse are always removed after analysis and the actual computation of the generator layout happens afterwards.

Only the second commit impacts behavior, and could be backported by itself.

r? types
This commit is contained in:
bors 2023-11-09 14:23:45 +00:00
commit b7583d38b7
40 changed files with 140 additions and 91 deletions

View file

@ -71,7 +71,7 @@ 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::Shallow => "shallow ", mir::BorrowKind::Fake => "fake ",
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

@ -49,7 +49,7 @@ pub fn categorize(context: PlaceContext) -> Option<DefUse> {
// cross suspension points so this behavior is unproblematic. // cross suspension points so this behavior is unproblematic.
PlaceContext::MutatingUse(MutatingUseContext::Borrow) | PlaceContext::MutatingUse(MutatingUseContext::Borrow) |
PlaceContext::NonMutatingUse(NonMutatingUseContext::SharedBorrow) | PlaceContext::NonMutatingUse(NonMutatingUseContext::SharedBorrow) |
PlaceContext::NonMutatingUse(NonMutatingUseContext::ShallowBorrow) | PlaceContext::NonMutatingUse(NonMutatingUseContext::FakeBorrow) |
// `PlaceMention` and `AscribeUserType` both evaluate the place, which must not // `PlaceMention` and `AscribeUserType` both evaluate the place, which must not
// contain dangling references. // contain dangling references.

View file

@ -1022,7 +1022,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::Shallow) => { (BorrowKind::Mut { .. }, BorrowKind::Fake) => {
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)
{ {
@ -1114,11 +1114,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
) )
} }
(BorrowKind::Shared, BorrowKind::Shared | BorrowKind::Shallow) (BorrowKind::Shared, BorrowKind::Shared | BorrowKind::Fake)
| ( | (BorrowKind::Fake, BorrowKind::Mut { .. } | BorrowKind::Shared | BorrowKind::Fake) => {
BorrowKind::Shallow, unreachable!()
BorrowKind::Mut { .. } | BorrowKind::Shared | BorrowKind::Shallow, }
) => unreachable!(),
}; };
if issued_spans == borrow_spans { if issued_spans == borrow_spans {
@ -2806,7 +2805,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::Shallow { if loan.kind == BorrowKind::Fake {
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

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

View file

@ -253,8 +253,8 @@ impl<'cx, 'tcx> InvalidationGenerator<'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::Shallow => { BorrowKind::Fake => {
(Shallow(Some(ArtificialField::ShallowBorrow)), Read(ReadKind::Borrow(bk))) (Shallow(Some(ArtificialField::FakeBorrow)), Read(ReadKind::Borrow(bk)))
} }
BorrowKind::Shared => (Deep, Read(ReadKind::Borrow(bk))), BorrowKind::Shared => (Deep, Read(ReadKind::Borrow(bk))),
BorrowKind::Mut { .. } => { BorrowKind::Mut { .. } => {
@ -376,8 +376,8 @@ impl<'cx, 'tcx> InvalidationGenerator<'cx, 'tcx> {
// have already taken the reservation // have already taken the reservation
} }
(Read(_), BorrowKind::Shallow | BorrowKind::Shared) (Read(_), BorrowKind::Fake | BorrowKind::Shared)
| (Read(ReadKind::Borrow(BorrowKind::Shallow)), BorrowKind::Mut { .. }) => { | (Read(ReadKind::Borrow(BorrowKind::Fake)), BorrowKind::Mut { .. }) => {
// Reads don't invalidate shared or shallow borrows // Reads don't invalidate shared or shallow borrows
} }
@ -422,7 +422,7 @@ impl<'cx, 'tcx> InvalidationGenerator<'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::Shallow => false, BorrowKind::Shared | BorrowKind::Fake => false,
BorrowKind::Mut { .. } => true, BorrowKind::Mut { .. } => true,
}); });

View file

@ -846,7 +846,7 @@ use self::ReadOrWrite::{Activation, Read, Reservation, Write};
#[derive(Copy, Clone, PartialEq, Eq, Debug)] #[derive(Copy, Clone, PartialEq, Eq, Debug)]
enum ArtificialField { enum ArtificialField {
ArrayLength, ArrayLength,
ShallowBorrow, FakeBorrow,
} }
#[derive(Copy, Clone, PartialEq, Eq, Debug)] #[derive(Copy, Clone, PartialEq, Eq, Debug)]
@ -1085,18 +1085,18 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
Control::Continue Control::Continue
} }
(Read(_), BorrowKind::Shared | BorrowKind::Shallow) (Read(_), BorrowKind::Shared | BorrowKind::Fake)
| (Read(ReadKind::Borrow(BorrowKind::Shallow)), BorrowKind::Mut { .. }) => { | (Read(ReadKind::Borrow(BorrowKind::Fake)), BorrowKind::Mut { .. }) => {
Control::Continue Control::Continue
} }
(Reservation(_), BorrowKind::Shallow | 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::Shallow) => { (Write(WriteKind::Move), BorrowKind::Fake) => {
// Handled by initialization checks. // Handled by initialization checks.
Control::Continue Control::Continue
} }
@ -1204,8 +1204,8 @@ 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::Shallow => { BorrowKind::Fake => {
(Shallow(Some(ArtificialField::ShallowBorrow)), Read(ReadKind::Borrow(bk))) (Shallow(Some(ArtificialField::FakeBorrow)), Read(ReadKind::Borrow(bk)))
} }
BorrowKind::Shared => (Deep, Read(ReadKind::Borrow(bk))), BorrowKind::Shared => (Deep, Read(ReadKind::Borrow(bk))),
BorrowKind::Mut { .. } => { BorrowKind::Mut { .. } => {
@ -1226,7 +1226,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
flow_state, flow_state,
); );
let action = if bk == BorrowKind::Shallow { let action = if bk == BorrowKind::Fake {
InitializationRequiringAction::MatchOn InitializationRequiringAction::MatchOn
} else { } else {
InitializationRequiringAction::Borrow InitializationRequiringAction::Borrow
@ -1583,7 +1583,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::Shallow => false, BorrowKind::Shared | BorrowKind::Fake => false,
BorrowKind::Mut { .. } => true, BorrowKind::Mut { .. } => true,
}); });
@ -2142,14 +2142,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::Shallow), | 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::Shallow), | 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_errors() && !self.has_buffered_errors()
@ -2173,7 +2173,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
return false; return false;
} }
Read( Read(
ReadKind::Borrow(BorrowKind::Mut { .. } | BorrowKind::Shared | BorrowKind::Shallow) ReadKind::Borrow(BorrowKind::Mut { .. } | BorrowKind::Shared | BorrowKind::Fake)
| ReadKind::Copy, | ReadKind::Copy,
) => { ) => {
// Access authorized // Access authorized

View file

@ -204,7 +204,7 @@ fn place_components_conflict<'tcx>(
match (elem, &base_ty.kind(), access) { match (elem, &base_ty.kind(), access) {
(_, _, Shallow(Some(ArtificialField::ArrayLength))) (_, _, Shallow(Some(ArtificialField::ArrayLength)))
| (_, _, Shallow(Some(ArtificialField::ShallowBorrow))) => { | (_, _, Shallow(Some(ArtificialField::FakeBorrow))) => {
// The array length is like additional fields on the // The array length is like additional fields on the
// type; it does not overlap any existing data there. // type; it does not overlap any existing data there.
// Furthermore, if cannot actually be a prefix of any // Furthermore, if cannot actually be a prefix of any
@ -273,10 +273,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::Shallow if borrow_kind == BorrowKind::Fake
&& borrow_place.projection.len() < access_place.projection.len() && borrow_place.projection.len() < access_place.projection.len()
{ {
debug!("borrow_conflicts_with_place: shallow borrow"); debug!("borrow_conflicts_with_place: fake borrow");
false false
} else { } else {
debug!("borrow_conflicts_with_place: full borrow, CONFLICT"); debug!("borrow_conflicts_with_place: full borrow, CONFLICT");

View file

@ -751,7 +751,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
PlaceContext::MutatingUse(_) => ty::Invariant, PlaceContext::MutatingUse(_) => ty::Invariant,
PlaceContext::NonUse(StorageDead | StorageLive | VarDebugInfo) => ty::Invariant, PlaceContext::NonUse(StorageDead | StorageLive | VarDebugInfo) => ty::Invariant,
PlaceContext::NonMutatingUse( PlaceContext::NonMutatingUse(
Inspect | Copy | Move | PlaceMention | SharedBorrow | ShallowBorrow | AddressOf Inspect | Copy | Move | PlaceMention | SharedBorrow | FakeBorrow | AddressOf
| Projection, | Projection,
) => ty::Covariant, ) => ty::Covariant,
PlaceContext::NonUse(AscribeUserTy(variance)) => variance, PlaceContext::NonUse(AscribeUserTy(variance)) => variance,

View file

@ -219,7 +219,7 @@ impl<'mir, 'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> Visitor<'tcx>
| PlaceContext::NonMutatingUse( | PlaceContext::NonMutatingUse(
NonMutatingUseContext::Inspect NonMutatingUseContext::Inspect
| NonMutatingUseContext::SharedBorrow | NonMutatingUseContext::SharedBorrow
| NonMutatingUseContext::ShallowBorrow | NonMutatingUseContext::FakeBorrow
| NonMutatingUseContext::AddressOf | NonMutatingUseContext::AddressOf
| NonMutatingUseContext::Projection, | NonMutatingUseContext::Projection,
) => { ) => {

View file

@ -423,8 +423,8 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
BorrowKind::Shared => { BorrowKind::Shared => {
PlaceContext::NonMutatingUse(NonMutatingUseContext::SharedBorrow) PlaceContext::NonMutatingUse(NonMutatingUseContext::SharedBorrow)
} }
BorrowKind::Shallow => { BorrowKind::Fake => {
PlaceContext::NonMutatingUse(NonMutatingUseContext::ShallowBorrow) PlaceContext::NonMutatingUse(NonMutatingUseContext::FakeBorrow)
} }
BorrowKind::Mut { .. } => { BorrowKind::Mut { .. } => {
PlaceContext::MutatingUse(MutatingUseContext::Borrow) PlaceContext::MutatingUse(MutatingUseContext::Borrow)
@ -500,7 +500,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
self.check_mut_borrow(place.local, hir::BorrowKind::Raw) self.check_mut_borrow(place.local, hir::BorrowKind::Raw)
} }
Rvalue::Ref(_, BorrowKind::Shared | BorrowKind::Shallow, 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::Shallow => { mir::BorrowKind::Shared | mir::BorrowKind::Fake => {
self.shared_borrow_allows_mutation(place) self.shared_borrow_allows_mutation(place)
} }
} }

View file

@ -456,7 +456,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::Shallow | BorrowKind::Mut { kind: MutBorrowKind::ClosureCapture } => { BorrowKind::Fake | BorrowKind::Mut { kind: MutBorrowKind::ClosureCapture } => {
return Err(Unpromotable); return Err(Unpromotable);
} }

View file

@ -848,11 +848,11 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
} }
match rvalue { match rvalue {
Rvalue::Use(_) | Rvalue::CopyForDeref(_) | Rvalue::Aggregate(..) => {} Rvalue::Use(_) | Rvalue::CopyForDeref(_) | Rvalue::Aggregate(..) => {}
Rvalue::Ref(_, BorrowKind::Shallow, _) => { 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,
"`Assign` statement with a `Shallow` borrow should have been removed in runtime MIR", "`Assign` statement with a `Fake` borrow should have been removed in runtime MIR",
); );
} }
} }

View file

@ -942,7 +942,7 @@ 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::Shallow => "shallow ", BorrowKind::Fake => "fake ",
BorrowKind::Mut { .. } => "mut ", BorrowKind::Mut { .. } => "mut ",
}; };

View file

@ -446,7 +446,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::Shallow => Mutability::Not, BorrowKind::Shared | BorrowKind::Fake => Mutability::Not,
BorrowKind::Mut { .. } => Mutability::Mut, BorrowKind::Mut { .. } => Mutability::Mut,
} }
} }
@ -454,7 +454,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::Shallow | BorrowKind::Fake
| BorrowKind::Mut { kind: MutBorrowKind::Default | MutBorrowKind::ClosureCapture } => { | BorrowKind::Mut { kind: MutBorrowKind::Default | MutBorrowKind::ClosureCapture } => {
false false
} }

View file

@ -123,7 +123,7 @@ pub enum AnalysisPhase {
/// * [`TerminatorKind::FalseEdge`] /// * [`TerminatorKind::FalseEdge`]
/// * [`StatementKind::FakeRead`] /// * [`StatementKind::FakeRead`]
/// * [`StatementKind::AscribeUserType`] /// * [`StatementKind::AscribeUserType`]
/// * [`Rvalue::Ref`] with `BorrowKind::Shallow` /// * [`Rvalue::Ref`] with `BorrowKind::Fake`
/// ///
/// Furthermore, `Deref` projections must be the first projection within any place (if they /// Furthermore, `Deref` projections must be the first projection within any place (if they
/// appear at all) /// appear at all)
@ -182,7 +182,7 @@ pub enum BorrowKind {
/// should not prevent `if let None = x { ... }`, for example, because the /// should not prevent `if let None = x { ... }`, for example, because the
/// mutating `(*x as Some).0` can't affect the discriminant of `x`. /// 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.
Shallow, Fake,
/// Data is mutable and not aliasable. /// Data is mutable and not aliasable.
Mut { kind: MutBorrowKind }, Mut { kind: MutBorrowKind },

View file

@ -278,7 +278,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::Shallow => hir::Mutability::Not, BorrowKind::Fake => hir::Mutability::Not,
} }
} }
} }

View file

@ -649,8 +649,8 @@ macro_rules! make_mir_visitor {
BorrowKind::Shared => PlaceContext::NonMutatingUse( BorrowKind::Shared => PlaceContext::NonMutatingUse(
NonMutatingUseContext::SharedBorrow NonMutatingUseContext::SharedBorrow
), ),
BorrowKind::Shallow => PlaceContext::NonMutatingUse( BorrowKind::Fake => PlaceContext::NonMutatingUse(
NonMutatingUseContext::ShallowBorrow NonMutatingUseContext::FakeBorrow
), ),
BorrowKind::Mut { .. } => BorrowKind::Mut { .. } =>
PlaceContext::MutatingUse(MutatingUseContext::Borrow), PlaceContext::MutatingUse(MutatingUseContext::Borrow),
@ -1261,8 +1261,8 @@ pub enum NonMutatingUseContext {
Move, Move,
/// Shared borrow. /// Shared borrow.
SharedBorrow, SharedBorrow,
/// Shallow borrow. /// A fake borrow.
ShallowBorrow, FakeBorrow,
/// AddressOf for *const pointer. /// AddressOf for *const pointer.
AddressOf, AddressOf,
/// PlaceMention statement. /// PlaceMention statement.
@ -1341,7 +1341,7 @@ impl PlaceContext {
matches!( matches!(
self, self,
PlaceContext::NonMutatingUse( PlaceContext::NonMutatingUse(
NonMutatingUseContext::SharedBorrow | NonMutatingUseContext::ShallowBorrow NonMutatingUseContext::SharedBorrow | NonMutatingUseContext::FakeBorrow
) | PlaceContext::MutatingUse(MutatingUseContext::Borrow) ) | PlaceContext::MutatingUse(MutatingUseContext::Borrow)
) )
} }

View file

@ -690,7 +690,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::Shallow, BorrowKind::Fake,
Place { local: base_place.local, projection }, Place { local: base_place.local, projection },
), ),
); );

View file

@ -2021,7 +2021,7 @@ 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::Shallow, place); let borrow = Rvalue::Ref(re_erased, BorrowKind::Fake, 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

@ -288,7 +288,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
); );
}; };
match borrow_kind { match borrow_kind {
BorrowKind::Shallow | BorrowKind::Shared => { BorrowKind::Fake | BorrowKind::Shared => {
if !ty.is_freeze(self.tcx, self.param_env) { if !ty.is_freeze(self.tcx, self.param_env) {
self.requires_unsafe(pat.span, BorrowOfLayoutConstrainedField); self.requires_unsafe(pat.span, BorrowOfLayoutConstrainedField);
} }
@ -483,7 +483,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::Shallow | 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)
@ -491,7 +491,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::Shallow | BorrowKind::Shared => {} BorrowKind::Fake | BorrowKind::Shared => {}
} }
} }
} }

View file

@ -5,7 +5,8 @@ use rustc_middle::mir::*;
use crate::{AnalysisDomain, GenKill, GenKillAnalysis}; use crate::{AnalysisDomain, GenKill, GenKillAnalysis};
/// A dataflow analysis that tracks whether a pointer or reference could possibly exist that points /// A dataflow analysis that tracks whether a pointer or reference could possibly exist that points
/// to a given local. /// to a given local. This analysis ignores fake borrows, so it should not be used by
/// borrowck.
/// ///
/// At present, this is used as a very limited form of alias analysis. For example, /// At present, this is used as a very limited form of alias analysis. For example,
/// `MaybeBorrowedLocals` is used to compute which locals are live during a yield expression for /// `MaybeBorrowedLocals` is used to compute which locals are live during a yield expression for
@ -91,13 +92,17 @@ where
self.super_rvalue(rvalue, location); self.super_rvalue(rvalue, location);
match rvalue { match rvalue {
Rvalue::AddressOf(_, borrowed_place) | Rvalue::Ref(_, _, borrowed_place) => { // We ignore fake borrows as these get removed after analysis and shouldn't effect
// the layout of generators.
Rvalue::AddressOf(_, borrowed_place)
| Rvalue::Ref(_, BorrowKind::Mut { .. } | BorrowKind::Shared, borrowed_place) => {
if !borrowed_place.is_indirect() { if !borrowed_place.is_indirect() {
self.trans.gen(borrowed_place.local); self.trans.gen(borrowed_place.local);
} }
} }
Rvalue::Cast(..) Rvalue::Cast(..)
| Rvalue::Ref(_, BorrowKind::Fake, _)
| Rvalue::ShallowInitBox(..) | Rvalue::ShallowInitBox(..)
| Rvalue::Use(..) | Rvalue::Use(..)
| Rvalue::ThreadLocalRef(..) | Rvalue::ThreadLocalRef(..)

View file

@ -201,7 +201,7 @@ impl DefUse {
| NonMutatingUseContext::Inspect | NonMutatingUseContext::Inspect
| NonMutatingUseContext::Move | NonMutatingUseContext::Move
| NonMutatingUseContext::PlaceMention | NonMutatingUseContext::PlaceMention
| NonMutatingUseContext::ShallowBorrow | NonMutatingUseContext::FakeBorrow
| NonMutatingUseContext::SharedBorrow, | NonMutatingUseContext::SharedBorrow,
) => Some(DefUse::Use), ) => Some(DefUse::Use),

View file

@ -4,13 +4,13 @@
//! //!
//! - [`AscribeUserType`] //! - [`AscribeUserType`]
//! - [`FakeRead`] //! - [`FakeRead`]
//! - [`Assign`] statements with a [`Shallow`] borrow //! - [`Assign`] statements with a [`Fake`] borrow
//! //!
//! [`AscribeUserType`]: rustc_middle::mir::StatementKind::AscribeUserType //! [`AscribeUserType`]: rustc_middle::mir::StatementKind::AscribeUserType
//! [`Assign`]: rustc_middle::mir::StatementKind::Assign //! [`Assign`]: rustc_middle::mir::StatementKind::Assign
//! [`FakeRead`]: rustc_middle::mir::StatementKind::FakeRead //! [`FakeRead`]: rustc_middle::mir::StatementKind::FakeRead
//! [`Nop`]: rustc_middle::mir::StatementKind::Nop //! [`Nop`]: rustc_middle::mir::StatementKind::Nop
//! [`Shallow`]: rustc_middle::mir::BorrowKind::Shallow //! [`Fake`]: rustc_middle::mir::BorrowKind::Fake
use crate::MirPass; use crate::MirPass;
use rustc_middle::mir::{Body, BorrowKind, Rvalue, StatementKind, TerminatorKind}; use rustc_middle::mir::{Body, BorrowKind, Rvalue, StatementKind, TerminatorKind};
@ -24,7 +24,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::Shallow, _))) | StatementKind::Assign(box (_, Rvalue::Ref(_, BorrowKind::Fake, _)))
| StatementKind::FakeRead(..) => statement.make_nop(), | StatementKind::FakeRead(..) => statement.make_nop(),
_ => (), _ => (),
} }

View file

@ -668,7 +668,7 @@ impl<'tcx> Visitor<'tcx> for CanConstProp {
// These can't ever be propagated under any scheme, as we can't reason about indirect // These can't ever be propagated under any scheme, as we can't reason about indirect
// mutation. // mutation.
| NonMutatingUse(NonMutatingUseContext::SharedBorrow) | NonMutatingUse(NonMutatingUseContext::SharedBorrow)
| NonMutatingUse(NonMutatingUseContext::ShallowBorrow) | NonMutatingUse(NonMutatingUseContext::FakeBorrow)
| NonMutatingUse(NonMutatingUseContext::AddressOf) | NonMutatingUse(NonMutatingUseContext::AddressOf)
| MutatingUse(MutatingUseContext::Borrow) | MutatingUse(MutatingUseContext::Borrow)
| MutatingUse(MutatingUseContext::AddressOf) => { | MutatingUse(MutatingUseContext::AddressOf) => {

View file

@ -131,7 +131,7 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'_, 'tcx> {
let observes_address = match ctxt { let observes_address = match ctxt {
PlaceContext::NonMutatingUse( PlaceContext::NonMutatingUse(
NonMutatingUseContext::SharedBorrow NonMutatingUseContext::SharedBorrow
| NonMutatingUseContext::ShallowBorrow | NonMutatingUseContext::FakeBorrow
| NonMutatingUseContext::AddressOf, | NonMutatingUseContext::AddressOf,
) => true, ) => true,
// For debuginfo, merging locals is ok. // For debuginfo, merging locals is ok.

View file

@ -637,6 +637,14 @@ struct LivenessInfo {
storage_liveness: IndexVec<BasicBlock, Option<BitSet<Local>>>, storage_liveness: IndexVec<BasicBlock, Option<BitSet<Local>>>,
} }
/// Computes which locals have to be stored in the state-machine for the
/// given coroutine.
///
/// The basic idea is as follows:
/// - a local is live until we encounter a `StorageDead` statement. In
/// case none exist, the local is considered to be always live.
/// - a local has to be stored if it is either directly used after the
/// the suspend point, or if it is live and has been previously borrowed.
fn locals_live_across_suspend_points<'tcx>( fn locals_live_across_suspend_points<'tcx>(
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
body: &Body<'tcx>, body: &Body<'tcx>,
@ -1449,16 +1457,15 @@ pub(crate) fn mir_coroutine_witnesses<'tcx>(
// The first argument is the coroutine type passed by value // The first argument is the coroutine type passed by value
let coroutine_ty = body.local_decls[ty::CAPTURE_STRUCT_LOCAL].ty; let coroutine_ty = body.local_decls[ty::CAPTURE_STRUCT_LOCAL].ty;
// Get the interior types and args which typeck computed
let movable = match *coroutine_ty.kind() { let movable = match *coroutine_ty.kind() {
ty::Coroutine(_, _, movability) => movability == hir::Movability::Movable, ty::Coroutine(_, _, movability) => movability == hir::Movability::Movable,
ty::Error(_) => return None, ty::Error(_) => return None,
_ => span_bug!(body.span, "unexpected coroutine type {}", coroutine_ty), _ => span_bug!(body.span, "unexpected coroutine type {}", coroutine_ty),
}; };
// When first entering the coroutine, move the resume argument into its new local. // The witness simply contains all locals live across suspend points.
let always_live_locals = always_storage_live_locals(&body);
let always_live_locals = always_storage_live_locals(&body);
let liveness_info = locals_live_across_suspend_points(tcx, body, &always_live_locals, movable); let liveness_info = locals_live_across_suspend_points(tcx, body, &always_live_locals, movable);
// Extract locals which are live across suspension point into `layout` // Extract locals which are live across suspension point into `layout`

View file

@ -234,7 +234,7 @@ impl<'tcx> Visitor<'tcx> for SsaVisitor<'_> {
// so we have to remove them too. // so we have to remove them too.
PlaceContext::NonMutatingUse( PlaceContext::NonMutatingUse(
NonMutatingUseContext::SharedBorrow NonMutatingUseContext::SharedBorrow
| NonMutatingUseContext::ShallowBorrow | NonMutatingUseContext::FakeBorrow
| NonMutatingUseContext::AddressOf, | NonMutatingUseContext::AddressOf,
) )
| PlaceContext::MutatingUse(_) => { | PlaceContext::MutatingUse(_) => {

View file

@ -455,7 +455,7 @@ impl<'tcx> Stable<'tcx> for mir::BorrowKind {
use mir::BorrowKind::*; use mir::BorrowKind::*;
match *self { match *self {
Shared => stable_mir::mir::BorrowKind::Shared, Shared => stable_mir::mir::BorrowKind::Shared,
Shallow => stable_mir::mir::BorrowKind::Shallow, Fake => stable_mir::mir::BorrowKind::Fake,
Mut { kind } => stable_mir::mir::BorrowKind::Mut { kind: kind.stable(tables) }, Mut { kind } => stable_mir::mir::BorrowKind::Mut { kind: kind.stable(tables) },
} }
} }

View file

@ -14,6 +14,7 @@ use crate::solve::EvalCtxt;
// //
// For types with an "existential" binder, i.e. coroutine witnesses, we also // For types with an "existential" binder, i.e. coroutine witnesses, we also
// instantiate the binder with placeholders eagerly. // instantiate the binder with placeholders eagerly.
#[instrument(level = "debug", skip(ecx), ret)]
pub(in crate::solve) fn instantiate_constituent_tys_for_auto_trait<'tcx>( pub(in crate::solve) fn instantiate_constituent_tys_for_auto_trait<'tcx>(
ecx: &EvalCtxt<'_, 'tcx>, ecx: &EvalCtxt<'_, 'tcx>,
ty: Ty<'tcx>, ty: Ty<'tcx>,
@ -107,6 +108,7 @@ pub(in crate::solve) fn replace_erased_lifetimes_with_bound_vars<'tcx>(
ty::Binder::bind_with_vars(ty, bound_vars) ty::Binder::bind_with_vars(ty, bound_vars)
} }
#[instrument(level = "debug", skip(ecx), ret)]
pub(in crate::solve) fn instantiate_constituent_tys_for_sized_trait<'tcx>( pub(in crate::solve) fn instantiate_constituent_tys_for_sized_trait<'tcx>(
ecx: &EvalCtxt<'_, 'tcx>, ecx: &EvalCtxt<'_, 'tcx>,
ty: Ty<'tcx>, ty: Ty<'tcx>,
@ -152,6 +154,7 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_sized_trait<'tcx>(
} }
} }
#[instrument(level = "debug", skip(ecx), ret)]
pub(in crate::solve) fn instantiate_constituent_tys_for_copy_clone_trait<'tcx>( pub(in crate::solve) fn instantiate_constituent_tys_for_copy_clone_trait<'tcx>(
ecx: &EvalCtxt<'_, 'tcx>, ecx: &EvalCtxt<'_, 'tcx>,
ty: Ty<'tcx>, ty: Ty<'tcx>,

View file

@ -437,9 +437,10 @@ pub enum BorrowKind {
Shared, Shared,
/// The immediately borrowed place must be immutable, but projections from /// 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 /// 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`. /// conflict with a mutable borrow of `a.b.c`.
Shallow, Fake,
/// Data is mutable and not aliasable. /// Data is mutable and not aliasable.
Mut { Mut {

View file

@ -52,7 +52,7 @@ fn full_tested_match() -> () {
bb5: { bb5: {
StorageLive(_6); StorageLive(_6);
_6 = &((_2 as Some).0: i32); _6 = &((_2 as Some).0: i32);
_4 = &shallow _2; _4 = &fake _2;
StorageLive(_7); StorageLive(_7);
_7 = guard() -> [return: bb6, unwind: bb12]; _7 = guard() -> [return: bb6, unwind: bb12];
} }

View file

@ -58,7 +58,7 @@ fn full_tested_match2() -> () {
bb5: { bb5: {
StorageLive(_6); StorageLive(_6);
_6 = &((_2 as Some).0: i32); _6 = &((_2 as Some).0: i32);
_4 = &shallow _2; _4 = &fake _2;
StorageLive(_7); StorageLive(_7);
_7 = guard() -> [return: bb6, unwind: bb12]; _7 = guard() -> [return: bb6, unwind: bb12];
} }

View file

@ -78,7 +78,7 @@ fn main() -> () {
bb8: { bb8: {
StorageLive(_7); StorageLive(_7);
_7 = &((_2 as Some).0: i32); _7 = &((_2 as Some).0: i32);
_5 = &shallow _2; _5 = &fake _2;
StorageLive(_8); StorageLive(_8);
_8 = guard() -> [return: bb9, unwind: bb20]; _8 = guard() -> [return: bb9, unwind: bb20];
} }
@ -120,7 +120,7 @@ fn main() -> () {
bb14: { bb14: {
StorageLive(_11); StorageLive(_11);
_11 = &((_2 as Some).0: i32); _11 = &((_2 as Some).0: i32);
_5 = &shallow _2; _5 = &fake _2;
StorageLive(_12); StorageLive(_12);
StorageLive(_13); StorageLive(_13);
_13 = (*_11); _13 = (*_11);

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 = &shallow (_2.0: bool); - _3 = &fake (_2.0: bool);
- _4 = &shallow (_2.1: bool); - _4 = &fake (_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 = &shallow (_2.0: bool); - _3 = &fake (_2.0: bool);
- _4 = &shallow (_2.1: bool); - _4 = &fake (_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 = &shallow (_2.0: bool); - _3 = &fake (_2.0: bool);
- _4 = &shallow (_2.1: bool); - _4 = &fake (_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 = &shallow (_2.0: bool); - _3 = &fake (_2.0: bool);
- _4 = &shallow (_2.1: bool); - _4 = &fake (_2.1: bool);
StorageLive(_12); StorageLive(_12);
StorageLive(_13); StorageLive(_13);
_13 = _1; _13 = _1;

View file

@ -68,7 +68,7 @@ fn main() -> () {
} }
bb9: { bb9: {
_8 = &shallow _1; _8 = &fake _1;
StorageLive(_9); StorageLive(_9);
_9 = _2; _9 = _2;
switchInt(move _9) -> [0: bb11, otherwise: bb10]; switchInt(move _9) -> [0: bb11, otherwise: bb10];

View file

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

View file

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

View file

@ -0,0 +1,34 @@
// check-pass
// edition: 2021
// regression test for #117059
struct SendNotSync(*const ());
unsafe impl Send for SendNotSync {}
// impl !Sync for SendNotSync {} // automatically disabled
struct Inner {
stream: SendNotSync,
state: bool,
}
struct SendSync;
impl std::ops::Deref for SendSync {
type Target = Inner;
fn deref(&self) -> &Self::Target {
todo!();
}
}
async fn next() {
let inner = SendSync;
match inner.state {
true if false => {}
false => async {}.await,
_ => {}
}
}
fn is_send<T: Send>(_: T) {}
fn main() {
is_send(next())
}