diff options
Diffstat (limited to 'embassy-executor-macros/src/macros/task.rs')
| -rw-r--r-- | embassy-executor-macros/src/macros/task.rs | 151 |
1 files changed, 102 insertions, 49 deletions
diff --git a/embassy-executor-macros/src/macros/task.rs b/embassy-executor-macros/src/macros/task.rs index 96c6267b2..0404dba64 100644 --- a/embassy-executor-macros/src/macros/task.rs +++ b/embassy-executor-macros/src/macros/task.rs | |||
| @@ -2,47 +2,68 @@ use darling::export::NestedMeta; | |||
| 2 | use darling::FromMeta; | 2 | use darling::FromMeta; |
| 3 | use proc_macro2::{Span, TokenStream}; | 3 | use proc_macro2::{Span, TokenStream}; |
| 4 | use quote::{format_ident, quote}; | 4 | use quote::{format_ident, quote}; |
| 5 | use syn::{parse_quote, Expr, ExprLit, ItemFn, Lit, LitInt, ReturnType, Type}; | 5 | use syn::visit::Visit; |
| 6 | use syn::{Expr, ExprLit, Lit, LitInt, ReturnType, Type}; | ||
| 6 | 7 | ||
| 7 | use crate::util::ctxt::Ctxt; | 8 | use crate::util::*; |
| 8 | 9 | ||
| 9 | #[derive(Debug, FromMeta)] | 10 | #[derive(Debug, FromMeta, Default)] |
| 10 | struct Args { | 11 | struct Args { |
| 11 | #[darling(default)] | 12 | #[darling(default)] |
| 12 | pool_size: Option<syn::Expr>, | 13 | pool_size: Option<syn::Expr>, |
| 13 | } | 14 | } |
| 14 | 15 | ||
| 15 | pub fn run(args: &[NestedMeta], f: syn::ItemFn) -> Result<TokenStream, TokenStream> { | 16 | pub fn run(args: TokenStream, item: TokenStream) -> TokenStream { |
| 16 | let args = Args::from_list(args).map_err(|e| e.write_errors())?; | 17 | let mut errors = TokenStream::new(); |
| 18 | |||
| 19 | // If any of the steps for this macro fail, we still want to expand to an item that is as close | ||
| 20 | // to the expected output as possible. This helps out IDEs such that completions and other | ||
| 21 | // related features keep working. | ||
| 22 | let f: ItemFn = match syn::parse2(item.clone()) { | ||
| 23 | Ok(x) => x, | ||
| 24 | Err(e) => return token_stream_with_error(item, e), | ||
| 25 | }; | ||
| 26 | |||
| 27 | let args = match NestedMeta::parse_meta_list(args) { | ||
| 28 | Ok(x) => x, | ||
| 29 | Err(e) => return token_stream_with_error(item, e), | ||
| 30 | }; | ||
| 31 | |||
| 32 | let args = match Args::from_list(&args) { | ||
| 33 | Ok(x) => x, | ||
| 34 | Err(e) => { | ||
| 35 | errors.extend(e.write_errors()); | ||
| 36 | Args::default() | ||
| 37 | } | ||
| 38 | }; | ||
| 17 | 39 | ||
| 18 | let pool_size = args.pool_size.unwrap_or(Expr::Lit(ExprLit { | 40 | let pool_size = args.pool_size.unwrap_or(Expr::Lit(ExprLit { |
| 19 | attrs: vec![], | 41 | attrs: vec![], |
| 20 | lit: Lit::Int(LitInt::new("1", Span::call_site())), | 42 | lit: Lit::Int(LitInt::new("1", Span::call_site())), |
| 21 | })); | 43 | })); |
| 22 | 44 | ||
| 23 | let ctxt = Ctxt::new(); | ||
| 24 | |||
| 25 | if f.sig.asyncness.is_none() { | 45 | if f.sig.asyncness.is_none() { |
| 26 | ctxt.error_spanned_by(&f.sig, "task functions must be async"); | 46 | error(&mut errors, &f.sig, "task functions must be async"); |
| 27 | } | 47 | } |
| 28 | if !f.sig.generics.params.is_empty() { | 48 | if !f.sig.generics.params.is_empty() { |
| 29 | ctxt.error_spanned_by(&f.sig, "task functions must not be generic"); | 49 | error(&mut errors, &f.sig, "task functions must not be generic"); |
| 30 | } | 50 | } |
| 31 | if !f.sig.generics.where_clause.is_none() { | 51 | if !f.sig.generics.where_clause.is_none() { |
| 32 | ctxt.error_spanned_by(&f.sig, "task functions must not have `where` clauses"); | 52 | error(&mut errors, &f.sig, "task functions must not have `where` clauses"); |
| 33 | } | 53 | } |
| 34 | if !f.sig.abi.is_none() { | 54 | if !f.sig.abi.is_none() { |
| 35 | ctxt.error_spanned_by(&f.sig, "task functions must not have an ABI qualifier"); | 55 | error(&mut errors, &f.sig, "task functions must not have an ABI qualifier"); |
| 36 | } | 56 | } |
| 37 | if !f.sig.variadic.is_none() { | 57 | if !f.sig.variadic.is_none() { |
| 38 | ctxt.error_spanned_by(&f.sig, "task functions must not be variadic"); | 58 | error(&mut errors, &f.sig, "task functions must not be variadic"); |
| 39 | } | 59 | } |
| 40 | match &f.sig.output { | 60 | match &f.sig.output { |
| 41 | ReturnType::Default => {} | 61 | ReturnType::Default => {} |
| 42 | ReturnType::Type(_, ty) => match &**ty { | 62 | ReturnType::Type(_, ty) => match &**ty { |
| 43 | Type::Tuple(tuple) if tuple.elems.is_empty() => {} | 63 | Type::Tuple(tuple) if tuple.elems.is_empty() => {} |
| 44 | Type::Never(_) => {} | 64 | Type::Never(_) => {} |
| 45 | _ => ctxt.error_spanned_by( | 65 | _ => error( |
| 66 | &mut errors, | ||
| 46 | &f.sig, | 67 | &f.sig, |
| 47 | "task functions must either not return a value, return `()` or return `!`", | 68 | "task functions must either not return a value, return `()` or return `!`", |
| 48 | ), | 69 | ), |
| @@ -55,26 +76,31 @@ pub fn run(args: &[NestedMeta], f: syn::ItemFn) -> Result<TokenStream, TokenStre | |||
| 55 | for arg in fargs.iter_mut() { | 76 | for arg in fargs.iter_mut() { |
| 56 | match arg { | 77 | match arg { |
| 57 | syn::FnArg::Receiver(_) => { | 78 | syn::FnArg::Receiver(_) => { |
| 58 | ctxt.error_spanned_by(arg, "task functions must not have receiver arguments"); | 79 | error(&mut errors, arg, "task functions must not have receiver arguments"); |
| 59 | } | 80 | } |
| 60 | syn::FnArg::Typed(t) => match t.pat.as_mut() { | 81 | syn::FnArg::Typed(t) => { |
| 61 | syn::Pat::Ident(id) => { | 82 | check_arg_ty(&mut errors, &t.ty); |
| 62 | id.mutability = None; | 83 | match t.pat.as_mut() { |
| 63 | args.push((id.clone(), t.attrs.clone())); | 84 | syn::Pat::Ident(id) => { |
| 85 | id.mutability = None; | ||
| 86 | args.push((id.clone(), t.attrs.clone())); | ||
| 87 | } | ||
| 88 | _ => { | ||
| 89 | error( | ||
| 90 | &mut errors, | ||
| 91 | arg, | ||
| 92 | "pattern matching in task arguments is not yet supported", | ||
| 93 | ); | ||
| 94 | } | ||
| 64 | } | 95 | } |
| 65 | _ => { | 96 | } |
| 66 | ctxt.error_spanned_by(arg, "pattern matching in task arguments is not yet supported"); | ||
| 67 | } | ||
| 68 | }, | ||
| 69 | } | 97 | } |
| 70 | } | 98 | } |
| 71 | 99 | ||
| 72 | ctxt.check()?; | ||
| 73 | |||
| 74 | let task_ident = f.sig.ident.clone(); | 100 | let task_ident = f.sig.ident.clone(); |
| 75 | let task_inner_ident = format_ident!("__{}_task", task_ident); | 101 | let task_inner_ident = format_ident!("__{}_task", task_ident); |
| 76 | 102 | ||
| 77 | let mut task_inner = f; | 103 | let mut task_inner = f.clone(); |
| 78 | let visibility = task_inner.vis.clone(); | 104 | let visibility = task_inner.vis.clone(); |
| 79 | task_inner.vis = syn::Visibility::Inherited; | 105 | task_inner.vis = syn::Visibility::Inherited; |
| 80 | task_inner.sig.ident = task_inner_ident.clone(); | 106 | task_inner.sig.ident = task_inner_ident.clone(); |
| @@ -91,35 +117,43 @@ pub fn run(args: &[NestedMeta], f: syn::ItemFn) -> Result<TokenStream, TokenStre | |||
| 91 | } | 117 | } |
| 92 | 118 | ||
| 93 | #[cfg(feature = "nightly")] | 119 | #[cfg(feature = "nightly")] |
| 94 | let mut task_outer: ItemFn = parse_quote! { | 120 | let mut task_outer_body = quote! { |
| 95 | #visibility fn #task_ident(#fargs) -> ::embassy_executor::SpawnToken<impl Sized> { | 121 | trait _EmbassyInternalTaskTrait { |
| 96 | trait _EmbassyInternalTaskTrait { | 122 | type Fut: ::core::future::Future + 'static; |
| 97 | type Fut: ::core::future::Future + 'static; | 123 | fn construct(#fargs) -> Self::Fut; |
| 98 | fn construct(#fargs) -> Self::Fut; | 124 | } |
| 99 | } | ||
| 100 | 125 | ||
| 101 | impl _EmbassyInternalTaskTrait for () { | 126 | impl _EmbassyInternalTaskTrait for () { |
| 102 | type Fut = impl core::future::Future + 'static; | 127 | type Fut = impl core::future::Future + 'static; |
| 103 | fn construct(#fargs) -> Self::Fut { | 128 | fn construct(#fargs) -> Self::Fut { |
| 104 | #task_inner_ident(#(#full_args,)*) | 129 | #task_inner_ident(#(#full_args,)*) |
| 105 | } | ||
| 106 | } | 130 | } |
| 107 | |||
| 108 | const POOL_SIZE: usize = #pool_size; | ||
| 109 | static POOL: ::embassy_executor::raw::TaskPool<<() as _EmbassyInternalTaskTrait>::Fut, POOL_SIZE> = ::embassy_executor::raw::TaskPool::new(); | ||
| 110 | unsafe { POOL._spawn_async_fn(move || <() as _EmbassyInternalTaskTrait>::construct(#(#full_args,)*)) } | ||
| 111 | } | 131 | } |
| 132 | |||
| 133 | const POOL_SIZE: usize = #pool_size; | ||
| 134 | static POOL: ::embassy_executor::raw::TaskPool<<() as _EmbassyInternalTaskTrait>::Fut, POOL_SIZE> = ::embassy_executor::raw::TaskPool::new(); | ||
| 135 | unsafe { POOL._spawn_async_fn(move || <() as _EmbassyInternalTaskTrait>::construct(#(#full_args,)*)) } | ||
| 112 | }; | 136 | }; |
| 113 | #[cfg(not(feature = "nightly"))] | 137 | #[cfg(not(feature = "nightly"))] |
| 114 | let mut task_outer: ItemFn = parse_quote! { | 138 | let mut task_outer_body = quote! { |
| 115 | #visibility fn #task_ident(#fargs) -> ::embassy_executor::SpawnToken<impl Sized> { | 139 | const POOL_SIZE: usize = #pool_size; |
| 116 | const POOL_SIZE: usize = #pool_size; | 140 | static POOL: ::embassy_executor::_export::TaskPoolRef = ::embassy_executor::_export::TaskPoolRef::new(); |
| 117 | static POOL: ::embassy_executor::_export::TaskPoolRef = ::embassy_executor::_export::TaskPoolRef::new(); | 141 | unsafe { POOL.get::<_, POOL_SIZE>()._spawn_async_fn(move || #task_inner_ident(#(#full_args,)*)) } |
| 118 | unsafe { POOL.get::<_, POOL_SIZE>()._spawn_async_fn(move || #task_inner_ident(#(#full_args,)*)) } | ||
| 119 | } | ||
| 120 | }; | 142 | }; |
| 121 | 143 | ||
| 122 | task_outer.attrs.append(&mut task_inner.attrs.clone()); | 144 | let task_outer_attrs = task_inner.attrs.clone(); |
| 145 | |||
| 146 | if !errors.is_empty() { | ||
| 147 | task_outer_body = quote! { | ||
| 148 | #![allow(unused_variables, unreachable_code)] | ||
| 149 | let _x: ::embassy_executor::SpawnToken<()> = ::core::todo!(); | ||
| 150 | _x | ||
| 151 | }; | ||
| 152 | } | ||
| 153 | |||
| 154 | // Copy the generics + where clause to avoid more spurious errors. | ||
| 155 | let generics = &f.sig.generics; | ||
| 156 | let where_clause = &f.sig.generics.where_clause; | ||
| 123 | 157 | ||
| 124 | let result = quote! { | 158 | let result = quote! { |
| 125 | // This is the user's task function, renamed. | 159 | // This is the user's task function, renamed. |
| @@ -129,8 +163,27 @@ pub fn run(args: &[NestedMeta], f: syn::ItemFn) -> Result<TokenStream, TokenStre | |||
| 129 | #[doc(hidden)] | 163 | #[doc(hidden)] |
| 130 | #task_inner | 164 | #task_inner |
| 131 | 165 | ||
| 132 | #task_outer | 166 | #(#task_outer_attrs)* |
| 167 | #visibility fn #task_ident #generics (#fargs) -> ::embassy_executor::SpawnToken<impl Sized> #where_clause{ | ||
| 168 | #task_outer_body | ||
| 169 | } | ||
| 170 | |||
| 171 | #errors | ||
| 133 | }; | 172 | }; |
| 134 | 173 | ||
| 135 | Ok(result) | 174 | result |
| 175 | } | ||
| 176 | |||
| 177 | fn check_arg_ty(errors: &mut TokenStream, ty: &Type) { | ||
| 178 | struct Visitor<'a> { | ||
| 179 | errors: &'a mut TokenStream, | ||
| 180 | } | ||
| 181 | |||
| 182 | impl<'a, 'ast> Visit<'ast> for Visitor<'a> { | ||
| 183 | fn visit_type_impl_trait(&mut self, i: &'ast syn::TypeImplTrait) { | ||
| 184 | error(self.errors, i, "`impl Trait` is not allowed in task arguments. It is syntax sugar for generics, and tasks can't be generic."); | ||
| 185 | } | ||
| 186 | } | ||
| 187 | |||
| 188 | Visit::visit_type(&mut Visitor { errors }, ty); | ||
| 136 | } | 189 | } |
