@@ -322,20 +322,9 @@ impl Visitor<'tcx> for Validator<'_, 'mir, 'tcx> {
322322 fn visit_rvalue ( & mut self , rvalue : & Rvalue < ' tcx > , location : Location ) {
323323 trace ! ( "visit_rvalue: rvalue={:?} location={:?}" , rvalue, location) ;
324324
325- // Check nested operands and places .
325+ // Special-case reborrows to be more like a copy of a reference .
326326 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) {
339328 let ctx = match kind {
340329 BorrowKind :: Shared => PlaceContext :: NonMutatingUse (
341330 NonMutatingUseContext :: SharedBorrow ,
@@ -351,14 +340,13 @@ impl Visitor<'tcx> for Validator<'_, 'mir, 'tcx> {
351340 ) ,
352341 } ;
353342 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 ;
357345 }
358- } else {
359- self . super_rvalue ( rvalue, location) ;
360346 }
361347
348+ self . super_rvalue ( rvalue, location) ;
349+
362350 match * rvalue {
363351 Rvalue :: Use ( _) |
364352 Rvalue :: Repeat ( ..) |
@@ -369,9 +357,87 @@ impl Visitor<'tcx> for Validator<'_, 'mir, 'tcx> {
369357 Rvalue :: Cast ( CastKind :: Pointer ( _) , ..) |
370358 Rvalue :: Discriminant ( ..) |
371359 Rvalue :: Len ( _) |
372- Rvalue :: Ref ( ..) |
373360 Rvalue :: Aggregate ( ..) => { }
374361
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+
375441 Rvalue :: Cast ( CastKind :: Misc , ref operand, cast_ty) => {
376442 let operand_ty = operand. ty ( self . body , self . tcx ) ;
377443 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> {
436502 }
437503 }
438504
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-
491505 fn visit_projection_elem (
492506 & mut self ,
493507 place_base : & PlaceBase < ' tcx > ,
@@ -725,3 +739,24 @@ fn check_return_ty_is_sync(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, hir_id: HirId)
725739 }
726740 } ) ;
727741}
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