Unnamed repository; edit this file 'description' to name the repository.
Lower coroutine closure arguments like rustc
This is important since we rely on this for closure analysis.
Chayim Refael Friedman 3 weeks ago
parent 6b43f30 · commit 5c0bf3a
-rw-r--r--crates/hir-def/src/expr_store/lower.rs68
-rw-r--r--crates/hir-def/src/expr_store/tests/body.rs8
2 files changed, 64 insertions, 12 deletions
diff --git a/crates/hir-def/src/expr_store/lower.rs b/crates/hir-def/src/expr_store/lower.rs
index 5c8e87c0e3..611cc511dd 100644
--- a/crates/hir-def/src/expr_store/lower.rs
+++ b/crates/hir-def/src/expr_store/lower.rs
@@ -968,37 +968,83 @@ impl<'db> ExprCollector<'db> {
kind: CoroutineKind,
coroutine_source: CoroutineSource,
) -> ExprId {
+ // Async function parameters are lowered into the closure body so that they are
+ // captured and so that the drop order matches the equivalent non-async functions.
+ //
+ // from:
+ //
+ // async fn foo(<pattern>: <ty>, <pattern>: <ty>, <pattern>: <ty>) {
+ // <body>
+ // }
+ //
+ // into:
+ //
+ // fn foo(__arg0: <ty>, __arg1: <ty>, __arg2: <ty>) {
+ // async move {
+ // let __arg2 = __arg2;
+ // let <pattern> = __arg2;
+ // let __arg1 = __arg1;
+ // let <pattern> = __arg1;
+ // let __arg0 = __arg0;
+ // let <pattern> = __arg0;
+ // drop-temps { <body> } // see comments later in fn for details
+ // }
+ // }
+ //
+ // If `<pattern>` is a simple ident, then it is lowered to a single
+ // `let <pattern> = <pattern>;` statement as an optimization.
+
let mut statements = Vec::new();
for param in params {
- let (name, hygiene) = match self.store.pats[*param] {
- Pat::Bind { id, .. }
+ let (name, hygiene, is_simple_parameter) = match self.store.pats[*param] {
+ // Check if this is a binding pattern, if so, we can optimize and avoid adding a
+ // `let <pat> = __argN;` statement. In this case, we do not rename the parameter.
+ Pat::Bind { id, subpat: None, .. }
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;
+ (self.store.bindings[id].name.clone(), self.store.bindings[id].hygiene, true)
}
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].hygiene)
+ (self.store.bindings[id].name.clone(), self.store.bindings[id].hygiene, false)
}
- _ => (self.generate_new_name(), HygieneId::ROOT),
+ _ => (self.generate_new_name(), HygieneId::ROOT, false),
};
- 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()));
+ let child_binding_id =
+ self.alloc_binding(name.clone(), BindingAnnotation::Mutable, hygiene);
+ let child_pat_id =
+ self.alloc_pat_desugared(Pat::Bind { id: child_binding_id, subpat: None });
+ let expr = self.alloc_expr_desugared(Expr::Path(name.clone().into()));
if !hygiene.is_root() {
self.store.ident_hygiene.insert(expr.into(), hygiene);
}
statements.push(Statement::Let {
- pat: *param,
+ pat: child_pat_id,
type_ref: None,
initializer: Some(expr),
else_branch: None,
});
- *param = pat_id;
+ if !is_simple_parameter {
+ let expr = self.alloc_expr_desugared(Expr::Path(name.clone().into()));
+ if !hygiene.is_root() {
+ self.store.ident_hygiene.insert(expr.into(), hygiene);
+ }
+ statements.push(Statement::Let {
+ pat: *param,
+ type_ref: None,
+ initializer: Some(expr),
+ else_branch: None,
+ });
+
+ let parent_binding_id =
+ self.alloc_binding(name.clone(), BindingAnnotation::Mutable, hygiene);
+ let parent_pat_id =
+ self.alloc_pat_desugared(Pat::Bind { id: parent_binding_id, subpat: None });
+ *param = parent_pat_id;
+ }
}
let coroutine = self.desugared_coroutine_expr(
diff --git a/crates/hir-def/src/expr_store/tests/body.rs b/crates/hir-def/src/expr_store/tests/body.rs
index db12775df9..9727d87cf0 100644
--- a/crates/hir-def/src/expr_store/tests/body.rs
+++ b/crates/hir-def/src/expr_store/tests/body.rs
@@ -652,9 +652,15 @@ fn async_fn_weird_param_patterns() {
async fn main(&self, param1: i32, ref mut param2: i32, _: i32, param4 @ _: i32, 123: i32) {}
"#,
expect![[r#"
- fn main(self, param1, mut param2, mut <ra@gennew>0, param4 @ _, mut <ra@gennew>1) async {
+ fn main(self, param1, mut param2, mut <ra@gennew>0, mut param4, mut <ra@gennew>1) async {
+ let mut param1 = param1;
+ let mut param2 = param2;
let ref mut param2 = param2;
+ let mut <ra@gennew>0 = <ra@gennew>0;
let _ = <ra@gennew>0;
+ let mut param4 = param4;
+ let param4 @ _ = param4;
+ let mut <ra@gennew>1 = <ra@gennew>1;
let 123 = <ra@gennew>1;
{}
}"#]],