Skip to content

Commit 199af7c

Browse files
Point out source of recursion
1 parent 82a2215 commit 199af7c

13 files changed

+139
-41
lines changed

compiler/rustc_middle/src/ty/util.rs

+36-9
Original file line numberDiff line numberDiff line change
@@ -729,16 +729,43 @@ impl<'tcx> TyCtxt<'tcx> {
729729
DefKind::AssocFn if self.associated_item(def_id).fn_has_self_parameter => "method",
730730
DefKind::Closure if let Some(coroutine_kind) = self.coroutine_kind(def_id) => {
731731
match coroutine_kind {
732-
hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, _) => {
733-
"async closure"
734-
}
735-
hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::AsyncGen, _) => {
736-
"async gen closure"
737-
}
732+
hir::CoroutineKind::Desugared(
733+
hir::CoroutineDesugaring::Async,
734+
hir::CoroutineSource::Fn,
735+
) => "async fn",
736+
hir::CoroutineKind::Desugared(
737+
hir::CoroutineDesugaring::Async,
738+
hir::CoroutineSource::Block,
739+
) => "async block",
740+
hir::CoroutineKind::Desugared(
741+
hir::CoroutineDesugaring::Async,
742+
hir::CoroutineSource::Closure,
743+
) => "async closure",
744+
hir::CoroutineKind::Desugared(
745+
hir::CoroutineDesugaring::AsyncGen,
746+
hir::CoroutineSource::Fn,
747+
) => "async gen fn",
748+
hir::CoroutineKind::Desugared(
749+
hir::CoroutineDesugaring::AsyncGen,
750+
hir::CoroutineSource::Block,
751+
) => "async gen block",
752+
hir::CoroutineKind::Desugared(
753+
hir::CoroutineDesugaring::AsyncGen,
754+
hir::CoroutineSource::Closure,
755+
) => "async gen closure",
756+
hir::CoroutineKind::Desugared(
757+
hir::CoroutineDesugaring::Gen,
758+
hir::CoroutineSource::Fn,
759+
) => "gen fn",
760+
hir::CoroutineKind::Desugared(
761+
hir::CoroutineDesugaring::Gen,
762+
hir::CoroutineSource::Block,
763+
) => "gen block",
764+
hir::CoroutineKind::Desugared(
765+
hir::CoroutineDesugaring::Gen,
766+
hir::CoroutineSource::Closure,
767+
) => "gen closure",
738768
hir::CoroutineKind::Coroutine(_) => "coroutine",
739-
hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Gen, _) => {
740-
"gen closure"
741-
}
742769
}
743770
}
744771
_ => def_kind.descr(def_id),

compiler/rustc_middle/src/values.rs

+51-7
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,8 @@ impl<'tcx, T> Value<TyCtxt<'tcx>> for Result<T, &'_ ty::layout::LayoutError<'_>>
138138
let guar = if cycle_error.cycle[0].query.dep_kind == dep_kinds::layout_of
139139
&& let Some(def_id) = cycle_error.cycle[0].query.ty_def_id
140140
&& let Some(def_id) = def_id.as_local()
141-
&& matches!(tcx.def_kind(def_id), DefKind::Closure)
141+
&& let def_kind = tcx.def_kind(def_id)
142+
&& matches!(def_kind, DefKind::Closure)
142143
&& let Some(coroutine_kind) = tcx.coroutine_kind(def_id)
143144
{
144145
// FIXME: `def_span` for an fn-like coroutine will point to the fn's body
@@ -150,13 +151,56 @@ impl<'tcx, T> Value<TyCtxt<'tcx>> for Result<T, &'_ ty::layout::LayoutError<'_>>
150151
} else {
151152
tcx.def_span(def_id)
152153
};
153-
struct_span_err!(tcx.sess.dcx(), span, E0733, "recursion in an `async fn` requires boxing")
154-
.span_label(span, "recursive `async fn`")
155-
.note("a recursive `async fn` must be rewritten to return a boxed `dyn Future`")
156-
.note(
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))
183+
} else {
184+
tcx.def_span(frame_def_id)
185+
};
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)),
191+
);
192+
}
193+
}
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(
157200
"consider using the `async_recursion` crate: https://crates.io/crates/async_recursion",
158-
)
159-
.emit()
201+
);
202+
}
203+
diag.emit()
160204
} else {
161205
report_cycle(tcx.sess, cycle_error).emit()
162206
};

tests/ui/async-await/in-trait/async-recursive-generic.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ trait MyTrait<T> {
66

77
impl<T> MyTrait<T> for T where T: Copy {
88
async fn foo_recursive(&self, n: usize) -> T {
9-
//~^ ERROR recursion in an `async fn` requires boxing
9+
//~^ ERROR recursion in an async fn requires boxing
1010
if n > 0 {
1111
self.foo_recursive(n - 1).await
1212
} else {

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

+6-3
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
1-
error[E0733]: recursion in an `async fn` requires boxing
1+
error[E0733]: recursion in an async fn requires boxing
22
--> $DIR/async-recursive-generic.rs:8:5
33
|
44
LL | async fn foo_recursive(&self, n: usize) -> T {
5-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ recursive `async fn`
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
...
7+
LL | self.foo_recursive(n - 1).await
8+
| ------------------------------- recursive call here
69
|
7-
= note: a recursive `async fn` must be rewritten to return a boxed `dyn Future`
10+
= note: a recursive `async fn` call must introduce indirection such as `Box::pin` to avoid an infinitely sized future
811
= note: consider using the `async_recursion` crate: https://crates.io/crates/async_recursion
912

1013
error: aborting due to 1 previous error

tests/ui/async-await/in-trait/async-recursive.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ trait MyTrait {
66

77
impl MyTrait for i32 {
88
async fn foo_recursive(&self, n: usize) -> i32 {
9-
//~^ ERROR recursion in an `async fn` requires boxing
9+
//~^ ERROR recursion in an async fn requires boxing
1010
if n > 0 {
1111
self.foo_recursive(n - 1).await
1212
} else {

tests/ui/async-await/in-trait/async-recursive.stderr

+6-3
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
1-
error[E0733]: recursion in an `async fn` requires boxing
1+
error[E0733]: recursion in an async fn requires boxing
22
--> $DIR/async-recursive.rs:8:5
33
|
44
LL | async fn foo_recursive(&self, n: usize) -> i32 {
5-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ recursive `async fn`
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
...
7+
LL | self.foo_recursive(n - 1).await
8+
| ------------------------------- recursive call here
69
|
7-
= note: a recursive `async fn` must be rewritten to return a boxed `dyn Future`
10+
= note: a recursive `async fn` call must introduce indirection such as `Box::pin` to avoid an infinitely sized future
811
= note: consider using the `async_recursion` crate: https://crates.io/crates/async_recursion
912

1013
error: aborting due to 1 previous error

tests/ui/async-await/mutually-recursive-async-impl-trait-type.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// Test that impl trait does not allow creating recursive types that are
33
// otherwise forbidden when using `async` and `await`.
44

5-
async fn rec_1() { //~ ERROR recursion in an `async fn`
5+
async fn rec_1() { //~ ERROR recursion in an async fn
66
rec_2().await;
77
}
88

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

+12-3
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,19 @@
1-
error[E0733]: recursion in an `async fn` requires boxing
1+
error[E0733]: recursion in an async fn requires boxing
22
--> $DIR/mutually-recursive-async-impl-trait-type.rs:5:1
33
|
44
LL | async fn rec_1() {
5-
| ^^^^^^^^^^^^^^^^ recursive `async fn`
5+
| ^^^^^^^^^^^^^^^^
6+
LL | rec_2().await;
7+
| ------------- recursive call here
68
|
7-
= note: a recursive `async fn` must be rewritten to return a boxed `dyn Future`
9+
note: which leads to this async fn
10+
--> $DIR/mutually-recursive-async-impl-trait-type.rs:9:1
11+
|
12+
LL | async fn rec_2() {
13+
| ^^^^^^^^^^^^^^^^
14+
LL | rec_1().await;
15+
| ------------- ...leading to this recursive call
16+
= note: a recursive `async fn` call must introduce indirection such as `Box::pin` to avoid an infinitely sized future
817
= note: consider using the `async_recursion` crate: https://crates.io/crates/async_recursion
918

1019
error: aborting due to 1 previous error

tests/ui/async-await/recursive-async-impl-trait-type.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
// otherwise forbidden when using `async` and `await`.
44

55
async fn recursive_async_function() -> () {
6-
//~^ ERROR recursion in an `async fn` requires boxing
6+
//~^ ERROR recursion in an async fn requires boxing
77
recursive_async_function().await;
88
}
99

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

+6-3
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
1-
error[E0733]: recursion in an `async fn` requires boxing
1+
error[E0733]: recursion in an async fn requires boxing
22
--> $DIR/recursive-async-impl-trait-type.rs:5:1
33
|
44
LL | async fn recursive_async_function() -> () {
5-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ recursive `async fn`
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
LL |
7+
LL | recursive_async_function().await;
8+
| -------------------------------- recursive call here
69
|
7-
= note: a recursive `async fn` must be rewritten to return a boxed `dyn Future`
10+
= note: a recursive `async fn` call must introduce indirection such as `Box::pin` to avoid an infinitely sized future
811
= note: consider using the `async_recursion` crate: https://crates.io/crates/async_recursion
912

1013
error: aborting due to 1 previous error

tests/ui/impl-trait/recursive-impl-trait-type-indirect.stderr

+5-5
Original file line numberDiff line numberDiff line change
@@ -109,14 +109,14 @@ LL |
109109
LL | (substs_change::<&T>(),)
110110
| ------------------------ returning here with type `(impl Sized,)`
111111

112-
error[E0733]: recursion in an `async fn` requires boxing
112+
error[E0733]: recursion in a coroutine requires boxing
113113
--> $DIR/recursive-impl-trait-type-indirect.rs:73:5
114114
|
115115
LL | move || {
116-
| ^^^^^^^ recursive `async fn`
117-
|
118-
= note: a recursive `async fn` must be rewritten to return a boxed `dyn Future`
119-
= note: consider using the `async_recursion` crate: https://crates.io/crates/async_recursion
116+
| ^^^^^^^
117+
LL |
118+
LL | let x = coroutine_hold();
119+
| - recursive call here
120120

121121
error[E0720]: cannot resolve opaque type
122122
--> $DIR/recursive-impl-trait-type-indirect.rs:86:26

tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ impl Recur for () {
1919

2020
fn recur(self) -> Self::Recur {
2121
async move { recur(self).await; }
22-
//~^ ERROR recursion in an `async fn` requires boxing
22+
//~^ ERROR recursion in an async block requires boxing
2323
}
2424
}
2525

tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.stderr

+12-3
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,19 @@
1-
error[E0733]: recursion in an `async fn` requires boxing
1+
error[E0733]: recursion in an async block requires boxing
22
--> $DIR/indirect-recursion-issue-112047.rs:21:9
33
|
44
LL | async move { recur(self).await; }
5-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ recursive `async fn`
5+
| ^^^^^^^^^^^^^-----------------^^^
6+
| |
7+
| recursive call here
68
|
7-
= note: a recursive `async fn` must be rewritten to return a boxed `dyn Future`
9+
note: which leads to this async fn
10+
--> $DIR/indirect-recursion-issue-112047.rs:13:1
11+
|
12+
LL | async fn recur(t: impl Recur) {
13+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
14+
LL | t.recur().await;
15+
| --------------- ...leading to this recursive call
16+
= note: a recursive `async fn` call must introduce indirection such as `Box::pin` to avoid an infinitely sized future
817
= note: consider using the `async_recursion` crate: https://crates.io/crates/async_recursion
918

1019
error: aborting due to 1 previous error

0 commit comments

Comments
 (0)