Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-def/src/expr_store/lower.rs')
| -rw-r--r-- | crates/hir-def/src/expr_store/lower.rs | 125 |
1 files changed, 94 insertions, 31 deletions
diff --git a/crates/hir-def/src/expr_store/lower.rs b/crates/hir-def/src/expr_store/lower.rs index fd8b50d714..5c8e87c0e3 100644 --- a/crates/hir-def/src/expr_store/lower.rs +++ b/crates/hir-def/src/expr_store/lower.rs @@ -47,8 +47,8 @@ use crate::{ }, hir::{ Array, Binding, BindingAnnotation, BindingId, BindingProblems, CaptureBy, ClosureKind, - CoroutineSource, Expr, ExprId, Item, Label, LabelId, Literal, MatchArm, Movability, - OffsetOf, Pat, PatId, RecordFieldPat, RecordLitField, RecordSpread, Statement, + CoroutineKind, CoroutineSource, Expr, ExprId, Item, Label, LabelId, Literal, MatchArm, + Movability, OffsetOf, Pat, PatId, RecordFieldPat, RecordLitField, RecordSpread, Statement, generics::GenericParams, }, item_scope::BuiltinShadowMode, @@ -72,6 +72,7 @@ pub(super) fn lower_body( parameters: Option<ast::ParamList>, body: Option<ast::Expr>, is_async_fn: bool, + is_gen_fn: bool, ) -> (Body, BodySourceMap) { // We cannot leave the root span map empty and let any identifier from it be treated as root, // because when inside nested macros `SyntaxContextId`s from the outer macro will be interleaved @@ -176,6 +177,8 @@ pub(super) fn lower_body( DefWithBodyId::VariantId(..) => Awaitable::No("enum variant"), } }, + is_async_fn, + is_gen_fn, ); collector.store.inference_roots = Some(smallvec![(body_expr, RootExprOrigin::BodyRoot)]); @@ -376,12 +379,20 @@ pub(crate) fn lower_function( expr_collector.lower_type_ref_opt(ret_type.ty(), &mut ExprCollector::impl_trait_allocator) }); - let return_type = if fn_.value.async_token().is_some() { - let path = hir_expand::mod_path::path![core::future::Future]; + let return_type = if fn_.value.async_token().is_some() || fn_.value.gen_token().is_some() { + let (path, assoc_name) = + match (fn_.value.async_token().is_some(), fn_.value.gen_token().is_some()) { + (true, true) => { + (hir_expand::mod_path::path![core::async_iter::AsyncIterator], sym::Item) + } + (true, false) => (hir_expand::mod_path::path![core::future::Future], sym::Output), + (false, true) => (hir_expand::mod_path::path![core::iter::Iterator], sym::Item), + (false, false) => unreachable!(), + }; let mut generic_args: Vec<_> = std::iter::repeat_n(None, path.segments().len() - 1).collect(); let binding = AssociatedTypeBinding { - name: Name::new_symbol_root(sym::Output), + name: Name::new_symbol_root(assoc_name), args: None, type_ref: Some( return_type @@ -950,10 +961,11 @@ impl<'db> ExprCollector<'db> { /// into the body. This is to make sure that the future actually owns the /// arguments that are passed to the function, and to ensure things like /// drop order are stable. - fn lower_async_block_with_moved_arguments( + fn lower_coroutine_with_moved_arguments( &mut self, params: &mut [PatId], body: ExprId, + kind: CoroutineKind, coroutine_source: CoroutineSource, ) -> ExprId { let mut statements = Vec::new(); @@ -989,7 +1001,8 @@ impl<'db> ExprCollector<'db> { *param = pat_id; } - let async_ = self.async_block( + let coroutine = self.desugared_coroutine_expr( + kind, coroutine_source, // The default capture mode here is by-ref. Later on during upvar analysis, // we will force the captured arguments to by-move, but for async closures, @@ -1001,11 +1014,12 @@ impl<'db> ExprCollector<'db> { Some(body), ); // It's important that this comes last, see the lowering of async closures for why. - self.alloc_expr_desugared(async_) + self.alloc_expr_desugared(coroutine) } - fn async_block( + fn desugared_coroutine_expr( &mut self, + kind: CoroutineKind, source: CoroutineSource, capture_by: CaptureBy, id: Option<BlockId>, @@ -1018,7 +1032,7 @@ impl<'db> ExprCollector<'db> { arg_types: Box::default(), ret_type: None, body: block, - closure_kind: ClosureKind::AsyncBlock { source }, + closure_kind: ClosureKind::Coroutine { kind, source }, capture_by, } } @@ -1028,12 +1042,20 @@ impl<'db> ExprCollector<'db> { params: &mut [PatId], expr: Option<ast::Expr>, awaitable: Awaitable, + is_async_fn: bool, + is_gen_fn: bool, ) -> ExprId { self.awaitable_context.replace(awaitable); self.with_label_rib(RibKind::Closure, |this| { let body = this.collect_expr_opt(expr); - if awaitable == Awaitable::Yes { - this.lower_async_block_with_moved_arguments(params, body, CoroutineSource::Fn) + if is_async_fn || is_gen_fn { + let kind = match (is_async_fn, is_gen_fn) { + (true, true) => CoroutineKind::AsyncGen, + (true, false) => CoroutineKind::Async, + (false, true) => CoroutineKind::Gen, + (false, false) => unreachable!(), + }; + this.lower_coroutine_with_moved_arguments(params, body, kind, CoroutineSource::Fn) } else { body } @@ -1192,7 +1214,44 @@ impl<'db> ExprCollector<'db> { self.with_label_rib(RibKind::Closure, |this| { this.with_awaitable_block(Awaitable::Yes, |this| { this.collect_block_(e, |this, id, statements, tail| { - this.async_block( + this.desugared_coroutine_expr( + CoroutineKind::Async, + CoroutineSource::Block, + capture_by, + id, + statements, + tail, + ) + }) + }) + }) + } + Some(ast::BlockModifier::Gen(_)) => { + let capture_by = + if e.move_token().is_some() { CaptureBy::Value } else { CaptureBy::Ref }; + self.with_label_rib(RibKind::Closure, |this| { + this.with_awaitable_block(Awaitable::No("non-async gen block"), |this| { + this.collect_block_(e, |this, id, statements, tail| { + this.desugared_coroutine_expr( + CoroutineKind::Gen, + CoroutineSource::Block, + capture_by, + id, + statements, + tail, + ) + }) + }) + }) + } + Some(ast::BlockModifier::AsyncGen(_)) => { + let capture_by = + if e.move_token().is_some() { CaptureBy::Value } else { CaptureBy::Ref }; + self.with_label_rib(RibKind::Closure, |this| { + this.with_awaitable_block(Awaitable::Yes, |this| { + this.collect_block_(e, |this, id, statements, tail| { + this.desugared_coroutine_expr( + CoroutineKind::AsyncGen, CoroutineSource::Block, capture_by, id, @@ -1213,14 +1272,6 @@ impl<'db> ExprCollector<'db> { }) }) } - // FIXME - Some(ast::BlockModifier::AsyncGen(_)) => { - self.with_awaitable_block(Awaitable::Yes, |this| this.collect_block(e)) - } - Some(ast::BlockModifier::Gen(_)) => self - .with_awaitable_block(Awaitable::No("non-async gen block"), |this| { - this.collect_block(e) - }), None => self.collect_block(e), }, ast::Expr::LoopExpr(e) => { @@ -1460,25 +1511,37 @@ impl<'db> ExprCollector<'db> { }; let mut body = this .with_awaitable_block(awaitable, |this| this.collect_expr_opt(e.body())); - - let closure_kind = if this.is_lowering_coroutine { - let movability = if e.static_token().is_some() { - Movability::Static + let kind = { + if e.async_token().is_some() && e.gen_token().is_some() { + Some(CoroutineKind::AsyncGen) + } else if e.async_token().is_some() { + Some(CoroutineKind::Async) + } else if e.gen_token().is_some() { + Some(CoroutineKind::Gen) } else { - Movability::Movable - }; - ClosureKind::Coroutine(movability) - } else if e.async_token().is_some() { + None + } + }; + + let closure_kind = if let Some(kind) = kind { // It's important that this expr is allocated immediately before the closure. // We rely on it for `coroutine_for_closure()`. - body = this.lower_async_block_with_moved_arguments( + body = this.lower_coroutine_with_moved_arguments( &mut args, body, + kind, CoroutineSource::Closure, ); body_is_bindings_owner = true; - ClosureKind::AsyncClosure + ClosureKind::CoroutineClosure(kind) + } else if this.is_lowering_coroutine { + let movability = if e.static_token().is_some() { + Movability::Static + } else { + Movability::Movable + }; + ClosureKind::OldCoroutine(movability) } else { ClosureKind::Closure }; |