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.rs80
1 files changed, 60 insertions, 20 deletions
diff --git a/crates/hir-def/src/expr_store/lower.rs b/crates/hir-def/src/expr_store/lower.rs
index 4ae4271b92..4fbf6d9517 100644
--- a/crates/hir-def/src/expr_store/lower.rs
+++ b/crates/hir-def/src/expr_store/lower.rs
@@ -47,7 +47,7 @@ use crate::{
hir::{
Array, Binding, BindingAnnotation, BindingId, BindingProblems, CaptureBy, ClosureKind,
Expr, ExprId, Item, Label, LabelId, Literal, MatchArm, Movability, OffsetOf, Pat, PatId,
- RecordFieldPat, RecordLitField, Statement, generics::GenericParams,
+ RecordFieldPat, RecordLitField, RecordSpread, Statement, generics::GenericParams,
},
item_scope::BuiltinShadowMode,
item_tree::FieldsShape,
@@ -150,6 +150,7 @@ pub(super) fn lower_body(
};
let body_expr = collector.collect(
+ &mut params,
body,
if is_async_fn {
Awaitable::Yes
@@ -903,24 +904,57 @@ impl<'db> ExprCollector<'db> {
})
}
- fn collect(&mut self, expr: Option<ast::Expr>, awaitable: Awaitable) -> ExprId {
+ /// 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 {
+ let mut statements = Vec::new();
+ for param in params {
+ let name = match self.store.pats[*param] {
+ Pat::Bind { id, .. }
+ if matches!(
+ self.store.bindings[id].mode,
+ BindingAnnotation::Unannotated | BindingAnnotation::Mutable
+ ) =>
+ {
+ // If this is a direct binding, we can leave it as-is, as it'll always be captured anyway.
+ continue;
+ }
+ 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.generate_new_name(),
+ };
+ let binding_id =
+ self.alloc_binding(name.clone(), BindingAnnotation::Mutable, HygieneId::ROOT);
+ let pat_id = self.alloc_pat_desugared(Pat::Bind { id: binding_id, subpat: None });
+ let expr = self.alloc_expr_desugared(Expr::Path(name.into()));
+ statements.push(Statement::Let {
+ pat: *param,
+ type_ref: None,
+ initializer: Some(expr),
+ else_branch: None,
+ });
+ *param = pat_id;
+ }
+
+ self.alloc_expr_desugared(Expr::Async {
+ id: None,
+ statements: statements.into_boxed_slice(),
+ tail: Some(body),
+ })
+ }
+
+ fn collect(
+ &mut self,
+ params: &mut Vec<PatId>,
+ expr: Option<ast::Expr>,
+ awaitable: Awaitable,
+ ) -> ExprId {
self.awaitable_context.replace(awaitable);
self.with_label_rib(RibKind::Closure, |this| {
- if awaitable == Awaitable::Yes {
- match expr {
- Some(e) => {
- let syntax_ptr = AstPtr::new(&e);
- let expr = this.collect_expr(e);
- this.alloc_expr_desugared_with_ptr(
- Expr::Async { id: None, statements: Box::new([]), tail: Some(expr) },
- syntax_ptr,
- )
- }
- None => this.missing_expr(),
- }
- } else {
- this.collect_expr_opt(expr)
- }
+ let body = this.collect_expr_opt(expr);
+ if awaitable == Awaitable::Yes { this.lower_async_fn(params, body) } else { body }
})
}
@@ -1232,10 +1266,16 @@ impl<'db> ExprCollector<'db> {
Some(RecordLitField { name, expr })
})
.collect();
- let spread = nfl.spread().map(|s| self.collect_expr(s));
+ let spread_expr = nfl.spread().map(|s| self.collect_expr(s));
+ let has_spread_syntax = nfl.dotdot_token().is_some();
+ let spread = match (spread_expr, has_spread_syntax) {
+ (None, false) => RecordSpread::None,
+ (None, true) => RecordSpread::FieldDefaults,
+ (Some(expr), _) => RecordSpread::Expr(expr),
+ };
Expr::RecordLit { path, fields, spread }
} else {
- Expr::RecordLit { path, fields: Box::default(), spread: None }
+ Expr::RecordLit { path, fields: Box::default(), spread: RecordSpread::None }
};
self.alloc_expr(record_lit, syntax_ptr)
@@ -1961,7 +2001,7 @@ impl<'db> ExprCollector<'db> {
}
}
- fn collect_expr_opt(&mut self, expr: Option<ast::Expr>) -> ExprId {
+ pub fn collect_expr_opt(&mut self, expr: Option<ast::Expr>) -> ExprId {
match expr {
Some(expr) => self.collect_expr(expr),
None => self.missing_expr(),