Unnamed repository; edit this file 'description' to name the repository.
Merge pull request #21492 from ChayimFriedman2/async-fn
fix: Ensure correct capturing of async fn params even when they use weird patterns
Shoyu Vanilla (Flint) 3 months ago
parent 1b8952b · parent 6a76596 · commit 66463ea
-rw-r--r--crates/hir-def/src/expr_store/lower.rs66
-rw-r--r--crates/hir-def/src/expr_store/tests/body.rs18
-rw-r--r--crates/hir-ty/src/tests/regression.rs1
-rw-r--r--crates/hir-ty/src/tests/simple.rs1
-rw-r--r--crates/hir-ty/src/tests/traits.rs3
5 files changed, 68 insertions, 21 deletions
diff --git a/crates/hir-def/src/expr_store/lower.rs b/crates/hir-def/src/expr_store/lower.rs
index 4ae4271b92..7922261592 100644
--- a/crates/hir-def/src/expr_store/lower.rs
+++ b/crates/hir-def/src/expr_store/lower.rs
@@ -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 }
})
}
diff --git a/crates/hir-def/src/expr_store/tests/body.rs b/crates/hir-def/src/expr_store/tests/body.rs
index 504c310684..8f857aeeff 100644
--- a/crates/hir-def/src/expr_store/tests/body.rs
+++ b/crates/hir-def/src/expr_store/tests/body.rs
@@ -659,3 +659,21 @@ fn main() {
}"#]]
.assert_eq(&body.pretty_print(&db, def, Edition::CURRENT))
}
+
+#[test]
+fn async_fn_weird_param_patterns() {
+ let (db, body, def) = lower(
+ r#"
+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 {
+ let ref mut param2 = param2;
+ let _ = <ra@gennew>0;
+ let 123 = <ra@gennew>1;
+ {}
+ }"#]]
+ .assert_eq(&body.pretty_print(&db, def, Edition::CURRENT))
+}
diff --git a/crates/hir-ty/src/tests/regression.rs b/crates/hir-ty/src/tests/regression.rs
index a04c46f8ea..4f1480c393 100644
--- a/crates/hir-ty/src/tests/regression.rs
+++ b/crates/hir-ty/src/tests/regression.rs
@@ -2235,7 +2235,6 @@ async fn f<A, B, C>() -> Bar {}
"#,
expect![[r#"
64..66 '{}': ()
- 64..66 '{}': impl Future<Output = ()>
"#]],
);
}
diff --git a/crates/hir-ty/src/tests/simple.rs b/crates/hir-ty/src/tests/simple.rs
index d2a4149bc6..44450939a6 100644
--- a/crates/hir-ty/src/tests/simple.rs
+++ b/crates/hir-ty/src/tests/simple.rs
@@ -2139,7 +2139,6 @@ async fn main() {
"#,
expect![[r#"
16..193 '{ ...2 }; }': ()
- 16..193 '{ ...2 }; }': impl Future<Output = ()>
26..27 'x': i32
30..43 'unsafe { 92 }': i32
39..41 '92': i32
diff --git a/crates/hir-ty/src/tests/traits.rs b/crates/hir-ty/src/tests/traits.rs
index b825a0a8f0..390553c0d7 100644
--- a/crates/hir-ty/src/tests/traits.rs
+++ b/crates/hir-ty/src/tests/traits.rs
@@ -4869,7 +4869,6 @@ async fn baz<T: AsyncFnOnce(u32) -> i32>(c: T) {
expect![[r#"
37..38 'a': T
43..83 '{ ...ait; }': ()
- 43..83 '{ ...ait; }': impl Future<Output = ()>
53..57 'fut1': <T as AsyncFnMut<(u32,)>>::CallRefFuture<'?>
60..61 'a': T
60..64 'a(0)': <T as AsyncFnMut<(u32,)>>::CallRefFuture<'?>
@@ -4878,7 +4877,6 @@ async fn baz<T: AsyncFnOnce(u32) -> i32>(c: T) {
70..80 'fut1.await': i32
124..129 'mut b': T
134..174 '{ ...ait; }': ()
- 134..174 '{ ...ait; }': impl Future<Output = ()>
144..148 'fut2': <T as AsyncFnMut<(u32,)>>::CallRefFuture<'?>
151..152 'b': T
151..155 'b(0)': <T as AsyncFnMut<(u32,)>>::CallRefFuture<'?>
@@ -4887,7 +4885,6 @@ async fn baz<T: AsyncFnOnce(u32) -> i32>(c: T) {
161..171 'fut2.await': i32
216..217 'c': T
222..262 '{ ...ait; }': ()
- 222..262 '{ ...ait; }': impl Future<Output = ()>
232..236 'fut3': <T as AsyncFnOnce<(u32,)>>::CallOnceFuture
239..240 'c': T
239..243 'c(0)': <T as AsyncFnOnce<(u32,)>>::CallOnceFuture