Skip to content

Commit 6cd7eec

Browse files
committed
WIP: implement borrowck
1 parent ad8bce1 commit 6cd7eec

3 files changed

Lines changed: 33 additions & 131 deletions

File tree

compiler/rustc_borrowck/src/borrow_set.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,8 @@ impl<'tcx> BorrowData<'tcx> {
126126
matches!(self.pinnedness, Pinnedness::Pinned { .. })
127127
}
128128

129-
/// Returns the Pin result local if this borrow is pinned.
129+
/// Returns the Pin result place if this borrow is pinned.
130+
#[allow(dead_code)]
130131
pub(crate) fn pin_target_local(&self) -> Option<mir::Local> {
131132
match self.pinnedness {
132133
Pinnedness::Pinned { to, .. } => Some(to.local),

compiler/rustc_borrowck/src/dataflow.rs

Lines changed: 15 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -240,12 +240,6 @@ impl<'tcx> OutOfScopePrecomputer<'_, 'tcx> {
240240
borrows_out_of_scope_at_location: FxIndexMap::default(),
241241
};
242242
for (borrow_index, borrow_data) in borrow_set.iter_enumerated() {
243-
// Skip pinned borrows: their lifetime is extended beyond NLL scope.
244-
// They are killed by StorageDead of the Pin result local or by
245-
// reassignment of the borrowed place.
246-
if borrow_data.is_pinned() {
247-
continue;
248-
}
249243
let borrow_region = borrow_data.region;
250244
let location = borrow_data.reserve_location;
251245
prec.precompute_borrows_out_of_scope(borrow_index, borrow_region, location);
@@ -362,10 +356,6 @@ impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> {
362356
loans_out_of_scope_at_location: FxIndexMap::default(),
363357
};
364358
for (loan_idx, loan_data) in borrow_set.iter_enumerated() {
365-
// Skip pinned borrows: their lifetime is extended beyond NLL scope.
366-
if loan_data.is_pinned() {
367-
continue;
368-
}
369359
let loan_issued_at = loan_data.reserve_location;
370360
prec.precompute_loans_out_of_scope(loan_idx, loan_issued_at);
371361
}
@@ -556,91 +546,6 @@ impl<'a, 'tcx> Borrows<'a, 'tcx> {
556546

557547
state.kill_all(definitely_conflicting_borrows);
558548
}
559-
560-
/// Kill pinned borrows whose borrowed place's local matches `local`.
561-
/// This is called in the early phase when the borrowed place's storage dies,
562-
/// to prevent spurious E0716 errors.
563-
fn kill_pinned_borrows_on_borrowed_local(
564-
&self,
565-
state: &mut <Self as Analysis<'tcx>>::Domain,
566-
local: mir::Local,
567-
) {
568-
debug!("kill_pinned_borrows_on_borrowed_local: local={:?}", local);
569-
570-
let to_kill: Vec<_> = self
571-
.borrow_set
572-
.iter_enumerated()
573-
.filter_map(|(idx, data)| {
574-
if data.is_pinned() && data.borrowed_place.local == local {
575-
Some(idx)
576-
} else {
577-
None
578-
}
579-
})
580-
.collect();
581-
582-
state.kill_all(to_kill.into_iter());
583-
}
584-
585-
/// Kill pinned borrows whose Pin result local matches `local`.
586-
/// This is called when the Pin value goes out of scope (StorageDead).
587-
fn kill_pinned_borrows_on_local(
588-
&self,
589-
state: &mut <Self as Analysis<'tcx>>::Domain,
590-
local: mir::Local,
591-
) {
592-
debug!("kill_pinned_borrows_on_local: local={:?}", local);
593-
594-
let to_kill: Vec<_> = self
595-
.borrow_set
596-
.iter_enumerated()
597-
.filter_map(
598-
|(idx, data)| {
599-
if data.pin_target_local() == Some(local) { Some(idx) } else { None }
600-
},
601-
)
602-
.collect();
603-
604-
state.kill_all(to_kill.into_iter());
605-
}
606-
607-
/// Kill pinned borrows whose borrowed place conflicts with `place`.
608-
/// Called in the early phase so reassignment of a pinned place is allowed.
609-
fn kill_pinned_borrows_on_reassignment(
610-
&self,
611-
state: &mut <Self as Analysis<'tcx>>::Domain,
612-
place: Place<'tcx>,
613-
) {
614-
debug!("kill_pinned_borrows_on_reassignment: place={:?}", place);
615-
616-
let other_borrows_of_local = self
617-
.borrow_set
618-
.local_map
619-
.get(&place.local)
620-
.into_iter()
621-
.flat_map(|bs| bs.iter())
622-
.copied();
623-
624-
if place.projection.is_empty() {
625-
// Only kill pinned borrows, not regular ones
626-
let pinned_borrows = other_borrows_of_local.filter(|&i| self.borrow_set[i].is_pinned());
627-
state.kill_all(pinned_borrows);
628-
return;
629-
}
630-
631-
let conflicting_pinned_borrows = other_borrows_of_local.filter(|&i| {
632-
self.borrow_set[i].is_pinned()
633-
&& places_conflict(
634-
self.tcx,
635-
self.body,
636-
self.borrow_set[i].borrowed_place,
637-
place,
638-
PlaceConflictBias::NoOverlap,
639-
)
640-
});
641-
642-
state.kill_all(conflicting_pinned_borrows);
643-
}
644549
}
645550

646551
type BorrowsDomain = MixedBitSet<BorrowIndex>;
@@ -670,29 +575,10 @@ impl<'tcx> rustc_mir_dataflow::Analysis<'tcx> for Borrows<'_, 'tcx> {
670575
fn apply_early_statement_effect(
671576
&self,
672577
state: &mut Self::Domain,
673-
statement: &mir::Statement<'tcx>,
578+
_statement: &mir::Statement<'tcx>,
674579
location: Location,
675580
) {
676581
self.kill_loans_out_of_scope_at_location(state, location);
677-
678-
match &statement.kind {
679-
// For pinned borrows whose lifetime is extended beyond NLL scope,
680-
// kill them early when the pinned place is reassigned. This allows
681-
// the reassignment to proceed without conflicting with the borrow.
682-
mir::StatementKind::Assign(box (lhs, _)) => {
683-
self.kill_pinned_borrows_on_reassignment(state, *lhs);
684-
}
685-
// Kill pinned borrows in the early phase when the borrowed place's
686-
// local has StorageDead. This must happen before the visitor's
687-
// StorageDead check (which runs after early effects) to avoid
688-
// spurious E0716 errors for temporaries whose storage dies before
689-
// the Pin result local.
690-
mir::StatementKind::StorageDead(local) => {
691-
self.kill_pinned_borrows_on_local(state, *local);
692-
self.kill_pinned_borrows_on_borrowed_local(state, *local);
693-
}
694-
_ => {}
695-
}
696582
}
697583

698584
fn apply_primary_statement_effect(
@@ -727,8 +613,6 @@ impl<'tcx> rustc_mir_dataflow::Analysis<'tcx> for Borrows<'_, 'tcx> {
727613
// Make sure there are no remaining borrows for locals that
728614
// are gone out of scope.
729615
self.kill_borrows_on_place(state, Place::from(*local));
730-
// Kill pinned borrows whose Pin result local is going out of scope.
731-
self.kill_pinned_borrows_on_local(state, *local);
732616
}
733617

734618
mir::StatementKind::FakeRead(..)
@@ -848,11 +732,22 @@ impl<'tcx> rustc_mir_dataflow::Analysis<'tcx> for Pins<'_, 'tcx> {
848732

849733
fn apply_early_statement_effect(
850734
&self,
851-
_state: &mut Self::Domain,
852-
_statement: &mir::Statement<'tcx>,
735+
state: &mut Self::Domain,
736+
statement: &mir::Statement<'tcx>,
853737
_location: Location,
854738
) {
855-
// Pins don't go out of scope based on regions like borrows do
739+
// Kill pins early on reassignment/StorageDead so that the visitor
740+
// (which runs after the early phase) sees the updated pin state.
741+
match &statement.kind {
742+
mir::StatementKind::Assign(box (lhs, _)) => {
743+
self.kill_pins_on_place(state, *lhs);
744+
}
745+
mir::StatementKind::StorageDead(local) => {
746+
self.kill_pins_on_place(state, Place::from(*local));
747+
self.kill_pins_by_pin_local(state, *local);
748+
}
749+
_ => {}
750+
}
856751
}
857752

858753
fn apply_primary_statement_effect(

compiler/rustc_borrowck/src/lib.rs

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1037,15 +1037,6 @@ impl<'a, 'tcx> ResultsVisitor<'tcx, Borrowck<'a, 'tcx>> for MirBorrowckCtxt<'a,
10371037
// so this "extra check" serves as a kind of backup.
10381038
for i in state.borrows.iter() {
10391039
let borrow = &self.borrow_set[i];
1040-
// Skip pinned borrows: their lifetime is managed by
1041-
// StorageDead of the Pin result local and reassignment,
1042-
// not by NLL regions. On unwind paths, StorageDead may
1043-
// not have been emitted, so these borrows can appear
1044-
// spuriously alive. The normal dataflow-based conflict
1045-
// detection already catches pin violations.
1046-
if borrow.is_pinned() {
1047-
continue;
1048-
}
10491040
self.check_for_invalidation_at_exit(loc, borrow, span);
10501041
}
10511042
}
@@ -1275,7 +1266,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
12751266
location: Location,
12761267
state: &'s BorrowckDomain,
12771268
) -> Cow<'s, MixedBitSet<BorrowIndex>> {
1278-
if let Some(polonius) = &self.polonius_output {
1269+
let mut borrows = if let Some(polonius) = &self.polonius_output {
12791270
// Use polonius output if it has been enabled.
12801271
let location = self.location_table.start_index(location);
12811272
let mut polonius_output = MixedBitSet::new_empty(self.borrow_set.len());
@@ -1285,7 +1276,22 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
12851276
Cow::Owned(polonius_output)
12861277
} else {
12871278
Cow::Borrowed(&state.borrows)
1279+
};
1280+
1281+
// For pinned borrows, the Pins dataflow determines their liveness
1282+
// independently of NLL regions. If a pin is active, its corresponding
1283+
// borrow should be treated as in scope even if NLL killed it.
1284+
for (borrow_idx, borrow_data) in self.borrow_set.iter_enumerated() {
1285+
if let crate::borrow_set::Pinnedness::Pinned { at, .. } = borrow_data.pinnedness {
1286+
if let Some(pin_idx) = self.pin_set.get_index_of(&at) {
1287+
if state.pins.contains(pin_idx) && !borrows.contains(borrow_idx) {
1288+
borrows.to_mut().insert(borrow_idx);
1289+
}
1290+
}
1291+
}
12881292
}
1293+
1294+
borrows
12891295
}
12901296

12911297
#[instrument(level = "debug", skip(self, state))]

0 commit comments

Comments
 (0)