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::{ErrorGuaranteed, Span};
use std::collections::VecDeque;
use std::fmt::Write;
impl<'tcx> Value<TyCtxt<'tcx>> for Ty<'_> {
@ -135,73 +136,79 @@ impl<'tcx, T> Value<TyCtxt<'tcx>> for Result<T, &'_ ty::layout::LayoutError<'_>>
cycle_error: &CycleError,
_guar: ErrorGuaranteed,
) -> Self {
let guar = if cycle_error.cycle[0].query.dep_kind == dep_kinds::layout_of
&& let Some(def_id) = cycle_error.cycle[0].query.ty_def_id
&& let Some(def_id) = def_id.as_local()
&& let def_kind = tcx.def_kind(def_id)
&& matches!(def_kind, DefKind::Closure)
&& let Some(coroutine_kind) = tcx.coroutine_kind(def_id)
{
// FIXME: `def_span` for an fn-like coroutine will point to the fn's body
// due to interactions between the desugaring into a closure expr and the
// def_span code. I'm not motivated to fix it, because I tried and it was
// not working, so just hack around it by grabbing the parent fn's span.
let span = if coroutine_kind.is_fn_like() {
tcx.def_span(tcx.local_parent(def_id))
} else {
tcx.def_span(def_id)
};
let mut diag = struct_span_err!(
tcx.sess.dcx(),
span,
E0733,
"recursion in {} {} requires boxing",
tcx.def_kind_descr_article(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() {
if frame.query.dep_kind != dep_kinds::layout_of {
continue;
}
let Some(frame_def_id) = frame.query.ty_def_id else {
continue;
};
let Some(frame_coroutine_kind) = tcx.coroutine_kind(frame_def_id) else {
continue;
};
let frame_span = frame
.query
.default_span(cycle_error.cycle[(i + 1) % cycle_error.cycle.len()].span);
if frame_span.is_dummy() {
continue;
}
if i == 0 {
diag.span_label(frame_span, "recursive call here");
} else {
let coroutine_span = if frame_coroutine_kind.is_fn_like() {
tcx.def_span(tcx.parent(frame_def_id))
let mut cycle: VecDeque<_> = cycle_error.cycle.iter().collect();
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 def_kind = tcx.def_kind(def_id)
&& matches!(def_kind, DefKind::Closure)
&& let Some(coroutine_kind) = tcx.coroutine_kind(def_id)
{
// FIXME: `def_span` for an fn-like coroutine will point to the fn's body
// due to interactions between the desugaring into a closure expr and the
// def_span code. I'm not motivated to fix it, because I tried and it was
// not working, so just hack around it by grabbing the parent fn's span.
let span = if coroutine_kind.is_fn_like() {
tcx.def_span(tcx.local_parent(def_id))
} else {
tcx.def_span(frame_def_id)
tcx.def_span(def_id)
};
let mut multispan = MultiSpan::from_span(coroutine_span);
multispan.push_span_label(frame_span, "...leading to this recursive call");
diag.span_note(
multispan,
format!("which leads to this {}", tcx.def_descr(frame_def_id)),
let mut diag = struct_span_err!(
tcx.sess.dcx(),
span,
E0733,
"recursion in {} {} requires boxing",
tcx.def_kind_descr_article(def_kind, def_id.to_def_id()),
tcx.def_kind_descr(def_kind, def_id.to_def_id()),
);
for (i, frame) in cycle.iter().enumerate() {
if frame.query.dep_kind != dep_kinds::layout_of {
continue;
}
let Some(frame_def_id) = frame.query.ty_def_id else {
continue;
};
let Some(frame_coroutine_kind) = tcx.coroutine_kind(frame_def_id) else {
continue;
};
let frame_span =
frame.query.default_span(cycle[(i + 1) % cycle.len()].span);
if frame_span.is_dummy() {
continue;
}
if i == 0 {
diag.span_label(frame_span, "recursive call here");
} else {
let coroutine_span: Span = if frame_coroutine_kind.is_fn_like() {
tcx.def_span(tcx.parent(frame_def_id))
} else {
tcx.def_span(frame_def_id)
};
let mut multispan = MultiSpan::from_span(coroutine_span);
multispan
.push_span_label(frame_span, "...leading to this recursive call");
diag.span_note(
multispan,
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!(
coroutine_kind,
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");
}
break 'search diag.emit();
} else {
cycle.rotate_left(1);
}
}
if matches!(
coroutine_kind,
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(
"consider using the `async_recursion` crate: https://crates.io/crates/async_recursion",
);
}
diag.emit()
} else {
report_cycle(tcx.sess, cycle_error).emit()
};

View File

@ -8,7 +8,6 @@ LL | self.foo_recursive(n - 1).await
| ------------------------------- recursive call here
|
= 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

View File

@ -8,7 +8,6 @@ LL | self.foo_recursive(n - 1).await
| ------------------------------- recursive call here
|
= 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

View File

@ -1,6 +1,7 @@
// edition: 2021
// Test doesn't fail until monomorphization time, unfortunately.
// build-fail
//~^^ ERROR cycle detected when computing layout of
fn main() {
let _ = async {
@ -31,6 +32,7 @@ where
C: First,
{
async fn second(self) {
//~^ ERROR recursion in an async fn requires boxing
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}>`...
= 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: 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
LL | async fn second(self) {
| ^^^^^^^^^^^^^^^^^^^^^
|
= note: a recursive `async fn` call must introduce indirection such as `Box::pin` to avoid an infinitely sized future
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;
| ------------- ...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: consider using the `async_recursion` crate: https://crates.io/crates/async_recursion
error: aborting due to 1 previous error

View File

@ -8,7 +8,6 @@ LL | recursive_async_function().await;
| -------------------------------- recursive call here
|
= 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

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
// otherwise forbidden.
#![feature(coroutines)]
#![allow(unconditional_recursion)]
@ -69,15 +68,6 @@ fn substs_change<T: 'static>() -> impl Sized {
(substs_change::<&T>(),)
}
fn coroutine_hold() -> impl Sized {
move || {
//~^ ERROR
let x = coroutine_hold();
yield;
x;
}
}
fn use_fn_ptr() -> impl Sized {
// OK, error already reported
fn_ptr()

View File

@ -1,5 +1,5 @@
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 {
| ^^^^^^^^^^ 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)>`
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 {
| ^^^^^^^^^^ recursive opaque type
@ -19,7 +19,7 @@ LL | (tuple(),)
| ---------- returning here with type `(impl Sized,)`
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 {
| ^^^^^^^^^^ recursive opaque type
@ -28,7 +28,7 @@ LL | [array()]
| --------- returning here with type `[impl Sized; 1]`
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 {
| ^^^^^^^^^^ recursive opaque type
@ -37,7 +37,7 @@ LL | &ptr() as *const _
| ------------------ returning here with type `*const impl Sized`
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 {
| ^^^^^^^^^^ recursive opaque type
@ -46,7 +46,7 @@ LL | fn_ptr as fn() -> _
| ------------------- returning here with type `fn() -> impl Sized`
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 {
| ^^^^^^^^^^ recursive opaque type
@ -55,10 +55,10 @@ LL | / move || {
LL | | x;
| | - closure captures itself here
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
--> $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 {
| ^^^^^^^^^^ recursive opaque type
@ -67,28 +67,28 @@ LL | / move || {
LL | | &x;
| | - closure captures itself here
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
--> $DIR/recursive-impl-trait-type-indirect.rs:48:21
--> $DIR/recursive-impl-trait-type-indirect.rs:47:21
|
LL | fn closure_sig() -> impl Sized {
| ^^^^^^^^^^ recursive opaque type
LL |
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
--> $DIR/recursive-impl-trait-type-indirect.rs:53:23
--> $DIR/recursive-impl-trait-type-indirect.rs:52:23
|
LL | fn coroutine_sig() -> impl Sized {
| ^^^^^^^^^^ recursive opaque type
LL |
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
--> $DIR/recursive-impl-trait-type-indirect.rs:58:27
--> $DIR/recursive-impl-trait-type-indirect.rs:57:27
|
LL | fn coroutine_capture() -> impl Sized {
| ^^^^^^^^^^ recursive opaque type
@ -98,10 +98,10 @@ LL | | yield;
LL | | x;
| | - coroutine captures itself here
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
--> $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 {
| ^^^^^^^^^^ recursive opaque type
@ -109,17 +109,8 @@ LL |
LL | (substs_change::<&T>(),)
| ------------------------ 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
--> $DIR/recursive-impl-trait-type-indirect.rs:86:26
--> $DIR/recursive-impl-trait-type-indirect.rs:76:26
|
LL | fn mutual_recursion() -> impl Sync {
| ^^^^^^^^^ recursive opaque type
@ -131,7 +122,7 @@ LL | fn mutual_recursion_b() -> impl Sized {
| ---------- returning this opaque type `impl Sized`
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 {
| --------- returning this opaque type `impl Sync`
@ -142,7 +133,6 @@ LL |
LL | mutual_recursion()
| ------------------ 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 an error, try `rustc --explain E0720`.
For more information about this error, try `rustc --explain E0720`.

View File

@ -14,7 +14,6 @@ LL | async fn recur(t: impl Recur) {
LL | t.recur().await;
| --------------- ...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: consider using the `async_recursion` crate: https://crates.io/crates/async_recursion
error: aborting due to 1 previous error