Skip to content

Commit 8120660

Browse files
committed
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.
1 parent 9d46c7a commit 8120660

Some content is hidden

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

45 files changed

+427
-441
lines changed

compiler/rustc_ast_lowering/src/expr.rs

+18-24
Original file line numberDiff line numberDiff line change
@@ -148,15 +148,16 @@ impl<'hir> LoweringContext<'_, 'hir> {
148148
self.arena.alloc_from_iter(arms.iter().map(|x| self.lower_arm(x))),
149149
hir::MatchSource::Normal,
150150
),
151-
ExprKind::Async(capture_clause, closure_node_id, ref block) => self
152-
.make_async_expr(
151+
ExprKind::Async(capture_clause, closure_node_id, ref block) => {
152+
return self.make_async_expr(
153153
capture_clause,
154154
closure_node_id,
155155
None,
156-
block.span,
156+
e.span,
157157
hir::AsyncGeneratorKind::Block,
158158
|this| this.with_new_scopes(|this| this.lower_block_expr(block)),
159-
),
159+
);
160+
}
160161
ExprKind::Await(ref expr) => {
161162
let dot_await_span = if expr.span.hi() < e.span.hi() {
162163
let span_with_whitespace = self
@@ -575,14 +576,14 @@ impl<'hir> LoweringContext<'_, 'hir> {
575576
}
576577
}
577578

578-
/// Lower an `async` construct to a generator that is then wrapped so it implements `Future`.
579+
/// Lower an `async` construct to a generator that implements `Future`.
579580
///
580581
/// This results in:
581582
///
582583
/// ```text
583-
/// std::future::from_generator(static move? |_task_context| -> <ret_ty> {
584+
/// static move? |_task_context| -> <ret_ty> {
584585
/// <body>
585-
/// })
586+
/// }
586587
/// ```
587588
pub(super) fn make_async_expr(
588589
&mut self,
@@ -592,20 +593,22 @@ impl<'hir> LoweringContext<'_, 'hir> {
592593
span: Span,
593594
async_gen_kind: hir::AsyncGeneratorKind,
594595
body: impl FnOnce(&mut Self) -> hir::Expr<'hir>,
595-
) -> hir::ExprKind<'hir> {
596+
) -> hir::Expr<'hir> {
596597
let output = match ret_ty {
597598
Some(ty) => hir::FnRetTy::Return(
598599
self.lower_ty(&ty, &ImplTraitContext::Disallowed(ImplTraitPosition::AsyncBlock)),
599600
),
600601
None => hir::FnRetTy::DefaultReturn(self.lower_span(span)),
601602
};
602603

603-
// Resume argument type. We let the compiler infer this to simplify the lowering. It is
604-
// fully constrained by `future::from_generator`.
604+
// Resume argument type: `ResumeTy`
605+
let unstable_span =
606+
self.mark_span_with_reason(DesugaringKind::Async, span, self.allow_gen_future.clone());
607+
let resume_ty = hir::QPath::LangItem(hir::LangItem::ResumeTy, unstable_span, None);
605608
let input_ty = hir::Ty {
606609
hir_id: self.next_id(),
607-
kind: hir::TyKind::Infer,
608-
span: self.lower_span(span),
610+
kind: hir::TyKind::Path(resume_ty),
611+
span: unstable_span,
609612
};
610613

611614
// The closure/generator `FnDecl` takes a single (resume) argument of type `input_ty`.
@@ -688,16 +691,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
688691

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

691-
// `future::from_generator`:
692-
let gen_future = self.expr_lang_item_path(
693-
unstable_span,
694-
hir::LangItem::FromGenerator,
695-
AttrVec::new(),
696-
None,
697-
);
698-
699-
// `future::from_generator(generator)`:
700-
hir::ExprKind::Call(self.arena.alloc(gen_future), arena_vec![self; generator])
694+
generator
701695
}
702696

703697
/// Desugar `<expr>.await` into:
@@ -1001,7 +995,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
1001995
}
1002996

1003997
// Transform `async |x: u8| -> X { ... }` into
1004-
// `|x: u8| future_from_generator(|| -> X { ... })`.
998+
// `|x: u8| || -> X { ... }`.
1005999
let body_id = this.lower_fn_body(&outer_decl, |this| {
10061000
let async_ret_ty =
10071001
if let FnRetTy::Ty(ty) = &decl.output { Some(ty.clone()) } else { None };
@@ -1013,7 +1007,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
10131007
hir::AsyncGeneratorKind::Closure,
10141008
|this| this.with_new_scopes(|this| this.lower_expr_mut(body)),
10151009
);
1016-
this.expr(fn_decl_span, async_body, AttrVec::new())
1010+
async_body
10171011
});
10181012
body_id
10191013
});

compiler/rustc_ast_lowering/src/item.rs

+1-4
Original file line numberDiff line numberDiff line change
@@ -1251,10 +1251,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
12511251
},
12521252
);
12531253

1254-
(
1255-
this.arena.alloc_from_iter(parameters),
1256-
this.expr(body.span, async_expr, AttrVec::new()),
1257-
)
1254+
(this.arena.alloc_from_iter(parameters), async_expr)
12581255
})
12591256
}
12601257

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_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

+2
Original file line numberDiff line numberDiff line change
@@ -270,9 +270,11 @@ language_item_table! {
270270
TryTraitBranch, sym::branch, branch_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
271271
TryTraitFromYeet, sym::from_yeet, from_yeet_fn, Target::Fn, GenericRequirement::None;
272272

273+
Poll, sym::Poll, poll, Target::Enum, GenericRequirement::None;
273274
PollReady, sym::Ready, poll_ready_variant, Target::Variant, GenericRequirement::None;
274275
PollPending, sym::Pending, poll_pending_variant, Target::Variant, GenericRequirement::None;
275276

277+
ResumeTy, sym::ResumeTy, resume_ty, Target::Struct, GenericRequirement::None;
276278
FromGenerator, sym::from_generator, from_generator_fn, Target::Fn, GenericRequirement::None;
277279
GetContext, sym::get_context, get_context_fn, Target::Fn, GenericRequirement::None;
278280

compiler/rustc_hir_typeck/src/callee.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -302,9 +302,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
302302
let fn_decl_span = if hir.body(body).generator_kind
303303
== Some(hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Closure))
304304
{
305-
// Actually need to unwrap a few more layers of HIR to get to
305+
// Actually need to unwrap one more layer of HIR to get to
306306
// the _real_ closure...
307-
let async_closure = hir.get_parent_node(hir.get_parent_node(parent_hir_id));
307+
let async_closure = hir.get_parent_node(parent_hir_id);
308308
if let hir::Node::Expr(hir::Expr {
309309
kind: hir::ExprKind::Closure(&hir::Closure { fn_decl_span, .. }),
310310
..

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
@@ -1729,14 +1729,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
17291729
let hir = self.tcx.hir();
17301730
let hir::Node::Expr(expr) = hir.get(hir_id) else { return false; };
17311731

1732-
// Skip over mentioning async lang item
1733-
if Some(def_id) == self.tcx.lang_items().from_generator_fn()
1734-
&& error.obligation.cause.span.desugaring_kind()
1735-
== Some(rustc_span::DesugaringKind::Async)
1736-
{
1737-
return false;
1738-
}
1739-
17401732
let Some(unsubstituted_pred) =
17411733
self.tcx.predicates_of(def_id).instantiate_identity(self.tcx).predicates.into_iter().nth(idx)
17421734
else { return false; };

compiler/rustc_lint/src/unused.rs

+10-1
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,16 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
300300
);
301301
true
302302
}
303-
ty::Generator(..) => {
303+
ty::Generator(def_id, ..) => {
304+
// async fn should be treated as "implementor of `Future`"
305+
if matches!(cx.tcx.generator_kind(def_id), Some(hir::GeneratorKind::Async(..)))
306+
{
307+
let def_id = cx.tcx.lang_items().future_trait().unwrap();
308+
let descr_pre = &format!("{}implementer{} of ", descr_pre, plural_suffix,);
309+
if check_must_use_def(cx, def_id, span, descr_pre, descr_post) {
310+
return true;
311+
}
312+
}
304313
cx.struct_span_lint(
305314
UNUSED_MUST_USE,
306315
span,

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
@@ -679,6 +679,17 @@ pub trait PrettyPrinter<'tcx>:
679679
}
680680
ty::Str => p!("str"),
681681
ty::Generator(did, substs, movability) => {
682+
// FIXME(swatinem): async constructs used to be pretty printed
683+
// as `impl Future` previously due to the `from_generator` wrapping.
684+
// lets special case this here for now to avoid churn in diagnostics.
685+
let generator_kind = self.tcx().generator_kind(did);
686+
if matches!(generator_kind, Some(hir::GeneratorKind::Async(..))) {
687+
let return_ty = substs.as_generator().return_ty();
688+
p!(write("impl Future<Output = {}>", return_ty));
689+
690+
return Ok(self);
691+
}
692+
682693
p!(write("["));
683694
match movability {
684695
hir::Movability::Movable => {}

0 commit comments

Comments
 (0)