Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[EXPERIMENT] Rewrite closure-of-async to async-closure #127827

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 28 additions & 3 deletions compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -1614,15 +1638,16 @@ 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, Ord, Eq, PartialOrd, Debug)]
#[derive(Encodable, Decodable, HashStable_Generic)]
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 ()|`.
Expand Down
71 changes: 59 additions & 12 deletions compiler/rustc_ast_lowering/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -219,18 +219,65 @@ 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(
_,
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,
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 {
Expand Down
24 changes: 13 additions & 11 deletions compiler/rustc_hir_typeck/src/closure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
26 changes: 2 additions & 24 deletions tests/ui/async-await/async-closures/wrong-fn-kind.stderr
Original file line number Diff line number Diff line change
@@ -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
|
Expand All @@ -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`.
25 changes: 11 additions & 14 deletions tests/ui/async-await/issue-69446-fnmut-capture.stderr
Original file line number Diff line number Diff line change
@@ -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<T>(_: impl FnMut() -> T)
| ^^^^^^^^^^^^ required by this bound in `bar`

error: aborting due to 1 previous error

2 changes: 2 additions & 0 deletions tests/ui/async-await/issue-70935-complex-spans.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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
|
Expand Down Expand Up @@ -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
|
Expand Down
11 changes: 9 additions & 2 deletions tests/ui/impl-trait/opaque-cast-field-access-in-future.stderr
Original file line number Diff line number Diff line change
@@ -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
|
Expand All @@ -6,6 +12,7 @@ LL | fn run() -> Foo<impl Future<Output = ()>> {
|
= 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`.
Loading