Skip to content

Commit 05b9a73

Browse files
committed
Implement borrowck of pinned borrows
1 parent e8e4541 commit 05b9a73

30 files changed

Lines changed: 768 additions & 362 deletions

File tree

compiler/rustc_borrowck/src/borrow_set.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,8 @@ impl<'tcx> fmt::Display for BorrowData<'tcx> {
124124
mir::BorrowKind::Mut {
125125
kind: mir::MutBorrowKind::Default | mir::MutBorrowKind::TwoPhaseBorrow,
126126
} => "mut ",
127+
mir::BorrowKind::Pinned(mir::Mutability::Not) => "pin const ",
128+
mir::BorrowKind::Pinned(mir::Mutability::Mut) => "pin mut ",
127129
};
128130
write!(w, "&{:?} {}{:?}", self.region, kind, self.borrowed_place)
129131
}

compiler/rustc_borrowck/src/borrowck_errors.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,38 @@ impl<'infcx, 'tcx> crate::MirBorrowckCtxt<'_, 'infcx, 'tcx> {
484484
pub(crate) fn temporary_value_borrowed_for_too_long(&self, span: Span) -> Diag<'infcx> {
485485
struct_span_code_err!(self.dcx(), span, E0716, "temporary value dropped while borrowed")
486486
}
487+
488+
pub(crate) fn cannot_move_out_while_pinned(
489+
&self,
490+
span: Span,
491+
pin_span: Span,
492+
place: &str,
493+
pin_place: &str,
494+
value_place: &str,
495+
) -> Diag<'infcx> {
496+
self.dcx().create_err(crate::session_diagnostics::MovePin {
497+
place,
498+
pin_place,
499+
value_place,
500+
span,
501+
pin_span,
502+
})
503+
}
504+
505+
pub(crate) fn cannot_mutably_borrow_pinned(
506+
&self,
507+
span: Span,
508+
pin_span: Span,
509+
place: &str,
510+
pin_place: &str,
511+
) -> Diag<'infcx> {
512+
self.dcx().create_err(crate::session_diagnostics::MutablyBorrowPin {
513+
place,
514+
span,
515+
pin_place,
516+
pin_span,
517+
})
518+
}
487519
}
488520

489521
pub(crate) fn borrowed_data_escapes_closure<'tcx>(

compiler/rustc_borrowck/src/dataflow.rs

Lines changed: 193 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ use crate::{BorrowSet, PlaceConflictBias, PlaceExt, RegionInferenceContext, plac
2121
// computed individually with `iterate_to_fixpoint`.
2222
pub(crate) struct Borrowck<'a, 'tcx> {
2323
pub(crate) borrows: Borrows<'a, 'tcx>,
24+
pub(crate) pins: Pins<'a, 'tcx>,
2425
pub(crate) uninits: MaybeUninitializedPlaces<'a, 'tcx>,
2526
pub(crate) ever_inits: EverInitializedPlaces<'a, 'tcx>,
2627
}
@@ -33,6 +34,7 @@ impl<'a, 'tcx> Analysis<'tcx> for Borrowck<'a, 'tcx> {
3334
fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain {
3435
BorrowckDomain {
3536
borrows: self.borrows.bottom_value(body),
37+
pinned_borrows: self.pins.bottom_value(body),
3638
uninits: self.uninits.bottom_value(body),
3739
ever_inits: self.ever_inits.bottom_value(body),
3840
}
@@ -50,6 +52,7 @@ impl<'a, 'tcx> Analysis<'tcx> for Borrowck<'a, 'tcx> {
5052
loc: Location,
5153
) {
5254
self.borrows.apply_early_statement_effect(&mut state.borrows, stmt, loc);
55+
self.pins.apply_early_statement_effect(&mut state.pinned_borrows, stmt, loc);
5356
self.uninits.apply_early_statement_effect(&mut state.uninits, stmt, loc);
5457
self.ever_inits.apply_early_statement_effect(&mut state.ever_inits, stmt, loc);
5558
}
@@ -61,6 +64,7 @@ impl<'a, 'tcx> Analysis<'tcx> for Borrowck<'a, 'tcx> {
6164
loc: Location,
6265
) {
6366
self.borrows.apply_primary_statement_effect(&mut state.borrows, stmt, loc);
67+
self.pins.apply_primary_statement_effect(&mut state.pinned_borrows, stmt, loc);
6468
self.uninits.apply_primary_statement_effect(&mut state.uninits, stmt, loc);
6569
self.ever_inits.apply_primary_statement_effect(&mut state.ever_inits, stmt, loc);
6670
}
@@ -72,6 +76,7 @@ impl<'a, 'tcx> Analysis<'tcx> for Borrowck<'a, 'tcx> {
7276
loc: Location,
7377
) {
7478
self.borrows.apply_early_terminator_effect(&mut state.borrows, term, loc);
79+
self.pins.apply_early_terminator_effect(&mut state.pinned_borrows, term, loc);
7580
self.uninits.apply_early_terminator_effect(&mut state.uninits, term, loc);
7681
self.ever_inits.apply_early_terminator_effect(&mut state.ever_inits, term, loc);
7782
}
@@ -83,6 +88,7 @@ impl<'a, 'tcx> Analysis<'tcx> for Borrowck<'a, 'tcx> {
8388
loc: Location,
8489
) -> TerminatorEdges<'mir, 'tcx> {
8590
self.borrows.apply_primary_terminator_effect(&mut state.borrows, term, loc);
91+
self.pins.apply_primary_terminator_effect(&mut state.pinned_borrows, term, loc);
8692
self.uninits.apply_primary_terminator_effect(&mut state.uninits, term, loc);
8793
self.ever_inits.apply_primary_terminator_effect(&mut state.ever_inits, term, loc);
8894

@@ -116,6 +122,8 @@ where
116122
fn fmt_with(&self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
117123
f.write_str("borrows: ")?;
118124
self.borrows.fmt_with(ctxt, f)?;
125+
f.write_str(" pinned_borrows: ")?;
126+
self.pinned_borrows.fmt_with(ctxt, f)?;
119127
f.write_str(" uninits: ")?;
120128
self.uninits.fmt_with(ctxt, f)?;
121129
f.write_str(" ever_inits: ")?;
@@ -134,6 +142,12 @@ where
134142
f.write_str("\n")?;
135143
}
136144

145+
if self.pinned_borrows != old.pinned_borrows {
146+
f.write_str("pinned_borrows: ")?;
147+
self.pinned_borrows.fmt_diff_with(&old.pinned_borrows, ctxt, f)?;
148+
f.write_str("\n")?;
149+
}
150+
137151
if self.uninits != old.uninits {
138152
f.write_str("uninits: ")?;
139153
self.uninits.fmt_diff_with(&old.uninits, ctxt, f)?;
@@ -154,6 +168,7 @@ where
154168
#[derive(Clone, Debug, PartialEq, Eq)]
155169
pub(crate) struct BorrowckDomain {
156170
pub(crate) borrows: BorrowsDomain,
171+
pub(crate) pinned_borrows: BorrowsDomain,
157172
pub(crate) uninits: MaybeUninitializedPlacesDomain,
158173
pub(crate) ever_inits: EverInitializedPlacesDomain,
159174
}
@@ -164,6 +179,8 @@ rustc_index::newtype_index! {
164179
pub struct BorrowIndex {}
165180
}
166181

182+
impl<C> DebugWithContext<C> for BorrowIndex {}
183+
167184
/// `Borrows` stores the data used in the analyses that track the flow
168185
/// of borrows.
169186
///
@@ -178,6 +195,19 @@ pub struct Borrows<'a, 'tcx> {
178195
borrows_out_of_scope_at_location: FxIndexMap<Location, Vec<BorrowIndex>>,
179196
}
180197

198+
/// `Pins` stores the data used in the analyses that track the flow
199+
/// of pins.
200+
///
201+
/// It uniquely identifies every pinned borrow by a
202+
/// `PinIndex`, and maps each such index to a `PinData`
203+
/// describing the pin. These indexes are used for representing the
204+
/// pins in compact bitvectors.
205+
pub(crate) struct Pins<'a, 'tcx> {
206+
tcx: TyCtxt<'tcx>,
207+
body: &'a Body<'tcx>,
208+
borrow_set: &'a BorrowSet<'tcx>,
209+
}
210+
181211
struct OutOfScopePrecomputer<'a, 'tcx> {
182212
visited: DenseBitSet<mir::BasicBlock>,
183213
visit_stack: Vec<mir::BasicBlock>,
@@ -617,4 +647,166 @@ impl<'tcx> rustc_mir_dataflow::Analysis<'tcx> for Borrows<'_, 'tcx> {
617647
}
618648
}
619649

620-
impl<C> DebugWithContext<C> for BorrowIndex {}
650+
impl<'a, 'tcx> Pins<'a, 'tcx> {
651+
pub(crate) fn new(
652+
tcx: TyCtxt<'tcx>,
653+
body: &'a Body<'tcx>,
654+
borrow_set: &'a BorrowSet<'tcx>,
655+
) -> Self {
656+
Pins { tcx, body, borrow_set }
657+
}
658+
659+
fn gen_pins_on_place(&self, state: &mut <Self as Analysis<'tcx>>::Domain, place: Place<'tcx>) {
660+
self.borrow_set
661+
.local_map
662+
.get(&place.local)
663+
.into_iter()
664+
.flat_map(|bs| bs.iter())
665+
.copied()
666+
.filter(|&index| self.borrow_set[index].borrowed_place == place)
667+
.for_each(|index| {
668+
state.gen_(index);
669+
});
670+
}
671+
672+
/// Kill any pins whose original pinned place conflicts with `place`.
673+
fn kill_pins_on_place(&self, state: &mut <Self as Analysis<'tcx>>::Domain, place: Place<'tcx>) {
674+
debug!("kill_pins_on_place: place={:?}", place);
675+
676+
let other_pins_of_local = self
677+
.borrow_set
678+
.local_map
679+
.get(&place.local)
680+
.into_iter()
681+
.flat_map(|bs| bs.iter())
682+
.copied();
683+
684+
// If the place is a local with no projections, all pins of this
685+
// local must be killed. This is purely an optimization so we don't have to call
686+
// `places_conflict` for every pin.
687+
if place.projection.is_empty() {
688+
state.kill_all(other_pins_of_local);
689+
return;
690+
}
691+
692+
// By passing `PlaceConflictBias::NoOverlap`, we conservatively assume that any given
693+
// pair of array indices are not equal, so that when `places_conflict` returns true, we
694+
// will be assured that two places being compared definitely denotes the same sets of
695+
// locations.
696+
let definitely_conflicting_pins = other_pins_of_local.filter(|&i| {
697+
places_conflict(
698+
self.tcx,
699+
self.body,
700+
self.borrow_set[i].borrowed_place,
701+
place,
702+
PlaceConflictBias::NoOverlap,
703+
)
704+
});
705+
706+
state.kill_all(definitely_conflicting_pins);
707+
}
708+
}
709+
710+
/// Forward dataflow computation of the set of pins that are in scope at a particular location.
711+
/// - we gen the introduced pins
712+
/// - we kill pins on locals going out of scope
713+
/// - we kill pins when the pinned place is moved or overwritten
714+
impl<'tcx> rustc_mir_dataflow::Analysis<'tcx> for Pins<'_, 'tcx> {
715+
type Domain = BorrowsDomain;
716+
717+
const NAME: &'static str = "pins";
718+
719+
fn bottom_value(&self, _: &mir::Body<'tcx>) -> Self::Domain {
720+
// bottom = nothing is pinned yet
721+
MixedBitSet::new_empty(self.borrow_set.len())
722+
}
723+
724+
fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut Self::Domain) {
725+
// no pins have been created prior to function execution
726+
}
727+
728+
fn apply_early_statement_effect(
729+
&self,
730+
state: &mut Self::Domain,
731+
statement: &mir::Statement<'tcx>,
732+
_location: Location,
733+
) {
734+
// Kill pins early on reassignment/StorageDead so that the visitor
735+
// (which runs after the early phase) sees the updated pin state.
736+
// Note: we do NOT kill pins when the Pin result local goes out of scope
737+
// (kill_pins_by_pin_local), because a pinned place stays pinned until
738+
// the place itself is reassigned.
739+
match &statement.kind {
740+
mir::StatementKind::Assign(box (lhs, _)) => {
741+
self.kill_pins_on_place(state, *lhs);
742+
}
743+
mir::StatementKind::StorageDead(local) => {
744+
self.kill_pins_on_place(state, Place::from(*local));
745+
}
746+
_ => {}
747+
}
748+
}
749+
750+
fn apply_primary_statement_effect(
751+
&self,
752+
state: &mut Self::Domain,
753+
stmt: &mir::Statement<'tcx>,
754+
_location: Location,
755+
) {
756+
match &stmt.kind {
757+
mir::StatementKind::Assign(box (lhs, rhs)) => {
758+
self.kill_pins_on_place(state, *lhs);
759+
760+
// Check if this is a pinned borrow
761+
if let mir::Rvalue::Ref(_, mir::BorrowKind::Pinned(_), place) = rhs {
762+
// Generate the pin
763+
self.gen_pins_on_place(state, *place);
764+
}
765+
}
766+
767+
mir::StatementKind::StorageDead(local) => {
768+
// Kill all pins on locals that are going out of scope
769+
self.kill_pins_on_place(state, Place::from(*local));
770+
}
771+
772+
mir::StatementKind::FakeRead(..)
773+
| mir::StatementKind::SetDiscriminant { .. }
774+
| mir::StatementKind::StorageLive(..)
775+
| mir::StatementKind::Retag { .. }
776+
| mir::StatementKind::PlaceMention(..)
777+
| mir::StatementKind::AscribeUserType(..)
778+
| mir::StatementKind::Coverage(..)
779+
| mir::StatementKind::Intrinsic(..)
780+
| mir::StatementKind::ConstEvalCounter
781+
| mir::StatementKind::BackwardIncompatibleDropHint { .. }
782+
| mir::StatementKind::Nop => {}
783+
}
784+
}
785+
786+
fn apply_early_terminator_effect(
787+
&self,
788+
_state: &mut Self::Domain,
789+
_terminator: &mir::Terminator<'tcx>,
790+
_location: Location,
791+
) {
792+
// No early terminator effects for pins
793+
}
794+
795+
fn apply_primary_terminator_effect<'mir>(
796+
&self,
797+
state: &mut Self::Domain,
798+
terminator: &'mir mir::Terminator<'tcx>,
799+
_location: Location,
800+
) -> TerminatorEdges<'mir, 'tcx> {
801+
if let mir::TerminatorKind::InlineAsm { operands, .. } = &terminator.kind {
802+
for op in operands {
803+
if let mir::InlineAsmOperand::Out { place: Some(place), .. }
804+
| mir::InlineAsmOperand::InOut { out_place: Some(place), .. } = *op
805+
{
806+
self.kill_pins_on_place(state, place);
807+
}
808+
}
809+
}
810+
terminator.edges()
811+
}
812+
}

compiler/rustc_borrowck/src/def_use.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,13 @@ pub(crate) fn categorize(context: PlaceContext) -> Option<DefUse> {
5656
PlaceContext::NonUse(NonUseContext::AscribeUserTy(_)) |
5757

5858
PlaceContext::MutatingUse(MutatingUseContext::RawBorrow) |
59+
PlaceContext::MutatingUse(MutatingUseContext::PinnedBorrow) |
5960
PlaceContext::NonMutatingUse(NonMutatingUseContext::RawBorrow) |
6061
PlaceContext::NonMutatingUse(NonMutatingUseContext::Inspect) |
6162
PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy) |
6263
PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) |
63-
PlaceContext::MutatingUse(MutatingUseContext::Retag) =>
64+
PlaceContext::MutatingUse(MutatingUseContext::Retag) |
65+
PlaceContext::NonMutatingUse(NonMutatingUseContext::PinnedBorrow) =>
6466
Some(DefUse::Use),
6567

6668
///////////////////////////////////////////////////////////////////////////

0 commit comments

Comments
 (0)