11//! This pass statically detects code which has undefined behaviour or is likely to be erroneous.
22//! It can be used to locate problems in MIR building or optimizations. It assumes that all code
33//! can be executed, so it has false positives.
4+ use rustc_data_structures:: fx:: FxHashSet ;
45use rustc_index:: bit_set:: BitSet ;
56use rustc_middle:: mir:: visit:: { PlaceContext , Visitor } ;
67use rustc_middle:: mir:: * ;
@@ -11,7 +12,6 @@ use rustc_mir_dataflow::{Analysis, ResultsCursor};
1112use std:: borrow:: Cow ;
1213
1314pub fn lint_body < ' tcx > ( tcx : TyCtxt < ' tcx > , body : & Body < ' tcx > , when : String ) {
14- let reachable_blocks = traversal:: reachable_as_bitset ( body) ;
1515 let always_live_locals = & always_storage_live_locals ( body) ;
1616
1717 let maybe_storage_live = MaybeStorageLive :: new ( Cow :: Borrowed ( always_live_locals) )
@@ -24,17 +24,19 @@ pub fn lint_body<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, when: String) {
2424 . iterate_to_fixpoint ( )
2525 . into_results_cursor ( body) ;
2626
27- Lint {
27+ let mut lint = Lint {
2828 tcx,
2929 when,
3030 body,
3131 is_fn_like : tcx. def_kind ( body. source . def_id ( ) ) . is_fn_like ( ) ,
3232 always_live_locals,
33- reachable_blocks,
3433 maybe_storage_live,
3534 maybe_storage_dead,
35+ places : Default :: default ( ) ,
36+ } ;
37+ for ( bb, data) in traversal:: reachable ( body) {
38+ lint. visit_basic_block_data ( bb, data) ;
3639 }
37- . visit_body ( body) ;
3840}
3941
4042struct Lint < ' a , ' tcx > {
@@ -43,9 +45,9 @@ struct Lint<'a, 'tcx> {
4345 body : & ' a Body < ' tcx > ,
4446 is_fn_like : bool ,
4547 always_live_locals : & ' a BitSet < Local > ,
46- reachable_blocks : BitSet < BasicBlock > ,
4748 maybe_storage_live : ResultsCursor < ' a , ' tcx , MaybeStorageLive < ' a > > ,
4849 maybe_storage_dead : ResultsCursor < ' a , ' tcx , MaybeStorageDead < ' a > > ,
50+ places : FxHashSet < PlaceRef < ' tcx > > ,
4951}
5052
5153impl < ' a , ' tcx > Lint < ' a , ' tcx > {
@@ -67,7 +69,7 @@ impl<'a, 'tcx> Lint<'a, 'tcx> {
6769
6870impl < ' a , ' tcx > Visitor < ' tcx > for Lint < ' a , ' tcx > {
6971 fn visit_local ( & mut self , local : Local , context : PlaceContext , location : Location ) {
70- if self . reachable_blocks . contains ( location . block ) && context. is_use ( ) {
72+ if context. is_use ( ) {
7173 self . maybe_storage_dead . seek_after_primary_effect ( location) ;
7274 if self . maybe_storage_dead . get ( ) . contains ( local) {
7375 self . fail ( location, format ! ( "use of local {local:?}, which has no storage here" ) ) ;
@@ -76,28 +78,38 @@ impl<'a, 'tcx> Visitor<'tcx> for Lint<'a, 'tcx> {
7678 }
7779
7880 fn visit_statement ( & mut self , statement : & Statement < ' tcx > , location : Location ) {
79- match statement. kind {
80- StatementKind :: StorageLive ( local) => {
81- if self . reachable_blocks . contains ( location. block ) {
82- self . maybe_storage_live . seek_before_primary_effect ( location) ;
83- if self . maybe_storage_live . get ( ) . contains ( local) {
81+ match & statement. kind {
82+ StatementKind :: Assign ( box ( dest, rvalue) ) => {
83+ if let Rvalue :: Use ( Operand :: Copy ( src) | Operand :: Move ( src) ) = rvalue {
84+ // The sides of an assignment must not alias. Currently this just checks whether
85+ // the places are identical.
86+ if dest == src {
8487 self . fail (
8588 location,
86- format ! ( "StorageLive({local:?}) which already has storage here" ) ,
89+ "encountered `Assign` statement with overlapping memory" ,
8790 ) ;
8891 }
8992 }
9093 }
94+ StatementKind :: StorageLive ( local) => {
95+ self . maybe_storage_live . seek_before_primary_effect ( location) ;
96+ if self . maybe_storage_live . get ( ) . contains ( * local) {
97+ self . fail (
98+ location,
99+ format ! ( "StorageLive({local:?}) which already has storage here" ) ,
100+ ) ;
101+ }
102+ }
91103 _ => { }
92104 }
93105
94106 self . super_statement ( statement, location) ;
95107 }
96108
97109 fn visit_terminator ( & mut self , terminator : & Terminator < ' tcx > , location : Location ) {
98- match terminator. kind {
110+ match & terminator. kind {
99111 TerminatorKind :: Return => {
100- if self . is_fn_like && self . reachable_blocks . contains ( location . block ) {
112+ if self . is_fn_like {
101113 self . maybe_storage_live . seek_after_primary_effect ( location) ;
102114 for local in self . maybe_storage_live . get ( ) . iter ( ) {
103115 if !self . always_live_locals . contains ( local) {
@@ -111,6 +123,28 @@ impl<'a, 'tcx> Visitor<'tcx> for Lint<'a, 'tcx> {
111123 }
112124 }
113125 }
126+ TerminatorKind :: Call { args, destination, .. } => {
127+ // The call destination place and Operand::Move place used as an argument might be
128+ // passed by a reference to the callee. Consequently they must be non-overlapping.
129+ // Currently this simply checks for duplicate places.
130+ self . places . clear ( ) ;
131+ self . places . insert ( destination. as_ref ( ) ) ;
132+ let mut has_duplicates = false ;
133+ for arg in args {
134+ if let Operand :: Move ( place) = arg {
135+ has_duplicates |= !self . places . insert ( place. as_ref ( ) ) ;
136+ }
137+ }
138+ if has_duplicates {
139+ self . fail (
140+ location,
141+ format ! (
142+ "encountered overlapping memory in `Move` arguments to `Call` terminator: {:?}" ,
143+ terminator. kind,
144+ ) ,
145+ ) ;
146+ }
147+ }
114148 _ => { }
115149 }
116150
0 commit comments