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.rs57
1 files changed, 34 insertions, 23 deletions
diff --git a/crates/hir-def/src/expr_store/lower.rs b/crates/hir-def/src/expr_store/lower.rs
index 5352991d7b..04437a59ac 100644
--- a/crates/hir-def/src/expr_store/lower.rs
+++ b/crates/hir-def/src/expr_store/lower.rs
@@ -945,12 +945,19 @@ impl<'db> ExprCollector<'db> {
})
}
- /// An `async fn` needs to capture all parameters in the generated `async` block, even if they have
- /// non-captured patterns such as wildcards (to ensure consistent drop order).
- fn lower_async_fn(&mut self, params: &mut Vec<PatId>, body: ExprId) -> ExprId {
+ /// Lowers a desugared coroutine body after moving all of the arguments
+ /// 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(
+ &mut self,
+ params: &mut [PatId],
+ body: ExprId,
+ coroutine_source: CoroutineSource,
+ ) -> ExprId {
let mut statements = Vec::new();
for param in params {
- let name = match self.store.pats[*param] {
+ let (name, hygiene) = match self.store.pats[*param] {
Pat::Bind { id, .. }
if matches!(
self.store.bindings[id].mode,
@@ -962,14 +969,16 @@ impl<'db> ExprCollector<'db> {
}
Pat::Bind { id, .. } => {
// If this is a `ref` binding, we can't leave it as is but we can at least reuse the name, for better display.
- self.store.bindings[id].name.clone()
+ (self.store.bindings[id].name.clone(), self.store.bindings[id].hygiene)
}
- _ => self.generate_new_name(),
+ _ => (self.generate_new_name(), HygieneId::ROOT),
};
- let binding_id =
- self.alloc_binding(name.clone(), BindingAnnotation::Mutable, HygieneId::ROOT);
+ let binding_id = self.alloc_binding(name.clone(), BindingAnnotation::Mutable, hygiene);
let pat_id = self.alloc_pat_desugared(Pat::Bind { id: binding_id, subpat: None });
let expr = self.alloc_expr_desugared(Expr::Path(name.into()));
+ if !hygiene.is_root() {
+ self.store.ident_hygiene.insert(expr.into(), hygiene);
+ }
statements.push(Statement::Let {
pat: *param,
type_ref: None,
@@ -980,12 +989,17 @@ impl<'db> ExprCollector<'db> {
}
let async_ = self.async_block(
- CoroutineSource::Fn,
- CaptureBy::Value,
+ 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,
+ // we want to make sure that we avoid unnecessarily moving captures, or else
+ // all async closures would default to `FnOnce` as their calling mode.
+ CaptureBy::Ref,
None,
statements.into_boxed_slice(),
Some(body),
);
+ // It's important that this comes last, see the lowering of async closures for why.
self.alloc_expr_desugared(async_)
}
@@ -1010,14 +1024,18 @@ impl<'db> ExprCollector<'db> {
fn collect(
&mut self,
- params: &mut Vec<PatId>,
+ params: &mut [PatId],
expr: Option<ast::Expr>,
awaitable: Awaitable,
) -> 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_fn(params, body) } else { body }
+ if awaitable == Awaitable::Yes {
+ this.lower_async_block_with_moved_arguments(params, body, CoroutineSource::Fn)
+ } else {
+ body
+ }
})
}
@@ -1450,18 +1468,11 @@ impl<'db> ExprCollector<'db> {
} else if e.async_token().is_some() {
// It's important that this expr is allocated immediately before the closure.
// We rely on it for `coroutine_for_closure()`.
- body = this.alloc_expr_desugared(Expr::Closure {
- args: Box::default(),
- arg_types: Box::default(),
- ret_type: None,
+ body = this.lower_async_block_with_moved_arguments(
+ &mut args,
body,
- closure_kind: ClosureKind::AsyncBlock {
- source: CoroutineSource::Closure,
- },
- // The block may need to capture by move, but we cannot know it now.
- // It will be fixed in capture analysis.
- capture_by: CaptureBy::Ref,
- });
+ CoroutineSource::Closure,
+ );
body_is_bindings_owner = true;
ClosureKind::AsyncClosure