Skip to content

Commit 0d03fe6

Browse files
bors[bot]oxalica
andauthored
Merge #5971
5971: Implement async blocks r=flodiebold a=oxalica Fix #4018 @flodiebold already gave a generic guide in the issue. Here's some concern about implementation detail: - Chalk doesn't support generator type yet. - Adding generator type as a brand new type (ctor) can be complex and need to *re-introduced* builtin impls. (Like how we implement closures before native closure support of chalk, which is already removed in #5401 ) - The output type of async block should be known after type inference of the whole body. - We cannot directly get the type from source like return-positon-impl-trait. But we still need to provide trait bounds when chalk asking for `opaque_ty_data`. - During the inference, the output type of async block can be temporary unknown and participate the later inference. `let a = async { None }; let _: i32 = a.await.unwrap();` So in this PR, the type of async blocks is inferred as an opaque type parameterized by the `Future::Output` type it should be, like what we do with closure type. And it really works now. Well, I still have some questions: - The bounds `AsyncBlockImplType<T>: Future<Output = T>` is currently generated in `opaque_ty_data`. I'm not sure if we should put this code here. - Type of async block is now rendered as `impl Future<Output = OutputType>`. Do we need to special display to hint that it's a async block? Note that closure type has its special format, instead of `impl Fn(..) -> ..` or function type. Co-authored-by: oxalica <[email protected]>
2 parents fda6937 + 529c369 commit 0d03fe6

File tree

12 files changed

+314
-55
lines changed

12 files changed

+314
-55
lines changed

crates/hir/src/code_model.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1283,6 +1283,8 @@ impl Type {
12831283
/// Checks that particular type `ty` implements `std::future::Future`.
12841284
/// This function is used in `.await` syntax completion.
12851285
pub fn impls_future(&self, db: &dyn HirDatabase) -> bool {
1286+
// No special case for the type of async block, since Chalk can figure it out.
1287+
12861288
let krate = self.krate;
12871289

12881290
let std_future_trait =
@@ -1600,6 +1602,11 @@ impl Type {
16001602
cb(type_.derived(ty.clone()));
16011603
}
16021604
}
1605+
TypeCtor::OpaqueType(..) => {
1606+
if let Some(bounds) = ty.impl_trait_bounds(db) {
1607+
walk_bounds(db, &type_.derived(ty.clone()), &bounds, cb);
1608+
}
1609+
}
16031610
_ => (),
16041611
}
16051612

crates/hir_def/src/body/lower.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,10 @@ impl ExprCollector<'_> {
239239
None => self.missing_expr(),
240240
},
241241
// FIXME: we need to record these effects somewhere...
242-
ast::Effect::Async(_) => self.collect_block_opt(e.block_expr()),
242+
ast::Effect::Async(_) => {
243+
let body = self.collect_block_opt(e.block_expr());
244+
self.alloc_expr(Expr::Async { body }, syntax_ptr)
245+
}
243246
},
244247
ast::Expr::BlockExpr(e) => self.collect_block(e),
245248
ast::Expr::LoopExpr(e) => {

crates/hir_def/src/expr.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,9 @@ pub enum Expr {
111111
TryBlock {
112112
body: ExprId,
113113
},
114+
Async {
115+
body: ExprId,
116+
},
114117
Cast {
115118
expr: ExprId,
116119
type_ref: TypeRef,
@@ -250,7 +253,7 @@ impl Expr {
250253
f(*expr);
251254
}
252255
}
253-
Expr::TryBlock { body } | Expr::Unsafe { body } => f(*body),
256+
Expr::TryBlock { body } | Expr::Unsafe { body } | Expr::Async { body } => f(*body),
254257
Expr::Loop { body, .. } => f(*body),
255258
Expr::While { condition, body, .. } => {
256259
f(*condition);

crates/hir_ty/src/display.rs

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -381,19 +381,24 @@ impl HirDisplay for ApplicationTy {
381381
}
382382
}
383383
TypeCtor::OpaqueType(opaque_ty_id) => {
384-
let bounds = match opaque_ty_id {
384+
match opaque_ty_id {
385385
OpaqueTyId::ReturnTypeImplTrait(func, idx) => {
386386
let datas =
387387
f.db.return_type_impl_traits(func).expect("impl trait id without data");
388388
let data = (*datas)
389389
.as_ref()
390390
.map(|rpit| rpit.impl_traits[idx as usize].bounds.clone());
391-
data.subst(&self.parameters)
391+
let bounds = data.subst(&self.parameters);
392+
write!(f, "impl ")?;
393+
write_bounds_like_dyn_trait(&bounds.value, f)?;
394+
// FIXME: it would maybe be good to distinguish this from the alias type (when debug printing), and to show the substitution
392395
}
393-
};
394-
write!(f, "impl ")?;
395-
write_bounds_like_dyn_trait(&bounds.value, f)?;
396-
// FIXME: it would maybe be good to distinguish this from the alias type (when debug printing), and to show the substitution
396+
OpaqueTyId::AsyncBlockTypeImplTrait(..) => {
397+
write!(f, "impl Future<Output = ")?;
398+
self.parameters[0].hir_fmt(f)?;
399+
write!(f, ">")?;
400+
}
401+
}
397402
}
398403
TypeCtor::Closure { .. } => {
399404
let sig = self.parameters[0].callable_sig(f.db);
@@ -474,18 +479,21 @@ impl HirDisplay for Ty {
474479
write_bounds_like_dyn_trait(predicates, f)?;
475480
}
476481
Ty::Opaque(opaque_ty) => {
477-
let bounds = match opaque_ty.opaque_ty_id {
482+
match opaque_ty.opaque_ty_id {
478483
OpaqueTyId::ReturnTypeImplTrait(func, idx) => {
479484
let datas =
480485
f.db.return_type_impl_traits(func).expect("impl trait id without data");
481486
let data = (*datas)
482487
.as_ref()
483488
.map(|rpit| rpit.impl_traits[idx as usize].bounds.clone());
484-
data.subst(&opaque_ty.parameters)
489+
let bounds = data.subst(&opaque_ty.parameters);
490+
write!(f, "impl ")?;
491+
write_bounds_like_dyn_trait(&bounds.value, f)?;
492+
}
493+
OpaqueTyId::AsyncBlockTypeImplTrait(..) => {
494+
write!(f, "{{async block}}")?;
485495
}
486496
};
487-
write!(f, "impl ")?;
488-
write_bounds_like_dyn_trait(&bounds.value, f)?;
489497
}
490498
Ty::Unknown => write!(f, "{{unknown}}")?,
491499
Ty::Infer(..) => write!(f, "_")?,

crates/hir_ty/src/infer/expr.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ use crate::{
1717
autoderef, method_resolution, op,
1818
traits::{FnTrait, InEnvironment},
1919
utils::{generics, variant_data, Generics},
20-
ApplicationTy, Binders, CallableDefId, InferTy, IntTy, Mutability, Obligation, Rawness, Substs,
21-
TraitRef, Ty, TypeCtor,
20+
ApplicationTy, Binders, CallableDefId, InferTy, IntTy, Mutability, Obligation, OpaqueTyId,
21+
Rawness, Substs, TraitRef, Ty, TypeCtor,
2222
};
2323

2424
use super::{
@@ -146,6 +146,13 @@ impl<'a> InferenceContext<'a> {
146146
// FIXME should be std::result::Result<{inner}, _>
147147
Ty::Unknown
148148
}
149+
Expr::Async { body } => {
150+
// Use the first type parameter as the output type of future.
151+
// existenail type AsyncBlockImplTrait<InnerType>: Future<Output = InnerType>
152+
let inner_ty = self.infer_expr(*body, &Expectation::none());
153+
let opaque_ty_id = OpaqueTyId::AsyncBlockTypeImplTrait(self.owner, *body);
154+
Ty::apply_one(TypeCtor::OpaqueType(opaque_ty_id), inner_ty)
155+
}
149156
Expr::Loop { body, label } => {
150157
self.breakables.push(BreakableContext {
151158
may_break: false,

crates/hir_ty/src/lib.rs

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,8 +129,9 @@ pub enum TypeCtor {
129129

130130
/// This represents a placeholder for an opaque type in situations where we
131131
/// don't know the hidden type (i.e. currently almost always). This is
132-
/// analogous to the `AssociatedType` type constructor. As with that one,
133-
/// these are only produced by Chalk.
132+
/// analogous to the `AssociatedType` type constructor.
133+
/// It is also used as the type of async block, with one type parameter
134+
/// representing the Future::Output type.
134135
OpaqueType(OpaqueTyId),
135136

136137
/// The type of a specific closure.
@@ -173,6 +174,8 @@ impl TypeCtor {
173174
let generic_params = generics(db.upcast(), func.into());
174175
generic_params.len()
175176
}
177+
// 1 param representing Future::Output type.
178+
OpaqueTyId::AsyncBlockTypeImplTrait(..) => 1,
176179
}
177180
}
178181
TypeCtor::FnPtr { num_args, is_varargs: _ } => num_args as usize + 1,
@@ -205,6 +208,7 @@ impl TypeCtor {
205208
OpaqueTyId::ReturnTypeImplTrait(func, _) => {
206209
Some(func.lookup(db.upcast()).module(db.upcast()).krate)
207210
}
211+
OpaqueTyId::AsyncBlockTypeImplTrait(def, _) => Some(def.module(db.upcast()).krate),
208212
},
209213
}
210214
}
@@ -843,6 +847,29 @@ impl Ty {
843847

844848
pub fn impl_trait_bounds(&self, db: &dyn HirDatabase) -> Option<Vec<GenericPredicate>> {
845849
match self {
850+
Ty::Apply(ApplicationTy { ctor: TypeCtor::OpaqueType(opaque_ty_id), .. }) => {
851+
match opaque_ty_id {
852+
OpaqueTyId::AsyncBlockTypeImplTrait(def, _expr) => {
853+
let krate = def.module(db.upcast()).krate;
854+
if let Some(future_trait) = db
855+
.lang_item(krate, "future_trait".into())
856+
.and_then(|item| item.as_trait())
857+
{
858+
// This is only used by type walking.
859+
// Parameters will be walked outside, and projection predicate is not used.
860+
// So just provide the Future trait.
861+
let impl_bound = GenericPredicate::Implemented(TraitRef {
862+
trait_: future_trait,
863+
substs: Substs::empty(),
864+
});
865+
Some(vec![impl_bound])
866+
} else {
867+
None
868+
}
869+
}
870+
OpaqueTyId::ReturnTypeImplTrait(..) => None,
871+
}
872+
}
846873
Ty::Opaque(opaque_ty) => {
847874
let predicates = match opaque_ty.opaque_ty_id {
848875
OpaqueTyId::ReturnTypeImplTrait(func, idx) => {
@@ -853,6 +880,8 @@ impl Ty {
853880
data.subst(&opaque_ty.parameters)
854881
})
855882
}
883+
// It always has an parameter for Future::Output type.
884+
OpaqueTyId::AsyncBlockTypeImplTrait(..) => unreachable!(),
856885
};
857886

858887
predicates.map(|it| it.value)
@@ -1065,6 +1094,7 @@ impl<T: TypeWalk> TypeWalk for Vec<T> {
10651094
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
10661095
pub enum OpaqueTyId {
10671096
ReturnTypeImplTrait(hir_def::FunctionId, u16),
1097+
AsyncBlockTypeImplTrait(hir_def::DefWithBodyId, ExprId),
10681098
}
10691099

10701100
#[derive(Clone, PartialEq, Eq, Debug, Hash)]

crates/hir_ty/src/tests/simple.rs

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1889,31 +1889,40 @@ fn fn_pointer_return() {
18891889
fn effects_smoke_test() {
18901890
check_infer(
18911891
r#"
1892-
fn main() {
1892+
async fn main() {
18931893
let x = unsafe { 92 };
18941894
let y = async { async { () }.await };
18951895
let z = try { () };
18961896
let t = 'a: { 92 };
18971897
}
1898+
1899+
#[prelude_import] use future::*;
1900+
1901+
mod future {
1902+
#[lang = "future_trait"]
1903+
pub trait Future { type Output; }
1904+
}
18981905
"#,
18991906
expect![[r#"
1900-
10..130 '{ ...2 }; }': ()
1901-
20..21 'x': i32
1902-
24..37 'unsafe { 92 }': i32
1903-
31..37 '{ 92 }': i32
1904-
33..35 '92': i32
1905-
47..48 'y': {unknown}
1906-
57..79 '{ asyn...wait }': {unknown}
1907-
59..77 'async ....await': {unknown}
1908-
65..71 '{ () }': ()
1909-
67..69 '()': ()
1910-
89..90 'z': {unknown}
1911-
93..103 'try { () }': {unknown}
1912-
97..103 '{ () }': ()
1913-
99..101 '()': ()
1914-
113..114 't': i32
1915-
121..127 '{ 92 }': i32
1916-
123..125 '92': i32
1907+
16..136 '{ ...2 }; }': ()
1908+
26..27 'x': i32
1909+
30..43 'unsafe { 92 }': i32
1910+
37..43 '{ 92 }': i32
1911+
39..41 '92': i32
1912+
53..54 'y': impl Future<Output = ()>
1913+
57..85 'async ...wait }': impl Future<Output = ()>
1914+
63..85 '{ asyn...wait }': ()
1915+
65..77 'async { () }': impl Future<Output = ()>
1916+
65..83 'async ....await': ()
1917+
71..77 '{ () }': ()
1918+
73..75 '()': ()
1919+
95..96 'z': {unknown}
1920+
99..109 'try { () }': {unknown}
1921+
103..109 '{ () }': ()
1922+
105..107 '()': ()
1923+
119..120 't': i32
1924+
127..133 '{ 92 }': i32
1925+
129..131 '92': i32
19171926
"#]],
19181927
)
19191928
}

crates/hir_ty/src/tests/traits.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,46 @@ mod future {
8585
);
8686
}
8787

88+
#[test]
89+
fn infer_async_block() {
90+
check_types(
91+
r#"
92+
//- /main.rs crate:main deps:core
93+
async fn test() {
94+
let a = async { 42 };
95+
a;
96+
// ^ impl Future<Output = i32>
97+
let x = a.await;
98+
x;
99+
// ^ i32
100+
let b = async {}.await;
101+
b;
102+
// ^ ()
103+
let c = async {
104+
let y = Option::None;
105+
y
106+
// ^ Option<u64>
107+
};
108+
let _: Option<u64> = c.await;
109+
c;
110+
// ^ impl Future<Output = Option<u64>>
111+
}
112+
113+
enum Option<T> { None, Some(T) }
114+
115+
//- /core.rs crate:core
116+
#[prelude_import] use future::*;
117+
mod future {
118+
#[lang = "future_trait"]
119+
trait Future {
120+
type Output;
121+
}
122+
}
123+
124+
"#,
125+
);
126+
}
127+
88128
#[test]
89129
fn infer_try() {
90130
check_types(

0 commit comments

Comments
 (0)