From 539ff78ebbdedbb75d0faf940e3ee69f5e7f276a Mon Sep 17 00:00:00 2001 From: Brezak Date: Wed, 23 Jul 2025 19:51:31 +0200 Subject: [PATCH] embassy-executor: explicitly return impl Future in task inner task --- embassy-executor-macros/src/macros/task.rs | 19 ++++++++++++++++--- embassy-executor/src/lib.rs | 8 ++++---- embassy-executor/tests/test.rs | 11 ++++++++++- .../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; use proc_macro2::{Span, TokenStream}; use quote::{format_ident, quote}; use syn::visit::{self, Visit}; -use syn::{Expr, ExprLit, Lit, LitInt, ReturnType, Type}; +use syn::{Expr, ExprLit, Lit, LitInt, ReturnType, Type, Visibility}; use crate::util::*; @@ -135,6 +135,13 @@ pub fn run(args: TokenStream, item: TokenStream) -> TokenStream { let task_inner_future_output = match &f.sig.output { ReturnType::Default => quote! {-> impl ::core::future::Future}, // Special case the never type since we can't stuff it into a `impl Future` + ReturnType::Type(arrow, maybe_never) + if f.sig.asyncness.is_some() && matches!(**maybe_never, Type::Never(_)) => + { + quote! { + #arrow impl ::core::future::Future + } + } ReturnType::Type(arrow, maybe_never) if matches!(**maybe_never, Type::Never(_)) => quote! { #arrow #maybe_never }, @@ -149,14 +156,20 @@ pub fn run(args: TokenStream, item: TokenStream) -> TokenStream { }, }; + // We have to rename the function since it might be recursive; + let mut task_inner_function = f.clone(); + let task_inner_function_ident = format_ident!("__{}_task_inner_function", task_ident); + task_inner_function.sig.ident = task_inner_function_ident.clone(); + task_inner_function.vis = Visibility::Inherited; + let task_inner_body = if errors.is_empty() { quote! { - #f + #task_inner_function // SAFETY: All the preconditions to `#task_ident` apply to // all contexts `#task_inner_ident` is called in #unsafety { - #task_ident(#(#full_args,)*) + #task_inner_function_ident(#(#full_args,)*) } } } 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 { ); #[allow(dead_code)] - trait HasOutput { + pub trait HasOutput { type Output; } @@ -225,7 +225,7 @@ pub mod _export { } #[allow(dead_code)] - type Never = ! as HasOutput>::Output; + pub type Never = ! as HasOutput>::Output; } /// Implementation details for embassy macros. @@ -242,7 +242,7 @@ pub mod _export { impl TaskReturnValue for Never {} #[allow(dead_code)] - trait HasOutput { + pub trait HasOutput { type Output; } @@ -251,5 +251,5 @@ pub mod _export { } #[allow(dead_code)] - type Never = ! as HasOutput>::Output; + pub type Never = ! as HasOutput>::Output; } 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}; use std::task::Poll; use embassy_executor::raw::Executor; -use embassy_executor::task; +use embassy_executor::{task, Spawner}; #[export_name = "__pender"] fn __pender(context: *mut ()) { @@ -317,3 +317,12 @@ fn executor_task_cfg_args() { let (_, _, _) = (a, b, c); } } + +#[test] +fn recursive_task() { + #[embassy_executor::task(pool_size = 2)] + async fn task1() { + let spawner = unsafe { Spawner::for_current_executor().await }; + spawner.spawn(task1()); + } +} 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 `!` = note: use `async fn` or change the return type to `impl Future` = help: the following other types implement trait `TaskReturnValue`: () - ! as _export::HasOutput>::Output + ! as HasOutput>::Output