@@ -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 , Terminator , 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,27 +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 > {
60- fn is_recursive_terminator ( & self , terminator : & Terminator < ' tcx > ) -> bool {
61- match & terminator. kind {
62- TerminatorKind :: Call { func, args, .. } => self . is_recursive_call ( func, args) ,
63- _ => false ,
64- }
65- }
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+ }
66112
113+ impl < ' tcx > TerminatorClassifier < ' tcx > for CallRecursion < ' tcx > {
67114 /// Returns `true` if `func` refers to the function we are searching in.
68- fn is_recursive_call ( & self , func : & Operand < ' tcx > , args : & [ Operand < ' tcx > ] ) -> bool {
69- 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+
70125 // Resolving function type to a specific instance that is being called is expensive. To
71126 // avoid the cost we check the number of arguments first, which is sufficient to reject
72127 // most of calls as non-recursive.
@@ -93,14 +148,30 @@ impl<'mir, 'tcx> Search<'mir, 'tcx> {
93148 // calling into an entirely different method (for example, a call from the default
94149 // method in the trait to `<A as Trait<B>>::method`, where `A` and/or `B` are
95150 // specific types).
96- return callee == caller && & call_args[ ..trait_args. len ( ) ] == trait_args;
151+ return callee == caller && & call_args[ ..self . trait_args . len ( ) ] == self . trait_args ;
97152 }
98153
99154 false
100155 }
101156}
102157
103- 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+ {
104175 type BreakVal = NonRecursive ;
105176
106177 fn node_examined (
@@ -145,7 +216,7 @@ impl<'mir, 'tcx> TriColorVisitor<BasicBlocks<'tcx>> for Search<'mir, 'tcx> {
145216 fn node_settled ( & mut self , bb : BasicBlock ) -> ControlFlow < Self :: BreakVal > {
146217 // When we examine a node for the last time, remember it if it is a recursive call.
147218 let terminator = self . body [ bb] . terminator ( ) ;
148- if self . is_recursive_terminator ( terminator) {
219+ if self . classifier . is_recursive_terminator ( self . tcx , self . body , terminator) {
149220 self . reachable_recursive_calls . push ( terminator. source_info . span ) ;
150221 }
151222
@@ -156,7 +227,8 @@ impl<'mir, 'tcx> TriColorVisitor<BasicBlocks<'tcx>> for Search<'mir, 'tcx> {
156227 let terminator = self . body [ bb] . terminator ( ) ;
157228 let ignore_unwind = terminator. unwind ( ) == Some ( & mir:: UnwindAction :: Cleanup ( target) )
158229 && terminator. successors ( ) . count ( ) > 1 ;
159- if ignore_unwind || self . is_recursive_terminator ( terminator) {
230+ if ignore_unwind || self . classifier . is_recursive_terminator ( self . tcx , self . body , terminator)
231+ {
160232 return true ;
161233 }
162234 match & terminator. kind {
0 commit comments