aboutsummaryrefslogtreecommitdiff
path: root/macros/src
diff options
context:
space:
mode:
authorGravatar Emil Fresk <emil.fresk@gmail.com> 2023-01-23 20:05:47 +0100
committerGravatar Henrik Tjäder <henrik@tjaders.com> 2023-03-01 00:33:31 +0100
commit306aa47170fd59369b7a184924e287dc3706d64d (patch)
tree75a331a63a4021f078e330bf2ce4edb1228e2ecf /macros/src
parentb8b881f446a226d6f3c4a7db7c9174590b47dbf6 (diff)
downloadrtic-306aa47170fd59369b7a184924e287dc3706d64d.tar.gz
rtic-306aa47170fd59369b7a184924e287dc3706d64d.tar.zst
rtic-306aa47170fd59369b7a184924e287dc3706d64d.zip
Add rtic-timer (timerqueue + monotonic) and rtic-monotonics (systick-monotonic)
Diffstat (limited to 'macros/src')
-rw-r--r--macros/src/analyze.rs49
-rw-r--r--macros/src/bindings.rs1
-rw-r--r--macros/src/check.rs70
-rw-r--r--macros/src/codegen.rs75
-rw-r--r--macros/src/codegen/assertions.rs53
-rw-r--r--macros/src/codegen/async_dispatchers.rs89
-rw-r--r--macros/src/codegen/hardware_tasks.rs87
-rw-r--r--macros/src/codegen/idle.rs58
-rw-r--r--macros/src/codegen/init.rs95
-rw-r--r--macros/src/codegen/local_resources.rs65
-rw-r--r--macros/src/codegen/local_resources_struct.rs102
-rw-r--r--macros/src/codegen/main.rs52
-rw-r--r--macros/src/codegen/module.rs194
-rw-r--r--macros/src/codegen/post_init.rs47
-rw-r--r--macros/src/codegen/pre_init.rs85
-rw-r--r--macros/src/codegen/shared_resources.rs183
-rw-r--r--macros/src/codegen/shared_resources_struct.rs119
-rw-r--r--macros/src/codegen/software_tasks.rs64
-rw-r--r--macros/src/codegen/util.rs238
-rw-r--r--macros/src/lib.rs92
-rw-r--r--macros/src/syntax.rs121
-rw-r--r--macros/src/syntax/.github/bors.toml3
-rw-r--r--macros/src/syntax/.github/workflows/build.yml213
-rw-r--r--macros/src/syntax/.github/workflows/changelog.yml28
-rw-r--r--macros/src/syntax/.github/workflows/properties/build.properties.json6
-rw-r--r--macros/src/syntax/.gitignore4
-rw-r--r--macros/src/syntax/.travis.yml31
-rw-r--r--macros/src/syntax/accessors.rs113
-rw-r--r--macros/src/syntax/analyze.rs417
-rw-r--r--macros/src/syntax/ast.rs335
-rw-r--r--macros/src/syntax/check.rs66
-rw-r--r--macros/src/syntax/optimize.rs36
-rw-r--r--macros/src/syntax/parse.rs363
-rw-r--r--macros/src/syntax/parse/app.rs480
-rw-r--r--macros/src/syntax/parse/hardware_task.rs76
-rw-r--r--macros/src/syntax/parse/idle.rs42
-rw-r--r--macros/src/syntax/parse/init.rs51
-rw-r--r--macros/src/syntax/parse/resource.rs55
-rw-r--r--macros/src/syntax/parse/software_task.rs76
-rw-r--r--macros/src/syntax/parse/util.rs338
-rw-r--r--macros/src/tests.rs4
-rw-r--r--macros/src/tests/single.rs40
42 files changed, 0 insertions, 4716 deletions
diff --git a/macros/src/analyze.rs b/macros/src/analyze.rs
deleted file mode 100644
index 65774f6c..00000000
--- a/macros/src/analyze.rs
+++ /dev/null
@@ -1,49 +0,0 @@
-use core::ops;
-use std::collections::{BTreeMap, BTreeSet};
-
-use crate::syntax::{
- analyze::{self, Priority},
- ast::{App, Dispatcher},
-};
-use syn::Ident;
-
-/// Extend the upstream `Analysis` struct with our field
-pub struct Analysis {
- parent: analyze::Analysis,
- pub interrupts: BTreeMap<Priority, (Ident, Dispatcher)>,
-}
-
-impl ops::Deref for Analysis {
- type Target = analyze::Analysis;
-
- fn deref(&self) -> &Self::Target {
- &self.parent
- }
-}
-
-// Assign an interrupt to each priority level
-pub fn app(analysis: analyze::Analysis, app: &App) -> Analysis {
- let mut available_interrupt = app.args.dispatchers.clone();
-
- // the set of priorities (each priority only once)
- let priorities = app
- .software_tasks
- .values()
- .map(|task| task.args.priority)
- .collect::<BTreeSet<_>>();
-
- // map from priorities to interrupts (holding name and attributes)
-
- let interrupts: BTreeMap<Priority, _> = priorities
- .iter()
- .filter(|prio| **prio > 0) // 0 prio tasks are run in main
- .copied()
- .rev()
- .map(|p| (p, available_interrupt.pop().expect("UNREACHABLE")))
- .collect();
-
- Analysis {
- parent: analysis,
- interrupts,
- }
-}
diff --git a/macros/src/bindings.rs b/macros/src/bindings.rs
deleted file mode 100644
index 8b137891..00000000
--- a/macros/src/bindings.rs
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/macros/src/check.rs b/macros/src/check.rs
deleted file mode 100644
index a05c82e8..00000000
--- a/macros/src/check.rs
+++ /dev/null
@@ -1,70 +0,0 @@
-use std::collections::HashSet;
-
-use crate::syntax::ast::App;
-use syn::parse;
-
-pub fn app(app: &App) -> parse::Result<()> {
- // Check that external (device-specific) interrupts are not named after known (Cortex-M)
- // exceptions
- for name in app.args.dispatchers.keys() {
- let name_s = name.to_string();
-
- match &*name_s {
- "NonMaskableInt" | "HardFault" | "MemoryManagement" | "BusFault" | "UsageFault"
- | "SecureFault" | "SVCall" | "DebugMonitor" | "PendSV" | "SysTick" => {
- return Err(parse::Error::new(
- name.span(),
- "Cortex-M exceptions can't be used as `extern` interrupts",
- ));
- }
-
- _ => {}
- }
- }
-
- // Check that there are enough external interrupts to dispatch the software tasks and the timer
- // queue handler
- let mut first = None;
- let priorities = app
- .software_tasks
- .iter()
- .map(|(name, task)| {
- first = Some(name);
- task.args.priority
- })
- .filter(|prio| *prio > 0)
- .collect::<HashSet<_>>();
-
- let need = priorities.len();
- let given = app.args.dispatchers.len();
- if need > given {
- let s = {
- format!(
- "not enough interrupts to dispatch \
- all software tasks (need: {need}; given: {given})"
- )
- };
-
- // If not enough tasks and first still is None, may cause
- // "custom attribute panicked" due to unwrap on None
- return Err(parse::Error::new(first.unwrap().span(), s));
- }
-
- // Check that all exceptions are valid; only exceptions with configurable priorities are
- // accepted
- for (name, task) in &app.hardware_tasks {
- let name_s = task.args.binds.to_string();
- match &*name_s {
- "NonMaskableInt" | "HardFault" => {
- return Err(parse::Error::new(
- name.span(),
- "only exceptions with configurable priority can be used as hardware tasks",
- ));
- }
-
- _ => {}
- }
- }
-
- Ok(())
-}
diff --git a/macros/src/codegen.rs b/macros/src/codegen.rs
deleted file mode 100644
index 24e98ce9..00000000
--- a/macros/src/codegen.rs
+++ /dev/null
@@ -1,75 +0,0 @@
-use proc_macro2::TokenStream as TokenStream2;
-use quote::quote;
-
-use crate::analyze::Analysis;
-use crate::syntax::ast::App;
-
-mod assertions;
-mod async_dispatchers;
-mod hardware_tasks;
-mod idle;
-mod init;
-mod local_resources;
-mod local_resources_struct;
-mod module;
-mod post_init;
-mod pre_init;
-mod shared_resources;
-mod shared_resources_struct;
-mod software_tasks;
-mod util;
-
-mod main;
-
-// TODO: organize codegen to actual parts of code
-// so `main::codegen` generates ALL the code for `fn main`,
-// `software_tasks::codegen` generates ALL the code for software tasks etc...
-
-#[allow(clippy::too_many_lines)]
-pub fn app(app: &App, analysis: &Analysis) -> TokenStream2 {
- // Generate the `main` function
- let main = main::codegen(app, analysis);
- let init_codegen = init::codegen(app, analysis);
- let idle_codegen = idle::codegen(app, analysis);
- let shared_resources_codegen = shared_resources::codegen(app, analysis);
- let local_resources_codegen = local_resources::codegen(app, analysis);
- let hardware_tasks_codegen = hardware_tasks::codegen(app, analysis);
- let software_tasks_codegen = software_tasks::codegen(app, analysis);
- let async_dispatchers_codegen = async_dispatchers::codegen(app, analysis);
-
- let user_imports = &app.user_imports;
- let user_code = &app.user_code;
- let name = &app.name;
- let device = &app.args.device;
-
- let rt_err = util::rt_err_ident();
-
- quote!(
- /// The RTIC application module
- pub mod #name {
- /// Always include the device crate which contains the vector table
- use #device as #rt_err;
-
- #(#user_imports)*
-
- #(#user_code)*
- /// User code end
-
- #init_codegen
-
- #idle_codegen
-
- #hardware_tasks_codegen
-
- #software_tasks_codegen
-
- #shared_resources_codegen
-
- #local_resources_codegen
-
- #async_dispatchers_codegen
-
- #main
- }
- )
-}
diff --git a/macros/src/codegen/assertions.rs b/macros/src/codegen/assertions.rs
deleted file mode 100644
index dd94aa6d..00000000
--- a/macros/src/codegen/assertions.rs
+++ /dev/null
@@ -1,53 +0,0 @@
-use proc_macro2::TokenStream as TokenStream2;
-use quote::quote;
-
-use crate::syntax::ast::App;
-use crate::{analyze::Analysis, codegen::util};
-
-/// Generates compile-time assertions that check that types implement the `Send` / `Sync` traits
-pub fn codegen(app: &App, analysis: &Analysis) -> Vec<TokenStream2> {
- let mut stmts = vec![];
-
- for ty in &analysis.send_types {
- stmts.push(quote!(rtic::export::assert_send::<#ty>();));
- }
-
- for ty in &analysis.sync_types {
- stmts.push(quote!(rtic::export::assert_sync::<#ty>();));
- }
-
- let device = &app.args.device;
- let chunks_name = util::priority_mask_chunks_ident();
- let no_basepri_checks: Vec<_> = app
- .hardware_tasks
- .iter()
- .filter_map(|(_, task)| {
- if !util::is_exception(&task.args.binds) {
- let interrupt_name = &task.args.binds;
- Some(quote!(
- if (#device::Interrupt::#interrupt_name as usize) >= (#chunks_name * 32) {
- ::core::panic!("An interrupt out of range is used while in armv6 or armv8m.base");
- }
- ))
- } else {
- None
- }
- })
- .collect();
-
- let const_check = quote! {
- const _CONST_CHECK: () = {
- if !rtic::export::have_basepri() {
- #(#no_basepri_checks)*
- } else {
- // TODO: Add armv7 checks here
- }
- };
-
- let _ = _CONST_CHECK;
- };
-
- stmts.push(const_check);
-
- stmts
-}
diff --git a/macros/src/codegen/async_dispatchers.rs b/macros/src/codegen/async_dispatchers.rs
deleted file mode 100644
index a12ad325..00000000
--- a/macros/src/codegen/async_dispatchers.rs
+++ /dev/null
@@ -1,89 +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) -> TokenStream2 {
- let mut items = vec![];
-
- let interrupts = &analysis.interrupts;
-
- // Generate executor definition and priority in global scope
- 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");
-
- items.push(quote!(
- #[allow(non_camel_case_types)]
- type #type_name = impl core::future::Future;
- #[allow(non_upper_case_globals)]
- static #exec_name: rtic::export::executor::AsyncTaskExecutor<#type_name> =
- rtic::export::executor::AsyncTaskExecutor::new();
- ));
- }
-
- for (&level, channel) in &analysis.channels {
- let mut stmts = vec![];
-
- let dispatcher_name = if level > 0 {
- util::suffixed(&interrupts.get(&level).expect("UNREACHABLE").0.to_string())
- } else {
- util::zero_prio_dispatcher_ident()
- };
-
- let pend_interrupt = if level > 0 {
- let device = &app.args.device;
- let enum_ = util::interrupt_ident();
-
- quote!(rtic::pend(#device::#enum_::#dispatcher_name);)
- } else {
- // For 0 priority tasks we don't need to pend anything
- quote!()
- };
-
- for name in channel.tasks.iter() {
- let exec_name = util::internal_task_ident(name, "EXEC");
- // TODO: Fix cfg
- // let task = &app.software_tasks[name];
- // let cfgs = &task.cfgs;
-
- stmts.push(quote!(
- #exec_name.poll(|| {
- #exec_name.set_pending();
- #pend_interrupt
- });
- ));
- }
-
- if level > 0 {
- let doc = format!("Interrupt handler to dispatch async tasks at priority {level}");
- let attribute = &interrupts.get(&level).expect("UNREACHABLE").1.attrs;
- items.push(quote!(
- #[allow(non_snake_case)]
- #[doc = #doc]
- #[no_mangle]
- #(#attribute)*
- unsafe fn #dispatcher_name() {
- /// The priority of this interrupt handler
- const PRIORITY: u8 = #level;
-
- rtic::export::run(PRIORITY, || {
- #(#stmts)*
- });
- }
- ));
- } else {
- items.push(quote!(
- #[allow(non_snake_case)]
- unsafe fn #dispatcher_name() -> ! {
- loop {
- #(#stmts)*
- }
- }
- ));
- }
- }
-
- quote!(#(#items)*)
-}
diff --git a/macros/src/codegen/hardware_tasks.rs b/macros/src/codegen/hardware_tasks.rs
deleted file mode 100644
index 8a5a8f6c..00000000
--- a/macros/src/codegen/hardware_tasks.rs
+++ /dev/null
@@ -1,87 +0,0 @@
-use crate::syntax::{ast::App, Context};
-use crate::{
- analyze::Analysis,
- codegen::{local_resources_struct, module, shared_resources_struct},
-};
-use proc_macro2::TokenStream as TokenStream2;
-use quote::quote;
-
-/// Generate support code for hardware tasks (`#[exception]`s and `#[interrupt]`s)
-pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 {
- let mut mod_app = vec![];
- let mut root = vec![];
- let mut user_tasks = vec![];
-
- for (name, task) in &app.hardware_tasks {
- let symbol = task.args.binds.clone();
- let priority = task.args.priority;
- let cfgs = &task.cfgs;
- let attrs = &task.attrs;
-
- mod_app.push(quote!(
- #[allow(non_snake_case)]
- #[no_mangle]
- #(#attrs)*
- #(#cfgs)*
- unsafe fn #symbol() {
- const PRIORITY: u8 = #priority;
-
- rtic::export::run(PRIORITY, || {
- #name(
- #name::Context::new()
- )
- });
- }
- ));
-
- // `${task}Locals`
- if !task.args.local_resources.is_empty() {
- let (item, constructor) =
- local_resources_struct::codegen(Context::HardwareTask(name), app);
-
- root.push(item);
-
- mod_app.push(constructor);
- }
-
- // `${task}Resources`
- if !task.args.shared_resources.is_empty() {
- let (item, constructor) =
- shared_resources_struct::codegen(Context::HardwareTask(name), app);
-
- root.push(item);
-
- mod_app.push(constructor);
- }
-
- // Module generation...
-
- root.push(module::codegen(Context::HardwareTask(name), app, analysis));
-
- // End module generation
-
- if !task.is_extern {
- let attrs = &task.attrs;
- let context = &task.context;
- let stmts = &task.stmts;
- user_tasks.push(quote!(
- #(#attrs)*
- #[allow(non_snake_case)]
- fn #name(#context: #name::Context) {
- use rtic::Mutex as _;
- use rtic::mutex::prelude::*;
-
- #(#stmts)*
- }
- ));
- }
- }
-
- quote!(
- #(#mod_app)*
-
- #(#root)*
-
- #(#user_tasks)*
- )
-}
diff --git a/macros/src/codegen/idle.rs b/macros/src/codegen/idle.rs
deleted file mode 100644
index 0c833ef3..00000000
--- a/macros/src/codegen/idle.rs
+++ /dev/null
@@ -1,58 +0,0 @@
-use crate::syntax::{ast::App, Context};
-use crate::{
- analyze::Analysis,
- codegen::{local_resources_struct, module, shared_resources_struct},
-};
-use proc_macro2::TokenStream as TokenStream2;
-use quote::quote;
-
-/// Generates support code for `#[idle]` functions
-pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 {
- if let Some(idle) = &app.idle {
- let mut mod_app = vec![];
- let mut root_idle = vec![];
-
- let name = &idle.name;
-
- if !idle.args.shared_resources.is_empty() {
- let (item, constructor) = shared_resources_struct::codegen(Context::Idle, app);
-
- root_idle.push(item);
- mod_app.push(constructor);
- }
-
- if !idle.args.local_resources.is_empty() {
- let (item, constructor) = local_resources_struct::codegen(Context::Idle, app);
-
- root_idle.push(item);
-
- mod_app.push(constructor);
- }
-
- root_idle.push(module::codegen(Context::Idle, app, analysis));
-
- let attrs = &idle.attrs;
- let context = &idle.context;
- let stmts = &idle.stmts;
- let user_idle = Some(quote!(
- #(#attrs)*
- #[allow(non_snake_case)]
- fn #name(#context: #name::Context) -> ! {
- use rtic::Mutex as _;
- use rtic::mutex::prelude::*;
-
- #(#stmts)*
- }
- ));
-
- quote!(
- #(#mod_app)*
-
- #(#root_idle)*
-
- #user_idle
- )
- } else {
- quote!()
- }
-}
diff --git a/macros/src/codegen/init.rs b/macros/src/codegen/init.rs
deleted file mode 100644
index 6e1059f7..00000000
--- a/macros/src/codegen/init.rs
+++ /dev/null
@@ -1,95 +0,0 @@
-use proc_macro2::TokenStream as TokenStream2;
-use quote::quote;
-
-use crate::{
- analyze::Analysis,
- codegen::{local_resources_struct, module},
- syntax::{ast::App, Context},
-};
-
-/// Generates support code for `#[init]` functions
-pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 {
- let init = &app.init;
- let name = &init.name;
-
- let mut root_init = vec![];
-
- let context = &init.context;
- let attrs = &init.attrs;
- let stmts = &init.stmts;
- let shared = &init.user_shared_struct;
- let local = &init.user_local_struct;
-
- let shared_resources: Vec<_> = app
- .shared_resources
- .iter()
- .map(|(k, v)| {
- let ty = &v.ty;
- let cfgs = &v.cfgs;
- let docs = &v.docs;
- quote!(
- #(#cfgs)*
- #(#docs)*
- #k: #ty,
- )
- })
- .collect();
- let local_resources: Vec<_> = app
- .local_resources
- .iter()
- .map(|(k, v)| {
- let ty = &v.ty;
- let cfgs = &v.cfgs;
- let docs = &v.docs;
- quote!(
- #(#cfgs)*
- #(#docs)*
- #k: #ty,
- )
- })
- .collect();
-
- root_init.push(quote! {
- struct #shared {
- #(#shared_resources)*
- }
-
- struct #local {
- #(#local_resources)*
- }
- });
-
- // let locals_pat = locals_pat.iter();
-
- let user_init_return = quote! {#shared, #local};
-
- let user_init = quote!(
- #(#attrs)*
- #[inline(always)]
- #[allow(non_snake_case)]
- fn #name(#context: #name::Context) -> (#user_init_return) {
- #(#stmts)*
- }
- );
-
- let mut mod_app = None;
-
- // `${task}Locals`
- if !init.args.local_resources.is_empty() {
- let (item, constructor) = local_resources_struct::codegen(Context::Init, app);
-
- root_init.push(item);
-
- mod_app = Some(constructor);
- }
-
- root_init.push(module::codegen(Context::Init, app, analysis));
-
- quote!(
- #mod_app
-
- #(#root_init)*
-
- #user_init
- )
-}
diff --git a/macros/src/codegen/local_resources.rs b/macros/src/codegen/local_resources.rs
deleted file mode 100644
index e6d15533..00000000
--- a/macros/src/codegen/local_resources.rs
+++ /dev/null
@@ -1,65 +0,0 @@
-use crate::syntax::ast::App;
-use crate::{analyze::Analysis, codegen::util};
-use proc_macro2::TokenStream as TokenStream2;
-use quote::quote;
-
-/// Generates `local` variables and local resource proxies
-///
-/// I.e. the `static` variables and theirs proxies.
-pub fn codegen(app: &App, _analysis: &Analysis) -> TokenStream2 {
- let mut mod_app = vec![];
-
- // All local resources declared in the `#[local]' struct
- for (name, res) in &app.local_resources {
- let cfgs = &res.cfgs;
- let ty = &res.ty;
- let mangled_name = util::static_local_resource_ident(name);
-
- let attrs = &res.attrs;
-
- // late resources in `util::link_section_uninit`
- // unless user specifies custom link section
- let section = if attrs.iter().any(|attr| attr.path.is_ident("link_section")) {
- None
- } else {
- Some(util::link_section_uninit())
- };
-
- // For future use
- // let doc = format!(" RTIC internal: {}:{}", file!(), line!());
- mod_app.push(quote!(
- #[allow(non_camel_case_types)]
- #[allow(non_upper_case_globals)]
- // #[doc = #doc]
- #[doc(hidden)]
- #(#attrs)*
- #(#cfgs)*
- #section
- static #mangled_name: rtic::RacyCell<core::mem::MaybeUninit<#ty>> = rtic::RacyCell::new(core::mem::MaybeUninit::uninit());
- ));
- }
-
- // All declared `local = [NAME: TY = EXPR]` local resources
- for (task_name, resource_name, task_local) in app.declared_local_resources() {
- let cfgs = &task_local.cfgs;
- let ty = &task_local.ty;
- let expr = &task_local.expr;
- let attrs = &task_local.attrs;
-
- let mangled_name = util::declared_static_local_resource_ident(resource_name, task_name);
-
- // For future use
- // let doc = format!(" RTIC internal: {}:{}", file!(), line!());
- mod_app.push(quote!(
- #[allow(non_camel_case_types)]
- #[allow(non_upper_case_globals)]
- // #[doc = #doc]
- #[doc(hidden)]
- #(#attrs)*
- #(#cfgs)*
- static #mangled_name: rtic::RacyCell<#ty> = rtic::RacyCell::new(#expr);
- ));
- }
-
- quote!(#(#mod_app)*)
-}
diff --git a/macros/src/codegen/local_resources_struct.rs b/macros/src/codegen/local_resources_struct.rs
deleted file mode 100644
index 100c3eb5..00000000
--- a/macros/src/codegen/local_resources_struct.rs
+++ /dev/null
@@ -1,102 +0,0 @@
-use crate::syntax::{
- ast::{App, TaskLocal},
- Context,
-};
-use proc_macro2::TokenStream as TokenStream2;
-use quote::quote;
-
-use crate::codegen::util;
-
-/// Generates local resources structs
-pub fn codegen(ctxt: Context, app: &App) -> (TokenStream2, TokenStream2) {
- let resources = match ctxt {
- Context::Init => &app.init.args.local_resources,
- Context::Idle => {
- &app.idle
- .as_ref()
- .expect("RTIC-ICE: unable to get idle name")
- .args
- .local_resources
- }
- Context::HardwareTask(name) => &app.hardware_tasks[name].args.local_resources,
- Context::SoftwareTask(name) => &app.software_tasks[name].args.local_resources,
- };
-
- let task_name = util::get_task_name(ctxt, app);
-
- let mut fields = vec![];
- let mut values = vec![];
-
- for (name, task_local) in resources {
- let (cfgs, ty, is_declared) = match task_local {
- TaskLocal::External => {
- let r = app.local_resources.get(name).expect("UNREACHABLE");
- (&r.cfgs, &r.ty, false)
- }
- TaskLocal::Declared(r) => (&r.cfgs, &r.ty, true),
- };
-
- let lt = if ctxt.runs_once() {
- quote!('static)
- } else {
- quote!('a)
- };
-
- let mangled_name = if matches!(task_local, TaskLocal::External) {
- util::static_local_resource_ident(name)
- } else {
- util::declared_static_local_resource_ident(name, &task_name)
- };
-
- fields.push(quote!(
- #(#cfgs)*
- #[allow(missing_docs)]
- pub #name: &#lt mut #ty
- ));
-
- let expr = if is_declared {
- // If the local resources is already initialized, we only need to access its value and
- // not go through an `MaybeUninit`
- quote!(&mut *#mangled_name.get_mut())
- } else {
- quote!(&mut *(&mut *#mangled_name.get_mut()).as_mut_ptr())
- };
-
- values.push(quote!(
- #(#cfgs)*
- #name: #expr
- ));
- }
-
- fields.push(quote!(
- #[doc(hidden)]
- pub __rtic_internal_marker: ::core::marker::PhantomData<&'a ()>
- ));
-
- values.push(quote!(__rtic_internal_marker: ::core::marker::PhantomData));
-
- let doc = format!("Local resources `{}` has access to", ctxt.ident(app));
- let ident = util::local_resources_ident(ctxt, app);
- let item = quote!(
- #[allow(non_snake_case)]
- #[allow(non_camel_case_types)]
- #[doc = #doc]
- pub struct #ident<'a> {
- #(#fields,)*
- }
- );
-
- let constructor = quote!(
- impl<'a> #ident<'a> {
- #[inline(always)]
- #[allow(missing_docs)]
- pub unsafe fn new() -> Self {
- #ident {
- #(#values,)*
- }
- }
- }
- );
-
- (item, constructor)
-}
diff --git a/macros/src/codegen/main.rs b/macros/src/codegen/main.rs
deleted file mode 100644
index 2775d259..00000000
--- a/macros/src/codegen/main.rs
+++ /dev/null
@@ -1,52 +0,0 @@
-use crate::{analyze::Analysis, codegen::util, syntax::ast::App};
-use proc_macro2::TokenStream as TokenStream2;
-use quote::quote;
-
-use super::{assertions, post_init, pre_init};
-
-/// Generates code for `fn main`
-pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 {
- let assertion_stmts = assertions::codegen(app, analysis);
-
- let pre_init_stmts = pre_init::codegen(app, analysis);
-
- let post_init_stmts = post_init::codegen(app, analysis);
-
- let call_idle = if let Some(idle) = &app.idle {
- let name = &idle.name;
- quote!(#name(#name::Context::new()))
- } else if analysis.channels.get(&0).is_some() {
- let dispatcher = util::zero_prio_dispatcher_ident();
- quote!(#dispatcher();)
- } else {
- quote!(loop {
- rtic::export::nop()
- })
- };
-
- let main = util::suffixed("main");
- let init_name = &app.init.name;
- quote!(
- #[doc(hidden)]
- #[no_mangle]
- unsafe extern "C" fn #main() -> ! {
- #(#assertion_stmts)*
-
- #(#pre_init_stmts)*
-
- #[inline(never)]
- fn __rtic_init_resources<F>(f: F) where F: FnOnce() {
- f();
- }
-
- // Wrap late_init_stmts in a function to ensure that stack space is reclaimed.
- __rtic_init_resources(||{
- let (shared_resources, local_resources) = #init_name(#init_name::Context::new(core.into()));
-
- #(#post_init_stmts)*
- });
-
- #call_idle
- }
- )
-}
diff --git a/macros/src/codegen/module.rs b/macros/src/codegen/module.rs
deleted file mode 100644
index 4725b9a9..00000000
--- a/macros/src/codegen/module.rs
+++ /dev/null
@@ -1,194 +0,0 @@
-use crate::syntax::{ast::App, Context};
-use crate::{analyze::Analysis, codegen::util};
-use proc_macro2::TokenStream as TokenStream2;
-use quote::quote;
-
-#[allow(clippy::too_many_lines)]
-pub fn codegen(ctxt: Context, app: &App, analysis: &Analysis) -> TokenStream2 {
- let mut items = vec![];
- let mut module_items = vec![];
- let mut fields = vec![];
- let mut values = vec![];
- // Used to copy task cfgs to the whole module
- let mut task_cfgs = vec![];
-
- let name = ctxt.ident(app);
-
- match ctxt {
- Context::Init => {
- fields.push(quote!(
- /// Core (Cortex-M) peripherals
- pub core: rtic::export::Peripherals
- ));
-
- if app.args.peripherals {
- let device = &app.args.device;
-
- fields.push(quote!(
- /// Device peripherals
- pub device: #device::Peripherals
- ));
-
- values.push(quote!(device: #device::Peripherals::steal()));
- }
-
- fields.push(quote!(
- /// Critical section token for init
- pub cs: rtic::export::CriticalSection<'a>
- ));
-
- values.push(quote!(cs: rtic::export::CriticalSection::new()));
-
- values.push(quote!(core));
- }
-
- Context::Idle | Context::HardwareTask(_) | Context::SoftwareTask(_) => {}
- }
-
- if ctxt.has_local_resources(app) {
- let ident = util::local_resources_ident(ctxt, app);
-
- module_items.push(quote!(
- #[doc(inline)]
- pub use super::#ident as LocalResources;
- ));
-
- fields.push(quote!(
- /// Local Resources this task has access to
- pub local: #name::LocalResources<'a>
- ));
-
- values.push(quote!(local: #name::LocalResources::new()));
- }
-
- if ctxt.has_shared_resources(app) {
- let ident = util::shared_resources_ident(ctxt, app);
-
- module_items.push(quote!(
- #[doc(inline)]
- pub use super::#ident as SharedResources;
- ));
-
- fields.push(quote!(
- /// Shared Resources this task has access to
- pub shared: #name::SharedResources<'a>
- ));
-
- values.push(quote!(shared: #name::SharedResources::new()));
- }
-
- let doc = match ctxt {
- Context::Idle => "Idle loop",
- Context::Init => "Initialization function",
- Context::HardwareTask(_) => "Hardware task",
- Context::SoftwareTask(_) => "Software task",
- };
-
- let v = Vec::new();
- let cfgs = match ctxt {
- Context::HardwareTask(t) => &app.hardware_tasks[t].cfgs,
- Context::SoftwareTask(t) => &app.software_tasks[t].cfgs,
- _ => &v,
- };
-
- let core = if ctxt.is_init() {
- Some(quote!(core: rtic::export::Peripherals,))
- } else {
- None
- };
-
- let internal_context_name = util::internal_task_ident(name, "Context");
- let exec_name = util::internal_task_ident(name, "EXEC");
-
- items.push(quote!(
- #(#cfgs)*
- /// Execution context
- #[allow(non_snake_case)]
- #[allow(non_camel_case_types)]
- pub struct #internal_context_name<'a> {
- #[doc(hidden)]
- __rtic_internal_p: ::core::marker::PhantomData<&'a ()>,
- #(#fields,)*
- }
-
- #(#cfgs)*
- impl<'a> #internal_context_name<'a> {
- #[inline(always)]
- #[allow(missing_docs)]
- pub unsafe fn new(#core) -> Self {
- #internal_context_name {
- __rtic_internal_p: ::core::marker::PhantomData,
- #(#values,)*
- }
- }
- }
- ));
-
- module_items.push(quote!(
- #(#cfgs)*
- #[doc(inline)]
- pub use super::#internal_context_name as Context;
- ));
-
- if let Context::SoftwareTask(..) = ctxt {
- let spawnee = &app.software_tasks[name];
- let priority = spawnee.args.priority;
- let cfgs = &spawnee.cfgs;
- // Store a copy of the task cfgs
- task_cfgs = cfgs.clone();
-
- let pend_interrupt = if priority > 0 {
- let device = &app.args.device;
- let enum_ = util::interrupt_ident();
- let interrupt = &analysis.interrupts.get(&priority).expect("UREACHABLE").0;
- quote!(rtic::pend(#device::#enum_::#interrupt);)
- } else {
- quote!()
- };
-
- let internal_spawn_ident = util::internal_task_ident(name, "spawn");
- let (input_args, input_tupled, input_untupled, input_ty) =
- util::regroup_inputs(&spawnee.inputs);
-
- // Spawn caller
- items.push(quote!(
- #(#cfgs)*
- /// Spawns the task directly
- #[allow(non_snake_case)]
- #[doc(hidden)]
- pub fn #internal_spawn_ident(#(#input_args,)*) -> Result<(), #input_ty> {
-
- if #exec_name.spawn(|| #name(unsafe { #name::Context::new() } #(,#input_untupled)*) ) {
-
- #pend_interrupt
-
- Ok(())
- } else {
- Err(#input_tupled)
- }
-
- }
- ));
-
- module_items.push(quote!(
- #(#cfgs)*
- #[doc(inline)]
- pub use super::#internal_spawn_ident as spawn;
- ));
- }
-
- if items.is_empty() {
- quote!()
- } else {
- quote!(
- #(#items)*
-
- #[allow(non_snake_case)]
- #(#task_cfgs)*
- #[doc = #doc]
- pub mod #name {
- #(#module_items)*
- }
- )
- }
-}
diff --git a/macros/src/codegen/post_init.rs b/macros/src/codegen/post_init.rs
deleted file mode 100644
index c4e53837..00000000
--- a/macros/src/codegen/post_init.rs
+++ /dev/null
@@ -1,47 +0,0 @@
-use crate::{analyze::Analysis, codegen::util, syntax::ast::App};
-use proc_macro2::TokenStream as TokenStream2;
-use quote::quote;
-
-/// Generates code that runs after `#[init]` returns
-pub fn codegen(app: &App, analysis: &Analysis) -> Vec<TokenStream2> {
- let mut stmts = vec![];
-
- // Initialize shared resources
- for (name, res) in &app.shared_resources {
- let mangled_name = util::static_shared_resource_ident(name);
- // If it's live
- let cfgs = res.cfgs.clone();
- if analysis.shared_resources.get(name).is_some() {
- stmts.push(quote!(
- // We include the cfgs
- #(#cfgs)*
- // Resource is a RacyCell<MaybeUninit<T>>
- // - `get_mut` to obtain a raw pointer to `MaybeUninit<T>`
- // - `write` the defined value for the late resource T
- #mangled_name.get_mut().write(core::mem::MaybeUninit::new(shared_resources.#name));
- ));
- }
- }
-
- // Initialize local resources
- for (name, res) in &app.local_resources {
- let mangled_name = util::static_local_resource_ident(name);
- // If it's live
- let cfgs = res.cfgs.clone();
- if analysis.local_resources.get(name).is_some() {
- stmts.push(quote!(
- // We include the cfgs
- #(#cfgs)*
- // Resource is a RacyCell<MaybeUninit<T>>
- // - `get_mut` to obtain a raw pointer to `MaybeUninit<T>`
- // - `write` the defined value for the late resource T
- #mangled_name.get_mut().write(core::mem::MaybeUninit::new(local_resources.#name));
- ));
- }
- }
-
- // Enable the interrupts -- this completes the `init`-ialization phase
- stmts.push(quote!(rtic::export::interrupt::enable();));
-
- stmts
-}
diff --git a/macros/src/codegen/pre_init.rs b/macros/src/codegen/pre_init.rs
deleted file mode 100644
index 28ba29c0..00000000
--- a/macros/src/codegen/pre_init.rs
+++ /dev/null
@@ -1,85 +0,0 @@
-use crate::syntax::ast::App;
-use proc_macro2::TokenStream as TokenStream2;
-use quote::quote;
-
-use crate::{analyze::Analysis, codegen::util};
-
-/// Generates code that runs before `#[init]`
-pub fn codegen(app: &App, analysis: &Analysis) -> Vec<TokenStream2> {
- let mut stmts = vec![];
-
- let rt_err = util::rt_err_ident();
-
- // Disable interrupts -- `init` must run with interrupts disabled
- stmts.push(quote!(rtic::export::interrupt::disable();));
-
- 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();
- ));
-
- let device = &app.args.device;
- let nvic_prio_bits = quote!(#device::NVIC_PRIO_BITS);
-
- // check that all dispatchers exists in the `Interrupt` enumeration regardless of whether
- // they are used or not
- let interrupt = util::interrupt_ident();
- for name in app.args.dispatchers.keys() {
- stmts.push(quote!(let _ = #rt_err::#interrupt::#name;));
- }
-
- 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| {
- if util::is_exception(&task.args.binds) {
- // We do exceptions in another pass
- None
- } else {
- Some((&task.args.priority, &task.args.binds))
- }
- })) {
- let es = format!(
- "Maximum priority used by interrupt vector '{name}' is more than supported by hardware"
- );
- // 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); };
- ));
-
- stmts.push(quote!(
- core.NVIC.set_priority(
- #rt_err::#interrupt::#name,
- rtic::export::logical2hw(#priority, #nvic_prio_bits),
- );
- ));
-
- // NOTE unmask the interrupt *after* setting its priority: changing the priority of a pended
- // interrupt is implementation defined
- stmts.push(quote!(rtic::export::NVIC::unmask(#rt_err::#interrupt::#name);));
- }
-
- // Set exception priorities
- for (name, priority) in app.hardware_tasks.values().filter_map(|task| {
- if util::is_exception(&task.args.binds) {
- Some((&task.args.binds, task.args.priority))
- } else {
- None
- }
- }) {
- let es = format!(
- "Maximum priority used by interrupt vector '{name}' is more than supported by hardware"
- );
- // 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); };
- ));
-
- stmts.push(quote!(core.SCB.set_priority(
- rtic::export::SystemHandler::#name,
- rtic::export::logical2hw(#priority, #nvic_prio_bits),
- );));
- }
-
- stmts
-}
diff --git a/macros/src/codegen/shared_resources.rs b/macros/src/codegen/shared_resources.rs
deleted file mode 100644
index 19fd13fe..00000000
--- a/macros/src/codegen/shared_resources.rs
+++ /dev/null
@@ -1,183 +0,0 @@
-use crate::syntax::{analyze::Ownership, ast::App};
-use crate::{analyze::Analysis, codegen::util};
-use proc_macro2::TokenStream as TokenStream2;
-use quote::quote;
-use std::collections::HashMap;
-
-/// Generates `static` variables and shared resource proxies
-pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 {
- let mut mod_app = vec![];
- let mut mod_resources = vec![];
-
- for (name, res) in &app.shared_resources {
- let cfgs = &res.cfgs;
- let ty = &res.ty;
- let mangled_name = &util::static_shared_resource_ident(name);
-
- let attrs = &res.attrs;
-
- // late resources in `util::link_section_uninit`
- // unless user specifies custom link section
- let section = if attrs.iter().any(|attr| attr.path.is_ident("link_section")) {
- None
- } else {
- Some(util::link_section_uninit())
- };
-
- // For future use
- // let doc = format!(" RTIC internal: {}:{}", file!(), line!());
- mod_app.push(quote!(
- #[allow(non_camel_case_types)]
- #[allow(non_upper_case_globals)]
- // #[doc = #doc]
- #[doc(hidden)]
- #(#attrs)*
- #(#cfgs)*
- #section
- static #mangled_name: rtic::RacyCell<core::mem::MaybeUninit<#ty>> = rtic::RacyCell::new(core::mem::MaybeUninit::uninit());
- ));
-
- // For future use
- // let doc = format!(" RTIC internal: {}:{}", file!(), line!());
-
- let shared_name = util::need_to_lock_ident(name);
-
- if !res.properties.lock_free {
- mod_resources.push(quote!(
- // #[doc = #doc]
- #[doc(hidden)]
- #[allow(non_camel_case_types)]
- #(#cfgs)*
- pub struct #shared_name<'a> {
- __rtic_internal_p: ::core::marker::PhantomData<&'a ()>,
- }
-
- #(#cfgs)*
- impl<'a> #shared_name<'a> {
- #[inline(always)]
- pub unsafe fn new() -> Self {
- #shared_name { __rtic_internal_p: ::core::marker::PhantomData }
- }
- }
- ));
-
- let ptr = quote!(
- #(#cfgs)*
- #mangled_name.get_mut() as *mut _
- );
-
- let ceiling = match analysis.ownerships.get(name) {
- Some(Ownership::Owned { priority } | Ownership::CoOwned { priority }) => *priority,
- Some(Ownership::Contended { ceiling }) => *ceiling,
- None => 0,
- };
-
- // For future use
- // let doc = format!(" RTIC internal ({} resource): {}:{}", doc, file!(), line!());
-
- mod_app.push(util::impl_mutex(
- app,
- cfgs,
- true,
- &shared_name,
- &quote!(#ty),
- ceiling,
- &ptr,
- ));
- }
- }
-
- let mod_resources = if mod_resources.is_empty() {
- quote!()
- } else {
- quote!(mod shared_resources {
- #(#mod_resources)*
- })
- };
-
- // Computing mapping of used interrupts to masks
- let interrupt_ids = analysis.interrupts.iter().map(|(p, (id, _))| (p, id));
-
- let mut prio_to_masks = HashMap::new();
- let device = &app.args.device;
- let mut uses_exceptions_with_resources = false;
-
- let mut mask_ids = Vec::new();
-
- for (&priority, name) in interrupt_ids.chain(app.hardware_tasks.values().flat_map(|task| {
- if !util::is_exception(&task.args.binds) {
- Some((&task.args.priority, &task.args.binds))
- } else {
- // If any resource to the exception uses non-lock-free or non-local resources this is
- // not allwed on thumbv6.
- uses_exceptions_with_resources = uses_exceptions_with_resources
- || task
- .args
- .shared_resources
- .iter()
- .map(|(ident, access)| {
- if access.is_exclusive() {
- if let Some(r) = app.shared_resources.get(ident) {
- !r.properties.lock_free
- } else {
- false
- }
- } else {
- false
- }
- })
- .any(|v| v);
-
- None
- }
- })) {
- let v: &mut Vec<_> = prio_to_masks.entry(priority - 1).or_default();
- v.push(quote!(#device::Interrupt::#name as u32));
- mask_ids.push(quote!(#device::Interrupt::#name as u32));
- }
-
- // Call rtic::export::create_mask([Mask; N]), where the array is the list of shifts
-
- let mut mask_arr = Vec::new();
- // NOTE: 0..3 assumes max 4 priority levels according to M0, M23 spec
- for i in 0..3 {
- let v = if let Some(v) = prio_to_masks.get(&i) {
- v.clone()
- } else {
- Vec::new()
- };
-
- mask_arr.push(quote!(
- rtic::export::create_mask([#(#v),*])
- ));
- }
-
- // Generate a constant for the number of chunks needed by Mask.
- let chunks_name = util::priority_mask_chunks_ident();
- mod_app.push(quote!(
- #[doc(hidden)]
- #[allow(non_upper_case_globals)]
- const #chunks_name: usize = rtic::export::compute_mask_chunks([#(#mask_ids),*]);
- ));
-
- let masks_name = util::priority_masks_ident();
- mod_app.push(quote!(
- #[doc(hidden)]
- #[allow(non_upper_case_globals)]
- const #masks_name: [rtic::export::Mask<#chunks_name>; 3] = [#(#mask_arr),*];
- ));
-
- if uses_exceptions_with_resources {
- mod_app.push(quote!(
- #[doc(hidden)]
- #[allow(non_upper_case_globals)]
- const __rtic_internal_V6_ERROR: () = rtic::export::no_basepri_panic();
- ));
- }
-
- quote!(
- #(#mod_app)*
-
- #mod_resources
- )
-}
diff --git a/macros/src/codegen/shared_resources_struct.rs b/macros/src/codegen/shared_resources_struct.rs
deleted file mode 100644
index fa6f0fcb..00000000
--- a/macros/src/codegen/shared_resources_struct.rs
+++ /dev/null
@@ -1,119 +0,0 @@
-use crate::syntax::{ast::App, Context};
-use proc_macro2::TokenStream as TokenStream2;
-use quote::quote;
-
-use crate::codegen::util;
-
-/// Generate shared resources structs
-pub fn codegen(ctxt: Context, app: &App) -> (TokenStream2, TokenStream2) {
- let resources = match ctxt {
- Context::Init => unreachable!("Tried to generate shared resources struct for init"),
- Context::Idle => {
- &app.idle
- .as_ref()
- .expect("RTIC-ICE: unable to get idle name")
- .args
- .shared_resources
- }
- Context::HardwareTask(name) => &app.hardware_tasks[name].args.shared_resources,
- Context::SoftwareTask(name) => &app.software_tasks[name].args.shared_resources,
- };
-
- let mut fields = vec![];
- let mut values = vec![];
-
- for (name, access) in resources {
- let res = app.shared_resources.get(name).expect("UNREACHABLE");
-
- let cfgs = &res.cfgs;
-
- // access hold if the resource is [x] (exclusive) or [&x] (shared)
- let mut_ = if access.is_exclusive() {
- Some(quote!(mut))
- } else {
- None
- };
- let ty = &res.ty;
- let mangled_name = util::static_shared_resource_ident(name);
- let shared_name = util::need_to_lock_ident(name);
-
- if res.properties.lock_free {
- // Lock free resources of `idle` and `init` get 'static lifetime
- let lt = if ctxt.runs_once() {
- quote!('static)
- } else {
- quote!('a)
- };
-
- fields.push(quote!(
- #(#cfgs)*
- #[allow(missing_docs)]
- pub #name: &#lt #mut_ #ty
- ));
- } else if access.is_shared() {
- fields.push(quote!(
- #(#cfgs)*
- #[allow(missing_docs)]
- pub #name: &'a #ty
- ));
- } else {
- fields.push(quote!(
- #(#cfgs)*
- #[allow(missing_docs)]
- pub #name: shared_resources::#shared_name<'a>
- ));
-
- values.push(quote!(
- #(#cfgs)*
- #name: shared_resources::#shared_name::new()
-
- ));
-
- // continue as the value has been filled,
- continue;
- }
-
- let expr = if access.is_exclusive() {
- quote!(&mut *(&mut *#mangled_name.get_mut()).as_mut_ptr())
- } else {
- quote!(&*(&*#mangled_name.get()).as_ptr())
- };
-
- values.push(quote!(
- #(#cfgs)*
- #name: #expr
- ));
- }
-
- fields.push(quote!(
- #[doc(hidden)]
- pub __rtic_internal_marker: core::marker::PhantomData<&'a ()>
- ));
-
- values.push(quote!(__rtic_internal_marker: core::marker::PhantomData));
-
- let doc = format!("Shared resources `{}` has access to", ctxt.ident(app));
- let ident = util::shared_resources_ident(ctxt, app);
- let item = quote!(
- #[allow(non_snake_case)]
- #[allow(non_camel_case_types)]
- #[doc = #doc]
- pub struct #ident<'a> {
- #(#fields,)*
- }
- );
-
- let constructor = quote!(
- impl<'a> #ident<'a> {
- #[inline(always)]
- #[allow(missing_docs)]
- pub unsafe fn new() -> Self {
- #ident {
- #(#values,)*
- }
- }
- }
- );
-
- (item, constructor)
-}
diff --git a/macros/src/codegen/software_tasks.rs b/macros/src/codegen/software_tasks.rs
deleted file mode 100644
index 34fc851a..00000000
--- a/macros/src/codegen/software_tasks.rs
+++ /dev/null
@@ -1,64 +0,0 @@
-use crate::syntax::{ast::App, Context};
-use crate::{
- analyze::Analysis,
- codegen::{local_resources_struct, module, shared_resources_struct},
-};
-use proc_macro2::TokenStream as TokenStream2;
-use quote::quote;
-
-pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 {
- let mut mod_app = vec![];
- let mut root = vec![];
- let mut user_tasks = vec![];
-
- // Any task
- for (name, task) in app.software_tasks.iter() {
- if !task.args.local_resources.is_empty() {
- let (item, constructor) =
- local_resources_struct::codegen(Context::SoftwareTask(name), 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), 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 inputs = &task.inputs;
-
- user_tasks.push(quote!(
- #(#attrs)*
- #(#cfgs)*
- #[allow(non_snake_case)]
- async fn #name<'a>(#context: #name::Context<'a> #(,#inputs)*) {
- use rtic::Mutex as _;
- use rtic::mutex::prelude::*;
-
- #(#stmts)*
- }
- ));
- }
-
- root.push(module::codegen(Context::SoftwareTask(name), app, analysis));
- }
-
- quote!(
- #(#mod_app)*
-
- #(#root)*
-
- #(#user_tasks)*
- )
-}
diff --git a/macros/src/codegen/util.rs b/macros/src/codegen/util.rs
deleted file mode 100644
index e121487c..00000000
--- a/macros/src/codegen/util.rs
+++ /dev/null
@@ -1,238 +0,0 @@
-use crate::syntax::{ast::App, Context};
-use core::sync::atomic::{AtomicUsize, Ordering};
-use proc_macro2::{Span, TokenStream as TokenStream2};
-use quote::quote;
-use syn::{Attribute, Ident, PatType};
-
-const RTIC_INTERNAL: &str = "__rtic_internal";
-
-/// Generates a `Mutex` implementation
-pub fn impl_mutex(
- app: &App,
- cfgs: &[Attribute],
- resources_prefix: bool,
- name: &Ident,
- ty: &TokenStream2,
- ceiling: u8,
- ptr: &TokenStream2,
-) -> TokenStream2 {
- let path = if resources_prefix {
- quote!(shared_resources::#name)
- } else {
- quote!(#name)
- };
-
- let device = &app.args.device;
- let masks_name = priority_masks_ident();
- quote!(
- #(#cfgs)*
- impl<'a> rtic::Mutex for #path<'a> {
- type T = #ty;
-
- #[inline(always)]
- fn lock<RTIC_INTERNAL_R>(&mut self, f: impl FnOnce(&mut #ty) -> RTIC_INTERNAL_R) -> RTIC_INTERNAL_R {
- /// Priority ceiling
- const CEILING: u8 = #ceiling;
-
- unsafe {
- rtic::export::lock(
- #ptr,
- CEILING,
- #device::NVIC_PRIO_BITS,
- &#masks_name,
- f,
- )
- }
- }
- }
- )
-}
-
-pub fn interrupt_ident() -> Ident {
- let span = Span::call_site();
- Ident::new("interrupt", span)
-}
-
-/// Whether `name` is an exception with configurable priority
-pub fn is_exception(name: &Ident) -> bool {
- let s = name.to_string();
-
- matches!(
- &*s,
- "MemoryManagement"
- | "BusFault"
- | "UsageFault"
- | "SecureFault"
- | "SVCall"
- | "DebugMonitor"
- | "PendSV"
- | "SysTick"
- )
-}
-
-/// Mark a name as internal
-pub fn mark_internal_name(name: &str) -> Ident {
- Ident::new(&format!("{RTIC_INTERNAL}_{name}"), Span::call_site())
-}
-
-/// Generate an internal identifier for tasks
-pub fn internal_task_ident(task: &Ident, ident_name: &str) -> Ident {
- mark_internal_name(&format!("{task}_{ident_name}"))
-}
-
-fn link_section_index() -> usize {
- static INDEX: AtomicUsize = AtomicUsize::new(0);
-
- INDEX.fetch_add(1, Ordering::Relaxed)
-}
-
-/// Add `link_section` attribute
-pub fn link_section_uninit() -> TokenStream2 {
- let section = format!(".uninit.rtic{}", link_section_index());
-
- 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<TokenStream2>,
- // tupled e.g. `_0`, `(_0, _1)`
- TokenStream2,
- // untupled e.g. &[`_0`], &[`_0`, `_1`]
- Vec<TokenStream2>,
- // 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 {
- Context::Init => app.init.name.to_string(),
- Context::Idle => app
- .idle
- .as_ref()
- .expect("RTIC-ICE: unable to find idle name")
- .name
- .to_string(),
- Context::HardwareTask(ident) | Context::SoftwareTask(ident) => ident.to_string(),
- };
-
- Ident::new(&s, Span::call_site())
-}
-
-/// Generates a pre-reexport identifier for the "shared resources" struct
-pub fn shared_resources_ident(ctxt: Context, app: &App) -> Ident {
- let mut s = match ctxt {
- Context::Init => app.init.name.to_string(),
- Context::Idle => app
- .idle
- .as_ref()
- .expect("RTIC-ICE: unable to find idle name")
- .name
- .to_string(),
- Context::HardwareTask(ident) | Context::SoftwareTask(ident) => ident.to_string(),
- };
-
- s.push_str("SharedResources");
-
- mark_internal_name(&s)
-}
-
-/// Generates a pre-reexport identifier for the "local resources" struct
-pub fn local_resources_ident(ctxt: Context, app: &App) -> Ident {
- let mut s = match ctxt {
- Context::Init => app.init.name.to_string(),
- Context::Idle => app
- .idle
- .as_ref()
- .expect("RTIC-ICE: unable to find idle name")
- .name
- .to_string(),
- Context::HardwareTask(ident) | Context::SoftwareTask(ident) => ident.to_string(),
- };
-
- s.push_str("LocalResources");
-
- mark_internal_name(&s)
-}
-
-/// Suffixed identifier
-pub fn suffixed(name: &str) -> Ident {
- let span = Span::call_site();
- Ident::new(name, span)
-}
-
-pub fn static_shared_resource_ident(name: &Ident) -> Ident {
- mark_internal_name(&format!("shared_resource_{name}"))
-}
-
-/// Generates an Ident for the number of 32 bit chunks used for Mask storage.
-pub fn priority_mask_chunks_ident() -> Ident {
- mark_internal_name("MASK_CHUNKS")
-}
-
-pub fn priority_masks_ident() -> Ident {
- mark_internal_name("MASKS")
-}
-
-pub fn static_local_resource_ident(name: &Ident) -> Ident {
- mark_internal_name(&format!("local_resource_{name}"))
-}
-
-pub fn declared_static_local_resource_ident(name: &Ident, task_name: &Ident) -> Ident {
- mark_internal_name(&format!("local_{task_name}_{name}"))
-}
-
-pub fn need_to_lock_ident(name: &Ident) -> Ident {
- Ident::new(&format!("{name}_that_needs_to_be_locked"), name.span())
-}
-
-pub fn zero_prio_dispatcher_ident() -> Ident {
- Ident::new("__rtic_internal_async_0_prio_dispatcher", Span::call_site())
-}
-
-/// The name to get better RT flag errors
-pub fn rt_err_ident() -> Ident {
- Ident::new(
- "you_must_enable_the_rt_feature_for_the_pac_in_your_cargo_toml",
- Span::call_site(),
- )
-}
diff --git a/macros/src/lib.rs b/macros/src/lib.rs
deleted file mode 100644
index a8422d09..00000000
--- a/macros/src/lib.rs
+++ /dev/null
@@ -1,92 +0,0 @@
-#![doc(
- html_logo_url = "https://raw.githubusercontent.com/rtic-rs/rtic/master/book/en/src/RTIC.svg",
- html_favicon_url = "https://raw.githubusercontent.com/rtic-rs/rtic/master/book/en/src/RTIC.svg"
-)]
-
-//deny_warnings_placeholder_for_ci
-
-use proc_macro::TokenStream;
-use std::{env, fs, path::Path};
-
-mod analyze;
-mod bindings;
-mod check;
-mod codegen;
-mod syntax;
-
-// Used for mocking the API in testing
-#[doc(hidden)]
-#[proc_macro_attribute]
-pub fn mock_app(args: TokenStream, input: TokenStream) -> TokenStream {
- if let Err(e) = syntax::parse(args, input) {
- e.to_compile_error().into()
- } else {
- "fn main() {}".parse().unwrap()
- }
-}
-
-/// Attribute used to declare a RTIC application
-///
-/// For user documentation see the [RTIC book](https://rtic.rs)
-///
-/// # Panics
-///
-/// Should never panic, cargo feeds a path which is later converted to a string
-#[proc_macro_attribute]
-pub fn app(args: TokenStream, input: TokenStream) -> TokenStream {
- let (app, analysis) = match syntax::parse(args, input) {
- Err(e) => return e.to_compile_error().into(),
- Ok(x) => x,
- };
-
- if let Err(e) = check::app(&app) {
- return e.to_compile_error().into();
- }
-
- let analysis = analyze::app(analysis, &app);
-
- let ts = codegen::app(&app, &analysis);
-
- // Default output path: <project_dir>/target/
- let mut out_dir = Path::new("target");
-
- // Get output directory from Cargo environment
- // TODO don't want to break builds if OUT_DIR is not set, is this ever the case?
- let out_str = env::var("OUT_DIR").unwrap_or_else(|_| "".to_string());
-
- if !out_dir.exists() {
- // Set out_dir to OUT_DIR
- out_dir = Path::new(&out_str);
-
- // Default build path, annotated below:
- // $(pwd)/target/thumbv7em-none-eabihf/debug/build/cortex-m-rtic-<HASH>/out/
- // <project_dir>/<target-dir>/<TARGET>/debug/build/cortex-m-rtic-<HASH>/out/
- //
- // traverse up to first occurrence of TARGET, approximated with starts_with("thumbv")
- // and use the parent() of this path
- //
- // If no "target" directory is found, <project_dir>/<out_dir_root> is used
- for path in out_dir.ancestors() {
- if let Some(dir) = path.components().last() {
- let dir = dir.as_os_str().to_str().unwrap();
-
- if dir.starts_with("thumbv") || dir.starts_with("riscv") {
- if let Some(out) = path.parent() {
- out_dir = out;
- break;
- }
- // If no parent, just use it
- out_dir = path;
- break;
- }
- }
- }
- }
-
- // Try to write the expanded code to disk
- if let Some(out_str) = out_dir.to_str() {
- fs::write(format!("{out_str}/rtic-expansion.rs"), ts.to_string()).ok();
- }
-
- ts.into()
-}
diff --git a/macros/src/syntax.rs b/macros/src/syntax.rs
deleted file mode 100644
index d6f5a476..00000000
--- a/macros/src/syntax.rs
+++ /dev/null
@@ -1,121 +0,0 @@
-#[allow(unused_extern_crates)]
-extern crate proc_macro;
-
-use proc_macro::TokenStream;
-
-use indexmap::{IndexMap, IndexSet};
-use proc_macro2::TokenStream as TokenStream2;
-use syn::Ident;
-
-use crate::syntax::ast::App;
-
-mod accessors;
-pub mod analyze;
-pub mod ast;
-mod check;
-mod parse;
-
-/// An ordered map keyed by identifier
-pub type Map<T> = IndexMap<Ident, T>;
-
-/// An order set
-pub type Set<T> = IndexSet<T>;
-
-/// Execution context
-#[derive(Clone, Copy)]
-pub enum Context<'a> {
- /// The `idle` context
- Idle,
-
- /// The `init`-ialization function
- Init,
-
- /// A async software task
- SoftwareTask(&'a Ident),
-
- /// A hardware task
- HardwareTask(&'a Ident),
-}
-
-impl<'a> Context<'a> {
- /// The identifier of this context
- pub fn ident(&self, app: &'a App) -> &'a Ident {
- match self {
- Context::HardwareTask(ident) => ident,
- Context::Idle => &app.idle.as_ref().unwrap().name,
- Context::Init => &app.init.name,
- Context::SoftwareTask(ident) => ident,
- }
- }
-
- /// Is this the `idle` context?
- pub fn is_idle(&self) -> bool {
- matches!(self, Context::Idle)
- }
-
- /// Is this the `init`-ialization context?
- pub fn is_init(&self) -> bool {
- matches!(self, Context::Init)
- }
-
- /// Whether this context runs only once
- pub fn runs_once(&self) -> bool {
- self.is_init() || self.is_idle()
- }
-
- /// Whether this context has shared resources
- pub fn has_shared_resources(&self, app: &App) -> bool {
- match *self {
- Context::HardwareTask(name) => {
- !app.hardware_tasks[name].args.shared_resources.is_empty()
- }
- Context::Idle => !app.idle.as_ref().unwrap().args.shared_resources.is_empty(),
- Context::Init => false,
- Context::SoftwareTask(name) => {
- !app.software_tasks[name].args.shared_resources.is_empty()
- }
- }
- }
-
- /// Whether this context has local resources
- pub fn has_local_resources(&self, app: &App) -> bool {
- match *self {
- Context::HardwareTask(name) => {
- !app.hardware_tasks[name].args.local_resources.is_empty()
- }
- Context::Idle => !app.idle.as_ref().unwrap().args.local_resources.is_empty(),
- Context::Init => !app.init.args.local_resources.is_empty(),
- Context::SoftwareTask(name) => {
- !app.software_tasks[name].args.local_resources.is_empty()
- }
- }
- }
-}
-
-/// Parses the input of the `#[app]` attribute
-pub fn parse(
- args: TokenStream,
- input: TokenStream,
-) -> Result<(ast::App, analyze::Analysis), syn::parse::Error> {
- parse2(args.into(), input.into())
-}
-
-/// `proc_macro2::TokenStream` version of `parse`
-pub fn parse2(
- args: TokenStream2,
- input: TokenStream2,
-) -> Result<(ast::App, analyze::Analysis), syn::parse::Error> {
- let app = parse::app(args, input)?;
- check::app(&app)?;
-
- match analyze::app(&app) {
- Err(e) => Err(e),
- // If no errors, return the app and analysis results
- Ok(analysis) => Ok((app, analysis)),
- }
-}
-
-enum Either<A, B> {
- Left(A),
- Right(B),
-}
diff --git a/macros/src/syntax/.github/bors.toml b/macros/src/syntax/.github/bors.toml
deleted file mode 100644
index aee6042f..00000000
--- a/macros/src/syntax/.github/bors.toml
+++ /dev/null
@@ -1,3 +0,0 @@
-block_labels = ["S-blocked"]
-delete_merged_branches = true
-status = ["ci"]
diff --git a/macros/src/syntax/.github/workflows/build.yml b/macros/src/syntax/.github/workflows/build.yml
deleted file mode 100644
index 29971b10..00000000
--- a/macros/src/syntax/.github/workflows/build.yml
+++ /dev/null
@@ -1,213 +0,0 @@
-name: Build
-on:
- pull_request:
- push:
- branches:
- - master
- - staging
- - trying
- - bors/staging
- - bors/trying
-
-env:
- CARGO_TERM_COLOR: always
-
-jobs:
- # Run cargo fmt --check
- style:
- name: style
- runs-on: ubuntu-20.04
- steps:
- - name: Checkout
- uses: actions/checkout@v2
-
- - name: Install Rust
- uses: actions-rs/toolchain@v1
- with:
- profile: minimal
- toolchain: stable
- override: true
- components: rustfmt
-
- - name: Fail on warnings
- run: sed -i 's,//deny_warnings_placeholder_for_ci,#![deny(warnings)],' src/lib.rs
-
- - name: cargo fmt --check
- uses: actions-rs/cargo@v1
- with:
- command: fmt
- args: --all -- --check
-
- # Compilation check
- check:
- name: check
- runs-on: ubuntu-20.04
- strategy:
- matrix:
- target:
- - x86_64-unknown-linux-gnu
- toolchain:
- - stable
- steps:
- - name: Checkout
- uses: actions/checkout@v2
-
- - name: Install Rust ${{ matrix.toolchain }} with target (${{ matrix.target }})
- uses: actions-rs/toolchain@v1
- with:
- toolchain: ${{ matrix.toolchain }}
- target: ${{ matrix.target }}
- override: true
-
- - name: Fail on warnings
- run: sed -i 's,//deny_warnings_placeholder_for_ci,#![deny(warnings)],' src/lib.rs
-
- - name: Cache Dependencies
- uses: Swatinem/rust-cache@v1
-
- - name: cargo check
- uses: actions-rs/cargo@v1
- with:
- use-cross: false
- command: check
- args: --target=${{ matrix.target }}
-
- # Clippy
- clippy:
- name: Cargo clippy
- runs-on: ubuntu-20.04
- steps:
- - name: Checkout
- uses: actions/checkout@v2
-
- - name: Install Rust stable
- uses: actions-rs/toolchain@v1
- with:
- toolchain: stable
- target: x86_64-unknown-linux-gnu
- override: true
-
- - name: Fail on warnings
- run: sed -i 's,//deny_warnings_placeholder_for_ci,#![deny(warnings)],' src/lib.rs
-
- - name: Cache Dependencies
- uses: Swatinem/rust-cache@v1
-
- - name: cargo clippy
- uses: actions-rs/cargo@v1
- with:
- use-cross: false
- command: clippy
-
- # Verify all examples
- testexamples:
- name: testexamples
- runs-on: ubuntu-20.04
- strategy:
- matrix:
- target:
- - x86_64-unknown-linux-gnu
- toolchain:
- - stable
- steps:
- - name: Checkout
- uses: actions/checkout@v2
-
- - name: Install Rust ${{ matrix.toolchain }} with target (${{ matrix.target }})
- uses: actions-rs/toolchain@v1
- with:
- toolchain: ${{ matrix.toolchain }}
- target: ${{ matrix.target }}
- override: true
-
- - name: Cache Dependencies
- uses: Swatinem/rust-cache@v1
-
- - name: Fail on warnings
- run: sed -i 's,//deny_warnings_placeholder_for_ci,#![deny(warnings)],' src/lib.rs
-
- - uses: actions-rs/cargo@v1
- with:
- use-cross: false
- command: test
- args: --examples
-
- # Run test suite for UI
- testui:
- name: testui
- runs-on: ubuntu-20.04
- strategy:
- matrix:
- target:
- - x86_64-unknown-linux-gnu
- toolchain:
- - stable
- steps:
- - name: Checkout
- uses: actions/checkout@v2
-
- - name: Install Rust ${{ matrix.toolchain }} with target (${{ matrix.target }})
- uses: actions-rs/toolchain@v1
- with:
- toolchain: ${{ matrix.toolchain }}
- target: ${{ matrix.target }}
- override: true
-
- - name: Cache Dependencies
- uses: Swatinem/rust-cache@v1
-
- - name: Fail on warnings
- run: sed -i 's,//deny_warnings_placeholder_for_ci,#![deny(warnings)],' src/lib.rs
-
-
- - uses: actions-rs/cargo@v1
- with:
- use-cross: false
- command: test
- args: --test ui
-
- # Run test suite
- test:
- name: test
- runs-on: ubuntu-20.04
- steps:
- - name: Checkout
- uses: actions/checkout@v2
-
- - name: Install Rust
- uses: actions-rs/toolchain@v1
- with:
- toolchain: stable
- target: thumbv7m-none-eabi
- override: true
-
- - name: Cache Dependencies
- uses: Swatinem/rust-cache@v1
-
- - name: Fail on warnings
- run: sed -i 's,//deny_warnings_placeholder_for_ci,#![deny(warnings)],' src/lib.rs
-
- - uses: actions-rs/cargo@v1
- with:
- use-cross: false
- command: test
- args: --lib
-
- # Refs: https://github.com/rust-lang/crater/blob/9ab6f9697c901c4a44025cf0a39b73ad5b37d198/.github/workflows/bors.yml#L125-L149
- #
- # ALL THE PREVIOUS JOBS NEEDS TO BE ADDED TO THE `needs` SECTION OF THIS JOB!
-
- ci-success:
- name: ci
- if: github.event_name == 'push' && success()
- needs:
- - style
- - check
- - clippy
- - testexamples
- - test
- - testui
- runs-on: ubuntu-20.04
- steps:
- - name: Mark the job as a success
- run: exit 0
diff --git a/macros/src/syntax/.github/workflows/changelog.yml b/macros/src/syntax/.github/workflows/changelog.yml
deleted file mode 100644
index ccf6eb91..00000000
--- a/macros/src/syntax/.github/workflows/changelog.yml
+++ /dev/null
@@ -1,28 +0,0 @@
-# Check that the changelog is updated for all changes.
-#
-# This is only run for PRs.
-
-on:
- pull_request:
- # opened, reopened, synchronize are the default types for pull_request.
- # labeled, unlabeled ensure this check is also run if a label is added or removed.
- types: [opened, reopened, labeled, unlabeled, synchronize]
-
-name: Changelog
-
-jobs:
- changelog:
- name: Changelog
- runs-on: ubuntu-latest
- steps:
- - name: Checkout sources
- uses: actions/checkout@v2
-
- - name: Check that changelog updated
- uses: dangoslen/changelog-enforcer@v3
- with:
- changeLogPath: CHANGELOG.md
- skipLabels: 'needs-changelog, skip-changelog'
- missingUpdateErrorMessage: 'Please add a changelog entry in the CHANGELOG.md file.'
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file
diff --git a/macros/src/syntax/.github/workflows/properties/build.properties.json b/macros/src/syntax/.github/workflows/properties/build.properties.json
deleted file mode 100644
index fd3eed37..00000000
--- a/macros/src/syntax/.github/workflows/properties/build.properties.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "name": "Build",
- "description": "RTIC Test Suite",
- "iconName": "rust",
- "categories": ["Rust"]
-}
diff --git a/macros/src/syntax/.gitignore b/macros/src/syntax/.gitignore
deleted file mode 100644
index f8d7c8b4..00000000
--- a/macros/src/syntax/.gitignore
+++ /dev/null
@@ -1,4 +0,0 @@
-**/*.rs.bk
-.#*
-/target/
-Cargo.lock
diff --git a/macros/src/syntax/.travis.yml b/macros/src/syntax/.travis.yml
deleted file mode 100644
index 52d1ffdd..00000000
--- a/macros/src/syntax/.travis.yml
+++ /dev/null
@@ -1,31 +0,0 @@
-language: rust
-
-matrix:
- include:
- # MSRV
- - env: TARGET=x86_64-unknown-linux-gnu
- rust: 1.36.0
-
- - env: TARGET=x86_64-unknown-linux-gnu
- rust: stable
-
-before_install: set -e
-
-script:
- - bash ci/script.sh
-
-after_script: set +e
-
-cache: cargo
-
-before_cache:
- - chmod -R a+r $HOME/.cargo;
-
-branches:
- only:
- - staging
- - trying
-
-notifications:
- email:
- on_success: never
diff --git a/macros/src/syntax/accessors.rs b/macros/src/syntax/accessors.rs
deleted file mode 100644
index e75dde6c..00000000
--- a/macros/src/syntax/accessors.rs
+++ /dev/null
@@ -1,113 +0,0 @@
-use syn::Ident;
-
-use crate::syntax::{
- analyze::Priority,
- ast::{Access, App, Local, TaskLocal},
-};
-
-impl App {
- pub(crate) fn shared_resource_accesses(
- &self,
- ) -> impl Iterator<Item = (Option<Priority>, &Ident, Access)> {
- self.idle
- .iter()
- .flat_map(|idle| {
- idle.args
- .shared_resources
- .iter()
- .map(move |(name, access)| (Some(0), name, *access))
- })
- .chain(self.hardware_tasks.values().flat_map(|task| {
- task.args
- .shared_resources
- .iter()
- .map(move |(name, access)| (Some(task.args.priority), name, *access))
- }))
- .chain(self.software_tasks.values().flat_map(|task| {
- task.args
- .shared_resources
- .iter()
- .map(move |(name, access)| (Some(task.args.priority), name, *access))
- }))
- }
-
- fn is_external(task_local: &TaskLocal) -> bool {
- matches!(task_local, TaskLocal::External)
- }
-
- pub(crate) fn local_resource_accesses(&self) -> impl Iterator<Item = &Ident> {
- self.init
- .args
- .local_resources
- .iter()
- .filter(|(_, task_local)| Self::is_external(task_local)) // Only check the resources declared in `#[local]`
- .map(move |(name, _)| name)
- .chain(self.idle.iter().flat_map(|idle| {
- idle.args
- .local_resources
- .iter()
- .filter(|(_, task_local)| Self::is_external(task_local)) // Only check the resources declared in `#[local]`
- .map(move |(name, _)| name)
- }))
- .chain(self.hardware_tasks.values().flat_map(|task| {
- task.args
- .local_resources
- .iter()
- .filter(|(_, task_local)| Self::is_external(task_local)) // Only check the resources declared in `#[local]`
- .map(move |(name, _)| name)
- }))
- .chain(self.software_tasks.values().flat_map(|task| {
- task.args
- .local_resources
- .iter()
- .filter(|(_, task_local)| Self::is_external(task_local)) // Only check the resources declared in `#[local]`
- .map(move |(name, _)| name)
- }))
- }
-
- fn get_declared_local(tl: &TaskLocal) -> Option<&Local> {
- match tl {
- TaskLocal::External => None,
- TaskLocal::Declared(l) => Some(l),
- }
- }
-
- /// Get all declared local resources, i.e. `local = [NAME: TYPE = EXPR]`.
- ///
- /// Returns a vector of (task name, resource name, `Local` struct)
- pub fn declared_local_resources(&self) -> Vec<(&Ident, &Ident, &Local)> {
- self.init
- .args
- .local_resources
- .iter()
- .filter_map(move |(name, tl)| {
- Self::get_declared_local(tl).map(|l| (&self.init.name, name, l))
- })
- .chain(self.idle.iter().flat_map(|idle| {
- idle.args
- .local_resources
- .iter()
- .filter_map(move |(name, tl)| {
- Self::get_declared_local(tl)
- .map(|l| (&self.idle.as_ref().unwrap().name, name, l))
- })
- }))
- .chain(self.hardware_tasks.iter().flat_map(|(task_name, task)| {
- task.args
- .local_resources
- .iter()
- .filter_map(move |(name, tl)| {
- Self::get_declared_local(tl).map(|l| (task_name, name, l))
- })
- }))
- .chain(self.software_tasks.iter().flat_map(|(task_name, task)| {
- task.args
- .local_resources
- .iter()
- .filter_map(move |(name, tl)| {
- Self::get_declared_local(tl).map(|l| (task_name, name, l))
- })
- }))
- .collect()
- }
-}
diff --git a/macros/src/syntax/analyze.rs b/macros/src/syntax/analyze.rs
deleted file mode 100644
index 3ed14877..00000000
--- a/macros/src/syntax/analyze.rs
+++ /dev/null
@@ -1,417 +0,0 @@
-//! RTIC application analysis
-
-use core::cmp;
-use std::collections::{BTreeMap, BTreeSet, HashMap};
-
-use indexmap::{IndexMap, IndexSet};
-use syn::{Ident, Type};
-
-use crate::syntax::{
- ast::{App, LocalResources, TaskLocal},
- Set,
-};
-
-pub(crate) fn app(app: &App) -> Result<Analysis, syn::Error> {
- // Collect all tasks into a vector
- type TaskName = Ident;
- type Priority = u8;
-
- // 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| (ht.name.clone(), Vec::new(), &ht.args.local_resources, 0))
- .chain(app.idle.iter().map(|ht| {
- (
- ht.name.clone(),
- ht.args
- .shared_resources
- .iter()
- .map(|(v, _)| v)
- .collect::<Vec<_>>(),
- &ht.args.local_resources,
- 0,
- )
- }))
- .chain(app.software_tasks.iter().map(|(name, ht)| {
- (
- name.clone(),
- ht.args
- .shared_resources
- .iter()
- .map(|(v, _)| v)
- .collect::<Vec<_>>(),
- &ht.args.local_resources,
- ht.args.priority,
- )
- }))
- .chain(app.hardware_tasks.iter().map(|(name, ht)| {
- (
- name.clone(),
- ht.args
- .shared_resources
- .iter()
- .map(|(v, _)| v)
- .collect::<Vec<_>>(),
- &ht.args.local_resources,
- ht.args.priority,
- )
- }))
- .collect();
-
- let mut error = vec![];
- let mut lf_res_with_error = vec![];
- let mut lf_hash = HashMap::new();
-
- // Collect lock free resources
- let lock_free: Vec<&Ident> = app
- .shared_resources
- .iter()
- .filter(|(_, r)| r.properties.lock_free)
- .map(|(i, _)| i)
- .collect();
-
- // Check that lock_free resources are correct
- for lf_res in lock_free.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 {
- // Check so async tasks do not use lock free resources
- if app.software_tasks.get(task).is_some() {
- error.push(syn::Error::new(
- r.span(),
- format!(
- "Lock free shared resource {:?} is used by an async tasks, which is forbidden",
- r.to_string(),
- ),
- ));
- }
-
- // 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
- // list of resources which will be annotated with errors
- if priority != lf_res.2 {
- lf_res_with_error.push(lf_res.1);
- lf_res_with_error.push(r);
- }
-
- // If the resource already violates lock free properties
- if lf_res_with_error.contains(&r) {
- lf_res_with_error.push(lf_res.1);
- lf_res_with_error.push(r);
- }
- }
- }
- }
- }
- }
-
- // Add error message in the resource struct
- for r in lock_free {
- if lf_res_with_error.contains(&&r) {
- error.push(syn::Error::new(
- r.span(),
- format!(
- "Lock free shared resource {:?} is used by tasks at different priorities",
- r.to_string(),
- ),
- ));
- }
- }
-
- // Add error message for each use of the shared resource
- for resource in lf_res_with_error.clone() {
- error.push(syn::Error::new(
- resource.span(),
- format!(
- "Shared resource {:?} is declared lock free but used by tasks at different priorities",
- resource.to_string(),
- ),
- ));
- }
-
- // Collect local resources
- let local: Vec<&Ident> = app.local_resources.iter().map(|(i, _)| i).collect();
-
- let mut lr_with_error = vec![];
- let mut lr_hash = HashMap::new();
-
- // Check that local resources are not shared
- for lr in local {
- 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 {
- match res {
- TaskLocal::External => {
- // HashMap returns the previous existing object if old.key == new.key
- if let Some(lr) = lr_hash.insert(name.to_string(), (task, name)) {
- lr_with_error.push(lr.1);
- lr_with_error.push(name);
- }
- }
- // If a declared local has the same name as the `#[local]` struct, it's an
- // direct error
- TaskLocal::Declared(_) => {
- lr_with_error.push(lr);
- lr_with_error.push(name);
- }
- }
- }
- }
- }
- }
-
- // Add error message for each use of the local resource
- for resource in lr_with_error.clone() {
- error.push(syn::Error::new(
- resource.span(),
- format!(
- "Local resource {:?} is used by multiple tasks or collides with multiple definitions",
- resource.to_string(),
- ),
- ));
- }
-
- // Check 0-priority async software tasks and idle dependency
- for (name, task) in &app.software_tasks {
- if task.args.priority == 0 {
- // If there is a 0-priority task, there must be no idle
- if app.idle.is_some() {
- error.push(syn::Error::new(
- name.span(),
- format!(
- "Async task {:?} has priority 0, but `#[idle]` is defined. 0-priority async tasks are only allowed if there is no `#[idle]`.",
- name.to_string(),
- )
- ));
- }
- }
- }
-
- // Collect errors if any and return/halt
- if !error.is_empty() {
- let mut err = error.get(0).unwrap().clone();
- error.iter().for_each(|e| err.combine(e.clone()));
- return Err(err);
- }
-
- // e. Location of resources
- let mut used_shared_resource = IndexSet::new();
- let mut ownerships = Ownerships::new();
- let mut sync_types = SyncTypes::new();
- for (prio, name, access) in app.shared_resource_accesses() {
- let res = app.shared_resources.get(name).expect("UNREACHABLE");
-
- // (e)
- // This shared resource is used
- used_shared_resource.insert(name.clone());
-
- // (c)
- if let Some(priority) = prio {
- if let Some(ownership) = ownerships.get_mut(name) {
- match *ownership {
- Ownership::Owned { priority: ceiling }
- | Ownership::CoOwned { priority: ceiling }
- | Ownership::Contended { ceiling }
- if priority != ceiling =>
- {
- *ownership = Ownership::Contended {
- ceiling: cmp::max(ceiling, priority),
- };
-
- if access.is_shared() {
- sync_types.insert(res.ty.clone());
- }
- }
-
- Ownership::Owned { priority: ceil } if ceil == priority => {
- *ownership = Ownership::CoOwned { priority };
- }
-
- _ => {}
- }
- } else {
- ownerships.insert(name.clone(), Ownership::Owned { priority });
- }
- }
- }
-
- // Create the list of used local resource Idents
- let mut used_local_resource = IndexSet::new();
-
- for (_, _, locals, _) in task_resources_list {
- for (local, _) in locals {
- used_local_resource.insert(local.clone());
- }
- }
-
- // Most shared resources need to be `Send`, only 0 prio does not need it
- let mut send_types = SendTypes::new();
-
- for (name, res) in app.shared_resources.iter() {
- if ownerships
- .get(name)
- .map(|ownership| match *ownership {
- Ownership::Owned { priority: ceiling }
- | Ownership::CoOwned { priority: ceiling }
- | Ownership::Contended { ceiling } => ceiling != 0,
- })
- .unwrap_or(false)
- {
- send_types.insert(res.ty.clone());
- }
- }
-
- // Most local resources need to be `Send` as well, only 0 prio does not need it
- for (name, res) in app.local_resources.iter() {
- if ownerships
- .get(name)
- .map(|ownership| match *ownership {
- Ownership::Owned { priority: ceiling }
- | Ownership::CoOwned { priority: ceiling }
- | Ownership::Contended { ceiling } => ceiling != 0,
- })
- .unwrap_or(false)
- {
- send_types.insert(res.ty.clone());
- }
- }
-
- let mut channels = Channels::new();
-
- for (name, spawnee) in &app.software_tasks {
- let spawnee_prio = spawnee.args.priority;
-
- let channel = channels.entry(spawnee_prio).or_default();
- channel.tasks.insert(name.clone());
-
- // All inputs are send as we do not know from where they may be spawned.
- 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()));
-
- Ok(Analysis {
- channels,
- shared_resources: used_shared_resource,
- local_resources: used_local_resource,
- ownerships,
- send_types,
- sync_types,
- })
-}
-
-// /// Priority ceiling
-// pub type Ceiling = Option<u8>;
-
-/// Task priority
-pub type Priority = u8;
-
-/// Resource name
-pub type Resource = Ident;
-
-/// Task name
-pub type Task = Ident;
-
-/// The result of analyzing an RTIC application
-pub struct Analysis {
- /// SPSC message channels
- pub channels: Channels,
-
- /// Shared resources
- ///
- /// If a resource is not listed here it means that's a "dead" (never
- /// accessed) resource and the backend should not generate code for it
- pub shared_resources: UsedSharedResource,
-
- /// Local resources
- ///
- /// If a resource is not listed here it means that's a "dead" (never
- /// accessed) resource and the backend should not generate code for it
- pub local_resources: UsedLocalResource,
-
- /// Resource ownership
- pub ownerships: Ownerships,
-
- /// These types must implement the `Send` trait
- pub send_types: SendTypes,
-
- /// These types must implement the `Sync` trait
- pub sync_types: SyncTypes,
-}
-
-/// All channels, keyed by dispatch priority
-pub type Channels = BTreeMap<Priority, Channel>;
-
-/// Location of all *used* shared resources
-pub type UsedSharedResource = IndexSet<Resource>;
-
-/// Location of all *used* local resources
-pub type UsedLocalResource = IndexSet<Resource>;
-
-/// Resource ownership
-pub type Ownerships = IndexMap<Resource, Ownership>;
-
-/// These types must implement the `Send` trait
-pub type SendTypes = Set<Box<Type>>;
-
-/// These types must implement the `Sync` trait
-pub type SyncTypes = Set<Box<Type>>;
-
-/// A channel used to send messages
-#[derive(Debug, Default)]
-pub struct Channel {
- /// The channel capacity
- pub capacity: u8,
-
- /// Tasks that can be spawned on this channel
- pub tasks: BTreeSet<Task>,
-}
-
-/// Resource ownership
-#[derive(Clone, Copy, Debug, PartialEq)]
-pub enum Ownership {
- /// Owned by a single task
- Owned {
- /// Priority of the task that owns this resource
- priority: u8,
- },
-
- /// "Co-owned" by more than one task; all of them have the same priority
- CoOwned {
- /// Priority of the tasks that co-own this resource
- priority: u8,
- },
-
- /// Contended by more than one task; the tasks have different priorities
- Contended {
- /// Priority ceiling
- ceiling: u8,
- },
-}
-
-// impl Ownership {
-// /// Whether this resource needs to a lock at this priority level
-// pub fn needs_lock(&self, priority: u8) -> bool {
-// match self {
-// Ownership::Owned { .. } | Ownership::CoOwned { .. } => false,
-//
-// Ownership::Contended { ceiling } => {
-// debug_assert!(*ceiling >= priority);
-//
-// priority < *ceiling
-// }
-// }
-// }
-//
-// /// Whether this resource is exclusively owned
-// pub fn is_owned(&self) -> bool {
-// matches!(self, Ownership::Owned { .. })
-// }
-// }
diff --git a/macros/src/syntax/ast.rs b/macros/src/syntax/ast.rs
deleted file mode 100644
index 27e6773f..00000000
--- a/macros/src/syntax/ast.rs
+++ /dev/null
@@ -1,335 +0,0 @@
-//! Abstract Syntax Tree
-
-use syn::{Attribute, Expr, Ident, Item, ItemUse, Pat, PatType, Path, Stmt, Type};
-
-use crate::syntax::Map;
-
-/// The `#[app]` attribute
-#[derive(Debug)]
-#[non_exhaustive]
-pub struct App {
- /// The arguments to the `#[app]` attribute
- pub args: AppArgs,
-
- /// The name of the `const` item on which the `#[app]` attribute has been placed
- pub name: Ident,
-
- /// The `#[init]` function
- pub init: Init,
-
- /// The `#[idle]` function
- pub idle: Option<Idle>,
-
- /// Resources shared between tasks defined in `#[shared]`
- pub shared_resources: Map<SharedResource>,
-
- /// Task local resources defined in `#[local]`
- pub local_resources: Map<LocalResource>,
-
- /// User imports
- pub user_imports: Vec<ItemUse>,
-
- /// User code
- pub user_code: Vec<Item>,
-
- /// Hardware tasks: `#[task(binds = ..)]`s
- pub hardware_tasks: Map<HardwareTask>,
-
- /// Async software tasks: `#[task]`
- pub software_tasks: Map<SoftwareTask>,
-}
-
-/// Interrupts used to dispatch software tasks
-pub type Dispatchers = Map<Dispatcher>;
-
-/// Interrupt that could be used to dispatch software tasks
-#[derive(Debug, Clone)]
-#[non_exhaustive]
-pub struct Dispatcher {
- /// Attributes that will apply to this interrupt handler
- pub attrs: Vec<Attribute>,
-}
-
-/// The arguments of the `#[app]` attribute
-#[derive(Debug)]
-pub struct AppArgs {
- /// Device
- pub device: Path,
-
- /// Peripherals
- pub peripherals: bool,
-
- /// Interrupts used to dispatch software tasks
- pub dispatchers: Dispatchers,
-}
-
-/// The `init`-ialization function
-#[derive(Debug)]
-#[non_exhaustive]
-pub struct Init {
- /// `init` context metadata
- pub args: InitArgs,
-
- /// Attributes that will apply to this `init` function
- pub attrs: Vec<Attribute>,
-
- /// The name of the `#[init]` function
- pub name: Ident,
-
- /// The context argument
- pub context: Box<Pat>,
-
- /// The statements that make up this `init` function
- pub stmts: Vec<Stmt>,
-
- /// The name of the user provided shared resources struct
- pub user_shared_struct: Ident,
-
- /// The name of the user provided local resources struct
- pub user_local_struct: Ident,
-}
-
-/// `init` context metadata
-#[derive(Debug)]
-#[non_exhaustive]
-pub struct InitArgs {
- /// Local resources that can be accessed from this context
- pub local_resources: LocalResources,
-}
-
-impl Default for InitArgs {
- fn default() -> Self {
- Self {
- local_resources: LocalResources::new(),
- }
- }
-}
-
-/// The `idle` context
-#[derive(Debug)]
-#[non_exhaustive]
-pub struct Idle {
- /// `idle` context metadata
- pub args: IdleArgs,
-
- /// Attributes that will apply to this `idle` function
- pub attrs: Vec<Attribute>,
-
- /// The name of the `#[idle]` function
- pub name: Ident,
-
- /// The context argument
- pub context: Box<Pat>,
-
- /// The statements that make up this `idle` function
- pub stmts: Vec<Stmt>,
-}
-
-/// `idle` context metadata
-#[derive(Debug)]
-#[non_exhaustive]
-pub struct IdleArgs {
- /// Local resources that can be accessed from this context
- pub local_resources: LocalResources,
-
- /// Shared resources that can be accessed from this context
- pub shared_resources: SharedResources,
-}
-
-impl Default for IdleArgs {
- fn default() -> Self {
- Self {
- local_resources: LocalResources::new(),
- shared_resources: SharedResources::new(),
- }
- }
-}
-
-/// Shared resource properties
-#[derive(Debug)]
-pub struct SharedResourceProperties {
- /// A lock free (exclusive resource)
- pub lock_free: bool,
-}
-
-/// A shared resource, defined in `#[shared]`
-#[derive(Debug)]
-#[non_exhaustive]
-pub struct SharedResource {
- /// `#[cfg]` attributes like `#[cfg(debug_assertions)]`
- pub cfgs: Vec<Attribute>,
-
- /// `#[doc]` attributes like `/// this is a docstring`
- pub docs: Vec<Attribute>,
-
- /// Attributes that will apply to this resource
- pub attrs: Vec<Attribute>,
-
- /// The type of this resource
- pub ty: Box<Type>,
-
- /// Shared resource properties
- pub properties: SharedResourceProperties,
-}
-
-/// A local resource, defined in `#[local]`
-#[derive(Debug)]
-#[non_exhaustive]
-pub struct LocalResource {
- /// `#[cfg]` attributes like `#[cfg(debug_assertions)]`
- pub cfgs: Vec<Attribute>,
-
- /// `#[doc]` attributes like `/// this is a docstring`
- pub docs: Vec<Attribute>,
-
- /// Attributes that will apply to this resource
- pub attrs: Vec<Attribute>,
-
- /// The type of this resource
- pub ty: Box<Type>,
-}
-
-/// An async software task
-#[derive(Debug)]
-#[non_exhaustive]
-pub struct SoftwareTask {
- /// Software task metadata
- pub args: SoftwareTaskArgs,
-
- /// `#[cfg]` attributes like `#[cfg(debug_assertions)]`
- pub cfgs: Vec<Attribute>,
-
- /// Attributes that will apply to this interrupt handler
- pub attrs: Vec<Attribute>,
-
- /// The context argument
- pub context: Box<Pat>,
-
- /// The inputs of this software task
- pub inputs: Vec<PatType>,
-
- /// The statements that make up the task handler
- pub stmts: Vec<Stmt>,
-
- /// The task is declared externally
- pub is_extern: bool,
-}
-
-/// Software task metadata
-#[derive(Debug)]
-#[non_exhaustive]
-pub struct SoftwareTaskArgs {
- /// The priority of this task
- pub priority: u8,
-
- /// Local resources that can be accessed from this context
- pub local_resources: LocalResources,
-
- /// Shared resources that can be accessed from this context
- pub shared_resources: SharedResources,
-}
-
-impl Default for SoftwareTaskArgs {
- fn default() -> Self {
- Self {
- priority: 1,
- local_resources: LocalResources::new(),
- shared_resources: SharedResources::new(),
- }
- }
-}
-
-/// A hardware task
-#[derive(Debug)]
-#[non_exhaustive]
-pub struct HardwareTask {
- /// Hardware task metadata
- pub args: HardwareTaskArgs,
-
- /// `#[cfg]` attributes like `#[cfg(debug_assertions)]`
- pub cfgs: Vec<Attribute>,
-
- /// Attributes that will apply to this interrupt handler
- pub attrs: Vec<Attribute>,
-
- /// The context argument
- pub context: Box<Pat>,
-
- /// The statements that make up the task handler
- pub stmts: Vec<Stmt>,
-
- /// The task is declared externally
- pub is_extern: bool,
-}
-
-/// Hardware task metadata
-#[derive(Debug)]
-#[non_exhaustive]
-pub struct HardwareTaskArgs {
- /// The interrupt or exception that this task is bound to
- pub binds: Ident,
-
- /// The priority of this task
- pub priority: u8,
-
- /// Local resources that can be accessed from this context
- pub local_resources: LocalResources,
-
- /// Shared resources that can be accessed from this context
- pub shared_resources: SharedResources,
-}
-
-/// A `static mut` variable local to and owned by a context
-#[derive(Debug)]
-#[non_exhaustive]
-pub struct Local {
- /// Attributes like `#[link_section]`
- pub attrs: Vec<Attribute>,
-
- /// `#[cfg]` attributes like `#[cfg(debug_assertions)]`
- pub cfgs: Vec<Attribute>,
-
- /// Type
- pub ty: Box<Type>,
-
- /// Initial value
- pub expr: Box<Expr>,
-}
-
-/// A wrapper of the 2 kinds of locals that tasks can have
-#[derive(Debug)]
-#[non_exhaustive]
-pub enum TaskLocal {
- /// The local is declared externally (i.e. `#[local]` struct)
- External,
- /// The local is declared in the task
- Declared(Local),
-}
-
-/// Resource access
-#[derive(Clone, Copy, Debug, Eq, PartialEq)]
-pub enum Access {
- /// `[x]`, a mutable resource
- Exclusive,
-
- /// `[&x]`, a static non-mutable resource
- Shared,
-}
-
-impl Access {
- /// Is this enum in the `Exclusive` variant?
- pub fn is_exclusive(&self) -> bool {
- *self == Access::Exclusive
- }
-
- /// Is this enum in the `Shared` variant?
- pub fn is_shared(&self) -> bool {
- *self == Access::Shared
- }
-}
-
-/// Shared resource access list in task attribute
-pub type SharedResources = Map<Access>;
-
-/// Local resource access/declaration list in task attribute
-pub type LocalResources = Map<TaskLocal>;
diff --git a/macros/src/syntax/check.rs b/macros/src/syntax/check.rs
deleted file mode 100644
index 989d4180..00000000
--- a/macros/src/syntax/check.rs
+++ /dev/null
@@ -1,66 +0,0 @@
-use std::collections::HashSet;
-
-use syn::parse;
-
-use crate::syntax::ast::App;
-
-pub fn app(app: &App) -> parse::Result<()> {
- // Check that all referenced resources have been declared
- // Check that resources are NOT `Exclusive`-ly shared
- let mut owners = HashSet::new();
- for (_, name, access) in app.shared_resource_accesses() {
- if app.shared_resources.get(name).is_none() {
- return Err(parse::Error::new(
- name.span(),
- "this shared resource has NOT been declared",
- ));
- }
-
- if access.is_exclusive() {
- owners.insert(name);
- }
- }
-
- for name in app.local_resource_accesses() {
- if app.local_resources.get(name).is_none() {
- return Err(parse::Error::new(
- name.span(),
- "this local resource has NOT been declared",
- ));
- }
- }
-
- // Check that no resource has both types of access (`Exclusive` & `Shared`)
- let exclusive_accesses = app
- .shared_resource_accesses()
- .filter_map(|(priority, name, access)| {
- if priority.is_some() && access.is_exclusive() {
- Some(name)
- } else {
- None
- }
- })
- .collect::<HashSet<_>>();
- for (_, name, access) in app.shared_resource_accesses() {
- if access.is_shared() && exclusive_accesses.contains(name) {
- return Err(parse::Error::new(
- name.span(),
- "this implementation doesn't support shared (`&-`) - exclusive (`&mut-`) locks; use `x` instead of `&x`",
- ));
- }
- }
-
- // check that dispatchers are not used as hardware tasks
- for task in app.hardware_tasks.values() {
- let binds = &task.args.binds;
-
- if app.args.dispatchers.contains_key(binds) {
- return Err(parse::Error::new(
- binds.span(),
- "dispatcher interrupts can't be used as hardware tasks",
- ));
- }
- }
-
- Ok(())
-}
diff --git a/macros/src/syntax/optimize.rs b/macros/src/syntax/optimize.rs
deleted file mode 100644
index e83ba31b..00000000
--- a/macros/src/syntax/optimize.rs
+++ /dev/null
@@ -1,36 +0,0 @@
-use std::collections::{BTreeSet, HashMap};
-
-use crate::syntax::ast::App;
-
-pub fn app(app: &mut App, settings: &Settings) {
- // "compress" priorities
- // If the user specified, for example, task priorities of "1, 3, 6",
- // compress them into "1, 2, 3" as to leave no gaps
- if settings.optimize_priorities {
- // all task priorities ordered in ascending order
- let priorities = app
- .hardware_tasks
- .values()
- .map(|task| Some(task.args.priority))
- .chain(
- app.software_tasks
- .values()
- .map(|task| Some(task.args.priority)),
- )
- .collect::<BTreeSet<_>>();
-
- let map = priorities
- .iter()
- .cloned()
- .zip(1..)
- .collect::<HashMap<_, _>>();
-
- for task in app.hardware_tasks.values_mut() {
- task.args.priority = map[&Some(task.args.priority)];
- }
-
- for task in app.software_tasks.values_mut() {
- task.args.priority = map[&Some(task.args.priority)];
- }
- }
-}
diff --git a/macros/src/syntax/parse.rs b/macros/src/syntax/parse.rs
deleted file mode 100644
index c78453a4..00000000
--- a/macros/src/syntax/parse.rs
+++ /dev/null
@@ -1,363 +0,0 @@
-mod app;
-mod hardware_task;
-mod idle;
-mod init;
-mod resource;
-mod software_task;
-mod util;
-
-use proc_macro2::TokenStream as TokenStream2;
-use syn::{
- braced, parenthesized,
- parse::{self, Parse, ParseStream, Parser},
- token::Brace,
- Ident, Item, LitInt, Token,
-};
-
-use crate::syntax::{
- ast::{App, AppArgs, HardwareTaskArgs, IdleArgs, InitArgs, SoftwareTaskArgs, TaskLocal},
- Either,
-};
-
-// Parse the app, both app arguments and body (input)
-pub fn app(args: TokenStream2, input: TokenStream2) -> parse::Result<App> {
- let args = AppArgs::parse(args)?;
- let input: Input = syn::parse2(input)?;
-
- App::parse(args, input)
-}
-
-pub(crate) struct Input {
- _mod_token: Token![mod],
- pub ident: Ident,
- _brace_token: Brace,
- pub items: Vec<Item>,
-}
-
-impl Parse for Input {
- fn parse(input: ParseStream<'_>) -> parse::Result<Self> {
- fn parse_items(input: ParseStream<'_>) -> parse::Result<Vec<Item>> {
- let mut items = vec![];
-
- while !input.is_empty() {
- items.push(input.parse()?);
- }
-
- Ok(items)
- }
-
- let content;
-
- let _mod_token = input.parse()?;
- let ident = input.parse()?;
- let _brace_token = braced!(content in input);
- let items = content.call(parse_items)?;
-
- Ok(Input {
- _mod_token,
- ident,
- _brace_token,
- items,
- })
- }
-}
-
-fn init_args(tokens: TokenStream2) -> parse::Result<InitArgs> {
- (|input: ParseStream<'_>| -> parse::Result<InitArgs> {
- if input.is_empty() {
- return Ok(InitArgs::default());
- }
-
- let mut local_resources = None;
-
- 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() {
- "local" => {
- if local_resources.is_some() {
- return Err(parse::Error::new(
- ident.span(),
- "argument appears more than once",
- ));
- }
-
- local_resources = Some(util::parse_local_resources(&content)?);
- }
- _ => {
- return Err(parse::Error::new(ident.span(), "unexpected argument"));
- }
- }
-
- if content.is_empty() {
- break;
- }
- // Handle comma: ,
- let _: Token![,] = content.parse()?;
- }
- }
-
- if let Some(locals) = &local_resources {
- for (ident, task_local) in locals {
- if let TaskLocal::External = task_local {
- return Err(parse::Error::new(
- ident.span(),
- "only declared local resources are allowed in init",
- ));
- }
- }
- }
-
- Ok(InitArgs {
- local_resources: local_resources.unwrap_or_default(),
- })
- })
- .parse2(tokens)
-}
-
-fn idle_args(tokens: TokenStream2) -> parse::Result<IdleArgs> {
- (|input: ParseStream<'_>| -> parse::Result<IdleArgs> {
- if input.is_empty() {
- return Ok(IdleArgs::default());
- }
-
- let mut shared_resources = None;
- let mut local_resources = None;
-
- 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() {
- "shared" => {
- if shared_resources.is_some() {
- return Err(parse::Error::new(
- ident.span(),
- "argument appears more than once",
- ));
- }
-
- shared_resources = Some(util::parse_shared_resources(&content)?);
- }
-
- "local" => {
- if local_resources.is_some() {
- return Err(parse::Error::new(
- ident.span(),
- "argument appears more than once",
- ));
- }
-
- local_resources = Some(util::parse_local_resources(&content)?);
- }
-
- _ => {
- return Err(parse::Error::new(ident.span(), "unexpected argument"));
- }
- }
- if content.is_empty() {
- break;
- }
-
- // Handle comma: ,
- let _: Token![,] = content.parse()?;
- }
- }
-
- Ok(IdleArgs {
- shared_resources: shared_resources.unwrap_or_default(),
- local_resources: local_resources.unwrap_or_default(),
- })
- })
- .parse2(tokens)
-}
-
-fn task_args(tokens: TokenStream2) -> parse::Result<Either<HardwareTaskArgs, SoftwareTaskArgs>> {
- (|input: ParseStream<'_>| -> parse::Result<Either<HardwareTaskArgs, SoftwareTaskArgs>> {
- if input.is_empty() {
- return Ok(Either::Right(SoftwareTaskArgs::default()));
- }
-
- let mut binds = None;
- let mut capacity = None;
- let mut priority = None;
- let mut shared_resources = None;
- let mut local_resources = None;
- let mut prio_span = None;
-
- let content;
- parenthesized!(content in input);
- loop {
- if content.is_empty() {
- break;
- }
-
- // Parse identifier name
- let ident: Ident = content.parse()?;
- let ident_s = ident.to_string();
-
- // Handle equal sign
- let _: Token![=] = content.parse()?;
-
- match &*ident_s {
- "binds" => {
- if binds.is_some() {
- return Err(parse::Error::new(
- ident.span(),
- "argument appears more than once",
- ));
- }
-
- if capacity.is_some() {
- return Err(parse::Error::new(
- ident.span(),
- "hardware tasks can't use the `capacity` argument",
- ));
- }
-
- // Parse identifier name
- let ident = content.parse()?;
-
- binds = Some(ident);
- }
-
- "capacity" => {
- if capacity.is_some() {
- return Err(parse::Error::new(
- ident.span(),
- "argument appears more than once",
- ));
- }
-
- if binds.is_some() {
- return Err(parse::Error::new(
- ident.span(),
- "hardware tasks can't use the `capacity` argument",
- ));
- }
-
- // #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::<u8>().ok();
- if value.is_none() || value == Some(0) {
- return Err(parse::Error::new(
- lit.span(),
- "this literal must be in the range 1...255",
- ));
- }
-
- capacity = Some(value.unwrap());
- }
-
- "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::<u8>().ok();
- if value.is_none() {
- return Err(parse::Error::new(
- lit.span(),
- "this literal must be in the range 0...255",
- ));
- }
-
- prio_span = Some(lit.span());
- priority = Some(value.unwrap());
- }
-
- "shared" => {
- if shared_resources.is_some() {
- return Err(parse::Error::new(
- ident.span(),
- "argument appears more than once",
- ));
- }
-
- shared_resources = Some(util::parse_shared_resources(&content)?);
- }
-
- "local" => {
- if local_resources.is_some() {
- return Err(parse::Error::new(
- ident.span(),
- "argument appears more than once",
- ));
- }
-
- local_resources = Some(util::parse_local_resources(&content)?);
- }
-
- _ => {
- return Err(parse::Error::new(ident.span(), "unexpected argument"));
- }
- }
-
- if content.is_empty() {
- break;
- }
-
- // Handle comma: ,
- let _: Token![,] = content.parse()?;
- }
- let priority = priority.unwrap_or(1);
- let shared_resources = shared_resources.unwrap_or_default();
- let local_resources = local_resources.unwrap_or_default();
-
- Ok(if let Some(binds) = binds {
- if priority == 0 {
- return Err(parse::Error::new(
- prio_span.unwrap(),
- "hardware tasks are not allowed to be at priority 0",
- ));
- }
-
- Either::Left(HardwareTaskArgs {
- binds,
- priority,
- shared_resources,
- local_resources,
- })
- } else {
- Either::Right(SoftwareTaskArgs {
- priority,
- shared_resources,
- local_resources,
- })
- })
- })
- .parse2(tokens)
-}
diff --git a/macros/src/syntax/parse/app.rs b/macros/src/syntax/parse/app.rs
deleted file mode 100644
index e797f75e..00000000
--- a/macros/src/syntax/parse/app.rs
+++ /dev/null
@@ -1,480 +0,0 @@
-use std::collections::HashSet;
-
-// use indexmap::map::Entry;
-use proc_macro2::TokenStream as TokenStream2;
-use syn::{
- parse::{self, ParseStream, Parser},
- spanned::Spanned,
- 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, SharedResource, SoftwareTask,
- },
- parse::{self as syntax_parse, util},
- Either, Map, Set,
-};
-
-impl AppArgs {
- pub(crate) fn parse(tokens: TokenStream2) -> parse::Result<Self> {
- (|input: ParseStream<'_>| -> parse::Result<Self> {
- let mut custom = Set::new();
- let mut device = None;
- let mut peripherals = true;
- let mut dispatchers = Dispatchers::new();
-
- loop {
- if input.is_empty() {
- break;
- }
-
- // #ident = ..
- let ident: Ident = input.parse()?;
- let _eq_token: Token![=] = input.parse()?;
-
- if custom.contains(&ident) {
- return Err(parse::Error::new(
- ident.span(),
- "argument appears more than once",
- ));
- }
-
- custom.insert(ident.clone());
-
- let ks = ident.to_string();
-
- match &*ks {
- "device" => {
- if let Ok(p) = input.parse::<Path>() {
- device = Some(p);
- } else {
- return Err(parse::Error::new(
- ident.span(),
- "unexpected argument value; this should be a path",
- ));
- }
- }
-
- "peripherals" => {
- if let Ok(p) = input.parse::<LitBool>() {
- peripherals = p.value;
- } else {
- return Err(parse::Error::new(
- ident.span(),
- "unexpected argument value; this should be a boolean",
- ));
- }
- }
-
- "dispatchers" => {
- if let Ok(p) = input.parse::<ExprArray>() {
- for e in p.elems {
- match e {
- Expr::Path(ep) => {
- let path = ep.path;
- let ident = if path.leading_colon.is_some()
- || path.segments.len() != 1
- {
- return Err(parse::Error::new(
- path.span(),
- "interrupt must be an identifier, not a path",
- ));
- } else {
- path.segments[0].ident.clone()
- };
- let span = ident.span();
- if dispatchers.contains_key(&ident) {
- return Err(parse::Error::new(
- span,
- "this extern interrupt is listed more than once",
- ));
- } else {
- dispatchers
- .insert(ident, Dispatcher { attrs: ep.attrs });
- }
- }
- _ => {
- return Err(parse::Error::new(
- e.span(),
- "interrupt must be an identifier",
- ));
- }
- }
- }
- } else {
- return Err(parse::Error::new(
- ident.span(),
- // increasing the length of the error message will break rustfmt
- "unexpected argument value; expected an array",
- ));
- }
- }
- _ => {
- return Err(parse::Error::new(ident.span(), "unexpected argument"));
- }
- }
-
- if input.is_empty() {
- break;
- }
-
- // ,
- let _: Token![,] = input.parse()?;
- }
-
- let device = if let Some(device) = device {
- device
- } else {
- return Err(parse::Error::new(input.span(), "missing `device = ...`"));
- };
-
- Ok(AppArgs {
- device,
- peripherals,
- dispatchers,
- })
- })
- .parse2(tokens)
- }
-}
-
-impl App {
- pub(crate) fn parse(args: AppArgs, input: Input) -> parse::Result<Self> {
- let mut init = None;
- let mut idle = None;
-
- let mut shared_resources_ident = None;
- let mut shared_resources = Map::new();
- let mut local_resources_ident = None;
- let mut local_resources = Map::new();
- let mut hardware_tasks = Map::new();
- let mut software_tasks = Map::new();
- let mut user_imports = vec![];
- let mut user_code = vec![];
-
- let mut seen_idents = HashSet::<Ident>::new();
- let mut bindings = HashSet::<Ident>::new();
-
- let mut check_binding = |ident: &Ident| {
- if bindings.contains(ident) {
- return Err(parse::Error::new(
- ident.span(),
- "this interrupt is already bound",
- ));
- } else {
- bindings.insert(ident.clone());
- }
-
- Ok(())
- };
-
- let mut check_ident = |ident: &Ident| {
- if seen_idents.contains(ident) {
- return Err(parse::Error::new(
- ident.span(),
- "this identifier has already been used",
- ));
- } else {
- seen_idents.insert(ident.clone());
- }
-
- Ok(())
- };
-
- for mut item in input.items {
- match item {
- Item::Fn(mut item) => {
- let span = item.sig.ident.span();
- if let Some(pos) = item
- .attrs
- .iter()
- .position(|attr| util::attr_eq(attr, "init"))
- {
- let args = InitArgs::parse(item.attrs.remove(pos).tokens)?;
-
- // If an init function already exists, error
- if init.is_some() {
- return Err(parse::Error::new(
- span,
- "`#[init]` function must appear at most once",
- ));
- }
-
- check_ident(&item.sig.ident)?;
-
- init = Some(Init::parse(args, item)?);
- } else if let Some(pos) = item
- .attrs
- .iter()
- .position(|attr| util::attr_eq(attr, "idle"))
- {
- let args = IdleArgs::parse(item.attrs.remove(pos).tokens)?;
-
- // If an idle function already exists, error
- if idle.is_some() {
- return Err(parse::Error::new(
- span,
- "`#[idle]` function must appear at most once",
- ));
- }
-
- check_ident(&item.sig.ident)?;
-
- idle = Some(Idle::parse(args, item)?);
- } else if let Some(pos) = item
- .attrs
- .iter()
- .position(|attr| util::attr_eq(attr, "task"))
- {
- if hardware_tasks.contains_key(&item.sig.ident)
- || software_tasks.contains_key(&item.sig.ident)
- {
- return Err(parse::Error::new(
- span,
- "this task is defined multiple times",
- ));
- }
-
- match syntax_parse::task_args(item.attrs.remove(pos).tokens)? {
- Either::Left(args) => {
- check_binding(&args.binds)?;
- check_ident(&item.sig.ident)?;
-
- hardware_tasks.insert(
- item.sig.ident.clone(),
- HardwareTask::parse(args, item)?,
- );
- }
-
- Either::Right(args) => {
- check_ident(&item.sig.ident)?;
-
- software_tasks.insert(
- item.sig.ident.clone(),
- SoftwareTask::parse(args, item)?,
- );
- }
- }
- } else {
- // Forward normal functions
- user_code.push(Item::Fn(item.clone()));
- }
- }
-
- Item::Struct(ref mut struct_item) => {
- // Match structures with the attribute #[shared], name of structure is not
- // important
- if let Some(_pos) = struct_item
- .attrs
- .iter()
- .position(|attr| util::attr_eq(attr, "shared"))
- {
- let span = struct_item.ident.span();
-
- shared_resources_ident = Some(struct_item.ident.clone());
-
- if !shared_resources.is_empty() {
- return Err(parse::Error::new(
- span,
- "`#[shared]` struct must appear at most once",
- ));
- }
-
- if struct_item.vis != Visibility::Inherited {
- return Err(parse::Error::new(
- struct_item.span(),
- "this item must have inherited / private visibility",
- ));
- }
-
- if let Fields::Named(fields) = &mut struct_item.fields {
- for field in &mut fields.named {
- let ident = field.ident.as_ref().expect("UNREACHABLE");
-
- if shared_resources.contains_key(ident) {
- return Err(parse::Error::new(
- ident.span(),
- "this resource is listed more than once",
- ));
- }
-
- shared_resources.insert(
- ident.clone(),
- SharedResource::parse(field, ident.span())?,
- );
- }
- } else {
- return Err(parse::Error::new(
- struct_item.span(),
- "this `struct` must have named fields",
- ));
- }
- } else if let Some(_pos) = struct_item
- .attrs
- .iter()
- .position(|attr| util::attr_eq(attr, "local"))
- {
- let span = struct_item.ident.span();
-
- local_resources_ident = Some(struct_item.ident.clone());
-
- if !local_resources.is_empty() {
- return Err(parse::Error::new(
- span,
- "`#[local]` struct must appear at most once",
- ));
- }
-
- if struct_item.vis != Visibility::Inherited {
- return Err(parse::Error::new(
- struct_item.span(),
- "this item must have inherited / private visibility",
- ));
- }
-
- if let Fields::Named(fields) = &mut struct_item.fields {
- for field in &mut fields.named {
- let ident = field.ident.as_ref().expect("UNREACHABLE");
-
- if local_resources.contains_key(ident) {
- return Err(parse::Error::new(
- ident.span(),
- "this resource is listed more than once",
- ));
- }
-
- local_resources.insert(
- ident.clone(),
- LocalResource::parse(field, ident.span())?,
- );
- }
- } else {
- return Err(parse::Error::new(
- struct_item.span(),
- "this `struct` must have named fields",
- ));
- }
- } else {
- // Structure without the #[resources] attribute should just be passed along
- user_code.push(item.clone());
- }
- }
-
- Item::ForeignMod(mod_) => {
- if !util::abi_is_rust(&mod_.abi) {
- return Err(parse::Error::new(
- mod_.abi.extern_token.span(),
- "this `extern` block must use the \"Rust\" ABI",
- ));
- }
-
- for item in mod_.items {
- if let ForeignItem::Fn(mut item) = item {
- let span = item.sig.ident.span();
- if let Some(pos) = item
- .attrs
- .iter()
- .position(|attr| util::attr_eq(attr, "task"))
- {
- if hardware_tasks.contains_key(&item.sig.ident)
- || software_tasks.contains_key(&item.sig.ident)
- {
- return Err(parse::Error::new(
- span,
- "this task is defined multiple times",
- ));
- }
-
- if item.attrs.len() != 1 {
- return Err(parse::Error::new(
- span,
- "`extern` task required `#[task(..)]` attribute",
- ));
- }
-
- match syntax_parse::task_args(item.attrs.remove(pos).tokens)? {
- Either::Left(args) => {
- check_binding(&args.binds)?;
- check_ident(&item.sig.ident)?;
-
- hardware_tasks.insert(
- item.sig.ident.clone(),
- HardwareTask::parse_foreign(args, item)?,
- );
- }
-
- Either::Right(args) => {
- check_ident(&item.sig.ident)?;
-
- software_tasks.insert(
- item.sig.ident.clone(),
- SoftwareTask::parse_foreign(args, item)?,
- );
- }
- }
- } else {
- return Err(parse::Error::new(
- span,
- "`extern` task required `#[task(..)]` attribute",
- ));
- }
- } else {
- return Err(parse::Error::new(
- item.span(),
- "this item must live outside the `#[app]` module",
- ));
- }
- }
- }
- Item::Use(itemuse_) => {
- // Store the user provided use-statements
- user_imports.push(itemuse_.clone());
- }
- _ => {
- // Anything else within the module should not make any difference
- user_code.push(item.clone());
- }
- }
- }
-
- let shared_resources_ident =
- shared_resources_ident.expect("No `#[shared]` resource struct defined");
- let local_resources_ident =
- local_resources_ident.expect("No `#[local]` resource struct defined");
- let init = init.expect("No `#[init]` function defined");
-
- if shared_resources_ident != init.user_shared_struct {
- return Err(parse::Error::new(
- init.user_shared_struct.span(),
- format!(
- "This name and the one defined on `#[shared]` are not the same. Should this be `{shared_resources_ident}`?"
- ),
- ));
- }
-
- if local_resources_ident != init.user_local_struct {
- return Err(parse::Error::new(
- init.user_local_struct.span(),
- format!(
- "This name and the one defined on `#[local]` are not the same. Should this be `{local_resources_ident}`?"
- ),
- ));
- }
-
- Ok(App {
- args,
- name: input.ident,
- init,
- idle,
- shared_resources,
- local_resources,
- user_imports,
- user_code,
- hardware_tasks,
- software_tasks,
- })
- }
-}
diff --git a/macros/src/syntax/parse/hardware_task.rs b/macros/src/syntax/parse/hardware_task.rs
deleted file mode 100644
index 7f6dfbe4..00000000
--- a/macros/src/syntax/parse/hardware_task.rs
+++ /dev/null
@@ -1,76 +0,0 @@
-use syn::{parse, ForeignItemFn, ItemFn, Stmt};
-
-use crate::syntax::parse::util::FilterAttrs;
-use crate::syntax::{
- ast::{HardwareTask, HardwareTaskArgs},
- parse::util,
-};
-
-impl HardwareTask {
- pub(crate) fn parse(args: HardwareTaskArgs, item: ItemFn) -> parse::Result<Self> {
- let span = item.sig.ident.span();
- let valid_signature = util::check_fn_signature(&item, false)
- && item.sig.inputs.len() == 1
- && util::type_is_unit(&item.sig.output);
-
- 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() {
- let FilterAttrs { cfgs, attrs, .. } = util::filter_attributes(item.attrs);
-
- return Ok(HardwareTask {
- args,
- cfgs,
- attrs,
- context,
- stmts: item.block.stmts,
- is_extern: false,
- });
- }
- }
- }
-
- Err(parse::Error::new(
- span,
- format!("this task handler must have type signature `fn({name}::Context)`"),
- ))
- }
-}
-
-impl HardwareTask {
- pub(crate) fn parse_foreign(
- args: HardwareTaskArgs,
- item: ForeignItemFn,
- ) -> parse::Result<Self> {
- let span = item.sig.ident.span();
- let valid_signature = util::check_foreign_fn_signature(&item, false)
- && item.sig.inputs.len() == 1
- && util::type_is_unit(&item.sig.output);
-
- 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() {
- let FilterAttrs { cfgs, attrs, .. } = util::filter_attributes(item.attrs);
-
- return Ok(HardwareTask {
- args,
- cfgs,
- attrs,
- context,
- stmts: Vec::<Stmt>::new(),
- is_extern: true,
- });
- }
- }
- }
-
- Err(parse::Error::new(
- span,
- format!("this task handler must have type signature `fn({name}::Context)`"),
- ))
- }
-}
diff --git a/macros/src/syntax/parse/idle.rs b/macros/src/syntax/parse/idle.rs
deleted file mode 100644
index 124c1366..00000000
--- a/macros/src/syntax/parse/idle.rs
+++ /dev/null
@@ -1,42 +0,0 @@
-use proc_macro2::TokenStream as TokenStream2;
-use syn::{parse, ItemFn};
-
-use crate::syntax::{
- ast::{Idle, IdleArgs},
- parse::util,
-};
-
-impl IdleArgs {
- pub(crate) fn parse(tokens: TokenStream2) -> parse::Result<Self> {
- crate::syntax::parse::idle_args(tokens)
- }
-}
-
-impl Idle {
- pub(crate) fn parse(args: IdleArgs, item: ItemFn) -> parse::Result<Self> {
- let valid_signature = util::check_fn_signature(&item, false)
- && item.sig.inputs.len() == 1
- && util::type_is_bottom(&item.sig.output);
-
- 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,
- });
- }
- }
- }
-
- Err(parse::Error::new(
- item.sig.ident.span(),
- format!("this `#[idle]` function must have signature `fn({name}::Context) -> !`"),
- ))
- }
-}
diff --git a/macros/src/syntax/parse/init.rs b/macros/src/syntax/parse/init.rs
deleted file mode 100644
index 0aea20bd..00000000
--- a/macros/src/syntax/parse/init.rs
+++ /dev/null
@@ -1,51 +0,0 @@
-use proc_macro2::TokenStream as TokenStream2;
-
-use syn::{parse, ItemFn};
-
-use crate::syntax::{
- ast::{Init, InitArgs},
- parse::{self as syntax_parse, util},
-};
-
-impl InitArgs {
- pub(crate) fn parse(tokens: TokenStream2) -> parse::Result<Self> {
- syntax_parse::init_args(tokens)
- }
-}
-
-impl Init {
- pub(crate) fn parse(args: InitArgs, item: ItemFn) -> parse::Result<Self> {
- let valid_signature = util::check_fn_signature(&item, false) && item.sig.inputs.len() == 1;
-
- let span = item.sig.ident.span();
-
- let name = item.sig.ident.to_string();
-
- if valid_signature {
- if let Ok((user_shared_struct, user_local_struct)) =
- util::type_is_init_return(&item.sig.output)
- {
- 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,
- });
- }
- }
- }
- }
-
- Err(parse::Error::new(
- span,
- format!(
- "the `#[init]` function must have signature `fn({name}::Context) -> (Shared resources struct, Local resources struct)`"
- ),
- ))
- }
-}
diff --git a/macros/src/syntax/parse/resource.rs b/macros/src/syntax/parse/resource.rs
deleted file mode 100644
index ff100576..00000000
--- a/macros/src/syntax/parse/resource.rs
+++ /dev/null
@@ -1,55 +0,0 @@
-use proc_macro2::Span;
-use syn::{parse, Field, Visibility};
-
-use crate::syntax::parse::util::FilterAttrs;
-use crate::syntax::{
- ast::{LocalResource, SharedResource, SharedResourceProperties},
- parse::util,
-};
-
-impl SharedResource {
- pub(crate) fn parse(item: &Field, span: Span) -> parse::Result<Self> {
- if item.vis != Visibility::Inherited {
- return Err(parse::Error::new(
- span,
- "this field must have inherited / private visibility",
- ));
- }
-
- let FilterAttrs {
- cfgs,
- mut attrs,
- docs,
- } = util::filter_attributes(item.attrs.clone());
-
- let lock_free = util::extract_lock_free(&mut attrs)?;
-
- Ok(SharedResource {
- cfgs,
- attrs,
- docs,
- ty: Box::new(item.ty.clone()),
- properties: SharedResourceProperties { lock_free },
- })
- }
-}
-
-impl LocalResource {
- pub(crate) fn parse(item: &Field, span: Span) -> parse::Result<Self> {
- if item.vis != Visibility::Inherited {
- return Err(parse::Error::new(
- span,
- "this field must have inherited / private visibility",
- ));
- }
-
- let FilterAttrs { cfgs, attrs, docs } = util::filter_attributes(item.attrs.clone());
-
- Ok(LocalResource {
- cfgs,
- attrs,
- docs,
- ty: Box::new(item.ty.clone()),
- })
- }
-}
diff --git a/macros/src/syntax/parse/software_task.rs b/macros/src/syntax/parse/software_task.rs
deleted file mode 100644
index 769aa653..00000000
--- a/macros/src/syntax/parse/software_task.rs
+++ /dev/null
@@ -1,76 +0,0 @@
-use syn::{parse, ForeignItemFn, ItemFn, Stmt};
-
-use crate::syntax::parse::util::FilterAttrs;
-use crate::syntax::{
- ast::{SoftwareTask, SoftwareTaskArgs},
- parse::util,
-};
-
-impl SoftwareTask {
- pub(crate) fn parse(args: SoftwareTaskArgs, item: ItemFn) -> parse::Result<Self> {
- 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();
-
- if valid_signature {
- if let Some((context, Ok(inputs))) = util::parse_inputs(item.sig.inputs, &name) {
- let FilterAttrs { cfgs, attrs, .. } = util::filter_attributes(item.attrs);
-
- return Ok(SoftwareTask {
- args,
- attrs,
- cfgs,
- context,
- inputs,
- stmts: item.block.stmts,
- is_extern: false,
- });
- }
- }
-
- Err(parse::Error::new(
- span,
- format!("this task handler must have type signature `async fn({name}::Context, ..)`"),
- ))
- }
-}
-
-impl SoftwareTask {
- pub(crate) fn parse_foreign(
- args: SoftwareTaskArgs,
- item: ForeignItemFn,
- ) -> parse::Result<Self> {
- 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();
-
- if valid_signature {
- if let Some((context, Ok(inputs))) = util::parse_inputs(item.sig.inputs, &name) {
- let FilterAttrs { cfgs, attrs, .. } = util::filter_attributes(item.attrs);
-
- return Ok(SoftwareTask {
- args,
- attrs,
- cfgs,
- context,
- inputs,
- stmts: Vec::<Stmt>::new(),
- is_extern: true,
- });
- }
- }
-
- Err(parse::Error::new(
- span,
- format!("this task handler must have type signature `async fn({name}::Context, ..)`"),
- ))
- }
-}
diff --git a/macros/src/syntax/parse/util.rs b/macros/src/syntax/parse/util.rs
deleted file mode 100644
index 5a5e0c0e..00000000
--- a/macros/src/syntax/parse/util.rs
+++ /dev/null
@@ -1,338 +0,0 @@
-use syn::{
- bracketed,
- parse::{self, ParseStream},
- punctuated::Punctuated,
- spanned::Spanned,
- Abi, AttrStyle, Attribute, Expr, FnArg, ForeignItemFn, Ident, ItemFn, Pat, PatType, Path,
- PathArguments, ReturnType, Token, Type, Visibility,
-};
-
-use crate::syntax::{
- ast::{Access, Local, LocalResources, SharedResources, TaskLocal},
- Map,
-};
-
-pub fn abi_is_rust(abi: &Abi) -> bool {
- match &abi.name {
- None => true,
- Some(s) => s.value() == "Rust",
- }
-}
-
-pub fn attr_eq(attr: &Attribute, name: &str) -> bool {
- attr.style == AttrStyle::Outer && attr.path.segments.len() == 1 && {
- let segment = attr.path.segments.first().unwrap();
- segment.arguments == PathArguments::None && *segment.ident.to_string() == *name
- }
-}
-
-/// checks that a function signature
-///
-/// - has no bounds (like where clauses)
-/// - is not `async`
-/// - is not `const`
-/// - is not `unsafe`
-/// - is not generic (has no type parameters)
-/// - is not variadic
-/// - uses the Rust ABI (and not e.g. "C")
-pub fn check_fn_signature(item: &ItemFn, allow_async: bool) -> bool {
- item.vis == Visibility::Inherited
- && item.sig.constness.is_none()
- && (item.sig.asyncness.is_none() || allow_async)
- && item.sig.abi.is_none()
- && item.sig.unsafety.is_none()
- && item.sig.generics.params.is_empty()
- && item.sig.generics.where_clause.is_none()
- && item.sig.variadic.is_none()
-}
-
-#[allow(dead_code)]
-pub fn check_foreign_fn_signature(item: &ForeignItemFn, allow_async: bool) -> bool {
- item.vis == Visibility::Inherited
- && item.sig.constness.is_none()
- && (item.sig.asyncness.is_none() || allow_async)
- && item.sig.abi.is_none()
- && item.sig.unsafety.is_none()
- && item.sig.generics.params.is_empty()
- && item.sig.generics.where_clause.is_none()
- && item.sig.variadic.is_none()
-}
-
-pub struct FilterAttrs {
- pub cfgs: Vec<Attribute>,
- pub docs: Vec<Attribute>,
- pub attrs: Vec<Attribute>,
-}
-
-pub fn filter_attributes(input_attrs: Vec<Attribute>) -> FilterAttrs {
- let mut cfgs = vec![];
- let mut docs = vec![];
- let mut attrs = vec![];
-
- for attr in input_attrs {
- if attr_eq(&attr, "cfg") {
- cfgs.push(attr);
- } else if attr_eq(&attr, "doc") {
- docs.push(attr);
- } else {
- attrs.push(attr);
- }
- }
-
- FilterAttrs { cfgs, docs, attrs }
-}
-
-pub fn extract_lock_free(attrs: &mut Vec<Attribute>) -> parse::Result<bool> {
- if let Some(pos) = attrs.iter().position(|attr| attr_eq(attr, "lock_free")) {
- attrs.remove(pos);
- Ok(true)
- } else {
- Ok(false)
- }
-}
-
-pub fn parse_shared_resources(content: ParseStream<'_>) -> parse::Result<SharedResources> {
- let inner;
- bracketed!(inner in content);
-
- let mut resources = Map::new();
- for e in inner.call(Punctuated::<Expr, Token![,]>::parse_terminated)? {
- let err = Err(parse::Error::new(
- e.span(),
- "identifier appears more than once in list",
- ));
- let (access, path) = match e {
- Expr::Path(e) => (Access::Exclusive, e.path),
-
- Expr::Reference(ref r) if r.mutability.is_none() => match &*r.expr {
- Expr::Path(e) => (Access::Shared, e.path.clone()),
-
- _ => return err,
- },
-
- _ => return err,
- };
-
- let ident = extract_resource_name_ident(path)?;
-
- if resources.contains_key(&ident) {
- return Err(parse::Error::new(
- ident.span(),
- "resource appears more than once in list",
- ));
- }
-
- resources.insert(ident, access);
- }
-
- Ok(resources)
-}
-
-fn extract_resource_name_ident(path: Path) -> parse::Result<Ident> {
- if path.leading_colon.is_some()
- || path.segments.len() != 1
- || path.segments[0].arguments != PathArguments::None
- {
- Err(parse::Error::new(
- path.span(),
- "resource must be an identifier, not a path",
- ))
- } else {
- Ok(path.segments[0].ident.clone())
- }
-}
-
-pub fn parse_local_resources(content: ParseStream<'_>) -> parse::Result<LocalResources> {
- let inner;
- bracketed!(inner in content);
-
- let mut resources = Map::new();
-
- for e in inner.call(Punctuated::<Expr, Token![,]>::parse_terminated)? {
- let err = Err(parse::Error::new(
- e.span(),
- "identifier appears more than once in list",
- ));
-
- let (name, local) = match e {
- // local = [IDENT],
- Expr::Path(path) => {
- if !path.attrs.is_empty() {
- return Err(parse::Error::new(
- path.span(),
- "attributes are not supported here",
- ));
- }
-
- let ident = extract_resource_name_ident(path.path)?;
- // let (cfgs, attrs) = extract_cfgs(path.attrs);
-
- (ident, TaskLocal::External)
- }
-
- // local = [IDENT: TYPE = EXPR]
- Expr::Assign(e) => {
- let (name, ty, cfgs, attrs) = match *e.left {
- Expr::Type(t) => {
- // Extract name and attributes
- let (name, cfgs, attrs) = match *t.expr {
- Expr::Path(path) => {
- let name = extract_resource_name_ident(path.path)?;
- let FilterAttrs { cfgs, attrs, .. } = filter_attributes(path.attrs);
-
- (name, cfgs, attrs)
- }
- _ => return err,
- };
-
- let ty = t.ty;
-
- // Error check
- match &*ty {
- Type::Array(_) => {}
- Type::Path(_) => {}
- Type::Ptr(_) => {}
- Type::Tuple(_) => {}
- _ => return Err(parse::Error::new(
- ty.span(),
- "unsupported type, must be an array, tuple, pointer or type path",
- )),
- };
-
- (name, ty, cfgs, attrs)
- }
- e => return Err(parse::Error::new(e.span(), "malformed, expected a type")),
- };
-
- let expr = e.right; // Expr
-
- (
- name,
- TaskLocal::Declared(Local {
- attrs,
- cfgs,
- ty,
- expr,
- }),
- )
- }
-
- expr => {
- return Err(parse::Error::new(
- expr.span(),
- "malformed, expected 'IDENT: TYPE = EXPR'",
- ))
- }
- };
-
- resources.insert(name, local);
- }
-
- Ok(resources)
-}
-
-type ParseInputResult = Option<(Box<Pat>, Result<Vec<PatType>, FnArg>)>;
-
-pub fn parse_inputs(inputs: Punctuated<FnArg, Token![,]>, name: &str) -> ParseInputResult {
- 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::<Result<Vec<_>, _>>();
-
- Some((first.pat, rest))
- } else {
- None
- }
- }
-
- _ => None,
- }
-}
-
-pub fn type_is_bottom(ty: &ReturnType) -> bool {
- if let ReturnType::Type(_, ty) = ty {
- matches!(**ty, Type::Never(_))
- } else {
- false
- }
-}
-
-fn extract_init_resource_name_ident(ty: Type) -> Result<Ident, ()> {
- match ty {
- Type::Path(path) => {
- let path = path.path;
-
- if path.leading_colon.is_some()
- || path.segments.len() != 1
- || path.segments[0].arguments != PathArguments::None
- {
- Err(())
- } else {
- Ok(path.segments[0].ident.clone())
- }
- }
- _ => Err(()),
- }
-}
-
-/// Checks Init's return type, return the user provided types for analysis
-pub fn type_is_init_return(ty: &ReturnType) -> Result<(Ident, Ident), ()> {
- match ty {
- ReturnType::Default => Err(()),
-
- ReturnType::Type(_, ty) => match &**ty {
- Type::Tuple(t) => {
- // return should be:
- // fn -> (User's #[shared] struct, User's #[local] struct)
- //
- // We check the length and the last one here, analysis checks that the user
- // provided structs are correct.
- if t.elems.len() == 2 {
- return Ok((
- extract_init_resource_name_ident(t.elems[0].clone())?,
- extract_init_resource_name_ident(t.elems[1].clone())?,
- ));
- }
-
- Err(())
- }
-
- _ => Err(()),
- },
- }
-}
-
-pub fn type_is_path(ty: &Type, segments: &[&str]) -> bool {
- match ty {
- Type::Path(tpath) if tpath.qself.is_none() => {
- tpath.path.segments.len() == segments.len()
- && tpath
- .path
- .segments
- .iter()
- .zip(segments)
- .all(|(lhs, rhs)| lhs.ident == **rhs)
- }
-
- _ => false,
- }
-}
-
-pub fn type_is_unit(ty: &ReturnType) -> bool {
- if let ReturnType::Type(_, ty) = ty {
- if let Type::Tuple(ref tuple) = **ty {
- tuple.elems.is_empty()
- } else {
- false
- }
- } else {
- true
- }
-}
diff --git a/macros/src/tests.rs b/macros/src/tests.rs
deleted file mode 100644
index e9e3326e..00000000
--- a/macros/src/tests.rs
+++ /dev/null
@@ -1,4 +0,0 @@
-// NOTE these tests are specific to the Cortex-M port; `rtic-syntax` has a more extensive test suite
-// that tests functionality common to all the RTIC ports
-
-mod single;
diff --git a/macros/src/tests/single.rs b/macros/src/tests/single.rs
deleted file mode 100644
index f20c9ccb..00000000
--- a/macros/src/tests/single.rs
+++ /dev/null
@@ -1,40 +0,0 @@
-use quote::quote;
-use rtic_syntax::Settings;
-
-#[test]
-fn analyze() {
- let mut settings = Settings::default();
- settings.parse_extern_interrupt = true;
- let (app, analysis) = rtic_syntax::parse2(
- // First interrupt is assigned to the highest priority dispatcher
- quote!(device = pac, dispatchers = [B, A]),
- quote!(
- mod app {
- #[shared]
- struct Shared {}
-
- #[local]
- struct Local {}
-
- #[init]
- fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {
- (Shared {}, Local {}, init::Monotonics())
- }
-
- #[task(priority = 1)]
- fn a(_: a::Context) {}
-
- #[task(priority = 2)]
- fn b(_: b::Context) {}
- }
- ),
- settings,
- )
- .unwrap();
-
- let analysis = crate::analyze::app(analysis, &app);
- let interrupts = &analysis.interrupts;
- assert_eq!(interrupts.len(), 2);
- assert_eq!(interrupts[&2].0.to_string(), "B");
- assert_eq!(interrupts[&1].0.to_string(), "A");
-}