Skip to content

Commit 841184b

Browse files
compiler-errorslcnr
andcommitted
Make cycle error more resilient to where it starts
Also don't recomment recursive_async crate anymore Co-authored-by: lcnr <[email protected]>
1 parent fa2ff51 commit 841184b

14 files changed

+134
-116
lines changed

Diff for: compiler/rustc_middle/src/values.rs

+69-62
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use rustc_query_system::Value;
1111
use rustc_span::def_id::LocalDefId;
1212
use rustc_span::{ErrorGuaranteed, Span};
1313

14+
use std::collections::VecDeque;
1415
use std::fmt::Write;
1516

1617
impl<'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

Diff for: tests/ui/async-await/in-trait/async-recursive-generic.stderr

-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ LL | self.foo_recursive(n - 1).await
88
| ------------------------------- recursive call here
99
|
1010
= note: a recursive `async fn` call must introduce indirection such as `Box::pin` to avoid an infinitely sized future
11-
= note: consider using the `async_recursion` crate: https://crates.io/crates/async_recursion
1211

1312
error: aborting due to 1 previous error
1413

Diff for: tests/ui/async-await/in-trait/async-recursive.stderr

-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ LL | self.foo_recursive(n - 1).await
88
| ------------------------------- recursive call here
99
|
1010
= note: a recursive `async fn` call must introduce indirection such as `Box::pin` to avoid an infinitely sized future
11-
= note: consider using the `async_recursion` crate: https://crates.io/crates/async_recursion
1211

1312
error: aborting due to 1 previous error
1413

Diff for: tests/ui/async-await/in-trait/indirect-recursion-issue-112047.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// edition: 2021
2+
3+
// Test doesn't fail until monomorphization time, unfortunately.
24
// build-fail
3-
//~^^ ERROR cycle detected when computing layout of
45

56
fn main() {
67
let _ = async {
@@ -31,6 +32,7 @@ where
3132
C: First,
3233
{
3334
async fn second(self) {
35+
//~^ ERROR recursion in an async fn requires boxing
3436
self.first().await.second().await;
3537
}
3638
}
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
1-
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}>`
1+
error[E0733]: recursion in an async fn requires boxing
2+
--> $DIR/indirect-recursion-issue-112047.rs:34:5
23
|
3-
= note: ...which requires computing layout of `core::mem::manually_drop::ManuallyDrop<{async fn body@$DIR/indirect-recursion-issue-112047.rs:33:27: 35:6}>`...
4-
= note: ...which requires computing layout of `{async fn body@$DIR/indirect-recursion-issue-112047.rs:33:27: 35:6}`...
5-
= note: ...which requires computing layout of `core::mem::maybe_uninit::MaybeUninit<<<A as First>::Second as Second>::{opaque#0}>`...
6-
= 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
7-
= note: cycle used when computing layout of `{async block@$DIR/indirect-recursion-issue-112047.rs:6:13: 8:6}`
8-
= 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
4+
LL | async fn second(self) {
5+
| ^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: a recursive `async fn` call must introduce indirection such as `Box::pin` to avoid an infinitely sized future
98

109
error: aborting due to 1 previous error
1110

12-
For more information about this error, try `rustc --explain E0391`.
11+
For more information about this error, try `rustc --explain E0733`.

Diff for: tests/ui/async-await/mutually-recursive-async-impl-trait-type.stderr

-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ LL | async fn rec_2() {
1414
LL | rec_1().await;
1515
| ------------- ...leading to this recursive call
1616
= note: a recursive `async fn` call must introduce indirection such as `Box::pin` to avoid an infinitely sized future
17-
= note: consider using the `async_recursion` crate: https://crates.io/crates/async_recursion
1817

1918
error: aborting due to 1 previous error
2019

Diff for: tests/ui/async-await/recursive-async-impl-trait-type.stderr

-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ LL | recursive_async_function().await;
88
| -------------------------------- recursive call here
99
|
1010
= note: a recursive `async fn` call must introduce indirection such as `Box::pin` to avoid an infinitely sized future
11-
= note: consider using the `async_recursion` crate: https://crates.io/crates/async_recursion
1211

1312
error: aborting due to 1 previous error
1413

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

Diff for: tests/ui/impl-trait/recursive-coroutine-indirect.rs

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// revisions: current next
2+
//[next] compile-flags: -Znext-solver
3+
#![feature(coroutines)]
4+
#![allow(unconditional_recursion)]
5+
fn coroutine_hold() -> impl Sized {
6+
move || { //~ ERROR recursion in a coroutine requires boxing
7+
let x = coroutine_hold();
8+
yield;
9+
x;
10+
}
11+
}
12+
13+
fn main() {}

Diff for: tests/ui/impl-trait/recursive-impl-trait-type-indirect.rs

-10
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
// Test that impl trait does not allow creating recursive types that are
22
// otherwise forbidden.
3-
43
#![feature(coroutines)]
54
#![allow(unconditional_recursion)]
65

@@ -69,15 +68,6 @@ fn substs_change<T: 'static>() -> impl Sized {
6968
(substs_change::<&T>(),)
7069
}
7170

72-
fn coroutine_hold() -> impl Sized {
73-
move || {
74-
//~^ ERROR
75-
let x = coroutine_hold();
76-
yield;
77-
x;
78-
}
79-
}
80-
8171
fn use_fn_ptr() -> impl Sized {
8272
// OK, error already reported
8373
fn_ptr()

0 commit comments

Comments
 (0)