1
Fork 0

Don't check for recursion in generator witness fields

This commit is contained in:
Michael Goulet 2023-11-08 06:56:06 +00:00
parent dfb9f5df2c
commit 82a2215481
19 changed files with 102 additions and 124 deletions

View file

@ -13,7 +13,7 @@ async fn foo(n: usize) {
To perform async recursion, the `async fn` needs to be desugared such that the To perform async recursion, the `async fn` needs to be desugared such that the
`Future` is explicit in the return type: `Future` is explicit in the return type:
```edition2018,compile_fail,E0720 ```edition2018,compile_fail,E0733
use std::future::Future; use std::future::Future;
fn foo_desugared(n: usize) -> impl Future<Output = ()> { fn foo_desugared(n: usize) -> impl Future<Output = ()> {
async move { async move {
@ -41,4 +41,18 @@ fn foo_recursive(n: usize) -> Pin<Box<dyn Future<Output = ()>>> {
The `Box<...>` ensures that the result is of known size, and the pin is The `Box<...>` ensures that the result is of known size, and the pin is
required to keep it in the same place in memory. required to keep it in the same place in memory.
Alternatively, the recursive call-site can be boxed:
```edition2018
use std::future::Future;
use std::pin::Pin;
fn foo_recursive(n: usize) -> impl Future<Output = ()> {
async move {
if n > 0 {
Box::pin(foo_recursive(n - 1)).await;
}
}
}
```
[`async`]: https://doc.rust-lang.org/std/keyword.async.html [`async`]: https://doc.rust-lang.org/std/keyword.async.html

View file

@ -1361,6 +1361,12 @@ impl CoroutineKind {
} }
} }
impl CoroutineKind {
pub fn is_fn_like(self) -> bool {
matches!(self, CoroutineKind::Desugared(_, CoroutineSource::Fn))
}
}
impl fmt::Display for CoroutineKind { impl fmt::Display for CoroutineKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {

View file

@ -213,13 +213,12 @@ fn check_opaque(tcx: TyCtxt<'_>, def_id: LocalDefId) {
return; return;
} }
let args = GenericArgs::identity_for_item(tcx, item.owner_id);
let span = tcx.def_span(item.owner_id.def_id); let span = tcx.def_span(item.owner_id.def_id);
if tcx.type_of(item.owner_id.def_id).instantiate_identity().references_error() { if tcx.type_of(item.owner_id.def_id).instantiate_identity().references_error() {
return; return;
} }
if check_opaque_for_cycles(tcx, item.owner_id.def_id, args, span, origin).is_err() { if check_opaque_for_cycles(tcx, item.owner_id.def_id, span).is_err() {
return; return;
} }
@ -230,16 +229,16 @@ fn check_opaque(tcx: TyCtxt<'_>, def_id: LocalDefId) {
pub(super) fn check_opaque_for_cycles<'tcx>( pub(super) fn check_opaque_for_cycles<'tcx>(
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
def_id: LocalDefId, def_id: LocalDefId,
args: GenericArgsRef<'tcx>,
span: Span, span: Span,
origin: &hir::OpaqueTyOrigin,
) -> Result<(), ErrorGuaranteed> { ) -> Result<(), ErrorGuaranteed> {
let args = GenericArgs::identity_for_item(tcx, def_id);
if tcx.try_expand_impl_trait_type(def_id.to_def_id(), args).is_err() { if tcx.try_expand_impl_trait_type(def_id.to_def_id(), args).is_err() {
let reported = match origin { let reported = opaque_type_cycle_error(tcx, def_id, span);
hir::OpaqueTyOrigin::AsyncFn(..) => async_opaque_type_cycle_error(tcx, span),
_ => opaque_type_cycle_error(tcx, def_id, span),
};
Err(reported) Err(reported)
} else if let Err(&LayoutError::Cycle(guar)) =
tcx.layout_of(tcx.param_env(def_id).and(Ty::new_opaque(tcx, def_id.to_def_id(), args)))
{
Err(guar)
} else { } else {
Ok(()) Ok(())
} }
@ -1300,16 +1299,6 @@ pub(super) fn check_type_params_are_used<'tcx>(
} }
} }
fn async_opaque_type_cycle_error(tcx: TyCtxt<'_>, span: Span) -> ErrorGuaranteed {
struct_span_err!(tcx.dcx(), span, E0733, "recursion in an `async fn` requires boxing")
.span_label_mv(span, "recursive `async fn`")
.note_mv("a recursive `async fn` must be rewritten to return a boxed `dyn Future`")
.note_mv(
"consider using the `async_recursion` crate: https://crates.io/crates/async_recursion",
)
.emit()
}
/// Emit an error for recursive opaque types. /// Emit an error for recursive opaque types.
/// ///
/// If this is a return `impl Trait`, find the item's return expressions and point at them. For /// If this is a return `impl Trait`, find the item's return expressions and point at them. For

View file

@ -40,7 +40,7 @@ pub trait Key: Sized {
None None
} }
fn ty_adt_id(&self) -> Option<DefId> { fn ty_def_id(&self) -> Option<DefId> {
None None
} }
} }
@ -406,9 +406,10 @@ impl<'tcx> Key for Ty<'tcx> {
DUMMY_SP DUMMY_SP
} }
fn ty_adt_id(&self) -> Option<DefId> { fn ty_def_id(&self) -> Option<DefId> {
match self.kind() { match *self.kind() {
ty::Adt(adt, _) => Some(adt.did()), ty::Adt(adt, _) => Some(adt.did()),
ty::Coroutine(def_id, ..) => Some(def_id),
_ => None, _ => None,
} }
} }
@ -452,6 +453,10 @@ impl<'tcx, T: Key> Key for ty::ParamEnvAnd<'tcx, T> {
fn default_span(&self, tcx: TyCtxt<'_>) -> Span { fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
self.value.default_span(tcx) self.value.default_span(tcx)
} }
fn ty_def_id(&self) -> Option<DefId> {
self.value.ty_def_id()
}
} }
impl Key for Symbol { impl Key for Symbol {
@ -550,7 +555,7 @@ impl<'tcx> Key for (ValidityRequirement, ty::ParamEnvAnd<'tcx, Ty<'tcx>>) {
DUMMY_SP DUMMY_SP
} }
fn ty_adt_id(&self) -> Option<DefId> { fn ty_def_id(&self) -> Option<DefId> {
match self.1.value.kind() { match self.1.value.kind() {
ty::Adt(adt, _) => Some(adt.did()), ty::Adt(adt, _) => Some(adt.did()),
_ => None, _ => None,

View file

@ -1387,6 +1387,8 @@ rustc_queries! {
) -> Result<ty::layout::TyAndLayout<'tcx>, &'tcx ty::layout::LayoutError<'tcx>> { ) -> Result<ty::layout::TyAndLayout<'tcx>, &'tcx ty::layout::LayoutError<'tcx>> {
depth_limit depth_limit
desc { "computing layout of `{}`", key.value } desc { "computing layout of `{}`", key.value }
// we emit our own error during query cycle handling
cycle_delay_bug
} }
/// Compute a `FnAbi` suitable for indirect calls, i.e. to `fn` pointers. /// Compute a `FnAbi` suitable for indirect calls, i.e. to `fn` pointers.

View file

@ -896,18 +896,7 @@ impl<'tcx> OpaqueTypeExpander<'tcx> {
} }
let args = args.fold_with(self); let args = args.fold_with(self);
if !self.check_recursion || self.seen_opaque_tys.insert(def_id) { if !self.check_recursion || self.seen_opaque_tys.insert(def_id) {
let expanded_ty = match self.expanded_cache.get(&(def_id, args)) {
Some(expanded_ty) => *expanded_ty,
None => {
for bty in self.tcx.coroutine_hidden_types(def_id) {
let hidden_ty = bty.instantiate(self.tcx, args);
self.fold_ty(hidden_ty);
}
let expanded_ty = Ty::new_coroutine_witness(self.tcx, def_id, args); let expanded_ty = Ty::new_coroutine_witness(self.tcx, def_id, args);
self.expanded_cache.insert((def_id, args), expanded_ty);
expanded_ty
}
};
if self.check_recursion { if self.check_recursion {
self.seen_opaque_tys.remove(&def_id); self.seen_opaque_tys.remove(&def_id);
} }

View file

@ -6,7 +6,7 @@ use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res}; use rustc_hir::def::{DefKind, Res};
use rustc_middle::ty::Representability; use rustc_middle::ty::Representability;
use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_query_system::query::CycleError; use rustc_query_system::query::{report_cycle, CycleError};
use rustc_query_system::Value; 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};
@ -97,7 +97,7 @@ impl<'tcx> Value<TyCtxt<'tcx>> for Representability {
} }
for info in &cycle_error.cycle { for info in &cycle_error.cycle {
if info.query.dep_kind == dep_kinds::representability_adt_ty if info.query.dep_kind == dep_kinds::representability_adt_ty
&& let Some(def_id) = info.query.ty_adt_id && let Some(def_id) = info.query.ty_def_id
&& let Some(def_id) = def_id.as_local() && let Some(def_id) = def_id.as_local()
&& !item_and_field_ids.iter().any(|&(id, _)| id == def_id) && !item_and_field_ids.iter().any(|&(id, _)| id == def_id)
{ {
@ -131,10 +131,36 @@ impl<'tcx> Value<TyCtxt<'tcx>> for ty::EarlyBinder<ty::Binder<'_, ty::FnSig<'_>>
impl<'tcx, T> Value<TyCtxt<'tcx>> for Result<T, &'_ ty::layout::LayoutError<'_>> { impl<'tcx, T> Value<TyCtxt<'tcx>> for Result<T, &'_ ty::layout::LayoutError<'_>> {
fn from_cycle_error( fn from_cycle_error(
_tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
_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 Some(def_id) = cycle_error.cycle[0].query.ty_def_id
&& let Some(def_id) = def_id.as_local()
&& matches!(tcx.def_kind(def_id), 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)
};
struct_span_err!(tcx.sess.dcx(), span, E0733, "recursion in an `async fn` requires boxing")
.span_label(span, "recursive `async fn`")
.note("a recursive `async fn` must be rewritten to return a boxed `dyn Future`")
.note(
"consider using the `async_recursion` crate: https://crates.io/crates/async_recursion",
)
.emit()
} else {
report_cycle(tcx.sess, cycle_error).emit()
};
// tcx.arena.alloc cannot be used because we are not allowed to use &'tcx LayoutError under // tcx.arena.alloc cannot be used because we are not allowed to use &'tcx LayoutError under
// min_specialization. Since this is an error path anyways, leaking doesn't matter (and really, // min_specialization. Since this is an error path anyways, leaking doesn't matter (and really,
// tcx.arena.alloc is pretty much equal to leaking). // tcx.arena.alloc is pretty much equal to leaking).

View file

@ -342,9 +342,9 @@ pub(crate) fn create_query_frame<
hasher.finish::<Hash64>() hasher.finish::<Hash64>()
}) })
}; };
let ty_adt_id = key.ty_adt_id(); let ty_def_id = key.ty_def_id();
QueryStackFrame::new(description, span, def_id, def_kind, kind, ty_adt_id, hash) QueryStackFrame::new(description, span, def_id, def_kind, kind, ty_def_id, hash)
} }
pub(crate) fn encode_query_results<'a, 'tcx, Q>( pub(crate) fn encode_query_results<'a, 'tcx, Q>(

View file

@ -35,7 +35,8 @@ pub struct QueryStackFrame {
span: Option<Span>, span: Option<Span>,
pub def_id: Option<DefId>, pub def_id: Option<DefId>,
pub def_kind: Option<DefKind>, pub def_kind: Option<DefKind>,
pub ty_adt_id: Option<DefId>, /// A def-id that is extracted from a `Ty` in a query key
pub ty_def_id: Option<DefId>,
pub dep_kind: DepKind, pub dep_kind: DepKind,
/// This hash is used to deterministically pick /// This hash is used to deterministically pick
/// a query to remove cycles in the parallel compiler. /// a query to remove cycles in the parallel compiler.
@ -51,7 +52,7 @@ impl QueryStackFrame {
def_id: Option<DefId>, def_id: Option<DefId>,
def_kind: Option<DefKind>, def_kind: Option<DefKind>,
dep_kind: DepKind, dep_kind: DepKind,
ty_adt_id: Option<DefId>, ty_def_id: Option<DefId>,
_hash: impl FnOnce() -> Hash64, _hash: impl FnOnce() -> Hash64,
) -> Self { ) -> Self {
Self { Self {
@ -59,7 +60,7 @@ impl QueryStackFrame {
span, span,
def_id, def_id,
def_kind, def_kind,
ty_adt_id, ty_def_id,
dep_kind, dep_kind,
#[cfg(parallel_compiler)] #[cfg(parallel_compiler)]
hash: _hash(), hash: _hash(),

View file

@ -239,16 +239,13 @@ impl<'cx, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'cx, 'tcx>
} }
let generic_ty = self.interner().type_of(data.def_id); let generic_ty = self.interner().type_of(data.def_id);
let concrete_ty = generic_ty.instantiate(self.interner(), args); let mut concrete_ty = generic_ty.instantiate(self.interner(), args);
self.anon_depth += 1; self.anon_depth += 1;
if concrete_ty == ty { if concrete_ty == ty {
bug!( concrete_ty = Ty::new_error_with_message(
"infinite recursion generic_ty: {:#?}, args: {:#?}, \ self.interner(),
concrete_ty: {:#?}, ty: {:#?}", DUMMY_SP,
generic_ty, "recursive opaque type",
args,
concrete_ty,
ty
); );
} }
let folded_ty = ensure_sufficient_stack(|| self.try_fold_ty(concrete_ty)); let folded_ty = ensure_sufficient_stack(|| self.try_fold_ty(concrete_ty));

View file

@ -6,7 +6,7 @@ async fn rec_1() { //~ ERROR recursion in an `async fn`
rec_2().await; rec_2().await;
} }
async fn rec_2() { //~ ERROR recursion in an `async fn` async fn rec_2() {
rec_1().await; rec_1().await;
} }

View file

@ -7,15 +7,6 @@ LL | async fn rec_1() {
= note: a recursive `async fn` must be rewritten to return a boxed `dyn Future` = note: a recursive `async fn` must be rewritten to return a boxed `dyn Future`
= note: consider using the `async_recursion` crate: https://crates.io/crates/async_recursion = note: consider using the `async_recursion` crate: https://crates.io/crates/async_recursion
error[E0733]: recursion in an `async fn` requires boxing error: aborting due to 1 previous error
--> $DIR/mutually-recursive-async-impl-trait-type.rs:9:1
|
LL | async fn rec_2() {
| ^^^^^^^^^^^^^^^^ recursive `async fn`
|
= note: a recursive `async fn` must be rewritten to return a boxed `dyn Future`
= note: consider using the `async_recursion` crate: https://crates.io/crates/async_recursion
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0733`. For more information about this error, try `rustc --explain E0733`.

View file

@ -1,12 +0,0 @@
error[E0720]: cannot resolve opaque type
--> $DIR/recursive-coroutine.rs:7:13
|
LL | fn foo() -> impl Coroutine<Yield = (), Return = ()> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ recursive opaque type
...
LL | let mut gen = Box::pin(foo());
| ------- coroutine captures itself here
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0720`.

View file

@ -1,12 +0,0 @@
error[E0720]: cannot resolve opaque type
--> $DIR/recursive-coroutine.rs:7:13
|
LL | fn foo() -> impl Coroutine<Yield = (), Return = ()> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ recursive opaque type
...
LL | let mut gen = Box::pin(foo());
| ------- coroutine captures itself here
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0720`.

View file

@ -1,3 +1,4 @@
// check-pass
// revisions: current next // revisions: current next
//[next] compile-flags: -Znext-solver //[next] compile-flags: -Znext-solver
#![feature(coroutines, coroutine_trait)] #![feature(coroutines, coroutine_trait)]
@ -5,12 +6,8 @@
use std::ops::{Coroutine, CoroutineState}; use std::ops::{Coroutine, CoroutineState};
fn foo() -> impl Coroutine<Yield = (), Return = ()> { fn foo() -> impl Coroutine<Yield = (), Return = ()> {
//~^ ERROR cannot resolve opaque type
//~| NOTE recursive opaque type
//~| NOTE in this expansion of desugaring of
|| { || {
let mut gen = Box::pin(foo()); let mut gen = Box::pin(foo());
//~^ NOTE coroutine captures itself here
let mut r = gen.as_mut().resume(()); let mut r = gen.as_mut().resume(());
while let CoroutineState::Yielded(v) = r { while let CoroutineState::Yielded(v) = r {
yield v; yield v;

View file

@ -70,8 +70,8 @@ fn substs_change<T: 'static>() -> impl Sized {
} }
fn coroutine_hold() -> impl Sized { fn coroutine_hold() -> impl Sized {
//~^ ERROR
move || { move || {
//~^ ERROR
let x = coroutine_hold(); let x = coroutine_hold();
yield; yield;
x; x;

View file

@ -109,14 +109,14 @@ LL |
LL | (substs_change::<&T>(),) LL | (substs_change::<&T>(),)
| ------------------------ returning here with type `(impl Sized,)` | ------------------------ returning here with type `(impl Sized,)`
error[E0720]: cannot resolve opaque type error[E0733]: recursion in an `async fn` requires boxing
--> $DIR/recursive-impl-trait-type-indirect.rs:72:24 --> $DIR/recursive-impl-trait-type-indirect.rs:73:5
| |
LL | fn coroutine_hold() -> impl Sized { LL | move || {
| ^^^^^^^^^^ recursive opaque type | ^^^^^^^ recursive `async fn`
... |
LL | let x = coroutine_hold(); = note: a recursive `async fn` must be rewritten to return a boxed `dyn Future`
| - coroutine captures itself here = note: consider using the `async_recursion` crate: https://crates.io/crates/async_recursion
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:86:26
@ -144,4 +144,5 @@ LL | mutual_recursion()
error: aborting due to 14 previous errors error: aborting due to 14 previous errors
For more information about this error, try `rustc --explain E0720`. Some errors have detailed explanations: E0720, E0733.
For more information about an error, try `rustc --explain E0720`.

View file

@ -1,5 +1,4 @@
// edition: 2021 // edition: 2021
// build-fail
#![feature(impl_trait_in_assoc_type)] #![feature(impl_trait_in_assoc_type)]
@ -20,7 +19,7 @@ impl Recur for () {
fn recur(self) -> Self::Recur { fn recur(self) -> Self::Recur {
async move { recur(self).await; } async move { recur(self).await; }
//~^ ERROR cycle detected when computing layout of //~^ ERROR recursion in an `async fn` requires boxing
} }
} }

View file

@ -1,27 +1,12 @@
error[E0391]: cycle detected when computing layout of `{async block@$DIR/indirect-recursion-issue-112047.rs:22:9: 22:42}` error[E0733]: recursion in an `async fn` requires boxing
--> $DIR/indirect-recursion-issue-112047.rs:22:22 --> $DIR/indirect-recursion-issue-112047.rs:21:9
| |
LL | async move { recur(self).await; } LL | async move { recur(self).await; }
| ^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ recursive `async fn`
| |
= note: ...which requires computing layout of `core::mem::maybe_uninit::MaybeUninit<{async fn body@$DIR/indirect-recursion-issue-112047.rs:14:31: 16:2}>`... = note: a recursive `async fn` must be rewritten to return a boxed `dyn Future`
= note: ...which requires computing layout of `core::mem::manually_drop::ManuallyDrop<{async fn body@$DIR/indirect-recursion-issue-112047.rs:14:31: 16:2}>`... = note: consider using the `async_recursion` crate: https://crates.io/crates/async_recursion
note: ...which requires computing layout of `{async fn body@$DIR/indirect-recursion-issue-112047.rs:14:31: 16:2}`...
--> $DIR/indirect-recursion-issue-112047.rs:15:5
|
LL | t.recur().await;
| ^^^^^^^^^^^^^^^
= note: ...which requires computing layout of `core::mem::maybe_uninit::MaybeUninit<<() as Recur>::Recur>`...
= note: ...which requires computing layout of `core::mem::maybe_uninit::MaybeUninit<{async block@$DIR/indirect-recursion-issue-112047.rs:22:9: 22:42}>`...
= note: ...which requires computing layout of `core::mem::manually_drop::ManuallyDrop<{async block@$DIR/indirect-recursion-issue-112047.rs:22:9: 22:42}>`...
= note: ...which again requires computing layout of `{async block@$DIR/indirect-recursion-issue-112047.rs:22:9: 22:42}`, completing the cycle
note: cycle used when elaborating drops for `<impl at $DIR/indirect-recursion-issue-112047.rs:18:1: 18:18>::recur`
--> $DIR/indirect-recursion-issue-112047.rs:21:5
|
LL | fn recur(self) -> Self::Recur {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= 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`.