@@ -11,6 +11,7 @@ use rustc_query_system::Value;
1111use rustc_span:: def_id:: LocalDefId ;
1212use rustc_span:: { ErrorGuaranteed , Span } ;
1313
14+ use std:: collections:: VecDeque ;
1415use std:: fmt:: Write ;
1516
1617impl < ' tcx > Value < TyCtxt < ' tcx > > for Ty < ' _ > {
@@ -135,73 +136,79 @@ impl<'tcx, T> Value<TyCtxt<'tcx>> for Result<T, &'_ ty::layout::LayoutError<'_>>
135136 cycle_error : & CycleError ,
136137 _guar : ErrorGuaranteed ,
137138 ) -> Self {
138- let guar = if cycle_error. cycle [ 0 ] . query . dep_kind == dep_kinds:: layout_of
139- && let Some ( def_id) = cycle_error. cycle [ 0 ] . query . ty_def_id
140- && let Some ( def_id) = def_id. as_local ( )
141- && let def_kind = tcx. def_kind ( def_id)
142- && matches ! ( def_kind, DefKind :: Closure )
143- && let Some ( coroutine_kind) = tcx. coroutine_kind ( def_id)
144- {
145- // FIXME: `def_span` for an fn-like coroutine will point to the fn's body
146- // due to interactions between the desugaring into a closure expr and the
147- // def_span code. I'm not motivated to fix it, because I tried and it was
148- // not working, so just hack around it by grabbing the parent fn's span.
149- let span = if coroutine_kind. is_fn_like ( ) {
150- tcx. def_span ( tcx. local_parent ( def_id) )
151- } else {
152- tcx. def_span ( def_id)
153- } ;
154- let mut diag = struct_span_err ! (
155- tcx. sess. dcx( ) ,
156- span,
157- E0733 ,
158- "recursion in {} {} requires boxing" ,
159- tcx. def_kind_descr_article( def_kind, def_id. to_def_id( ) ) ,
160- tcx. def_kind_descr( def_kind, def_id. to_def_id( ) ) ,
161- ) ;
162- for ( i, frame) in cycle_error. cycle . iter ( ) . enumerate ( ) {
163- if frame. query . dep_kind != dep_kinds:: layout_of {
164- continue ;
165- }
166- let Some ( frame_def_id) = frame. query . ty_def_id else {
167- continue ;
168- } ;
169- let Some ( frame_coroutine_kind) = tcx. coroutine_kind ( frame_def_id) else {
170- continue ;
171- } ;
172- let frame_span = frame
173- . query
174- . default_span ( cycle_error. cycle [ ( i + 1 ) % cycle_error. cycle . len ( ) ] . span ) ;
175- if frame_span. is_dummy ( ) {
176- continue ;
177- }
178- if i == 0 {
179- diag. span_label ( frame_span, "recursive call here" ) ;
180- } else {
181- let coroutine_span = if frame_coroutine_kind. is_fn_like ( ) {
182- tcx. def_span ( tcx. parent ( frame_def_id) )
139+ let mut cycle: VecDeque < _ > = cycle_error. cycle . iter ( ) . collect ( ) ;
140+
141+ let guar = ' search: {
142+ for _ in 0 ..cycle. len ( ) {
143+ if cycle[ 0 ] . query . dep_kind == dep_kinds:: layout_of
144+ && let Some ( def_id) = cycle[ 0 ] . query . ty_def_id
145+ && let Some ( def_id) = def_id. as_local ( )
146+ && let def_kind = tcx. def_kind ( def_id)
147+ && matches ! ( def_kind, DefKind :: Closure )
148+ && let Some ( coroutine_kind) = tcx. coroutine_kind ( def_id)
149+ {
150+ // FIXME: `def_span` for an fn-like coroutine will point to the fn's body
151+ // due to interactions between the desugaring into a closure expr and the
152+ // def_span code. I'm not motivated to fix it, because I tried and it was
153+ // not working, so just hack around it by grabbing the parent fn's span.
154+ let span = if coroutine_kind. is_fn_like ( ) {
155+ tcx. def_span ( tcx. local_parent ( def_id) )
183156 } else {
184- tcx. def_span ( frame_def_id )
157+ tcx. def_span ( def_id )
185158 } ;
186- let mut multispan = MultiSpan :: from_span ( coroutine_span) ;
187- multispan. push_span_label ( frame_span, "...leading to this recursive call" ) ;
188- diag. span_note (
189- multispan,
190- format ! ( "which leads to this {}" , tcx. def_descr( frame_def_id) ) ,
159+ let mut diag = struct_span_err ! (
160+ tcx. sess. dcx( ) ,
161+ span,
162+ E0733 ,
163+ "recursion in {} {} requires boxing" ,
164+ tcx. def_kind_descr_article( def_kind, def_id. to_def_id( ) ) ,
165+ tcx. def_kind_descr( def_kind, def_id. to_def_id( ) ) ,
191166 ) ;
167+ for ( i, frame) in cycle. iter ( ) . enumerate ( ) {
168+ if frame. query . dep_kind != dep_kinds:: layout_of {
169+ continue ;
170+ }
171+ let Some ( frame_def_id) = frame. query . ty_def_id else {
172+ continue ;
173+ } ;
174+ let Some ( frame_coroutine_kind) = tcx. coroutine_kind ( frame_def_id) else {
175+ continue ;
176+ } ;
177+ let frame_span =
178+ frame. query . default_span ( cycle[ ( i + 1 ) % cycle. len ( ) ] . span ) ;
179+ if frame_span. is_dummy ( ) {
180+ continue ;
181+ }
182+ if i == 0 {
183+ diag. span_label ( frame_span, "recursive call here" ) ;
184+ } else {
185+ let coroutine_span: Span = if frame_coroutine_kind. is_fn_like ( ) {
186+ tcx. def_span ( tcx. parent ( frame_def_id) )
187+ } else {
188+ tcx. def_span ( frame_def_id)
189+ } ;
190+ let mut multispan = MultiSpan :: from_span ( coroutine_span) ;
191+ multispan
192+ . push_span_label ( frame_span, "...leading to this recursive call" ) ;
193+ diag. span_note (
194+ multispan,
195+ format ! ( "which leads to this {}" , tcx. def_descr( frame_def_id) ) ,
196+ ) ;
197+ }
198+ }
199+ // FIXME: We could report a structured suggestion if we had
200+ // enough info here... Maybe we can use a hacky HIR walker.
201+ if matches ! (
202+ coroutine_kind,
203+ hir:: CoroutineKind :: Desugared ( hir:: CoroutineDesugaring :: Async , _)
204+ ) {
205+ diag. note ( "a recursive `async fn` call must introduce indirection such as `Box::pin` to avoid an infinitely sized future" ) ;
206+ }
207+ break ' search diag. emit ( ) ;
208+ } else {
209+ cycle. rotate_left ( 1 ) ;
192210 }
193211 }
194- if matches ! (
195- coroutine_kind,
196- hir:: CoroutineKind :: Desugared ( hir:: CoroutineDesugaring :: Async , _)
197- ) {
198- diag. note ( "a recursive `async fn` call must introduce indirection such as `Box::pin` to avoid an infinitely sized future" ) ;
199- diag. note (
200- "consider using the `async_recursion` crate: https://crates.io/crates/async_recursion" ,
201- ) ;
202- }
203- diag. emit ( )
204- } else {
205212 report_cycle ( tcx. sess , cycle_error) . emit ( )
206213 } ;
207214
0 commit comments