Skip to content

Commit 96e4153

Browse files
Rollup merge of #151753 - aapoalas:aapoalas/reborrow-and-coerce-shared-first-version, r=oli-obk
Experiment: Reborrow traits With this PR we now have basic functional Reborrow and CoerceShared traits. The current limitations are: 1. Reborrowable types can only have one lifetime parameter, so as to avoid having to figure out and store in rmeta the information of which lifetimes weaken during reborrowing. 2. Reborrowing of `&mut` wrappers is working (though I've not tested generic wrappers like `Option<&mut T>` yet), but CoerceShared of `&mut` wrappers currently causes an ICE. The remaining tasks to complete before I'd consider this PR mergeable are: - [x] Fix ICE on CoerceShared. Unfortunately this might require dipping into rmeta. - [x] Expand the tests to give a more complete view of the current state of the experiment. Reborrow traits experiment: #145612 Co-authored by @dingxiangfei2009
2 parents 71597bc + 2d88ee8 commit 96e4153

77 files changed

Lines changed: 1247 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
@@ -1265,6 +1265,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
12651265
let mut error_reported = false;
12661266

12671267
let borrows_in_scope = self.borrows_in_scope(location, state);
1268+
debug!(?borrows_in_scope, ?location);
12681269

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

1511+
&Rvalue::Reborrow(_target, mutability, place) => {
1512+
let access_kind = (
1513+
Deep,
1514+
if mutability == Mutability::Mut {
1515+
Write(WriteKind::MutableBorrow(BorrowKind::Mut {
1516+
kind: MutBorrowKind::Default,
1517+
}))
1518+
} else {
1519+
Read(ReadKind::Borrow(BorrowKind::Shared))
1520+
},
1521+
);
1522+
1523+
self.access_place(
1524+
location,
1525+
(place, span),
1526+
access_kind,
1527+
LocalMutationIsAllowed::Yes,
1528+
state,
1529+
);
1530+
1531+
let action = InitializationRequiringAction::Borrow;
1532+
1533+
self.check_if_path_or_subpath_is_moved(
1534+
location,
1535+
action,
1536+
(place.as_ref(), span),
1537+
state,
1538+
);
1539+
}
1540+
15101541
&Rvalue::RawPtr(kind, place) => {
15111542
let access_kind = match kind {
15121543
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
@@ -274,6 +274,21 @@ impl<'a, 'tcx> LoanInvalidationsGenerator<'a, 'tcx> {
274274
self.access_place(location, place, access_kind, LocalMutationIsAllowed::No);
275275
}
276276

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

compiler/rustc_borrowck/src/type_check/mod.rs

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

1583+
Rvalue::Reborrow(target, mutability, borrowed_place) => {
1584+
self.add_generic_reborrow_constraint(
1585+
*mutability,
1586+
location,
1587+
borrowed_place,
1588+
*target,
1589+
);
1590+
}
1591+
15831592
Rvalue::BinaryOp(
15841593
BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge,
15851594
box (left, right),
@@ -2218,6 +2227,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
22182227
| Rvalue::ThreadLocalRef(..)
22192228
| Rvalue::Repeat(..)
22202229
| Rvalue::Ref(..)
2230+
| Rvalue::Reborrow(..)
22212231
| Rvalue::RawPtr(..)
22222232
| Rvalue::Cast(..)
22232233
| Rvalue::BinaryOp(..)
@@ -2422,6 +2432,116 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
24222432
}
24232433
}
24242434

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

521+
// Note: Exclusive reborrowing is always equal to a memcpy, as the types do not change.
522+
// Generic shared reborrowing is not (necessarily) a simple memcpy, but currently the
523+
// coherence check places such restrictions on the CoerceShared trait as to guarantee
524+
// that it is.
525+
mir::Rvalue::Reborrow(_, _, place) => {
526+
self.codegen_operand(bx, &mir::Operand::Copy(place))
527+
}
528+
521529
mir::Rvalue::RawPtr(kind, place) => {
522530
let mk_ptr = move |tcx: TyCtxt<'tcx>, ty: Ty<'tcx>| {
523531
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
@@ -610,6 +610,10 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
610610
}
611611
}
612612

613+
Rvalue::Reborrow(..) => {
614+
// FIXME(reborrow): figure out if this is relevant at all.
615+
}
616+
613617
Rvalue::RawPtr(RawPtrKind::FakeForPtrMetadata, place) => {
614618
// These are only inserted for slice length, so the place must already be indirect.
615619
// 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
@@ -252,6 +252,8 @@ where
252252
in_place::<Q, _>(cx, in_local, place.as_ref())
253253
}
254254

255+
Rvalue::Reborrow(_, _, place) => in_place::<Q, _>(cx, in_local, place.as_ref()),
256+
255257
Rvalue::WrapUnsafeBinder(op, _) => in_operand::<Q, _>(cx, in_local, op),
256258

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

compiler/rustc_const_eval/src/check_consts/resolver.rs

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

194+
mir::Rvalue::Reborrow(target, mutability, borrowed_place) => {
195+
// A Reborrow allows mutation if it is Reborrow or if the CoerceShared target isn't
196+
// Freeze.
197+
if !borrowed_place.is_indirect()
198+
&& (mutability.is_mut() || !target.is_freeze(self.ccx.tcx, self.ccx.typing_env))
199+
{
200+
if Q::in_any_value_of_ty(self.ccx, *target) {
201+
self.state.qualif.insert(borrowed_place.local);
202+
self.state.borrow.insert(borrowed_place.local);
203+
}
204+
}
205+
}
206+
194207
mir::Rvalue::Cast(..)
195208
| mir::Rvalue::Use(..)
196209
| mir::Rvalue::CopyForDeref(..)

0 commit comments

Comments
 (0)