Skip to content

Commit 5dfb4b0

Browse files
committed
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::b::async_fn$0 at .\src\lib.rs:10 5: core::future::from_generator::impl$1::poll<enum2$<async_codegen::b::async_fn_env$0> > at /rustc/897e37553bba8b42751c67658967889d11ecd120\library\core\src\future\mod.rs:91 6: async_codegen::a::async_fn$0 at .\src\lib.rs:4 7: core::future::from_generator::impl$1::poll<enum2$<async_codegen::a::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::b::async_fn$0 at .\src\lib.rs:10 4: async_codegen::a::async_fn$0 at .\src\lib.rs:4 ```
2 parents 1dda298 + 9f36f98 commit 5dfb4b0

File tree

41 files changed

+461
-213
lines changed

Some content is hidden

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

41 files changed

+461
-213
lines changed

compiler/rustc_ast_lowering/src/expr.rs

+23-13
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
149149
*capture_clause,
150150
*closure_node_id,
151151
None,
152-
block.span,
152+
e.span,
153153
hir::AsyncGeneratorKind::Block,
154154
|this| this.with_new_scopes(|this| this.lower_block_expr(block)),
155155
),
@@ -569,12 +569,12 @@ impl<'hir> LoweringContext<'_, 'hir> {
569569
}
570570
}
571571

572-
/// Lower an `async` construct to a generator that is then wrapped so it implements `Future`.
572+
/// Lower an `async` construct to a generator that implements `Future`.
573573
///
574574
/// This results in:
575575
///
576576
/// ```text
577-
/// std::future::from_generator(static move? |_task_context| -> <ret_ty> {
577+
/// std::future::identity_future(static move? |_task_context| -> <ret_ty> {
578578
/// <body>
579579
/// })
580580
/// ```
@@ -589,12 +589,14 @@ impl<'hir> LoweringContext<'_, 'hir> {
589589
) -> hir::ExprKind<'hir> {
590590
let output = ret_ty.unwrap_or_else(|| hir::FnRetTy::DefaultReturn(self.lower_span(span)));
591591

592-
// Resume argument type. We let the compiler infer this to simplify the lowering. It is
593-
// fully constrained by `future::from_generator`.
592+
// Resume argument type: `ResumeTy`
593+
let unstable_span =
594+
self.mark_span_with_reason(DesugaringKind::Async, span, self.allow_gen_future.clone());
595+
let resume_ty = hir::QPath::LangItem(hir::LangItem::ResumeTy, unstable_span, None);
594596
let input_ty = hir::Ty {
595597
hir_id: self.next_id(),
596-
kind: hir::TyKind::Infer,
597-
span: self.lower_span(span),
598+
kind: hir::TyKind::Path(resume_ty),
599+
span: unstable_span,
598600
};
599601

600602
// The closure/generator `FnDecl` takes a single (resume) argument of type `input_ty`.
@@ -677,16 +679,24 @@ impl<'hir> LoweringContext<'_, 'hir> {
677679

678680
let generator = hir::Expr { hir_id, kind: generator_kind, span: self.lower_span(span) };
679681

680-
// `future::from_generator`:
681-
let gen_future = self.expr_lang_item_path(
682+
// FIXME(swatinem):
683+
// For some reason, the async block needs to flow through *any*
684+
// call (like the identity function), as otherwise type and lifetime
685+
// inference have a hard time figuring things out.
686+
// Without this, we would get:
687+
// E0720 in src/test/ui/impl-trait/in-trait/default-body-with-rpit.rs
688+
// E0700 in src/test/ui/self/self_lifetime-async.rs
689+
690+
// `future::identity_future`:
691+
let identity_future = self.expr_lang_item_path(
682692
unstable_span,
683-
hir::LangItem::FromGenerator,
693+
hir::LangItem::IdentityFuture,
684694
AttrVec::new(),
685695
None,
686696
);
687697

688-
// `future::from_generator(generator)`:
689-
hir::ExprKind::Call(self.arena.alloc(gen_future), arena_vec![self; generator])
698+
// `future::identity_future(generator)`:
699+
hir::ExprKind::Call(self.arena.alloc(identity_future), arena_vec![self; generator])
690700
}
691701

692702
/// Desugar `<expr>.await` into:
@@ -990,7 +1000,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
9901000
}
9911001

9921002
// Transform `async |x: u8| -> X { ... }` into
993-
// `|x: u8| future_from_generator(|| -> X { ... })`.
1003+
// `|x: u8| identity_future(|| -> X { ... })`.
9941004
let body_id = this.lower_fn_body(&outer_decl, |this| {
9951005
let async_ret_ty = if let FnRetTy::Ty(ty) = &decl.output {
9961006
let itctx = ImplTraitContext::Disallowed(ImplTraitPosition::AsyncBlock);

compiler/rustc_borrowck/src/diagnostics/region_errors.rs

+11-4
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use rustc_middle::ty::subst::InternalSubsts;
2121
use rustc_middle::ty::Region;
2222
use rustc_middle::ty::TypeVisitor;
2323
use rustc_middle::ty::{self, RegionVid, Ty};
24-
use rustc_span::symbol::{kw, sym, Ident};
24+
use rustc_span::symbol::{kw, Ident};
2525
use rustc_span::Span;
2626

2727
use crate::borrowck_errors;
@@ -514,8 +514,11 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
514514
span: *span,
515515
ty_err: match output_ty.kind() {
516516
ty::Closure(_, _) => FnMutReturnTypeErr::ReturnClosure { span: *span },
517-
ty::Adt(def, _)
518-
if self.infcx.tcx.is_diagnostic_item(sym::gen_future, def.did()) =>
517+
ty::Generator(def, ..)
518+
if matches!(
519+
self.infcx.tcx.generator_kind(def),
520+
Some(hir::GeneratorKind::Async(_))
521+
) =>
519522
{
520523
FnMutReturnTypeErr::ReturnAsyncBlock { span: *span }
521524
}
@@ -927,10 +930,14 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
927930
// only when the block is a closure
928931
if let hir::ExprKind::Closure(hir::Closure {
929932
capture_clause: hir::CaptureBy::Ref,
933+
body,
930934
..
931935
}) = expr.kind
932936
{
933-
closure_span = Some(expr.span.shrink_to_lo());
937+
let body = map.body(*body);
938+
if !matches!(body.generator_kind, Some(hir::GeneratorKind::Async(..))) {
939+
closure_span = Some(expr.span.shrink_to_lo());
940+
}
934941
}
935942
}
936943
}

compiler/rustc_borrowck/src/type_check/mod.rs

-1
Original file line numberDiff line numberDiff line change
@@ -2588,7 +2588,6 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
25882588
}
25892589

25902590
// For closures, we have some **extra requirements** we
2591-
//
25922591
// have to check. In particular, in their upvars and
25932592
// signatures, closures often reference various regions
25942593
// from the surrounding function -- we call those the

compiler/rustc_const_eval/src/transform/check_consts/check.rs

+11-10
Original file line numberDiff line numberDiff line change
@@ -449,8 +449,17 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
449449
| Rvalue::CopyForDeref(..)
450450
| Rvalue::Repeat(..)
451451
| Rvalue::Discriminant(..)
452-
| Rvalue::Len(_)
453-
| Rvalue::Aggregate(..) => {}
452+
| Rvalue::Len(_) => {}
453+
454+
Rvalue::Aggregate(ref kind, ..) => {
455+
if let AggregateKind::Generator(def_id, ..) = kind.as_ref() {
456+
if let Some(generator_kind) = self.tcx.generator_kind(def_id.to_def_id()) {
457+
if matches!(generator_kind, hir::GeneratorKind::Async(..)) {
458+
self.check_op(ops::Generator(generator_kind));
459+
}
460+
}
461+
}
462+
}
454463

455464
Rvalue::Ref(_, kind @ BorrowKind::Mut { .. }, ref place)
456465
| Rvalue::Ref(_, kind @ BorrowKind::Unique, ref place) => {
@@ -889,14 +898,6 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
889898
return;
890899
}
891900

892-
// `async` blocks get lowered to `std::future::from_generator(/* a closure */)`.
893-
let is_async_block = Some(callee) == tcx.lang_items().from_generator_fn();
894-
if is_async_block {
895-
let kind = hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Block);
896-
self.check_op(ops::Generator(kind));
897-
return;
898-
}
899-
900901
if !tcx.is_const_fn_raw(callee) {
901902
if !tcx.is_const_default_method(callee) {
902903
// To get to here we must have already found a const impl for the

compiler/rustc_hir/src/lang_items.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -280,10 +280,14 @@ language_item_table! {
280280

281281
PointerSized, sym::pointer_sized, pointer_sized, Target::Trait, GenericRequirement::Exact(0);
282282

283+
Poll, sym::Poll, poll, Target::Enum, GenericRequirement::None;
283284
PollReady, sym::Ready, poll_ready_variant, Target::Variant, GenericRequirement::None;
284285
PollPending, sym::Pending, poll_pending_variant, Target::Variant, GenericRequirement::None;
285286

286-
FromGenerator, sym::from_generator, from_generator_fn, Target::Fn, GenericRequirement::None;
287+
// FIXME(swatinem): the following lang items are used for async lowering and
288+
// should become obsolete eventually.
289+
ResumeTy, sym::ResumeTy, resume_ty, Target::Struct, GenericRequirement::None;
290+
IdentityFuture, sym::identity_future, identity_future_fn, Target::Fn, GenericRequirement::None;
287291
GetContext, sym::get_context, get_context_fn, Target::Fn, GenericRequirement::None;
288292

289293
FuturePoll, sym::poll, future_poll_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;

compiler/rustc_hir_typeck/src/check.rs

+9-4
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,15 @@ pub(super) fn check_fn<'a, 'tcx>(
5656

5757
fn_maybe_err(tcx, span, fn_sig.abi);
5858

59-
if body.generator_kind.is_some() && can_be_generator.is_some() {
60-
let yield_ty = fcx
61-
.next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::TypeInference, span });
62-
fcx.require_type_is_sized(yield_ty, span, traits::SizedYieldType);
59+
if let Some(kind) = body.generator_kind && can_be_generator.is_some() {
60+
let yield_ty = if kind == hir::GeneratorKind::Gen {
61+
let yield_ty = fcx
62+
.next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::TypeInference, span });
63+
fcx.require_type_is_sized(yield_ty, span, traits::SizedYieldType);
64+
yield_ty
65+
} else {
66+
tcx.mk_unit()
67+
};
6368

6469
// Resume type defaults to `()` if the generator has no argument.
6570
let resume_ty = fn_sig.inputs().get(0).copied().unwrap_or_else(|| tcx.mk_unit());

compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs

-8
Original file line numberDiff line numberDiff line change
@@ -1734,14 +1734,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
17341734
let hir = self.tcx.hir();
17351735
let hir::Node::Expr(expr) = hir.get(hir_id) else { return false; };
17361736

1737-
// Skip over mentioning async lang item
1738-
if Some(def_id) == self.tcx.lang_items().from_generator_fn()
1739-
&& error.obligation.cause.span.desugaring_kind()
1740-
== Some(rustc_span::DesugaringKind::Async)
1741-
{
1742-
return false;
1743-
}
1744-
17451737
let Some(unsubstituted_pred) =
17461738
self.tcx.predicates_of(def_id).instantiate_identity(self.tcx).predicates.into_iter().nth(idx)
17471739
else { return false; };

compiler/rustc_lint/src/unused.rs

+14-1
Original file line numberDiff line numberDiff line change
@@ -319,7 +319,20 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
319319
.map(|inner| MustUsePath::Array(Box::new(inner), len)),
320320
},
321321
ty::Closure(..) => Some(MustUsePath::Closure(span)),
322-
ty::Generator(..) => Some(MustUsePath::Generator(span)),
322+
ty::Generator(def_id, ..) => {
323+
// async fn should be treated as "implementor of `Future`"
324+
let must_use = if matches!(
325+
cx.tcx.generator_kind(def_id),
326+
Some(hir::GeneratorKind::Async(..))
327+
) {
328+
let def_id = cx.tcx.lang_items().future_trait().unwrap();
329+
is_def_must_use(cx, def_id, span)
330+
.map(|inner| MustUsePath::Opaque(Box::new(inner)))
331+
} else {
332+
None
333+
};
334+
must_use.or(Some(MustUsePath::Generator(span)))
335+
}
323336
_ => None,
324337
}
325338
}

compiler/rustc_middle/src/traits/mod.rs

+20
Original file line numberDiff line numberDiff line change
@@ -660,6 +660,9 @@ pub enum ImplSource<'tcx, N> {
660660
/// ImplSource automatically generated for a generator.
661661
Generator(ImplSourceGeneratorData<'tcx, N>),
662662

663+
/// ImplSource automatically generated for a generator backing an async future.
664+
Future(ImplSourceFutureData<'tcx, N>),
665+
663666
/// ImplSource for a trait alias.
664667
TraitAlias(ImplSourceTraitAliasData<'tcx, N>),
665668

@@ -676,6 +679,7 @@ impl<'tcx, N> ImplSource<'tcx, N> {
676679
ImplSource::AutoImpl(d) => d.nested,
677680
ImplSource::Closure(c) => c.nested,
678681
ImplSource::Generator(c) => c.nested,
682+
ImplSource::Future(c) => c.nested,
679683
ImplSource::Object(d) => d.nested,
680684
ImplSource::FnPointer(d) => d.nested,
681685
ImplSource::DiscriminantKind(ImplSourceDiscriminantKindData)
@@ -694,6 +698,7 @@ impl<'tcx, N> ImplSource<'tcx, N> {
694698
ImplSource::AutoImpl(d) => &d.nested,
695699
ImplSource::Closure(c) => &c.nested,
696700
ImplSource::Generator(c) => &c.nested,
701+
ImplSource::Future(c) => &c.nested,
697702
ImplSource::Object(d) => &d.nested,
698703
ImplSource::FnPointer(d) => &d.nested,
699704
ImplSource::DiscriminantKind(ImplSourceDiscriminantKindData)
@@ -737,6 +742,11 @@ impl<'tcx, N> ImplSource<'tcx, N> {
737742
substs: c.substs,
738743
nested: c.nested.into_iter().map(f).collect(),
739744
}),
745+
ImplSource::Future(c) => ImplSource::Future(ImplSourceFutureData {
746+
generator_def_id: c.generator_def_id,
747+
substs: c.substs,
748+
nested: c.nested.into_iter().map(f).collect(),
749+
}),
740750
ImplSource::FnPointer(p) => ImplSource::FnPointer(ImplSourceFnPointerData {
741751
fn_ty: p.fn_ty,
742752
nested: p.nested.into_iter().map(f).collect(),
@@ -796,6 +806,16 @@ pub struct ImplSourceGeneratorData<'tcx, N> {
796806
pub nested: Vec<N>,
797807
}
798808

809+
#[derive(Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, Lift)]
810+
#[derive(TypeFoldable, TypeVisitable)]
811+
pub struct ImplSourceFutureData<'tcx, N> {
812+
pub generator_def_id: DefId,
813+
pub substs: SubstsRef<'tcx>,
814+
/// Nested obligations. This can be non-empty if the generator
815+
/// signature contains associated types.
816+
pub nested: Vec<N>,
817+
}
818+
799819
#[derive(Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, Lift)]
800820
#[derive(TypeFoldable, TypeVisitable)]
801821
pub struct ImplSourceClosureData<'tcx, N> {

compiler/rustc_middle/src/traits/select.rs

+4
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,10 @@ pub enum SelectionCandidate<'tcx> {
131131
/// generated for a generator.
132132
GeneratorCandidate,
133133

134+
/// Implementation of a `Future` trait by one of the generator types
135+
/// generated for an async construct.
136+
FutureCandidate,
137+
134138
/// Implementation of a `Fn`-family trait by one of the anonymous
135139
/// types generated for a fn pointer type (e.g., `fn(int) -> int`)
136140
FnPointerCandidate {

compiler/rustc_middle/src/traits/structural_impls.rs

+12
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ impl<'tcx, N: fmt::Debug> fmt::Debug for traits::ImplSource<'tcx, N> {
1515

1616
super::ImplSource::Generator(ref d) => write!(f, "{:?}", d),
1717

18+
super::ImplSource::Future(ref d) => write!(f, "{:?}", d),
19+
1820
super::ImplSource::FnPointer(ref d) => write!(f, "({:?})", d),
1921

2022
super::ImplSource::DiscriminantKind(ref d) => write!(f, "{:?}", d),
@@ -58,6 +60,16 @@ impl<'tcx, N: fmt::Debug> fmt::Debug for traits::ImplSourceGeneratorData<'tcx, N
5860
}
5961
}
6062

63+
impl<'tcx, N: fmt::Debug> fmt::Debug for traits::ImplSourceFutureData<'tcx, N> {
64+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
65+
write!(
66+
f,
67+
"ImplSourceFutureData(generator_def_id={:?}, substs={:?}, nested={:?})",
68+
self.generator_def_id, self.substs, self.nested
69+
)
70+
}
71+
}
72+
6173
impl<'tcx, N: fmt::Debug> fmt::Debug for traits::ImplSourceClosureData<'tcx, N> {
6274
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
6375
write!(

compiler/rustc_middle/src/ty/print/pretty.rs

+11
Original file line numberDiff line numberDiff line change
@@ -681,6 +681,17 @@ pub trait PrettyPrinter<'tcx>:
681681
}
682682
ty::Str => p!("str"),
683683
ty::Generator(did, substs, movability) => {
684+
// FIXME(swatinem): async constructs used to be pretty printed
685+
// as `impl Future` previously due to the `from_generator` wrapping.
686+
// lets special case this here for now to avoid churn in diagnostics.
687+
let generator_kind = self.tcx().generator_kind(did);
688+
if matches!(generator_kind, Some(hir::GeneratorKind::Async(..))) {
689+
let return_ty = substs.as_generator().return_ty();
690+
p!(write("impl Future<Output = {}>", return_ty));
691+
692+
return Ok(self);
693+
}
694+
684695
p!(write("["));
685696
match movability {
686697
hir::Movability::Movable => {}

0 commit comments

Comments
 (0)