Skip to content

Commit b5ae4c9

Browse files
committed
Make sure async constructs do not impl Generator
Async lowering turns async functions and blocks into generators internally. Though these special kinds of generators should not `impl Generator` themselves. The other way around, normal generators should not `impl Future`.
1 parent 8de4b13 commit b5ae4c9

File tree

3 files changed

+131
-1
lines changed

3 files changed

+131
-1
lines changed

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

+5-1
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
203203
// type/region parameters.
204204
let self_ty = obligation.self_ty().skip_binder();
205205
match self_ty.kind() {
206-
ty::Generator(..) => {
206+
// async constructs get lowered to a special kind of generator that
207+
// should *not* `impl Generator`.
208+
ty::Generator(did, ..) if !self.tcx().generator_is_async(*did) => {
207209
debug!(?self_ty, ?obligation, "assemble_generator_candidates",);
208210

209211
candidates.vec.push(GeneratorCandidate);
@@ -223,6 +225,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
223225
) {
224226
let self_ty = obligation.self_ty().skip_binder();
225227
if let ty::Generator(did, ..) = self_ty.kind() {
228+
// async constructs get lowered to a special kind of generator that
229+
// should directly `impl Future`.
226230
if self.tcx().generator_is_async(*did) {
227231
debug!(?self_ty, ?obligation, "assemble_future_candidates",);
228232

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// edition:2018
2+
#![feature(generators, generator_trait)]
3+
4+
use std::future::Future;
5+
use std::ops::Generator;
6+
7+
async fn async_fn() {}
8+
fn returns_async_block() -> impl Future<Output = ()> {
9+
async {}
10+
}
11+
fn returns_generator() -> impl Generator<(), Yield = (), Return = ()> {
12+
|| {
13+
let _: () = yield ();
14+
}
15+
}
16+
17+
fn takes_future(_f: impl Future<Output = ()>) {}
18+
fn takes_generator<ResumeTy>(_g: impl Generator<ResumeTy, Yield = (), Return = ()>) {}
19+
20+
fn main() {
21+
// okay:
22+
takes_future(async_fn());
23+
takes_future(returns_async_block());
24+
takes_future(async {});
25+
takes_generator(returns_generator());
26+
takes_generator(|| {
27+
let _: () = yield ();
28+
});
29+
30+
// async futures are not generators:
31+
takes_generator(async_fn());
32+
//~^ ERROR the trait bound
33+
takes_generator(returns_async_block());
34+
//~^ ERROR the trait bound
35+
takes_generator(async {});
36+
//~^ ERROR the trait bound
37+
38+
// generators are not futures:
39+
takes_future(returns_generator());
40+
//~^ ERROR is not a future
41+
takes_future(|ctx| {
42+
//~^ ERROR is not a future
43+
ctx = yield ();
44+
});
45+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
error[E0277]: the trait bound `impl Future<Output = ()>: Generator<_>` is not satisfied
2+
--> $DIR/generator-not-future.rs:31:21
3+
|
4+
LL | takes_generator(async_fn());
5+
| --------------- ^^^^^^^^^^ the trait `Generator<_>` is not implemented for `impl Future<Output = ()>`
6+
| |
7+
| required by a bound introduced by this call
8+
|
9+
note: required by a bound in `takes_generator`
10+
--> $DIR/generator-not-future.rs:18:39
11+
|
12+
LL | fn takes_generator<ResumeTy>(_g: impl Generator<ResumeTy, Yield = (), Return = ()>) {}
13+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `takes_generator`
14+
15+
error[E0277]: the trait bound `impl Future<Output = ()>: Generator<_>` is not satisfied
16+
--> $DIR/generator-not-future.rs:33:21
17+
|
18+
LL | takes_generator(returns_async_block());
19+
| --------------- ^^^^^^^^^^^^^^^^^^^^^ the trait `Generator<_>` is not implemented for `impl Future<Output = ()>`
20+
| |
21+
| required by a bound introduced by this call
22+
|
23+
note: required by a bound in `takes_generator`
24+
--> $DIR/generator-not-future.rs:18:39
25+
|
26+
LL | fn takes_generator<ResumeTy>(_g: impl Generator<ResumeTy, Yield = (), Return = ()>) {}
27+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `takes_generator`
28+
29+
error[E0277]: the trait bound `[async block@$DIR/generator-not-future.rs:35:21: 35:29]: Generator<_>` is not satisfied
30+
--> $DIR/generator-not-future.rs:35:21
31+
|
32+
LL | takes_generator(async {});
33+
| --------------- ^^^^^^^^ the trait `Generator<_>` is not implemented for `[async block@$DIR/generator-not-future.rs:35:21: 35:29]`
34+
| |
35+
| required by a bound introduced by this call
36+
|
37+
note: required by a bound in `takes_generator`
38+
--> $DIR/generator-not-future.rs:18:39
39+
|
40+
LL | fn takes_generator<ResumeTy>(_g: impl Generator<ResumeTy, Yield = (), Return = ()>) {}
41+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `takes_generator`
42+
43+
error[E0277]: `impl Generator<Yield = (), Return = ()>` is not a future
44+
--> $DIR/generator-not-future.rs:39:18
45+
|
46+
LL | takes_future(returns_generator());
47+
| ------------ ^^^^^^^^^^^^^^^^^^^ `impl Generator<Yield = (), Return = ()>` is not a future
48+
| |
49+
| required by a bound introduced by this call
50+
|
51+
= help: the trait `Future` is not implemented for `impl Generator<Yield = (), Return = ()>`
52+
= note: impl Generator<Yield = (), Return = ()> must be a future or must implement `IntoFuture` to be awaited
53+
note: required by a bound in `takes_future`
54+
--> $DIR/generator-not-future.rs:17:26
55+
|
56+
LL | fn takes_future(_f: impl Future<Output = ()>) {}
57+
| ^^^^^^^^^^^^^^^^^^^ required by this bound in `takes_future`
58+
59+
error[E0277]: `[generator@$DIR/generator-not-future.rs:41:18: 41:23]` is not a future
60+
--> $DIR/generator-not-future.rs:41:18
61+
|
62+
LL | takes_future(|ctx| {
63+
| _____------------_^
64+
| | |
65+
| | required by a bound introduced by this call
66+
LL | |
67+
LL | | ctx = yield ();
68+
LL | | });
69+
| |_____^ `[generator@$DIR/generator-not-future.rs:41:18: 41:23]` is not a future
70+
|
71+
= help: the trait `Future` is not implemented for `[generator@$DIR/generator-not-future.rs:41:18: 41:23]`
72+
= note: [generator@$DIR/generator-not-future.rs:41:18: 41:23] must be a future or must implement `IntoFuture` to be awaited
73+
note: required by a bound in `takes_future`
74+
--> $DIR/generator-not-future.rs:17:26
75+
|
76+
LL | fn takes_future(_f: impl Future<Output = ()>) {}
77+
| ^^^^^^^^^^^^^^^^^^^ required by this bound in `takes_future`
78+
79+
error: aborting due to 5 previous errors
80+
81+
For more information about this error, try `rustc --explain E0277`.

0 commit comments

Comments
 (0)