Skip to content

Commit 82a2215

Browse files
Don't check for recursion in generator witness fields
1 parent dfb9f5d commit 82a2215

File tree

19 files changed

+102
-124
lines changed

19 files changed

+102
-124
lines changed

compiler/rustc_error_codes/src/error_codes/E0733.md

+15-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ async fn foo(n: usize) {
1313
To perform async recursion, the `async fn` needs to be desugared such that the
1414
`Future` is explicit in the return type:
1515

16-
```edition2018,compile_fail,E0720
16+
```edition2018,compile_fail,E0733
1717
use std::future::Future;
1818
fn foo_desugared(n: usize) -> impl Future<Output = ()> {
1919
async move {
@@ -41,4 +41,18 @@ fn foo_recursive(n: usize) -> Pin<Box<dyn Future<Output = ()>>> {
4141
The `Box<...>` ensures that the result is of known size, and the pin is
4242
required to keep it in the same place in memory.
4343

44+
Alternatively, the recursive call-site can be boxed:
45+
46+
```edition2018
47+
use std::future::Future;
48+
use std::pin::Pin;
49+
fn foo_recursive(n: usize) -> impl Future<Output = ()> {
50+
async move {
51+
if n > 0 {
52+
Box::pin(foo_recursive(n - 1)).await;
53+
}
54+
}
55+
}
56+
```
57+
4458
[`async`]: https://doc.rust-lang.org/std/keyword.async.html

compiler/rustc_hir/src/hir.rs

+6
Original file line numberDiff line numberDiff line change
@@ -1361,6 +1361,12 @@ impl CoroutineKind {
13611361
}
13621362
}
13631363

1364+
impl CoroutineKind {
1365+
pub fn is_fn_like(self) -> bool {
1366+
matches!(self, CoroutineKind::Desugared(_, CoroutineSource::Fn))
1367+
}
1368+
}
1369+
13641370
impl fmt::Display for CoroutineKind {
13651371
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
13661372
match self {

compiler/rustc_hir_analysis/src/check/check.rs

+7-18
Original file line numberDiff line numberDiff line change
@@ -213,13 +213,12 @@ fn check_opaque(tcx: TyCtxt<'_>, def_id: LocalDefId) {
213213
return;
214214
}
215215

216-
let args = GenericArgs::identity_for_item(tcx, item.owner_id);
217216
let span = tcx.def_span(item.owner_id.def_id);
218217

219218
if tcx.type_of(item.owner_id.def_id).instantiate_identity().references_error() {
220219
return;
221220
}
222-
if check_opaque_for_cycles(tcx, item.owner_id.def_id, args, span, origin).is_err() {
221+
if check_opaque_for_cycles(tcx, item.owner_id.def_id, span).is_err() {
223222
return;
224223
}
225224

@@ -230,16 +229,16 @@ fn check_opaque(tcx: TyCtxt<'_>, def_id: LocalDefId) {
230229
pub(super) fn check_opaque_for_cycles<'tcx>(
231230
tcx: TyCtxt<'tcx>,
232231
def_id: LocalDefId,
233-
args: GenericArgsRef<'tcx>,
234232
span: Span,
235-
origin: &hir::OpaqueTyOrigin,
236233
) -> Result<(), ErrorGuaranteed> {
234+
let args = GenericArgs::identity_for_item(tcx, def_id);
237235
if tcx.try_expand_impl_trait_type(def_id.to_def_id(), args).is_err() {
238-
let reported = match origin {
239-
hir::OpaqueTyOrigin::AsyncFn(..) => async_opaque_type_cycle_error(tcx, span),
240-
_ => opaque_type_cycle_error(tcx, def_id, span),
241-
};
236+
let reported = opaque_type_cycle_error(tcx, def_id, span);
242237
Err(reported)
238+
} else if let Err(&LayoutError::Cycle(guar)) =
239+
tcx.layout_of(tcx.param_env(def_id).and(Ty::new_opaque(tcx, def_id.to_def_id(), args)))
240+
{
241+
Err(guar)
243242
} else {
244243
Ok(())
245244
}
@@ -1300,16 +1299,6 @@ pub(super) fn check_type_params_are_used<'tcx>(
13001299
}
13011300
}
13021301

1303-
fn async_opaque_type_cycle_error(tcx: TyCtxt<'_>, span: Span) -> ErrorGuaranteed {
1304-
struct_span_err!(tcx.dcx(), span, E0733, "recursion in an `async fn` requires boxing")
1305-
.span_label_mv(span, "recursive `async fn`")
1306-
.note_mv("a recursive `async fn` must be rewritten to return a boxed `dyn Future`")
1307-
.note_mv(
1308-
"consider using the `async_recursion` crate: https://crates.io/crates/async_recursion",
1309-
)
1310-
.emit()
1311-
}
1312-
13131302
/// Emit an error for recursive opaque types.
13141303
///
13151304
/// If this is a return `impl Trait`, find the item's return expressions and point at them. For

compiler/rustc_middle/src/query/keys.rs

+9-4
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ pub trait Key: Sized {
4040
None
4141
}
4242

43-
fn ty_adt_id(&self) -> Option<DefId> {
43+
fn ty_def_id(&self) -> Option<DefId> {
4444
None
4545
}
4646
}
@@ -406,9 +406,10 @@ impl<'tcx> Key for Ty<'tcx> {
406406
DUMMY_SP
407407
}
408408

409-
fn ty_adt_id(&self) -> Option<DefId> {
410-
match self.kind() {
409+
fn ty_def_id(&self) -> Option<DefId> {
410+
match *self.kind() {
411411
ty::Adt(adt, _) => Some(adt.did()),
412+
ty::Coroutine(def_id, ..) => Some(def_id),
412413
_ => None,
413414
}
414415
}
@@ -452,6 +453,10 @@ impl<'tcx, T: Key> Key for ty::ParamEnvAnd<'tcx, T> {
452453
fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
453454
self.value.default_span(tcx)
454455
}
456+
457+
fn ty_def_id(&self) -> Option<DefId> {
458+
self.value.ty_def_id()
459+
}
455460
}
456461

457462
impl Key for Symbol {
@@ -550,7 +555,7 @@ impl<'tcx> Key for (ValidityRequirement, ty::ParamEnvAnd<'tcx, Ty<'tcx>>) {
550555
DUMMY_SP
551556
}
552557

553-
fn ty_adt_id(&self) -> Option<DefId> {
558+
fn ty_def_id(&self) -> Option<DefId> {
554559
match self.1.value.kind() {
555560
ty::Adt(adt, _) => Some(adt.did()),
556561
_ => None,

compiler/rustc_middle/src/query/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1387,6 +1387,8 @@ rustc_queries! {
13871387
) -> Result<ty::layout::TyAndLayout<'tcx>, &'tcx ty::layout::LayoutError<'tcx>> {
13881388
depth_limit
13891389
desc { "computing layout of `{}`", key.value }
1390+
// we emit our own error during query cycle handling
1391+
cycle_delay_bug
13901392
}
13911393

13921394
/// Compute a `FnAbi` suitable for indirect calls, i.e. to `fn` pointers.

compiler/rustc_middle/src/ty/util.rs

+1-12
Original file line numberDiff line numberDiff line change
@@ -896,18 +896,7 @@ impl<'tcx> OpaqueTypeExpander<'tcx> {
896896
}
897897
let args = args.fold_with(self);
898898
if !self.check_recursion || self.seen_opaque_tys.insert(def_id) {
899-
let expanded_ty = match self.expanded_cache.get(&(def_id, args)) {
900-
Some(expanded_ty) => *expanded_ty,
901-
None => {
902-
for bty in self.tcx.coroutine_hidden_types(def_id) {
903-
let hidden_ty = bty.instantiate(self.tcx, args);
904-
self.fold_ty(hidden_ty);
905-
}
906-
let expanded_ty = Ty::new_coroutine_witness(self.tcx, def_id, args);
907-
self.expanded_cache.insert((def_id, args), expanded_ty);
908-
expanded_ty
909-
}
910-
};
899+
let expanded_ty = Ty::new_coroutine_witness(self.tcx, def_id, args);
911900
if self.check_recursion {
912901
self.seen_opaque_tys.remove(&def_id);
913902
}

compiler/rustc_middle/src/values.rs

+31-5
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use rustc_hir as hir;
66
use rustc_hir::def::{DefKind, Res};
77
use rustc_middle::ty::Representability;
88
use rustc_middle::ty::{self, Ty, TyCtxt};
9-
use rustc_query_system::query::CycleError;
9+
use rustc_query_system::query::{report_cycle, CycleError};
1010
use rustc_query_system::Value;
1111
use rustc_span::def_id::LocalDefId;
1212
use rustc_span::{ErrorGuaranteed, Span};
@@ -97,7 +97,7 @@ impl<'tcx> Value<TyCtxt<'tcx>> for Representability {
9797
}
9898
for info in &cycle_error.cycle {
9999
if info.query.dep_kind == dep_kinds::representability_adt_ty
100-
&& let Some(def_id) = info.query.ty_adt_id
100+
&& let Some(def_id) = info.query.ty_def_id
101101
&& let Some(def_id) = def_id.as_local()
102102
&& !item_and_field_ids.iter().any(|&(id, _)| id == def_id)
103103
{
@@ -131,10 +131,36 @@ impl<'tcx> Value<TyCtxt<'tcx>> for ty::EarlyBinder<ty::Binder<'_, ty::FnSig<'_>>
131131

132132
impl<'tcx, T> Value<TyCtxt<'tcx>> for Result<T, &'_ ty::layout::LayoutError<'_>> {
133133
fn from_cycle_error(
134-
_tcx: TyCtxt<'tcx>,
135-
_cycle_error: &CycleError,
136-
guar: ErrorGuaranteed,
134+
tcx: TyCtxt<'tcx>,
135+
cycle_error: &CycleError,
136+
_guar: ErrorGuaranteed,
137137
) -> 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+
&& matches!(tcx.def_kind(def_id), DefKind::Closure)
142+
&& let Some(coroutine_kind) = tcx.coroutine_kind(def_id)
143+
{
144+
// FIXME: `def_span` for an fn-like coroutine will point to the fn's body
145+
// due to interactions between the desugaring into a closure expr and the
146+
// def_span code. I'm not motivated to fix it, because I tried and it was
147+
// not working, so just hack around it by grabbing the parent fn's span.
148+
let span = if coroutine_kind.is_fn_like() {
149+
tcx.def_span(tcx.local_parent(def_id))
150+
} else {
151+
tcx.def_span(def_id)
152+
};
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(
157+
"consider using the `async_recursion` crate: https://crates.io/crates/async_recursion",
158+
)
159+
.emit()
160+
} else {
161+
report_cycle(tcx.sess, cycle_error).emit()
162+
};
163+
138164
// tcx.arena.alloc cannot be used because we are not allowed to use &'tcx LayoutError under
139165
// min_specialization. Since this is an error path anyways, leaking doesn't matter (and really,
140166
// tcx.arena.alloc is pretty much equal to leaking).

compiler/rustc_query_impl/src/plumbing.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -342,9 +342,9 @@ pub(crate) fn create_query_frame<
342342
hasher.finish::<Hash64>()
343343
})
344344
};
345-
let ty_adt_id = key.ty_adt_id();
345+
let ty_def_id = key.ty_def_id();
346346

347-
QueryStackFrame::new(description, span, def_id, def_kind, kind, ty_adt_id, hash)
347+
QueryStackFrame::new(description, span, def_id, def_kind, kind, ty_def_id, hash)
348348
}
349349

350350
pub(crate) fn encode_query_results<'a, 'tcx, Q>(

compiler/rustc_query_system/src/query/mod.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ pub struct QueryStackFrame {
3535
span: Option<Span>,
3636
pub def_id: Option<DefId>,
3737
pub def_kind: Option<DefKind>,
38-
pub ty_adt_id: Option<DefId>,
38+
/// A def-id that is extracted from a `Ty` in a query key
39+
pub ty_def_id: Option<DefId>,
3940
pub dep_kind: DepKind,
4041
/// This hash is used to deterministically pick
4142
/// a query to remove cycles in the parallel compiler.
@@ -51,15 +52,15 @@ impl QueryStackFrame {
5152
def_id: Option<DefId>,
5253
def_kind: Option<DefKind>,
5354
dep_kind: DepKind,
54-
ty_adt_id: Option<DefId>,
55+
ty_def_id: Option<DefId>,
5556
_hash: impl FnOnce() -> Hash64,
5657
) -> Self {
5758
Self {
5859
description,
5960
span,
6061
def_id,
6162
def_kind,
62-
ty_adt_id,
63+
ty_def_id,
6364
dep_kind,
6465
#[cfg(parallel_compiler)]
6566
hash: _hash(),

compiler/rustc_trait_selection/src/traits/query/normalize.rs

+5-8
Original file line numberDiff line numberDiff line change
@@ -239,16 +239,13 @@ impl<'cx, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'cx, 'tcx>
239239
}
240240

241241
let generic_ty = self.interner().type_of(data.def_id);
242-
let concrete_ty = generic_ty.instantiate(self.interner(), args);
242+
let mut concrete_ty = generic_ty.instantiate(self.interner(), args);
243243
self.anon_depth += 1;
244244
if concrete_ty == ty {
245-
bug!(
246-
"infinite recursion generic_ty: {:#?}, args: {:#?}, \
247-
concrete_ty: {:#?}, ty: {:#?}",
248-
generic_ty,
249-
args,
250-
concrete_ty,
251-
ty
245+
concrete_ty = Ty::new_error_with_message(
246+
self.interner(),
247+
DUMMY_SP,
248+
"recursive opaque type",
252249
);
253250
}
254251
let folded_ty = ensure_sufficient_stack(|| self.try_fold_ty(concrete_ty));

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ async fn rec_1() { //~ ERROR recursion in an `async fn`
66
rec_2().await;
77
}
88

9-
async fn rec_2() { //~ ERROR recursion in an `async fn`
9+
async fn rec_2() {
1010
rec_1().await;
1111
}
1212

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

+1-10
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,6 @@ LL | async fn rec_1() {
77
= note: a recursive `async fn` must be rewritten to return a boxed `dyn Future`
88
= note: consider using the `async_recursion` crate: https://crates.io/crates/async_recursion
99

10-
error[E0733]: recursion in an `async fn` requires boxing
11-
--> $DIR/mutually-recursive-async-impl-trait-type.rs:9:1
12-
|
13-
LL | async fn rec_2() {
14-
| ^^^^^^^^^^^^^^^^ recursive `async fn`
15-
|
16-
= note: a recursive `async fn` must be rewritten to return a boxed `dyn Future`
17-
= note: consider using the `async_recursion` crate: https://crates.io/crates/async_recursion
18-
19-
error: aborting due to 2 previous errors
10+
error: aborting due to 1 previous error
2011

2112
For more information about this error, try `rustc --explain E0733`.

tests/ui/impl-trait/recursive-coroutine.current.stderr

-12
This file was deleted.

tests/ui/impl-trait/recursive-coroutine.next.stderr

-12
This file was deleted.

tests/ui/impl-trait/recursive-coroutine.rs

+1-4
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,13 @@
1+
// check-pass
12
// revisions: current next
23
//[next] compile-flags: -Znext-solver
34
#![feature(coroutines, coroutine_trait)]
45

56
use std::ops::{Coroutine, CoroutineState};
67

78
fn foo() -> impl Coroutine<Yield = (), Return = ()> {
8-
//~^ ERROR cannot resolve opaque type
9-
//~| NOTE recursive opaque type
10-
//~| NOTE in this expansion of desugaring of
119
|| {
1210
let mut gen = Box::pin(foo());
13-
//~^ NOTE coroutine captures itself here
1411
let mut r = gen.as_mut().resume(());
1512
while let CoroutineState::Yielded(v) = r {
1613
yield v;

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,8 @@ fn substs_change<T: 'static>() -> impl Sized {
7070
}
7171

7272
fn coroutine_hold() -> impl Sized {
73-
//~^ ERROR
7473
move || {
74+
//~^ ERROR
7575
let x = coroutine_hold();
7676
yield;
7777
x;

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

+9-8
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[E0720]: cannot resolve opaque type
113-
--> $DIR/recursive-impl-trait-type-indirect.rs:72:24
112+
error[E0733]: recursion in an `async fn` requires boxing
113+
--> $DIR/recursive-impl-trait-type-indirect.rs:73:5
114114
|
115-
LL | fn coroutine_hold() -> impl Sized {
116-
| ^^^^^^^^^^ recursive opaque type
117-
...
118-
LL | let x = coroutine_hold();
119-
| - coroutine captures itself here
115+
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
120120

121121
error[E0720]: cannot resolve opaque type
122122
--> $DIR/recursive-impl-trait-type-indirect.rs:86:26
@@ -144,4 +144,5 @@ LL | mutual_recursion()
144144

145145
error: aborting due to 14 previous errors
146146

147-
For more information about this error, try `rustc --explain E0720`.
147+
Some errors have detailed explanations: E0720, E0733.
148+
For more information about an error, try `rustc --explain E0720`.

0 commit comments

Comments
 (0)