aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--embassy-executor-macros/src/macros/task.rs34
-rw-r--r--embassy-executor/src/lib.rs47
-rw-r--r--embassy-executor/tests/test.rs24
-rw-r--r--embassy-executor/tests/ui.rs4
-rw-r--r--embassy-executor/tests/ui/bad_return_impl_trait.rs9
-rw-r--r--embassy-executor/tests/ui/bad_return_impl_trait.stderr120
-rw-r--r--embassy-executor/tests/ui/bad_return_impl_trait_nightly.rs9
-rw-r--r--embassy-executor/tests/ui/bad_return_impl_trait_nightly.stderr9
8 files changed, 239 insertions, 17 deletions
diff --git a/embassy-executor-macros/src/macros/task.rs b/embassy-executor-macros/src/macros/task.rs
index 91d6beee8..91bf8e940 100644
--- a/embassy-executor-macros/src/macros/task.rs
+++ b/embassy-executor-macros/src/macros/task.rs
@@ -51,7 +51,11 @@ pub fn run(args: TokenStream, item: TokenStream) -> TokenStream {
51 .embassy_executor 51 .embassy_executor
52 .unwrap_or(Expr::Verbatim(TokenStream::from_str("::embassy_executor").unwrap())); 52 .unwrap_or(Expr::Verbatim(TokenStream::from_str("::embassy_executor").unwrap()));
53 53
54 if f.sig.asyncness.is_none() { 54 let returns_impl_trait = match &f.sig.output {
55 ReturnType::Type(_, ty) => matches!(**ty, Type::ImplTrait(_)),
56 _ => false,
57 };
58 if f.sig.asyncness.is_none() && !returns_impl_trait {
55 error(&mut errors, &f.sig, "task functions must be async"); 59 error(&mut errors, &f.sig, "task functions must be async");
56 } 60 }
57 if !f.sig.generics.params.is_empty() { 61 if !f.sig.generics.params.is_empty() {
@@ -66,17 +70,19 @@ pub fn run(args: TokenStream, item: TokenStream) -> TokenStream {
66 if !f.sig.variadic.is_none() { 70 if !f.sig.variadic.is_none() {
67 error(&mut errors, &f.sig, "task functions must not be variadic"); 71 error(&mut errors, &f.sig, "task functions must not be variadic");
68 } 72 }
69 match &f.sig.output { 73 if f.sig.asyncness.is_some() {
70 ReturnType::Default => {} 74 match &f.sig.output {
71 ReturnType::Type(_, ty) => match &**ty { 75 ReturnType::Default => {}
72 Type::Tuple(tuple) if tuple.elems.is_empty() => {} 76 ReturnType::Type(_, ty) => match &**ty {
73 Type::Never(_) => {} 77 Type::Tuple(tuple) if tuple.elems.is_empty() => {}
74 _ => error( 78 Type::Never(_) => {}
75 &mut errors, 79 _ => error(
76 &f.sig, 80 &mut errors,
77 "task functions must either not return a value, return `()` or return `!`", 81 &f.sig,
78 ), 82 "task functions must either not return a value, return `()` or return `!`",
79 }, 83 ),
84 },
85 }
80 } 86 }
81 87
82 let mut args = Vec::new(); 88 let mut args = Vec::new();
@@ -128,12 +134,12 @@ pub fn run(args: TokenStream, item: TokenStream) -> TokenStream {
128 #[cfg(feature = "nightly")] 134 #[cfg(feature = "nightly")]
129 let mut task_outer_body = quote! { 135 let mut task_outer_body = quote! {
130 trait _EmbassyInternalTaskTrait { 136 trait _EmbassyInternalTaskTrait {
131 type Fut: ::core::future::Future + 'static; 137 type Fut: ::core::future::Future<Output: #embassy_executor::_export::TaskReturnValue> + 'static;
132 fn construct(#fargs) -> Self::Fut; 138 fn construct(#fargs) -> Self::Fut;
133 } 139 }
134 140
135 impl _EmbassyInternalTaskTrait for () { 141 impl _EmbassyInternalTaskTrait for () {
136 type Fut = impl core::future::Future + 'static; 142 type Fut = impl core::future::Future<Output: #embassy_executor::_export::TaskReturnValue> + 'static;
137 fn construct(#fargs) -> Self::Fut { 143 fn construct(#fargs) -> Self::Fut {
138 #task_inner_ident(#(#full_args,)*) 144 #task_inner_ident(#(#full_args,)*)
139 } 145 }
diff --git a/embassy-executor/src/lib.rs b/embassy-executor/src/lib.rs
index dfe420bab..70abfcc3a 100644
--- a/embassy-executor/src/lib.rs
+++ b/embassy-executor/src/lib.rs
@@ -65,8 +65,17 @@ pub mod _export {
65 65
66 use crate::raw::TaskPool; 66 use crate::raw::TaskPool;
67 67
68 trait TaskReturnValue {}
69 impl TaskReturnValue for () {}
70 impl TaskReturnValue for Never {}
71
72 #[diagnostic::on_unimplemented(
73 message = "task function futures must resolve to `()`",
74 note = "use `async fn` or change the return type to `impl Future<Output = ()>`"
75 )]
76 #[allow(private_bounds)]
68 pub trait TaskFn<Args>: Copy { 77 pub trait TaskFn<Args>: Copy {
69 type Fut: Future + 'static; 78 type Fut: Future<Output: TaskReturnValue> + 'static;
70 } 79 }
71 80
72 macro_rules! task_fn_impl { 81 macro_rules! task_fn_impl {
@@ -74,7 +83,7 @@ pub mod _export {
74 impl<F, Fut, $($Tn,)*> TaskFn<($($Tn,)*)> for F 83 impl<F, Fut, $($Tn,)*> TaskFn<($($Tn,)*)> for F
75 where 84 where
76 F: Copy + FnOnce($($Tn,)*) -> Fut, 85 F: Copy + FnOnce($($Tn,)*) -> Fut,
77 Fut: Future + 'static, 86 Fut: Future<Output: TaskReturnValue> + 'static,
78 { 87 {
79 type Fut = Fut; 88 type Fut = Fut;
80 } 89 }
@@ -205,4 +214,38 @@ pub mod _export {
205 Align268435456: 268435456, 214 Align268435456: 268435456,
206 Align536870912: 536870912, 215 Align536870912: 536870912,
207 ); 216 );
217
218 #[allow(dead_code)]
219 trait HasOutput {
220 type Output;
221 }
222
223 impl<O> HasOutput for fn() -> O {
224 type Output = O;
225 }
226
227 #[allow(dead_code)]
228 type Never = <fn() -> ! as HasOutput>::Output;
229}
230
231/// Implementation details for embassy macros.
232/// Do not use. Used for macros and HALs only. Not covered by semver guarantees.
233#[doc(hidden)]
234#[cfg(feature = "nightly")]
235pub mod _export {
236 pub trait TaskReturnValue {}
237 impl TaskReturnValue for () {}
238 impl TaskReturnValue for Never {}
239
240 #[allow(dead_code)]
241 trait HasOutput {
242 type Output;
243 }
244
245 impl<O> HasOutput for fn() -> O {
246 type Output = O;
247 }
248
249 #[allow(dead_code)]
250 type Never = <fn() -> ! as HasOutput>::Output;
208} 251}
diff --git a/embassy-executor/tests/test.rs b/embassy-executor/tests/test.rs
index 78c49c071..c7ff4554c 100644
--- a/embassy-executor/tests/test.rs
+++ b/embassy-executor/tests/test.rs
@@ -1,7 +1,7 @@
1#![cfg_attr(feature = "nightly", feature(impl_trait_in_assoc_type))] 1#![cfg_attr(feature = "nightly", feature(impl_trait_in_assoc_type))]
2 2
3use std::boxed::Box; 3use std::boxed::Box;
4use std::future::poll_fn; 4use std::future::{poll_fn, Future};
5use std::sync::{Arc, Mutex}; 5use std::sync::{Arc, Mutex};
6use std::task::Poll; 6use std::task::Poll;
7 7
@@ -74,6 +74,28 @@ fn executor_task() {
74} 74}
75 75
76#[test] 76#[test]
77fn executor_task_rpit() {
78 #[task]
79 fn task1(trace: Trace) -> impl Future<Output = ()> {
80 async move { trace.push("poll task1") }
81 }
82
83 let (executor, trace) = setup();
84 executor.spawner().spawn(task1(trace.clone())).unwrap();
85
86 unsafe { executor.poll() };
87 unsafe { executor.poll() };
88
89 assert_eq!(
90 trace.get(),
91 &[
92 "pend", // spawning a task pends the executor
93 "poll task1", // poll only once.
94 ]
95 )
96}
97
98#[test]
77fn executor_task_self_wake() { 99fn executor_task_self_wake() {
78 #[task] 100 #[task]
79 async fn task1(trace: Trace) { 101 async fn task1(trace: Trace) {
diff --git a/embassy-executor/tests/ui.rs b/embassy-executor/tests/ui.rs
index 278a4b903..ed8228e27 100644
--- a/embassy-executor/tests/ui.rs
+++ b/embassy-executor/tests/ui.rs
@@ -17,6 +17,10 @@ fn ui() {
17 t.compile_fail("tests/ui/nonstatic_struct_elided.rs"); 17 t.compile_fail("tests/ui/nonstatic_struct_elided.rs");
18 t.compile_fail("tests/ui/nonstatic_struct_generic.rs"); 18 t.compile_fail("tests/ui/nonstatic_struct_generic.rs");
19 t.compile_fail("tests/ui/not_async.rs"); 19 t.compile_fail("tests/ui/not_async.rs");
20 // #[cfg(not(feature = "nightly"))] // output differs on stable and nightly
21 // t.compile_fail("tests/ui/bad_return_impl_trait.rs");
22 #[cfg(feature = "nightly")]
23 t.compile_fail("tests/ui/bad_return_impl_trait_nightly.rs");
20 t.compile_fail("tests/ui/self_ref.rs"); 24 t.compile_fail("tests/ui/self_ref.rs");
21 t.compile_fail("tests/ui/self.rs"); 25 t.compile_fail("tests/ui/self.rs");
22 t.compile_fail("tests/ui/type_error.rs"); 26 t.compile_fail("tests/ui/type_error.rs");
diff --git a/embassy-executor/tests/ui/bad_return_impl_trait.rs b/embassy-executor/tests/ui/bad_return_impl_trait.rs
new file mode 100644
index 000000000..baaa7dc5a
--- /dev/null
+++ b/embassy-executor/tests/ui/bad_return_impl_trait.rs
@@ -0,0 +1,9 @@
1#![cfg_attr(feature = "nightly", feature(impl_trait_in_assoc_type))]
2use core::future::Future;
3
4#[embassy_executor::task]
5fn task() -> impl Future<Output = u32> {
6 async { 5 }
7}
8
9fn main() {}
diff --git a/embassy-executor/tests/ui/bad_return_impl_trait.stderr b/embassy-executor/tests/ui/bad_return_impl_trait.stderr
new file mode 100644
index 000000000..9e2df353e
--- /dev/null
+++ b/embassy-executor/tests/ui/bad_return_impl_trait.stderr
@@ -0,0 +1,120 @@
1error[E0277]: task function futures must resolve to `()`
2 --> tests/ui/bad_return_impl_trait.rs:5:4
3 |
44 | #[embassy_executor::task]
5 | ------------------------- required by a bound introduced by this call
65 | fn task() -> impl Future<Output = u32> {
7 | ^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Future<Output = u32> {__task_task}`
8 |
9 = note: use `async fn` or change the return type to `impl Future<Output = ()>`
10note: required by a bound in `task_pool_size`
11 --> src/lib.rs
12 |
13 | pub const fn task_pool_size<F, Args, Fut, const POOL_SIZE: usize>(_: F) -> usize
14 | -------------- required by a bound in this function
15 | where
16 | F: TaskFn<Args, Fut = Fut>,
17 | ^^^^^^^^^ required by this bound in `task_pool_size`
18
19error[E0277]: task function futures must resolve to `()`
20 --> tests/ui/bad_return_impl_trait.rs:4:1
21 |
224 | #[embassy_executor::task]
23 | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Future<Output = u32> {__task_task}`
24 |
25 = note: use `async fn` or change the return type to `impl Future<Output = ()>`
26note: required by a bound in `task_pool_size`
27 --> src/lib.rs
28 |
29 | pub const fn task_pool_size<F, Args, Fut, const POOL_SIZE: usize>(_: F) -> usize
30 | -------------- required by a bound in this function
31 | where
32 | F: TaskFn<Args, Fut = Fut>,
33 | ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `task_pool_size`
34 = note: this error originates in the attribute macro `embassy_executor::task` (in Nightly builds, run with -Z macro-backtrace for more info)
35
36error[E0277]: task function futures must resolve to `()`
37 --> tests/ui/bad_return_impl_trait.rs:5:4
38 |
394 | #[embassy_executor::task]
40 | ------------------------- required by a bound introduced by this call
415 | fn task() -> impl Future<Output = u32> {
42 | ^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Future<Output = u32> {__task_task}`
43 |
44 = note: use `async fn` or change the return type to `impl Future<Output = ()>`
45note: required by a bound in `task_pool_align`
46 --> src/lib.rs
47 |
48 | pub const fn task_pool_align<F, Args, Fut, const POOL_SIZE: usize>(_: F) -> usize
49 | --------------- required by a bound in this function
50 | where
51 | F: TaskFn<Args, Fut = Fut>,
52 | ^^^^^^^^^ required by this bound in `task_pool_align`
53
54error[E0277]: task function futures must resolve to `()`
55 --> tests/ui/bad_return_impl_trait.rs:4:1
56 |
574 | #[embassy_executor::task]
58 | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Future<Output = u32> {__task_task}`
59 |
60 = note: use `async fn` or change the return type to `impl Future<Output = ()>`
61note: required by a bound in `task_pool_align`
62 --> src/lib.rs
63 |
64 | pub const fn task_pool_align<F, Args, Fut, const POOL_SIZE: usize>(_: F) -> usize
65 | --------------- required by a bound in this function
66 | where
67 | F: TaskFn<Args, Fut = Fut>,
68 | ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `task_pool_align`
69 = note: this error originates in the attribute macro `embassy_executor::task` (in Nightly builds, run with -Z macro-backtrace for more info)
70
71error[E0277]: task function futures must resolve to `()`
72 --> tests/ui/bad_return_impl_trait.rs:5:4
73 |
744 | #[embassy_executor::task]
75 | ------------------------- required by a bound introduced by this call
765 | fn task() -> impl Future<Output = u32> {
77 | ^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Future<Output = u32> {__task_task}`
78 |
79 = note: use `async fn` or change the return type to `impl Future<Output = ()>`
80note: required by a bound in `task_pool_new`
81 --> src/lib.rs
82 |
83 | pub const fn task_pool_new<F, Args, Fut, const POOL_SIZE: usize>(_: F) -> TaskPool<Fut, POOL_SIZE>
84 | ------------- required by a bound in this function
85 | where
86 | F: TaskFn<Args, Fut = Fut>,
87 | ^^^^^^^^^ required by this bound in `task_pool_new`
88
89error[E0277]: task function futures must resolve to `()`
90 --> tests/ui/bad_return_impl_trait.rs:4:1
91 |
924 | #[embassy_executor::task]
93 | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Future<Output = u32> {__task_task}`
94 |
95 = note: use `async fn` or change the return type to `impl Future<Output = ()>`
96note: required by a bound in `task_pool_new`
97 --> src/lib.rs
98 |
99 | pub const fn task_pool_new<F, Args, Fut, const POOL_SIZE: usize>(_: F) -> TaskPool<Fut, POOL_SIZE>
100 | ------------- required by a bound in this function
101 | where
102 | F: TaskFn<Args, Fut = Fut>,
103 | ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `task_pool_new`
104 = note: this error originates in the attribute macro `embassy_executor::task` (in Nightly builds, run with -Z macro-backtrace for more info)
105
106error[E0277]: task function futures must resolve to `()`
107 --> tests/ui/bad_return_impl_trait.rs:5:4
108 |
1094 | #[embassy_executor::task]
110 | ------------------------- required by a bound introduced by this call
1115 | fn task() -> impl Future<Output = u32> {
112 | ^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Future<Output = u32> {__task_task}`
113 |
114 = note: use `async fn` or change the return type to `impl Future<Output = ()>`
115note: required by a bound in `__task_pool_get`
116 --> tests/ui/bad_return_impl_trait.rs:4:1
117 |
1184 | #[embassy_executor::task]
119 | ^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `__task_pool_get`
120 = note: this error originates in the attribute macro `embassy_executor::task` (in Nightly builds, run with -Z macro-backtrace for more info)
diff --git a/embassy-executor/tests/ui/bad_return_impl_trait_nightly.rs b/embassy-executor/tests/ui/bad_return_impl_trait_nightly.rs
new file mode 100644
index 000000000..baaa7dc5a
--- /dev/null
+++ b/embassy-executor/tests/ui/bad_return_impl_trait_nightly.rs
@@ -0,0 +1,9 @@
1#![cfg_attr(feature = "nightly", feature(impl_trait_in_assoc_type))]
2use core::future::Future;
3
4#[embassy_executor::task]
5fn task() -> impl Future<Output = u32> {
6 async { 5 }
7}
8
9fn main() {}
diff --git a/embassy-executor/tests/ui/bad_return_impl_trait_nightly.stderr b/embassy-executor/tests/ui/bad_return_impl_trait_nightly.stderr
new file mode 100644
index 000000000..21e20d5c0
--- /dev/null
+++ b/embassy-executor/tests/ui/bad_return_impl_trait_nightly.stderr
@@ -0,0 +1,9 @@
1error[E0277]: the trait bound `u32: TaskReturnValue` is not satisfied
2 --> tests/ui/bad_return_impl_trait_nightly.rs:4:1
3 |
44 | #[embassy_executor::task]
5 | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `TaskReturnValue` is not implemented for `u32`
6 |
7 = help: the following other types implement trait `TaskReturnValue`:
8 ()
9 <fn() -> ! as _export::HasOutput>::Output