1
Fork 0

Avoid GenFuture shim when compiling async constructs

Previously, async constructs would be lowered to "normal" generators,
with an additional `from_generator` / `GenFuture` shim in between to
convert from `Generator` to `Future`.

The compiler will now special-case these generators internally so that
async constructs will *directly* implement `Future` without the need
to go through the `from_generator` / `GenFuture` shim.

The primary motivation for this change was hiding this implementation
detail in stack traces and debuginfo, but it can in theory also help
the optimizer as there is less abstractions to see through.
This commit is contained in:
Arpad Borsos 2022-11-18 22:56:22 +01:00
parent fd815a5091
commit 9f36f988ad
No known key found for this signature in database
GPG key ID: FC7BCA77824B3298
41 changed files with 459 additions and 211 deletions

View file

@ -149,7 +149,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
*capture_clause,
*closure_node_id,
None,
block.span,
e.span,
hir::AsyncGeneratorKind::Block,
|this| this.with_new_scopes(|this| this.lower_block_expr(block)),
),
@ -569,12 +569,12 @@ impl<'hir> LoweringContext<'_, 'hir> {
}
}
/// Lower an `async` construct to a generator that is then wrapped so it implements `Future`.
/// Lower an `async` construct to a generator that implements `Future`.
///
/// This results in:
///
/// ```text
/// std::future::from_generator(static move? |_task_context| -> <ret_ty> {
/// std::future::identity_future(static move? |_task_context| -> <ret_ty> {
/// <body>
/// })
/// ```
@ -589,12 +589,14 @@ impl<'hir> LoweringContext<'_, 'hir> {
) -> hir::ExprKind<'hir> {
let output = ret_ty.unwrap_or_else(|| hir::FnRetTy::DefaultReturn(self.lower_span(span)));
// Resume argument type. We let the compiler infer this to simplify the lowering. It is
// fully constrained by `future::from_generator`.
// Resume argument type: `ResumeTy`
let unstable_span =
self.mark_span_with_reason(DesugaringKind::Async, span, self.allow_gen_future.clone());
let resume_ty = hir::QPath::LangItem(hir::LangItem::ResumeTy, unstable_span, None);
let input_ty = hir::Ty {
hir_id: self.next_id(),
kind: hir::TyKind::Infer,
span: self.lower_span(span),
kind: hir::TyKind::Path(resume_ty),
span: unstable_span,
};
// The closure/generator `FnDecl` takes a single (resume) argument of type `input_ty`.
@ -677,16 +679,24 @@ impl<'hir> LoweringContext<'_, 'hir> {
let generator = hir::Expr { hir_id, kind: generator_kind, span: self.lower_span(span) };
// `future::from_generator`:
let gen_future = self.expr_lang_item_path(
// FIXME(swatinem):
// For some reason, the async block needs to flow through *any*
// call (like the identity function), as otherwise type and lifetime
// inference have a hard time figuring things out.
// Without this, we would get:
// E0720 in src/test/ui/impl-trait/in-trait/default-body-with-rpit.rs
// E0700 in src/test/ui/self/self_lifetime-async.rs
// `future::identity_future`:
let identity_future = self.expr_lang_item_path(
unstable_span,
hir::LangItem::FromGenerator,
hir::LangItem::IdentityFuture,
AttrVec::new(),
None,
);
// `future::from_generator(generator)`:
hir::ExprKind::Call(self.arena.alloc(gen_future), arena_vec![self; generator])
// `future::identity_future(generator)`:
hir::ExprKind::Call(self.arena.alloc(identity_future), arena_vec![self; generator])
}
/// Desugar `<expr>.await` into:
@ -990,7 +1000,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
}
// Transform `async |x: u8| -> X { ... }` into
// `|x: u8| future_from_generator(|| -> X { ... })`.
// `|x: u8| identity_future(|| -> X { ... })`.
let body_id = this.lower_fn_body(&outer_decl, |this| {
let async_ret_ty = if let FnRetTy::Ty(ty) = &decl.output {
let itctx = ImplTraitContext::Disallowed(ImplTraitPosition::AsyncBlock);