aboutsummaryrefslogtreecommitdiff
path: root/embassy-executor-macros
diff options
context:
space:
mode:
Diffstat (limited to 'embassy-executor-macros')
-rw-r--r--embassy-executor-macros/Cargo.toml3
-rw-r--r--embassy-executor-macros/src/lib.rs25
-rw-r--r--embassy-executor-macros/src/macros/main.rs22
-rw-r--r--embassy-executor-macros/src/macros/task.rs128
4 files changed, 143 insertions, 35 deletions
diff --git a/embassy-executor-macros/Cargo.toml b/embassy-executor-macros/Cargo.toml
index 5a38f2f06..9c2b40d03 100644
--- a/embassy-executor-macros/Cargo.toml
+++ b/embassy-executor-macros/Cargo.toml
@@ -1,6 +1,6 @@
1[package] 1[package]
2name = "embassy-executor-macros" 2name = "embassy-executor-macros"
3version = "0.6.2" 3version = "0.7.0"
4edition = "2021" 4edition = "2021"
5license = "MIT OR Apache-2.0" 5license = "MIT OR Apache-2.0"
6description = "macros for creating the entry point and tasks for embassy-executor" 6description = "macros for creating the entry point and tasks for embassy-executor"
@@ -23,3 +23,4 @@ proc-macro = true
23 23
24[features] 24[features]
25nightly = [] 25nightly = []
26metadata-name = []
diff --git a/embassy-executor-macros/src/lib.rs b/embassy-executor-macros/src/lib.rs
index 8e737db6a..962ce2e75 100644
--- a/embassy-executor-macros/src/lib.rs
+++ b/embassy-executor-macros/src/lib.rs
@@ -70,6 +70,31 @@ pub fn main_cortex_m(args: TokenStream, item: TokenStream) -> TokenStream {
70 main::run(args.into(), item.into(), &main::ARCH_CORTEX_M).into() 70 main::run(args.into(), item.into(), &main::ARCH_CORTEX_M).into()
71} 71}
72 72
73/// Creates a new `executor` instance and declares an application entry point for Cortex-A/R
74/// spawning the corresponding function body as an async task.
75///
76/// The following restrictions apply:
77///
78/// * The function must accept exactly 1 parameter, an `embassy_executor::Spawner` handle that it
79/// can use to spawn additional tasks.
80/// * The function must be declared `async`.
81/// * The function must not use generics.
82/// * Only a single `main` task may be declared.
83///
84/// ## Examples
85/// Spawning a task:
86///
87/// ``` rust
88/// #[embassy_executor::main]
89/// async fn main(_s: embassy_executor::Spawner) {
90/// // Function body
91/// }
92/// ```
93#[proc_macro_attribute]
94pub fn main_cortex_ar(args: TokenStream, item: TokenStream) -> TokenStream {
95 main::run(args.into(), item.into(), &main::ARCH_CORTEX_AR).into()
96}
97
73/// Creates a new `executor` instance and declares an architecture agnostic application entry point spawning 98/// Creates a new `executor` instance and declares an architecture agnostic application entry point spawning
74/// the corresponding function body as an async task. 99/// the corresponding function body as an async task.
75/// 100///
diff --git a/embassy-executor-macros/src/macros/main.rs b/embassy-executor-macros/src/macros/main.rs
index 95722b19b..dc470e51c 100644
--- a/embassy-executor-macros/src/macros/main.rs
+++ b/embassy-executor-macros/src/macros/main.rs
@@ -37,6 +37,12 @@ pub static ARCH_CORTEX_M: Arch = Arch {
37 executor_required: false, 37 executor_required: false,
38}; 38};
39 39
40pub static ARCH_CORTEX_AR: Arch = Arch {
41 default_entry: None,
42 flavor: Flavor::Standard,
43 executor_required: false,
44};
45
40pub static ARCH_SPIN: Arch = Arch { 46pub static ARCH_SPIN: Arch = Arch {
41 default_entry: None, 47 default_entry: None,
42 flavor: Flavor::Standard, 48 flavor: Flavor::Standard,
@@ -164,6 +170,14 @@ For example: `#[embassy_executor::main(entry = ..., executor = \"some_crate::Exe
164 let f_body = f.body; 170 let f_body = f.body;
165 let out = &f.sig.output; 171 let out = &f.sig.output;
166 172
173 let name_main_task = if cfg!(feature = "metadata-name") {
174 quote!(
175 main_task.metadata().set_name("main\0");
176 )
177 } else {
178 quote!()
179 };
180
167 let (main_ret, mut main_body) = match arch.flavor { 181 let (main_ret, mut main_body) = match arch.flavor {
168 Flavor::Standard => ( 182 Flavor::Standard => (
169 quote!(!), 183 quote!(!),
@@ -175,7 +189,9 @@ For example: `#[embassy_executor::main(entry = ..., executor = \"some_crate::Exe
175 let mut executor = #executor::new(); 189 let mut executor = #executor::new();
176 let executor = unsafe { __make_static(&mut executor) }; 190 let executor = unsafe { __make_static(&mut executor) };
177 executor.run(|spawner| { 191 executor.run(|spawner| {
178 spawner.must_spawn(__embassy_main(spawner)); 192 let main_task = __embassy_main(spawner).unwrap();
193 #name_main_task
194 spawner.spawn(main_task);
179 }) 195 })
180 }, 196 },
181 ), 197 ),
@@ -185,7 +201,9 @@ For example: `#[embassy_executor::main(entry = ..., executor = \"some_crate::Exe
185 let executor = ::std::boxed::Box::leak(::std::boxed::Box::new(#executor::new())); 201 let executor = ::std::boxed::Box::leak(::std::boxed::Box::new(#executor::new()));
186 202
187 executor.start(|spawner| { 203 executor.start(|spawner| {
188 spawner.must_spawn(__embassy_main(spawner)); 204 let main_task = __embassy_main(spawner).unwrap();
205 #name_main_task
206 spawner.spawn(main_task);
189 }); 207 });
190 208
191 Ok(()) 209 Ok(())
diff --git a/embassy-executor-macros/src/macros/task.rs b/embassy-executor-macros/src/macros/task.rs
index 91d6beee8..755948882 100644
--- a/embassy-executor-macros/src/macros/task.rs
+++ b/embassy-executor-macros/src/macros/task.rs
@@ -5,7 +5,7 @@ use darling::FromMeta;
5use proc_macro2::{Span, TokenStream}; 5use proc_macro2::{Span, TokenStream};
6use quote::{format_ident, quote}; 6use quote::{format_ident, quote};
7use syn::visit::{self, Visit}; 7use syn::visit::{self, Visit};
8use syn::{Expr, ExprLit, Lit, LitInt, ReturnType, Type}; 8use syn::{Expr, ExprLit, Lit, LitInt, ReturnType, Type, Visibility};
9 9
10use crate::util::*; 10use crate::util::*;
11 11
@@ -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();
@@ -106,13 +112,11 @@ pub fn run(args: TokenStream, item: TokenStream) -> TokenStream {
106 } 112 }
107 } 113 }
108 114
109 let task_ident = f.sig.ident.clone(); 115 // Copy the generics + where clause to avoid more spurious errors.
110 let task_inner_ident = format_ident!("__{}_task", task_ident); 116 let generics = &f.sig.generics;
111 117 let where_clause = &f.sig.generics.where_clause;
112 let mut task_inner = f.clone(); 118 let unsafety = &f.sig.unsafety;
113 let visibility = task_inner.vis.clone(); 119 let visibility = &f.vis;
114 task_inner.vis = syn::Visibility::Inherited;
115 task_inner.sig.ident = task_inner_ident.clone();
116 120
117 // assemble the original input arguments, 121 // assemble the original input arguments,
118 // including any attributes that may have 122 // including any attributes that may have
@@ -125,15 +129,79 @@ pub fn run(args: TokenStream, item: TokenStream) -> TokenStream {
125 )); 129 ));
126 } 130 }
127 131
132 let task_ident = f.sig.ident.clone();
133 let task_inner_ident = format_ident!("__{}_task", task_ident);
134
135 let task_inner_future_output = match &f.sig.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 = !>`
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 }
145 ReturnType::Type(arrow, maybe_never) if matches!(**maybe_never, Type::Never(_)) => quote! {
146 #arrow #maybe_never
147 },
148 // Grab the arrow span, why not
149 ReturnType::Type(arrow, typ) if f.sig.asyncness.is_some() => quote! {
150 #arrow impl ::core::future::Future<Output = #typ>
151 },
152 // We assume that if `f` isn't async, it must return `-> impl Future<...>`
153 // This is checked using traits later
154 ReturnType::Type(arrow, typ) => quote! {
155 #arrow #typ
156 },
157 };
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
165 let task_inner_body = if errors.is_empty() {
166 quote! {
167 #task_inner_function
168
169 // SAFETY: All the preconditions to `#task_ident` apply to
170 // all contexts `#task_inner_ident` is called in
171 #unsafety {
172 #task_inner_function_ident(#(#full_args,)*)
173 }
174 }
175 } else {
176 quote! {
177 async {::core::todo!()}
178 }
179 };
180
181 let task_inner = quote! {
182 #visibility fn #task_inner_ident #generics (#fargs)
183 #task_inner_future_output
184 #where_clause
185 {
186 #task_inner_body
187 }
188 };
189
190 let spawn = if returns_impl_trait {
191 quote!(spawn)
192 } else {
193 quote!(_spawn_async_fn)
194 };
195
128 #[cfg(feature = "nightly")] 196 #[cfg(feature = "nightly")]
129 let mut task_outer_body = quote! { 197 let mut task_outer_body = quote! {
130 trait _EmbassyInternalTaskTrait { 198 trait _EmbassyInternalTaskTrait {
131 type Fut: ::core::future::Future + 'static; 199 type Fut: ::core::future::Future<Output: #embassy_executor::_export::TaskReturnValue> + 'static;
132 fn construct(#fargs) -> Self::Fut; 200 fn construct(#fargs) -> Self::Fut;
133 } 201 }
134 202
135 impl _EmbassyInternalTaskTrait for () { 203 impl _EmbassyInternalTaskTrait for () {
136 type Fut = impl core::future::Future + 'static; 204 type Fut = impl core::future::Future<Output: #embassy_executor::_export::TaskReturnValue> + 'static;
137 fn construct(#fargs) -> Self::Fut { 205 fn construct(#fargs) -> Self::Fut {
138 #task_inner_ident(#(#full_args,)*) 206 #task_inner_ident(#(#full_args,)*)
139 } 207 }
@@ -141,7 +209,7 @@ pub fn run(args: TokenStream, item: TokenStream) -> TokenStream {
141 209
142 const POOL_SIZE: usize = #pool_size; 210 const POOL_SIZE: usize = #pool_size;
143 static POOL: #embassy_executor::raw::TaskPool<<() as _EmbassyInternalTaskTrait>::Fut, POOL_SIZE> = #embassy_executor::raw::TaskPool::new(); 211 static POOL: #embassy_executor::raw::TaskPool<<() as _EmbassyInternalTaskTrait>::Fut, POOL_SIZE> = #embassy_executor::raw::TaskPool::new();
144 unsafe { POOL._spawn_async_fn(move || <() as _EmbassyInternalTaskTrait>::construct(#(#full_args,)*)) } 212 unsafe { POOL.#spawn(move || <() as _EmbassyInternalTaskTrait>::construct(#(#full_args,)*)) }
145 }; 213 };
146 #[cfg(not(feature = "nightly"))] 214 #[cfg(not(feature = "nightly"))]
147 let mut task_outer_body = quote! { 215 let mut task_outer_body = quote! {
@@ -158,23 +226,19 @@ pub fn run(args: TokenStream, item: TokenStream) -> TokenStream {
158 {#embassy_executor::_export::task_pool_size::<_, _, _, POOL_SIZE>(#task_inner_ident)}, 226 {#embassy_executor::_export::task_pool_size::<_, _, _, POOL_SIZE>(#task_inner_ident)},
159 {#embassy_executor::_export::task_pool_align::<_, _, _, POOL_SIZE>(#task_inner_ident)}, 227 {#embassy_executor::_export::task_pool_align::<_, _, _, POOL_SIZE>(#task_inner_ident)},
160 > = unsafe { ::core::mem::transmute(#embassy_executor::_export::task_pool_new::<_, _, _, POOL_SIZE>(#task_inner_ident)) }; 228 > = unsafe { ::core::mem::transmute(#embassy_executor::_export::task_pool_new::<_, _, _, POOL_SIZE>(#task_inner_ident)) };
161 unsafe { __task_pool_get(#task_inner_ident)._spawn_async_fn(move || #task_inner_ident(#(#full_args,)*)) } 229 unsafe { __task_pool_get(#task_inner_ident).#spawn(move || #task_inner_ident(#(#full_args,)*)) }
162 }; 230 };
163 231
164 let task_outer_attrs = task_inner.attrs.clone(); 232 let task_outer_attrs = &f.attrs;
165 233
166 if !errors.is_empty() { 234 if !errors.is_empty() {
167 task_outer_body = quote! { 235 task_outer_body = quote! {
168 #![allow(unused_variables, unreachable_code)] 236 #![allow(unused_variables, unreachable_code)]
169 let _x: #embassy_executor::SpawnToken<()> = ::core::todo!(); 237 let _x: ::core::result::Result<#embassy_executor::SpawnToken<()>, #embassy_executor::SpawnError> = ::core::todo!();
170 _x 238 _x
171 }; 239 };
172 } 240 }
173 241
174 // Copy the generics + where clause to avoid more spurious errors.
175 let generics = &f.sig.generics;
176 let where_clause = &f.sig.generics.where_clause;
177
178 let result = quote! { 242 let result = quote! {
179 // This is the user's task function, renamed. 243 // This is the user's task function, renamed.
180 // We put it outside the #task_ident fn below, because otherwise 244 // We put it outside the #task_ident fn below, because otherwise
@@ -184,7 +248,7 @@ pub fn run(args: TokenStream, item: TokenStream) -> TokenStream {
184 #task_inner 248 #task_inner
185 249
186 #(#task_outer_attrs)* 250 #(#task_outer_attrs)*
187 #visibility fn #task_ident #generics (#fargs) -> #embassy_executor::SpawnToken<impl Sized> #where_clause{ 251 #visibility #unsafety fn #task_ident #generics (#fargs) -> ::core::result::Result<#embassy_executor::SpawnToken<impl Sized>, #embassy_executor::SpawnError> #where_clause{
188 #task_outer_body 252 #task_outer_body
189 } 253 }
190 254
@@ -201,7 +265,7 @@ fn check_arg_ty(errors: &mut TokenStream, ty: &Type) {
201 265
202 impl<'a, 'ast> Visit<'ast> for Visitor<'a> { 266 impl<'a, 'ast> Visit<'ast> for Visitor<'a> {
203 fn visit_type_reference(&mut self, i: &'ast syn::TypeReference) { 267 fn visit_type_reference(&mut self, i: &'ast syn::TypeReference) {
204 // only check for elided lifetime here. If not elided, it's checked by `visit_lifetime`. 268 // Only check for elided lifetime here. If not elided, it's checked by `visit_lifetime`.
205 if i.lifetime.is_none() { 269 if i.lifetime.is_none() {
206 error( 270 error(
207 self.errors, 271 self.errors,