Skip to content

Commit 357cc34

Browse files
committed
Auto merge of #127827 - compiler-errors:async-closure-closure-async, r=<try>
[EXPERIMENT] Rewrite closure-of-async to async-closure to see what the fallout is, to gauge how forwards-compatible `async || {}` is w/ `|| async {}`. uwuwuwu
2 parents eb10639 + 3851fe0 commit 357cc34

File tree

14 files changed

+175
-76
lines changed

14 files changed

+175
-76
lines changed

compiler/rustc_ast/src/ast.rs

+28-3
Original file line numberDiff line numberDiff line change
@@ -1214,6 +1214,30 @@ impl Expr {
12141214
}
12151215
}
12161216

1217+
pub fn peel_uwu(&self) -> &Expr {
1218+
let mut expr = self;
1219+
loop {
1220+
match &expr.kind {
1221+
ExprKind::Block(blk, None) => {
1222+
if blk.stmts.len() == 1
1223+
&& let StmtKind::Expr(blk) = &blk.stmts[0].kind
1224+
{
1225+
expr = blk;
1226+
} else {
1227+
break;
1228+
}
1229+
}
1230+
ExprKind::Paren(paren) => {
1231+
expr = paren;
1232+
}
1233+
_ => {
1234+
break;
1235+
}
1236+
}
1237+
}
1238+
expr
1239+
}
1240+
12171241
pub fn peel_parens(&self) -> &Expr {
12181242
let mut expr = self;
12191243
while let ExprKind::Paren(inner) = &expr.kind {
@@ -1614,15 +1638,16 @@ pub struct QSelf {
16141638
}
16151639

16161640
/// A capture clause used in closures and `async` blocks.
1617-
#[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
1641+
#[derive(Clone, Copy, PartialEq, Ord, Eq, PartialOrd, Debug)]
1642+
#[derive(Encodable, Decodable, HashStable_Generic)]
16181643
pub enum CaptureBy {
1644+
/// `move` keyword was not specified.
1645+
Ref,
16191646
/// `move |x| y + x`.
16201647
Value {
16211648
/// The span of the `move` keyword.
16221649
move_kw: Span,
16231650
},
1624-
/// `move` keyword was not specified.
1625-
Ref,
16261651
}
16271652

16281653
/// Closure lifetime binder, `for<'a, 'b>` in `for<'a, 'b> |_: &'a (), _: &'b ()|`.

compiler/rustc_ast_lowering/src/expr.rs

+59-12
Original file line numberDiff line numberDiff line change
@@ -218,18 +218,65 @@ impl<'hir> LoweringContext<'_, 'hir> {
218218
*fn_decl_span,
219219
*fn_arg_span,
220220
),
221-
None => self.lower_expr_closure(
222-
binder,
223-
*capture_clause,
224-
e.id,
225-
hir_id,
226-
*constness,
227-
*movability,
228-
fn_decl,
229-
body,
230-
*fn_decl_span,
231-
*fn_arg_span,
232-
),
221+
None => {
222+
let peeled = body.peel_uwu();
223+
if let ast::ExprKind::Gen(
224+
gen_capture_clause,
225+
block,
226+
gen_kind @ ast::GenBlockKind::Async,
227+
span,
228+
) = &peeled.kind
229+
{
230+
let coroutine_kind = match gen_kind {
231+
GenBlockKind::Async => CoroutineKind::Async {
232+
span: *span,
233+
closure_id: peeled.node_id(),
234+
return_impl_trait_id: self.next_node_id(),
235+
},
236+
GenBlockKind::Gen => CoroutineKind::Gen {
237+
span: *span,
238+
closure_id: peeled.node_id(),
239+
return_impl_trait_id: self.next_node_id(),
240+
},
241+
GenBlockKind::AsyncGen => CoroutineKind::AsyncGen {
242+
span: *span,
243+
closure_id: peeled.node_id(),
244+
return_impl_trait_id: self.next_node_id(),
245+
},
246+
};
247+
let id = self.next_node_id();
248+
self.lower_expr_coroutine_closure(
249+
binder,
250+
(*capture_clause).max(*gen_capture_clause),
251+
e.id,
252+
hir_id,
253+
coroutine_kind,
254+
fn_decl,
255+
&ast::Expr {
256+
id,
257+
span: *span,
258+
kind: ExprKind::Block(block.clone(), None),
259+
attrs: thin_vec![],
260+
tokens: None,
261+
},
262+
*fn_decl_span,
263+
*fn_arg_span,
264+
)
265+
} else {
266+
self.lower_expr_closure(
267+
binder,
268+
*capture_clause,
269+
e.id,
270+
hir_id,
271+
*constness,
272+
*movability,
273+
fn_decl,
274+
body,
275+
*fn_decl_span,
276+
*fn_arg_span,
277+
)
278+
}
279+
}
233280
},
234281
ExprKind::Gen(capture_clause, block, genblock_kind, decl_span) => {
235282
let desugaring_kind = match genblock_kind {

compiler/rustc_mir_transform/src/shim.rs

+3
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,9 @@ fn build_clone_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, self_ty: Ty<'tcx>) -
434434
match self_ty.kind() {
435435
ty::FnDef(..) | ty::FnPtr(_) => builder.copy_shim(),
436436
ty::Closure(_, args) => builder.tuple_like_shim(dest, src, args.as_closure().upvar_tys()),
437+
ty::CoroutineClosure(_, args) => {
438+
builder.tuple_like_shim(dest, src, args.as_coroutine_closure().upvar_tys())
439+
}
437440
ty::Tuple(..) => builder.tuple_like_shim(dest, src, self_ty.tuple_fields()),
438441
ty::Coroutine(coroutine_def_id, args) => {
439442
assert_eq!(tcx.coroutine_movability(*coroutine_def_id), hir::Movability::Movable);

compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,10 @@ where
216216
// impl Copy/Clone for Closure where Self::TupledUpvars: Copy/Clone
217217
ty::Closure(_, args) => Ok(vec![ty::Binder::dummy(args.as_closure().tupled_upvars_ty())]),
218218

219-
ty::CoroutineClosure(..) => Err(NoSolution),
219+
// impl Copy/Clone for CoroutineClosure where Self::TupledUpvars: Copy/Clone
220+
ty::CoroutineClosure(_, args) => {
221+
Ok(vec![ty::Binder::dummy(args.as_coroutine_closure().tupled_upvars_ty())])
222+
}
220223

221224
// only when `coroutine_clone` is enabled and the coroutine is movable
222225
// impl Copy/Clone for Coroutine where T: Copy/Clone forall T in (upvars, witnesses)

compiler/rustc_trait_selection/src/traits/select/mod.rs

+15-2
Original file line numberDiff line numberDiff line change
@@ -2262,8 +2262,21 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
22622262
}
22632263
}
22642264

2265-
// FIXME(async_closures): These are never clone, for now.
2266-
ty::CoroutineClosure(_, _) => None,
2265+
ty::CoroutineClosure(_, args) => {
2266+
// (*) binder moved here
2267+
let ty = self.infcx.shallow_resolve(args.as_coroutine_closure().tupled_upvars_ty());
2268+
if let ty::Infer(ty::TyVar(_)) = ty.kind() {
2269+
// Not yet resolved.
2270+
Ambiguous
2271+
} else {
2272+
Where(
2273+
obligation
2274+
.predicate
2275+
.rebind(args.as_coroutine_closure().upvar_tys().to_vec()),
2276+
)
2277+
}
2278+
}
2279+
22672280
// `Copy` and `Clone` are automatically implemented for an anonymous adt
22682281
// if all of its fields are `Copy` and `Clone`
22692282
ty::Adt(adt, args) if adt.is_anonymous() => {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
//@ aux-build:block-on.rs
2+
//@ edition:2021
3+
//@ run-pass
4+
//@ check-run-results
5+
6+
#![feature(async_closure)]
7+
8+
extern crate block_on;
9+
10+
async fn for_each(f: impl async FnOnce(&str) + Clone) {
11+
f.clone()("world").await;
12+
f.clone()("world2").await;
13+
}
14+
15+
fn main() {
16+
block_on::block_on(async_main());
17+
}
18+
19+
async fn async_main() {
20+
let x = String::from("Hello,");
21+
for_each(async move |s| {
22+
println!("{x} {s}");
23+
}).await;
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Hello, world
2+
Hello, world2

tests/ui/async-await/async-closures/move-consuming-capture.stderr

+9
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,15 @@ LL | x().await;
1111
|
1212
note: `async_call_once` takes ownership of the receiver `self`, which moves `x`
1313
--> $SRC_DIR/core/src/ops/async_function.rs:LL:COL
14+
help: you could `clone` the value and consume it, if the `NoCopy: Clone` trait bound could be satisfied
15+
|
16+
LL | x.clone()().await;
17+
| ++++++++
18+
help: consider annotating `NoCopy` with `#[derive(Clone)]`
19+
|
20+
LL + #[derive(Clone)]
21+
LL | struct NoCopy;
22+
|
1423

1524
error: aborting due to 1 previous error
1625

tests/ui/async-await/async-closures/without-precise-captures-we-are-powerless.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ fn simple<'a>(x: &'a i32) {
1919

2020
let c = async move || { println!("{}", *x); };
2121
outlives::<'a>(c()); //~ ERROR `c` does not live long enough
22-
outlives::<'a>(call_once(c)); //~ ERROR cannot move out of `c`
22+
outlives::<'a>(call_once(c));
2323
}
2424

2525
struct S<'a>(&'a i32);

tests/ui/async-await/async-closures/without-precise-captures-we-are-powerless.stderr

+6-17
Original file line numberDiff line numberDiff line change
@@ -29,22 +29,6 @@ LL | outlives::<'a>(call_once(c));
2929
LL | }
3030
| - `c` dropped here while still borrowed
3131

32-
error[E0505]: cannot move out of `c` because it is borrowed
33-
--> $DIR/without-precise-captures-we-are-powerless.rs:22:30
34-
|
35-
LL | fn simple<'a>(x: &'a i32) {
36-
| -- lifetime `'a` defined here
37-
...
38-
LL | let c = async move || { println!("{}", *x); };
39-
| - binding `c` declared here
40-
LL | outlives::<'a>(c());
41-
| ---
42-
| |
43-
| borrow of `c` occurs here
44-
| argument requires that `c` is borrowed for `'a`
45-
LL | outlives::<'a>(call_once(c));
46-
| ^ move out of `c` occurs here
47-
4832
error[E0597]: `x` does not live long enough
4933
--> $DIR/without-precise-captures-we-are-powerless.rs:28:13
5034
|
@@ -72,6 +56,11 @@ LL | outlives::<'a>(call_once(c));
7256
LL |
7357
LL | let c = async move || { println!("{}", *x.0); };
7458
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ move out of `x` occurs here
59+
|
60+
help: consider cloning the value if the performance cost is acceptable
61+
|
62+
LL | let c = async || { println!("{}", *x.0); }.clone();
63+
| ++++++++
7564

7665
error[E0597]: `c` does not live long enough
7766
--> $DIR/without-precise-captures-we-are-powerless.rs:33:20
@@ -146,7 +135,7 @@ LL | // outlives::<'a>(call_once(c)); // FIXME(async_closures): Figure out w
146135
LL | }
147136
| - `c` dropped here while still borrowed
148137

149-
error: aborting due to 10 previous errors
138+
error: aborting due to 9 previous errors
150139

151140
Some errors have detailed explanations: E0505, E0597, E0621.
152141
For more information about an error, try `rustc --explain E0505`.
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,3 @@
1-
error[E0525]: expected a closure that implements the `async Fn` trait, but this closure only implements `async FnOnce`
2-
--> $DIR/wrong-fn-kind.rs:17:20
3-
|
4-
LL | needs_async_fn(move || async move {
5-
| -------------- -^^^^^^
6-
| | |
7-
| _____|______________this closure implements `async FnOnce`, not `async Fn`
8-
| | |
9-
| | required by a bound introduced by this call
10-
LL | |
11-
LL | | println!("{x}");
12-
| | - closure is `async FnOnce` because it moves the variable `x` out of its environment
13-
LL | | });
14-
| |_____- the requirement to implement `async Fn` derives from here
15-
|
16-
note: required by a bound in `needs_async_fn`
17-
--> $DIR/wrong-fn-kind.rs:5:27
18-
|
19-
LL | fn needs_async_fn(_: impl async Fn()) {}
20-
| ^^^^^^^^^^ required by this bound in `needs_async_fn`
21-
221
error[E0596]: cannot borrow `x` as mutable, as it is a captured variable in a `Fn` closure
232
--> $DIR/wrong-fn-kind.rs:9:20
243
|
@@ -35,7 +14,6 @@ LL |
3514
LL | x += 1;
3615
| - mutable borrow occurs due to use of `x` in closure
3716

38-
error: aborting due to 2 previous errors
17+
error: aborting due to 1 previous error
3918

40-
Some errors have detailed explanations: E0525, E0596.
41-
For more information about an error, try `rustc --explain E0525`.
19+
For more information about this error, try `rustc --explain E0596`.
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,16 @@
1-
error: captured variable cannot escape `FnMut` closure body
2-
--> $DIR/issue-69446-fnmut-capture.rs:19:17
1+
error: async closure does not implement `FnMut` because it captures state from its environment
2+
--> $DIR/issue-69446-fnmut-capture.rs:19:9
33
|
4-
LL | let mut x = Foo;
5-
| ----- variable defined here
6-
LL | bar(move || async {
7-
| _______________-_^
8-
| | |
9-
| | inferred to be a `FnMut` closure
10-
LL | | x.foo();
11-
| | - variable captured here
12-
LL | | });
13-
| |_____^ returns an `async` block that contains a reference to a captured variable, which then escapes the closure body
4+
LL | bar(move || async {
5+
| --- ^^^^^^^
6+
| |
7+
| required by a bound introduced by this call
148
|
15-
= note: `FnMut` closures only have access to their captured variables while they are executing...
16-
= note: ...therefore, they cannot allow references to captured variables to escape
9+
note: required by a bound in `bar`
10+
--> $DIR/issue-69446-fnmut-capture.rs:12:25
11+
|
12+
LL | async fn bar<T>(_: impl FnMut() -> T)
13+
| ^^^^^^^^^^^^ required by this bound in `bar`
1714

1815
error: aborting due to 1 previous error
1916

tests/ui/async-await/issue-70935-complex-spans.stderr

+2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ note: required because it appears within the type `NotSync`
1313
LL | struct NotSync(PhantomData<*mut ()>);
1414
| ^^^^^^^
1515
= note: required for `&NotSync` to implement `Send`
16+
= note: required because it appears within the type `(&NotSync,)`
1617
note: required because it's used within this closure
1718
--> $DIR/issue-70935-complex-spans.rs:19:13
1819
|
@@ -46,6 +47,7 @@ note: required because it appears within the type `NotSync`
4647
LL | struct NotSync(PhantomData<*mut ()>);
4748
| ^^^^^^^
4849
= note: required for `&NotSync` to implement `Send`
50+
= note: required because it appears within the type `(&NotSync,)`
4951
note: required because it's used within this closure
5052
--> $DIR/issue-70935-complex-spans.rs:19:13
5153
|
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
error[E0282]: type annotations needed
2+
--> $DIR/opaque-cast-field-access-in-future.rs:7:14
3+
|
4+
LL | &mut foo.bar;
5+
| ^^^ cannot infer type
6+
17
error[E0283]: type annotations needed
28
--> $DIR/opaque-cast-field-access-in-future.rs:22:17
39
|
@@ -6,6 +12,7 @@ LL | fn run() -> Foo<impl Future<Output = ()>> {
612
|
713
= note: cannot satisfy `_: Future`
814

9-
error: aborting due to 1 previous error
15+
error: aborting due to 2 previous errors
1016

11-
For more information about this error, try `rustc --explain E0283`.
17+
Some errors have detailed explanations: E0282, E0283.
18+
For more information about an error, try `rustc --explain E0282`.

0 commit comments

Comments
 (0)