Skip to content

Commit 2038ae9

Browse files
committed
Reborrow traits
1 parent 1f7f8ea commit 2038ae9

79 files changed

Lines changed: 1263 additions & 104 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

compiler/rustc_borrowck/src/borrow_set.rs

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@ use std::fmt;
22
use std::ops::Index;
33

44
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
5+
use rustc_hir::Mutability;
56
use rustc_index::bit_set::DenseBitSet;
67
use rustc_middle::mir::visit::{MutatingUseContext, NonUseContext, PlaceContext, Visitor};
78
use rustc_middle::mir::{self, Body, Local, Location, traversal};
8-
use rustc_middle::span_bug;
99
use rustc_middle::ty::{RegionVid, TyCtxt};
10+
use rustc_middle::{bug, span_bug, ty};
1011
use rustc_mir_dataflow::move_paths::MoveData;
1112
use tracing::debug;
1213

@@ -300,6 +301,50 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherBorrows<'a, 'tcx> {
300301
idx
301302
};
302303

304+
self.local_map.entry(borrowed_place.local).or_default().insert(idx);
305+
} else if let &mir::Rvalue::Reborrow(target, mutability, borrowed_place) = rvalue {
306+
let borrowed_place_ty = borrowed_place.ty(self.body, self.tcx).ty;
307+
let &ty::Adt(reborrowed_adt, _reborrowed_args) = borrowed_place_ty.kind() else {
308+
unreachable!()
309+
};
310+
let &ty::Adt(target_adt, assigned_args) = target.kind() else { unreachable!() };
311+
let Some(ty::GenericArgKind::Lifetime(region)) = assigned_args.get(0).map(|r| r.kind())
312+
else {
313+
bug!(
314+
"hir-typeck passed but {} does not have a lifetime argument",
315+
if mutability == Mutability::Mut { "Reborrow" } else { "CoerceShared" }
316+
);
317+
};
318+
let region = region.as_var();
319+
let kind = if mutability == Mutability::Mut {
320+
// Reborrow
321+
if target_adt.did() != reborrowed_adt.did() {
322+
bug!(
323+
"hir-typeck passed but Reborrow involves mismatching types at {location:?}"
324+
)
325+
}
326+
327+
mir::BorrowKind::Mut { kind: mir::MutBorrowKind::Default }
328+
} else {
329+
// CoerceShared
330+
if target_adt.did() == reborrowed_adt.did() {
331+
bug!(
332+
"hir-typeck passed but CoerceShared involves matching types at {location:?}"
333+
)
334+
}
335+
mir::BorrowKind::Shared
336+
};
337+
let borrow = BorrowData {
338+
kind,
339+
region,
340+
reserve_location: location,
341+
activation_location: TwoPhaseActivation::NotTwoPhase,
342+
borrowed_place,
343+
assigned_place: *assigned_place,
344+
};
345+
let (idx, _) = self.location_map.insert_full(location, borrow);
346+
let idx = BorrowIndex::from(idx);
347+
303348
self.local_map.entry(borrowed_place.local).or_default().insert(idx);
304349
}
305350

compiler/rustc_borrowck/src/dataflow.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -549,7 +549,7 @@ impl<'tcx> rustc_mir_dataflow::Analysis<'tcx> for Borrows<'_, 'tcx> {
549549
) {
550550
match &stmt.kind {
551551
mir::StatementKind::Assign(box (lhs, rhs)) => {
552-
if let mir::Rvalue::Ref(_, _, place) = rhs {
552+
if let mir::Rvalue::Ref(_, _, place) | mir::Rvalue::Reborrow(_, _, place) = rhs {
553553
if place.ignore_borrow(
554554
self.tcx,
555555
self.body,

compiler/rustc_borrowck/src/lib.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1266,6 +1266,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
12661266
let mut error_reported = false;
12671267

12681268
let borrows_in_scope = self.borrows_in_scope(location, state);
1269+
debug!(?borrows_in_scope, ?location);
12691270

12701271
each_borrow_involving_path(
12711272
self,
@@ -1508,6 +1509,36 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
15081509
);
15091510
}
15101511

1512+
&Rvalue::Reborrow(_target, mutability, place) => {
1513+
let access_kind = (
1514+
Deep,
1515+
if mutability == Mutability::Mut {
1516+
Write(WriteKind::MutableBorrow(BorrowKind::Mut {
1517+
kind: MutBorrowKind::Default,
1518+
}))
1519+
} else {
1520+
Read(ReadKind::Borrow(BorrowKind::Shared))
1521+
},
1522+
);
1523+
1524+
self.access_place(
1525+
location,
1526+
(place, span),
1527+
access_kind,
1528+
LocalMutationIsAllowed::Yes,
1529+
state,
1530+
);
1531+
1532+
let action = InitializationRequiringAction::Borrow;
1533+
1534+
self.check_if_path_or_subpath_is_moved(
1535+
location,
1536+
action,
1537+
(place.as_ref(), span),
1538+
state,
1539+
);
1540+
}
1541+
15111542
&Rvalue::RawPtr(kind, place) => {
15121543
let access_kind = match kind {
15131544
RawPtrKind::Mut => (

compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,21 @@ impl<'a, 'tcx> LoanInvalidationsGenerator<'a, 'tcx> {
275275
self.access_place(location, place, access_kind, LocalMutationIsAllowed::No);
276276
}
277277

278+
&Rvalue::Reborrow(_target, mutability, place) => {
279+
let access_kind = (
280+
Deep,
281+
if mutability == Mutability::Mut {
282+
Reservation(WriteKind::MutableBorrow(BorrowKind::Mut {
283+
kind: MutBorrowKind::TwoPhaseBorrow,
284+
}))
285+
} else {
286+
Read(ReadKind::Borrow(BorrowKind::Shared))
287+
},
288+
);
289+
290+
self.access_place(location, place, access_kind, LocalMutationIsAllowed::No);
291+
}
292+
278293
&Rvalue::RawPtr(kind, place) => {
279294
let access_kind = match kind {
280295
RawPtrKind::Mut => (

compiler/rustc_borrowck/src/type_check/mod.rs

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1581,6 +1581,15 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
15811581
self.add_reborrow_constraint(location, *region, borrowed_place);
15821582
}
15831583

1584+
Rvalue::Reborrow(target, mutability, borrowed_place) => {
1585+
self.add_generic_reborrow_constraint(
1586+
*mutability,
1587+
location,
1588+
borrowed_place,
1589+
*target,
1590+
);
1591+
}
1592+
15841593
Rvalue::BinaryOp(
15851594
BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge,
15861595
box (left, right),
@@ -2217,6 +2226,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
22172226
| Rvalue::ThreadLocalRef(_)
22182227
| Rvalue::Repeat(..)
22192228
| Rvalue::Ref(..)
2229+
| Rvalue::Reborrow(..)
22202230
| Rvalue::RawPtr(..)
22212231
| Rvalue::Cast(..)
22222232
| Rvalue::BinaryOp(..)
@@ -2421,6 +2431,122 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
24212431
}
24222432
}
24232433

2434+
fn add_generic_reborrow_constraint(
2435+
&mut self,
2436+
mutability: Mutability,
2437+
location: Location,
2438+
borrowed_place: &Place<'tcx>,
2439+
dest_ty: Ty<'tcx>,
2440+
) {
2441+
// These constraints are only meaningful during borrowck:
2442+
let Self { borrow_set, location_table, polonius_facts, constraints, infcx, body, .. } =
2443+
self;
2444+
2445+
// If we are reborrowing the referent of another reference, we
2446+
// need to add outlives relationships. In a case like `&mut
2447+
// *p`, where the `p` has type `&'b mut Foo`, for example, we
2448+
// need to ensure that `'b: 'a`.
2449+
2450+
debug!(
2451+
"add_generic_reborrow_constraint({:?}, {:?}, {:?}, {:?})",
2452+
mutability, location, borrowed_place, dest_ty
2453+
);
2454+
2455+
let tcx = infcx.tcx;
2456+
let def = body.source.def_id().expect_local();
2457+
let upvars = tcx.closure_captures(def);
2458+
let field =
2459+
path_utils::is_upvar_field_projection(tcx, upvars, borrowed_place.as_ref(), body);
2460+
let category = if let Some(field) = field {
2461+
ConstraintCategory::ClosureUpvar(field)
2462+
} else {
2463+
ConstraintCategory::Boring
2464+
};
2465+
2466+
let borrowed_ty = borrowed_place.ty(self.body, tcx).ty;
2467+
2468+
let ty::Adt(dest_adt, dest_args) = dest_ty.kind() else { bug!() };
2469+
let [dest_arg, ..] = ***dest_args else { bug!() };
2470+
let ty::GenericArgKind::Lifetime(dest_region) = dest_arg.kind() else { bug!() };
2471+
constraints.liveness_constraints.add_location(dest_region.as_var(), location);
2472+
2473+
// In Polonius mode, we also push a `loan_issued_at` fact
2474+
// linking the loan to the region.
2475+
if let Some(polonius_facts) = polonius_facts {
2476+
let _prof_timer = infcx.tcx.prof.generic_activity("polonius_fact_generation");
2477+
if let Some(borrow_index) = borrow_set.get_index_of(&location) {
2478+
let region_vid = dest_region.as_var();
2479+
polonius_facts.loan_issued_at.push((
2480+
region_vid.into(),
2481+
borrow_index,
2482+
location_table.mid_index(location),
2483+
));
2484+
}
2485+
}
2486+
2487+
if mutability.is_not() {
2488+
// FIXME(@aapoalas): for CoerceShared we need to relate the types manually, field by
2489+
// field. We cannot just attempt to relate `T` and `<T as CoerceShared>::Target` by
2490+
// calling relate_types as they are (generally) two unrelated user-defined ADTs, such as
2491+
// `CustomMut<'a>` and `CustomRef<'a>`, or `CustomMut<'a, T>` and `CustomRef<'a, T>`.
2492+
// Field-by-field relate_types is expected to work based on the wf-checks that the
2493+
// CoerceShared trait performs.
2494+
let ty::Adt(borrowed_adt, borrowed_args) = borrowed_ty.kind() else { unreachable!() };
2495+
let borrowed_fields = borrowed_adt.all_fields().collect::<Vec<_>>();
2496+
for dest_field in dest_adt.all_fields() {
2497+
let Some(borrowed_field) =
2498+
borrowed_fields.iter().find(|f| f.name == dest_field.name)
2499+
else {
2500+
continue;
2501+
};
2502+
let dest_ty = dest_field.ty(tcx, dest_args);
2503+
let borrowed_ty = borrowed_field.ty(tcx, borrowed_args);
2504+
if let (
2505+
ty::Ref(borrow_region, _, Mutability::Mut),
2506+
ty::Ref(ref_region, _, Mutability::Not),
2507+
) = (borrowed_ty.kind(), dest_ty.kind())
2508+
{
2509+
self.relate_types(
2510+
borrowed_ty.peel_refs(),
2511+
ty::Variance::Covariant,
2512+
dest_ty.peel_refs(),
2513+
location.to_locations(),
2514+
category,
2515+
)
2516+
.unwrap();
2517+
self.constraints.outlives_constraints.push(OutlivesConstraint {
2518+
sup: ref_region.as_var(),
2519+
sub: borrow_region.as_var(),
2520+
locations: location.to_locations(),
2521+
span: location.to_locations().span(self.body),
2522+
category,
2523+
variance_info: ty::VarianceDiagInfo::default(),
2524+
from_closure: false,
2525+
});
2526+
} else {
2527+
self.relate_types(
2528+
borrowed_ty,
2529+
ty::Variance::Covariant,
2530+
dest_ty,
2531+
location.to_locations(),
2532+
category,
2533+
)
2534+
.unwrap();
2535+
}
2536+
}
2537+
} else {
2538+
// Exclusive reborrow
2539+
self.relate_types(
2540+
borrowed_ty,
2541+
ty::Variance::Covariant,
2542+
dest_ty,
2543+
location.to_locations(),
2544+
category,
2545+
)
2546+
.unwrap();
2547+
}
2548+
}
2549+
24242550
fn prove_aggregate_predicates(
24252551
&mut self,
24262552
aggregate_kind: &AggregateKind<'tcx>,

compiler/rustc_codegen_cranelift/src/base.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -629,6 +629,11 @@ fn codegen_stmt<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, cur_block: Block, stmt:
629629
let ref_ = place.place_ref(fx, lval.layout());
630630
lval.write_cvalue(fx, ref_);
631631
}
632+
Rvalue::Reborrow(_, _, place) => {
633+
let cplace = codegen_place(fx, place);
634+
let val = cplace.to_cvalue(fx);
635+
lval.write_cvalue(fx, val)
636+
}
632637
Rvalue::ThreadLocalRef(def_id) => {
633638
let val = crate::constant::codegen_tls_ref(fx, def_id, lval.layout());
634639
lval.write_cvalue(fx, val);

compiler/rustc_codegen_ssa/src/mir/rvalue.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -510,6 +510,14 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
510510
self.codegen_place_to_pointer(bx, place, mk_ref)
511511
}
512512

513+
// Note: Exclusive reborrowing is always equal to a memcpy, as the types do not change.
514+
// Generic shared reborrowing is not (necessarily) a simple memcpy, but currently the
515+
// coherence check places such restrictions on the CoerceShared trait as to guarantee
516+
// that it is.
517+
mir::Rvalue::Reborrow(_, _, place) => {
518+
self.codegen_operand(bx, &mir::Operand::Copy(place))
519+
}
520+
513521
mir::Rvalue::RawPtr(kind, place) => {
514522
let mk_ptr = move |tcx: TyCtxt<'tcx>, ty: Ty<'tcx>| {
515523
Ty::new_ptr(tcx, ty, kind.to_mutbl_lossy())

compiler/rustc_const_eval/src/check_consts/check.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -609,6 +609,10 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
609609
}
610610
}
611611

612+
Rvalue::Reborrow(..) => {
613+
// FIXME(@aapoalas): figure out if this is relevant at all.
614+
}
615+
612616
Rvalue::RawPtr(RawPtrKind::FakeForPtrMetadata, place) => {
613617
// These are only inserted for slice length, so the place must already be indirect.
614618
// This implies we do not have to worry about whether the borrow escapes.

compiler/rustc_const_eval/src/check_consts/qualifs.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,8 @@ where
255255
in_place::<Q, _>(cx, in_local, place.as_ref())
256256
}
257257

258+
Rvalue::Reborrow(_, _, place) => in_place::<Q, _>(cx, in_local, place.as_ref()),
259+
258260
Rvalue::WrapUnsafeBinder(op, _) => in_operand::<Q, _>(cx, in_local, op),
259261

260262
Rvalue::Aggregate(kind, operands) => {

compiler/rustc_const_eval/src/check_consts/resolver.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,16 @@ where
191191
}
192192
}
193193

194+
mir::Rvalue::Reborrow(_target, mutability, borrowed_place) => {
195+
if !borrowed_place.is_indirect() && mutability.is_mut() {
196+
let place_ty = borrowed_place.ty(self.ccx.body, self.ccx.tcx).ty;
197+
if Q::in_any_value_of_ty(self.ccx, place_ty) {
198+
self.state.qualif.insert(borrowed_place.local);
199+
self.state.borrow.insert(borrowed_place.local);
200+
}
201+
}
202+
}
203+
194204
mir::Rvalue::Cast(..)
195205
| mir::Rvalue::Use(..)
196206
| mir::Rvalue::CopyForDeref(..)

0 commit comments

Comments
 (0)