@@ -322,20 +322,9 @@ impl Visitor<'tcx> for Validator<'_, 'mir, 'tcx> {
322
322
fn visit_rvalue ( & mut self , rvalue : & Rvalue < ' tcx > , location : Location ) {
323
323
trace ! ( "visit_rvalue: rvalue={:?} location={:?}" , rvalue, location) ;
324
324
325
- // Check nested operands and places .
325
+ // Special-case reborrows to be more like a copy of a reference .
326
326
if let Rvalue :: Ref ( _, kind, ref place) = * rvalue {
327
- // Special-case reborrows to be more like a copy of a reference.
328
- let mut reborrow_place = None ;
329
- if let & [ ref proj_base @ .., elem] = place. projection . as_ref ( ) {
330
- if elem == ProjectionElem :: Deref {
331
- let base_ty = Place :: ty_from ( & place. base , proj_base, self . body , self . tcx ) . ty ;
332
- if let ty:: Ref ( ..) = base_ty. kind {
333
- reborrow_place = Some ( proj_base) ;
334
- }
335
- }
336
- }
337
-
338
- if let Some ( proj) = reborrow_place {
327
+ if let Some ( reborrowed_proj) = place_as_reborrow ( self . tcx , self . body , place) {
339
328
let ctx = match kind {
340
329
BorrowKind :: Shared => PlaceContext :: NonMutatingUse (
341
330
NonMutatingUseContext :: SharedBorrow ,
@@ -351,14 +340,13 @@ impl Visitor<'tcx> for Validator<'_, 'mir, 'tcx> {
351
340
) ,
352
341
} ;
353
342
self . visit_place_base ( & place. base , ctx, location) ;
354
- self . visit_projection ( & place. base , proj, ctx, location) ;
355
- } else {
356
- self . super_rvalue ( rvalue, location) ;
343
+ self . visit_projection ( & place. base , reborrowed_proj, ctx, location) ;
344
+ return ;
357
345
}
358
- } else {
359
- self . super_rvalue ( rvalue, location) ;
360
346
}
361
347
348
+ self . super_rvalue ( rvalue, location) ;
349
+
362
350
match * rvalue {
363
351
Rvalue :: Use ( _) |
364
352
Rvalue :: Repeat ( ..) |
@@ -369,9 +357,87 @@ impl Visitor<'tcx> for Validator<'_, 'mir, 'tcx> {
369
357
Rvalue :: Cast ( CastKind :: Pointer ( _) , ..) |
370
358
Rvalue :: Discriminant ( ..) |
371
359
Rvalue :: Len ( _) |
372
- Rvalue :: Ref ( ..) |
373
360
Rvalue :: Aggregate ( ..) => { }
374
361
362
+ | Rvalue :: Ref ( _, kind @ BorrowKind :: Mut { .. } , ref place)
363
+ | Rvalue :: Ref ( _, kind @ BorrowKind :: Unique , ref place)
364
+ => {
365
+ let ty = place. ty ( self . body , self . tcx ) . ty ;
366
+ let is_allowed = match ty. kind {
367
+ // Inside a `static mut`, `&mut [...]` is allowed.
368
+ ty:: Array ( ..) | ty:: Slice ( _) if self . const_kind ( ) == ConstKind :: StaticMut
369
+ => true ,
370
+
371
+ // FIXME(ecstaticmorse): We could allow `&mut []` inside a const context given
372
+ // that this is merely a ZST and it is already eligible for promotion.
373
+ // This may require an RFC?
374
+ /*
375
+ ty::Array(_, len) if len.try_eval_usize(cx.tcx, cx.param_env) == Some(0)
376
+ => true,
377
+ */
378
+
379
+ _ => false ,
380
+ } ;
381
+
382
+ if !is_allowed {
383
+ self . check_op ( ops:: MutBorrow ( kind) ) ;
384
+ }
385
+ }
386
+
387
+ // Taking a shared borrow of a `static` is always legal, even if that `static` has
388
+ // interior mutability.
389
+ | Rvalue :: Ref ( _, BorrowKind :: Shared , ref place)
390
+ | Rvalue :: Ref ( _, BorrowKind :: Shallow , ref place)
391
+ if matches ! ( place. base, PlaceBase :: Static ( _) )
392
+ => { }
393
+
394
+ | Rvalue :: Ref ( _, kind @ BorrowKind :: Shared , ref place)
395
+ | Rvalue :: Ref ( _, kind @ BorrowKind :: Shallow , ref place)
396
+ => {
397
+ // FIXME: Change the `in_*` methods to take a `FnMut` so we don't have to manually
398
+ // seek the cursors beforehand.
399
+ self . qualifs . has_mut_interior . cursor . seek_before ( location) ;
400
+ self . qualifs . indirectly_mutable . seek ( location) ;
401
+
402
+ let borrowed_place_has_mut_interior = HasMutInterior :: in_place (
403
+ & self . item ,
404
+ & |local| self . qualifs . has_mut_interior_eager_seek ( local) ,
405
+ place. as_ref ( ) ,
406
+ ) ;
407
+
408
+ if borrowed_place_has_mut_interior {
409
+ let src_derived_from_illegal_borrow = borrowed_place
410
+ . as_local ( )
411
+ . map_or ( false , |local| self . derived_from_illegal_borrow . contains ( local) ) ;
412
+
413
+ // Don't emit errors for borrows of values that are *themselves* the result of
414
+ // an illegal borrow (e.g., the outermost `&` in `&&Cell::new(42)`). We want to
415
+ // point the user to the place where the original illegal borrow occurred, not
416
+ // to subsequent borrows of the resulting value.
417
+ let dest_derived_from_illegal_borrow = if !src_derived_from_illegal_borrow {
418
+ self . check_op ( ops:: MutBorrow ( kind) ) == CheckOpResult :: Forbidden
419
+ } else {
420
+ true
421
+ } ;
422
+
423
+ // When the target of the assignment is a local with no projections, it will be
424
+ // marked as derived from an illegal borrow if necessary.
425
+ //
426
+ // FIXME: should we also clear `derived_from_illegal_borrow` when a local is
427
+ // assigned a new value?
428
+
429
+ if dest_derived_from_illegal_borrow {
430
+ let block = & self . body [ location. block ] ;
431
+ let statement = & block. statements [ location. statement_index ] ;
432
+ if let StatementKind :: Assign ( box ( dest, _) ) = & statement. kind {
433
+ if let Some ( dest) = dest. as_local ( ) {
434
+ self . derived_from_illegal_borrow . insert ( dest) ;
435
+ }
436
+ }
437
+ }
438
+ }
439
+ }
440
+
375
441
Rvalue :: Cast ( CastKind :: Misc , ref operand, cast_ty) => {
376
442
let operand_ty = operand. ty ( self . body , self . tcx ) ;
377
443
let cast_in = CastTy :: from_ty ( operand_ty) . expect ( "bad input type for cast" ) ;
@@ -436,58 +502,6 @@ impl Visitor<'tcx> for Validator<'_, 'mir, 'tcx> {
436
502
}
437
503
}
438
504
439
- fn visit_assign ( & mut self , dest : & Place < ' tcx > , rvalue : & Rvalue < ' tcx > , location : Location ) {
440
- trace ! ( "visit_assign: dest={:?} rvalue={:?} location={:?}" , dest, rvalue, location) ;
441
-
442
- // Error on mutable borrows or shared borrows of values with interior mutability.
443
- //
444
- // This replicates the logic at the start of `assign` in the old const checker. Note that
445
- // it depends on `HasMutInterior` being set for mutable borrows as well as values with
446
- // interior mutability.
447
- if let Rvalue :: Ref ( _, kind, ref borrowed_place) = * rvalue {
448
- // FIXME: Change the `in_*` methods to take a `FnMut` so we don't have to manually seek
449
- // the cursors beforehand.
450
- self . qualifs . has_mut_interior . cursor . seek_before ( location) ;
451
- self . qualifs . indirectly_mutable . seek ( location) ;
452
-
453
- let rvalue_has_mut_interior = HasMutInterior :: in_rvalue (
454
- & self . item ,
455
- & |local| self . qualifs . has_mut_interior_eager_seek ( local) ,
456
- rvalue,
457
- ) ;
458
-
459
- if rvalue_has_mut_interior {
460
- let is_derived_from_illegal_borrow = match borrowed_place. as_local ( ) {
461
- // If an unprojected local was borrowed and its value was the result of an
462
- // illegal borrow, suppress this error and mark the result of this borrow as
463
- // illegal as well.
464
- Some ( borrowed_local)
465
- if self . derived_from_illegal_borrow . contains ( borrowed_local) =>
466
- {
467
- true
468
- }
469
-
470
- // Otherwise proceed normally: check the legality of a mutable borrow in this
471
- // context.
472
- _ => self . check_op ( ops:: MutBorrow ( kind) ) == CheckOpResult :: Forbidden ,
473
- } ;
474
-
475
- // When the target of the assignment is a local with no projections, mark it as
476
- // derived from an illegal borrow if necessary.
477
- //
478
- // FIXME: should we also clear `derived_from_illegal_borrow` when a local is
479
- // assigned a new value?
480
- if is_derived_from_illegal_borrow {
481
- if let Some ( dest) = dest. as_local ( ) {
482
- self . derived_from_illegal_borrow . insert ( dest) ;
483
- }
484
- }
485
- }
486
- }
487
-
488
- self . super_assign ( dest, rvalue, location) ;
489
- }
490
-
491
505
fn visit_projection_elem (
492
506
& mut self ,
493
507
place_base : & PlaceBase < ' tcx > ,
@@ -725,3 +739,24 @@ fn check_return_ty_is_sync(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, hir_id: HirId)
725
739
}
726
740
} ) ;
727
741
}
742
+
743
+ fn place_as_reborrow (
744
+ tcx : TyCtxt < ' tcx > ,
745
+ body : & Body < ' tcx > ,
746
+ place : & ' a Place < ' tcx > ,
747
+ ) -> Option < & ' a [ PlaceElem < ' tcx > ] > {
748
+ place
749
+ . projection
750
+ . split_last ( )
751
+ . and_then ( |( outermost, inner) | {
752
+ if outermost != & ProjectionElem :: Deref {
753
+ return None ;
754
+ }
755
+
756
+ let inner_ty = Place :: ty_from ( & place. base , inner, body, tcx) . ty ;
757
+ match inner_ty. kind {
758
+ ty:: Ref ( ..) => Some ( inner) ,
759
+ _ => None ,
760
+ }
761
+ } )
762
+ }
0 commit comments