Last nits

This commit is contained in:
Michael Goulet 2024-01-08 14:31:25 +00:00
parent 841184bcae
commit 9a756034a9
2 changed files with 41 additions and 37 deletions

View file

@ -10,48 +10,30 @@ async fn foo(n: usize) {
} }
``` ```
To perform async recursion, the `async fn` needs to be desugared such that the The recursive invocation can be boxed:
`Future` is explicit in the return type:
```edition2018,compile_fail,E0733
use std::future::Future;
fn foo_desugared(n: usize) -> impl Future<Output = ()> {
async move {
if n > 0 {
foo_desugared(n - 1).await;
}
}
}
```
Finally, the future is wrapped in a pinned box:
```edition2018 ```edition2018
use std::future::Future; async fn foo(n: usize) {
use std::pin::Pin; if n > 0 {
fn foo_recursive(n: usize) -> Pin<Box<dyn Future<Output = ()>>> { Box::pin(foo(n - 1)).await;
Box::pin(async move { }
if n > 0 {
foo_recursive(n - 1).await;
}
})
} }
``` ```
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: Alternatively, the body can be boxed:
```edition2018 ```edition2018
use std::future::Future; use std::future::Future;
use std::pin::Pin; use std::pin::Pin;
fn foo_recursive(n: usize) -> impl Future<Output = ()> { fn foo(n: usize) -> Pin<Box<dyn Future<Output = ()>>> {
async move { Box::pin(async move {
if n > 0 { if n > 0 {
Box::pin(foo_recursive(n - 1)).await; foo(n - 1).await;
} }
} })
} }
``` ```

View file

@ -13,6 +13,7 @@ use rustc_span::{ErrorGuaranteed, Span};
use std::collections::VecDeque; use std::collections::VecDeque;
use std::fmt::Write; use std::fmt::Write;
use std::ops::ControlFlow;
impl<'tcx> Value<TyCtxt<'tcx>> for Ty<'_> { impl<'tcx> Value<TyCtxt<'tcx>> for Ty<'_> {
fn from_cycle_error(tcx: TyCtxt<'tcx>, _: &CycleError, guar: ErrorGuaranteed) -> Self { fn from_cycle_error(tcx: TyCtxt<'tcx>, _: &CycleError, guar: ErrorGuaranteed) -> Self {
@ -130,16 +131,34 @@ impl<'tcx> Value<TyCtxt<'tcx>> for ty::EarlyBinder<ty::Binder<'_, ty::FnSig<'_>>
} }
} }
// Take a cycle of `Q` and try `try_cycle` on every permutation, falling back to `otherwise`.
fn search_for_cycle_permutation<Q, T>(
cycle: &[Q],
try_cycle: impl Fn(&mut VecDeque<&Q>) -> ControlFlow<T, ()>,
otherwise: impl FnOnce() -> T,
) -> T {
let mut cycle: VecDeque<_> = cycle.iter().collect();
for _ in 0..cycle.len() {
match try_cycle(&mut cycle) {
ControlFlow::Continue(_) => {
cycle.rotate_left(1);
}
ControlFlow::Break(t) => return t,
}
}
otherwise()
}
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 mut cycle: VecDeque<_> = cycle_error.cycle.iter().collect(); let diag = search_for_cycle_permutation(
&cycle_error.cycle,
let guar = 'search: { |cycle| {
for _ in 0..cycle.len() {
if cycle[0].query.dep_kind == dep_kinds::layout_of if cycle[0].query.dep_kind == dep_kinds::layout_of
&& let Some(def_id) = cycle[0].query.ty_def_id && let Some(def_id) = cycle[0].query.ty_def_id
&& let Some(def_id) = def_id.as_local() && let Some(def_id) = def_id.as_local()
@ -204,13 +223,16 @@ impl<'tcx, T> Value<TyCtxt<'tcx>> for Result<T, &'_ ty::layout::LayoutError<'_>>
) { ) {
diag.note("a recursive `async fn` call must introduce indirection such as `Box::pin` to avoid an infinitely sized future"); diag.note("a recursive `async fn` call must introduce indirection such as `Box::pin` to avoid an infinitely sized future");
} }
break 'search diag.emit();
ControlFlow::Break(diag)
} else { } else {
cycle.rotate_left(1); ControlFlow::Continue(())
} }
} },
report_cycle(tcx.sess, cycle_error).emit() || report_cycle(tcx.sess, cycle_error),
}; );
let guar = diag.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,