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
| -rw-r--r-- | crates/hir-def/src/expr_store/lower.rs | 66 | ||||
| -rw-r--r-- | crates/hir-def/src/expr_store/tests/body.rs | 18 | ||||
| -rw-r--r-- | crates/hir-ty/src/tests/regression.rs | 1 | ||||
| -rw-r--r-- | crates/hir-ty/src/tests/simple.rs | 1 | ||||
| -rw-r--r-- | crates/hir-ty/src/tests/traits.rs | 3 |
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 |