diff options
| author | Brezak <[email protected]> | 2025-07-23 19:51:31 +0200 |
|---|---|---|
| committer | Brezak <[email protected]> | 2025-07-23 20:55:42 +0200 |
| commit | 539ff78ebbdedbb75d0faf940e3ee69f5e7f276a (patch) | |
| tree | 7096952c556d7262e59178e11f5d81753d08d12a | |
| parent | 1b42e624246f9355a91ef98ddf96d5af1b9b3687 (diff) | |
embassy-executor: explicitly return impl Future in task inner task
| -rw-r--r-- | embassy-executor-macros/src/macros/task.rs | 19 | ||||
| -rw-r--r-- | embassy-executor/src/lib.rs | 8 | ||||
| -rw-r--r-- | embassy-executor/tests/test.rs | 11 | ||||
| -rw-r--r-- | embassy-executor/tests/ui/bad_return_impl_future_nightly.stderr | 2 |
4 files changed, 31 insertions, 9 deletions
diff --git a/embassy-executor-macros/src/macros/task.rs b/embassy-executor-macros/src/macros/task.rs index 5b360b128..fc8673743 100644 --- a/embassy-executor-macros/src/macros/task.rs +++ b/embassy-executor-macros/src/macros/task.rs | |||
| @@ -5,7 +5,7 @@ use darling::FromMeta; | |||
| 5 | use proc_macro2::{Span, TokenStream}; | 5 | use proc_macro2::{Span, TokenStream}; |
| 6 | use quote::{format_ident, quote}; | 6 | use quote::{format_ident, quote}; |
| 7 | use syn::visit::{self, Visit}; | 7 | use syn::visit::{self, Visit}; |
| 8 | use syn::{Expr, ExprLit, Lit, LitInt, ReturnType, Type}; | 8 | use syn::{Expr, ExprLit, Lit, LitInt, ReturnType, Type, Visibility}; |
| 9 | 9 | ||
| 10 | use crate::util::*; | 10 | use crate::util::*; |
| 11 | 11 | ||
| @@ -135,6 +135,13 @@ pub fn run(args: TokenStream, item: TokenStream) -> TokenStream { | |||
| 135 | let task_inner_future_output = match &f.sig.output { | 135 | let task_inner_future_output = match &f.sig.output { |
| 136 | ReturnType::Default => quote! {-> impl ::core::future::Future<Output = ()>}, | 136 | ReturnType::Default => quote! {-> impl ::core::future::Future<Output = ()>}, |
| 137 | // Special case the never type since we can't stuff it into a `impl Future<Output = !>` | 137 | // Special case the never type since we can't stuff it into a `impl Future<Output = !>` |
| 138 | ReturnType::Type(arrow, maybe_never) | ||
| 139 | if f.sig.asyncness.is_some() && matches!(**maybe_never, Type::Never(_)) => | ||
| 140 | { | ||
| 141 | quote! { | ||
| 142 | #arrow impl ::core::future::Future<Output=#embassy_executor::_export::Never> | ||
| 143 | } | ||
| 144 | } | ||
| 138 | ReturnType::Type(arrow, maybe_never) if matches!(**maybe_never, Type::Never(_)) => quote! { | 145 | ReturnType::Type(arrow, maybe_never) if matches!(**maybe_never, Type::Never(_)) => quote! { |
| 139 | #arrow #maybe_never | 146 | #arrow #maybe_never |
| 140 | }, | 147 | }, |
| @@ -149,14 +156,20 @@ pub fn run(args: TokenStream, item: TokenStream) -> TokenStream { | |||
| 149 | }, | 156 | }, |
| 150 | }; | 157 | }; |
| 151 | 158 | ||
| 159 | // We have to rename the function since it might be recursive; | ||
| 160 | let mut task_inner_function = f.clone(); | ||
| 161 | let task_inner_function_ident = format_ident!("__{}_task_inner_function", task_ident); | ||
| 162 | task_inner_function.sig.ident = task_inner_function_ident.clone(); | ||
| 163 | task_inner_function.vis = Visibility::Inherited; | ||
| 164 | |||
| 152 | let task_inner_body = if errors.is_empty() { | 165 | let task_inner_body = if errors.is_empty() { |
| 153 | quote! { | 166 | quote! { |
| 154 | #f | 167 | #task_inner_function |
| 155 | 168 | ||
| 156 | // SAFETY: All the preconditions to `#task_ident` apply to | 169 | // SAFETY: All the preconditions to `#task_ident` apply to |
| 157 | // all contexts `#task_inner_ident` is called in | 170 | // all contexts `#task_inner_ident` is called in |
| 158 | #unsafety { | 171 | #unsafety { |
| 159 | #task_ident(#(#full_args,)*) | 172 | #task_inner_function_ident(#(#full_args,)*) |
| 160 | } | 173 | } |
| 161 | } | 174 | } |
| 162 | } else { | 175 | } else { |
diff --git a/embassy-executor/src/lib.rs b/embassy-executor/src/lib.rs index e174a0594..0747db032 100644 --- a/embassy-executor/src/lib.rs +++ b/embassy-executor/src/lib.rs | |||
| @@ -216,7 +216,7 @@ pub mod _export { | |||
| 216 | ); | 216 | ); |
| 217 | 217 | ||
| 218 | #[allow(dead_code)] | 218 | #[allow(dead_code)] |
| 219 | trait HasOutput { | 219 | pub trait HasOutput { |
| 220 | type Output; | 220 | type Output; |
| 221 | } | 221 | } |
| 222 | 222 | ||
| @@ -225,7 +225,7 @@ pub mod _export { | |||
| 225 | } | 225 | } |
| 226 | 226 | ||
| 227 | #[allow(dead_code)] | 227 | #[allow(dead_code)] |
| 228 | type Never = <fn() -> ! as HasOutput>::Output; | 228 | pub type Never = <fn() -> ! as HasOutput>::Output; |
| 229 | } | 229 | } |
| 230 | 230 | ||
| 231 | /// Implementation details for embassy macros. | 231 | /// Implementation details for embassy macros. |
| @@ -242,7 +242,7 @@ pub mod _export { | |||
| 242 | impl TaskReturnValue for Never {} | 242 | impl TaskReturnValue for Never {} |
| 243 | 243 | ||
| 244 | #[allow(dead_code)] | 244 | #[allow(dead_code)] |
| 245 | trait HasOutput { | 245 | pub trait HasOutput { |
| 246 | type Output; | 246 | type Output; |
| 247 | } | 247 | } |
| 248 | 248 | ||
| @@ -251,5 +251,5 @@ pub mod _export { | |||
| 251 | } | 251 | } |
| 252 | 252 | ||
| 253 | #[allow(dead_code)] | 253 | #[allow(dead_code)] |
| 254 | type Never = <fn() -> ! as HasOutput>::Output; | 254 | pub type Never = <fn() -> ! as HasOutput>::Output; |
| 255 | } | 255 | } |
diff --git a/embassy-executor/tests/test.rs b/embassy-executor/tests/test.rs index c1e7ec5d7..b84d3785a 100644 --- a/embassy-executor/tests/test.rs +++ b/embassy-executor/tests/test.rs | |||
| @@ -7,7 +7,7 @@ use std::sync::{Arc, Mutex}; | |||
| 7 | use std::task::Poll; | 7 | use std::task::Poll; |
| 8 | 8 | ||
| 9 | use embassy_executor::raw::Executor; | 9 | use embassy_executor::raw::Executor; |
| 10 | use embassy_executor::task; | 10 | use embassy_executor::{task, Spawner}; |
| 11 | 11 | ||
| 12 | #[export_name = "__pender"] | 12 | #[export_name = "__pender"] |
| 13 | fn __pender(context: *mut ()) { | 13 | fn __pender(context: *mut ()) { |
| @@ -317,3 +317,12 @@ fn executor_task_cfg_args() { | |||
| 317 | let (_, _, _) = (a, b, c); | 317 | let (_, _, _) = (a, b, c); |
| 318 | } | 318 | } |
| 319 | } | 319 | } |
| 320 | |||
| 321 | #[test] | ||
| 322 | fn recursive_task() { | ||
| 323 | #[embassy_executor::task(pool_size = 2)] | ||
| 324 | async fn task1() { | ||
| 325 | let spawner = unsafe { Spawner::for_current_executor().await }; | ||
| 326 | spawner.spawn(task1()); | ||
| 327 | } | ||
| 328 | } | ||
diff --git a/embassy-executor/tests/ui/bad_return_impl_future_nightly.stderr b/embassy-executor/tests/ui/bad_return_impl_future_nightly.stderr index 73ceb989d..3c3c9503b 100644 --- a/embassy-executor/tests/ui/bad_return_impl_future_nightly.stderr +++ b/embassy-executor/tests/ui/bad_return_impl_future_nightly.stderr | |||
| @@ -7,4 +7,4 @@ error[E0277]: task futures must resolve to `()` or `!` | |||
| 7 | = note: use `async fn` or change the return type to `impl Future<Output = ()>` | 7 | = note: use `async fn` or change the return type to `impl Future<Output = ()>` |
| 8 | = help: the following other types implement trait `TaskReturnValue`: | 8 | = help: the following other types implement trait `TaskReturnValue`: |
| 9 | () | 9 | () |
| 10 | <fn() -> ! as _export::HasOutput>::Output | 10 | <fn() -> ! as HasOutput>::Output |
