11use rustc_hir:: { Expr , HirId } ;
2+ use rustc_index:: bit_set:: BitSet ;
23use rustc_middle:: mir:: visit:: { MutatingUseContext , NonMutatingUseContext , PlaceContext , Visitor } ;
34use rustc_middle:: mir:: {
4- traversal, Body , InlineAsmOperand , Local , Location , Place , StatementKind , TerminatorKind , START_BLOCK ,
5+ traversal, BasicBlock , Body , InlineAsmOperand , Local , Location , Place , StatementKind , TerminatorKind , START_BLOCK ,
56} ;
67use rustc_middle:: ty:: TyCtxt ;
78
@@ -79,8 +80,32 @@ impl<'a, 'tcx> Visitor<'tcx> for V<'a> {
7980 }
8081}
8182
83+ /// Checks if the block is part of a cycle
84+ pub fn block_in_cycle ( body : & Body < ' _ > , block : BasicBlock ) -> bool {
85+ let mut seen = BitSet :: new_empty ( body. basic_blocks . len ( ) ) ;
86+ let mut to_visit = Vec :: with_capacity ( body. basic_blocks . len ( ) / 2 ) ;
87+
88+ seen. insert ( block) ;
89+ let mut next = block;
90+ loop {
91+ for succ in body. basic_blocks [ next] . terminator ( ) . successors ( ) {
92+ if seen. insert ( succ) {
93+ to_visit. push ( succ) ;
94+ } else if succ == block {
95+ return true ;
96+ }
97+ }
98+
99+ if let Some ( x) = to_visit. pop ( ) {
100+ next = x;
101+ } else {
102+ return false ;
103+ }
104+ }
105+ }
106+
82107/// Convenience wrapper around `visit_local_usage`.
83- pub fn used_exactly_once ( mir : & rustc_middle :: mir :: Body < ' _ > , local : rustc_middle:: mir:: Local ) -> Option < bool > {
108+ pub fn used_exactly_once ( mir : & Body < ' _ > , local : rustc_middle:: mir:: Local ) -> Option < bool > {
84109 visit_local_usage (
85110 & [ local] ,
86111 mir,
@@ -91,11 +116,14 @@ pub fn used_exactly_once(mir: &rustc_middle::mir::Body<'_>, local: rustc_middle:
91116 )
92117 . map ( |mut vec| {
93118 let LocalUsage { local_use_locs, .. } = vec. remove ( 0 ) ;
94- local_use_locs
119+ let mut locations = local_use_locs
95120 . into_iter ( )
96- . filter ( |location| !is_local_assignment ( mir, local, * location) )
97- . count ( )
98- == 1
121+ . filter ( |& location| !is_local_assignment ( mir, local, location) ) ;
122+ if let Some ( location) = locations. next ( ) {
123+ locations. next ( ) . is_none ( ) && !block_in_cycle ( mir, location. block )
124+ } else {
125+ false
126+ }
99127 } )
100128}
101129
0 commit comments