From e172dd27fd454645f90d5f4c8e98c1dab0f54924 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 16 Jul 2024 15:30:32 -0400 Subject: [PATCH 1/5] Rewrite closure-of-async to async-closure --- compiler/rustc_ast/src/ast.rs | 30 +++++++++- compiler/rustc_ast_lowering/src/expr.rs | 59 +++++++++++++++---- .../async-closures/wrong-fn-kind.stderr | 26 +------- 3 files changed, 76 insertions(+), 39 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index fc1af3fc3dd11..33f77b4bab67f 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -1214,6 +1214,30 @@ impl Expr { } } + pub fn peel_uwu(&self) -> &Expr { + let mut expr = self; + loop { + match &expr.kind { + ExprKind::Block(blk, None) => { + if blk.stmts.len() == 1 + && let StmtKind::Expr(blk) = &blk.stmts[0].kind + { + expr = blk; + } else { + break; + } + } + ExprKind::Paren(paren) => { + expr = paren; + } + _ => { + break; + } + } + } + expr + } + pub fn peel_parens(&self) -> &Expr { let mut expr = self; while let ExprKind::Paren(inner) = &expr.kind { @@ -1614,15 +1638,15 @@ pub struct QSelf { } /// A capture clause used in closures and `async` blocks. -#[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)] +#[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug, HashStable_Generic, Ord, PartialOrd)] pub enum CaptureBy { + /// `move` keyword was not specified. + Ref, /// `move |x| y + x`. Value { /// The span of the `move` keyword. move_kw: Span, }, - /// `move` keyword was not specified. - Ref, } /// Closure lifetime binder, `for<'a, 'b>` in `for<'a, 'b> |_: &'a (), _: &'b ()|`. diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 124fe6bd380d2..4f1107b27a31c 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -219,18 +219,53 @@ impl<'hir> LoweringContext<'_, 'hir> { *fn_decl_span, *fn_arg_span, ), - None => self.lower_expr_closure( - binder, - *capture_clause, - e.id, - hir_id, - *constness, - *movability, - fn_decl, - body, - *fn_decl_span, - *fn_arg_span, - ), + None => { + let peeled = body.peel_uwu(); + if let ast::ExprKind::Gen( + gen_capture_clause, + block, + gen_kind @ ast::GenBlockKind::Async, + span, + ) = &peeled.kind + { + let coroutine_kind = match gen_kind { + GenBlockKind::Async => CoroutineKind::Async { span: *span, closure_id: peeled.node_id(), return_impl_trait_id: self.next_node_id() }, + GenBlockKind::Gen => CoroutineKind::Gen { span: *span, closure_id: peeled.node_id(), return_impl_trait_id: self.next_node_id() }, + GenBlockKind::AsyncGen => CoroutineKind::AsyncGen { span: *span, closure_id: peeled.node_id(), return_impl_trait_id: self.next_node_id() }, + }; + let id = self.next_node_id(); + self.lower_expr_coroutine_closure( + binder, + capture_clause.max(*gen_capture_clause), + e.id, + hir_id, + coroutine_kind, + fn_decl, + &ast::Expr { + id, + span: *span, + kind: ExprKind::Block(block.clone(), None), + attrs: thin_vec![], + tokens: None, + }, + *fn_decl_span, + *fn_arg_span, + ) + } else { + self.lower_expr_closure( + binder, + *capture_clause, + e.id, + hir_id, + *constness, + *movability, + fn_decl, + body, + *fn_decl_span, + *fn_arg_span, + ) + } + } }, ExprKind::Gen(capture_clause, block, genblock_kind, decl_span) => { let desugaring_kind = match genblock_kind { diff --git a/tests/ui/async-await/async-closures/wrong-fn-kind.stderr b/tests/ui/async-await/async-closures/wrong-fn-kind.stderr index 4b626c1bed6c3..a5a67bf887c5c 100644 --- a/tests/ui/async-await/async-closures/wrong-fn-kind.stderr +++ b/tests/ui/async-await/async-closures/wrong-fn-kind.stderr @@ -1,24 +1,3 @@ -error[E0525]: expected a closure that implements the `async Fn` trait, but this closure only implements `async FnOnce` - --> $DIR/wrong-fn-kind.rs:17:20 - | -LL | needs_async_fn(move || async move { - | -------------- -^^^^^^ - | | | - | _____|______________this closure implements `async FnOnce`, not `async Fn` - | | | - | | required by a bound introduced by this call -LL | | -LL | | println!("{x}"); - | | - closure is `async FnOnce` because it moves the variable `x` out of its environment -LL | | }); - | |_____- the requirement to implement `async Fn` derives from here - | -note: required by a bound in `needs_async_fn` - --> $DIR/wrong-fn-kind.rs:5:27 - | -LL | fn needs_async_fn(_: impl async Fn()) {} - | ^^^^^^^^^^ required by this bound in `needs_async_fn` - error[E0596]: cannot borrow `x` as mutable, as it is a captured variable in a `Fn` closure --> $DIR/wrong-fn-kind.rs:9:20 | @@ -35,7 +14,6 @@ LL | LL | x += 1; | - mutable borrow occurs due to use of `x` in closure -error: aborting due to 2 previous errors +error: aborting due to 1 previous error -Some errors have detailed explanations: E0525, E0596. -For more information about an error, try `rustc --explain E0525`. +For more information about this error, try `rustc --explain E0596`. From d839703c54526e539d9ec6146f04c1480a6d06c2 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 16 Jul 2024 15:42:50 -0400 Subject: [PATCH 2/5] sins --- compiler/rustc_ast/src/ast.rs | 3 ++- compiler/rustc_ast_lowering/src/expr.rs | 20 ++++++++++++--- .../issue-69446-fnmut-capture.stderr | 25 ++++++++----------- .../issue-70935-complex-spans.stderr | 2 ++ .../opaque-cast-field-access-in-future.stderr | 11 ++++++-- 5 files changed, 40 insertions(+), 21 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 33f77b4bab67f..c1ae940354f62 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -1638,7 +1638,8 @@ pub struct QSelf { } /// A capture clause used in closures and `async` blocks. -#[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug, HashStable_Generic, Ord, PartialOrd)] +#[derive(Clone, Copy, PartialEq, Ord, Eq, PartialOrd, Debug)] +#[derive(Encodable, Decodable, HashStable_Generic)] pub enum CaptureBy { /// `move` keyword was not specified. Ref, diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 4f1107b27a31c..d3e6d1b4bf2df 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -229,14 +229,26 @@ impl<'hir> LoweringContext<'_, 'hir> { ) = &peeled.kind { let coroutine_kind = match gen_kind { - GenBlockKind::Async => CoroutineKind::Async { span: *span, closure_id: peeled.node_id(), return_impl_trait_id: self.next_node_id() }, - GenBlockKind::Gen => CoroutineKind::Gen { span: *span, closure_id: peeled.node_id(), return_impl_trait_id: self.next_node_id() }, - GenBlockKind::AsyncGen => CoroutineKind::AsyncGen { span: *span, closure_id: peeled.node_id(), return_impl_trait_id: self.next_node_id() }, + GenBlockKind::Async => CoroutineKind::Async { + span: *span, + closure_id: peeled.node_id(), + return_impl_trait_id: self.next_node_id(), + }, + GenBlockKind::Gen => CoroutineKind::Gen { + span: *span, + closure_id: peeled.node_id(), + return_impl_trait_id: self.next_node_id(), + }, + GenBlockKind::AsyncGen => CoroutineKind::AsyncGen { + span: *span, + closure_id: peeled.node_id(), + return_impl_trait_id: self.next_node_id(), + }, }; let id = self.next_node_id(); self.lower_expr_coroutine_closure( binder, - capture_clause.max(*gen_capture_clause), + (*capture_clause).max(*gen_capture_clause), e.id, hir_id, coroutine_kind, diff --git a/tests/ui/async-await/issue-69446-fnmut-capture.stderr b/tests/ui/async-await/issue-69446-fnmut-capture.stderr index 0366c2f44c0e7..8a94209e09617 100644 --- a/tests/ui/async-await/issue-69446-fnmut-capture.stderr +++ b/tests/ui/async-await/issue-69446-fnmut-capture.stderr @@ -1,19 +1,16 @@ -error: captured variable cannot escape `FnMut` closure body - --> $DIR/issue-69446-fnmut-capture.rs:19:17 +error: async closure does not implement `FnMut` because it captures state from its environment + --> $DIR/issue-69446-fnmut-capture.rs:19:9 | -LL | let mut x = Foo; - | ----- variable defined here -LL | bar(move || async { - | _______________-_^ - | | | - | | inferred to be a `FnMut` closure -LL | | x.foo(); - | | - variable captured here -LL | | }); - | |_____^ returns an `async` block that contains a reference to a captured variable, which then escapes the closure body +LL | bar(move || async { + | --- ^^^^^^^ + | | + | required by a bound introduced by this call | - = note: `FnMut` closures only have access to their captured variables while they are executing... - = note: ...therefore, they cannot allow references to captured variables to escape +note: required by a bound in `bar` + --> $DIR/issue-69446-fnmut-capture.rs:12:25 + | +LL | async fn bar(_: impl FnMut() -> T) + | ^^^^^^^^^^^^ required by this bound in `bar` error: aborting due to 1 previous error diff --git a/tests/ui/async-await/issue-70935-complex-spans.stderr b/tests/ui/async-await/issue-70935-complex-spans.stderr index 1ca0b339c16ad..080c3af6be6a2 100644 --- a/tests/ui/async-await/issue-70935-complex-spans.stderr +++ b/tests/ui/async-await/issue-70935-complex-spans.stderr @@ -13,6 +13,7 @@ note: required because it appears within the type `NotSync` LL | struct NotSync(PhantomData<*mut ()>); | ^^^^^^^ = note: required for `&NotSync` to implement `Send` + = note: required because it appears within the type `(&NotSync,)` note: required because it's used within this closure --> $DIR/issue-70935-complex-spans.rs:19:13 | @@ -46,6 +47,7 @@ note: required because it appears within the type `NotSync` LL | struct NotSync(PhantomData<*mut ()>); | ^^^^^^^ = note: required for `&NotSync` to implement `Send` + = note: required because it appears within the type `(&NotSync,)` note: required because it's used within this closure --> $DIR/issue-70935-complex-spans.rs:19:13 | diff --git a/tests/ui/impl-trait/opaque-cast-field-access-in-future.stderr b/tests/ui/impl-trait/opaque-cast-field-access-in-future.stderr index 5ade6a69d4b0d..df375ccd773a2 100644 --- a/tests/ui/impl-trait/opaque-cast-field-access-in-future.stderr +++ b/tests/ui/impl-trait/opaque-cast-field-access-in-future.stderr @@ -1,3 +1,9 @@ +error[E0282]: type annotations needed + --> $DIR/opaque-cast-field-access-in-future.rs:7:14 + | +LL | &mut foo.bar; + | ^^^ cannot infer type + error[E0283]: type annotations needed --> $DIR/opaque-cast-field-access-in-future.rs:22:17 | @@ -6,6 +12,7 @@ LL | fn run() -> Foo> { | = note: cannot satisfy `_: Future` -error: aborting due to 1 previous error +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0283`. +Some errors have detailed explanations: E0282, E0283. +For more information about an error, try `rustc --explain E0282`. From 20e9c9eb9e17916e0db41a08ebcfe991533ff062 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 25 Jul 2024 14:02:33 -0400 Subject: [PATCH 3/5] Make coroutine-closures possible to be cloned --- .../without-precise-captures-we-are-powerless.stderr | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/ui/async-await/async-closures/without-precise-captures-we-are-powerless.stderr b/tests/ui/async-await/async-closures/without-precise-captures-we-are-powerless.stderr index 1df5abdbb18e3..ed32a53e80728 100644 --- a/tests/ui/async-await/async-closures/without-precise-captures-we-are-powerless.stderr +++ b/tests/ui/async-await/async-closures/without-precise-captures-we-are-powerless.stderr @@ -56,6 +56,11 @@ LL | outlives::<'a>(call_once(c)); LL | LL | let c = async move || { println!("{}", *x.0); }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ move out of `x` occurs here + | +help: consider cloning the value if the performance cost is acceptable + | +LL | let c = async || { println!("{}", *x.0); }.clone(); + | ++++++++ error[E0597]: `c` does not live long enough --> $DIR/without-precise-captures-we-are-powerless.rs:33:20 From 61e27198dea413e85b448d9a75e8fd327065af0e Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sat, 27 Jul 2024 11:48:32 -0400 Subject: [PATCH 4/5] Infer args from for closure from Fn* bound even if it has no inferrable return --- compiler/rustc_hir_typeck/src/closure.rs | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs index a7953acc95c80..ee49f01c77efc 100644 --- a/compiler/rustc_hir_typeck/src/closure.rs +++ b/compiler/rustc_hir_typeck/src/closure.rs @@ -14,7 +14,7 @@ use rustc_middle::span_bug; use rustc_middle::ty::visit::{TypeVisitable, TypeVisitableExt}; use rustc_middle::ty::{self, GenericArgs, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor}; use rustc_span::def_id::LocalDefId; -use rustc_span::Span; +use rustc_span::{Span, DUMMY_SP}; use rustc_target::spec::abi::Abi; use rustc_trait_selection::error_reporting::traits::ArgKind; use rustc_trait_selection::traits; @@ -563,25 +563,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return None; }; + let mut return_ty = None; + // FIXME: We may want to elaborate here, though I assume this will be exceedingly rare. for bound in self.obligations_for_self_ty(return_vid) { if let Some(ret_projection) = bound.predicate.as_projection_clause() && let Some(ret_projection) = ret_projection.no_bound_vars() && self.tcx.is_lang_item(ret_projection.def_id(), LangItem::FutureOutput) { - let sig = projection.rebind(self.tcx.mk_fn_sig( - input_tys, - ret_projection.term.expect_type(), - false, - hir::Safety::Safe, - Abi::Rust, - )); - - return Some(ExpectedSig { cause_span, sig }); + return_ty = Some(ret_projection.term.expect_type()); } } - None + let sig = projection.rebind(self.tcx.mk_fn_sig( + input_tys, + return_ty.unwrap_or_else(|| self.next_ty_var(cause_span.unwrap_or(DUMMY_SP))), + false, + hir::Safety::Safe, + Abi::Rust, + )); + + return Some(ExpectedSig { cause_span, sig }); } fn sig_of_closure( From ce86e83f7909687b94132c10bbb67f3a7bb69c8e Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 9 Aug 2024 15:12:54 -0400 Subject: [PATCH 5/5] Don't merge capture clauses --- compiler/rustc_ast_lowering/src/expr.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index d3e6d1b4bf2df..c20223765bb04 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -222,7 +222,7 @@ impl<'hir> LoweringContext<'_, 'hir> { None => { let peeled = body.peel_uwu(); if let ast::ExprKind::Gen( - gen_capture_clause, + _, block, gen_kind @ ast::GenBlockKind::Async, span, @@ -248,7 +248,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let id = self.next_node_id(); self.lower_expr_coroutine_closure( binder, - (*capture_clause).max(*gen_capture_clause), + *capture_clause, e.id, hir_id, coroutine_kind,