Skip to content

Commit dc59612

Browse files
committed
Use deep fake borrows for deref patterns
1 parent 30c9e19 commit dc59612

File tree

4 files changed

+56
-28
lines changed

4 files changed

+56
-28
lines changed

compiler/rustc_mir_build/src/build/matches/mod.rs

+8-9
Original file line numberDiff line numberDiff line change
@@ -375,10 +375,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
375375
match_start_span: Span,
376376
match_has_guard: bool,
377377
candidates: &mut [&mut Candidate<'pat, 'tcx>],
378-
) -> Vec<(Place<'tcx>, Local)> {
378+
) -> Vec<(Place<'tcx>, Local, FakeBorrowKind)> {
379379
// The set of places that we are creating fake borrows of. If there are no match guards then
380380
// we don't need any fake borrows, so don't track them.
381-
let fake_borrows: Vec<(Place<'tcx>, Local)> = if match_has_guard {
381+
let fake_borrows: Vec<(Place<'tcx>, Local, FakeBorrowKind)> = if match_has_guard {
382382
util::collect_fake_borrows(
383383
self,
384384
candidates,
@@ -457,7 +457,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
457457
scrutinee_span: Span,
458458
arm_candidates: Vec<(&'_ Arm<'tcx>, Candidate<'_, 'tcx>)>,
459459
outer_source_info: SourceInfo,
460-
fake_borrow_temps: Vec<(Place<'tcx>, Local)>,
460+
fake_borrow_temps: Vec<(Place<'tcx>, Local, FakeBorrowKind)>,
461461
) -> BlockAnd<()> {
462462
let arm_end_blocks: Vec<_> = arm_candidates
463463
.into_iter()
@@ -541,7 +541,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
541541
&mut self,
542542
outer_source_info: SourceInfo,
543543
candidate: Candidate<'_, 'tcx>,
544-
fake_borrow_temps: &[(Place<'tcx>, Local)],
544+
fake_borrow_temps: &[(Place<'tcx>, Local, FakeBorrowKind)],
545545
scrutinee_span: Span,
546546
arm_match_scope: Option<(&Arm<'tcx>, region::Scope)>,
547547
storages_alive: bool,
@@ -1980,7 +1980,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
19801980
&mut self,
19811981
candidate: Candidate<'pat, 'tcx>,
19821982
parent_data: &[PatternExtraData<'tcx>],
1983-
fake_borrows: &[(Place<'tcx>, Local)],
1983+
fake_borrows: &[(Place<'tcx>, Local, FakeBorrowKind)],
19841984
scrutinee_span: Span,
19851985
arm_match_scope: Option<(&Arm<'tcx>, region::Scope)>,
19861986
schedule_drops: bool,
@@ -2110,9 +2110,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
21102110

21112111
let re_erased = tcx.lifetimes.re_erased;
21122112
let scrutinee_source_info = self.source_info(scrutinee_span);
2113-
for &(place, temp) in fake_borrows {
2114-
let borrow =
2115-
Rvalue::Ref(re_erased, BorrowKind::Fake(FakeBorrowKind::Shallow), place);
2113+
for &(place, temp, kind) in fake_borrows {
2114+
let borrow = Rvalue::Ref(re_erased, BorrowKind::Fake(kind), place);
21162115
self.cfg.push_assign(block, scrutinee_source_info, Place::from(temp), borrow);
21172116
}
21182117

@@ -2135,7 +2134,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
21352134
let guard_frame = self.guard_context.pop().unwrap();
21362135
debug!("Exiting guard building context with locals: {:?}", guard_frame);
21372136

2138-
for &(_, temp) in fake_borrows {
2137+
for &(_, temp, _) in fake_borrows {
21392138
let cause = FakeReadCause::ForMatchGuard;
21402139
self.cfg.push_fake_read(post_guard_block, guard_end, cause, Place::from(temp));
21412140
}

compiler/rustc_mir_build/src/build/matches/util.rs

+22-19
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::build::expr::as_place::{PlaceBase, PlaceBuilder};
22
use crate::build::matches::{Binding, Candidate, FlatPat, MatchPair, TestCase};
33
use crate::build::Builder;
4-
use rustc_data_structures::fx::FxIndexSet;
4+
use rustc_data_structures::fx::FxIndexMap;
55
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
66
use rustc_middle::mir::*;
77
use rustc_middle::thir::{self, *};
@@ -272,7 +272,11 @@ pub(super) struct FakeBorrowCollector<'a, 'b, 'tcx> {
272272
/// Base of the scrutinee place. Used to distinguish bindings inside the scrutinee place from
273273
/// bindings inside deref patterns.
274274
scrutinee_base: PlaceBase,
275-
fake_borrows: FxIndexSet<Place<'tcx>>,
275+
/// Store for each place the kind of borrow to take. In case of conflicts, we take the strongest
276+
/// borrow (i.e. Deep > Shallow).
277+
/// Invariant: for any place in `fake_borrows`, all the prefixes of this place that are
278+
/// dereferences are also borrowed with the same of stronger borrow kind.
279+
fake_borrows: FxIndexMap<Place<'tcx>, FakeBorrowKind>,
276280
}
277281

278282
/// Determine the set of places that have to be stable across match guards.
@@ -315,9 +319,9 @@ pub(super) fn collect_fake_borrows<'tcx>(
315319
candidates: &[&mut Candidate<'_, 'tcx>],
316320
temp_span: Span,
317321
scrutinee_base: PlaceBase,
318-
) -> Vec<(Place<'tcx>, Local)> {
322+
) -> Vec<(Place<'tcx>, Local, FakeBorrowKind)> {
319323
let mut collector =
320-
FakeBorrowCollector { cx, scrutinee_base, fake_borrows: FxIndexSet::default() };
324+
FakeBorrowCollector { cx, scrutinee_base, fake_borrows: FxIndexMap::default() };
321325
for candidate in candidates.iter() {
322326
collector.visit_candidate(candidate);
323327
}
@@ -326,40 +330,40 @@ pub(super) fn collect_fake_borrows<'tcx>(
326330
let tcx = cx.tcx;
327331
fake_borrows
328332
.iter()
329-
.copied()
330-
.map(|matched_place| {
333+
.map(|(matched_place, borrow_kind)| {
331334
let fake_borrow_deref_ty = matched_place.ty(&cx.local_decls, tcx).ty;
332335
let fake_borrow_ty =
333336
Ty::new_imm_ref(tcx, tcx.lifetimes.re_erased, fake_borrow_deref_ty);
334337
let mut fake_borrow_temp = LocalDecl::new(fake_borrow_ty, temp_span);
335338
fake_borrow_temp.local_info = ClearCrossCrate::Set(Box::new(LocalInfo::FakeBorrow));
336339
let fake_borrow_temp = cx.local_decls.push(fake_borrow_temp);
337-
(matched_place, fake_borrow_temp)
340+
(*matched_place, fake_borrow_temp, *borrow_kind)
338341
})
339342
.collect()
340343
}
341344

342345
impl<'a, 'b, 'tcx> FakeBorrowCollector<'a, 'b, 'tcx> {
343346
// Fake borrow this place and its dereference prefixes.
344-
fn fake_borrow(&mut self, place: Place<'tcx>) {
345-
let new = self.fake_borrows.insert(place);
346-
if !new {
347+
fn fake_borrow(&mut self, place: Place<'tcx>, kind: FakeBorrowKind) {
348+
if self.fake_borrows.get(&place).is_some_and(|k| *k >= kind) {
347349
return;
348350
}
351+
self.fake_borrows.insert(place, kind);
349352
// Also fake borrow the prefixes of any fake borrow.
350-
self.fake_borrow_deref_prefixes(place);
353+
self.fake_borrow_deref_prefixes(place, kind);
351354
}
352355

353356
// Fake borrow the prefixes of this place that are dereferences.
354-
fn fake_borrow_deref_prefixes(&mut self, place: Place<'tcx>) {
357+
fn fake_borrow_deref_prefixes(&mut self, place: Place<'tcx>, kind: FakeBorrowKind) {
355358
for (place_ref, elem) in place.as_ref().iter_projections().rev() {
356359
if let ProjectionElem::Deref = elem {
357360
// Insert a shallow borrow after a deref. For other projections the borrow of
358361
// `place_ref` will conflict with any mutation of `place.base`.
359-
let new = self.fake_borrows.insert(place_ref.to_place(self.cx.tcx));
360-
if !new {
362+
let place = place_ref.to_place(self.cx.tcx);
363+
if self.fake_borrows.get(&place).is_some_and(|k| *k >= kind) {
361364
return;
362365
}
366+
self.fake_borrows.insert(place, kind);
363367
}
364368
}
365369
}
@@ -400,15 +404,14 @@ impl<'a, 'b, 'tcx> FakeBorrowCollector<'a, 'b, 'tcx> {
400404
// // UB because we reached the unreachable.
401405
// }
402406
// ```
403-
// FIXME(deref_patterns): Hence we fake borrow using a non-shallow borrow.
407+
// Hence we fake borrow using a deep borrow.
404408
if let Some(place) = match_pair.place {
405-
// FIXME(deref_patterns): use a non-shallow borrow.
406-
self.fake_borrow(place);
409+
self.fake_borrow(place, FakeBorrowKind::Deep);
407410
}
408411
} else {
409412
// Insert a Shallow borrow of any place that is switched on.
410413
if let Some(place) = match_pair.place {
411-
self.fake_borrow(place);
414+
self.fake_borrow(place, FakeBorrowKind::Shallow);
412415
}
413416

414417
for subpair in &match_pair.subpairs {
@@ -448,7 +451,7 @@ impl<'a, 'b, 'tcx> FakeBorrowCollector<'a, 'b, 'tcx> {
448451
// _ if { u = true; false } => (),
449452
// x => (),
450453
// }
451-
self.fake_borrow_deref_prefixes(*source);
454+
self.fake_borrow_deref_prefixes(*source, FakeBorrowKind::Shallow);
452455
}
453456
}
454457

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#![feature(deref_patterns)]
2+
#![allow(incomplete_features)]
3+
4+
#[rustfmt::skip]
5+
fn main() {
6+
let mut b = Box::new(false);
7+
match b {
8+
deref!(true) => {}
9+
_ if { *b = true; false } => {}
10+
//~^ ERROR cannot assign `*b` in match guard
11+
deref!(false) => {}
12+
_ => {},
13+
}
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
error[E0510]: cannot assign `*b` in match guard
2+
--> $DIR/fake_borrows.rs:9:16
3+
|
4+
LL | match b {
5+
| - value is immutable in match guard
6+
LL | deref!(true) => {}
7+
LL | _ if { *b = true; false } => {}
8+
| ^^^^^^^^^ cannot assign
9+
10+
error: aborting due to 1 previous error
11+
12+
For more information about this error, try `rustc --explain E0510`.

0 commit comments

Comments
 (0)