@@ -3,14 +3,18 @@ use rustc_data_structures::graph::iterate::{
33 NodeStatus , TriColorDepthFirstSearch , TriColorVisitor ,
44} ;
55use rustc_hir:: def:: DefKind ;
6- use rustc_middle:: mir:: { self , BasicBlock , BasicBlocks , Body , Operand , TerminatorKind } ;
7- use rustc_middle:: ty:: { self , Instance , TyCtxt } ;
6+ use rustc_middle:: mir:: { self , BasicBlock , BasicBlocks , Body , Terminator , TerminatorKind } ;
7+ use rustc_middle:: ty:: { self , Instance , Ty , TyCtxt } ;
88use rustc_middle:: ty:: { GenericArg , GenericArgs } ;
99use rustc_session:: lint:: builtin:: UNCONDITIONAL_RECURSION ;
1010use rustc_span:: Span ;
1111use std:: ops:: ControlFlow ;
1212
1313pub ( crate ) fn check < ' tcx > ( tcx : TyCtxt < ' tcx > , body : & Body < ' tcx > ) {
14+ check_call_recursion ( tcx, body) ;
15+ }
16+
17+ fn check_call_recursion < ' tcx > ( tcx : TyCtxt < ' tcx > , body : & Body < ' tcx > ) {
1418 let def_id = body. source . def_id ( ) . expect_local ( ) ;
1519
1620 if let DefKind :: Fn | DefKind :: AssocFn = tcx. def_kind ( def_id) {
@@ -23,7 +27,19 @@ pub(crate) fn check<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) {
2327 _ => & [ ] ,
2428 } ;
2529
26- let mut vis = Search { tcx, body, reachable_recursive_calls : vec ! [ ] , trait_args } ;
30+ check_recursion ( tcx, body, CallRecursion { trait_args } )
31+ }
32+ }
33+
34+ fn check_recursion < ' tcx > (
35+ tcx : TyCtxt < ' tcx > ,
36+ body : & Body < ' tcx > ,
37+ classifier : impl TerminatorClassifier < ' tcx > ,
38+ ) {
39+ let def_id = body. source . def_id ( ) . expect_local ( ) ;
40+
41+ if let DefKind :: Fn | DefKind :: AssocFn = tcx. def_kind ( def_id) {
42+ let mut vis = Search { tcx, body, classifier, reachable_recursive_calls : vec ! [ ] } ;
2743 if let Some ( NonRecursive ) =
2844 TriColorDepthFirstSearch :: new ( & body. basic_blocks ) . run_from_start ( & mut vis)
2945 {
@@ -46,20 +62,66 @@ pub(crate) fn check<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) {
4662 }
4763}
4864
65+ /// Requires drop elaboration to have been performed first.
66+ pub fn check_drop_recursion < ' tcx > ( tcx : TyCtxt < ' tcx > , body : & Body < ' tcx > ) {
67+ let def_id = body. source . def_id ( ) . expect_local ( ) ;
68+
69+ // First check if `body` is an `fn drop()` of `Drop`
70+ if let DefKind :: AssocFn = tcx. def_kind ( def_id) &&
71+ let Some ( trait_ref) = tcx. impl_of_method ( def_id. to_def_id ( ) ) . and_then ( |def_id| tcx. impl_trait_ref ( def_id) ) &&
72+ let Some ( drop_trait) = tcx. lang_items ( ) . drop_trait ( ) && drop_trait == trait_ref. instantiate_identity ( ) . def_id {
73+
74+ // It was. Now figure out for what type `Drop` is implemented and then
75+ // check for recursion.
76+ if let ty:: Ref ( _, dropped_ty, _) = tcx. liberate_late_bound_regions (
77+ def_id. to_def_id ( ) ,
78+ tcx. fn_sig ( def_id) . instantiate_identity ( ) . input ( 0 ) ,
79+ ) . kind ( ) {
80+ check_recursion ( tcx, body, RecursiveDrop { drop_for : * dropped_ty } ) ;
81+ }
82+ }
83+ }
84+
85+ trait TerminatorClassifier < ' tcx > {
86+ fn is_recursive_terminator (
87+ & self ,
88+ tcx : TyCtxt < ' tcx > ,
89+ body : & Body < ' tcx > ,
90+ terminator : & Terminator < ' tcx > ,
91+ ) -> bool ;
92+ }
93+
4994struct NonRecursive ;
5095
51- struct Search < ' mir , ' tcx > {
96+ struct Search < ' mir , ' tcx , C : TerminatorClassifier < ' tcx > > {
5297 tcx : TyCtxt < ' tcx > ,
5398 body : & ' mir Body < ' tcx > ,
54- trait_args : & ' tcx [ GenericArg < ' tcx > ] ,
99+ classifier : C ,
55100
56101 reachable_recursive_calls : Vec < Span > ,
57102}
58103
59- impl < ' mir , ' tcx > Search < ' mir , ' tcx > {
104+ struct CallRecursion < ' tcx > {
105+ trait_args : & ' tcx [ GenericArg < ' tcx > ] ,
106+ }
107+
108+ struct RecursiveDrop < ' tcx > {
109+ /// The type that `Drop` is implemented for.
110+ drop_for : Ty < ' tcx > ,
111+ }
112+
113+ impl < ' tcx > TerminatorClassifier < ' tcx > for CallRecursion < ' tcx > {
60114 /// Returns `true` if `func` refers to the function we are searching in.
61- fn is_recursive_call ( & self , func : & Operand < ' tcx > , args : & [ Operand < ' tcx > ] ) -> bool {
62- let Search { tcx, body, trait_args, .. } = * self ;
115+ fn is_recursive_terminator (
116+ & self ,
117+ tcx : TyCtxt < ' tcx > ,
118+ body : & Body < ' tcx > ,
119+ terminator : & Terminator < ' tcx > ,
120+ ) -> bool {
121+ let TerminatorKind :: Call { func, args, .. } = & terminator. kind else {
122+ return false ;
123+ } ;
124+
63125 // Resolving function type to a specific instance that is being called is expensive. To
64126 // avoid the cost we check the number of arguments first, which is sufficient to reject
65127 // most of calls as non-recursive.
@@ -86,14 +148,30 @@ impl<'mir, 'tcx> Search<'mir, 'tcx> {
86148 // calling into an entirely different method (for example, a call from the default
87149 // method in the trait to `<A as Trait<B>>::method`, where `A` and/or `B` are
88150 // specific types).
89- return callee == caller && & call_args[ ..trait_args. len ( ) ] == trait_args;
151+ return callee == caller && & call_args[ ..self . trait_args . len ( ) ] == self . trait_args ;
90152 }
91153
92154 false
93155 }
94156}
95157
96- impl < ' mir , ' tcx > TriColorVisitor < BasicBlocks < ' tcx > > for Search < ' mir , ' tcx > {
158+ impl < ' tcx > TerminatorClassifier < ' tcx > for RecursiveDrop < ' tcx > {
159+ fn is_recursive_terminator (
160+ & self ,
161+ tcx : TyCtxt < ' tcx > ,
162+ body : & Body < ' tcx > ,
163+ terminator : & Terminator < ' tcx > ,
164+ ) -> bool {
165+ let TerminatorKind :: Drop { place, .. } = & terminator. kind else { return false } ;
166+
167+ let dropped_ty = place. ty ( body, tcx) . ty ;
168+ dropped_ty == self . drop_for
169+ }
170+ }
171+
172+ impl < ' mir , ' tcx , C : TerminatorClassifier < ' tcx > > TriColorVisitor < BasicBlocks < ' tcx > >
173+ for Search < ' mir , ' tcx , C >
174+ {
97175 type BreakVal = NonRecursive ;
98176
99177 fn node_examined (
@@ -138,26 +216,23 @@ impl<'mir, 'tcx> TriColorVisitor<BasicBlocks<'tcx>> for Search<'mir, 'tcx> {
138216 fn node_settled ( & mut self , bb : BasicBlock ) -> ControlFlow < Self :: BreakVal > {
139217 // When we examine a node for the last time, remember it if it is a recursive call.
140218 let terminator = self . body [ bb] . terminator ( ) ;
141- if let TerminatorKind :: Call { func, args, .. } = & terminator. kind {
142- if self . is_recursive_call ( func, args) {
143- self . reachable_recursive_calls . push ( terminator. source_info . span ) ;
144- }
219+ if self . classifier . is_recursive_terminator ( self . tcx , self . body , terminator) {
220+ self . reachable_recursive_calls . push ( terminator. source_info . span ) ;
145221 }
146222
147223 ControlFlow :: Continue ( ( ) )
148224 }
149225
150226 fn ignore_edge ( & mut self , bb : BasicBlock , target : BasicBlock ) -> bool {
151227 let terminator = self . body [ bb] . terminator ( ) ;
152- if terminator. unwind ( ) == Some ( & mir:: UnwindAction :: Cleanup ( target) )
153- && terminator. successors ( ) . count ( ) > 1
228+ let ignore_unwind = terminator. unwind ( ) == Some ( & mir:: UnwindAction :: Cleanup ( target) )
229+ && terminator. successors ( ) . count ( ) > 1 ;
230+ if ignore_unwind || self . classifier . is_recursive_terminator ( self . tcx , self . body , terminator)
154231 {
155232 return true ;
156233 }
157- // Don't traverse successors of recursive calls or false CFG edges.
158- match self . body [ bb] . terminator ( ) . kind {
159- TerminatorKind :: Call { ref func, ref args, .. } => self . is_recursive_call ( func, args) ,
160- TerminatorKind :: FalseEdge { imaginary_target, .. } => imaginary_target == target,
234+ match & terminator. kind {
235+ TerminatorKind :: FalseEdge { imaginary_target, .. } => imaginary_target == & target,
161236 _ => false ,
162237 }
163238 }
0 commit comments