From f8352122a301c30db7c7851ebf50ad1608ebdad3 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Tue, 3 Jan 2023 15:10:59 +0100 Subject: [PATCH] Min codegen --- macros/src/analyze.rs | 23 +- macros/src/codegen.rs | 29 +-- macros/src/codegen/assertions.rs | 5 - macros/src/codegen/async_dispatchers.rs | 64 ++--- macros/src/codegen/dispatchers.rs | 146 ----------- macros/src/codegen/module.rs | 300 ++--------------------- macros/src/codegen/monotonic.rs | 280 --------------------- macros/src/codegen/post_init.rs | 20 +- macros/src/codegen/pre_init.rs | 68 +---- macros/src/codegen/shared_resources.rs | 6 +- macros/src/codegen/software_tasks.rs | 179 -------------- macros/src/codegen/timer_queue.rs | 170 ------------- macros/src/codegen/util.rs | 111 +-------- macros/src/syntax/analyze.rs | 57 +---- macros/src/syntax/ast.rs | 50 +--- macros/src/syntax/parse.rs | 121 +-------- macros/src/syntax/parse/app.rs | 58 +---- macros/src/syntax/parse/hardware_task.rs | 44 ++-- macros/src/syntax/parse/idle.rs | 18 +- macros/src/syntax/parse/init.rs | 22 +- macros/src/syntax/parse/software_task.rs | 26 +- macros/src/syntax/parse/util.rs | 26 +- 22 files changed, 129 insertions(+), 1694 deletions(-) delete mode 100644 macros/src/codegen/dispatchers.rs delete mode 100644 macros/src/codegen/monotonic.rs delete mode 100644 macros/src/codegen/software_tasks.rs delete mode 100644 macros/src/codegen/timer_queue.rs diff --git a/macros/src/analyze.rs b/macros/src/analyze.rs index ec12cfb4dc..cb42ad6f2a 100644 --- a/macros/src/analyze.rs +++ b/macros/src/analyze.rs @@ -10,8 +10,7 @@ use syn::Ident; /// Extend the upstream `Analysis` struct with our field pub struct Analysis { parent: analyze::Analysis, - pub interrupts_normal: BTreeMap, - pub interrupts_async: BTreeMap, + pub interrupts: BTreeMap, } impl ops::Deref for Analysis { @@ -30,27 +29,12 @@ pub fn app(analysis: analyze::Analysis, app: &App) -> Analysis { let priorities = app .software_tasks .values() - .filter(|task| !task.is_async) - .map(|task| task.args.priority) - .collect::>(); - - let priorities_async = app - .software_tasks - .values() - .filter(|task| task.is_async) .map(|task| task.args.priority) .collect::>(); // map from priorities to interrupts (holding name and attributes) - let interrupts_normal: BTreeMap = priorities - .iter() - .copied() - .rev() - .map(|p| (p, available_interrupt.pop().expect("UNREACHABLE"))) - .collect(); - - let interrupts_async: BTreeMap = priorities_async + let interrupts: BTreeMap = priorities .iter() .copied() .rev() @@ -59,7 +43,6 @@ pub fn app(analysis: analyze::Analysis, app: &App) -> Analysis { Analysis { parent: analysis, - interrupts_normal, - interrupts_async, + interrupts, } } diff --git a/macros/src/codegen.rs b/macros/src/codegen.rs index ef817325d8..618d9f3a68 100644 --- a/macros/src/codegen.rs +++ b/macros/src/codegen.rs @@ -6,20 +6,20 @@ use crate::syntax::ast::App; mod assertions; mod async_dispatchers; -mod dispatchers; +// mod dispatchers; mod hardware_tasks; mod idle; mod init; mod local_resources; mod local_resources_struct; mod module; -mod monotonic; +// mod monotonic; mod post_init; mod pre_init; mod shared_resources; mod shared_resources_struct; -mod software_tasks; -mod timer_queue; +// mod software_tasks; +// mod timer_queue; mod util; #[allow(clippy::too_many_lines)] @@ -92,14 +92,7 @@ pub fn app(app: &App, analysis: &Analysis) -> TokenStream2 { let (mod_app_hardware_tasks, root_hardware_tasks, user_hardware_tasks) = hardware_tasks::codegen(app, analysis); - let (mod_app_software_tasks, root_software_tasks, user_software_tasks) = - software_tasks::codegen(app, analysis); - - let monotonics = monotonic::codegen(app, analysis); - - let mod_app_dispatchers = dispatchers::codegen(app, analysis); let mod_app_async_dispatchers = async_dispatchers::codegen(app, analysis); - let mod_app_timer_queue = timer_queue::codegen(app, analysis); let user_imports = &app.user_imports; let user_code = &app.user_code; let name = &app.name; @@ -113,8 +106,6 @@ pub fn app(app: &App, analysis: &Analysis) -> TokenStream2 { /// Always include the device crate which contains the vector table use #device as #rt_err; - #monotonics - #(#user_imports)* /// User code from within the module @@ -125,8 +116,6 @@ pub fn app(app: &App, analysis: &Analysis) -> TokenStream2 { #(#user_hardware_tasks)* - #(#user_software_tasks)* - #(#root)* #mod_shared_resources @@ -135,9 +124,7 @@ pub fn app(app: &App, analysis: &Analysis) -> TokenStream2 { #(#root_hardware_tasks)* - #(#root_software_tasks)* - - /// App module + /// app module #(#mod_app)* #(#mod_app_shared_resources)* @@ -146,14 +133,8 @@ pub fn app(app: &App, analysis: &Analysis) -> TokenStream2 { #(#mod_app_hardware_tasks)* - #(#mod_app_software_tasks)* - - #(#mod_app_dispatchers)* - #(#mod_app_async_dispatchers)* - #(#mod_app_timer_queue)* - #(#mains)* } ) diff --git a/macros/src/codegen/assertions.rs b/macros/src/codegen/assertions.rs index 0f8326c732..dd94aa6d8c 100644 --- a/macros/src/codegen/assertions.rs +++ b/macros/src/codegen/assertions.rs @@ -16,11 +16,6 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec { stmts.push(quote!(rtic::export::assert_sync::<#ty>();)); } - for (_, monotonic) in &app.monotonics { - let ty = &monotonic.ty; - stmts.push(quote!(rtic::export::assert_monotonic::<#ty>();)); - } - let device = &app.args.device; let chunks_name = util::priority_mask_chunks_ident(); let no_basepri_checks: Vec<_> = app diff --git a/macros/src/codegen/async_dispatchers.rs b/macros/src/codegen/async_dispatchers.rs index 8b0e928bda..aa854d7f8e 100644 --- a/macros/src/codegen/async_dispatchers.rs +++ b/macros/src/codegen/async_dispatchers.rs @@ -7,65 +7,47 @@ use quote::quote; pub fn codegen(app: &App, analysis: &Analysis) -> Vec { let mut items = vec![]; - let interrupts = &analysis.interrupts_async; + let interrupts = &analysis.interrupts; // Generate executor definition and priority in global scope - for (name, task) in app.software_tasks.iter() { - if task.is_async { - let type_name = util::internal_task_ident(name, "F"); - let exec_name = util::internal_task_ident(name, "EXEC"); - let prio_name = util::internal_task_ident(name, "PRIORITY"); + for (name, _) in app.software_tasks.iter() { + let type_name = util::internal_task_ident(name, "F"); + let exec_name = util::internal_task_ident(name, "EXEC"); + let prio_name = util::internal_task_ident(name, "PRIORITY"); - items.push(quote!( - #[allow(non_camel_case_types)] - type #type_name = impl core::future::Future + 'static; - #[allow(non_upper_case_globals)] - static #exec_name: - rtic::RacyCell> = - rtic::RacyCell::new(rtic::export::executor::AsyncTaskExecutor::new()); + items.push(quote!( + #[allow(non_camel_case_types)] + type #type_name = impl core::future::Future + 'static; + #[allow(non_upper_case_globals)] + static #exec_name: + rtic::RacyCell> = + rtic::RacyCell::new(rtic::export::executor::AsyncTaskExecutor::new()); - // The executors priority, this can be any value - we will overwrite it when we - // start a task - #[allow(non_upper_case_globals)] - static #prio_name: rtic::RacyCell = - unsafe { rtic::RacyCell::new(rtic::export::Priority::new(0)) }; - )); - } + // The executors priority, this can be any value - we will overwrite it when we + // start a task + #[allow(non_upper_case_globals)] + static #prio_name: rtic::RacyCell = + unsafe { rtic::RacyCell::new(rtic::export::Priority::new(0)) }; + )); } for (&level, channel) in &analysis.channels { - if channel - .tasks - .iter() - .map(|task_name| !app.software_tasks[task_name].is_async) - .all(|is_not_async| is_not_async) - { - // check if all tasks are not async, if so don't generate this. - continue; - } - let mut stmts = vec![]; let device = &app.args.device; let enum_ = util::interrupt_ident(); let interrupt = util::suffixed(&interrupts[&level].0.to_string()); - for name in channel - .tasks - .iter() - .filter(|name| app.software_tasks[*name].is_async) - { + for name in channel.tasks.iter() { let exec_name = util::internal_task_ident(name, "EXEC"); let prio_name = util::internal_task_ident(name, "PRIORITY"); - let task = &app.software_tasks[name]; + // let task = &app.software_tasks[name]; // let cfgs = &task.cfgs; - let (_, tupled, pats, input_types) = util::regroup_inputs(&task.inputs); let executor_run_ident = util::executor_run_ident(name); - let n = util::capacity_literal(channel.capacity as usize + 1); let rq = util::rq_async_ident(name); let (rq_ty, rq_expr) = { ( - quote!(rtic::export::ASYNCRQ<#input_types, #n>), + quote!(rtic::export::ASYNCRQ<(), 2>), // TODO: This needs updating to a counter instead of a queue quote!(rtic::export::Queue::new()), ) }; @@ -79,13 +61,13 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec { stmts.push(quote!( if !(&*#exec_name.get()).is_running() { - if let Some(#tupled) = rtic::export::interrupt::free(|_| (&mut *#rq.get_mut()).dequeue()) { + if let Some(()) = rtic::export::interrupt::free(|_| (&mut *#rq.get_mut()).dequeue()) { // The async executor needs a static priority #prio_name.get_mut().write(rtic::export::Priority::new(PRIORITY)); let priority: &'static _ = &*#prio_name.get(); - (&mut *#exec_name.get_mut()).spawn(#name(#name::Context::new(priority) #(,#pats)*)); + (&mut *#exec_name.get_mut()).spawn(#name(#name::Context::new(priority))); #executor_run_ident.store(true, core::sync::atomic::Ordering::Relaxed); } } diff --git a/macros/src/codegen/dispatchers.rs b/macros/src/codegen/dispatchers.rs deleted file mode 100644 index 1a8b40422b..0000000000 --- a/macros/src/codegen/dispatchers.rs +++ /dev/null @@ -1,146 +0,0 @@ -use crate::syntax::ast::App; -use crate::{analyze::Analysis, codegen::util}; -use proc_macro2::TokenStream as TokenStream2; -use quote::quote; - -/// Generates task dispatchers -pub fn codegen(app: &App, analysis: &Analysis) -> Vec { - let mut items = vec![]; - - let interrupts = &analysis.interrupts_normal; - - for (&level, channel) in &analysis.channels { - if channel - .tasks - .iter() - .map(|task_name| app.software_tasks[task_name].is_async) - .all(|is_async| is_async) - { - // check if all tasks are async, if so don't generate this. - continue; - } - - let mut stmts = vec![]; - - let variants = channel - .tasks - .iter() - .filter(|name| !app.software_tasks[*name].is_async) - .map(|name| { - let cfgs = &app.software_tasks[name].cfgs; - - quote!( - #(#cfgs)* - #name - ) - }) - .collect::>(); - - // For future use - // let doc = format!( - // "Software tasks to be dispatched at priority level {}", - // level, - // ); - let t = util::spawn_t_ident(level); - items.push(quote!( - #[allow(non_snake_case)] - #[allow(non_camel_case_types)] - #[derive(Clone, Copy)] - // #[doc = #doc] - #[doc(hidden)] - pub enum #t { - #(#variants,)* - } - )); - - let n = util::capacity_literal(channel.capacity as usize + 1); - let rq = util::rq_ident(level); - // let (_, _, _, input_ty) = util::regroup_inputs(inputs); - let (rq_ty, rq_expr) = { - ( - quote!(rtic::export::SCRQ<#t, #n>), - quote!(rtic::export::Queue::new()), - ) - }; - - // For future use - // let doc = format!( - // "Queue of tasks ready to be dispatched at priority level {}", - // level - // ); - items.push(quote!( - #[doc(hidden)] - #[allow(non_camel_case_types)] - #[allow(non_upper_case_globals)] - static #rq: rtic::RacyCell<#rq_ty> = rtic::RacyCell::new(#rq_expr); - )); - - let interrupt = util::suffixed( - &interrupts - .get(&level) - .expect("RTIC-ICE: Unable to get interrrupt") - .0 - .to_string(), - ); - let arms = channel - .tasks - .iter() - .map(|name| { - let task = &app.software_tasks[name]; - let cfgs = &task.cfgs; - let fq = util::fq_ident(name); - let inputs = util::inputs_ident(name); - let (_, tupled, pats, _) = util::regroup_inputs(&task.inputs); - - if !task.is_async { - quote!( - #(#cfgs)* - #t::#name => { - let #tupled = - (&*#inputs - .get()) - .get_unchecked(usize::from(index)) - .as_ptr() - .read(); - (&mut *#fq.get_mut()).split().0.enqueue_unchecked(index); - let priority = &rtic::export::Priority::new(PRIORITY); - #name( - #name::Context::new(priority) - #(,#pats)* - ) - } - ) - } else { - quote!() - } - }) - .collect::>(); - - stmts.push(quote!( - while let Some((task, index)) = (&mut *#rq.get_mut()).split().1.dequeue() { - match task { - #(#arms)* - } - } - )); - - let doc = format!("Interrupt handler to dispatch tasks at priority {}", level); - let attribute = &interrupts[&level].1.attrs; - items.push(quote!( - #[allow(non_snake_case)] - #[doc = #doc] - #[no_mangle] - #(#attribute)* - unsafe fn #interrupt() { - /// The priority of this interrupt handler - const PRIORITY: u8 = #level; - - rtic::export::run(PRIORITY, || { - #(#stmts)* - }); - } - )); - } - - items -} diff --git a/macros/src/codegen/module.rs b/macros/src/codegen/module.rs index 7ac06c5c05..eb0cb65ba0 100644 --- a/macros/src/codegen/module.rs +++ b/macros/src/codegen/module.rs @@ -102,33 +102,6 @@ pub fn codegen( values.push(quote!(shared: #name::SharedResources::new(#priority))); } - if let Context::Init = ctxt { - let monotonic_types: Vec<_> = app - .monotonics - .iter() - .map(|(_, monotonic)| { - let mono = &monotonic.ty; - quote! {#mono} - }) - .collect(); - - let internal_monotonics_ident = util::mark_internal_name("Monotonics"); - - items.push(quote!( - /// Monotonics used by the system - #[allow(non_snake_case)] - #[allow(non_camel_case_types)] - pub struct #internal_monotonics_ident( - #(pub #monotonic_types),* - ); - )); - - module_items.push(quote!( - #[doc(inline)] - pub use super::#internal_monotonics_ident as Monotonics; - )); - } - let doc = match ctxt { Context::Idle => "Idle loop", Context::Init => "Initialization function", @@ -192,280 +165,45 @@ pub fn codegen( if let Context::SoftwareTask(..) = ctxt { let spawnee = &app.software_tasks[name]; let priority = spawnee.args.priority; - let t = util::spawn_t_ident(priority); let cfgs = &spawnee.cfgs; // Store a copy of the task cfgs task_cfgs = cfgs.clone(); - let (args, tupled, untupled, ty) = util::regroup_inputs(&spawnee.inputs); - let args = &args; - let tupled = &tupled; - let fq = util::fq_ident(name); - let rq = util::rq_ident(priority); - let inputs = util::inputs_ident(name); let device = &app.args.device; let enum_ = util::interrupt_ident(); - let interrupt = if spawnee.is_async { - &analysis - .interrupts_async - .get(&priority) - .expect("RTIC-ICE: interrupt identifer not found") - .0 - } else { - &analysis - .interrupts_normal - .get(&priority) - .expect("RTIC-ICE: interrupt identifer not found") - .0 - }; + let interrupt = &analysis + .interrupts + .get(&priority) + .expect("RTIC-ICE: interrupt identifer not found") + .0; let internal_spawn_ident = util::internal_task_ident(name, "spawn"); // Spawn caller - if spawnee.is_async { - let rq = util::rq_async_ident(name); - items.push(quote!( + let rq = util::rq_async_ident(name); + items.push(quote!( - #(#cfgs)* - /// Spawns the task directly - #[allow(non_snake_case)] - #[doc(hidden)] - pub fn #internal_spawn_ident(#(#args,)*) -> Result<(), #ty> { - let input = #tupled; + #(#cfgs)* + /// Spawns the task directly + #[allow(non_snake_case)] + #[doc(hidden)] + pub fn #internal_spawn_ident() -> Result<(), ()> { + unsafe { + let r = rtic::export::interrupt::free(|_| (&mut *#rq.get_mut()).enqueue(())); - unsafe { - let r = rtic::export::interrupt::free(|_| (&mut *#rq.get_mut()).enqueue(input)); - - if r.is_ok() { - rtic::pend(#device::#enum_::#interrupt); - } - - r - } - })); - } else { - items.push(quote!( - - #(#cfgs)* - /// Spawns the task directly - #[allow(non_snake_case)] - #[doc(hidden)] - pub fn #internal_spawn_ident(#(#args,)*) -> Result<(), #ty> { - let input = #tupled; - - unsafe { - if let Some(index) = rtic::export::interrupt::free(|_| (&mut *#fq.get_mut()).dequeue()) { - (&mut *#inputs - .get_mut()) - .get_unchecked_mut(usize::from(index)) - .as_mut_ptr() - .write(input); - - rtic::export::interrupt::free(|_| { - (&mut *#rq.get_mut()).enqueue_unchecked((#t::#name, index)); - }); - rtic::pend(#device::#enum_::#interrupt); - - Ok(()) - } else { - Err(input) - } + if r.is_ok() { + rtic::pend(#device::#enum_::#interrupt); } - })); - } + r + } + })); module_items.push(quote!( #(#cfgs)* #[doc(inline)] pub use super::#internal_spawn_ident as spawn; )); - - // Schedule caller - if !spawnee.is_async { - for (_, monotonic) in &app.monotonics { - let instants = util::monotonic_instants_ident(name, &monotonic.ident); - let monotonic_name = monotonic.ident.to_string(); - - let tq = util::tq_ident(&monotonic.ident.to_string()); - let t = util::schedule_t_ident(); - let m = &monotonic.ident; - let m_ident = util::monotonic_ident(&monotonic_name); - let m_isr = &monotonic.args.binds; - let enum_ = util::interrupt_ident(); - let spawn_handle_string = format!("{}::SpawnHandle", m); - - let (enable_interrupt, pend) = if &*m_isr.to_string() == "SysTick" { - ( - quote!(core::mem::transmute::<_, rtic::export::SYST>(()).enable_interrupt()), - quote!(rtic::export::SCB::set_pendst()), - ) - } else { - let rt_err = util::rt_err_ident(); - ( - quote!(rtic::export::NVIC::unmask(#rt_err::#enum_::#m_isr)), - quote!(rtic::pend(#rt_err::#enum_::#m_isr)), - ) - }; - - let tq_marker = &util::timer_queue_marker_ident(); - - let internal_spawn_handle_ident = - util::internal_monotonics_ident(name, m, "SpawnHandle"); - let internal_spawn_at_ident = util::internal_monotonics_ident(name, m, "spawn_at"); - let internal_spawn_after_ident = - util::internal_monotonics_ident(name, m, "spawn_after"); - - if monotonic.args.default { - module_items.push(quote!( - #[doc(inline)] - pub use #m::spawn_after; - #[doc(inline)] - pub use #m::spawn_at; - #[doc(inline)] - pub use #m::SpawnHandle; - )); - } - module_items.push(quote!( - pub mod #m { - #[doc(inline)] - pub use super::super::#internal_spawn_after_ident as spawn_after; - #[doc(inline)] - pub use super::super::#internal_spawn_at_ident as spawn_at; - #[doc(inline)] - pub use super::super::#internal_spawn_handle_ident as SpawnHandle; - } - )); - - items.push(quote!( - #(#cfgs)* - #[allow(non_snake_case)] - #[allow(non_camel_case_types)] - pub struct #internal_spawn_handle_ident { - #[doc(hidden)] - marker: u32, - } - - #(#cfgs)* - impl core::fmt::Debug for #internal_spawn_handle_ident { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - f.debug_struct(#spawn_handle_string).finish() - } - } - - #(#cfgs)* - impl #internal_spawn_handle_ident { - pub fn cancel(self) -> Result<#ty, ()> { - rtic::export::interrupt::free(|_| unsafe { - let tq = &mut *#tq.get_mut(); - if let Some((_task, index)) = tq.cancel_task_marker(self.marker) { - // Get the message - let msg = (&*#inputs - .get()) - .get_unchecked(usize::from(index)) - .as_ptr() - .read(); - // Return the index to the free queue - (&mut *#fq.get_mut()).split().0.enqueue_unchecked(index); - - Ok(msg) - } else { - Err(()) - } - }) - } - - #[inline] - pub fn reschedule_after( - self, - duration: <#m as rtic::Monotonic>::Duration - ) -> Result { - self.reschedule_at(monotonics::#m::now() + duration) - } - - pub fn reschedule_at( - self, - instant: <#m as rtic::Monotonic>::Instant - ) -> Result { - rtic::export::interrupt::free(|_| unsafe { - let marker = #tq_marker.get().read(); - #tq_marker.get_mut().write(marker.wrapping_add(1)); - - let tq = (&mut *#tq.get_mut()); - - tq.update_task_marker(self.marker, marker, instant, || #pend).map(|_| #name::#m::SpawnHandle { marker }) - }) - } - } - - - #(#cfgs)* - /// Spawns the task after a set duration relative to the current time - /// - /// This will use the time `Instant::new(0)` as baseline if called in `#[init]`, - /// so if you use a non-resetable timer use `spawn_at` when in `#[init]` - #[allow(non_snake_case)] - pub fn #internal_spawn_after_ident( - duration: <#m as rtic::Monotonic>::Duration - #(,#args)* - ) -> Result<#name::#m::SpawnHandle, #ty> - { - let instant = monotonics::#m::now(); - - #internal_spawn_at_ident(instant + duration #(,#untupled)*) - } - - #(#cfgs)* - /// Spawns the task at a fixed time instant - #[allow(non_snake_case)] - pub fn #internal_spawn_at_ident( - instant: <#m as rtic::Monotonic>::Instant - #(,#args)* - ) -> Result<#name::#m::SpawnHandle, #ty> { - unsafe { - let input = #tupled; - if let Some(index) = rtic::export::interrupt::free(|_| (&mut *#fq.get_mut()).dequeue()) { - (&mut *#inputs - .get_mut()) - .get_unchecked_mut(usize::from(index)) - .as_mut_ptr() - .write(input); - - (&mut *#instants - .get_mut()) - .get_unchecked_mut(usize::from(index)) - .as_mut_ptr() - .write(instant); - - rtic::export::interrupt::free(|_| { - let marker = #tq_marker.get().read(); - let nr = rtic::export::TaskNotReady { - task: #t::#name, - index, - instant, - marker, - }; - - #tq_marker.get_mut().write(#tq_marker.get().read().wrapping_add(1)); - - let tq = &mut *#tq.get_mut(); - - tq.enqueue_task_unchecked( - nr, - || #enable_interrupt, - || #pend, - (&mut *#m_ident.get_mut()).as_mut()); - - Ok(#name::#m::SpawnHandle { marker }) - }) - } else { - Err(input) - } - } - } - )); - } - } } if items.is_empty() { diff --git a/macros/src/codegen/monotonic.rs b/macros/src/codegen/monotonic.rs deleted file mode 100644 index 417a1d6a1c..0000000000 --- a/macros/src/codegen/monotonic.rs +++ /dev/null @@ -1,280 +0,0 @@ -use crate::syntax::ast::App; -use proc_macro2::TokenStream as TokenStream2; -use quote::quote; - -use crate::{analyze::Analysis, codegen::util}; - -/// Generates monotonic module dispatchers -pub fn codegen(app: &App, _analysis: &Analysis) -> TokenStream2 { - let mut monotonic_parts: Vec<_> = Vec::new(); - - let tq_marker = util::timer_queue_marker_ident(); - - for (_, monotonic) in &app.monotonics { - // let instants = util::monotonic_instants_ident(name, &monotonic.ident); - let monotonic_name = monotonic.ident.to_string(); - - let tq = util::tq_ident(&monotonic_name); - let m = &monotonic.ident; - let m_ident = util::monotonic_ident(&monotonic_name); - let m_isr = &monotonic.args.binds; - let enum_ = util::interrupt_ident(); - let name_str = &m.to_string(); - let ident = util::monotonic_ident(name_str); - let doc = &format!( - "This module holds the static implementation for `{}::now()`", - name_str - ); - - let (enable_interrupt, pend) = if &*m_isr.to_string() == "SysTick" { - ( - quote!(core::mem::transmute::<_, rtic::export::SYST>(()).enable_interrupt()), - quote!(rtic::export::SCB::set_pendst()), - ) - } else { - let rt_err = util::rt_err_ident(); - ( - quote!(rtic::export::NVIC::unmask(super::super::#rt_err::#enum_::#m_isr)), - quote!(rtic::pend(super::super::#rt_err::#enum_::#m_isr)), - ) - }; - - let default_monotonic = if monotonic.args.default { - quote!( - #[doc(inline)] - pub use #m::now; - #[doc(inline)] - pub use #m::delay; - #[doc(inline)] - pub use #m::delay_until; - #[doc(inline)] - pub use #m::timeout_at; - #[doc(inline)] - pub use #m::timeout_after; - ) - } else { - quote!() - }; - - monotonic_parts.push(quote! { - #default_monotonic - - #[doc = #doc] - #[allow(non_snake_case)] - pub mod #m { - /// Read the current time from this monotonic - pub fn now() -> ::Instant { - rtic::export::interrupt::free(|_| { - use rtic::Monotonic as _; - if let Some(m) = unsafe{ &mut *super::super::#ident.get_mut() } { - m.now() - } else { - ::zero() - } - }) - } - - /// Delay - #[inline(always)] - #[allow(non_snake_case)] - pub fn delay(duration: ::Duration) - -> DelayFuture { - let until = now() + duration; - DelayFuture { until, waker_storage: None } - } - - /// Delay until a specific time - #[inline(always)] - #[allow(non_snake_case)] - pub fn delay_until(instant: ::Instant) - -> DelayFuture { - let until = instant; - DelayFuture { until, waker_storage: None } - } - - /// Delay future. - #[allow(non_snake_case)] - #[allow(non_camel_case_types)] - pub struct DelayFuture { - until: ::Instant, - waker_storage: Option>>, - } - - impl Drop for DelayFuture { - fn drop(&mut self) { - if let Some(waker_storage) = &mut self.waker_storage { - rtic::export::interrupt::free(|_| unsafe { - let tq = &mut *super::super::#tq.get_mut(); - tq.cancel_waker_marker(waker_storage.val.marker); - }); - } - } - } - - impl core::future::Future for DelayFuture { - type Output = (); - - fn poll( - mut self: core::pin::Pin<&mut Self>, - cx: &mut core::task::Context<'_> - ) -> core::task::Poll { - let mut s = self.as_mut(); - let now = now(); - let until = s.until; - let is_ws_none = s.waker_storage.is_none(); - - if now >= until { - return core::task::Poll::Ready(()); - } else if is_ws_none { - rtic::export::interrupt::free(|_| unsafe { - let marker = super::super::#tq_marker.get().read(); - super::super::#tq_marker.get_mut().write(marker.wrapping_add(1)); - - let nr = s.waker_storage.insert(rtic::export::IntrusiveNode::new(rtic::export::WakerNotReady { - waker: cx.waker().clone(), - instant: until, - marker, - })); - - let tq = &mut *super::super::#tq.get_mut(); - - tq.enqueue_waker( - core::mem::transmute(nr), // Transmute the reference to static - || #enable_interrupt, - || #pend, - (&mut *super::super::#m_ident.get_mut()).as_mut()); - }); - } - - core::task::Poll::Pending - } - } - - /// Timeout future. - #[allow(non_snake_case)] - #[allow(non_camel_case_types)] - pub struct TimeoutFuture { - future: F, - until: ::Instant, - waker_storage: Option>>, - } - - impl Drop for TimeoutFuture { - fn drop(&mut self) { - if let Some(waker_storage) = &mut self.waker_storage { - rtic::export::interrupt::free(|_| unsafe { - let tq = &mut *super::super::#tq.get_mut(); - tq.cancel_waker_marker(waker_storage.val.marker); - }); - } - } - } - - /// Timeout after - #[allow(non_snake_case)] - #[inline(always)] - pub fn timeout_after( - future: F, - duration: ::Duration - ) -> TimeoutFuture { - let until = now() + duration; - TimeoutFuture { - future, - until, - waker_storage: None, - } - } - - /// Timeout at - #[allow(non_snake_case)] - #[inline(always)] - pub fn timeout_at( - future: F, - instant: ::Instant - ) -> TimeoutFuture { - TimeoutFuture { - future, - until: instant, - waker_storage: None, - } - } - - impl core::future::Future for TimeoutFuture - where - F: core::future::Future, - { - type Output = Result; - - fn poll( - self: core::pin::Pin<&mut Self>, - cx: &mut core::task::Context<'_> - ) -> core::task::Poll { - // SAFETY: We don't move the underlying pinned value. - let mut s = unsafe { self.get_unchecked_mut() }; - let future = unsafe { core::pin::Pin::new_unchecked(&mut s.future) }; - let now = now(); - let until = s.until; - let is_ws_none = s.waker_storage.is_none(); - - match future.poll(cx) { - core::task::Poll::Ready(r) => { - if let Some(waker_storage) = &mut s.waker_storage { - rtic::export::interrupt::free(|_| unsafe { - let tq = &mut *super::super::#tq.get_mut(); - tq.cancel_waker_marker(waker_storage.val.marker); - }); - } - - return core::task::Poll::Ready(Ok(r)); - } - core::task::Poll::Pending => { - if now >= until { - // Timeout - return core::task::Poll::Ready(Err(super::TimeoutError)); - } else if is_ws_none { - rtic::export::interrupt::free(|_| unsafe { - let marker = super::super::#tq_marker.get().read(); - super::super::#tq_marker.get_mut().write(marker.wrapping_add(1)); - - let nr = s.waker_storage.insert(rtic::export::IntrusiveNode::new(rtic::export::WakerNotReady { - waker: cx.waker().clone(), - instant: until, - marker, - })); - - let tq = &mut *super::super::#tq.get_mut(); - - tq.enqueue_waker( - core::mem::transmute(nr), // Transmute the reference to static - || #enable_interrupt, - || #pend, - (&mut *super::super::#m_ident.get_mut()).as_mut()); - }); - } - } - } - - core::task::Poll::Pending - } - } - } - }); - } - - if monotonic_parts.is_empty() { - quote!() - } else { - quote!( - pub use rtic::Monotonic as _; - - /// Holds static methods for each monotonic. - pub mod monotonics { - /// A timeout error. - #[derive(Debug)] - pub struct TimeoutError; - - #(#monotonic_parts)* - } - ) - } -} diff --git a/macros/src/codegen/post_init.rs b/macros/src/codegen/post_init.rs index df5daa1ec6..e8183b9368 100644 --- a/macros/src/codegen/post_init.rs +++ b/macros/src/codegen/post_init.rs @@ -1,7 +1,6 @@ use crate::syntax::ast::App; -use proc_macro2::{Span, TokenStream as TokenStream2}; +use proc_macro2::TokenStream as TokenStream2; use quote::quote; -use syn::Index; use crate::{analyze::Analysis, codegen::util}; @@ -43,23 +42,6 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec { } } - for (i, (monotonic, _)) in app.monotonics.iter().enumerate() { - // For future use - // let doc = format!(" RTIC internal: {}:{}", file!(), line!()); - // stmts.push(quote!(#[doc = #doc])); - - #[allow(clippy::cast_possible_truncation)] - let idx = Index { - index: i as u32, - span: Span::call_site(), - }; - stmts.push(quote!(monotonics.#idx.reset();)); - - // Store the monotonic - let name = util::monotonic_ident(&monotonic.to_string()); - stmts.push(quote!(#name.get_mut().write(Some(monotonics.#idx));)); - } - // Enable the interrupts -- this completes the `init`-ialization phase stmts.push(quote!(rtic::export::interrupt::enable();)); diff --git a/macros/src/codegen/pre_init.rs b/macros/src/codegen/pre_init.rs index ef3acba76d..14926888ab 100644 --- a/macros/src/codegen/pre_init.rs +++ b/macros/src/codegen/pre_init.rs @@ -13,20 +13,6 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec { // Disable interrupts -- `init` must run with interrupts disabled stmts.push(quote!(rtic::export::interrupt::disable();)); - // Populate the FreeQueue - for (name, task) in &app.software_tasks { - if task.is_async { - continue; - } - - let cap = task.args.capacity; - let fq_ident = util::fq_ident(name); - - stmts.push(quote!( - (0..#cap).for_each(|i| (&mut *#fq_ident.get_mut()).enqueue_unchecked(i)); - )); - } - stmts.push(quote!( // To set the variable in cortex_m so the peripherals cannot be taken multiple times let mut core: rtic::export::Peripherals = rtic::export::Peripherals::steal().into(); @@ -42,11 +28,7 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec { stmts.push(quote!(let _ = #rt_err::#interrupt::#name;)); } - let interrupt_ids = analysis - .interrupts_normal - .iter() - .map(|(p, (id, _))| (p, id)) - .chain(analysis.interrupts_async.iter().map(|(p, (id, _))| (p, id))); + let interrupt_ids = analysis.interrupts.iter().map(|(p, (id, _))| (p, id)); // Unmask interrupts and set their priorities for (&priority, name) in interrupt_ids.chain(app.hardware_tasks.values().filter_map(|task| { @@ -101,53 +83,5 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec { );)); } - // Initialize monotonic's interrupts - for (_, monotonic) in &app.monotonics { - let priority = if let Some(prio) = monotonic.args.priority { - quote! { #prio } - } else { - quote! { (1 << #nvic_prio_bits) } - }; - let binds = &monotonic.args.binds; - - let name = &monotonic.ident; - let es = format!( - "Maximum priority used by monotonic '{}' is more than supported by hardware", - name - ); - // Compile time assert that this priority is supported by the device - stmts.push(quote!( - const _: () = if (1 << #nvic_prio_bits) < #priority as usize { ::core::panic!(#es); }; - )); - - let mono_type = &monotonic.ty; - - if &*binds.to_string() == "SysTick" { - stmts.push(quote!( - core.SCB.set_priority( - rtic::export::SystemHandler::SysTick, - rtic::export::logical2hw(#priority, #nvic_prio_bits), - ); - - // Always enable monotonic interrupts if they should never be off - if !<#mono_type as rtic::Monotonic>::DISABLE_INTERRUPT_ON_EMPTY_QUEUE { - core::mem::transmute::<_, rtic::export::SYST>(()) - .enable_interrupt(); - } - )); - } else { - stmts.push(quote!( - core.NVIC.set_priority( - #rt_err::#interrupt::#binds, - rtic::export::logical2hw(#priority, #nvic_prio_bits), - ); - - // Always enable monotonic interrupts if they should never be off - if !<#mono_type as rtic::Monotonic>::DISABLE_INTERRUPT_ON_EMPTY_QUEUE { - rtic::export::NVIC::unmask(#rt_err::#interrupt::#binds); - } - )); - } - } stmts } diff --git a/macros/src/codegen/shared_resources.rs b/macros/src/codegen/shared_resources.rs index 66f3800234..b63e7432d6 100644 --- a/macros/src/codegen/shared_resources.rs +++ b/macros/src/codegen/shared_resources.rs @@ -111,11 +111,7 @@ pub fn codegen( }; // Computing mapping of used interrupts to masks - let interrupt_ids = analysis - .interrupts_normal - .iter() - .map(|(p, (id, _))| (p, id)) - .chain(analysis.interrupts_async.iter().map(|(p, (id, _))| (p, id))); + let interrupt_ids = analysis.interrupts.iter().map(|(p, (id, _))| (p, id)); let mut prio_to_masks = HashMap::new(); let device = &app.args.device; diff --git a/macros/src/codegen/software_tasks.rs b/macros/src/codegen/software_tasks.rs deleted file mode 100644 index f9247daed2..0000000000 --- a/macros/src/codegen/software_tasks.rs +++ /dev/null @@ -1,179 +0,0 @@ -use crate::syntax::{ast::App, Context}; -use crate::{ - analyze::Analysis, - codegen::{local_resources_struct, module, shared_resources_struct, util}, -}; -use proc_macro2::TokenStream as TokenStream2; -use quote::quote; - -pub fn codegen( - app: &App, - analysis: &Analysis, -) -> ( - // mod_app_software_tasks -- free queues, buffers and `${task}Resources` constructors - Vec, - // root_software_tasks -- items that must be placed in the root of the crate: - // - `${task}Locals` structs - // - `${task}Resources` structs - // - `${task}` modules - Vec, - // user_software_tasks -- the `#[task]` functions written by the user - Vec, -) { - let mut mod_app = vec![]; - let mut root = vec![]; - let mut user_tasks = vec![]; - - // Any task - for (name, task) in app.software_tasks.iter() { - let inputs = &task.inputs; - let (_, _, _, input_ty) = util::regroup_inputs(inputs); - - let cap = task.args.capacity; - let cap_lit = util::capacity_literal(cap as usize); - let cap_lit_p1 = util::capacity_literal(cap as usize + 1); - - if !task.is_async { - // Create free queues and inputs / instants buffers - let fq = util::fq_ident(name); - - #[allow(clippy::redundant_closure)] - let (fq_ty, fq_expr, mk_uninit): (_, _, Box Option<_>>) = { - ( - quote!(rtic::export::SCFQ<#cap_lit_p1>), - quote!(rtic::export::Queue::new()), - Box::new(|| Some(util::link_section_uninit())), - ) - }; - - mod_app.push(quote!( - // /// Queue version of a free-list that keeps track of empty slots in - // /// the following buffers - #[allow(non_camel_case_types)] - #[allow(non_upper_case_globals)] - #[doc(hidden)] - static #fq: rtic::RacyCell<#fq_ty> = rtic::RacyCell::new(#fq_expr); - )); - - let elems = &(0..cap) - .map(|_| quote!(core::mem::MaybeUninit::uninit())) - .collect::>(); - - for (_, monotonic) in &app.monotonics { - let instants = util::monotonic_instants_ident(name, &monotonic.ident); - let mono_type = &monotonic.ty; - - let uninit = mk_uninit(); - // For future use - // let doc = format!(" RTIC internal: {}:{}", file!(), line!()); - mod_app.push(quote!( - #uninit - // /// Buffer that holds the instants associated to the inputs of a task - // #[doc = #doc] - #[allow(non_camel_case_types)] - #[allow(non_upper_case_globals)] - #[doc(hidden)] - static #instants: - rtic::RacyCell<[core::mem::MaybeUninit<<#mono_type as rtic::Monotonic>::Instant>; #cap_lit]> = - rtic::RacyCell::new([#(#elems,)*]); - )); - } - - let uninit = mk_uninit(); - let inputs_ident = util::inputs_ident(name); - - // Buffer that holds the inputs of a task - mod_app.push(quote!( - #uninit - #[allow(non_camel_case_types)] - #[allow(non_upper_case_globals)] - #[doc(hidden)] - static #inputs_ident: rtic::RacyCell<[core::mem::MaybeUninit<#input_ty>; #cap_lit]> = - rtic::RacyCell::new([#(#elems,)*]); - )); - } - - if task.is_async { - let executor_ident = util::executor_run_ident(name); - mod_app.push(quote!( - #[allow(non_camel_case_types)] - #[allow(non_upper_case_globals)] - #[doc(hidden)] - static #executor_ident: core::sync::atomic::AtomicBool = - core::sync::atomic::AtomicBool::new(false); - )); - } - - let inputs = &task.inputs; - - // `${task}Resources` - let mut shared_needs_lt = false; - let mut local_needs_lt = false; - - // `${task}Locals` - if !task.args.local_resources.is_empty() { - let (item, constructor) = local_resources_struct::codegen( - Context::SoftwareTask(name), - &mut local_needs_lt, - app, - ); - - root.push(item); - - mod_app.push(constructor); - } - - if !task.args.shared_resources.is_empty() { - let (item, constructor) = shared_resources_struct::codegen( - Context::SoftwareTask(name), - &mut shared_needs_lt, - app, - ); - - root.push(item); - - mod_app.push(constructor); - } - - if !&task.is_extern { - let context = &task.context; - let attrs = &task.attrs; - let cfgs = &task.cfgs; - let stmts = &task.stmts; - let (async_marker, context_lifetime) = if task.is_async { - ( - quote!(async), - if shared_needs_lt || local_needs_lt { - quote!(<'static>) - } else { - quote!() - }, - ) - } else { - (quote!(), quote!()) - }; - - user_tasks.push(quote!( - #(#attrs)* - #(#cfgs)* - #[allow(non_snake_case)] - #async_marker fn #name(#context: #name::Context #context_lifetime #(,#inputs)*) { - use rtic::Mutex as _; - use rtic::mutex::prelude::*; - - #(#stmts)* - } - )); - } - - root.push(module::codegen( - Context::SoftwareTask(name), - shared_needs_lt, - local_needs_lt, - app, - analysis, - )); - } - - (mod_app, root, user_tasks) -} diff --git a/macros/src/codegen/timer_queue.rs b/macros/src/codegen/timer_queue.rs deleted file mode 100644 index 281148d9b4..0000000000 --- a/macros/src/codegen/timer_queue.rs +++ /dev/null @@ -1,170 +0,0 @@ -use crate::syntax::ast::App; -use crate::{analyze::Analysis, codegen::util}; -use proc_macro2::TokenStream as TokenStream2; -use quote::quote; - -/// Generates timer queues and timer queue handlers -#[allow(clippy::too_many_lines)] -pub fn codegen(app: &App, analysis: &Analysis) -> Vec { - let mut items = vec![]; - - if !app.monotonics.is_empty() { - // Generate the marker counter used to track for `cancel` and `reschedule` - let tq_marker = util::timer_queue_marker_ident(); - items.push(quote!( - // #[doc = #doc] - #[doc(hidden)] - #[allow(non_camel_case_types)] - #[allow(non_upper_case_globals)] - static #tq_marker: rtic::RacyCell = rtic::RacyCell::new(0); - )); - - let t = util::schedule_t_ident(); - - // Enumeration of `schedule`-able tasks - { - let variants = app - .software_tasks - .iter() - .filter(|(_, task)| !task.is_async) - .map(|(name, task)| { - let cfgs = &task.cfgs; - - quote!( - #(#cfgs)* - #name - ) - }) - .collect::>(); - - // For future use - // let doc = "Tasks that can be scheduled".to_string(); - items.push(quote!( - // #[doc = #doc] - #[doc(hidden)] - #[allow(non_camel_case_types)] - #[derive(Clone, Copy)] - pub enum #t { - #(#variants,)* - } - )); - } - } - - for (_, monotonic) in &app.monotonics { - let monotonic_name = monotonic.ident.to_string(); - let tq = util::tq_ident(&monotonic_name); - let t = util::schedule_t_ident(); - let mono_type = &monotonic.ty; - let m_ident = util::monotonic_ident(&monotonic_name); - - // Static variables and resource proxy - { - // For future use - // let doc = &format!("Timer queue for {}", monotonic_name); - let cap: usize = app - .software_tasks - .iter() - .map(|(_name, task)| task.args.capacity as usize) - .sum(); - let n_task = util::capacity_literal(cap); - let tq_ty = quote!(rtic::export::TimerQueue<#mono_type, #t, #n_task>); - - // For future use - // let doc = format!(" RTIC internal: {}:{}", file!(), line!()); - items.push(quote!( - #[doc(hidden)] - #[allow(non_camel_case_types)] - #[allow(non_upper_case_globals)] - static #tq: rtic::RacyCell<#tq_ty> = rtic::RacyCell::new( - rtic::export::TimerQueue { - task_queue: rtic::export::SortedLinkedList::new_u16(), - waker_queue: rtic::export::IntrusiveSortedLinkedList::new(), - } - ); - )); - - let mono = util::monotonic_ident(&monotonic_name); - // For future use - // let doc = &format!("Storage for {}", monotonic_name); - - items.push(quote!( - #[doc(hidden)] - #[allow(non_camel_case_types)] - #[allow(non_upper_case_globals)] - static #mono: rtic::RacyCell> = rtic::RacyCell::new(None); - )); - } - - // Timer queue handler - { - let enum_ = util::interrupt_ident(); - let rt_err = util::rt_err_ident(); - - let arms = app - .software_tasks - .iter() - .filter(|(_, task)| !task.is_async) - .map(|(name, task)| { - let cfgs = &task.cfgs; - let priority = task.args.priority; - let rq = util::rq_ident(priority); - let rqt = util::spawn_t_ident(priority); - - // The interrupt that runs the task dispatcher - let interrupt = &analysis.interrupts_normal.get(&priority).expect("RTIC-ICE: interrupt not found").0; - - let pend = { - quote!( - rtic::pend(#rt_err::#enum_::#interrupt); - ) - }; - - quote!( - #(#cfgs)* - #t::#name => { - rtic::export::interrupt::free(|_| - (&mut *#rq.get_mut()).split().0.enqueue_unchecked((#rqt::#name, index)) - ); - - #pend - } - ) - }) - .collect::>(); - - let bound_interrupt = &monotonic.args.binds; - let disable_isr = if &*bound_interrupt.to_string() == "SysTick" { - quote!(core::mem::transmute::<_, rtic::export::SYST>(()).disable_interrupt()) - } else { - quote!(rtic::export::NVIC::mask(#rt_err::#enum_::#bound_interrupt)) - }; - - items.push(quote!( - #[no_mangle] - #[allow(non_snake_case)] - unsafe fn #bound_interrupt() { - while let Some((task, index)) = rtic::export::interrupt::free(|_| - if let Some(mono) = (&mut *#m_ident.get_mut()).as_mut() { - (&mut *#tq.get_mut()).dequeue(|| #disable_isr, mono) - } else { - // We can only use the timer queue if `init` has returned, and it - // writes the `Some(monotonic)` we are accessing here. - core::hint::unreachable_unchecked() - }) - { - match task { - #(#arms)* - } - } - - rtic::export::interrupt::free(|_| if let Some(mono) = (&mut *#m_ident.get_mut()).as_mut() { - mono.on_interrupt(); - }); - } - )); - } - } - - items -} diff --git a/macros/src/codegen/util.rs b/macros/src/codegen/util.rs index 151906da5f..61bde98fa5 100644 --- a/macros/src/codegen/util.rs +++ b/macros/src/codegen/util.rs @@ -3,20 +3,10 @@ use core::sync::atomic::{AtomicUsize, Ordering}; use crate::syntax::{ast::App, Context}; use proc_macro2::{Span, TokenStream as TokenStream2}; use quote::quote; -use syn::{Attribute, Ident, LitInt, PatType}; +use syn::{Attribute, Ident}; const RTIC_INTERNAL: &str = "__rtic_internal"; -/// Turns `capacity` into an unsuffixed integer literal -pub fn capacity_literal(capacity: usize) -> LitInt { - LitInt::new(&capacity.to_string(), Span::call_site()) -} - -/// Identifier for the free queue -pub fn fq_ident(task: &Ident) -> Ident { - mark_internal_name(&format!("{}_FQ", task)) -} - /// Generates a `Mutex` implementation pub fn impl_mutex( app: &App, @@ -60,30 +50,16 @@ pub fn impl_mutex( ) } -/// Generates an identifier for the `INPUTS` buffer (`spawn` & `schedule` API) -pub fn inputs_ident(task: &Ident) -> Ident { - mark_internal_name(&format!("{}_INPUTS", task)) -} - /// Generates an identifier for the `EXECUTOR_RUN` atomics (`async` API) pub fn executor_run_ident(task: &Ident) -> Ident { mark_internal_name(&format!("{}_EXECUTOR_RUN", task)) } -/// Generates an identifier for the `INSTANTS` buffer (`schedule` API) -pub fn monotonic_instants_ident(task: &Ident, monotonic: &Ident) -> Ident { - mark_internal_name(&format!("{}_{}_INSTANTS", task, monotonic)) -} - pub fn interrupt_ident() -> Ident { let span = Span::call_site(); Ident::new("interrupt", span) } -pub fn timer_queue_marker_ident() -> Ident { - mark_internal_name("TIMER_QUEUE_MARKER") -} - /// Whether `name` is an exception with configurable priority pub fn is_exception(name: &Ident) -> bool { let s = name.to_string(); @@ -106,11 +82,6 @@ pub fn mark_internal_name(name: &str) -> Ident { Ident::new(&format!("{}_{}", RTIC_INTERNAL, name), Span::call_site()) } -/// Generate an internal identifier for monotonics -pub fn internal_monotonics_ident(task: &Ident, monotonic: &Ident, ident_name: &str) -> Ident { - mark_internal_name(&format!("{}_{}_{}", task, monotonic, ident_name,)) -} - /// Generate an internal identifier for tasks pub fn internal_task_ident(task: &Ident, ident_name: &str) -> Ident { mark_internal_name(&format!("{}_{}", task, ident_name)) @@ -129,55 +100,6 @@ pub fn link_section_uninit() -> TokenStream2 { quote!(#[link_section = #section]) } -// Regroups the inputs of a task -// -// `inputs` could be &[`input: Foo`] OR &[`mut x: i32`, `ref y: i64`] -pub fn regroup_inputs( - inputs: &[PatType], -) -> ( - // args e.g. &[`_0`], &[`_0: i32`, `_1: i64`] - Vec, - // tupled e.g. `_0`, `(_0, _1)` - TokenStream2, - // untupled e.g. &[`_0`], &[`_0`, `_1`] - Vec, - // ty e.g. `Foo`, `(i32, i64)` - TokenStream2, -) { - if inputs.len() == 1 { - let ty = &inputs[0].ty; - - ( - vec![quote!(_0: #ty)], - quote!(_0), - vec![quote!(_0)], - quote!(#ty), - ) - } else { - let mut args = vec![]; - let mut pats = vec![]; - let mut tys = vec![]; - - for (i, input) in inputs.iter().enumerate() { - let i = Ident::new(&format!("_{}", i), Span::call_site()); - let ty = &input.ty; - - args.push(quote!(#i: #ty)); - - pats.push(quote!(#i)); - - tys.push(quote!(#ty)); - } - - let tupled = { - let pats = pats.clone(); - quote!((#(#pats,)*)) - }; - let ty = quote!((#(#tys,)*)); - (args, tupled, pats, ty) - } -} - /// Get the ident for the name of the task pub fn get_task_name(ctxt: Context, app: &App) -> Ident { let s = match ctxt { @@ -230,48 +152,17 @@ pub fn local_resources_ident(ctxt: Context, app: &App) -> Ident { mark_internal_name(&s) } -/// Generates an identifier for a ready queue -/// -/// There may be several task dispatchers, one for each priority level. -/// The ready queues are SPSC queues -pub fn rq_ident(priority: u8) -> Ident { - mark_internal_name(&format!("P{}_RQ", priority)) -} - /// Generates an identifier for a ready queue, async task version pub fn rq_async_ident(async_task_name: &Ident) -> Ident { mark_internal_name(&format!("ASYNC_TACK_{}_RQ", async_task_name)) } -/// Generates an identifier for the `enum` of `schedule`-able tasks -pub fn schedule_t_ident() -> Ident { - mark_internal_name("SCHED_T") -} - -/// Generates an identifier for the `enum` of `spawn`-able tasks -/// -/// This identifier needs the same structure as the `RQ` identifier because there's one ready queue -/// for each of these `T` enums -pub fn spawn_t_ident(priority: u8) -> Ident { - mark_internal_name(&format!("P{}_T", priority)) -} - /// Suffixed identifier pub fn suffixed(name: &str) -> Ident { let span = Span::call_site(); Ident::new(name, span) } -/// Generates an identifier for a timer queue -pub fn tq_ident(name: &str) -> Ident { - mark_internal_name(&format!("TQ_{}", name)) -} - -/// Generates an identifier for monotonic timer storage -pub fn monotonic_ident(name: &str) -> Ident { - mark_internal_name(&format!("MONOTONIC_STORAGE_{}", name)) -} - pub fn static_shared_resource_ident(name: &Ident) -> Ident { mark_internal_name(&format!("shared_resource_{}", name)) } diff --git a/macros/src/syntax/analyze.rs b/macros/src/syntax/analyze.rs index 44960b9e8a..ff0577dacc 100644 --- a/macros/src/syntax/analyze.rs +++ b/macros/src/syntax/analyze.rs @@ -16,19 +16,11 @@ pub(crate) fn app(app: &App) -> Result { type TaskName = String; type Priority = u8; - // The task list is a Tuple (Name, Shared Resources, Local Resources, Priority, IsAsync) - let task_resources_list: Vec<(TaskName, Vec<&Ident>, &LocalResources, Priority, bool)> = + // The task list is a Tuple (Name, Shared Resources, Local Resources, Priority) + let task_resources_list: Vec<(TaskName, Vec<&Ident>, &LocalResources, Priority)> = Some(&app.init) .iter() - .map(|ht| { - ( - "init".to_string(), - Vec::new(), - &ht.args.local_resources, - 0, - false, - ) - }) + .map(|ht| ("init".to_string(), Vec::new(), &ht.args.local_resources, 0)) .chain(app.idle.iter().map(|ht| { ( "idle".to_string(), @@ -39,7 +31,6 @@ pub(crate) fn app(app: &App) -> Result { .collect::>(), &ht.args.local_resources, 0, - false, ) })) .chain(app.software_tasks.iter().map(|(name, ht)| { @@ -52,7 +43,6 @@ pub(crate) fn app(app: &App) -> Result { .collect::>(), &ht.args.local_resources, ht.args.priority, - ht.is_async, ) })) .chain(app.hardware_tasks.iter().map(|(name, ht)| { @@ -65,7 +55,6 @@ pub(crate) fn app(app: &App) -> Result { .collect::>(), &ht.args.local_resources, ht.args.priority, - false, ) })) .collect(); @@ -84,21 +73,20 @@ pub(crate) fn app(app: &App) -> Result { // Check that lock_free resources are correct for lf_res in lock_free.iter() { - for (task, tr, _, priority, is_async) in task_resources_list.iter() { + for (task, tr, _, priority) in task_resources_list.iter() { for r in tr { // Get all uses of resources annotated lock_free if lf_res == r { // lock_free resources are not allowed in async tasks - if *is_async { - error.push(syn::Error::new( + error.push(syn::Error::new( r.span(), format!( "Lock free shared resource {:?} is used by an async tasks, which is forbidden", r.to_string(), ), )); - } + // TODO: Should this be removed? // HashMap returns the previous existing object if old.key == new.key if let Some(lf_res) = lf_hash.insert(r.to_string(), (task, r, priority)) { // Check if priority differ, if it does, append to @@ -150,7 +138,7 @@ pub(crate) fn app(app: &App) -> Result { // Check that local resources are not shared for lr in local { - for (task, _, local_resources, _, _) in task_resources_list.iter() { + for (task, _, local_resources, _) in task_resources_list.iter() { for (name, res) in local_resources.iter() { // Get all uses of resources annotated lock_free if lr == name { @@ -193,18 +181,7 @@ pub(crate) fn app(app: &App) -> Result { error.push(syn::Error::new( name.span(), format!( - "Software task {:?} has priority 0, but `#[idle]` is defined. 0-priority software tasks are only allowed if there is no `#[idle]`.", - name.to_string(), - ) - )); - } - - // 0-priority tasks must be async - if !task.is_async { - error.push(syn::Error::new( - name.span(), - format!( - "Software task {:?} has priority 0, but is not `async`. 0-priority software tasks must be `async`.", + "Async task {:?} has priority 0, but `#[idle]` is defined. 0-priority async tasks are only allowed if there is no `#[idle]`.", name.to_string(), ) )); @@ -263,7 +240,7 @@ pub(crate) fn app(app: &App) -> Result { // Create the list of used local resource Idents let mut used_local_resource = IndexSet::new(); - for (_, _, locals, _, _) in task_resources_list { + for (_, _, locals, _) in task_resources_list { for (local, _) in locals { used_local_resource.insert(local.clone()); } @@ -307,27 +284,11 @@ pub(crate) fn app(app: &App) -> Result { let channel = channels.entry(spawnee_prio).or_default(); channel.tasks.insert(name.clone()); - - if !spawnee.args.only_same_priority_spawn { - // Require `Send` if the task can be spawned from other priorities - spawnee.inputs.iter().for_each(|input| { - send_types.insert(input.ty.clone()); - }); - } } // No channel should ever be empty debug_assert!(channels.values().all(|channel| !channel.tasks.is_empty())); - // Compute channel capacities - for channel in channels.values_mut() { - channel.capacity = channel - .tasks - .iter() - .map(|name| app.software_tasks[name].args.capacity) - .sum(); - } - Ok(Analysis { channels, shared_resources: used_shared_resource, diff --git a/macros/src/syntax/ast.rs b/macros/src/syntax/ast.rs index 0f2e36f44c..ea6e402c9a 100644 --- a/macros/src/syntax/ast.rs +++ b/macros/src/syntax/ast.rs @@ -1,6 +1,6 @@ //! Abstract Syntax Tree -use syn::{Attribute, Expr, Ident, Item, ItemUse, Pat, PatType, Path, Stmt, Type}; +use syn::{Attribute, Expr, Ident, Item, ItemUse, Pat, Path, Stmt, Type}; use crate::syntax::Map; @@ -20,9 +20,6 @@ pub struct App { /// The `#[idle]` function pub idle: Option, - /// Monotonic clocks - pub monotonics: Map, - /// Resources shared between tasks defined in `#[shared]` pub shared_resources: Map, @@ -38,7 +35,7 @@ pub struct App { /// Hardware tasks: `#[task(binds = ..)]`s pub hardware_tasks: Map, - /// Software tasks: `#[task]` + /// Async software tasks: `#[task]` pub software_tasks: Map, } @@ -192,38 +189,7 @@ pub struct LocalResource { pub ty: Box, } -/// Monotonic -#[derive(Debug)] -#[non_exhaustive] -pub struct Monotonic { - /// `#[cfg]` attributes like `#[cfg(debug_assertions)]` - pub cfgs: Vec, - - /// The identifier of the monotonic - pub ident: Ident, - - /// The type of this monotonic - pub ty: Box, - - /// Monotonic args - pub args: MonotonicArgs, -} - -/// Monotonic metadata -#[derive(Debug)] -#[non_exhaustive] -pub struct MonotonicArgs { - /// The interrupt or exception that this monotonic is bound to - pub binds: Ident, - - /// The priority of this monotonic - pub priority: Option, - - /// If this is the default monotonic - pub default: bool, -} - -/// A software task +/// An async software task #[derive(Debug)] #[non_exhaustive] pub struct SoftwareTask { @@ -239,26 +205,17 @@ pub struct SoftwareTask { /// The context argument pub context: Box, - /// The inputs of this software task - pub inputs: Vec, - /// The statements that make up the task handler pub stmts: Vec, /// The task is declared externally pub is_extern: bool, - - /// If the task is marked as `async` - pub is_async: bool, } /// Software task metadata #[derive(Debug)] #[non_exhaustive] pub struct SoftwareTaskArgs { - /// The task capacity: the maximum number of pending messages that can be queued - pub capacity: u8, - /// The priority of this task pub priority: u8, @@ -275,7 +232,6 @@ pub struct SoftwareTaskArgs { impl Default for SoftwareTaskArgs { fn default() -> Self { Self { - capacity: 1, priority: 1, local_resources: LocalResources::new(), shared_resources: SharedResources::new(), diff --git a/macros/src/syntax/parse.rs b/macros/src/syntax/parse.rs index ceedaa9891..abdd677ab8 100644 --- a/macros/src/syntax/parse.rs +++ b/macros/src/syntax/parse.rs @@ -2,7 +2,6 @@ mod app; mod hardware_task; mod idle; mod init; -mod monotonic; mod resource; mod software_task; mod util; @@ -11,15 +10,12 @@ use proc_macro2::TokenStream as TokenStream2; use syn::{ braced, parenthesized, parse::{self, Parse, ParseStream, Parser}, - token::{self, Brace}, - Ident, Item, LitBool, LitInt, Path, Token, + token::Brace, + Ident, Item, LitInt, Token, }; use crate::syntax::{ - ast::{ - App, AppArgs, HardwareTaskArgs, IdleArgs, InitArgs, MonotonicArgs, SoftwareTaskArgs, - TaskLocal, - }, + ast::{App, AppArgs, HardwareTaskArgs, IdleArgs, InitArgs, SoftwareTaskArgs, TaskLocal}, Either, }; @@ -388,7 +384,6 @@ fn task_args(tokens: TokenStream2) -> parse::Result parse::Result parse::Result { - (|input: ParseStream<'_>| -> parse::Result { - let mut binds = None; - let mut priority = None; - let mut default = None; - - if !input.peek(token::Paren) { - return Err(parse::Error::new( - path.segments.first().unwrap().ident.span(), - "expected opening ( in #[monotonic( ... )]", - )); - } - - let content; - parenthesized!(content in input); - - if !content.is_empty() { - loop { - // Parse identifier name - let ident: Ident = content.parse()?; - // Handle equal sign - let _: Token![=] = content.parse()?; - - match &*ident.to_string() { - "binds" => { - if binds.is_some() { - return Err(parse::Error::new( - ident.span(), - "argument appears more than once", - )); - } - // Parse identifier name - let ident = content.parse()?; - - binds = Some(ident); - } - - "priority" => { - if priority.is_some() { - return Err(parse::Error::new( - ident.span(), - "argument appears more than once", - )); - } - - // #lit - let lit: LitInt = content.parse()?; - - if !lit.suffix().is_empty() { - return Err(parse::Error::new( - lit.span(), - "this literal must be unsuffixed", - )); - } - - let value = lit.base10_parse::().ok(); - if value.is_none() || value == Some(0) { - return Err(parse::Error::new( - lit.span(), - "this literal must be in the range 1...255", - )); - } - - priority = Some(value.unwrap()); - } - - "default" => { - if default.is_some() { - return Err(parse::Error::new( - ident.span(), - "argument appears more than once", - )); - } - - let lit: LitBool = content.parse()?; - default = Some(lit.value); - } - - _ => { - return Err(parse::Error::new(ident.span(), "unexpected argument")); - } - } - if content.is_empty() { - break; - } - - // Handle comma: , - let _: Token![,] = content.parse()?; - } - } - - let binds = if let Some(r) = binds { - r - } else { - return Err(parse::Error::new( - content.span(), - "`binds = ...` is missing", - )); - }; - let default = default.unwrap_or(false); - - Ok(MonotonicArgs { - binds, - priority, - default, - }) - }) - .parse2(tokens) -} diff --git a/macros/src/syntax/parse/app.rs b/macros/src/syntax/parse/app.rs index dd7c399908..8a9242e91d 100644 --- a/macros/src/syntax/parse/app.rs +++ b/macros/src/syntax/parse/app.rs @@ -5,14 +5,14 @@ use proc_macro2::TokenStream as TokenStream2; use syn::{ parse::{self, ParseStream, Parser}, spanned::Spanned, - Expr, ExprArray, Fields, ForeignItem, Ident, Item, LitBool, Path, Token, Type, Visibility, + Expr, ExprArray, Fields, ForeignItem, Ident, Item, LitBool, Path, Token, Visibility, }; use super::Input; use crate::syntax::{ ast::{ App, AppArgs, Dispatcher, Dispatchers, HardwareTask, Idle, IdleArgs, Init, InitArgs, - LocalResource, Monotonic, MonotonicArgs, SharedResource, SoftwareTask, + LocalResource, SharedResource, SoftwareTask, }, parse::{self as syntax_parse, util}, Either, Map, Set, @@ -150,7 +150,6 @@ impl App { let mut shared_resources = Map::new(); let mut local_resources_ident = None; let mut local_resources = Map::new(); - let mut monotonics = Map::new(); let mut hardware_tasks = Map::new(); let mut software_tasks = Map::new(); let mut user_imports = vec![]; @@ -158,7 +157,6 @@ impl App { let mut seen_idents = HashSet::::new(); let mut bindings = HashSet::::new(); - let mut monotonic_types = HashSet::::new(); let mut check_binding = |ident: &Ident| { if bindings.contains(ident) { @@ -186,19 +184,6 @@ impl App { Ok(()) }; - let mut check_monotonic = |ty: &Type| { - if monotonic_types.contains(ty) { - return Err(parse::Error::new( - ty.span(), - "this type is already used by another monotonic", - )); - } else { - monotonic_types.insert(ty.clone()); - } - - Ok(()) - }; - for mut item in input.items { match item { Item::Fn(mut item) => { @@ -448,44 +433,6 @@ impl App { // Store the user provided use-statements user_imports.push(itemuse_.clone()); } - Item::Type(ref mut type_item) => { - // Match types with the attribute #[monotonic] - if let Some(pos) = type_item - .attrs - .iter() - .position(|attr| util::attr_eq(attr, "monotonic")) - { - let span = type_item.ident.span(); - - if monotonics.contains_key(&type_item.ident) { - return Err(parse::Error::new( - span, - "`#[monotonic(...)]` on a specific type must appear at most once", - )); - } - - if type_item.vis != Visibility::Inherited { - return Err(parse::Error::new( - type_item.span(), - "this item must have inherited / private visibility", - )); - } - - check_monotonic(&*type_item.ty)?; - - let m = type_item.attrs.remove(pos); - let args = MonotonicArgs::parse(m)?; - - check_binding(&args.binds)?; - - let monotonic = Monotonic::parse(args, type_item, span)?; - - monotonics.insert(type_item.ident.clone(), monotonic); - } - - // All types are passed on - user_code.push(item.clone()); - } _ => { // Anything else within the module should not make any difference user_code.push(item.clone()); @@ -524,7 +471,6 @@ impl App { name: input.ident, init, idle, - monotonics, shared_resources, local_resources, user_imports, diff --git a/macros/src/syntax/parse/hardware_task.rs b/macros/src/syntax/parse/hardware_task.rs index 304bfcd3f0..ff94bc5190 100644 --- a/macros/src/syntax/parse/hardware_task.rs +++ b/macros/src/syntax/parse/hardware_task.rs @@ -23,19 +23,17 @@ impl HardwareTask { } if valid_signature { - if let Some((context, Ok(rest))) = util::parse_inputs(item.sig.inputs, &name) { - if rest.is_empty() { - let FilterAttrs { cfgs, attrs, .. } = util::filter_attributes(item.attrs); + if let Some(context) = util::parse_inputs(item.sig.inputs, &name) { + let FilterAttrs { cfgs, attrs, .. } = util::filter_attributes(item.attrs); - return Ok(HardwareTask { - args, - cfgs, - attrs, - context, - stmts: item.block.stmts, - is_extern: false, - }); - } + return Ok(HardwareTask { + args, + cfgs, + attrs, + context, + stmts: item.block.stmts, + is_extern: false, + }); } } @@ -69,19 +67,17 @@ impl HardwareTask { } if valid_signature { - if let Some((context, Ok(rest))) = util::parse_inputs(item.sig.inputs, &name) { - if rest.is_empty() { - let FilterAttrs { cfgs, attrs, .. } = util::filter_attributes(item.attrs); + if let Some(context) = util::parse_inputs(item.sig.inputs, &name) { + let FilterAttrs { cfgs, attrs, .. } = util::filter_attributes(item.attrs); - return Ok(HardwareTask { - args, - cfgs, - attrs, - context, - stmts: Vec::::new(), - is_extern: true, - }); - } + return Ok(HardwareTask { + args, + cfgs, + attrs, + context, + stmts: Vec::::new(), + is_extern: true, + }); } } diff --git a/macros/src/syntax/parse/idle.rs b/macros/src/syntax/parse/idle.rs index d9f3a99e6f..ffec358fc4 100644 --- a/macros/src/syntax/parse/idle.rs +++ b/macros/src/syntax/parse/idle.rs @@ -21,16 +21,14 @@ impl Idle { let name = item.sig.ident.to_string(); if valid_signature { - if let Some((context, Ok(rest))) = util::parse_inputs(item.sig.inputs, &name) { - if rest.is_empty() { - return Ok(Idle { - args, - attrs: item.attrs, - context, - name: item.sig.ident, - stmts: item.block.stmts, - }); - } + if let Some(context) = util::parse_inputs(item.sig.inputs, &name) { + return Ok(Idle { + args, + attrs: item.attrs, + context, + name: item.sig.ident, + stmts: item.block.stmts, + }); } } diff --git a/macros/src/syntax/parse/init.rs b/macros/src/syntax/parse/init.rs index 727ee20508..5ec1abaf46 100644 --- a/macros/src/syntax/parse/init.rs +++ b/macros/src/syntax/parse/init.rs @@ -25,18 +25,16 @@ impl Init { if let Ok((user_shared_struct, user_local_struct)) = util::type_is_init_return(&item.sig.output, &name) { - if let Some((context, Ok(rest))) = util::parse_inputs(item.sig.inputs, &name) { - if rest.is_empty() { - return Ok(Init { - args, - attrs: item.attrs, - context, - name: item.sig.ident, - stmts: item.block.stmts, - user_shared_struct, - user_local_struct, - }); - } + if let Some(context) = util::parse_inputs(item.sig.inputs, &name) { + return Ok(Init { + args, + attrs: item.attrs, + context, + name: item.sig.ident, + stmts: item.block.stmts, + user_shared_struct, + user_local_struct, + }); } } } diff --git a/macros/src/syntax/parse/software_task.rs b/macros/src/syntax/parse/software_task.rs index 2b1ac4a5b4..6be597e8fb 100644 --- a/macros/src/syntax/parse/software_task.rs +++ b/macros/src/syntax/parse/software_task.rs @@ -8,17 +8,16 @@ use crate::syntax::{ impl SoftwareTask { pub(crate) fn parse(args: SoftwareTaskArgs, item: ItemFn) -> parse::Result { - let valid_signature = - util::check_fn_signature(&item, true) && util::type_is_unit(&item.sig.output); + let valid_signature = util::check_fn_signature(&item, true) + && util::type_is_unit(&item.sig.output) + && item.sig.asyncness.is_some(); let span = item.sig.ident.span(); let name = item.sig.ident.to_string(); - let is_async = item.sig.asyncness.is_some(); - if valid_signature { - if let Some((context, Ok(inputs))) = util::parse_inputs(item.sig.inputs, &name) { + if let Some(context) = util::parse_inputs(item.sig.inputs, &name) { let FilterAttrs { cfgs, attrs, .. } = util::filter_attributes(item.attrs); return Ok(SoftwareTask { @@ -26,10 +25,8 @@ impl SoftwareTask { attrs, cfgs, context, - inputs, stmts: item.block.stmts, is_extern: false, - is_async, }); } } @@ -37,7 +34,7 @@ impl SoftwareTask { Err(parse::Error::new( span, &format!( - "this task handler must have type signature `(async) fn({}::Context, ..)`", + "this task handler must have type signature `async fn({}::Context)`", name ), )) @@ -49,17 +46,16 @@ impl SoftwareTask { args: SoftwareTaskArgs, item: ForeignItemFn, ) -> parse::Result { - let valid_signature = - util::check_foreign_fn_signature(&item, true) && util::type_is_unit(&item.sig.output); + let valid_signature = util::check_foreign_fn_signature(&item, true) + && util::type_is_unit(&item.sig.output) + && item.sig.asyncness.is_some(); let span = item.sig.ident.span(); let name = item.sig.ident.to_string(); - let is_async = item.sig.asyncness.is_some(); - if valid_signature { - if let Some((context, Ok(inputs))) = util::parse_inputs(item.sig.inputs, &name) { + if let Some(context) = util::parse_inputs(item.sig.inputs, &name) { let FilterAttrs { cfgs, attrs, .. } = util::filter_attributes(item.attrs); return Ok(SoftwareTask { @@ -67,10 +63,8 @@ impl SoftwareTask { attrs, cfgs, context, - inputs, stmts: Vec::::new(), is_extern: true, - is_async, }); } } @@ -78,7 +72,7 @@ impl SoftwareTask { Err(parse::Error::new( span, &format!( - "this task handler must have type signature `(async) fn({}::Context, ..)`", + "this task handler must have type signature `async fn({}::Context)`", name ), )) diff --git a/macros/src/syntax/parse/util.rs b/macros/src/syntax/parse/util.rs index 3fa51ef803..119129c0ae 100644 --- a/macros/src/syntax/parse/util.rs +++ b/macros/src/syntax/parse/util.rs @@ -3,8 +3,8 @@ use syn::{ parse::{self, ParseStream}, punctuated::Punctuated, spanned::Spanned, - Abi, AttrStyle, Attribute, Expr, FnArg, ForeignItemFn, Ident, ItemFn, Pat, PatType, Path, - PathArguments, ReturnType, Token, Type, Visibility, + Abi, AttrStyle, Attribute, Expr, FnArg, ForeignItemFn, Ident, ItemFn, Pat, Path, PathArguments, + ReturnType, Token, Type, Visibility, }; use crate::syntax::{ @@ -231,29 +231,23 @@ pub fn parse_local_resources(content: ParseStream<'_>) -> parse::Result, Result, FnArg>)>; - -pub fn parse_inputs(inputs: Punctuated, name: &str) -> ParseInputResult { +pub fn parse_inputs(inputs: Punctuated, name: &str) -> Option> { let mut inputs = inputs.into_iter(); match inputs.next() { Some(FnArg::Typed(first)) => { if type_is_path(&first.ty, &[name, "Context"]) { - let rest = inputs - .map(|arg| match arg { - FnArg::Typed(arg) => Ok(arg), - _ => Err(arg), - }) - .collect::, _>>(); - - Some((first.pat, rest)) - } else { - None + // No more inputs + if inputs.next().is_none() { + return Some(first.pat); + } } } - _ => None, + _ => {} } + + None } pub fn type_is_bottom(ty: &ReturnType) -> bool {