Skip to content

Commit d007e81

Browse files
committed
WIP: implelment borrowck
1 parent ad34318 commit d007e81

3 files changed

Lines changed: 145 additions & 2 deletions

File tree

compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,9 @@ use super::{DescribePlaceOpt, RegionName, RegionNameSource, UseSpans};
4444
use crate::borrow_set::{BorrowData, TwoPhaseActivation};
4545
use crate::diagnostics::conflict_errors::StorageDeadOrDrop::LocalStorageDead;
4646
use crate::diagnostics::{CapturedMessageOpt, call_kind, find_all_local_uses};
47+
use crate::pin_set::PinData;
4748
use crate::prefixes::IsPrefixOf;
48-
use crate::{InitializationRequiringAction, MirBorrowckCtxt, WriteKind, borrowck_errors};
49+
use crate::{InitializationRequiringAction, MirBorrowckCtxt, ReadOrWrite, WriteKind, borrowck_errors};
4950

5051
#[derive(Debug)]
5152
struct MoveSite {
@@ -1683,6 +1684,45 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
16831684
err
16841685
}
16851686

1687+
pub(crate) fn report_pin_violation(
1688+
&mut self,
1689+
_location: Location,
1690+
place_span: (Place<'tcx>, Span),
1691+
pin_data: &PinData<'tcx>,
1692+
access_kind: ReadOrWrite,
1693+
) {
1694+
use ReadOrWrite::Write;
1695+
use WriteKind::{Move, MutableBorrow};
1696+
1697+
let (place, span) = place_span;
1698+
1699+
let violation_kind = match access_kind {
1700+
Write(Move) => "move",
1701+
Write(MutableBorrow(_)) => "mutably borrow",
1702+
_ => unreachable!(),
1703+
};
1704+
1705+
let place_desc = self
1706+
.describe_place(place.as_ref())
1707+
.unwrap_or_else(|| "value".to_string());
1708+
1709+
let mut err = self.root_cx.tcx.dcx().struct_span_err(
1710+
span,
1711+
format!("cannot {} `{}` because it is pinned", violation_kind, place_desc),
1712+
);
1713+
1714+
// Show where the pin was created
1715+
let pin_span = self.body.source_info(pin_data.location).span;
1716+
err.span_note(pin_span, "pin created here");
1717+
1718+
err.help(
1719+
"pinned values cannot be moved or mutably borrowed, \
1720+
as this would invalidate the pin guarantee",
1721+
);
1722+
1723+
self.buffer_error(err);
1724+
}
1725+
16861726
pub(crate) fn report_conflicting_borrow(
16871727
&self,
16881728
location: Location,

compiler/rustc_borrowck/src/lib.rs

Lines changed: 80 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ use rustc_mir_dataflow::points::DenseLocationMap;
4848
use rustc_mir_dataflow::{Analysis, EntryStates, Results, ResultsVisitor, visit_results};
4949
use rustc_session::lint::builtin::{TAIL_EXPR_DROP_ORDER, UNUSED_MUT};
5050
use rustc_span::{ErrorGuaranteed, Span, Symbol};
51+
use rustc_trait_selection::infer::InferCtxtExt;
5152
use smallvec::SmallVec;
5253
use tracing::{debug, instrument};
5354

@@ -486,6 +487,7 @@ fn borrowck_check_region_constraints<'tcx>(
486487
used_mut: Default::default(),
487488
used_mut_upvars: SmallVec::new(),
488489
borrow_set: &borrow_set,
490+
pin_set: &pin_set,
489491
upvars: &[],
490492
local_names: OnceCell::from(IndexVec::from_elem(None, &promoted_body.local_decls)),
491493
region_names: RefCell::default(),
@@ -525,6 +527,7 @@ fn borrowck_check_region_constraints<'tcx>(
525527
used_mut: Default::default(),
526528
used_mut_upvars: SmallVec::new(),
527529
borrow_set: &borrow_set,
530+
pin_set: &pin_set,
528531
upvars: tcx.closure_captures(def),
529532
local_names: OnceCell::new(),
530533
region_names: RefCell::default(),
@@ -771,6 +774,9 @@ pub(crate) struct MirBorrowckCtxt<'a, 'infcx, 'tcx> {
771774
/// The set of borrows extracted from the MIR
772775
borrow_set: &'a BorrowSet<'tcx>,
773776

777+
/// The set of pins extracted from the MIR
778+
pin_set: &'a PinSet<'tcx>,
779+
774780
/// Information about upvars not necessarily preserved in types or MIR
775781
upvars: &'tcx [&'tcx ty::CapturedPlace<'tcx>],
776782

@@ -1243,8 +1249,10 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
12431249
location,
12441250
);
12451251
let conflict_error = self.check_access_for_conflict(location, place_span, sd, rw, state);
1252+
let pin_conflict_error =
1253+
self.check_access_for_pin_conflict(location, place_span, sd, rw, state);
12461254

1247-
if conflict_error || mutability_error {
1255+
if conflict_error || mutability_error || pin_conflict_error {
12481256
debug!("access_place: logging error place_span=`{:?}` kind=`{:?}`", place_span, kind);
12491257
self.access_place_error_reported.insert((place_span.0, place_span.1));
12501258
}
@@ -1268,6 +1276,77 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
12681276
}
12691277
}
12701278

1279+
fn pins_in_scope<'s>(
1280+
&self,
1281+
_location: Location,
1282+
state: &'s BorrowckDomain,
1283+
) -> &'s MixedBitSet<PinIndex> {
1284+
&state.pins
1285+
}
1286+
1287+
#[instrument(level = "debug", skip(self, state))]
1288+
fn check_access_for_pin_conflict(
1289+
&mut self,
1290+
location: Location,
1291+
place_span: (Place<'tcx>, Span),
1292+
sd: AccessDepth,
1293+
rw: ReadOrWrite,
1294+
state: &BorrowckDomain,
1295+
) -> bool {
1296+
use ReadOrWrite::Write;
1297+
use WriteKind::{Move, MutableBorrow};
1298+
1299+
// Early return: only check Move and MutableBorrow operations
1300+
let is_violating_access = matches!(rw, Write(Move) | Write(MutableBorrow(_)));
1301+
if !is_violating_access {
1302+
return false;
1303+
}
1304+
1305+
let mut error_reported = false;
1306+
let pins_in_scope = self.pins_in_scope(location, state);
1307+
1308+
// Check each active pin for conflicts
1309+
for pin_index in pins_in_scope.iter() {
1310+
let pin_data = &self.pin_set[pin_index];
1311+
1312+
// Check if accessed place conflicts with pinned place
1313+
if places_conflict(
1314+
self.infcx.tcx,
1315+
self.body,
1316+
pin_data.pinned_place,
1317+
place_span.0,
1318+
PlaceConflictBias::Overlap,
1319+
) {
1320+
// Get the type of the pinned place (Pin<Ptr>)
1321+
let pinned_ty = pin_data.pinned_place.ty(self.body, self.infcx.tcx).ty;
1322+
1323+
// Extract the pointee type from Pin<&[mut] T> to get T
1324+
if let Some(pointee_ty) = pinned_ty.builtin_deref(true) {
1325+
// Check if the pointee type implements Unpin
1326+
let unpin_trait = self.infcx.tcx.lang_items().unpin_trait();
1327+
if let Some(unpin_trait) = unpin_trait {
1328+
let param_env = self.infcx.tcx.param_env(
1329+
self.body.source.def_id()
1330+
);
1331+
let ty_is_unpin = self.infcx.type_implements_trait(
1332+
unpin_trait,
1333+
[pointee_ty],
1334+
param_env,
1335+
).must_apply_modulo_regions();
1336+
1337+
// Only report violation if the type does NOT implement Unpin
1338+
if !ty_is_unpin {
1339+
error_reported = true;
1340+
self.report_pin_violation(location, place_span, pin_data, rw);
1341+
}
1342+
}
1343+
}
1344+
}
1345+
}
1346+
1347+
error_reported
1348+
}
1349+
12711350
#[instrument(level = "debug", skip(self, state))]
12721351
fn check_access_for_conflict(
12731352
&mut self,
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
//@ check-pass
2+
// Test for pin violation detection - currently a placeholder
3+
// The pin violation detection is implemented but may not trigger
4+
// for all pin patterns yet. This test documents expected behavior.
5+
6+
#![feature(pin_ergonomics)]
7+
#![allow(incomplete_features, dead_code)]
8+
9+
use std::marker::PhantomPinned;
10+
11+
#[derive(Default)]
12+
struct Foo {
13+
_pinned: PhantomPinned,
14+
}
15+
16+
// Test basic pin borrow patterns
17+
fn test_pin_borrows() {
18+
let mut foo = Foo::default();
19+
let _pinned = &pin mut foo;
20+
// Currently, &pin mut creates a special borrow, not a Pin aggregate
21+
// So our violation detection may not trigger here
22+
}
23+
24+
fn main() {}

0 commit comments

Comments
 (0)