Make cycle error more resilient to where it starts

Also don't recomment recursive_async crate anymore

Co-authored-by: lcnr <rust@lcnr.de>
This commit is contained in:
Michael Goulet 2023-11-24 15:05:11 +00:00
parent fa2ff51ace
commit 841184bcae
14 changed files with 134 additions and 116 deletions

View file

@ -11,6 +11,7 @@ use rustc_query_system::Value;
use rustc_span::def_id::LocalDefId; use rustc_span::def_id::LocalDefId;
use rustc_span::{ErrorGuaranteed, Span}; use rustc_span::{ErrorGuaranteed, Span};
use std::collections::VecDeque;
use std::fmt::Write; use std::fmt::Write;
impl<'tcx> Value<TyCtxt<'tcx>> for Ty<'_> { impl<'tcx> Value<TyCtxt<'tcx>> for Ty<'_> {
@ -135,8 +136,12 @@ impl<'tcx, T> Value<TyCtxt<'tcx>> for Result<T, &'_ ty::layout::LayoutError<'_>>
cycle_error: &CycleError, cycle_error: &CycleError,
_guar: ErrorGuaranteed, _guar: ErrorGuaranteed,
) -> Self { ) -> Self {
let guar = if cycle_error.cycle[0].query.dep_kind == dep_kinds::layout_of let mut cycle: VecDeque<_> = cycle_error.cycle.iter().collect();
&& let Some(def_id) = cycle_error.cycle[0].query.ty_def_id
let guar = 'search: {
for _ in 0..cycle.len() {
if cycle[0].query.dep_kind == dep_kinds::layout_of
&& let Some(def_id) = cycle[0].query.ty_def_id
&& let Some(def_id) = def_id.as_local() && let Some(def_id) = def_id.as_local()
&& let def_kind = tcx.def_kind(def_id) && let def_kind = tcx.def_kind(def_id)
&& matches!(def_kind, DefKind::Closure) && matches!(def_kind, DefKind::Closure)
@ -159,7 +164,7 @@ impl<'tcx, T> Value<TyCtxt<'tcx>> for Result<T, &'_ ty::layout::LayoutError<'_>>
tcx.def_kind_descr_article(def_kind, def_id.to_def_id()), tcx.def_kind_descr_article(def_kind, def_id.to_def_id()),
tcx.def_kind_descr(def_kind, def_id.to_def_id()), tcx.def_kind_descr(def_kind, def_id.to_def_id()),
); );
for (i, frame) in cycle_error.cycle.iter().enumerate() { for (i, frame) in cycle.iter().enumerate() {
if frame.query.dep_kind != dep_kinds::layout_of { if frame.query.dep_kind != dep_kinds::layout_of {
continue; continue;
} }
@ -169,39 +174,41 @@ impl<'tcx, T> Value<TyCtxt<'tcx>> for Result<T, &'_ ty::layout::LayoutError<'_>>
let Some(frame_coroutine_kind) = tcx.coroutine_kind(frame_def_id) else { let Some(frame_coroutine_kind) = tcx.coroutine_kind(frame_def_id) else {
continue; continue;
}; };
let frame_span = frame let frame_span =
.query frame.query.default_span(cycle[(i + 1) % cycle.len()].span);
.default_span(cycle_error.cycle[(i + 1) % cycle_error.cycle.len()].span);
if frame_span.is_dummy() { if frame_span.is_dummy() {
continue; continue;
} }
if i == 0 { if i == 0 {
diag.span_label(frame_span, "recursive call here"); diag.span_label(frame_span, "recursive call here");
} else { } else {
let coroutine_span = if frame_coroutine_kind.is_fn_like() { let coroutine_span: Span = if frame_coroutine_kind.is_fn_like() {
tcx.def_span(tcx.parent(frame_def_id)) tcx.def_span(tcx.parent(frame_def_id))
} else { } else {
tcx.def_span(frame_def_id) tcx.def_span(frame_def_id)
}; };
let mut multispan = MultiSpan::from_span(coroutine_span); let mut multispan = MultiSpan::from_span(coroutine_span);
multispan.push_span_label(frame_span, "...leading to this recursive call"); multispan
.push_span_label(frame_span, "...leading to this recursive call");
diag.span_note( diag.span_note(
multispan, multispan,
format!("which leads to this {}", tcx.def_descr(frame_def_id)), format!("which leads to this {}", tcx.def_descr(frame_def_id)),
); );
} }
} }
// FIXME: We could report a structured suggestion if we had
// enough info here... Maybe we can use a hacky HIR walker.
if matches!( if matches!(
coroutine_kind, coroutine_kind,
hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, _) hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, _)
) { ) {
diag.note("a recursive `async fn` call must introduce indirection such as `Box::pin` to avoid an infinitely sized future"); diag.note("a recursive `async fn` call must introduce indirection such as `Box::pin` to avoid an infinitely sized future");
diag.note(
"consider using the `async_recursion` crate: https://crates.io/crates/async_recursion",
);
} }
diag.emit() break 'search diag.emit();
} else { } else {
cycle.rotate_left(1);
}
}
report_cycle(tcx.sess, cycle_error).emit() report_cycle(tcx.sess, cycle_error).emit()
}; };

View file

@ -8,7 +8,6 @@ LL | self.foo_recursive(n - 1).await
| ------------------------------- recursive call here | ------------------------------- recursive call here
| |
= note: a recursive `async fn` call must introduce indirection such as `Box::pin` to avoid an infinitely sized future = note: a recursive `async fn` call must introduce indirection such as `Box::pin` to avoid an infinitely sized future
= note: consider using the `async_recursion` crate: https://crates.io/crates/async_recursion
error: aborting due to 1 previous error error: aborting due to 1 previous error

View file

@ -8,7 +8,6 @@ LL | self.foo_recursive(n - 1).await
| ------------------------------- recursive call here | ------------------------------- recursive call here
| |
= note: a recursive `async fn` call must introduce indirection such as `Box::pin` to avoid an infinitely sized future = note: a recursive `async fn` call must introduce indirection such as `Box::pin` to avoid an infinitely sized future
= note: consider using the `async_recursion` crate: https://crates.io/crates/async_recursion
error: aborting due to 1 previous error error: aborting due to 1 previous error

View file

@ -1,6 +1,7 @@
// edition: 2021 // edition: 2021
// Test doesn't fail until monomorphization time, unfortunately.
// build-fail // build-fail
//~^^ ERROR cycle detected when computing layout of
fn main() { fn main() {
let _ = async { let _ = async {
@ -31,6 +32,7 @@ where
C: First, C: First,
{ {
async fn second(self) { async fn second(self) {
//~^ ERROR recursion in an async fn requires boxing
self.first().await.second().await; self.first().await.second().await;
} }
} }

View file

@ -1,12 +1,11 @@
error[E0391]: cycle detected when computing layout of `core::mem::maybe_uninit::MaybeUninit<{async fn body@$DIR/indirect-recursion-issue-112047.rs:33:27: 35:6}>` error[E0733]: recursion in an async fn requires boxing
--> $DIR/indirect-recursion-issue-112047.rs:34:5
| |
= note: ...which requires computing layout of `core::mem::manually_drop::ManuallyDrop<{async fn body@$DIR/indirect-recursion-issue-112047.rs:33:27: 35:6}>`... LL | async fn second(self) {
= note: ...which requires computing layout of `{async fn body@$DIR/indirect-recursion-issue-112047.rs:33:27: 35:6}`... | ^^^^^^^^^^^^^^^^^^^^^
= note: ...which requires computing layout of `core::mem::maybe_uninit::MaybeUninit<<<A as First>::Second as Second>::{opaque#0}>`... |
= note: ...which again requires computing layout of `core::mem::maybe_uninit::MaybeUninit<{async fn body@$DIR/indirect-recursion-issue-112047.rs:33:27: 35:6}>`, completing the cycle = note: a recursive `async fn` call must introduce indirection such as `Box::pin` to avoid an infinitely sized future
= note: cycle used when computing layout of `{async block@$DIR/indirect-recursion-issue-112047.rs:6:13: 8:6}`
= note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information
error: aborting due to 1 previous error error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0391`. For more information about this error, try `rustc --explain E0733`.

View file

@ -14,7 +14,6 @@ LL | async fn rec_2() {
LL | rec_1().await; LL | rec_1().await;
| ------------- ...leading to this recursive call | ------------- ...leading to this recursive call
= note: a recursive `async fn` call must introduce indirection such as `Box::pin` to avoid an infinitely sized future = note: a recursive `async fn` call must introduce indirection such as `Box::pin` to avoid an infinitely sized future
= note: consider using the `async_recursion` crate: https://crates.io/crates/async_recursion
error: aborting due to 1 previous error error: aborting due to 1 previous error

View file

@ -8,7 +8,6 @@ LL | recursive_async_function().await;
| -------------------------------- recursive call here | -------------------------------- recursive call here
| |
= note: a recursive `async fn` call must introduce indirection such as `Box::pin` to avoid an infinitely sized future = note: a recursive `async fn` call must introduce indirection such as `Box::pin` to avoid an infinitely sized future
= note: consider using the `async_recursion` crate: https://crates.io/crates/async_recursion
error: aborting due to 1 previous error error: aborting due to 1 previous error

View file

@ -0,0 +1,11 @@
error[E0733]: recursion in a coroutine requires boxing
--> $DIR/recursive-coroutine-indirect.rs:6:5
|
LL | move || {
| ^^^^^^^
LL | let x = coroutine_hold();
| - recursive call here
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0733`.

View file

@ -0,0 +1,11 @@
error[E0733]: recursion in a coroutine requires boxing
--> $DIR/recursive-coroutine-indirect.rs:6:5
|
LL | move || {
| ^^^^^^^
LL | let x = coroutine_hold();
| - recursive call here
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0733`.

View file

@ -0,0 +1,13 @@
// revisions: current next
//[next] compile-flags: -Znext-solver
#![feature(coroutines)]
#![allow(unconditional_recursion)]
fn coroutine_hold() -> impl Sized {
move || { //~ ERROR recursion in a coroutine requires boxing
let x = coroutine_hold();
yield;
x;
}
}
fn main() {}

View file

@ -1,6 +1,5 @@
// Test that impl trait does not allow creating recursive types that are // Test that impl trait does not allow creating recursive types that are
// otherwise forbidden. // otherwise forbidden.
#![feature(coroutines)] #![feature(coroutines)]
#![allow(unconditional_recursion)] #![allow(unconditional_recursion)]
@ -69,15 +68,6 @@ fn substs_change<T: 'static>() -> impl Sized {
(substs_change::<&T>(),) (substs_change::<&T>(),)
} }
fn coroutine_hold() -> impl Sized {
move || {
//~^ ERROR
let x = coroutine_hold();
yield;
x;
}
}
fn use_fn_ptr() -> impl Sized { fn use_fn_ptr() -> impl Sized {
// OK, error already reported // OK, error already reported
fn_ptr() fn_ptr()

View file

@ -1,5 +1,5 @@
error[E0720]: cannot resolve opaque type error[E0720]: cannot resolve opaque type
--> $DIR/recursive-impl-trait-type-indirect.rs:7:22 --> $DIR/recursive-impl-trait-type-indirect.rs:6:22
| |
LL | fn option(i: i32) -> impl Sized { LL | fn option(i: i32) -> impl Sized {
| ^^^^^^^^^^ recursive opaque type | ^^^^^^^^^^ recursive opaque type
@ -10,7 +10,7 @@ LL | if i < 0 { None } else { Some((option(i - 1), i)) }
| returning here with type `Option<(impl Sized, i32)>` | returning here with type `Option<(impl Sized, i32)>`
error[E0720]: cannot resolve opaque type error[E0720]: cannot resolve opaque type
--> $DIR/recursive-impl-trait-type-indirect.rs:12:15 --> $DIR/recursive-impl-trait-type-indirect.rs:11:15
| |
LL | fn tuple() -> impl Sized { LL | fn tuple() -> impl Sized {
| ^^^^^^^^^^ recursive opaque type | ^^^^^^^^^^ recursive opaque type
@ -19,7 +19,7 @@ LL | (tuple(),)
| ---------- returning here with type `(impl Sized,)` | ---------- returning here with type `(impl Sized,)`
error[E0720]: cannot resolve opaque type error[E0720]: cannot resolve opaque type
--> $DIR/recursive-impl-trait-type-indirect.rs:17:15 --> $DIR/recursive-impl-trait-type-indirect.rs:16:15
| |
LL | fn array() -> impl Sized { LL | fn array() -> impl Sized {
| ^^^^^^^^^^ recursive opaque type | ^^^^^^^^^^ recursive opaque type
@ -28,7 +28,7 @@ LL | [array()]
| --------- returning here with type `[impl Sized; 1]` | --------- returning here with type `[impl Sized; 1]`
error[E0720]: cannot resolve opaque type error[E0720]: cannot resolve opaque type
--> $DIR/recursive-impl-trait-type-indirect.rs:22:13 --> $DIR/recursive-impl-trait-type-indirect.rs:21:13
| |
LL | fn ptr() -> impl Sized { LL | fn ptr() -> impl Sized {
| ^^^^^^^^^^ recursive opaque type | ^^^^^^^^^^ recursive opaque type
@ -37,7 +37,7 @@ LL | &ptr() as *const _
| ------------------ returning here with type `*const impl Sized` | ------------------ returning here with type `*const impl Sized`
error[E0720]: cannot resolve opaque type error[E0720]: cannot resolve opaque type
--> $DIR/recursive-impl-trait-type-indirect.rs:27:16 --> $DIR/recursive-impl-trait-type-indirect.rs:26:16
| |
LL | fn fn_ptr() -> impl Sized { LL | fn fn_ptr() -> impl Sized {
| ^^^^^^^^^^ recursive opaque type | ^^^^^^^^^^ recursive opaque type
@ -46,7 +46,7 @@ LL | fn_ptr as fn() -> _
| ------------------- returning here with type `fn() -> impl Sized` | ------------------- returning here with type `fn() -> impl Sized`
error[E0720]: cannot resolve opaque type error[E0720]: cannot resolve opaque type
--> $DIR/recursive-impl-trait-type-indirect.rs:32:25 --> $DIR/recursive-impl-trait-type-indirect.rs:31:25
| |
LL | fn closure_capture() -> impl Sized { LL | fn closure_capture() -> impl Sized {
| ^^^^^^^^^^ recursive opaque type | ^^^^^^^^^^ recursive opaque type
@ -55,10 +55,10 @@ LL | / move || {
LL | | x; LL | | x;
| | - closure captures itself here | | - closure captures itself here
LL | | } LL | | }
| |_____- returning here with type `{closure@$DIR/recursive-impl-trait-type-indirect.rs:35:5: 35:12}` | |_____- returning here with type `{closure@$DIR/recursive-impl-trait-type-indirect.rs:34:5: 34:12}`
error[E0720]: cannot resolve opaque type error[E0720]: cannot resolve opaque type
--> $DIR/recursive-impl-trait-type-indirect.rs:40:29 --> $DIR/recursive-impl-trait-type-indirect.rs:39:29
| |
LL | fn closure_ref_capture() -> impl Sized { LL | fn closure_ref_capture() -> impl Sized {
| ^^^^^^^^^^ recursive opaque type | ^^^^^^^^^^ recursive opaque type
@ -67,28 +67,28 @@ LL | / move || {
LL | | &x; LL | | &x;
| | - closure captures itself here | | - closure captures itself here
LL | | } LL | | }
| |_____- returning here with type `{closure@$DIR/recursive-impl-trait-type-indirect.rs:43:5: 43:12}` | |_____- returning here with type `{closure@$DIR/recursive-impl-trait-type-indirect.rs:42:5: 42:12}`
error[E0720]: cannot resolve opaque type error[E0720]: cannot resolve opaque type
--> $DIR/recursive-impl-trait-type-indirect.rs:48:21 --> $DIR/recursive-impl-trait-type-indirect.rs:47:21
| |
LL | fn closure_sig() -> impl Sized { LL | fn closure_sig() -> impl Sized {
| ^^^^^^^^^^ recursive opaque type | ^^^^^^^^^^ recursive opaque type
LL | LL |
LL | || closure_sig() LL | || closure_sig()
| ---------------- returning here with type `{closure@$DIR/recursive-impl-trait-type-indirect.rs:50:5: 50:7}` | ---------------- returning here with type `{closure@$DIR/recursive-impl-trait-type-indirect.rs:49:5: 49:7}`
error[E0720]: cannot resolve opaque type error[E0720]: cannot resolve opaque type
--> $DIR/recursive-impl-trait-type-indirect.rs:53:23 --> $DIR/recursive-impl-trait-type-indirect.rs:52:23
| |
LL | fn coroutine_sig() -> impl Sized { LL | fn coroutine_sig() -> impl Sized {
| ^^^^^^^^^^ recursive opaque type | ^^^^^^^^^^ recursive opaque type
LL | LL |
LL | || coroutine_sig() LL | || coroutine_sig()
| ------------------ returning here with type `{closure@$DIR/recursive-impl-trait-type-indirect.rs:55:5: 55:7}` | ------------------ returning here with type `{closure@$DIR/recursive-impl-trait-type-indirect.rs:54:5: 54:7}`
error[E0720]: cannot resolve opaque type error[E0720]: cannot resolve opaque type
--> $DIR/recursive-impl-trait-type-indirect.rs:58:27 --> $DIR/recursive-impl-trait-type-indirect.rs:57:27
| |
LL | fn coroutine_capture() -> impl Sized { LL | fn coroutine_capture() -> impl Sized {
| ^^^^^^^^^^ recursive opaque type | ^^^^^^^^^^ recursive opaque type
@ -98,10 +98,10 @@ LL | | yield;
LL | | x; LL | | x;
| | - coroutine captures itself here | | - coroutine captures itself here
LL | | } LL | | }
| |_____- returning here with type `{coroutine@$DIR/recursive-impl-trait-type-indirect.rs:61:5: 61:12}` | |_____- returning here with type `{coroutine@$DIR/recursive-impl-trait-type-indirect.rs:60:5: 60:12}`
error[E0720]: cannot resolve opaque type error[E0720]: cannot resolve opaque type
--> $DIR/recursive-impl-trait-type-indirect.rs:67:35 --> $DIR/recursive-impl-trait-type-indirect.rs:66:35
| |
LL | fn substs_change<T: 'static>() -> impl Sized { LL | fn substs_change<T: 'static>() -> impl Sized {
| ^^^^^^^^^^ recursive opaque type | ^^^^^^^^^^ recursive opaque type
@ -109,17 +109,8 @@ LL |
LL | (substs_change::<&T>(),) LL | (substs_change::<&T>(),)
| ------------------------ returning here with type `(impl Sized,)` | ------------------------ returning here with type `(impl Sized,)`
error[E0733]: recursion in a coroutine requires boxing
--> $DIR/recursive-impl-trait-type-indirect.rs:73:5
|
LL | move || {
| ^^^^^^^
LL |
LL | let x = coroutine_hold();
| - recursive call here
error[E0720]: cannot resolve opaque type error[E0720]: cannot resolve opaque type
--> $DIR/recursive-impl-trait-type-indirect.rs:86:26 --> $DIR/recursive-impl-trait-type-indirect.rs:76:26
| |
LL | fn mutual_recursion() -> impl Sync { LL | fn mutual_recursion() -> impl Sync {
| ^^^^^^^^^ recursive opaque type | ^^^^^^^^^ recursive opaque type
@ -131,7 +122,7 @@ LL | fn mutual_recursion_b() -> impl Sized {
| ---------- returning this opaque type `impl Sized` | ---------- returning this opaque type `impl Sized`
error[E0720]: cannot resolve opaque type error[E0720]: cannot resolve opaque type
--> $DIR/recursive-impl-trait-type-indirect.rs:91:28 --> $DIR/recursive-impl-trait-type-indirect.rs:81:28
| |
LL | fn mutual_recursion() -> impl Sync { LL | fn mutual_recursion() -> impl Sync {
| --------- returning this opaque type `impl Sync` | --------- returning this opaque type `impl Sync`
@ -142,7 +133,6 @@ LL |
LL | mutual_recursion() LL | mutual_recursion()
| ------------------ returning here with type `impl Sync` | ------------------ returning here with type `impl Sync`
error: aborting due to 14 previous errors error: aborting due to 13 previous errors
Some errors have detailed explanations: E0720, E0733. For more information about this error, try `rustc --explain E0720`.
For more information about an error, try `rustc --explain E0720`.

View file

@ -14,7 +14,6 @@ LL | async fn recur(t: impl Recur) {
LL | t.recur().await; LL | t.recur().await;
| --------------- ...leading to this recursive call | --------------- ...leading to this recursive call
= note: a recursive `async fn` call must introduce indirection such as `Box::pin` to avoid an infinitely sized future = note: a recursive `async fn` call must introduce indirection such as `Box::pin` to avoid an infinitely sized future
= note: consider using the `async_recursion` crate: https://crates.io/crates/async_recursion
error: aborting due to 1 previous error error: aborting due to 1 previous error