Merge pull request #4266 from 0e4ef622/task-rpit

Allow `-> impl Future<Output = ()>` in #[task]
This commit is contained in:
Dario Nieuwenhuis 2025-06-22 21:27:56 +00:00 committed by GitHub
commit 8a23a4dfa4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 421 additions and 17 deletions

View File

@ -51,7 +51,11 @@ pub fn run(args: TokenStream, item: TokenStream) -> TokenStream {
.embassy_executor
.unwrap_or(Expr::Verbatim(TokenStream::from_str("::embassy_executor").unwrap()));
if f.sig.asyncness.is_none() {
let returns_impl_trait = match &f.sig.output {
ReturnType::Type(_, ty) => matches!(**ty, Type::ImplTrait(_)),
_ => false,
};
if f.sig.asyncness.is_none() && !returns_impl_trait {
error(&mut errors, &f.sig, "task functions must be async");
}
if !f.sig.generics.params.is_empty() {
@ -66,17 +70,19 @@ pub fn run(args: TokenStream, item: TokenStream) -> TokenStream {
if !f.sig.variadic.is_none() {
error(&mut errors, &f.sig, "task functions must not be variadic");
}
match &f.sig.output {
ReturnType::Default => {}
ReturnType::Type(_, ty) => match &**ty {
Type::Tuple(tuple) if tuple.elems.is_empty() => {}
Type::Never(_) => {}
_ => error(
&mut errors,
&f.sig,
"task functions must either not return a value, return `()` or return `!`",
),
},
if f.sig.asyncness.is_some() {
match &f.sig.output {
ReturnType::Default => {}
ReturnType::Type(_, ty) => match &**ty {
Type::Tuple(tuple) if tuple.elems.is_empty() => {}
Type::Never(_) => {}
_ => error(
&mut errors,
&f.sig,
"task functions must either not return a value, return `()` or return `!`",
),
},
}
}
let mut args = Vec::new();
@ -128,12 +134,12 @@ pub fn run(args: TokenStream, item: TokenStream) -> TokenStream {
#[cfg(feature = "nightly")]
let mut task_outer_body = quote! {
trait _EmbassyInternalTaskTrait {
type Fut: ::core::future::Future + 'static;
type Fut: ::core::future::Future<Output: #embassy_executor::_export::TaskReturnValue> + 'static;
fn construct(#fargs) -> Self::Fut;
}
impl _EmbassyInternalTaskTrait for () {
type Fut = impl core::future::Future + 'static;
type Fut = impl core::future::Future<Output: #embassy_executor::_export::TaskReturnValue> + 'static;
fn construct(#fargs) -> Self::Fut {
#task_inner_ident(#(#full_args,)*)
}

View File

@ -59,6 +59,7 @@ avr-device = { version = "0.7.0", optional = true }
critical-section = { version = "1.1", features = ["std"] }
trybuild = "1.0"
embassy-sync = { path = "../embassy-sync" }
rustversion = "1.0.21"
[features]

View File

@ -65,8 +65,17 @@ pub mod _export {
use crate::raw::TaskPool;
trait TaskReturnValue {}
impl TaskReturnValue for () {}
impl TaskReturnValue for Never {}
#[diagnostic::on_unimplemented(
message = "task futures must resolve to `()` or `!`",
note = "use `async fn` or change the return type to `impl Future<Output = ()>`"
)]
#[allow(private_bounds)]
pub trait TaskFn<Args>: Copy {
type Fut: Future + 'static;
type Fut: Future<Output: TaskReturnValue> + 'static;
}
macro_rules! task_fn_impl {
@ -74,7 +83,7 @@ pub mod _export {
impl<F, Fut, $($Tn,)*> TaskFn<($($Tn,)*)> for F
where
F: Copy + FnOnce($($Tn,)*) -> Fut,
Fut: Future + 'static,
Fut: Future<Output: TaskReturnValue> + 'static,
{
type Fut = Fut;
}
@ -205,4 +214,42 @@ pub mod _export {
Align268435456: 268435456,
Align536870912: 536870912,
);
#[allow(dead_code)]
trait HasOutput {
type Output;
}
impl<O> HasOutput for fn() -> O {
type Output = O;
}
#[allow(dead_code)]
type Never = <fn() -> ! as HasOutput>::Output;
}
/// Implementation details for embassy macros.
/// Do not use. Used for macros and HALs only. Not covered by semver guarantees.
#[doc(hidden)]
#[cfg(feature = "nightly")]
pub mod _export {
#[diagnostic::on_unimplemented(
message = "task futures must resolve to `()` or `!`",
note = "use `async fn` or change the return type to `impl Future<Output = ()>`"
)]
pub trait TaskReturnValue {}
impl TaskReturnValue for () {}
impl TaskReturnValue for Never {}
#[allow(dead_code)]
trait HasOutput {
type Output;
}
impl<O> HasOutput for fn() -> O {
type Output = O;
}
#[allow(dead_code)]
type Never = <fn() -> ! as HasOutput>::Output;
}

View File

@ -1,7 +1,8 @@
#![cfg_attr(feature = "nightly", feature(impl_trait_in_assoc_type))]
#![cfg_attr(feature = "nightly", feature(never_type))]
use std::boxed::Box;
use std::future::poll_fn;
use std::future::{poll_fn, Future};
use std::sync::{Arc, Mutex};
use std::task::Poll;
@ -58,6 +59,39 @@ fn executor_task() {
trace.push("poll task1")
}
#[task]
async fn task2() -> ! {
panic!()
}
let (executor, trace) = setup();
executor.spawner().spawn(task1(trace.clone())).unwrap();
unsafe { executor.poll() };
unsafe { executor.poll() };
assert_eq!(
trace.get(),
&[
"pend", // spawning a task pends the executor
"poll task1", // poll only once.
]
)
}
#[test]
fn executor_task_rpit() {
#[task]
fn task1(trace: Trace) -> impl Future<Output = ()> {
async move { trace.push("poll task1") }
}
#[cfg(feature = "nightly")]
#[task]
fn task2() -> impl Future<Output = !> {
async { panic!() }
}
let (executor, trace) = setup();
executor.spawner().spawn(task1(trace.clone())).unwrap();

View File

@ -17,6 +17,15 @@ fn ui() {
t.compile_fail("tests/ui/nonstatic_struct_elided.rs");
t.compile_fail("tests/ui/nonstatic_struct_generic.rs");
t.compile_fail("tests/ui/not_async.rs");
if rustversion::cfg!(stable) {
// output is slightly different on nightly
t.compile_fail("tests/ui/bad_return_impl_future.rs");
t.compile_fail("tests/ui/return_impl_send.rs");
}
if cfg!(feature = "nightly") {
t.compile_fail("tests/ui/bad_return_impl_future_nightly.rs");
t.compile_fail("tests/ui/return_impl_send_nightly.rs");
}
t.compile_fail("tests/ui/self_ref.rs");
t.compile_fail("tests/ui/self.rs");
t.compile_fail("tests/ui/type_error.rs");

View File

@ -0,0 +1,9 @@
#![cfg_attr(feature = "nightly", feature(impl_trait_in_assoc_type))]
use core::future::Future;
#[embassy_executor::task]
fn task() -> impl Future<Output = u32> {
async { 5 }
}
fn main() {}

View File

@ -0,0 +1,120 @@
error[E0277]: task futures must resolve to `()` or `!`
--> tests/ui/bad_return_impl_future.rs:5:4
|
4 | #[embassy_executor::task]
| ------------------------- required by a bound introduced by this call
5 | fn task() -> impl Future<Output = u32> {
| ^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Future<Output = u32> {__task_task}`
|
= note: use `async fn` or change the return type to `impl Future<Output = ()>`
note: required by a bound in `task_pool_size`
--> src/lib.rs
|
| pub const fn task_pool_size<F, Args, Fut, const POOL_SIZE: usize>(_: F) -> usize
| -------------- required by a bound in this function
| where
| F: TaskFn<Args, Fut = Fut>,
| ^^^^^^^^^ required by this bound in `task_pool_size`
error[E0277]: task futures must resolve to `()` or `!`
--> tests/ui/bad_return_impl_future.rs:4:1
|
4 | #[embassy_executor::task]
| ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Future<Output = u32> {__task_task}`
|
= note: use `async fn` or change the return type to `impl Future<Output = ()>`
note: required by a bound in `task_pool_size`
--> src/lib.rs
|
| pub const fn task_pool_size<F, Args, Fut, const POOL_SIZE: usize>(_: F) -> usize
| -------------- required by a bound in this function
| where
| F: TaskFn<Args, Fut = Fut>,
| ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `task_pool_size`
= note: this error originates in the attribute macro `embassy_executor::task` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0277]: task futures must resolve to `()` or `!`
--> tests/ui/bad_return_impl_future.rs:5:4
|
4 | #[embassy_executor::task]
| ------------------------- required by a bound introduced by this call
5 | fn task() -> impl Future<Output = u32> {
| ^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Future<Output = u32> {__task_task}`
|
= note: use `async fn` or change the return type to `impl Future<Output = ()>`
note: required by a bound in `task_pool_align`
--> src/lib.rs
|
| pub const fn task_pool_align<F, Args, Fut, const POOL_SIZE: usize>(_: F) -> usize
| --------------- required by a bound in this function
| where
| F: TaskFn<Args, Fut = Fut>,
| ^^^^^^^^^ required by this bound in `task_pool_align`
error[E0277]: task futures must resolve to `()` or `!`
--> tests/ui/bad_return_impl_future.rs:4:1
|
4 | #[embassy_executor::task]
| ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Future<Output = u32> {__task_task}`
|
= note: use `async fn` or change the return type to `impl Future<Output = ()>`
note: required by a bound in `task_pool_align`
--> src/lib.rs
|
| pub const fn task_pool_align<F, Args, Fut, const POOL_SIZE: usize>(_: F) -> usize
| --------------- required by a bound in this function
| where
| F: TaskFn<Args, Fut = Fut>,
| ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `task_pool_align`
= note: this error originates in the attribute macro `embassy_executor::task` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0277]: task futures must resolve to `()` or `!`
--> tests/ui/bad_return_impl_future.rs:5:4
|
4 | #[embassy_executor::task]
| ------------------------- required by a bound introduced by this call
5 | fn task() -> impl Future<Output = u32> {
| ^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Future<Output = u32> {__task_task}`
|
= note: use `async fn` or change the return type to `impl Future<Output = ()>`
note: required by a bound in `task_pool_new`
--> src/lib.rs
|
| pub const fn task_pool_new<F, Args, Fut, const POOL_SIZE: usize>(_: F) -> TaskPool<Fut, POOL_SIZE>
| ------------- required by a bound in this function
| where
| F: TaskFn<Args, Fut = Fut>,
| ^^^^^^^^^ required by this bound in `task_pool_new`
error[E0277]: task futures must resolve to `()` or `!`
--> tests/ui/bad_return_impl_future.rs:4:1
|
4 | #[embassy_executor::task]
| ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Future<Output = u32> {__task_task}`
|
= note: use `async fn` or change the return type to `impl Future<Output = ()>`
note: required by a bound in `task_pool_new`
--> src/lib.rs
|
| pub const fn task_pool_new<F, Args, Fut, const POOL_SIZE: usize>(_: F) -> TaskPool<Fut, POOL_SIZE>
| ------------- required by a bound in this function
| where
| F: TaskFn<Args, Fut = Fut>,
| ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `task_pool_new`
= note: this error originates in the attribute macro `embassy_executor::task` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0277]: task futures must resolve to `()` or `!`
--> tests/ui/bad_return_impl_future.rs:5:4
|
4 | #[embassy_executor::task]
| ------------------------- required by a bound introduced by this call
5 | fn task() -> impl Future<Output = u32> {
| ^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Future<Output = u32> {__task_task}`
|
= note: use `async fn` or change the return type to `impl Future<Output = ()>`
note: required by a bound in `__task_pool_get`
--> tests/ui/bad_return_impl_future.rs:4:1
|
4 | #[embassy_executor::task]
| ^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `__task_pool_get`
= note: this error originates in the attribute macro `embassy_executor::task` (in Nightly builds, run with -Z macro-backtrace for more info)

View File

@ -0,0 +1,9 @@
#![cfg_attr(feature = "nightly", feature(impl_trait_in_assoc_type))]
use core::future::Future;
#[embassy_executor::task]
fn task() -> impl Future<Output = u32> {
async { 5 }
}
fn main() {}

View File

@ -0,0 +1,10 @@
error[E0277]: task futures must resolve to `()` or `!`
--> tests/ui/bad_return_impl_future_nightly.rs:4:1
|
4 | #[embassy_executor::task]
| ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `TaskReturnValue` is not implemented for `u32`
|
= note: use `async fn` or change the return type to `impl Future<Output = ()>`
= help: the following other types implement trait `TaskReturnValue`:
()
<fn() -> ! as _export::HasOutput>::Output

View File

@ -0,0 +1,6 @@
#![cfg_attr(feature = "nightly", feature(impl_trait_in_assoc_type))]
#[embassy_executor::task]
fn task() -> impl Send {}
fn main() {}

View File

@ -0,0 +1,137 @@
error[E0277]: task futures must resolve to `()` or `!`
--> tests/ui/return_impl_send.rs:4:4
|
3 | #[embassy_executor::task]
| ------------------------- required by a bound introduced by this call
4 | fn task() -> impl Send {}
| ^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Send {__task_task}`
|
= note: use `async fn` or change the return type to `impl Future<Output = ()>`
note: required by a bound in `task_pool_size`
--> src/lib.rs
|
| pub const fn task_pool_size<F, Args, Fut, const POOL_SIZE: usize>(_: F) -> usize
| -------------- required by a bound in this function
| where
| F: TaskFn<Args, Fut = Fut>,
| ^^^^^^^^^ required by this bound in `task_pool_size`
error[E0277]: task futures must resolve to `()` or `!`
--> tests/ui/return_impl_send.rs:3:1
|
3 | #[embassy_executor::task]
| ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Send {__task_task}`
|
= note: use `async fn` or change the return type to `impl Future<Output = ()>`
note: required by a bound in `task_pool_size`
--> src/lib.rs
|
| pub const fn task_pool_size<F, Args, Fut, const POOL_SIZE: usize>(_: F) -> usize
| -------------- required by a bound in this function
| where
| F: TaskFn<Args, Fut = Fut>,
| ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `task_pool_size`
= note: this error originates in the attribute macro `embassy_executor::task` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0277]: task futures must resolve to `()` or `!`
--> tests/ui/return_impl_send.rs:4:4
|
3 | #[embassy_executor::task]
| ------------------------- required by a bound introduced by this call
4 | fn task() -> impl Send {}
| ^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Send {__task_task}`
|
= note: use `async fn` or change the return type to `impl Future<Output = ()>`
note: required by a bound in `task_pool_align`
--> src/lib.rs
|
| pub const fn task_pool_align<F, Args, Fut, const POOL_SIZE: usize>(_: F) -> usize
| --------------- required by a bound in this function
| where
| F: TaskFn<Args, Fut = Fut>,
| ^^^^^^^^^ required by this bound in `task_pool_align`
error[E0277]: task futures must resolve to `()` or `!`
--> tests/ui/return_impl_send.rs:3:1
|
3 | #[embassy_executor::task]
| ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Send {__task_task}`
|
= note: use `async fn` or change the return type to `impl Future<Output = ()>`
note: required by a bound in `task_pool_align`
--> src/lib.rs
|
| pub const fn task_pool_align<F, Args, Fut, const POOL_SIZE: usize>(_: F) -> usize
| --------------- required by a bound in this function
| where
| F: TaskFn<Args, Fut = Fut>,
| ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `task_pool_align`
= note: this error originates in the attribute macro `embassy_executor::task` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0277]: task futures must resolve to `()` or `!`
--> tests/ui/return_impl_send.rs:4:4
|
3 | #[embassy_executor::task]
| ------------------------- required by a bound introduced by this call
4 | fn task() -> impl Send {}
| ^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Send {__task_task}`
|
= note: use `async fn` or change the return type to `impl Future<Output = ()>`
note: required by a bound in `task_pool_new`
--> src/lib.rs
|
| pub const fn task_pool_new<F, Args, Fut, const POOL_SIZE: usize>(_: F) -> TaskPool<Fut, POOL_SIZE>
| ------------- required by a bound in this function
| where
| F: TaskFn<Args, Fut = Fut>,
| ^^^^^^^^^ required by this bound in `task_pool_new`
error[E0277]: task futures must resolve to `()` or `!`
--> tests/ui/return_impl_send.rs:3:1
|
3 | #[embassy_executor::task]
| ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Send {__task_task}`
|
= note: use `async fn` or change the return type to `impl Future<Output = ()>`
note: required by a bound in `task_pool_new`
--> src/lib.rs
|
| pub const fn task_pool_new<F, Args, Fut, const POOL_SIZE: usize>(_: F) -> TaskPool<Fut, POOL_SIZE>
| ------------- required by a bound in this function
| where
| F: TaskFn<Args, Fut = Fut>,
| ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `task_pool_new`
= note: this error originates in the attribute macro `embassy_executor::task` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0277]: task futures must resolve to `()` or `!`
--> tests/ui/return_impl_send.rs:4:4
|
3 | #[embassy_executor::task]
| ------------------------- required by a bound introduced by this call
4 | fn task() -> impl Send {}
| ^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Send {__task_task}`
|
= note: use `async fn` or change the return type to `impl Future<Output = ()>`
note: required by a bound in `__task_pool_get`
--> tests/ui/return_impl_send.rs:3:1
|
3 | #[embassy_executor::task]
| ^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `__task_pool_get`
= note: this error originates in the attribute macro `embassy_executor::task` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0277]: `impl Send` is not a future
--> tests/ui/return_impl_send.rs:3:1
|
3 | #[embassy_executor::task]
| ^^^^^^^^^^^^^^^^^^^^^^^^^ `impl Send` is not a future
|
= help: the trait `Future` is not implemented for `impl Send`
note: required by a bound in `TaskPool::<F, N>::_spawn_async_fn`
--> src/raw/mod.rs
|
| impl<F: Future + 'static, const N: usize> TaskPool<F, N> {
| ^^^^^^ required by this bound in `TaskPool::<F, N>::_spawn_async_fn`
...
| pub unsafe fn _spawn_async_fn<FutFn>(&'static self, future: FutFn) -> SpawnToken<impl Sized>
| --------------- required by a bound in this associated function
= note: this error originates in the attribute macro `embassy_executor::task` (in Nightly builds, run with -Z macro-backtrace for more info)

View File

@ -0,0 +1,6 @@
#![cfg_attr(feature = "nightly", feature(impl_trait_in_assoc_type))]
#[embassy_executor::task]
fn task() -> impl Send {}
fn main() {}

View File

@ -0,0 +1,10 @@
error[E0277]: `impl Send` is not a future
--> tests/ui/return_impl_send_nightly.rs:3:1
|
3 | #[embassy_executor::task]
| ^^^^^^^^^^^^^^^^^^^^^^^^^
| |
| `impl Send` is not a future
| return type was inferred to be `impl Send` here
|
= help: the trait `Future` is not implemented for `impl Send`