Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 009418c

Browse files
committed
Auto merge of rust-lang#118136 - lcnr:recursive-async, r=<try>
[perf] alternative approach to rust-lang#117703 cc rust-lang#117703 r? `@compiler-errors`
2 parents e24e5af + fab367a commit 009418c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+344
-280
lines changed

compiler/rustc_error_codes/src/error_codes/E0733.md

Lines changed: 15 additions & 1 deletion
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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1494,6 +1494,15 @@ pub enum CoroutineKind {
14941494
Coroutine,
14951495
}
14961496

1497+
impl CoroutineKind {
1498+
pub fn is_fn_like(self) -> bool {
1499+
matches!(
1500+
self,
1501+
CoroutineKind::Async(CoroutineSource::Fn) | CoroutineKind::Gen(CoroutineSource::Fn)
1502+
)
1503+
}
1504+
}
1505+
14971506
impl fmt::Display for CoroutineKind {
14981507
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
14991508
match self {

compiler/rustc_hir_analysis/src/check/check.rs

Lines changed: 14 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -216,13 +216,12 @@ fn check_opaque(tcx: TyCtxt<'_>, id: hir::ItemId) {
216216
return;
217217
}
218218

219-
let args = GenericArgs::identity_for_item(tcx, item.owner_id);
220219
let span = tcx.def_span(item.owner_id.def_id);
221220

222221
if tcx.type_of(item.owner_id.def_id).instantiate_identity().references_error() {
223222
return;
224223
}
225-
if check_opaque_for_cycles(tcx, item.owner_id.def_id, args, span, &origin).is_err() {
224+
if check_opaque_for_cycles(tcx, item.owner_id.def_id, span).is_err() {
226225
return;
227226
}
228227

@@ -233,19 +232,16 @@ fn check_opaque(tcx: TyCtxt<'_>, id: hir::ItemId) {
233232
pub(super) fn check_opaque_for_cycles<'tcx>(
234233
tcx: TyCtxt<'tcx>,
235234
def_id: LocalDefId,
236-
args: GenericArgsRef<'tcx>,
237235
span: Span,
238-
origin: &hir::OpaqueTyOrigin,
239236
) -> Result<(), ErrorGuaranteed> {
237+
let args = GenericArgs::identity_for_item(tcx, def_id);
238+
240239
if tcx.try_expand_impl_trait_type(def_id.to_def_id(), args).is_err() {
241-
let reported = match origin {
242-
hir::OpaqueTyOrigin::AsyncFn(..) => async_opaque_type_cycle_error(tcx, span),
243-
_ => opaque_type_cycle_error(tcx, def_id, span),
244-
};
245-
Err(reported)
246-
} else {
247-
Ok(())
240+
let reported = opaque_type_cycle_error(tcx, def_id, span);
241+
return Err(reported);
248242
}
243+
244+
Ok(())
249245
}
250246

251247
/// Check that the concrete type behind `impl Trait` actually implements `Trait`.
@@ -1324,16 +1320,6 @@ pub(super) fn check_mod_item_types(tcx: TyCtxt<'_>, module_def_id: LocalModDefId
13241320
}
13251321
}
13261322

1327-
fn async_opaque_type_cycle_error(tcx: TyCtxt<'_>, span: Span) -> ErrorGuaranteed {
1328-
struct_span_err!(tcx.sess, span, E0733, "recursion in an `async fn` requires boxing")
1329-
.span_label(span, "recursive `async fn`")
1330-
.note("a recursive `async fn` must be rewritten to return a boxed `dyn Future`")
1331-
.note(
1332-
"consider using the `async_recursion` crate: https://crates.io/crates/async_recursion",
1333-
)
1334-
.emit()
1335-
}
1336-
13371323
/// Emit an error for recursive opaque types.
13381324
///
13391325
/// If this is a return `impl Trait`, find the item's return expressions and point at them. For
@@ -1514,5 +1500,12 @@ pub(super) fn check_coroutine_obligations(tcx: TyCtxt<'_>, def_id: LocalDefId) {
15141500
debug!(?errors);
15151501
if !errors.is_empty() {
15161502
infcx.err_ctxt().report_fulfillment_errors(errors);
1503+
return;
1504+
}
1505+
1506+
if let Err(&LayoutError::Cycle(_guar)) =
1507+
tcx.layout_of(param_env.and(tcx.type_of(def_id).instantiate_identity()))
1508+
{
1509+
debug!("layout error in coroutine")
15171510
}
15181511
}

compiler/rustc_middle/src/query/keys.rs

Lines changed: 9 additions & 4 deletions
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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1393,6 +1393,8 @@ rustc_queries! {
13931393
) -> Result<ty::layout::TyAndLayout<'tcx>, &'tcx ty::layout::LayoutError<'tcx>> {
13941394
depth_limit
13951395
desc { "computing layout of `{}`", key.value }
1396+
// we emit our own error during query cycle handling
1397+
cycle_delay_bug
13961398
}
13971399

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

compiler/rustc_middle/src/query/plumbing.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ pub struct DynamicQuery<'tcx, C: QueryCache> {
5353
fn(tcx: TyCtxt<'tcx>, key: &C::Key, index: SerializedDepNodeIndex) -> bool,
5454
pub hash_result: HashResult<C::Value>,
5555
pub value_from_cycle_error:
56-
fn(tcx: TyCtxt<'tcx>, cycle: &[QueryInfo], guar: ErrorGuaranteed) -> C::Value,
56+
fn(tcx: TyCtxt<'tcx>, cycle_error: &CycleError, guar: ErrorGuaranteed) -> C::Value,
5757
pub format_value: fn(&C::Value) -> String,
5858
}
5959

compiler/rustc_middle/src/ty/util.rs

Lines changed: 16 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -701,22 +701,6 @@ impl<'tcx> TyCtxt<'tcx> {
701701
.map(|decl| ty::EarlyBinder::bind(decl.ty))
702702
}
703703

704-
/// Normalizes all opaque types in the given value, replacing them
705-
/// with their underlying types.
706-
pub fn expand_opaque_types(self, val: Ty<'tcx>) -> Ty<'tcx> {
707-
let mut visitor = OpaqueTypeExpander {
708-
seen_opaque_tys: FxHashSet::default(),
709-
expanded_cache: FxHashMap::default(),
710-
primary_def_id: None,
711-
found_recursion: false,
712-
found_any_recursion: false,
713-
check_recursion: false,
714-
expand_coroutines: false,
715-
tcx: self,
716-
};
717-
val.fold_with(&mut visitor)
718-
}
719-
720704
/// Expands the given impl trait type, stopping if the type is recursive.
721705
#[instrument(skip(self), level = "debug", ret)]
722706
pub fn try_expand_impl_trait_type(
@@ -731,7 +715,6 @@ impl<'tcx> TyCtxt<'tcx> {
731715
found_recursion: false,
732716
found_any_recursion: false,
733717
check_recursion: true,
734-
expand_coroutines: true,
735718
tcx: self,
736719
};
737720

@@ -749,9 +732,13 @@ impl<'tcx> TyCtxt<'tcx> {
749732
match def_kind {
750733
DefKind::AssocFn if self.associated_item(def_id).fn_has_self_parameter => "method",
751734
DefKind::Coroutine => match self.coroutine_kind(def_id).unwrap() {
752-
rustc_hir::CoroutineKind::Async(..) => "async closure",
753-
rustc_hir::CoroutineKind::Coroutine => "coroutine",
754-
rustc_hir::CoroutineKind::Gen(..) => "gen closure",
735+
hir::CoroutineKind::Async(hir::CoroutineSource::Fn) => "async fn",
736+
hir::CoroutineKind::Async(hir::CoroutineSource::Block) => "async block",
737+
hir::CoroutineKind::Async(hir::CoroutineSource::Closure) => "async closure",
738+
hir::CoroutineKind::Gen(hir::CoroutineSource::Fn) => "gen fn",
739+
hir::CoroutineKind::Gen(hir::CoroutineSource::Block) => "gen block",
740+
hir::CoroutineKind::Gen(hir::CoroutineSource::Closure) => "gen closure",
741+
hir::CoroutineKind::Coroutine => "coroutine",
755742
},
756743
_ => def_kind.descr(def_id),
757744
}
@@ -767,9 +754,9 @@ impl<'tcx> TyCtxt<'tcx> {
767754
match def_kind {
768755
DefKind::AssocFn if self.associated_item(def_id).fn_has_self_parameter => "a",
769756
DefKind::Coroutine => match self.coroutine_kind(def_id).unwrap() {
770-
rustc_hir::CoroutineKind::Async(..) => "an",
771-
rustc_hir::CoroutineKind::Coroutine => "a",
772-
rustc_hir::CoroutineKind::Gen(..) => "a",
757+
hir::CoroutineKind::Async(..) => "an",
758+
hir::CoroutineKind::Coroutine => "a",
759+
hir::CoroutineKind::Gen(..) => "a",
773760
},
774761
_ => def_kind.article(),
775762
}
@@ -808,7 +795,6 @@ struct OpaqueTypeExpander<'tcx> {
808795
primary_def_id: Option<DefId>,
809796
found_recursion: bool,
810797
found_any_recursion: bool,
811-
expand_coroutines: bool,
812798
/// Whether or not to check for recursive opaque types.
813799
/// This is `true` when we're explicitly checking for opaque type
814800
/// recursion, and 'false' otherwise to avoid unnecessary work.
@@ -845,37 +831,6 @@ impl<'tcx> OpaqueTypeExpander<'tcx> {
845831
None
846832
}
847833
}
848-
849-
fn expand_coroutine(&mut self, def_id: DefId, args: GenericArgsRef<'tcx>) -> Option<Ty<'tcx>> {
850-
if self.found_any_recursion {
851-
return None;
852-
}
853-
let args = args.fold_with(self);
854-
if !self.check_recursion || self.seen_opaque_tys.insert(def_id) {
855-
let expanded_ty = match self.expanded_cache.get(&(def_id, args)) {
856-
Some(expanded_ty) => *expanded_ty,
857-
None => {
858-
for bty in self.tcx.coroutine_hidden_types(def_id) {
859-
let hidden_ty = bty.instantiate(self.tcx, args);
860-
self.fold_ty(hidden_ty);
861-
}
862-
let expanded_ty = Ty::new_coroutine_witness(self.tcx, def_id, args);
863-
self.expanded_cache.insert((def_id, args), expanded_ty);
864-
expanded_ty
865-
}
866-
};
867-
if self.check_recursion {
868-
self.seen_opaque_tys.remove(&def_id);
869-
}
870-
Some(expanded_ty)
871-
} else {
872-
// If another opaque type that we contain is recursive, then it
873-
// will report the error, so we don't have to.
874-
self.found_any_recursion = true;
875-
self.found_recursion = def_id == *self.primary_def_id.as_ref().unwrap();
876-
None
877-
}
878-
}
879834
}
880835

881836
impl<'tcx> TypeFolder<TyCtxt<'tcx>> for OpaqueTypeExpander<'tcx> {
@@ -884,22 +839,20 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for OpaqueTypeExpander<'tcx> {
884839
}
885840

886841
fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
887-
let mut t = if let ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) = *t.kind() {
842+
if let ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) = *t.kind() {
888843
self.expand_opaque_ty(def_id, args).unwrap_or(t)
889-
} else if t.has_opaque_types() || t.has_coroutines() {
844+
} else if t.has_opaque_types() {
890845
t.super_fold_with(self)
891846
} else {
892847
t
893-
};
894-
if self.expand_coroutines {
895-
if let ty::CoroutineWitness(def_id, args) = *t.kind() {
896-
t = self.expand_coroutine(def_id, args).unwrap_or(t);
897-
}
898848
}
899-
t
900849
}
901850

902851
fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> {
852+
if !p.has_opaque_types() {
853+
return p;
854+
}
855+
903856
if let ty::PredicateKind::Clause(clause) = p.kind().skip_binder()
904857
&& let ty::ClauseKind::Projection(projection_pred) = clause
905858
{
@@ -1433,7 +1386,6 @@ pub fn reveal_opaque_types_in_bounds<'tcx>(
14331386
found_recursion: false,
14341387
found_any_recursion: false,
14351388
check_recursion: false,
1436-
expand_coroutines: false,
14371389
tcx,
14381390
};
14391391
val.fold_with(&mut visitor)

0 commit comments

Comments
 (0)