Skip to content

Commit c10c973

Browse files
committed
Reborrow traits
1 parent 25396cf commit c10c973

File tree

82 files changed

+1288
-108
lines changed

Some content is hidden

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

82 files changed

+1288
-108
lines changed

compiler/rustc_borrowck/src/borrow_set.rs

Lines changed: 50 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,54 @@ 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(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) =
311+
assigned_place.ty(self.body, self.tcx).ty.kind()
312+
else {
313+
unreachable!()
314+
};
315+
let Some(ty::GenericArgKind::Lifetime(region)) = assigned_args.get(0).map(|r| r.kind())
316+
else {
317+
bug!(
318+
"hir-typeck passed but {} does not have a lifetime argument",
319+
if mutability == Mutability::Mut { "Reborrow" } else { "CoerceShared" }
320+
);
321+
};
322+
let region = region.as_var();
323+
let kind = if mutability == Mutability::Mut {
324+
// Reborrow
325+
if target_adt.did() != reborrowed_adt.did() {
326+
bug!(
327+
"hir-typeck passed but Reborrow involves mismatching types at {location:?}"
328+
)
329+
}
330+
331+
mir::BorrowKind::Mut { kind: mir::MutBorrowKind::Default }
332+
} else {
333+
// CoerceShared
334+
if target_adt.did() == reborrowed_adt.did() {
335+
bug!(
336+
"hir-typeck passed but CoerceShared involves matching types at {location:?}"
337+
)
338+
}
339+
mir::BorrowKind::Shared
340+
};
341+
let borrow = BorrowData {
342+
kind,
343+
region,
344+
reserve_location: location,
345+
activation_location: TwoPhaseActivation::NotTwoPhase,
346+
borrowed_place,
347+
assigned_place: *assigned_place,
348+
};
349+
let (idx, _) = self.location_map.insert_full(location, borrow);
350+
let idx = BorrowIndex::from(idx);
351+
303352
self.local_map.entry(borrowed_place.local).or_default().insert(idx);
304353
}
305354

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
@@ -1268,6 +1268,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
12681268
let mut error_reported = false;
12691269

12701270
let borrows_in_scope = self.borrows_in_scope(location, state);
1271+
debug!(?borrows_in_scope, ?location);
12711272

12721273
each_borrow_involving_path(
12731274
self,
@@ -1510,6 +1511,36 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
15101511
);
15111512
}
15121513

1514+
&Rvalue::Reborrow(mutability, place) => {
1515+
let access_kind = (
1516+
Deep,
1517+
if mutability == Mutability::Mut {
1518+
Write(WriteKind::MutableBorrow(BorrowKind::Mut {
1519+
kind: MutBorrowKind::Default,
1520+
}))
1521+
} else {
1522+
Read(ReadKind::Borrow(BorrowKind::Shared))
1523+
},
1524+
);
1525+
1526+
self.access_place(
1527+
location,
1528+
(place, span),
1529+
access_kind,
1530+
LocalMutationIsAllowed::Yes,
1531+
state,
1532+
);
1533+
1534+
let action = InitializationRequiringAction::Borrow;
1535+
1536+
self.check_if_path_or_subpath_is_moved(
1537+
location,
1538+
action,
1539+
(place.as_ref(), span),
1540+
state,
1541+
);
1542+
}
1543+
15131544
&Rvalue::RawPtr(kind, place) => {
15141545
let access_kind = match kind {
15151546
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(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: 137 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -551,6 +551,16 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
551551
}
552552

553553
impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
554+
fn visit_assign(&mut self, place: &Place<'tcx>, rvalue: &Rvalue<'tcx>, location: Location) {
555+
// check rvalue is Reborrow
556+
if let Rvalue::Reborrow(mutability, rvalue) = rvalue {
557+
self.add_generic_reborrow_constraint(*mutability, location, place, rvalue);
558+
} else {
559+
// rest of the cases
560+
self.super_assign(place, rvalue, location);
561+
}
562+
}
563+
554564
fn visit_span(&mut self, span: Span) {
555565
if !span.is_dummy() {
556566
debug!(?span);
@@ -628,8 +638,11 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
628638
debug!(?rv_ty);
629639
let rv_ty = self.normalize(rv_ty, location);
630640
debug!("normalized rv_ty: {:?}", rv_ty);
631-
if let Err(terr) =
632-
self.sub_types(rv_ty, place_ty, location.to_locations(), category)
641+
// Note: we've checked Reborrow/CoerceShared type matches
642+
// separately in fn visit_assign.
643+
if !matches!(rv, Rvalue::Reborrow(_, _))
644+
&& let Err(terr) =
645+
self.sub_types(rv_ty, place_ty, location.to_locations(), category)
633646
{
634647
span_mirbug!(
635648
self,
@@ -1582,6 +1595,12 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
15821595
self.add_reborrow_constraint(location, *region, borrowed_place);
15831596
}
15841597

1598+
Rvalue::Reborrow(..) => {
1599+
// Reborrow needs to produce a relation between the source and destination fields,
1600+
// which means that we have had to already handle this in visit_assign.
1601+
unreachable!()
1602+
}
1603+
15851604
Rvalue::BinaryOp(
15861605
BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge,
15871606
box (left, right),
@@ -2218,6 +2237,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
22182237
| Rvalue::ThreadLocalRef(_)
22192238
| Rvalue::Repeat(..)
22202239
| Rvalue::Ref(..)
2240+
| Rvalue::Reborrow(..)
22212241
| Rvalue::RawPtr(..)
22222242
| Rvalue::Cast(..)
22232243
| Rvalue::BinaryOp(..)
@@ -2422,6 +2442,121 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
24222442
}
24232443
}
24242444

2445+
fn add_generic_reborrow_constraint(
2446+
&mut self,
2447+
mutability: Mutability,
2448+
location: Location,
2449+
dest: &Place<'tcx>,
2450+
borrowed_place: &Place<'tcx>,
2451+
) {
2452+
// These constraints are only meaningful during borrowck:
2453+
let Self { borrow_set, location_table, polonius_facts, constraints, infcx, body, .. } =
2454+
self;
2455+
2456+
// If we are reborrowing the referent of another reference, we
2457+
// need to add outlives relationships. In a case like `&mut
2458+
// *p`, where the `p` has type `&'b mut Foo`, for example, we
2459+
// need to ensure that `'b: 'a`.
2460+
2461+
debug!(
2462+
"add_generic_reborrow_constraint({:?}, {:?}, {:?}, {:?})",
2463+
mutability, location, dest, borrowed_place
2464+
);
2465+
2466+
let tcx = infcx.tcx;
2467+
let def = body.source.def_id().expect_local();
2468+
let upvars = tcx.closure_captures(def);
2469+
let field =
2470+
path_utils::is_upvar_field_projection(tcx, upvars, borrowed_place.as_ref(), body);
2471+
let category = if let Some(field) = field {
2472+
ConstraintCategory::ClosureUpvar(field)
2473+
} else {
2474+
ConstraintCategory::Boring
2475+
};
2476+
2477+
let dest_ty = dest.ty(self.body, tcx).ty;
2478+
let borrowed_ty = borrowed_place.ty(self.body, tcx).ty;
2479+
let ty::Adt(_, args) = dest_ty.kind() else { bug!() };
2480+
let [arg, ..] = ***args else { bug!() };
2481+
let ty::GenericArgKind::Lifetime(reborrow_region) = arg.kind() else { bug!() };
2482+
constraints.liveness_constraints.add_location(reborrow_region.as_var(), location);
2483+
2484+
// In Polonius mode, we also push a `loan_issued_at` fact
2485+
// linking the loan to the region.
2486+
if let Some(polonius_facts) = polonius_facts {
2487+
let _prof_timer = infcx.tcx.prof.generic_activity("polonius_fact_generation");
2488+
if let Some(borrow_index) = borrow_set.get_index_of(&location) {
2489+
let region_vid = reborrow_region.as_var();
2490+
polonius_facts.loan_issued_at.push((
2491+
region_vid.into(),
2492+
borrow_index,
2493+
location_table.mid_index(location),
2494+
));
2495+
}
2496+
}
2497+
2498+
if mutability.is_not() {
2499+
// FIXME: for shared reborrow we need to relate the types manually,
2500+
// field by field with CoerceShared drilling down and down and down.
2501+
// We cannot just attempt to relate T and <T as CoerceShared>::Target
2502+
// by calling relate_types.
2503+
let ty::Adt(dest_adt, dest_args) = dest_ty.kind() else { unreachable!() };
2504+
let ty::Adt(borrowed_adt, borrowed_args) = borrowed_ty.kind() else { unreachable!() };
2505+
let borrowed_fields = borrowed_adt.all_fields().collect::<Vec<_>>();
2506+
for dest_field in dest_adt.all_fields() {
2507+
let Some(borrowed_field) =
2508+
borrowed_fields.iter().find(|f| f.name == dest_field.name)
2509+
else {
2510+
continue;
2511+
};
2512+
let dest_ty = dest_field.ty(tcx, dest_args);
2513+
let borrowed_ty = borrowed_field.ty(tcx, borrowed_args);
2514+
if let (
2515+
ty::Ref(borrow_region, _, Mutability::Mut),
2516+
ty::Ref(ref_region, _, Mutability::Not),
2517+
) = (borrowed_ty.kind(), dest_ty.kind())
2518+
{
2519+
self.relate_types(
2520+
borrowed_ty.peel_refs(),
2521+
ty::Variance::Covariant,
2522+
dest_ty.peel_refs(),
2523+
location.to_locations(),
2524+
category,
2525+
)
2526+
.unwrap();
2527+
self.constraints.outlives_constraints.push(OutlivesConstraint {
2528+
sup: ref_region.as_var(),
2529+
sub: borrow_region.as_var(),
2530+
locations: location.to_locations(),
2531+
span: location.to_locations().span(self.body),
2532+
category,
2533+
variance_info: ty::VarianceDiagInfo::default(),
2534+
from_closure: false,
2535+
});
2536+
} else {
2537+
self.relate_types(
2538+
borrowed_ty,
2539+
ty::Variance::Covariant,
2540+
dest_ty,
2541+
location.to_locations(),
2542+
category,
2543+
)
2544+
.unwrap();
2545+
}
2546+
}
2547+
} else {
2548+
// Exclusive reborrow
2549+
self.relate_types(
2550+
borrowed_ty,
2551+
ty::Variance::Covariant,
2552+
dest_ty,
2553+
location.to_locations(),
2554+
category,
2555+
)
2556+
.unwrap();
2557+
}
2558+
}
2559+
24252560
fn prove_aggregate_predicates(
24262561
&mut self,
24272562
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: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -510,6 +510,12 @@ 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) => self.codegen_operand(bx, &mir::Operand::Copy(place)),
518+
513519
mir::Rvalue::RawPtr(kind, place) => {
514520
let mk_ptr = move |tcx: TyCtxt<'tcx>, ty: Ty<'tcx>| {
515521
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(@aapoalas): 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
@@ -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(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)