@@ -150,7 +150,7 @@ macro_rules! make_mir_visitor {
150150
151151 fn visit_lvalue( & mut self ,
152152 lvalue: & $( $mutability) * Lvalue <' tcx>,
153- context: LvalueContext ,
153+ context: LvalueContext < ' tcx> ,
154154 location: Location ) {
155155 self . super_lvalue( lvalue, context, location) ;
156156 }
@@ -581,7 +581,7 @@ macro_rules! make_mir_visitor {
581581
582582 fn super_lvalue( & mut self ,
583583 lvalue: & $( $mutability) * Lvalue <' tcx>,
584- context: LvalueContext ,
584+ context: LvalueContext < ' tcx> ,
585585 location: Location ) {
586586 match * lvalue {
587587 Lvalue :: Var ( _) |
@@ -606,7 +606,12 @@ macro_rules! make_mir_visitor {
606606 ref $( $mutability) * base,
607607 ref $( $mutability) * elem,
608608 } = * proj;
609- self . visit_lvalue( base, LvalueContext :: Projection , location) ;
609+ let context = if context. is_mutating_use( ) {
610+ LvalueContext :: Projection ( Mutability :: Mut )
611+ } else {
612+ LvalueContext :: Projection ( Mutability :: Not )
613+ } ;
614+ self . visit_lvalue( base, context, location) ;
610615 self . visit_projection_elem( elem, context, location) ;
611616 }
612617
@@ -751,6 +756,21 @@ macro_rules! make_mir_visitor {
751756
752757 fn super_const_usize( & mut self , _substs: & $( $mutability) * ConstUsize ) {
753758 }
759+
760+ // Convenience methods
761+
762+ fn visit_location( & mut self , mir: & $( $mutability) * Mir <' tcx>, location: Location ) {
763+ let basic_block = & $( $mutability) * mir[ location. block] ;
764+ if basic_block. statements. len( ) == location. statement_index {
765+ if let Some ( ref $( $mutability) * terminator) = basic_block. terminator {
766+ self . visit_terminator( location. block, terminator, location)
767+ }
768+ } else {
769+ let statement = & $( $mutability) *
770+ basic_block. statements[ location. statement_index] ;
771+ self . visit_statement( location. block, statement, location)
772+ }
773+ }
754774 }
755775 }
756776}
@@ -775,8 +795,20 @@ pub enum LvalueContext<'tcx> {
775795 // Being borrowed
776796 Borrow { region : & ' tcx Region , kind : BorrowKind } ,
777797
778- // Used as base for another lvalue, e.g. `x` in `x.y`
779- Projection ,
798+ // Used as base for another lvalue, e.g. `x` in `x.y`.
799+ //
800+ // The `Mutability` argument specifies whether the projection is being performed in order to
801+ // (potentially) mutate the lvalue. For example, the projection `x.y` is marked as a mutation
802+ // in these cases:
803+ //
804+ // x.y = ...;
805+ // f(&mut x.y);
806+ //
807+ // But not in these cases:
808+ //
809+ // z = x.y;
810+ // f(&x.y);
811+ Projection ( Mutability ) ,
780812
781813 // Consumed as part of an operand
782814 Consume ,
@@ -785,3 +817,69 @@ pub enum LvalueContext<'tcx> {
785817 StorageLive ,
786818 StorageDead ,
787819}
820+
821+ impl < ' tcx > LvalueContext < ' tcx > {
822+ /// Returns true if this lvalue context represents a drop.
823+ pub fn is_drop ( & self ) -> bool {
824+ match * self {
825+ LvalueContext :: Drop => true ,
826+ _ => false ,
827+ }
828+ }
829+
830+ /// Returns true if this lvalue context represents a storage live or storage dead marker.
831+ pub fn is_storage_marker ( & self ) -> bool {
832+ match * self {
833+ LvalueContext :: StorageLive | LvalueContext :: StorageDead => true ,
834+ _ => false ,
835+ }
836+ }
837+
838+ /// Returns true if this lvalue context represents a storage live marker.
839+ pub fn is_storage_live_marker ( & self ) -> bool {
840+ match * self {
841+ LvalueContext :: StorageLive => true ,
842+ _ => false ,
843+ }
844+ }
845+
846+ /// Returns true if this lvalue context represents a storage dead marker.
847+ pub fn is_storage_dead_marker ( & self ) -> bool {
848+ match * self {
849+ LvalueContext :: StorageDead => true ,
850+ _ => false ,
851+ }
852+ }
853+
854+ /// Returns true if this lvalue context represents a use that potentially changes the value.
855+ pub fn is_mutating_use ( & self ) -> bool {
856+ match * self {
857+ LvalueContext :: Store | LvalueContext :: Call |
858+ LvalueContext :: Borrow { kind : BorrowKind :: Mut , .. } |
859+ LvalueContext :: Projection ( Mutability :: Mut ) |
860+ LvalueContext :: Drop => true ,
861+ LvalueContext :: Inspect |
862+ LvalueContext :: Borrow { kind : BorrowKind :: Shared , .. } |
863+ LvalueContext :: Borrow { kind : BorrowKind :: Unique , .. } |
864+ LvalueContext :: Projection ( Mutability :: Not ) | LvalueContext :: Consume |
865+ LvalueContext :: StorageLive | LvalueContext :: StorageDead => false ,
866+ }
867+ }
868+
869+ /// Returns true if this lvalue context represents a use that does not change the value.
870+ pub fn is_nonmutating_use ( & self ) -> bool {
871+ match * self {
872+ LvalueContext :: Inspect | LvalueContext :: Borrow { kind : BorrowKind :: Shared , .. } |
873+ LvalueContext :: Borrow { kind : BorrowKind :: Unique , .. } |
874+ LvalueContext :: Projection ( Mutability :: Not ) | LvalueContext :: Consume => true ,
875+ LvalueContext :: Borrow { kind : BorrowKind :: Mut , .. } | LvalueContext :: Store |
876+ LvalueContext :: Call | LvalueContext :: Projection ( Mutability :: Mut ) |
877+ LvalueContext :: Drop | LvalueContext :: StorageLive | LvalueContext :: StorageDead => false ,
878+ }
879+ }
880+
881+ pub fn is_use ( & self ) -> bool {
882+ self . is_mutating_use ( ) || self . is_nonmutating_use ( )
883+ }
884+ }
885+
0 commit comments