Skip to content

Commit 3a47c64

Browse files
Lower async closures to TyKind::CoroutineClosure
Instead of `TyKind::Closure`. Note: the same `InternedCoroutineId` is used both for the *async closure* as well as for the *async block it returns*. When used in `TyKind::CoroutineClosure`, it represents the closure. When used in `TyKind::Coroutine`, it represents the async block. The generic args are different, though. Also noteworthy is that we distinguish between the different kinds of coroutines (general coroutines, async coroutines and eventually gen coroutines too) via the expression producing them (stored in the `InternedCoroutineId`). It might be worth it to introduce a `CoroutineKind` field to `InternedCoroutineId`, although this is not done in this PR.
1 parent c859e76 commit 3a47c64

File tree

8 files changed

+314
-86
lines changed

8 files changed

+314
-86
lines changed

src/tools/rust-analyzer/crates/hir-ty/src/display.rs

Lines changed: 85 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ use rustc_apfloat::{
3838
use rustc_ast_ir::FloatTy;
3939
use rustc_hash::FxHashSet;
4040
use rustc_type_ir::{
41-
AliasTyKind, BoundVarIndexKind, CoroutineArgsParts, RegionKind, Upcast,
41+
AliasTyKind, BoundVarIndexKind, CoroutineArgsParts, CoroutineClosureArgsParts, RegionKind,
42+
Upcast,
4243
inherent::{AdtDef, GenericArgs as _, IntoKind, SliceLike, Term as _, Ty as _, Tys as _},
4344
};
4445
use smallvec::SmallVec;
@@ -1444,14 +1445,83 @@ impl<'db> HirDisplay<'db> for Ty<'db> {
14441445
}
14451446
if f.closure_style == ClosureStyle::RANotation || !sig.output().is_unit() {
14461447
write!(f, " -> ")?;
1447-
// FIXME: We display `AsyncFn` as `-> impl Future`, but this is hard to fix because
1448-
// we don't have a trait environment here, required to normalize `<Ret as Future>::Output`.
14491448
sig.output().hir_fmt(f)?;
14501449
}
14511450
} else {
14521451
write!(f, "{{closure}}")?;
14531452
}
14541453
}
1454+
TyKind::CoroutineClosure(id, args) => {
1455+
let id = id.0;
1456+
if f.display_kind.is_source_code() {
1457+
if !f.display_kind.allows_opaque() {
1458+
return Err(HirDisplayError::DisplaySourceCodeError(
1459+
DisplaySourceCodeError::OpaqueType,
1460+
));
1461+
} else if f.closure_style != ClosureStyle::ImplFn {
1462+
never!("Only `impl Fn` is valid for displaying closures in source code");
1463+
}
1464+
}
1465+
match f.closure_style {
1466+
ClosureStyle::Hide => return write!(f, "{TYPE_HINT_TRUNCATION}"),
1467+
ClosureStyle::ClosureWithId => {
1468+
return write!(
1469+
f,
1470+
"{{async closure#{:?}}}",
1471+
salsa::plumbing::AsId::as_id(&id).index()
1472+
);
1473+
}
1474+
ClosureStyle::ClosureWithSubst => {
1475+
write!(
1476+
f,
1477+
"{{async closure#{:?}}}",
1478+
salsa::plumbing::AsId::as_id(&id).index()
1479+
)?;
1480+
return hir_fmt_generics(f, args.as_slice(), None, None);
1481+
}
1482+
_ => (),
1483+
}
1484+
let CoroutineClosureArgsParts { closure_kind_ty, signature_parts_ty, .. } =
1485+
args.split_coroutine_closure_args();
1486+
let kind = closure_kind_ty.to_opt_closure_kind().unwrap();
1487+
let kind = match kind {
1488+
rustc_type_ir::ClosureKind::Fn => "AsyncFn",
1489+
rustc_type_ir::ClosureKind::FnMut => "AsyncFnMut",
1490+
rustc_type_ir::ClosureKind::FnOnce => "AsyncFnOnce",
1491+
};
1492+
let TyKind::FnPtr(coroutine_sig, _) = signature_parts_ty.kind() else {
1493+
unreachable!("invalid coroutine closure signature");
1494+
};
1495+
let coroutine_sig = coroutine_sig.skip_binder();
1496+
let coroutine_inputs = coroutine_sig.inputs();
1497+
let TyKind::Tuple(coroutine_inputs) = coroutine_inputs.as_slice()[1].kind() else {
1498+
unreachable!("invalid coroutine closure signature");
1499+
};
1500+
let TyKind::Tuple(coroutine_output) = coroutine_sig.output().kind() else {
1501+
unreachable!("invalid coroutine closure signature");
1502+
};
1503+
let coroutine_output = coroutine_output.as_slice()[1];
1504+
match f.closure_style {
1505+
ClosureStyle::ImplFn => write!(f, "impl {kind}(")?,
1506+
ClosureStyle::RANotation => write!(f, "async |")?,
1507+
_ => unreachable!(),
1508+
}
1509+
if coroutine_inputs.is_empty() {
1510+
} else if f.should_truncate() {
1511+
write!(f, "{TYPE_HINT_TRUNCATION}")?;
1512+
} else {
1513+
f.write_joined(coroutine_inputs, ", ")?;
1514+
};
1515+
match f.closure_style {
1516+
ClosureStyle::ImplFn => write!(f, ")")?,
1517+
ClosureStyle::RANotation => write!(f, "|")?,
1518+
_ => unreachable!(),
1519+
}
1520+
if f.closure_style == ClosureStyle::RANotation || !coroutine_output.is_unit() {
1521+
write!(f, " -> ")?;
1522+
coroutine_output.hir_fmt(f)?;
1523+
}
1524+
}
14551525
TyKind::Placeholder(_) => write!(f, "{{placeholder}}")?,
14561526
TyKind::Param(param) => {
14571527
// FIXME: We should not access `param.id`, it should be removed, and we should know the
@@ -1545,8 +1615,13 @@ impl<'db> HirDisplay<'db> for Ty<'db> {
15451615
let CoroutineArgsParts { resume_ty, yield_ty, return_ty, .. } =
15461616
subst.split_coroutine_args();
15471617
let body = db.body(owner);
1548-
match &body[expr_id] {
1549-
hir_def::hir::Expr::Async { .. } => {
1618+
let expr = &body[expr_id];
1619+
match expr {
1620+
hir_def::hir::Expr::Closure {
1621+
closure_kind: hir_def::hir::ClosureKind::Async,
1622+
..
1623+
}
1624+
| hir_def::hir::Expr::Async { .. } => {
15501625
let future_trait =
15511626
LangItem::Future.resolve_trait(db, owner.module(db).krate());
15521627
let output = future_trait.and_then(|t| {
@@ -1573,7 +1648,10 @@ impl<'db> HirDisplay<'db> for Ty<'db> {
15731648
return_ty.hir_fmt(f)?;
15741649
write!(f, ">")?;
15751650
}
1576-
_ => {
1651+
hir_def::hir::Expr::Closure {
1652+
closure_kind: hir_def::hir::ClosureKind::Coroutine(..),
1653+
..
1654+
} => {
15771655
if f.display_kind.is_source_code() {
15781656
return Err(HirDisplayError::DisplaySourceCodeError(
15791657
DisplaySourceCodeError::Coroutine,
@@ -1589,12 +1667,12 @@ impl<'db> HirDisplay<'db> for Ty<'db> {
15891667
write!(f, " -> ")?;
15901668
return_ty.hir_fmt(f)?;
15911669
}
1670+
_ => panic!("invalid expr for coroutine: {expr:?}"),
15921671
}
15931672
}
15941673
TyKind::CoroutineWitness(..) => write!(f, "{{coroutine witness}}")?,
15951674
TyKind::Pat(_, _) => write!(f, "{{pat}}")?,
15961675
TyKind::UnsafeBinder(_) => write!(f, "{{unsafe binder}}")?,
1597-
TyKind::CoroutineClosure(_, _) => write!(f, "{{coroutine closure}}")?,
15981676
TyKind::Alias(_, _) => write!(f, "{{alias}}")?,
15991677
}
16001678
Ok(())

src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs

Lines changed: 68 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@ use hir_def::{
1111
type_ref::TypeRefId,
1212
};
1313
use rustc_type_ir::{
14-
ClosureArgs, ClosureArgsParts, CoroutineArgs, CoroutineArgsParts, Interner, TypeSuperVisitable,
15-
TypeVisitable, TypeVisitableExt, TypeVisitor,
14+
ClosureArgs, ClosureArgsParts, CoroutineArgs, CoroutineArgsParts, CoroutineClosureArgs,
15+
CoroutineClosureArgsParts, Interner, TypeSuperVisitable, TypeVisitable, TypeVisitableExt,
16+
TypeVisitor,
1617
inherent::{BoundExistentialPredicates, GenericArgs as _, IntoKind, SliceLike, Ty as _},
1718
};
1819
use tracing::debug;
@@ -22,8 +23,9 @@ use crate::{
2223
db::{InternedClosure, InternedCoroutine},
2324
infer::{BreakableKind, Diverges, coerce::CoerceMany},
2425
next_solver::{
25-
AliasTy, Binder, ClauseKind, DbInterner, ErrorGuaranteed, FnSig, GenericArgs, PolyFnSig,
26-
PolyProjectionPredicate, Predicate, PredicateKind, SolverDefId, Ty, TyKind,
26+
AliasTy, Binder, BoundRegionKind, BoundVarKind, BoundVarKinds, ClauseKind, DbInterner,
27+
ErrorGuaranteed, FnSig, GenericArgs, PolyFnSig, PolyProjectionPredicate, Predicate,
28+
PredicateKind, SolverDefId, Ty, TyKind,
2729
abi::Safety,
2830
infer::{
2931
BoundRegionConversionTime, InferOk, InferResult,
@@ -72,6 +74,8 @@ impl<'db> InferenceContext<'_, 'db> {
7274
let sig_ty = Ty::new_fn_ptr(interner, bound_sig);
7375

7476
let parent_args = GenericArgs::identity_for_item(interner, self.generic_def.into());
77+
// FIXME: Make this an infer var and infer it later.
78+
let tupled_upvars_ty = self.types.unit;
7579
let (id, ty, resume_yield_tys) = match closure_kind {
7680
ClosureKind::Coroutine(_) => {
7781
let yield_ty = self.table.next_ty_var();
@@ -80,11 +84,11 @@ impl<'db> InferenceContext<'_, 'db> {
8084
// FIXME: Infer the upvars later.
8185
let parts = CoroutineArgsParts {
8286
parent_args,
83-
kind_ty: Ty::new_unit(interner),
87+
kind_ty: self.types.unit,
8488
resume_ty,
8589
yield_ty,
8690
return_ty: body_ret_ty,
87-
tupled_upvars_ty: Ty::new_unit(interner),
91+
tupled_upvars_ty,
8892
};
8993

9094
let coroutine_id =
@@ -97,9 +101,7 @@ impl<'db> InferenceContext<'_, 'db> {
97101

98102
(None, coroutine_ty, Some((resume_ty, yield_ty)))
99103
}
100-
// FIXME(next-solver): `ClosureKind::Async` should really be a separate arm that creates a `CoroutineClosure`.
101-
// But for now we treat it as a closure.
102-
ClosureKind::Closure | ClosureKind::Async => {
104+
ClosureKind::Closure => {
103105
let closure_id = self.db.intern_closure(InternedClosure(self.owner, tgt_expr));
104106
match expected_kind {
105107
Some(kind) => {
@@ -117,15 +119,15 @@ impl<'db> InferenceContext<'_, 'db> {
117119
}
118120
None => {}
119121
};
120-
// FIXME: Infer the kind and the upvars later when needed.
122+
// FIXME: Infer the kind later if needed.
121123
let parts = ClosureArgsParts {
122124
parent_args,
123125
closure_kind_ty: Ty::from_closure_kind(
124126
interner,
125127
expected_kind.unwrap_or(rustc_type_ir::ClosureKind::Fn),
126128
),
127129
closure_sig_as_fn_ptr_ty: sig_ty,
128-
tupled_upvars_ty: Ty::new_unit(interner),
130+
tupled_upvars_ty,
129131
};
130132
let closure_ty = Ty::new_closure(
131133
interner,
@@ -136,6 +138,61 @@ impl<'db> InferenceContext<'_, 'db> {
136138
self.add_current_closure_dependency(closure_id);
137139
(Some(closure_id), closure_ty, None)
138140
}
141+
ClosureKind::Async => {
142+
// async closures always return the type ascribed after the `->` (if present),
143+
// and yield `()`.
144+
let bound_return_ty = bound_sig.skip_binder().output();
145+
let bound_yield_ty = self.types.unit;
146+
// rustc uses a special lang item type for the resume ty. I don't believe this can cause us problems.
147+
let resume_ty = self.types.unit;
148+
149+
// FIXME: Infer the kind later if needed.
150+
let closure_kind_ty = Ty::from_closure_kind(
151+
interner,
152+
expected_kind.unwrap_or(rustc_type_ir::ClosureKind::Fn),
153+
);
154+
155+
// FIXME: Infer captures later.
156+
// `for<'env> fn() -> ()`, for no captures.
157+
let coroutine_captures_by_ref_ty = Ty::new_fn_ptr(
158+
interner,
159+
Binder::bind_with_vars(
160+
interner.mk_fn_sig([], self.types.unit, false, Safety::Safe, FnAbi::Rust),
161+
BoundVarKinds::new_from_iter(
162+
interner,
163+
[BoundVarKind::Region(BoundRegionKind::ClosureEnv)],
164+
),
165+
),
166+
);
167+
let closure_args = CoroutineClosureArgs::new(
168+
interner,
169+
CoroutineClosureArgsParts {
170+
parent_args,
171+
closure_kind_ty,
172+
signature_parts_ty: Ty::new_fn_ptr(
173+
interner,
174+
bound_sig.map_bound(|sig| {
175+
interner.mk_fn_sig(
176+
[
177+
resume_ty,
178+
Ty::new_tup_from_iter(interner, sig.inputs().iter()),
179+
],
180+
Ty::new_tup(interner, &[bound_yield_ty, bound_return_ty]),
181+
sig.c_variadic,
182+
sig.safety,
183+
sig.abi,
184+
)
185+
}),
186+
),
187+
tupled_upvars_ty,
188+
coroutine_captures_by_ref_ty,
189+
},
190+
);
191+
192+
let coroutine_id =
193+
self.db.intern_coroutine(InternedCoroutine(self.owner, tgt_expr)).into();
194+
(None, Ty::new_coroutine_closure(interner, coroutine_id, closure_args.args), None)
195+
}
139196
};
140197

141198
// Now go through the argument patterns

src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs

Lines changed: 53 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ use rustc_abi::{ReprFlags, ReprOptions};
1717
use rustc_hash::FxHashSet;
1818
use rustc_index::bit_set::DenseBitSet;
1919
use rustc_type_ir::{
20-
AliasTermKind, AliasTyKind, BoundVar, CollectAndApply, DebruijnIndex, EarlyBinder,
21-
FlagComputation, Flags, GenericArgKind, ImplPolarity, InferTy, Interner, TraitRef,
20+
AliasTermKind, AliasTyKind, BoundVar, CollectAndApply, CoroutineWitnessTypes, DebruijnIndex,
21+
EarlyBinder, FlagComputation, Flags, GenericArgKind, ImplPolarity, InferTy, Interner, TraitRef,
2222
TypeVisitableExt, UniverseIndex, Upcast, Variance,
2323
elaborate::elaborate,
2424
error::TypeError,
@@ -29,7 +29,7 @@ use rustc_type_ir::{
2929

3030
use crate::{
3131
FnAbi,
32-
db::HirDatabase,
32+
db::{HirDatabase, InternedCoroutine},
3333
method_resolution::{ALL_FLOAT_FPS, ALL_INT_FPS, TyFingerprint},
3434
next_solver::{
3535
AdtIdWrapper, BoundConst, CallableIdWrapper, CanonicalVarKind, ClosureIdWrapper,
@@ -1205,12 +1205,28 @@ impl<'db> Interner for DbInterner<'db> {
12051205
self.db().callable_item_signature(def_id.0)
12061206
}
12071207

1208-
fn coroutine_movability(self, _def_id: Self::CoroutineId) -> rustc_ast_ir::Movability {
1209-
unimplemented!()
1208+
fn coroutine_movability(self, def_id: Self::CoroutineId) -> rustc_ast_ir::Movability {
1209+
// FIXME: Make this a query? I don't believe this can be accessed from bodies other than
1210+
// the current infer query, except with revealed opaques - is it rare enough to not matter?
1211+
let InternedCoroutine(owner, expr_id) = def_id.0.loc(self.db);
1212+
let body = self.db.body(owner);
1213+
let expr = &body[expr_id];
1214+
match *expr {
1215+
hir_def::hir::Expr::Closure { closure_kind, .. } => match closure_kind {
1216+
hir_def::hir::ClosureKind::Coroutine(movability) => match movability {
1217+
hir_def::hir::Movability::Static => rustc_ast_ir::Movability::Static,
1218+
hir_def::hir::Movability::Movable => rustc_ast_ir::Movability::Movable,
1219+
},
1220+
hir_def::hir::ClosureKind::Async => rustc_ast_ir::Movability::Static,
1221+
_ => panic!("unexpected expression for a coroutine: {expr:?}"),
1222+
},
1223+
hir_def::hir::Expr::Async { .. } => rustc_ast_ir::Movability::Static,
1224+
_ => panic!("unexpected expression for a coroutine: {expr:?}"),
1225+
}
12101226
}
12111227

1212-
fn coroutine_for_closure(self, _def_id: Self::CoroutineId) -> Self::CoroutineId {
1213-
unimplemented!()
1228+
fn coroutine_for_closure(self, def_id: Self::CoroutineClosureId) -> Self::CoroutineId {
1229+
def_id
12141230
}
12151231

12161232
fn generics_require_sized_self(self, def_id: Self::DefId) -> bool {
@@ -1725,23 +1741,39 @@ impl<'db> Interner for DbInterner<'db> {
17251741
panic!("Bug encountered in next-trait-solver: {}", msg.to_string())
17261742
}
17271743

1728-
fn is_general_coroutine(self, _coroutine_def_id: Self::CoroutineId) -> bool {
1729-
// FIXME(next-solver)
1730-
true
1744+
fn is_general_coroutine(self, def_id: Self::CoroutineId) -> bool {
1745+
// FIXME: Make this a query? I don't believe this can be accessed from bodies other than
1746+
// the current infer query, except with revealed opaques - is it rare enough to not matter?
1747+
let InternedCoroutine(owner, expr_id) = def_id.0.loc(self.db);
1748+
let body = self.db.body(owner);
1749+
matches!(
1750+
body[expr_id],
1751+
hir_def::hir::Expr::Closure {
1752+
closure_kind: hir_def::hir::ClosureKind::Coroutine(_),
1753+
..
1754+
}
1755+
)
17311756
}
17321757

1733-
fn coroutine_is_async(self, _coroutine_def_id: Self::CoroutineId) -> bool {
1734-
// FIXME(next-solver)
1735-
true
1758+
fn coroutine_is_async(self, def_id: Self::CoroutineId) -> bool {
1759+
// FIXME: Make this a query? I don't believe this can be accessed from bodies other than
1760+
// the current infer query, except with revealed opaques - is it rare enough to not matter?
1761+
let InternedCoroutine(owner, expr_id) = def_id.0.loc(self.db);
1762+
let body = self.db.body(owner);
1763+
matches!(
1764+
body[expr_id],
1765+
hir_def::hir::Expr::Closure { closure_kind: hir_def::hir::ClosureKind::Async, .. }
1766+
| hir_def::hir::Expr::Async { .. }
1767+
)
17361768
}
17371769

17381770
fn coroutine_is_gen(self, _coroutine_def_id: Self::CoroutineId) -> bool {
1739-
// FIXME(next-solver)
1771+
// We don't handle gen coroutines yet.
17401772
false
17411773
}
17421774

17431775
fn coroutine_is_async_gen(self, _coroutine_def_id: Self::CoroutineId) -> bool {
1744-
// FIXME(next-solver)
1776+
// We don't handle gen coroutines yet.
17451777
false
17461778
}
17471779

@@ -1897,10 +1929,12 @@ impl<'db> Interner for DbInterner<'db> {
18971929
fn coroutine_hidden_types(
18981930
self,
18991931
_def_id: Self::CoroutineId,
1900-
) -> EarlyBinder<Self, rustc_type_ir::Binder<Self, rustc_type_ir::CoroutineWitnessTypes<Self>>>
1901-
{
1902-
// FIXME(next-solver)
1903-
unimplemented!()
1932+
) -> EarlyBinder<Self, Binder<'db, CoroutineWitnessTypes<Self>>> {
1933+
// FIXME: Actually implement this.
1934+
EarlyBinder::bind(Binder::dummy(CoroutineWitnessTypes {
1935+
types: Tys::default(),
1936+
assumptions: RegionAssumptions::default(),
1937+
}))
19041938
}
19051939

19061940
fn is_default_trait(self, def_id: Self::TraitId) -> bool {

0 commit comments

Comments
 (0)