@@ -7,6 +7,7 @@ use rustc_middle::mir::*;
7
7
use rustc_middle:: thir:: { self , * } ;
8
8
use rustc_middle:: ty:: TypeVisitableExt ;
9
9
use rustc_middle:: ty:: { self , Ty } ;
10
+ use rustc_span:: Span ;
10
11
11
12
impl < ' a , ' tcx > Builder < ' a , ' tcx > {
12
13
pub ( crate ) fn field_match_pairs < ' pat > (
@@ -267,19 +268,99 @@ impl<'pat, 'tcx> MatchPair<'pat, 'tcx> {
267
268
268
269
pub ( super ) struct FakeBorrowCollector < ' a , ' b , ' tcx > {
269
270
cx : & ' a mut Builder < ' b , ' tcx > ,
271
+ /// Base of the scrutinee place. Used to distinguish bindings inside the scrutinee place from
272
+ /// bindings inside deref patterns.
273
+ scrutinee_base : PlaceBase ,
270
274
fake_borrows : FxIndexSet < Place < ' tcx > > ,
271
275
}
272
276
277
+ /// Determine the set of places that have to be stable across match guards.
278
+ ///
279
+ /// Returns a list of places that need a fake borrow along with a local to store it.
280
+ ///
281
+ /// Match exhaustiveness checking is not able to handle the case where the place being matched on is
282
+ /// mutated in the guards. We add "fake borrows" to the guards that prevent any mutation of the
283
+ /// place being matched. There are a some subtleties:
284
+ ///
285
+ /// 1. Borrowing `*x` doesn't prevent assigning to `x`. If `x` is a shared reference, the borrow
286
+ /// isn't even tracked. As such we have to add fake borrows of any prefixes of a place.
287
+ /// 2. We don't want `match x { (Some(_), _) => (), .. }` to conflict with mutable borrows of `x.1`, so we
288
+ /// only add fake borrows for places which are bound or tested by the match.
289
+ /// 3. We don't want `match x { Some(_) => (), .. }` to conflict with mutable borrows of `(x as
290
+ /// Some).0`, so the borrows are a special shallow borrow that only affects the place and not its
291
+ /// projections.
292
+ /// ```rust
293
+ /// let mut x = (Some(0), true);
294
+ /// match x {
295
+ /// (Some(_), false) => {}
296
+ /// _ if { if let Some(ref mut y) = x.0 { *y += 1 }; true } => {}
297
+ /// _ => {}
298
+ /// }
299
+ /// ```
300
+ /// 4. The fake borrows may be of places in inactive variants, e.g. here we need to fake borrow `x`
301
+ /// and `(x as Some).0`, but when we reach the guard `x` may not be `Some`.
302
+ /// ```rust
303
+ /// let mut x = (Some(Some(0)), true);
304
+ /// match x {
305
+ /// (Some(Some(_)), false) => {}
306
+ /// _ if { if let Some(Some(ref mut y)) = x.0 { *y += 1 }; true } => {}
307
+ /// _ => {}
308
+ /// }
309
+ /// ```
310
+ /// So it would be UB to generate code for the fake borrows. They therefore have to be removed by
311
+ /// a MIR pass run after borrow checking.
312
+ pub ( super ) fn collect_fake_borrows < ' tcx > (
313
+ cx : & mut Builder < ' _ , ' tcx > ,
314
+ candidates : & [ & mut Candidate < ' _ , ' tcx > ] ,
315
+ temp_span : Span ,
316
+ scrutinee_base : PlaceBase ,
317
+ ) -> Vec < ( Place < ' tcx > , Local ) > {
318
+ let mut collector =
319
+ FakeBorrowCollector { cx, scrutinee_base, fake_borrows : FxIndexSet :: default ( ) } ;
320
+ for candidate in candidates. iter ( ) {
321
+ collector. visit_candidate ( candidate) ;
322
+ }
323
+ let fake_borrows = collector. fake_borrows ;
324
+ debug ! ( "add_fake_borrows fake_borrows = {:?}" , fake_borrows) ;
325
+ let tcx = cx. tcx ;
326
+ fake_borrows
327
+ . iter ( )
328
+ . copied ( )
329
+ . map ( |matched_place| {
330
+ let fake_borrow_deref_ty = matched_place. ty ( & cx. local_decls , tcx) . ty ;
331
+ let fake_borrow_ty =
332
+ Ty :: new_imm_ref ( tcx, tcx. lifetimes . re_erased , fake_borrow_deref_ty) ;
333
+ let mut fake_borrow_temp = LocalDecl :: new ( fake_borrow_ty, temp_span) ;
334
+ fake_borrow_temp. local_info = ClearCrossCrate :: Set ( Box :: new ( LocalInfo :: FakeBorrow ) ) ;
335
+ let fake_borrow_temp = cx. local_decls . push ( fake_borrow_temp) ;
336
+ ( matched_place, fake_borrow_temp)
337
+ } )
338
+ . collect ( )
339
+ }
340
+
273
341
impl < ' a , ' b , ' tcx > FakeBorrowCollector < ' a , ' b , ' tcx > {
274
- pub ( super ) fn collect_fake_borrows (
275
- cx : & ' a mut Builder < ' b , ' tcx > ,
276
- candidates : & [ & mut Candidate < ' _ , ' tcx > ] ,
277
- ) -> FxIndexSet < Place < ' tcx > > {
278
- let mut collector = Self { cx, fake_borrows : FxIndexSet :: default ( ) } ;
279
- for candidate in candidates. iter ( ) {
280
- collector. visit_candidate ( candidate) ;
342
+ // Fake borrow this place and its dereference prefixes.
343
+ fn fake_borrow ( & mut self , place : Place < ' tcx > ) {
344
+ let new = self . fake_borrows . insert ( place) ;
345
+ if !new {
346
+ return ;
347
+ }
348
+ // Also fake borrow the prefixes of any fake borrow.
349
+ self . fake_borrow_deref_prefixes ( place) ;
350
+ }
351
+
352
+ // Fake borrow the prefixes of this place that are dereferences.
353
+ fn fake_borrow_deref_prefixes ( & mut self , place : Place < ' tcx > ) {
354
+ for ( place_ref, elem) in place. as_ref ( ) . iter_projections ( ) . rev ( ) {
355
+ if let ProjectionElem :: Deref = elem {
356
+ // Insert a shallow borrow after a deref. For other projections the borrow of
357
+ // `place_ref` will conflict with any mutation of `place.base`.
358
+ let new = self . fake_borrows . insert ( place_ref. to_place ( self . cx . tcx ) ) ;
359
+ if !new {
360
+ return ;
361
+ }
362
+ }
281
363
}
282
- collector. fake_borrows
283
364
}
284
365
285
366
fn visit_candidate ( & mut self , candidate : & Candidate < ' _ , ' tcx > ) {
@@ -305,10 +386,28 @@ impl<'a, 'b, 'tcx> FakeBorrowCollector<'a, 'b, 'tcx> {
305
386
for flat_pat in pats. iter ( ) {
306
387
self . visit_flat_pat ( flat_pat)
307
388
}
389
+ } else if matches ! ( match_pair. test_case, TestCase :: Deref { .. } ) {
390
+ // The subpairs of a deref pattern are all places relative to the deref temporary, so we
391
+ // don't fake borrow them. Problem is, if we only shallowly fake-borrowed
392
+ // `match_pair.place`, this would allow:
393
+ // ```
394
+ // let mut b = Box::new(false);
395
+ // match b {
396
+ // deref!(true) => {} // not reached because `*b == false`
397
+ // _ if { *b = true; false } => {} // not reached because the guard is `false`
398
+ // deref!(false) => {} // not reached because the guard changed it
399
+ // // UB because we reached the unreachable.
400
+ // }
401
+ // ```
402
+ // FIXME(deref_patterns): Hence we fake borrow using a non-shallow borrow.
403
+ if let Some ( place) = match_pair. place {
404
+ // FIXME(deref_patterns): use a non-shallow borrow.
405
+ self . fake_borrow ( place) ;
406
+ }
308
407
} else {
309
408
// Insert a Shallow borrow of any place that is switched on.
310
409
if let Some ( place) = match_pair. place {
311
- self . fake_borrows . insert ( place) ;
410
+ self . fake_borrow ( place) ;
312
411
}
313
412
314
413
for subpair in & match_pair. subpairs {
@@ -318,6 +417,14 @@ impl<'a, 'b, 'tcx> FakeBorrowCollector<'a, 'b, 'tcx> {
318
417
}
319
418
320
419
fn visit_binding ( & mut self , Binding { source, .. } : & Binding < ' tcx > ) {
420
+ if let PlaceBase :: Local ( l) = self . scrutinee_base
421
+ && l != source. local
422
+ {
423
+ // The base of this place is a temporary created for deref patterns. We don't emit fake
424
+ // borrows for these as they are not initialized in all branches.
425
+ return ;
426
+ }
427
+
321
428
// Insert a borrows of prefixes of places that are bound and are
322
429
// behind a dereference projection.
323
430
//
@@ -334,13 +441,13 @@ impl<'a, 'b, 'tcx> FakeBorrowCollector<'a, 'b, 'tcx> {
334
441
// y if { y == 1 && (x = &2) == () } => y,
335
442
// _ => 3,
336
443
// }
337
- if let Some ( i ) = source . projection . iter ( ) . rposition ( |elem| elem == ProjectionElem :: Deref ) {
338
- let proj_base = & source . projection [ ..i ] ;
339
- self . fake_borrows . insert ( Place {
340
- local : source . local ,
341
- projection : self . cx . tcx . mk_place_elems ( proj_base ) ,
342
- } ) ;
343
- }
444
+ //
445
+ // We don't just fake borrow the whole place because this is allowed:
446
+ // match u {
447
+ // _ if { u = true; false } => () ,
448
+ // x => ( ),
449
+ // }
450
+ self . fake_borrow_deref_prefixes ( * source ) ;
344
451
}
345
452
}
346
453
0 commit comments