1
Fork 0

Auto merge of #104321 - Swatinem:async-gen, r=oli-obk

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.

---

Given this demo code:

```rust
pub async fn a(arg: u32) -> Backtrace {
    let bt = b().await;
    let _arg = arg;
    bt
}

pub async fn b() -> Backtrace {
    Backtrace::force_capture()
}
```

I would get the following with the latest stable compiler (on Windows):

```
   4: async_codegen:🅱️:async_fn$0
             at .\src\lib.rs:10
   5: core::future::from_generator::impl$1::poll<enum2$<async_codegen:🅱️:async_fn_env$0> >
             at /rustc/897e37553bba8b42751c67658967889d11ecd120\library\core\src\future\mod.rs:91
   6: async_codegen:🅰️:async_fn$0
             at .\src\lib.rs:4
   7: core::future::from_generator::impl$1::poll<enum2$<async_codegen:🅰️:async_fn_env$0> >
             at /rustc/897e37553bba8b42751c67658967889d11ecd120\library\core\src\future\mod.rs:91
```

whereas now I get a much cleaner stack trace:

```
   3: async_codegen:🅱️:async_fn$0
             at .\src\lib.rs:10
   4: async_codegen:🅰️:async_fn$0
             at .\src\lib.rs:4
```
This commit is contained in:
bors 2022-11-24 17:14:42 +00:00
commit 5dfb4b0afa
41 changed files with 459 additions and 211 deletions

View file

@ -23,10 +23,11 @@ use crate::traits::{
BuiltinDerivedObligation, ImplDerivedObligation, ImplDerivedObligationCause, ImplSource,
ImplSourceAutoImplData, ImplSourceBuiltinData, ImplSourceClosureData,
ImplSourceConstDestructData, ImplSourceDiscriminantKindData, ImplSourceFnPointerData,
ImplSourceGeneratorData, ImplSourceObjectData, ImplSourcePointeeData, ImplSourceTraitAliasData,
ImplSourceTraitUpcastingData, ImplSourceUserDefinedData, Normalized, ObjectCastObligation,
Obligation, ObligationCause, OutputTypeParameterMismatch, PredicateObligation, Selection,
SelectionError, TraitNotObjectSafe, TraitObligation, Unimplemented, VtblSegment,
ImplSourceFutureData, ImplSourceGeneratorData, ImplSourceObjectData, ImplSourcePointeeData,
ImplSourceTraitAliasData, ImplSourceTraitUpcastingData, ImplSourceUserDefinedData, Normalized,
ObjectCastObligation, Obligation, ObligationCause, OutputTypeParameterMismatch,
PredicateObligation, Selection, SelectionError, TraitNotObjectSafe, TraitObligation,
Unimplemented, VtblSegment,
};
use super::BuiltinImplConditions;
@ -89,6 +90,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
ImplSource::Generator(vtable_generator)
}
FutureCandidate => {
let vtable_future = self.confirm_future_candidate(obligation)?;
ImplSource::Future(vtable_future)
}
FnPointerCandidate { .. } => {
let data = self.confirm_fn_pointer_candidate(obligation)?;
ImplSource::FnPointer(data)
@ -685,7 +691,21 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
debug!(?obligation, ?generator_def_id, ?substs, "confirm_generator_candidate");
let trait_ref = self.generator_trait_ref_unnormalized(obligation, substs);
let gen_sig = substs.as_generator().poly_sig();
// (1) Feels icky to skip the binder here, but OTOH we know
// that the self-type is an generator type and hence is
// in fact unparameterized (or at least does not reference any
// regions bound in the obligation). Still probably some
// refactoring could make this nicer.
let trait_ref = super::util::generator_trait_ref_and_outputs(
self.tcx(),
obligation.predicate.def_id(),
obligation.predicate.skip_binder().self_ty(), // (1)
gen_sig,
)
.map_bound(|(trait_ref, ..)| trait_ref);
let nested = self.confirm_poly_trait_refs(obligation, trait_ref)?;
debug!(?trait_ref, ?nested, "generator candidate obligations");
@ -693,6 +713,36 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
Ok(ImplSourceGeneratorData { generator_def_id, substs, nested })
}
fn confirm_future_candidate(
&mut self,
obligation: &TraitObligation<'tcx>,
) -> Result<ImplSourceFutureData<'tcx, PredicateObligation<'tcx>>, SelectionError<'tcx>> {
// Okay to skip binder because the substs on generator types never
// touch bound regions, they just capture the in-scope
// type/region parameters.
let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder());
let ty::Generator(generator_def_id, substs, _) = *self_ty.kind() else {
bug!("closure candidate for non-closure {:?}", obligation);
};
debug!(?obligation, ?generator_def_id, ?substs, "confirm_future_candidate");
let gen_sig = substs.as_generator().poly_sig();
let trait_ref = super::util::future_trait_ref_and_outputs(
self.tcx(),
obligation.predicate.def_id(),
obligation.predicate.no_bound_vars().expect("future has no bound vars").self_ty(),
gen_sig,
)
.map_bound(|(trait_ref, ..)| trait_ref);
let nested = self.confirm_poly_trait_refs(obligation, trait_ref)?;
debug!(?trait_ref, ?nested, "future candidate obligations");
Ok(ImplSourceFutureData { generator_def_id, substs, nested })
}
#[instrument(skip(self), level = "debug")]
fn confirm_closure_candidate(
&mut self,