From b5ae4c9629f697034fb93f660202f354f13b74c3 Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Wed, 30 Nov 2022 19:34:19 +0100 Subject: [PATCH] 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`. --- .../src/traits/select/candidate_assembly.rs | 6 +- .../ui/async-await/generator-not-future.rs | 45 +++++++++++ .../async-await/generator-not-future.stderr | 81 +++++++++++++++++++ 3 files changed, 131 insertions(+), 1 deletion(-) create mode 100644 src/test/ui/async-await/generator-not-future.rs create mode 100644 src/test/ui/async-await/generator-not-future.stderr diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index fe5135661b5..e4b70f0d2ff 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -203,7 +203,9 @@ fn assemble_generator_candidates( // type/region parameters. let self_ty = obligation.self_ty().skip_binder(); match self_ty.kind() { - ty::Generator(..) => { + // async constructs get lowered to a special kind of generator that + // should *not* `impl Generator`. + ty::Generator(did, ..) if !self.tcx().generator_is_async(*did) => { debug!(?self_ty, ?obligation, "assemble_generator_candidates",); candidates.vec.push(GeneratorCandidate); @@ -223,6 +225,8 @@ fn assemble_future_candidates( ) { let self_ty = obligation.self_ty().skip_binder(); if let ty::Generator(did, ..) = self_ty.kind() { + // async constructs get lowered to a special kind of generator that + // should directly `impl Future`. if self.tcx().generator_is_async(*did) { debug!(?self_ty, ?obligation, "assemble_future_candidates",); diff --git a/src/test/ui/async-await/generator-not-future.rs b/src/test/ui/async-await/generator-not-future.rs new file mode 100644 index 00000000000..37d7cfa6fb7 --- /dev/null +++ b/src/test/ui/async-await/generator-not-future.rs @@ -0,0 +1,45 @@ +// edition:2018 +#![feature(generators, generator_trait)] + +use std::future::Future; +use std::ops::Generator; + +async fn async_fn() {} +fn returns_async_block() -> impl Future { + async {} +} +fn returns_generator() -> impl Generator<(), Yield = (), Return = ()> { + || { + let _: () = yield (); + } +} + +fn takes_future(_f: impl Future) {} +fn takes_generator(_g: impl Generator) {} + +fn main() { + // okay: + takes_future(async_fn()); + takes_future(returns_async_block()); + takes_future(async {}); + takes_generator(returns_generator()); + takes_generator(|| { + let _: () = yield (); + }); + + // async futures are not generators: + takes_generator(async_fn()); + //~^ ERROR the trait bound + takes_generator(returns_async_block()); + //~^ ERROR the trait bound + takes_generator(async {}); + //~^ ERROR the trait bound + + // generators are not futures: + takes_future(returns_generator()); + //~^ ERROR is not a future + takes_future(|ctx| { + //~^ ERROR is not a future + ctx = yield (); + }); +} diff --git a/src/test/ui/async-await/generator-not-future.stderr b/src/test/ui/async-await/generator-not-future.stderr new file mode 100644 index 00000000000..1b81b461f0a --- /dev/null +++ b/src/test/ui/async-await/generator-not-future.stderr @@ -0,0 +1,81 @@ +error[E0277]: the trait bound `impl Future: Generator<_>` is not satisfied + --> $DIR/generator-not-future.rs:31:21 + | +LL | takes_generator(async_fn()); + | --------------- ^^^^^^^^^^ the trait `Generator<_>` is not implemented for `impl Future` + | | + | required by a bound introduced by this call + | +note: required by a bound in `takes_generator` + --> $DIR/generator-not-future.rs:18:39 + | +LL | fn takes_generator(_g: impl Generator) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `takes_generator` + +error[E0277]: the trait bound `impl Future: Generator<_>` is not satisfied + --> $DIR/generator-not-future.rs:33:21 + | +LL | takes_generator(returns_async_block()); + | --------------- ^^^^^^^^^^^^^^^^^^^^^ the trait `Generator<_>` is not implemented for `impl Future` + | | + | required by a bound introduced by this call + | +note: required by a bound in `takes_generator` + --> $DIR/generator-not-future.rs:18:39 + | +LL | fn takes_generator(_g: impl Generator) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `takes_generator` + +error[E0277]: the trait bound `[async block@$DIR/generator-not-future.rs:35:21: 35:29]: Generator<_>` is not satisfied + --> $DIR/generator-not-future.rs:35:21 + | +LL | takes_generator(async {}); + | --------------- ^^^^^^^^ the trait `Generator<_>` is not implemented for `[async block@$DIR/generator-not-future.rs:35:21: 35:29]` + | | + | required by a bound introduced by this call + | +note: required by a bound in `takes_generator` + --> $DIR/generator-not-future.rs:18:39 + | +LL | fn takes_generator(_g: impl Generator) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `takes_generator` + +error[E0277]: `impl Generator` is not a future + --> $DIR/generator-not-future.rs:39:18 + | +LL | takes_future(returns_generator()); + | ------------ ^^^^^^^^^^^^^^^^^^^ `impl Generator` is not a future + | | + | required by a bound introduced by this call + | + = help: the trait `Future` is not implemented for `impl Generator` + = note: impl Generator must be a future or must implement `IntoFuture` to be awaited +note: required by a bound in `takes_future` + --> $DIR/generator-not-future.rs:17:26 + | +LL | fn takes_future(_f: impl Future) {} + | ^^^^^^^^^^^^^^^^^^^ required by this bound in `takes_future` + +error[E0277]: `[generator@$DIR/generator-not-future.rs:41:18: 41:23]` is not a future + --> $DIR/generator-not-future.rs:41:18 + | +LL | takes_future(|ctx| { + | _____------------_^ + | | | + | | required by a bound introduced by this call +LL | | +LL | | ctx = yield (); +LL | | }); + | |_____^ `[generator@$DIR/generator-not-future.rs:41:18: 41:23]` is not a future + | + = help: the trait `Future` is not implemented for `[generator@$DIR/generator-not-future.rs:41:18: 41:23]` + = note: [generator@$DIR/generator-not-future.rs:41:18: 41:23] must be a future or must implement `IntoFuture` to be awaited +note: required by a bound in `takes_future` + --> $DIR/generator-not-future.rs:17:26 + | +LL | fn takes_future(_f: impl Future) {} + | ^^^^^^^^^^^^^^^^^^^ required by this bound in `takes_future` + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0277`.