From 5b5393da19bf1125a22d03fa26b8204f6d4ab31a Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 8 Jan 2024 14:31:25 +0000 Subject: [PATCH] Last nits --- .../src/error_codes/E0733.md | 38 +++++------------- compiler/rustc_middle/src/values.rs | 40 ++++++++++++++----- 2 files changed, 41 insertions(+), 37 deletions(-) diff --git a/compiler/rustc_error_codes/src/error_codes/E0733.md b/compiler/rustc_error_codes/src/error_codes/E0733.md index cceb0880350ec..42c01975dd8d3 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0733.md +++ b/compiler/rustc_error_codes/src/error_codes/E0733.md @@ -10,48 +10,30 @@ async fn foo(n: usize) { } ``` -To perform async recursion, the `async fn` needs to be desugared such that the -`Future` is explicit in the return type: - -```edition2018,compile_fail,E0733 -use std::future::Future; -fn foo_desugared(n: usize) -> impl Future { - async move { - if n > 0 { - foo_desugared(n - 1).await; - } - } -} -``` - -Finally, the future is wrapped in a pinned box: +The recursive invocation can be boxed: ```edition2018 -use std::future::Future; -use std::pin::Pin; -fn foo_recursive(n: usize) -> Pin>> { - Box::pin(async move { - if n > 0 { - foo_recursive(n - 1).await; - } - }) +async fn foo(n: usize) { + if n > 0 { + Box::pin(foo(n - 1)).await; + } } ``` The `Box<...>` ensures that the result is of known size, and the pin is 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 use std::future::Future; use std::pin::Pin; -fn foo_recursive(n: usize) -> impl Future { - async move { +fn foo(n: usize) -> Pin>> { + Box::pin(async move { if n > 0 { - Box::pin(foo_recursive(n - 1)).await; + foo(n - 1).await; } - } + }) } ``` diff --git a/compiler/rustc_middle/src/values.rs b/compiler/rustc_middle/src/values.rs index 819fecd5050d4..915e71dac6cfd 100644 --- a/compiler/rustc_middle/src/values.rs +++ b/compiler/rustc_middle/src/values.rs @@ -13,6 +13,7 @@ use rustc_span::{ErrorGuaranteed, Span}; use std::collections::VecDeque; use std::fmt::Write; +use std::ops::ControlFlow; impl<'tcx> Value> for Ty<'_> { fn from_cycle_error(tcx: TyCtxt<'tcx>, _: &CycleError, guar: ErrorGuaranteed) -> Self { @@ -130,16 +131,34 @@ impl<'tcx> Value> for ty::EarlyBinder> } } +// Take a cycle of `Q` and try `try_cycle` on every permutation, falling back to `otherwise`. +fn search_for_cycle_permutation( + cycle: &[Q], + try_cycle: impl Fn(&mut VecDeque<&Q>) -> ControlFlow, + 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> for Result> { fn from_cycle_error( tcx: TyCtxt<'tcx>, cycle_error: &CycleError, _guar: ErrorGuaranteed, ) -> Self { - let mut cycle: VecDeque<_> = cycle_error.cycle.iter().collect(); - - let guar = 'search: { - for _ in 0..cycle.len() { + let mut diag = search_for_cycle_permutation( + &cycle_error.cycle, + |cycle| { if cycle[0].query.dep_kind == dep_kinds::layout_of && let Some(def_id) = cycle[0].query.ty_def_id && let Some(def_id) = def_id.as_local() @@ -204,13 +223,16 @@ impl<'tcx, T> Value> for Result> ) { 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 { - 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 // min_specialization. Since this is an error path anyways, leaking doesn't matter (and really,