Skip to content

Commit 4c09456

Browse files
Actually use the inferred ClosureKind from signature inference in coroutine-closures
1 parent ceab612 commit 4c09456

File tree

9 files changed

+203
-60
lines changed

9 files changed

+203
-60
lines changed

compiler/rustc_hir_typeck/src/closure.rs

+23-9
Original file line numberDiff line numberDiff line change
@@ -227,11 +227,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
227227
kind: TypeVariableOriginKind::ClosureSynthetic,
228228
span: expr_span,
229229
});
230-
let closure_kind_ty = self.next_ty_var(TypeVariableOrigin {
231-
// FIXME(eddyb) distinguish closure kind inference variables from the rest.
232-
kind: TypeVariableOriginKind::ClosureSynthetic,
233-
span: expr_span,
234-
});
230+
231+
let closure_kind_ty = match expected_kind {
232+
Some(kind) => Ty::from_closure_kind(tcx, kind),
233+
234+
// Create a type variable (for now) to represent the closure kind.
235+
// It will be unified during the upvar inference phase (`upvar.rs`)
236+
None => self.next_ty_var(TypeVariableOrigin {
237+
kind: TypeVariableOriginKind::ClosureSynthetic,
238+
span: expr_span,
239+
}),
240+
};
241+
235242
let coroutine_captures_by_ref_ty = self.next_ty_var(TypeVariableOrigin {
236243
kind: TypeVariableOriginKind::ClosureSynthetic,
237244
span: expr_span,
@@ -262,10 +269,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
262269
},
263270
);
264271

265-
let coroutine_kind_ty = self.next_ty_var(TypeVariableOrigin {
266-
kind: TypeVariableOriginKind::ClosureSynthetic,
267-
span: expr_span,
268-
});
272+
let coroutine_kind_ty = match expected_kind {
273+
Some(kind) => Ty::from_coroutine_closure_kind(tcx, kind),
274+
275+
// Create a type variable (for now) to represent the closure kind.
276+
// It will be unified during the upvar inference phase (`upvar.rs`)
277+
None => self.next_ty_var(TypeVariableOrigin {
278+
kind: TypeVariableOriginKind::ClosureSynthetic,
279+
span: expr_span,
280+
}),
281+
};
282+
269283
let coroutine_upvars_ty = self.next_ty_var(TypeVariableOrigin {
270284
kind: TypeVariableOriginKind::ClosureSynthetic,
271285
span: expr_span,

compiler/rustc_hir_typeck/src/upvar.rs

+16-10
Original file line numberDiff line numberDiff line change
@@ -402,16 +402,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
402402
);
403403

404404
// Additionally, we can now constrain the coroutine's kind type.
405-
let ty::Coroutine(_, coroutine_args) =
406-
*self.typeck_results.borrow().expr_ty(body.value).kind()
407-
else {
408-
bug!();
409-
};
410-
self.demand_eqtype(
411-
span,
412-
coroutine_args.as_coroutine().kind_ty(),
413-
Ty::from_coroutine_closure_kind(self.tcx, closure_kind),
414-
);
405+
//
406+
// We only do this if `infer_kind`, because if we have constrained
407+
// the kind from closure signature inference, the kind inferred
408+
// for the inner coroutine may actually be more restrictive.
409+
if infer_kind {
410+
let ty::Coroutine(_, coroutine_args) =
411+
*self.typeck_results.borrow().expr_ty(body.value).kind()
412+
else {
413+
bug!();
414+
};
415+
self.demand_eqtype(
416+
span,
417+
coroutine_args.as_coroutine().kind_ty(),
418+
Ty::from_coroutine_closure_kind(self.tcx, closure_kind),
419+
);
420+
}
415421
}
416422

417423
self.log_closure_min_capture_info(closure_def_id, span);

src/tools/miri/tests/pass/async-closure-captures.rs

+34
Original file line numberDiff line numberDiff line change
@@ -88,4 +88,38 @@ async fn async_main() {
8888
};
8989
call_once(c).await;
9090
}
91+
92+
fn force_fnonce<T>(f: impl async FnOnce() -> T) -> impl async FnOnce() -> T {
93+
f
94+
}
95+
96+
// Capture something with `move`, but infer to `AsyncFnOnce`
97+
{
98+
let x = Hello(6);
99+
let c = force_fnonce(async move || {
100+
println!("{x:?}");
101+
});
102+
call_once(c).await;
103+
104+
let x = &Hello(7);
105+
let c = force_fnonce(async move || {
106+
println!("{x:?}");
107+
});
108+
call_once(c).await;
109+
}
110+
111+
// Capture something by-ref, but infer to `AsyncFnOnce`
112+
{
113+
let x = Hello(8);
114+
let c = force_fnonce(async || {
115+
println!("{x:?}");
116+
});
117+
call_once(c).await;
118+
119+
let x = &Hello(9);
120+
let c = force_fnonce(async || {
121+
println!("{x:?}");
122+
});
123+
call_once(c).await;
124+
}
91125
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
error[E0597]: `x` does not live long enough
2+
--> $DIR/async-closure-captures.rs:LL:CC
3+
|
4+
LL | let c = force_fnonce(async move || {
5+
| ____________________________________________-
6+
LL | | println!("{x:?}");
7+
| | ^ borrowed value does not live long enough
8+
LL | | });
9+
| | --
10+
| | ||
11+
| | |`x` dropped here while still borrowed
12+
| |_________|borrow later used here
13+
| value captured here by coroutine
14+
15+
error[E0597]: `x` does not live long enough
16+
--> $DIR/async-closure-captures.rs:LL:CC
17+
|
18+
LL | let c = force_fnonce(async move || {
19+
| ____________________________________________-
20+
LL | | println!("{x:?}");
21+
| | ^ borrowed value does not live long enough
22+
LL | | });
23+
| | --
24+
| | ||
25+
| | |`x` dropped here while still borrowed
26+
| |_________|borrow later used here
27+
| value captured here by coroutine
28+
29+
error: aborting due to 2 previous errors
30+
31+
For more information about this error, try `rustc --explain E0597`.

src/tools/miri/tests/pass/async-closure-captures.stdout

-10
This file was deleted.

tests/ui/async-await/async-closures/captures.rs

+38-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//@ aux-build:block-on.rs
22
//@ edition:2021
3-
//@ run-pass
4-
//@ check-run-results
3+
4+
55

66
// Same as miri's `tests/pass/async-closure-captures.rs`, keep in sync
77

@@ -79,4 +79,40 @@ async fn async_main() {
7979
};
8080
call_once(c).await;
8181
}
82+
83+
fn force_fnonce<T>(f: impl async FnOnce() -> T) -> impl async FnOnce() -> T {
84+
f
85+
}
86+
87+
// Capture something with `move`, but infer to `AsyncFnOnce`
88+
{
89+
let x = Hello(6);
90+
let c = force_fnonce(async move || {
91+
println!("{x:?}");
92+
});
93+
call_once(c).await;
94+
95+
let x = &Hello(7);
96+
let c = force_fnonce(async move || {
97+
println!("{x:?}");
98+
});
99+
call_once(c).await;
100+
}
101+
102+
// Capture something by-ref, but infer to `AsyncFnOnce`
103+
{
104+
let x = Hello(8);
105+
let c = force_fnonce(async || {
106+
println!("{x:?}");
107+
//~^ ERROR `x` does not live long enough
108+
});
109+
call_once(c).await;
110+
111+
let x = &Hello(9);
112+
let c = force_fnonce(async || {
113+
println!("{x:?}");
114+
//~^ ERROR `x` does not live long enough
115+
});
116+
call_once(c).await;
117+
}
82118
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
error[E0597]: `x` does not live long enough
2+
--> $DIR/captures.rs:89:24
3+
|
4+
LL | let c = force_fnonce(async move || {
5+
| ____________________________________________-
6+
LL | | println!("{x:?}");
7+
| | ^ borrowed value does not live long enough
8+
LL | | });
9+
| | --
10+
| | ||
11+
| | |`x` dropped here while still borrowed
12+
| |_________|borrow later used here
13+
| value captured here by coroutine
14+
15+
error[E0597]: `x` does not live long enough
16+
--> $DIR/captures.rs:95:24
17+
|
18+
LL | let c = force_fnonce(async move || {
19+
| ____________________________________________-
20+
LL | | println!("{x:?}");
21+
| | ^ borrowed value does not live long enough
22+
LL | | });
23+
| | --
24+
| | ||
25+
| | |`x` dropped here while still borrowed
26+
| |_________|borrow later used here
27+
| value captured here by coroutine
28+
29+
error: aborting due to 2 previous errors
30+
31+
For more information about this error, try `rustc --explain E0597`.

tests/ui/async-await/async-closures/wrong-fn-kind.rs

+7-3
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,22 @@
22

33
#![feature(async_closure)]
44

5-
fn main() {
6-
fn needs_async_fn(_: impl async Fn()) {}
5+
fn needs_async_fn(_: impl async Fn()) {}
76

7+
fn a() {
88
let mut x = 1;
99
needs_async_fn(async || {
10-
//~^ ERROR expected a closure that implements the `async Fn` trait, but this closure only implements `async FnMut`
10+
//~^ ERROR cannot borrow `x` as mutable, as it is a captured variable in a `Fn` closure
1111
x += 1;
1212
});
13+
}
1314

15+
fn b() {
1416
let x = String::new();
1517
needs_async_fn(move || async move {
1618
//~^ ERROR expected a closure that implements the `async Fn` trait, but this closure only implements `async FnOnce`
1719
println!("{x}");
1820
});
1921
}
22+
23+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,5 @@
1-
error[E0525]: expected a closure that implements the `async Fn` trait, but this closure only implements `async FnMut`
2-
--> $DIR/wrong-fn-kind.rs:9:20
3-
|
4-
LL | needs_async_fn(async || {
5-
| -------------- -^^^^^^^
6-
| | |
7-
| _____|______________this closure implements `async FnMut`, not `async Fn`
8-
| | |
9-
| | required by a bound introduced by this call
10-
LL | |
11-
LL | | x += 1;
12-
| | - closure is `async FnMut` because it mutates the variable `x` here
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:6:31
18-
|
19-
LL | fn needs_async_fn(_: impl async Fn()) {}
20-
| ^^^^^^^^^^ required by this bound in `needs_async_fn`
21-
221
error[E0525]: expected a closure that implements the `async Fn` trait, but this closure only implements `async FnOnce`
23-
--> $DIR/wrong-fn-kind.rs:15:20
2+
--> $DIR/wrong-fn-kind.rs:17:20
243
|
254
LL | needs_async_fn(move || async move {
265
| -------------- -^^^^^^
@@ -35,11 +14,29 @@ LL | | });
3514
| |_____- the requirement to implement `async Fn` derives from here
3615
|
3716
note: required by a bound in `needs_async_fn`
38-
--> $DIR/wrong-fn-kind.rs:6:31
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+
22+
error[E0596]: cannot borrow `x` as mutable, as it is a captured variable in a `Fn` closure
23+
--> $DIR/wrong-fn-kind.rs:9:29
3924
|
40-
LL | fn needs_async_fn(_: impl async Fn()) {}
41-
| ^^^^^^^^^^ required by this bound in `needs_async_fn`
25+
LL | fn needs_async_fn(_: impl async Fn()) {}
26+
| --------------- change this to accept `FnMut` instead of `Fn`
27+
...
28+
LL | needs_async_fn(async || {
29+
| _____--------------_--------_^
30+
| | | |
31+
| | | in this closure
32+
| | expects `Fn` instead of `FnMut`
33+
LL | |
34+
LL | | x += 1;
35+
| | - mutable borrow occurs due to use of `x` in closure
36+
LL | | });
37+
| |_____^ cannot borrow as mutable
4238

4339
error: aborting due to 2 previous errors
4440

45-
For more information about this error, try `rustc --explain E0525`.
41+
Some errors have detailed explanations: E0525, E0596.
42+
For more information about an error, try `rustc --explain E0525`.

0 commit comments

Comments
 (0)