From 7614b96fe45240dafe91ae549e712b560e2d4c10 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Sat, 31 Dec 2022 14:45:13 +0100 Subject: RTIC v2: Initial commit rtic-syntax is now part of RTIC repository --- macros/.gitignore | 2 + macros/Cargo.toml | 41 +- macros/src/analyze.rs | 42 +- macros/src/bindings.rs | 0 macros/src/codegen.rs | 97 +--- macros/src/codegen/assertions.rs | 10 +- macros/src/codegen/async_dispatchers.rs | 129 +++++ macros/src/codegen/dispatchers.rs | 67 ++- macros/src/codegen/hardware_tasks.rs | 16 +- macros/src/codegen/idle.rs | 15 +- macros/src/codegen/init.rs | 16 +- macros/src/codegen/local_resources.rs | 6 +- macros/src/codegen/local_resources_struct.rs | 19 +- macros/src/codegen/module.rs | 263 +++++----- macros/src/codegen/monotonic.rs | 280 +++++++++++ macros/src/codegen/post_init.rs | 17 +- macros/src/codegen/pre_init.rs | 22 +- macros/src/codegen/shared_resources.rs | 18 +- macros/src/codegen/shared_resources_struct.rs | 40 +- macros/src/codegen/software_tasks.rs | 132 ++--- macros/src/codegen/timer_queue.rs | 33 +- macros/src/codegen/util.rs | 43 +- macros/src/lib.rs | 84 ++-- macros/src/syntax.rs | 158 ++++++ macros/src/syntax/.github/bors.toml | 3 + macros/src/syntax/.github/workflows/build.yml | 213 ++++++++ macros/src/syntax/.github/workflows/changelog.yml | 28 ++ .../workflows/properties/build.properties.json | 6 + macros/src/syntax/.gitignore | 4 + macros/src/syntax/.travis.yml | 31 ++ macros/src/syntax/accessors.rs | 113 +++++ macros/src/syntax/analyze.rs | 448 +++++++++++++++++ macros/src/syntax/ast.rs | 380 +++++++++++++++ macros/src/syntax/check.rs | 66 +++ macros/src/syntax/optimize.rs | 36 ++ macros/src/syntax/parse.rs | 520 ++++++++++++++++++++ macros/src/syntax/parse/app.rs | 539 +++++++++++++++++++++ macros/src/syntax/parse/hardware_task.rs | 96 ++++ macros/src/syntax/parse/idle.rs | 45 ++ macros/src/syntax/parse/init.rs | 52 ++ macros/src/syntax/parse/monotonic.rs | 42 ++ macros/src/syntax/parse/resource.rs | 55 +++ macros/src/syntax/parse/software_task.rs | 86 ++++ macros/src/syntax/parse/util.rs | 338 +++++++++++++ macros/tests/ui.rs | 7 + macros/ui/async-local-resouces.rs | 28 ++ macros/ui/async-local-resouces.stderr | 5 + macros/ui/async-zero-prio-tasks.rs | 19 + macros/ui/async-zero-prio-tasks.stderr | 11 + macros/ui/extern-interrupt-used.rs | 16 + macros/ui/extern-interrupt-used.stderr | 5 + macros/ui/idle-double-local.rs | 9 + macros/ui/idle-double-local.stderr | 5 + macros/ui/idle-double-shared.rs | 9 + macros/ui/idle-double-shared.stderr | 5 + macros/ui/idle-input.rs | 9 + macros/ui/idle-input.stderr | 5 + macros/ui/idle-no-context.rs | 9 + macros/ui/idle-no-context.stderr | 5 + macros/ui/idle-not-divergent.rs | 7 + macros/ui/idle-not-divergent.stderr | 5 + macros/ui/idle-output.rs | 9 + macros/ui/idle-output.stderr | 5 + macros/ui/idle-pub.rs | 9 + macros/ui/idle-pub.stderr | 5 + macros/ui/idle-unsafe.rs | 9 + macros/ui/idle-unsafe.stderr | 5 + macros/ui/init-divergent.rs | 13 + macros/ui/init-divergent.stderr | 5 + macros/ui/init-double-local.rs | 7 + macros/ui/init-double-local.stderr | 5 + macros/ui/init-double-shared.rs | 7 + macros/ui/init-double-shared.stderr | 5 + macros/ui/init-input.rs | 13 + macros/ui/init-input.stderr | 5 + macros/ui/init-no-context.rs | 13 + macros/ui/init-no-context.stderr | 5 + macros/ui/init-output.rs | 9 + macros/ui/init-output.stderr | 5 + macros/ui/init-pub.rs | 13 + macros/ui/init-pub.stderr | 5 + macros/ui/init-unsafe.rs | 7 + macros/ui/init-unsafe.stderr | 5 + macros/ui/interrupt-double.rs | 10 + macros/ui/interrupt-double.stderr | 5 + macros/ui/local-collision-2.rs | 21 + macros/ui/local-collision-2.stderr | 17 + macros/ui/local-collision.rs | 21 + macros/ui/local-collision.stderr | 11 + macros/ui/local-malformed-1.rs | 16 + macros/ui/local-malformed-1.stderr | 5 + macros/ui/local-malformed-2.rs | 16 + macros/ui/local-malformed-2.stderr | 5 + macros/ui/local-malformed-3.rs | 16 + macros/ui/local-malformed-3.stderr | 5 + macros/ui/local-malformed-4.rs | 16 + macros/ui/local-malformed-4.stderr | 5 + macros/ui/local-not-declared.rs | 16 + macros/ui/local-not-declared.stderr | 5 + macros/ui/local-pub.rs | 9 + macros/ui/local-pub.stderr | 5 + macros/ui/local-shared-attribute.rs | 14 + macros/ui/local-shared-attribute.stderr | 5 + macros/ui/local-shared.rs | 28 ++ macros/ui/local-shared.stderr | 11 + macros/ui/monotonic-binds-collision-task.rs | 10 + macros/ui/monotonic-binds-collision-task.stderr | 5 + macros/ui/monotonic-binds-collision.rs | 10 + macros/ui/monotonic-binds-collision.stderr | 5 + macros/ui/monotonic-double-binds.rs | 7 + macros/ui/monotonic-double-binds.stderr | 5 + macros/ui/monotonic-double-default.rs | 7 + macros/ui/monotonic-double-default.stderr | 5 + macros/ui/monotonic-double-prio.rs | 7 + macros/ui/monotonic-double-prio.stderr | 5 + macros/ui/monotonic-double.rs | 10 + macros/ui/monotonic-double.stderr | 5 + macros/ui/monotonic-name-collision.rs | 10 + macros/ui/monotonic-name-collision.stderr | 5 + macros/ui/monotonic-no-binds.rs | 7 + macros/ui/monotonic-no-binds.stderr | 5 + macros/ui/monotonic-no-paran.rs | 8 + macros/ui/monotonic-no-paran.stderr | 5 + macros/ui/monotonic-timer-collision.rs | 10 + macros/ui/monotonic-timer-collision.stderr | 5 + macros/ui/monotonic-with-attrs.rs | 8 + macros/ui/monotonic-with-attrs.stderr | 5 + macros/ui/pub-local.stderr | 5 + macros/ui/pub-shared.stderr | 5 + macros/ui/shared-lock-free.rs | 38 ++ macros/ui/shared-lock-free.stderr | 17 + macros/ui/shared-not-declared.rs | 16 + macros/ui/shared-not-declared.stderr | 5 + macros/ui/shared-pub.rs | 9 + macros/ui/shared-pub.stderr | 5 + macros/ui/task-bind.rs | 7 + macros/ui/task-bind.stderr | 5 + macros/ui/task-divergent.rs | 9 + macros/ui/task-divergent.stderr | 5 + macros/ui/task-double-capacity.rs | 7 + macros/ui/task-double-capacity.stderr | 5 + macros/ui/task-double-local.rs | 7 + macros/ui/task-double-local.stderr | 5 + macros/ui/task-double-priority.rs | 7 + macros/ui/task-double-priority.stderr | 5 + macros/ui/task-double-shared.rs | 7 + macros/ui/task-double-shared.stderr | 5 + macros/ui/task-idle.rs | 13 + macros/ui/task-idle.stderr | 5 + macros/ui/task-init.rs | 17 + macros/ui/task-init.stderr | 5 + macros/ui/task-interrupt-same-prio-spawn.rs | 7 + macros/ui/task-interrupt-same-prio-spawn.stderr | 5 + macros/ui/task-interrupt.rs | 10 + macros/ui/task-interrupt.stderr | 5 + macros/ui/task-no-context.rs | 7 + macros/ui/task-no-context.stderr | 5 + macros/ui/task-priority-too-high.rs | 7 + macros/ui/task-priority-too-high.stderr | 5 + macros/ui/task-priority-too-low.rs | 7 + macros/ui/task-priority-too-low.stderr | 5 + macros/ui/task-pub.rs | 7 + macros/ui/task-pub.stderr | 5 + macros/ui/task-unsafe.rs | 7 + macros/ui/task-unsafe.stderr | 5 + 165 files changed, 5205 insertions(+), 481 deletions(-) create mode 100644 macros/.gitignore create mode 100644 macros/src/bindings.rs create mode 100644 macros/src/codegen/async_dispatchers.rs create mode 100644 macros/src/codegen/monotonic.rs create mode 100644 macros/src/syntax.rs create mode 100644 macros/src/syntax/.github/bors.toml create mode 100644 macros/src/syntax/.github/workflows/build.yml create mode 100644 macros/src/syntax/.github/workflows/changelog.yml create mode 100644 macros/src/syntax/.github/workflows/properties/build.properties.json create mode 100644 macros/src/syntax/.gitignore create mode 100644 macros/src/syntax/.travis.yml create mode 100644 macros/src/syntax/accessors.rs create mode 100644 macros/src/syntax/analyze.rs create mode 100644 macros/src/syntax/ast.rs create mode 100644 macros/src/syntax/check.rs create mode 100644 macros/src/syntax/optimize.rs create mode 100644 macros/src/syntax/parse.rs create mode 100644 macros/src/syntax/parse/app.rs create mode 100644 macros/src/syntax/parse/hardware_task.rs create mode 100644 macros/src/syntax/parse/idle.rs create mode 100644 macros/src/syntax/parse/init.rs create mode 100644 macros/src/syntax/parse/monotonic.rs create mode 100644 macros/src/syntax/parse/resource.rs create mode 100644 macros/src/syntax/parse/software_task.rs create mode 100644 macros/src/syntax/parse/util.rs create mode 100644 macros/tests/ui.rs create mode 100644 macros/ui/async-local-resouces.rs create mode 100644 macros/ui/async-local-resouces.stderr create mode 100644 macros/ui/async-zero-prio-tasks.rs create mode 100644 macros/ui/async-zero-prio-tasks.stderr create mode 100644 macros/ui/extern-interrupt-used.rs create mode 100644 macros/ui/extern-interrupt-used.stderr create mode 100644 macros/ui/idle-double-local.rs create mode 100644 macros/ui/idle-double-local.stderr create mode 100644 macros/ui/idle-double-shared.rs create mode 100644 macros/ui/idle-double-shared.stderr create mode 100644 macros/ui/idle-input.rs create mode 100644 macros/ui/idle-input.stderr create mode 100644 macros/ui/idle-no-context.rs create mode 100644 macros/ui/idle-no-context.stderr create mode 100644 macros/ui/idle-not-divergent.rs create mode 100644 macros/ui/idle-not-divergent.stderr create mode 100644 macros/ui/idle-output.rs create mode 100644 macros/ui/idle-output.stderr create mode 100644 macros/ui/idle-pub.rs create mode 100644 macros/ui/idle-pub.stderr create mode 100644 macros/ui/idle-unsafe.rs create mode 100644 macros/ui/idle-unsafe.stderr create mode 100644 macros/ui/init-divergent.rs create mode 100644 macros/ui/init-divergent.stderr create mode 100644 macros/ui/init-double-local.rs create mode 100644 macros/ui/init-double-local.stderr create mode 100644 macros/ui/init-double-shared.rs create mode 100644 macros/ui/init-double-shared.stderr create mode 100644 macros/ui/init-input.rs create mode 100644 macros/ui/init-input.stderr create mode 100644 macros/ui/init-no-context.rs create mode 100644 macros/ui/init-no-context.stderr create mode 100644 macros/ui/init-output.rs create mode 100644 macros/ui/init-output.stderr create mode 100644 macros/ui/init-pub.rs create mode 100644 macros/ui/init-pub.stderr create mode 100644 macros/ui/init-unsafe.rs create mode 100644 macros/ui/init-unsafe.stderr create mode 100644 macros/ui/interrupt-double.rs create mode 100644 macros/ui/interrupt-double.stderr create mode 100644 macros/ui/local-collision-2.rs create mode 100644 macros/ui/local-collision-2.stderr create mode 100644 macros/ui/local-collision.rs create mode 100644 macros/ui/local-collision.stderr create mode 100644 macros/ui/local-malformed-1.rs create mode 100644 macros/ui/local-malformed-1.stderr create mode 100644 macros/ui/local-malformed-2.rs create mode 100644 macros/ui/local-malformed-2.stderr create mode 100644 macros/ui/local-malformed-3.rs create mode 100644 macros/ui/local-malformed-3.stderr create mode 100644 macros/ui/local-malformed-4.rs create mode 100644 macros/ui/local-malformed-4.stderr create mode 100644 macros/ui/local-not-declared.rs create mode 100644 macros/ui/local-not-declared.stderr create mode 100644 macros/ui/local-pub.rs create mode 100644 macros/ui/local-pub.stderr create mode 100644 macros/ui/local-shared-attribute.rs create mode 100644 macros/ui/local-shared-attribute.stderr create mode 100644 macros/ui/local-shared.rs create mode 100644 macros/ui/local-shared.stderr create mode 100644 macros/ui/monotonic-binds-collision-task.rs create mode 100644 macros/ui/monotonic-binds-collision-task.stderr create mode 100644 macros/ui/monotonic-binds-collision.rs create mode 100644 macros/ui/monotonic-binds-collision.stderr create mode 100644 macros/ui/monotonic-double-binds.rs create mode 100644 macros/ui/monotonic-double-binds.stderr create mode 100644 macros/ui/monotonic-double-default.rs create mode 100644 macros/ui/monotonic-double-default.stderr create mode 100644 macros/ui/monotonic-double-prio.rs create mode 100644 macros/ui/monotonic-double-prio.stderr create mode 100644 macros/ui/monotonic-double.rs create mode 100644 macros/ui/monotonic-double.stderr create mode 100644 macros/ui/monotonic-name-collision.rs create mode 100644 macros/ui/monotonic-name-collision.stderr create mode 100644 macros/ui/monotonic-no-binds.rs create mode 100644 macros/ui/monotonic-no-binds.stderr create mode 100644 macros/ui/monotonic-no-paran.rs create mode 100644 macros/ui/monotonic-no-paran.stderr create mode 100644 macros/ui/monotonic-timer-collision.rs create mode 100644 macros/ui/monotonic-timer-collision.stderr create mode 100644 macros/ui/monotonic-with-attrs.rs create mode 100644 macros/ui/monotonic-with-attrs.stderr create mode 100644 macros/ui/pub-local.stderr create mode 100644 macros/ui/pub-shared.stderr create mode 100644 macros/ui/shared-lock-free.rs create mode 100644 macros/ui/shared-lock-free.stderr create mode 100644 macros/ui/shared-not-declared.rs create mode 100644 macros/ui/shared-not-declared.stderr create mode 100644 macros/ui/shared-pub.rs create mode 100644 macros/ui/shared-pub.stderr create mode 100644 macros/ui/task-bind.rs create mode 100644 macros/ui/task-bind.stderr create mode 100644 macros/ui/task-divergent.rs create mode 100644 macros/ui/task-divergent.stderr create mode 100644 macros/ui/task-double-capacity.rs create mode 100644 macros/ui/task-double-capacity.stderr create mode 100644 macros/ui/task-double-local.rs create mode 100644 macros/ui/task-double-local.stderr create mode 100644 macros/ui/task-double-priority.rs create mode 100644 macros/ui/task-double-priority.stderr create mode 100644 macros/ui/task-double-shared.rs create mode 100644 macros/ui/task-double-shared.stderr create mode 100644 macros/ui/task-idle.rs create mode 100644 macros/ui/task-idle.stderr create mode 100644 macros/ui/task-init.rs create mode 100644 macros/ui/task-init.stderr create mode 100644 macros/ui/task-interrupt-same-prio-spawn.rs create mode 100644 macros/ui/task-interrupt-same-prio-spawn.stderr create mode 100644 macros/ui/task-interrupt.rs create mode 100644 macros/ui/task-interrupt.stderr create mode 100644 macros/ui/task-no-context.rs create mode 100644 macros/ui/task-no-context.stderr create mode 100644 macros/ui/task-priority-too-high.rs create mode 100644 macros/ui/task-priority-too-high.stderr create mode 100644 macros/ui/task-priority-too-low.rs create mode 100644 macros/ui/task-priority-too-low.stderr create mode 100644 macros/ui/task-pub.rs create mode 100644 macros/ui/task-pub.stderr create mode 100644 macros/ui/task-unsafe.rs create mode 100644 macros/ui/task-unsafe.stderr (limited to 'macros') diff --git a/macros/.gitignore b/macros/.gitignore new file mode 100644 index 0000000..4fffb2f --- /dev/null +++ b/macros/.gitignore @@ -0,0 +1,2 @@ +/target +/Cargo.lock diff --git a/macros/Cargo.toml b/macros/Cargo.toml index c3f0561..1cc9556 100644 --- a/macros/Cargo.toml +++ b/macros/Cargo.toml @@ -1,28 +1,41 @@ [package] authors = [ "The Real-Time Interrupt-driven Concurrency developers", + "Emil Fresk ", + "Henrik Tjäder ", "Jorge Aparicio ", + "Per Lindgren ", ] -categories = ["concurrency", "embedded", "no-std"] -description = "Procedural macros of the cortex-m-rtic crate" -documentation = "https://rtic-rs.github.io/cortex-m-rtic/api/cortex_m_rtic" +categories = ["concurrency", "embedded", "no-std", "asynchronous"] +description = "Procedural macros, syntax parsing, and codegen of the RTIC crate" +documentation = "https://rtic-rs.github.io/rtic/api/rtic" edition = "2021" -keywords = ["arm", "cortex-m"] +keywords = ["arm", "cortex-m", "risc-v", "embedded", "async", "runtime", "futures", "await", "no-std", "rtos", "bare-metal"] license = "MIT OR Apache-2.0" -name = "cortex-m-rtic-macros" +name = "rtic-macros" readme = "../README.md" -repository = "https://github.com/rtic-rs/cortex-m-rtic" -version = "1.1.6" +repository = "https://github.com/rtic-rs/rtic" + +version = "2.0.0-alpha.0" [lib] proc-macro = true +[feature] +default = [] +debugprint = [] +# list of supported codegen backends +thumbv6 = [] +thumbv7 = [] +# riscv-clic = [] +# riscv-ch32 = [] + [dependencies] -proc-macro2 = "1" -proc-macro-error = "1" -quote = "1" -syn = "1" -rtic-syntax = "1.0.3" +indexmap = "1.9.2" +proc-macro2 = "1.0.49" +proc-macro-error = "1.0.4" +quote = "1.0.23" +syn = { version = "1.0.107", features = ["extra-traits", "full"] } -[features] -debugprint = [] +[dev-dependencies] +trybuild = "1.0.73" diff --git a/macros/src/analyze.rs b/macros/src/analyze.rs index d255b7f..ec12cfb 100644 --- a/macros/src/analyze.rs +++ b/macros/src/analyze.rs @@ -1,17 +1,17 @@ use core::ops; use std::collections::{BTreeMap, BTreeSet}; -use rtic_syntax::{ +use crate::syntax::{ analyze::{self, Priority}, - ast::{App, ExternInterrupt}, - P, + ast::{App, Dispatcher}, }; use syn::Ident; /// Extend the upstream `Analysis` struct with our field pub struct Analysis { - parent: P, - pub interrupts: BTreeMap, + parent: analyze::Analysis, + pub interrupts_normal: BTreeMap, + pub interrupts_async: BTreeMap, } impl ops::Deref for Analysis { @@ -23,25 +23,43 @@ impl ops::Deref for Analysis { } // Assign an interrupt to each priority level -pub fn app(analysis: P, app: &App) -> P { +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() + .filter(|task| !task.is_async) + .map(|task| task.args.priority) + .collect::>(); + + let priorities_async = app + .software_tasks + .values() + .filter(|task| task.is_async) .map(|task| task.args.priority) .collect::>(); // map from priorities to interrupts (holding name and attributes) - let interrupts: BTreeMap = priorities + + let interrupts_normal: BTreeMap = priorities .iter() .copied() .rev() - .zip(&app.args.extern_interrupts) - .map(|(p, (id, ext))| (p, (id.clone(), ext.clone()))) + .map(|p| (p, available_interrupt.pop().expect("UNREACHABLE"))) .collect(); - P::new(Analysis { + let interrupts_async: BTreeMap = priorities_async + .iter() + .copied() + .rev() + .map(|p| (p, available_interrupt.pop().expect("UNREACHABLE"))) + .collect(); + + Analysis { parent: analysis, - interrupts, - }) + interrupts_normal, + interrupts_async, + } } diff --git a/macros/src/bindings.rs b/macros/src/bindings.rs new file mode 100644 index 0000000..e69de29 diff --git a/macros/src/codegen.rs b/macros/src/codegen.rs index 89173d4..ef81732 100644 --- a/macros/src/codegen.rs +++ b/macros/src/codegen.rs @@ -1,10 +1,11 @@ use proc_macro2::TokenStream as TokenStream2; use quote::quote; -use rtic_syntax::ast::App; -use crate::{analyze::Analysis, check::Extra}; +use crate::analyze::Analysis; +use crate::syntax::ast::App; mod assertions; +mod async_dispatchers; mod dispatchers; mod hardware_tasks; mod idle; @@ -12,6 +13,7 @@ mod init; mod local_resources; mod local_resources_struct; mod module; +mod monotonic; mod post_init; mod pre_init; mod shared_resources; @@ -21,22 +23,22 @@ mod timer_queue; mod util; #[allow(clippy::too_many_lines)] -pub fn app(app: &App, analysis: &Analysis, extra: &Extra) -> TokenStream2 { +pub fn app(app: &App, analysis: &Analysis) -> TokenStream2 { let mut mod_app = vec![]; let mut mains = vec![]; let mut root = vec![]; let mut user = vec![]; // Generate the `main` function - let assertion_stmts = assertions::codegen(app, analysis, extra); + let assertion_stmts = assertions::codegen(app, analysis); - let pre_init_stmts = pre_init::codegen(app, analysis, extra); + let pre_init_stmts = pre_init::codegen(app, analysis); - let (mod_app_init, root_init, user_init, call_init) = init::codegen(app, analysis, extra); + let (mod_app_init, root_init, user_init, call_init) = init::codegen(app, analysis); let post_init_stmts = post_init::codegen(app, analysis); - let (mod_app_idle, root_idle, user_idle, call_idle) = idle::codegen(app, analysis, extra); + let (mod_app_idle, root_idle, user_idle, call_idle) = idle::codegen(app, analysis); user.push(quote!( #user_init @@ -84,82 +86,25 @@ pub fn app(app: &App, analysis: &Analysis, extra: &Extra) -> TokenStream2 { } )); - let (mod_app_shared_resources, mod_shared_resources) = - shared_resources::codegen(app, analysis, extra); - let (mod_app_local_resources, mod_local_resources) = - local_resources::codegen(app, analysis, extra); + let (mod_app_shared_resources, mod_shared_resources) = shared_resources::codegen(app, analysis); + let (mod_app_local_resources, mod_local_resources) = local_resources::codegen(app, analysis); let (mod_app_hardware_tasks, root_hardware_tasks, user_hardware_tasks) = - hardware_tasks::codegen(app, analysis, extra); + hardware_tasks::codegen(app, analysis); let (mod_app_software_tasks, root_software_tasks, user_software_tasks) = - software_tasks::codegen(app, analysis, extra); + software_tasks::codegen(app, analysis); - let mod_app_dispatchers = dispatchers::codegen(app, analysis, extra); - let mod_app_timer_queue = timer_queue::codegen(app, analysis, extra); + let monotonics = monotonic::codegen(app, analysis); + + let mod_app_dispatchers = dispatchers::codegen(app, analysis); + let mod_app_async_dispatchers = async_dispatchers::codegen(app, analysis); + let mod_app_timer_queue = timer_queue::codegen(app, analysis); let user_imports = &app.user_imports; let user_code = &app.user_code; let name = &app.name; - let device = &extra.device; - - let monotonic_parts: Vec<_> = app - .monotonics - .iter() - .map(|(_, monotonic)| { - let name = &monotonic.ident; - let name_str = &name.to_string(); - let cfgs = &monotonic.cfgs; - let ident = util::monotonic_ident(name_str); - let doc = &format!( - "This module holds the static implementation for `{}::now()`", - name_str - ); - - let default_monotonic = if monotonic.args.default { - quote!( - #(#cfgs)* - pub use #name::now; - ) - } else { - quote!() - }; - - quote! { - #default_monotonic - - #[doc = #doc] - #[allow(non_snake_case)] - #(#cfgs)* - pub mod #name { - - /// Read the current time from this monotonic - pub fn now() -> ::Instant { - rtic::export::interrupt::free(|_| { - use rtic::Monotonic as _; - if let Some(m) = unsafe{ &mut *super::super::#ident.get_mut() } { - m.now() - } else { - ::zero() - } - }) - } - } - } - }) - .collect(); - - let monotonics = if monotonic_parts.is_empty() { - quote!() - } else { - quote!( - pub use rtic::Monotonic as _; - - /// Holds static methods for each monotonic. - pub mod monotonics { - #(#monotonic_parts)* - } - ) - }; + let device = &app.args.device; + let rt_err = util::rt_err_ident(); quote!( @@ -205,6 +150,8 @@ pub fn app(app: &App, analysis: &Analysis, extra: &Extra) -> TokenStream2 { #(#mod_app_dispatchers)* + #(#mod_app_async_dispatchers)* + #(#mod_app_timer_queue)* #(#mains)* diff --git a/macros/src/codegen/assertions.rs b/macros/src/codegen/assertions.rs index 3e0ad61..0f8326c 100644 --- a/macros/src/codegen/assertions.rs +++ b/macros/src/codegen/assertions.rs @@ -1,11 +1,11 @@ use proc_macro2::TokenStream as TokenStream2; use quote::quote; -use crate::{analyze::Analysis, check::Extra, codegen::util}; -use rtic_syntax::ast::App; +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, extra: &Extra) -> Vec { +pub fn codegen(app: &App, analysis: &Analysis) -> Vec { let mut stmts = vec![]; for ty in &analysis.send_types { @@ -21,7 +21,7 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec();)); } - let device = &extra.device; + let device = &app.args.device; let chunks_name = util::priority_mask_chunks_ident(); let no_basepri_checks: Vec<_> = app .hardware_tasks @@ -29,9 +29,7 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec= (#chunks_name * 32) { ::core::panic!("An interrupt out of range is used while in armv6 or armv8m.base"); } diff --git a/macros/src/codegen/async_dispatchers.rs b/macros/src/codegen/async_dispatchers.rs new file mode 100644 index 0000000..8b0e928 --- /dev/null +++ b/macros/src/codegen/async_dispatchers.rs @@ -0,0 +1,129 @@ +use crate::syntax::ast::App; +use crate::{analyze::Analysis, codegen::util}; +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; + +/// Generates task dispatchers +pub fn codegen(app: &App, analysis: &Analysis) -> Vec { + let mut items = vec![]; + + let interrupts = &analysis.interrupts_async; + + // Generate executor definition and priority in global scope + for (name, task) in app.software_tasks.iter() { + if task.is_async { + let type_name = util::internal_task_ident(name, "F"); + let exec_name = util::internal_task_ident(name, "EXEC"); + let prio_name = util::internal_task_ident(name, "PRIORITY"); + + items.push(quote!( + #[allow(non_camel_case_types)] + type #type_name = impl core::future::Future + 'static; + #[allow(non_upper_case_globals)] + static #exec_name: + rtic::RacyCell> = + rtic::RacyCell::new(rtic::export::executor::AsyncTaskExecutor::new()); + + // The executors priority, this can be any value - we will overwrite it when we + // start a task + #[allow(non_upper_case_globals)] + static #prio_name: rtic::RacyCell = + unsafe { rtic::RacyCell::new(rtic::export::Priority::new(0)) }; + )); + } + } + + for (&level, channel) in &analysis.channels { + if channel + .tasks + .iter() + .map(|task_name| !app.software_tasks[task_name].is_async) + .all(|is_not_async| is_not_async) + { + // check if all tasks are not async, if so don't generate this. + continue; + } + + let mut stmts = vec![]; + let device = &app.args.device; + let enum_ = util::interrupt_ident(); + let interrupt = util::suffixed(&interrupts[&level].0.to_string()); + + for name in channel + .tasks + .iter() + .filter(|name| app.software_tasks[*name].is_async) + { + let exec_name = util::internal_task_ident(name, "EXEC"); + let prio_name = util::internal_task_ident(name, "PRIORITY"); + let task = &app.software_tasks[name]; + // let cfgs = &task.cfgs; + let (_, tupled, pats, input_types) = util::regroup_inputs(&task.inputs); + let executor_run_ident = util::executor_run_ident(name); + + let n = util::capacity_literal(channel.capacity as usize + 1); + let rq = util::rq_async_ident(name); + let (rq_ty, rq_expr) = { + ( + quote!(rtic::export::ASYNCRQ<#input_types, #n>), + quote!(rtic::export::Queue::new()), + ) + }; + + items.push(quote!( + #[doc(hidden)] + #[allow(non_camel_case_types)] + #[allow(non_upper_case_globals)] + static #rq: rtic::RacyCell<#rq_ty> = rtic::RacyCell::new(#rq_expr); + )); + + stmts.push(quote!( + if !(&*#exec_name.get()).is_running() { + if let Some(#tupled) = rtic::export::interrupt::free(|_| (&mut *#rq.get_mut()).dequeue()) { + + // The async executor needs a static priority + #prio_name.get_mut().write(rtic::export::Priority::new(PRIORITY)); + let priority: &'static _ = &*#prio_name.get(); + + (&mut *#exec_name.get_mut()).spawn(#name(#name::Context::new(priority) #(,#pats)*)); + #executor_run_ident.store(true, core::sync::atomic::Ordering::Relaxed); + } + } + + if #executor_run_ident.load(core::sync::atomic::Ordering::Relaxed) { + #executor_run_ident.store(false, core::sync::atomic::Ordering::Relaxed); + if (&mut *#exec_name.get_mut()).poll(|| { + #executor_run_ident.store(true, core::sync::atomic::Ordering::Release); + rtic::pend(#device::#enum_::#interrupt); + }) && !rtic::export::interrupt::free(|_| (&*#rq.get_mut()).is_empty()) { + // If the ready queue is not empty and the executor finished, restart this + // dispatch to check if the executor should be restarted. + rtic::pend(#device::#enum_::#interrupt); + } + } + )); + } + + let doc = format!( + "Interrupt handler to dispatch async tasks at priority {}", + level + ); + let attribute = &interrupts[&level].1.attrs; + items.push(quote!( + #[allow(non_snake_case)] + #[doc = #doc] + #[no_mangle] + #(#attribute)* + unsafe fn #interrupt() { + /// The priority of this interrupt handler + const PRIORITY: u8 = #level; + + rtic::export::run(PRIORITY, || { + #(#stmts)* + }); + } + )); + } + + items +} diff --git a/macros/src/codegen/dispatchers.rs b/macros/src/codegen/dispatchers.rs index a90a97c..1a8b404 100644 --- a/macros/src/codegen/dispatchers.rs +++ b/macros/src/codegen/dispatchers.rs @@ -1,21 +1,31 @@ +use crate::syntax::ast::App; +use crate::{analyze::Analysis, codegen::util}; use proc_macro2::TokenStream as TokenStream2; use quote::quote; -use rtic_syntax::ast::App; - -use crate::{analyze::Analysis, check::Extra, codegen::util}; /// Generates task dispatchers -pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec { +pub fn codegen(app: &App, analysis: &Analysis) -> Vec { let mut items = vec![]; - let interrupts = &analysis.interrupts; + let interrupts = &analysis.interrupts_normal; for (&level, channel) in &analysis.channels { + if channel + .tasks + .iter() + .map(|task_name| app.software_tasks[task_name].is_async) + .all(|is_async| is_async) + { + // check if all tasks are async, if so don't generate this. + continue; + } + let mut stmts = vec![]; let variants = channel .tasks .iter() + .filter(|name| !app.software_tasks[*name].is_async) .map(|name| { let cfgs = &app.software_tasks[name].cfgs; @@ -45,6 +55,7 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec), @@ -64,6 +75,13 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec = rtic::RacyCell::new(#rq_expr); )); + let interrupt = util::suffixed( + &interrupts + .get(&level) + .expect("RTIC-ICE: Unable to get interrrupt") + .0 + .to_string(), + ); let arms = channel .tasks .iter() @@ -74,23 +92,27 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec { - let #tupled = - (&*#inputs - .get()) - .get_unchecked(usize::from(index)) - .as_ptr() - .read(); - (&mut *#fq.get_mut()).split().0.enqueue_unchecked(index); - let priority = &rtic::export::Priority::new(PRIORITY); - #name( - #name::Context::new(priority) - #(,#pats)* - ) - } - ) + if !task.is_async { + quote!( + #(#cfgs)* + #t::#name => { + let #tupled = + (&*#inputs + .get()) + .get_unchecked(usize::from(index)) + .as_ptr() + .read(); + (&mut *#fq.get_mut()).split().0.enqueue_unchecked(index); + let priority = &rtic::export::Priority::new(PRIORITY); + #name( + #name::Context::new(priority) + #(,#pats)* + ) + } + ) + } else { + quote!() + } }) .collect::>(); @@ -103,7 +125,6 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec ( // mod_app_hardware_tasks -- interrupt handlers and `${task}Resources` constructors Vec, @@ -33,12 +30,10 @@ pub fn codegen( let priority = task.args.priority; let cfgs = &task.cfgs; let attrs = &task.attrs; - let user_hardware_task_isr_doc = &format!(" User HW task ISR trampoline for {name}"); mod_app.push(quote!( #[allow(non_snake_case)] #[no_mangle] - #[doc = #user_hardware_task_isr_doc] #(#attrs)* #(#cfgs)* unsafe fn #symbol() { @@ -87,19 +82,14 @@ pub fn codegen( local_needs_lt, app, analysis, - extra, )); - let user_hardware_task_doc = &format!(" User HW task: {name}"); if !task.is_extern { let attrs = &task.attrs; - let cfgs = &task.cfgs; let context = &task.context; let stmts = &task.stmts; user_tasks.push(quote!( - #[doc = #user_hardware_task_doc] #(#attrs)* - #(#cfgs)* #[allow(non_snake_case)] fn #name(#context: #name::Context) { use rtic::Mutex as _; diff --git a/macros/src/codegen/idle.rs b/macros/src/codegen/idle.rs index 77a7f9f..9867939 100644 --- a/macros/src/codegen/idle.rs +++ b/macros/src/codegen/idle.rs @@ -1,18 +1,15 @@ -use proc_macro2::TokenStream as TokenStream2; -use quote::quote; -use rtic_syntax::{ast::App, Context}; - +use crate::syntax::{ast::App, Context}; use crate::{ analyze::Analysis, - check::Extra, 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, - extra: &Extra, ) -> ( // mod_app_idle -- the `${idle}Resources` constructor Vec, @@ -57,16 +54,13 @@ pub fn codegen( local_needs_lt, app, analysis, - extra, )); - let idle_doc = " User provided idle function".to_string(); let attrs = &idle.attrs; let context = &idle.context; let stmts = &idle.stmts; let user_idle = Some(quote!( #(#attrs)* - #[doc = #idle_doc] #[allow(non_snake_case)] fn #name(#context: #name::Context) -> ! { use rtic::Mutex as _; @@ -82,6 +76,9 @@ pub fn codegen( (mod_app, root_idle, user_idle, call_idle) } else { + // TODO: No idle defined, check for 0-priority tasks and generate an executor if needed + + // ( vec![], vec![], diff --git a/macros/src/codegen/init.rs b/macros/src/codegen/init.rs index 34f86f2..9a6fe2d 100644 --- a/macros/src/codegen/init.rs +++ b/macros/src/codegen/init.rs @@ -1,11 +1,10 @@ use proc_macro2::TokenStream as TokenStream2; use quote::quote; -use rtic_syntax::{ast::App, Context}; use crate::{ analyze::Analysis, - check::Extra, codegen::{local_resources_struct, module}, + syntax::{ast::App, Context}, }; type CodegenResult = ( @@ -24,7 +23,7 @@ type CodegenResult = ( ); /// Generates support code for `#[init]` functions -pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> CodegenResult { +pub fn codegen(app: &App, analysis: &Analysis) -> CodegenResult { let init = &app.init; let mut local_needs_lt = false; let name = &init.name; @@ -65,27 +64,22 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> CodegenResult { ) }) .collect(); - - let shared_resources_doc = " RTIC shared resource struct".to_string(); - let local_resources_doc = " RTIC local resource struct".to_string(); root_init.push(quote! { - #[doc = #shared_resources_doc] struct #shared { #(#shared_resources)* } - #[doc = #local_resources_doc] struct #local { #(#local_resources)* } }); + // let locals_pat = locals_pat.iter(); + let user_init_return = quote! {#shared, #local, #name::Monotonics}; - let user_init_doc = " User provided init function".to_string(); let user_init = quote!( #(#attrs)* - #[doc = #user_init_doc] #[inline(always)] #[allow(non_snake_case)] fn #name(#context: #name::Context) -> (#user_init_return) { @@ -105,6 +99,7 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> CodegenResult { mod_app = Some(constructor); } + // let locals_new = locals_new.iter(); let call_init = quote! { let (shared_resources, local_resources, mut monotonics) = #name(#name::Context::new(core.into())); }; @@ -115,7 +110,6 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> CodegenResult { local_needs_lt, app, analysis, - extra, )); (mod_app, root_init, user_init, call_init) diff --git a/macros/src/codegen/local_resources.rs b/macros/src/codegen/local_resources.rs index 6e7c1da..6fc63cd 100644 --- a/macros/src/codegen/local_resources.rs +++ b/macros/src/codegen/local_resources.rs @@ -1,8 +1,7 @@ +use crate::syntax::ast::App; +use crate::{analyze::Analysis, codegen::util}; use proc_macro2::TokenStream as TokenStream2; use quote::quote; -use rtic_syntax::ast::App; - -use crate::{analyze::Analysis, check::Extra, codegen::util}; /// Generates `local` variables and local resource proxies /// @@ -10,7 +9,6 @@ use crate::{analyze::Analysis, check::Extra, codegen::util}; pub fn codegen( app: &App, _analysis: &Analysis, - _extra: &Extra, ) -> ( // mod_app -- the `static` variables behind the proxies Vec, diff --git a/macros/src/codegen/local_resources_struct.rs b/macros/src/codegen/local_resources_struct.rs index 74bdbf8..309fd8d 100644 --- a/macros/src/codegen/local_resources_struct.rs +++ b/macros/src/codegen/local_resources_struct.rs @@ -1,9 +1,9 @@ -use proc_macro2::TokenStream as TokenStream2; -use quote::quote; -use rtic_syntax::{ +use crate::syntax::{ ast::{App, TaskLocal}, Context, }; +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; use crate::codegen::util; @@ -13,7 +13,13 @@ pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2, let resources = match ctxt { Context::Init => &app.init.args.local_resources, - Context::Idle => &app.idle.as_ref().unwrap().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, }; @@ -49,9 +55,7 @@ pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2, util::declared_static_local_resource_ident(name, &task_name) }; - let local_resource_doc = format!(" Local resource `{name}`"); fields.push(quote!( - #[doc = #local_resource_doc] #(#cfgs)* pub #name: &#lt mut #ty )); @@ -84,7 +88,7 @@ pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2, } } - let doc = format!(" Local resources `{}` has access to", ctxt.ident(app)); + 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)] @@ -98,7 +102,6 @@ pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2, let constructor = quote!( impl<#lt> #ident<#lt> { #[inline(always)] - #[doc(hidden)] pub unsafe fn new() -> Self { #ident { #(#values,)* diff --git a/macros/src/codegen/module.rs b/macros/src/codegen/module.rs index 8dcdbcf..7ac06c5 100644 --- a/macros/src/codegen/module.rs +++ b/macros/src/codegen/module.rs @@ -1,7 +1,7 @@ -use crate::{analyze::Analysis, check::Extra, codegen::util}; +use crate::syntax::{ast::App, Context}; +use crate::{analyze::Analysis, codegen::util}; use proc_macro2::TokenStream as TokenStream2; use quote::quote; -use rtic_syntax::{ast::App, Context}; #[allow(clippy::too_many_lines)] pub fn codegen( @@ -10,12 +10,13 @@ pub fn codegen( local_resources_tick: bool, app: &App, analysis: &Analysis, - extra: &Extra, ) -> 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); @@ -27,8 +28,8 @@ pub fn codegen( pub core: rtic::export::Peripherals )); - if extra.peripherals { - let device = &extra.device; + if app.args.peripherals { + let device = &app.args.device; fields.push(quote!( /// Device peripherals @@ -52,14 +53,6 @@ pub fn codegen( Context::Idle | Context::HardwareTask(_) | Context::SoftwareTask(_) => {} } - // if ctxt.has_locals(app) { - // let ident = util::locals_ident(ctxt, app); - // module_items.push(quote!( - // #[doc(inline)] - // pub use super::#ident as Locals; - // )); - // } - if ctxt.has_local_resources(app) { let ident = util::local_resources_ident(ctxt, app); let lt = if local_resources_tick { @@ -114,12 +107,8 @@ pub fn codegen( .monotonics .iter() .map(|(_, monotonic)| { - let cfgs = &monotonic.cfgs; let mono = &monotonic.ty; - quote! { - #(#cfgs)* - pub #mono - } + quote! {#mono} }) .collect(); @@ -130,7 +119,7 @@ pub fn codegen( #[allow(non_snake_case)] #[allow(non_camel_case_types)] pub struct #internal_monotonics_ident( - #(#monotonic_types),* + #(pub #monotonic_types),* ); )); @@ -141,10 +130,10 @@ pub fn codegen( } let doc = match ctxt { - Context::Idle => " Idle loop", - Context::Init => " Initialization function", - Context::HardwareTask(_) => " Hardware task", - Context::SoftwareTask(_) => " Software task", + Context::Idle => "Idle loop", + Context::Init => "Initialization function", + Context::HardwareTask(_) => "Hardware task", + Context::SoftwareTask(_) => "Software task", }; let v = Vec::new(); @@ -175,8 +164,8 @@ pub fn codegen( let internal_context_name = util::internal_task_ident(name, "Context"); items.push(quote!( - /// Execution context #(#cfgs)* + /// Execution context #[allow(non_snake_case)] #[allow(non_camel_case_types)] pub struct #internal_context_name<#lt> { @@ -185,7 +174,6 @@ pub fn codegen( #(#cfgs)* impl<#lt> #internal_context_name<#lt> { - #[doc(hidden)] #[inline(always)] pub unsafe fn new(#core #priority) -> Self { #internal_context_name { @@ -196,8 +184,8 @@ pub fn codegen( )); module_items.push(quote!( - #[doc(inline)] #(#cfgs)* + #[doc(inline)] pub use super::#internal_context_name as Context; )); @@ -206,6 +194,8 @@ pub fn codegen( let priority = spawnee.args.priority; let t = util::spawn_t_ident(priority); let cfgs = &spawnee.cfgs; + // Store a copy of the task cfgs + task_cfgs = cfgs.clone(); let (args, tupled, untupled, ty) = util::regroup_inputs(&spawnee.inputs); let args = &args; let tupled = &tupled; @@ -213,112 +203,141 @@ pub fn codegen( let rq = util::rq_ident(priority); let inputs = util::inputs_ident(name); - let device = &extra.device; + let device = &app.args.device; let enum_ = util::interrupt_ident(); - let interrupt = &analysis - .interrupts - .get(&priority) - .expect("RTIC-ICE: interrupt identifer not found") - .0; + let interrupt = if spawnee.is_async { + &analysis + .interrupts_async + .get(&priority) + .expect("RTIC-ICE: interrupt identifer not found") + .0 + } else { + &analysis + .interrupts_normal + .get(&priority) + .expect("RTIC-ICE: interrupt identifer not found") + .0 + }; let internal_spawn_ident = util::internal_task_ident(name, "spawn"); // Spawn caller - items.push(quote!( + if spawnee.is_async { + let rq = util::rq_async_ident(name); + items.push(quote!( - /// Spawns the task directly - #(#cfgs)* - pub fn #internal_spawn_ident(#(#args,)*) -> Result<(), #ty> { - let input = #tupled; + #(#cfgs)* + /// Spawns the task directly + #[allow(non_snake_case)] + #[doc(hidden)] + pub fn #internal_spawn_ident(#(#args,)*) -> Result<(), #ty> { + let input = #tupled; - unsafe { - if let Some(index) = rtic::export::interrupt::free(|_| (&mut *#fq.get_mut()).dequeue()) { - (&mut *#inputs - .get_mut()) - .get_unchecked_mut(usize::from(index)) - .as_mut_ptr() - .write(input); + unsafe { + let r = rtic::export::interrupt::free(|_| (&mut *#rq.get_mut()).enqueue(input)); - rtic::export::interrupt::free(|_| { - (&mut *#rq.get_mut()).enqueue_unchecked((#t::#name, index)); - }); + if r.is_ok() { + rtic::pend(#device::#enum_::#interrupt); + } - rtic::pend(#device::#enum_::#interrupt); + r + } + })); + } else { + items.push(quote!( - Ok(()) - } else { - Err(input) + #(#cfgs)* + /// Spawns the task directly + #[allow(non_snake_case)] + #[doc(hidden)] + pub fn #internal_spawn_ident(#(#args,)*) -> Result<(), #ty> { + let input = #tupled; + + unsafe { + if let Some(index) = rtic::export::interrupt::free(|_| (&mut *#fq.get_mut()).dequeue()) { + (&mut *#inputs + .get_mut()) + .get_unchecked_mut(usize::from(index)) + .as_mut_ptr() + .write(input); + + rtic::export::interrupt::free(|_| { + (&mut *#rq.get_mut()).enqueue_unchecked((#t::#name, index)); + }); + rtic::pend(#device::#enum_::#interrupt); + + Ok(()) + } else { + Err(input) + } } - } - })); + })); + } module_items.push(quote!( - #[doc(inline)] #(#cfgs)* + #[doc(inline)] pub use super::#internal_spawn_ident as spawn; )); // Schedule caller - for (_, monotonic) in &app.monotonics { - let instants = util::monotonic_instants_ident(name, &monotonic.ident); - let monotonic_name = monotonic.ident.to_string(); - - let tq = util::tq_ident(&monotonic.ident.to_string()); - let t = util::schedule_t_ident(); - let m = &monotonic.ident; - let cfgs = &monotonic.cfgs; - let m_ident = util::monotonic_ident(&monotonic_name); - let m_isr = &monotonic.args.binds; - let enum_ = util::interrupt_ident(); - let spawn_handle_string = format!("{}::SpawnHandle", m); - - let (enable_interrupt, pend) = if &*m_isr.to_string() == "SysTick" { - ( - quote!(core::mem::transmute::<_, rtic::export::SYST>(()).enable_interrupt()), - quote!(rtic::export::SCB::set_pendst()), - ) - } else { - let rt_err = util::rt_err_ident(); - ( - quote!(rtic::export::NVIC::unmask(#rt_err::#enum_::#m_isr)), - quote!(rtic::pend(#rt_err::#enum_::#m_isr)), - ) - }; - - let tq_marker = &util::timer_queue_marker_ident(); - - // For future use - // let doc = format!(" RTIC internal: {}:{}", file!(), line!()); - // items.push(quote!(#[doc = #doc])); - let internal_spawn_handle_ident = - util::internal_monotonics_ident(name, m, "SpawnHandle"); - let internal_spawn_at_ident = util::internal_monotonics_ident(name, m, "spawn_at"); - let internal_spawn_after_ident = - util::internal_monotonics_ident(name, m, "spawn_after"); - - if monotonic.args.default { + if !spawnee.is_async { + for (_, monotonic) in &app.monotonics { + let instants = util::monotonic_instants_ident(name, &monotonic.ident); + let monotonic_name = monotonic.ident.to_string(); + + let tq = util::tq_ident(&monotonic.ident.to_string()); + let t = util::schedule_t_ident(); + let m = &monotonic.ident; + let m_ident = util::monotonic_ident(&monotonic_name); + let m_isr = &monotonic.args.binds; + let enum_ = util::interrupt_ident(); + let spawn_handle_string = format!("{}::SpawnHandle", m); + + let (enable_interrupt, pend) = if &*m_isr.to_string() == "SysTick" { + ( + quote!(core::mem::transmute::<_, rtic::export::SYST>(()).enable_interrupt()), + quote!(rtic::export::SCB::set_pendst()), + ) + } else { + let rt_err = util::rt_err_ident(); + ( + quote!(rtic::export::NVIC::unmask(#rt_err::#enum_::#m_isr)), + quote!(rtic::pend(#rt_err::#enum_::#m_isr)), + ) + }; + + let tq_marker = &util::timer_queue_marker_ident(); + + let internal_spawn_handle_ident = + util::internal_monotonics_ident(name, m, "SpawnHandle"); + let internal_spawn_at_ident = util::internal_monotonics_ident(name, m, "spawn_at"); + let internal_spawn_after_ident = + util::internal_monotonics_ident(name, m, "spawn_after"); + + if monotonic.args.default { + module_items.push(quote!( + #[doc(inline)] + pub use #m::spawn_after; + #[doc(inline)] + pub use #m::spawn_at; + #[doc(inline)] + pub use #m::SpawnHandle; + )); + } module_items.push(quote!( - #(#cfgs)* - pub use #m::spawn_after; - #(#cfgs)* - pub use #m::spawn_at; - #(#cfgs)* - pub use #m::SpawnHandle; + pub mod #m { + #[doc(inline)] + pub use super::super::#internal_spawn_after_ident as spawn_after; + #[doc(inline)] + pub use super::super::#internal_spawn_at_ident as spawn_at; + #[doc(inline)] + pub use super::super::#internal_spawn_handle_ident as SpawnHandle; + } )); - } - module_items.push(quote!( - #[doc(hidden)] - #(#cfgs)* - pub mod #m { - pub use super::super::#internal_spawn_after_ident as spawn_after; - pub use super::super::#internal_spawn_at_ident as spawn_at; - pub use super::super::#internal_spawn_handle_ident as SpawnHandle; - } - )); - items.push(quote!( - #[doc(hidden)] + items.push(quote!( #(#cfgs)* #[allow(non_snake_case)] #[allow(non_camel_case_types)] @@ -329,7 +348,6 @@ pub fn codegen( #(#cfgs)* impl core::fmt::Debug for #internal_spawn_handle_ident { - #[doc(hidden)] fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_struct(#spawn_handle_string).finish() } @@ -340,7 +358,7 @@ pub fn codegen( pub fn cancel(self) -> Result<#ty, ()> { rtic::export::interrupt::free(|_| unsafe { let tq = &mut *#tq.get_mut(); - if let Some((_task, index)) = tq.cancel_marker(self.marker) { + if let Some((_task, index)) = tq.cancel_task_marker(self.marker) { // Get the message let msg = (&*#inputs .get()) @@ -357,9 +375,7 @@ pub fn codegen( }) } - /// Reschedule after #[inline] - #(#cfgs)* pub fn reschedule_after( self, duration: <#m as rtic::Monotonic>::Duration @@ -367,8 +383,6 @@ pub fn codegen( self.reschedule_at(monotonics::#m::now() + duration) } - /// Reschedule at - #(#cfgs)* pub fn reschedule_at( self, instant: <#m as rtic::Monotonic>::Instant @@ -379,16 +393,17 @@ pub fn codegen( let tq = (&mut *#tq.get_mut()); - tq.update_marker(self.marker, marker, instant, || #pend).map(|_| #name::#m::SpawnHandle { marker }) + tq.update_task_marker(self.marker, marker, instant, || #pend).map(|_| #name::#m::SpawnHandle { marker }) }) } } + + #(#cfgs)* /// Spawns the task after a set duration relative to the current time /// /// This will use the time `Instant::new(0)` as baseline if called in `#[init]`, /// so if you use a non-resetable timer use `spawn_at` when in `#[init]` - #(#cfgs)* #[allow(non_snake_case)] pub fn #internal_spawn_after_ident( duration: <#m as rtic::Monotonic>::Duration @@ -424,10 +439,10 @@ pub fn codegen( rtic::export::interrupt::free(|_| { let marker = #tq_marker.get().read(); - let nr = rtic::export::NotReady { - instant, - index, + let nr = rtic::export::TaskNotReady { task: #t::#name, + index, + instant, marker, }; @@ -435,7 +450,7 @@ pub fn codegen( let tq = &mut *#tq.get_mut(); - tq.enqueue_unchecked( + tq.enqueue_task_unchecked( nr, || #enable_interrupt, || #pend, @@ -449,6 +464,7 @@ pub fn codegen( } } )); + } } } @@ -457,8 +473,9 @@ pub fn codegen( } else { quote!( #(#items)* + #[allow(non_snake_case)] - #(#cfgs)* + #(#task_cfgs)* #[doc = #doc] pub mod #name { #(#module_items)* diff --git a/macros/src/codegen/monotonic.rs b/macros/src/codegen/monotonic.rs new file mode 100644 index 0000000..417a1d6 --- /dev/null +++ b/macros/src/codegen/monotonic.rs @@ -0,0 +1,280 @@ +use crate::syntax::ast::App; +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; + +use crate::{analyze::Analysis, codegen::util}; + +/// Generates monotonic module dispatchers +pub fn codegen(app: &App, _analysis: &Analysis) -> TokenStream2 { + let mut monotonic_parts: Vec<_> = Vec::new(); + + let tq_marker = util::timer_queue_marker_ident(); + + for (_, monotonic) in &app.monotonics { + // let instants = util::monotonic_instants_ident(name, &monotonic.ident); + let monotonic_name = monotonic.ident.to_string(); + + let tq = util::tq_ident(&monotonic_name); + let m = &monotonic.ident; + let m_ident = util::monotonic_ident(&monotonic_name); + let m_isr = &monotonic.args.binds; + let enum_ = util::interrupt_ident(); + let name_str = &m.to_string(); + let ident = util::monotonic_ident(name_str); + let doc = &format!( + "This module holds the static implementation for `{}::now()`", + name_str + ); + + let (enable_interrupt, pend) = if &*m_isr.to_string() == "SysTick" { + ( + quote!(core::mem::transmute::<_, rtic::export::SYST>(()).enable_interrupt()), + quote!(rtic::export::SCB::set_pendst()), + ) + } else { + let rt_err = util::rt_err_ident(); + ( + quote!(rtic::export::NVIC::unmask(super::super::#rt_err::#enum_::#m_isr)), + quote!(rtic::pend(super::super::#rt_err::#enum_::#m_isr)), + ) + }; + + let default_monotonic = if monotonic.args.default { + quote!( + #[doc(inline)] + pub use #m::now; + #[doc(inline)] + pub use #m::delay; + #[doc(inline)] + pub use #m::delay_until; + #[doc(inline)] + pub use #m::timeout_at; + #[doc(inline)] + pub use #m::timeout_after; + ) + } else { + quote!() + }; + + monotonic_parts.push(quote! { + #default_monotonic + + #[doc = #doc] + #[allow(non_snake_case)] + pub mod #m { + /// Read the current time from this monotonic + pub fn now() -> ::Instant { + rtic::export::interrupt::free(|_| { + use rtic::Monotonic as _; + if let Some(m) = unsafe{ &mut *super::super::#ident.get_mut() } { + m.now() + } else { + ::zero() + } + }) + } + + /// Delay + #[inline(always)] + #[allow(non_snake_case)] + pub fn delay(duration: ::Duration) + -> DelayFuture { + let until = now() + duration; + DelayFuture { until, waker_storage: None } + } + + /// Delay until a specific time + #[inline(always)] + #[allow(non_snake_case)] + pub fn delay_until(instant: ::Instant) + -> DelayFuture { + let until = instant; + DelayFuture { until, waker_storage: None } + } + + /// Delay future. + #[allow(non_snake_case)] + #[allow(non_camel_case_types)] + pub struct DelayFuture { + until: ::Instant, + waker_storage: Option>>, + } + + impl Drop for DelayFuture { + fn drop(&mut self) { + if let Some(waker_storage) = &mut self.waker_storage { + rtic::export::interrupt::free(|_| unsafe { + let tq = &mut *super::super::#tq.get_mut(); + tq.cancel_waker_marker(waker_storage.val.marker); + }); + } + } + } + + impl core::future::Future for DelayFuture { + type Output = (); + + fn poll( + mut self: core::pin::Pin<&mut Self>, + cx: &mut core::task::Context<'_> + ) -> core::task::Poll { + let mut s = self.as_mut(); + let now = now(); + let until = s.until; + let is_ws_none = s.waker_storage.is_none(); + + if now >= until { + return core::task::Poll::Ready(()); + } else if is_ws_none { + rtic::export::interrupt::free(|_| unsafe { + let marker = super::super::#tq_marker.get().read(); + super::super::#tq_marker.get_mut().write(marker.wrapping_add(1)); + + let nr = s.waker_storage.insert(rtic::export::IntrusiveNode::new(rtic::export::WakerNotReady { + waker: cx.waker().clone(), + instant: until, + marker, + })); + + let tq = &mut *super::super::#tq.get_mut(); + + tq.enqueue_waker( + core::mem::transmute(nr), // Transmute the reference to static + || #enable_interrupt, + || #pend, + (&mut *super::super::#m_ident.get_mut()).as_mut()); + }); + } + + core::task::Poll::Pending + } + } + + /// Timeout future. + #[allow(non_snake_case)] + #[allow(non_camel_case_types)] + pub struct TimeoutFuture { + future: F, + until: ::Instant, + waker_storage: Option>>, + } + + impl Drop for TimeoutFuture { + fn drop(&mut self) { + if let Some(waker_storage) = &mut self.waker_storage { + rtic::export::interrupt::free(|_| unsafe { + let tq = &mut *super::super::#tq.get_mut(); + tq.cancel_waker_marker(waker_storage.val.marker); + }); + } + } + } + + /// Timeout after + #[allow(non_snake_case)] + #[inline(always)] + pub fn timeout_after( + future: F, + duration: ::Duration + ) -> TimeoutFuture { + let until = now() + duration; + TimeoutFuture { + future, + until, + waker_storage: None, + } + } + + /// Timeout at + #[allow(non_snake_case)] + #[inline(always)] + pub fn timeout_at( + future: F, + instant: ::Instant + ) -> TimeoutFuture { + TimeoutFuture { + future, + until: instant, + waker_storage: None, + } + } + + impl core::future::Future for TimeoutFuture + where + F: core::future::Future, + { + type Output = Result; + + fn poll( + self: core::pin::Pin<&mut Self>, + cx: &mut core::task::Context<'_> + ) -> core::task::Poll { + // SAFETY: We don't move the underlying pinned value. + let mut s = unsafe { self.get_unchecked_mut() }; + let future = unsafe { core::pin::Pin::new_unchecked(&mut s.future) }; + let now = now(); + let until = s.until; + let is_ws_none = s.waker_storage.is_none(); + + match future.poll(cx) { + core::task::Poll::Ready(r) => { + if let Some(waker_storage) = &mut s.waker_storage { + rtic::export::interrupt::free(|_| unsafe { + let tq = &mut *super::super::#tq.get_mut(); + tq.cancel_waker_marker(waker_storage.val.marker); + }); + } + + return core::task::Poll::Ready(Ok(r)); + } + core::task::Poll::Pending => { + if now >= until { + // Timeout + return core::task::Poll::Ready(Err(super::TimeoutError)); + } else if is_ws_none { + rtic::export::interrupt::free(|_| unsafe { + let marker = super::super::#tq_marker.get().read(); + super::super::#tq_marker.get_mut().write(marker.wrapping_add(1)); + + let nr = s.waker_storage.insert(rtic::export::IntrusiveNode::new(rtic::export::WakerNotReady { + waker: cx.waker().clone(), + instant: until, + marker, + })); + + let tq = &mut *super::super::#tq.get_mut(); + + tq.enqueue_waker( + core::mem::transmute(nr), // Transmute the reference to static + || #enable_interrupt, + || #pend, + (&mut *super::super::#m_ident.get_mut()).as_mut()); + }); + } + } + } + + core::task::Poll::Pending + } + } + } + }); + } + + if monotonic_parts.is_empty() { + quote!() + } else { + quote!( + pub use rtic::Monotonic as _; + + /// Holds static methods for each monotonic. + pub mod monotonics { + /// A timeout error. + #[derive(Debug)] + pub struct TimeoutError; + + #(#monotonic_parts)* + } + ) + } +} diff --git a/macros/src/codegen/post_init.rs b/macros/src/codegen/post_init.rs index 460b4e2..df5daa1 100644 --- a/macros/src/codegen/post_init.rs +++ b/macros/src/codegen/post_init.rs @@ -1,6 +1,6 @@ +use crate::syntax::ast::App; use proc_macro2::{Span, TokenStream as TokenStream2}; use quote::quote; -use rtic_syntax::ast::App; use syn::Index; use crate::{analyze::Analysis, codegen::util}; @@ -43,28 +43,21 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec { } } - for (i, (monotonic_ident, monotonic)) in app.monotonics.iter().enumerate() { + for (i, (monotonic, _)) in app.monotonics.iter().enumerate() { // For future use // let doc = format!(" RTIC internal: {}:{}", file!(), line!()); // stmts.push(quote!(#[doc = #doc])); - let cfgs = &monotonic.cfgs; #[allow(clippy::cast_possible_truncation)] let idx = Index { index: i as u32, span: Span::call_site(), }; - stmts.push(quote!( - #(#cfgs)* - monotonics.#idx.reset(); - )); + stmts.push(quote!(monotonics.#idx.reset();)); // Store the monotonic - let name = util::monotonic_ident(&monotonic_ident.to_string()); - stmts.push(quote!( - #(#cfgs)* - #name.get_mut().write(Some(monotonics.#idx)); - )); + let name = util::monotonic_ident(&monotonic.to_string()); + stmts.push(quote!(#name.get_mut().write(Some(monotonics.#idx));)); } // Enable the interrupts -- this completes the `init`-ialization phase diff --git a/macros/src/codegen/pre_init.rs b/macros/src/codegen/pre_init.rs index 2362cb7..ef3acba 100644 --- a/macros/src/codegen/pre_init.rs +++ b/macros/src/codegen/pre_init.rs @@ -1,11 +1,11 @@ +use crate::syntax::ast::App; use proc_macro2::TokenStream as TokenStream2; use quote::quote; -use rtic_syntax::ast::App; -use crate::{analyze::Analysis, check::Extra, codegen::util}; +use crate::{analyze::Analysis, codegen::util}; /// Generates code that runs before `#[init]` -pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec { +pub fn codegen(app: &App, analysis: &Analysis) -> Vec { let mut stmts = vec![]; let rt_err = util::rt_err_ident(); @@ -15,12 +15,14 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec Vec ( // mod_app -- the `static` variables behind the proxies Vec, @@ -90,7 +89,7 @@ pub fn codegen( // let doc = format!(" RTIC internal ({} resource): {}:{}", doc, file!(), line!()); mod_app.push(util::impl_mutex( - extra, + app, cfgs, true, &shared_name, @@ -112,10 +111,14 @@ pub fn codegen( }; // Computing mapping of used interrupts to masks - let interrupt_ids = analysis.interrupts.iter().map(|(p, (id, _))| (p, id)); + let interrupt_ids = analysis + .interrupts_normal + .iter() + .map(|(p, (id, _))| (p, id)) + .chain(analysis.interrupts_async.iter().map(|(p, (id, _))| (p, id))); let mut prio_to_masks = HashMap::new(); - let device = &extra.device; + let device = &app.args.device; let mut uses_exceptions_with_resources = false; let mut mask_ids = Vec::new(); @@ -147,8 +150,7 @@ pub fn codegen( None } })) { - #[allow(clippy::or_fun_call)] - let v = prio_to_masks.entry(priority - 1).or_insert(Vec::new()); + 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)); } diff --git a/macros/src/codegen/shared_resources_struct.rs b/macros/src/codegen/shared_resources_struct.rs index df36271..1d46aa4 100644 --- a/macros/src/codegen/shared_resources_struct.rs +++ b/macros/src/codegen/shared_resources_struct.rs @@ -1,6 +1,6 @@ +use crate::syntax::{ast::App, Context}; use proc_macro2::TokenStream as TokenStream2; use quote::quote; -use rtic_syntax::{ast::App, Context}; use crate::codegen::util; @@ -10,24 +10,17 @@ pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2, let resources = match ctxt { Context::Init => unreachable!("Tried to generate shared resources struct for init"), - Context::Idle => &app.idle.as_ref().unwrap().args.shared_resources, + 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 v = Vec::new(); - let task_cfgs = match ctxt { - Context::HardwareTask(t) => { - &app.hardware_tasks[t].cfgs - // ... - } - Context::SoftwareTask(t) => { - &app.software_tasks[t].cfgs - // ... - } - _ => &v, - }; - let mut fields = vec![]; let mut values = vec![]; let mut has_cfgs = false; @@ -57,18 +50,14 @@ pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2, quote!('a) }; - let lock_free_resource_doc = format!(" Lock free resource `{name}`"); fields.push(quote!( - #[doc = #lock_free_resource_doc] #(#cfgs)* pub #name: &#lt #mut_ #ty )); } else if access.is_shared() { lt = Some(quote!('a)); - let shared_resource_doc = format!(" Shared resource `{name}`"); fields.push(quote!( - #[doc = #shared_resource_doc] #(#cfgs)* pub #name: &'a #ty )); @@ -76,16 +65,12 @@ pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2, // Resource proxy lt = Some(quote!('a)); - let resource_doc = - format!(" Resource proxy resource `{name}`. Use method `.lock()` to gain access"); fields.push(quote!( - #[doc = #resource_doc] #(#cfgs)* pub #name: shared_resources::#shared_name<'a> )); values.push(quote!( - #[doc(hidden)] #(#cfgs)* #name: shared_resources::#shared_name::new(priority) @@ -95,17 +80,13 @@ pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2, continue; } - let resource_doc; let expr = if access.is_exclusive() { - resource_doc = format!(" Exclusive access resource `{name}`"); quote!(&mut *(&mut *#mangled_name.get_mut()).as_mut_ptr()) } else { - resource_doc = format!(" Non-exclusive access resource `{name}`"); quote!(&*(&*#mangled_name.get()).as_ptr()) }; values.push(quote!( - #[doc = #resource_doc] #(#cfgs)* #name: #expr )); @@ -125,13 +106,12 @@ pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2, } } - let doc = format!(" Shared resources `{}` has access to", ctxt.ident(app)); + 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] - #(#task_cfgs)* pub struct #ident<#lt> { #(#fields,)* } @@ -143,9 +123,7 @@ pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2, Some(quote!(priority: &#lt rtic::export::Priority)) }; let constructor = quote!( - #(#task_cfgs)* impl<#lt> #ident<#lt> { - #[doc(hidden)] #[inline(always)] pub unsafe fn new(#arg) -> Self { #ident { diff --git a/macros/src/codegen/software_tasks.rs b/macros/src/codegen/software_tasks.rs index 226121d..f9247da 100644 --- a/macros/src/codegen/software_tasks.rs +++ b/macros/src/codegen/software_tasks.rs @@ -1,17 +1,14 @@ -use proc_macro2::TokenStream as TokenStream2; -use quote::quote; -use rtic_syntax::{ast::App, Context}; - +use crate::syntax::{ast::App, Context}; use crate::{ analyze::Analysis, - check::Extra, codegen::{local_resources_struct, module, shared_resources_struct, util}, }; +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; pub fn codegen( app: &App, analysis: &Analysis, - extra: &Extra, ) -> ( // mod_app_software_tasks -- free queues, buffers and `${task}Resources` constructors Vec, @@ -27,74 +24,87 @@ pub fn codegen( let mut root = vec![]; let mut user_tasks = vec![]; - for (name, task) in &app.software_tasks { + // Any task + for (name, task) in app.software_tasks.iter() { let inputs = &task.inputs; - let cfgs = &task.cfgs; let (_, _, _, input_ty) = util::regroup_inputs(inputs); let cap = task.args.capacity; let cap_lit = util::capacity_literal(cap as usize); let cap_lit_p1 = util::capacity_literal(cap as usize + 1); - // Create free queues and inputs / instants buffers - let fq = util::fq_ident(name); - - #[allow(clippy::redundant_closure)] - let (fq_ty, fq_expr, mk_uninit): (_, _, Box Option<_>>) = { - ( - quote!(rtic::export::SCFQ<#cap_lit_p1>), - quote!(rtic::export::Queue::new()), - Box::new(|| Some(util::link_section_uninit())), - ) - }; - mod_app.push(quote!( - // /// Queue version of a free-list that keeps track of empty slots in - // /// the following buffers - #(#cfgs)* - #[allow(non_camel_case_types)] - #[allow(non_upper_case_globals)] - #[doc(hidden)] - static #fq: rtic::RacyCell<#fq_ty> = rtic::RacyCell::new(#fq_expr); - )); - - let elems = &(0..cap) - .map(|_| quote!(core::mem::MaybeUninit::uninit())) - .collect::>(); + if !task.is_async { + // Create free queues and inputs / instants buffers + let fq = util::fq_ident(name); - for (_, monotonic) in &app.monotonics { - let instants = util::monotonic_instants_ident(name, &monotonic.ident); - let mono_type = &monotonic.ty; - let cfgs = &monotonic.cfgs; + #[allow(clippy::redundant_closure)] + let (fq_ty, fq_expr, mk_uninit): (_, _, Box Option<_>>) = { + ( + quote!(rtic::export::SCFQ<#cap_lit_p1>), + quote!(rtic::export::Queue::new()), + Box::new(|| Some(util::link_section_uninit())), + ) + }; - let uninit = mk_uninit(); - // For future use - // let doc = format!(" RTIC internal: {}:{}", file!(), line!()); mod_app.push(quote!( + // /// Queue version of a free-list that keeps track of empty slots in + // /// the following buffers + #[allow(non_camel_case_types)] + #[allow(non_upper_case_globals)] + #[doc(hidden)] + static #fq: rtic::RacyCell<#fq_ty> = rtic::RacyCell::new(#fq_expr); + )); + + let elems = &(0..cap) + .map(|_| quote!(core::mem::MaybeUninit::uninit())) + .collect::>(); + + for (_, monotonic) in &app.monotonics { + let instants = util::monotonic_instants_ident(name, &monotonic.ident); + let mono_type = &monotonic.ty; + + let uninit = mk_uninit(); + // For future use + // let doc = format!(" RTIC internal: {}:{}", file!(), line!()); + mod_app.push(quote!( #uninit // /// Buffer that holds the instants associated to the inputs of a task // #[doc = #doc] #[allow(non_camel_case_types)] #[allow(non_upper_case_globals)] #[doc(hidden)] - #(#cfgs)* static #instants: rtic::RacyCell<[core::mem::MaybeUninit<<#mono_type as rtic::Monotonic>::Instant>; #cap_lit]> = rtic::RacyCell::new([#(#elems,)*]); )); + } + + let uninit = mk_uninit(); + let inputs_ident = util::inputs_ident(name); + + // Buffer that holds the inputs of a task + mod_app.push(quote!( + #uninit + #[allow(non_camel_case_types)] + #[allow(non_upper_case_globals)] + #[doc(hidden)] + static #inputs_ident: rtic::RacyCell<[core::mem::MaybeUninit<#input_ty>; #cap_lit]> = + rtic::RacyCell::new([#(#elems,)*]); + )); } - let uninit = mk_uninit(); - let inputs_ident = util::inputs_ident(name); - mod_app.push(quote!( - #uninit - // /// Buffer that holds the inputs of a task - #[allow(non_camel_case_types)] - #[allow(non_upper_case_globals)] - #[doc(hidden)] - #(#cfgs)* - static #inputs_ident: rtic::RacyCell<[core::mem::MaybeUninit<#input_ty>; #cap_lit]> = - rtic::RacyCell::new([#(#elems,)*]); - )); + if task.is_async { + let executor_ident = util::executor_run_ident(name); + mod_app.push(quote!( + #[allow(non_camel_case_types)] + #[allow(non_upper_case_globals)] + #[doc(hidden)] + static #executor_ident: core::sync::atomic::AtomicBool = + core::sync::atomic::AtomicBool::new(false); + )); + } + + let inputs = &task.inputs; // `${task}Resources` let mut shared_needs_lt = false; @@ -130,13 +140,24 @@ pub fn codegen( let attrs = &task.attrs; let cfgs = &task.cfgs; let stmts = &task.stmts; - let user_task_doc = format!(" User SW task {name}"); + let (async_marker, context_lifetime) = if task.is_async { + ( + quote!(async), + if shared_needs_lt || local_needs_lt { + quote!(<'static>) + } else { + quote!() + }, + ) + } else { + (quote!(), quote!()) + }; + user_tasks.push(quote!( - #[doc = #user_task_doc] #(#attrs)* #(#cfgs)* #[allow(non_snake_case)] - fn #name(#context: #name::Context #(,#inputs)*) { + #async_marker fn #name(#context: #name::Context #context_lifetime #(,#inputs)*) { use rtic::Mutex as _; use rtic::mutex::prelude::*; @@ -151,7 +172,6 @@ pub fn codegen( local_needs_lt, app, analysis, - extra, )); } diff --git a/macros/src/codegen/timer_queue.rs b/macros/src/codegen/timer_queue.rs index f5867dc..281148d 100644 --- a/macros/src/codegen/timer_queue.rs +++ b/macros/src/codegen/timer_queue.rs @@ -1,18 +1,18 @@ +use crate::syntax::ast::App; +use crate::{analyze::Analysis, codegen::util}; use proc_macro2::TokenStream as TokenStream2; use quote::quote; -use rtic_syntax::ast::App; - -use crate::{analyze::Analysis, check::Extra, codegen::util}; /// Generates timer queues and timer queue handlers #[allow(clippy::too_many_lines)] -pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec { +pub fn codegen(app: &App, analysis: &Analysis) -> Vec { let mut items = vec![]; if !app.monotonics.is_empty() { // Generate the marker counter used to track for `cancel` and `reschedule` let tq_marker = util::timer_queue_marker_ident(); items.push(quote!( + // #[doc = #doc] #[doc(hidden)] #[allow(non_camel_case_types)] #[allow(non_upper_case_globals)] @@ -26,6 +26,7 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec Vec Vec); + let n_task = util::capacity_literal(cap); + let tq_ty = quote!(rtic::export::TimerQueue<#mono_type, #t, #n_task>); // For future use // let doc = format!(" RTIC internal: {}:{}", file!(), line!()); @@ -76,9 +76,12 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec = - rtic::RacyCell::new(rtic::export::TimerQueue(rtic::export::SortedLinkedList::new_u16())); + static #tq: rtic::RacyCell<#tq_ty> = rtic::RacyCell::new( + rtic::export::TimerQueue { + task_queue: rtic::export::SortedLinkedList::new_u16(), + waker_queue: rtic::export::IntrusiveSortedLinkedList::new(), + } + ); )); let mono = util::monotonic_ident(&monotonic_name); @@ -89,7 +92,6 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec> = rtic::RacyCell::new(None); )); } @@ -102,6 +104,7 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec Vec Vec { - rtic::export::interrupt::free(|_| (&mut *#rq.get_mut()).split().0.enqueue_unchecked((#rqt::#name, index))); + rtic::export::interrupt::free(|_| + (&mut *#rq.get_mut()).split().0.enqueue_unchecked((#rqt::#name, index)) + ); #pend } @@ -128,7 +133,6 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec>(); - let cfgs = &monotonic.cfgs; let bound_interrupt = &monotonic.args.binds; let disable_isr = if &*bound_interrupt.to_string() == "SysTick" { quote!(core::mem::transmute::<_, rtic::export::SYST>(()).disable_interrupt()) @@ -139,7 +143,6 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec Ident { /// Generates a `Mutex` implementation pub fn impl_mutex( - extra: &Extra, + app: &App, cfgs: &[Attribute], resources_prefix: bool, name: &Ident, @@ -35,7 +33,7 @@ pub fn impl_mutex( (quote!(#name), quote!(self.priority)) }; - let device = &extra.device; + let device = &app.args.device; let masks_name = priority_masks_ident(); quote!( #(#cfgs)* @@ -67,6 +65,11 @@ pub fn inputs_ident(task: &Ident) -> Ident { mark_internal_name(&format!("{}_INPUTS", task)) } +/// Generates an identifier for the `EXECUTOR_RUN` atomics (`async` API) +pub fn executor_run_ident(task: &Ident) -> Ident { + mark_internal_name(&format!("{}_EXECUTOR_RUN", task)) +} + /// Generates an identifier for the `INSTANTS` buffer (`schedule` API) pub fn monotonic_instants_ident(task: &Ident, monotonic: &Ident) -> Ident { mark_internal_name(&format!("{}_{}_INSTANTS", task, monotonic)) @@ -179,7 +182,12 @@ pub fn regroup_inputs( 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().unwrap().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(), }; @@ -190,7 +198,12 @@ pub fn get_task_name(ctxt: Context, app: &App) -> Ident { 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().unwrap().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(), }; @@ -203,7 +216,12 @@ pub fn shared_resources_ident(ctxt: Context, app: &App) -> Ident { 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().unwrap().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(), }; @@ -220,9 +238,14 @@ pub fn rq_ident(priority: u8) -> Ident { mark_internal_name(&format!("P{}_RQ", priority)) } +/// Generates an identifier for a ready queue, async task version +pub fn rq_async_ident(async_task_name: &Ident) -> Ident { + mark_internal_name(&format!("ASYNC_TACK_{}_RQ", async_task_name)) +} + /// Generates an identifier for the `enum` of `schedule`-able tasks pub fn schedule_t_ident() -> Ident { - Ident::new("SCHED_T", Span::call_site()) + mark_internal_name("SCHED_T") } /// Generates an identifier for the `enum` of `spawn`-able tasks @@ -230,7 +253,7 @@ pub fn schedule_t_ident() -> Ident { /// This identifier needs the same structure as the `RQ` identifier because there's one ready queue /// for each of these `T` enums pub fn spawn_t_ident(priority: u8) -> Ident { - Ident::new(&format!("P{}_T", priority), Span::call_site()) + mark_internal_name(&format!("P{}_T", priority)) } /// Suffixed identifier diff --git a/macros/src/lib.rs b/macros/src/lib.rs index 2b52601..7729dcb 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -1,21 +1,46 @@ #![doc( - html_logo_url = "https://raw.githubusercontent.com/rtic-rs/cortex-m-rtic/master/book/en/src/RTIC.svg", - html_favicon_url = "https://raw.githubusercontent.com/rtic-rs/cortex-m-rtic/master/book/en/src/RTIC.svg" + 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 -extern crate proc_macro; +//deny_warnings_placeholder_for_ci use proc_macro::TokenStream; use std::{env, fs, path::Path}; -use rtic_syntax::Settings; - mod analyze; -mod check; +mod bindings; mod codegen; -#[cfg(test)] -mod tests; +mod syntax; + +// Used for mocking the API in testing +#[doc(hidden)] +#[proc_macro_attribute] +pub fn mock_app(args: TokenStream, input: TokenStream) -> TokenStream { + let mut settings = syntax::Settings::default(); + let mut rtic_args = vec![]; + for arg in args.to_string().split(',') { + if arg.trim() == "parse_binds" { + settings.parse_binds = true; + } else if arg.trim() == "parse_extern_interrupt" { + settings.parse_extern_interrupt = true; + } else { + rtic_args.push(arg.to_string()); + } + } + + // rtic_args.push("device = mock".into()); + + let args = rtic_args.join(", ").parse(); + + println!("args: {:?}", args); + + if let Err(e) = syntax::parse(args.unwrap(), input, settings) { + e.to_compile_error().into() + } else { + "fn main() {}".parse().unwrap() + } +} /// Attribute used to declare a RTIC application /// @@ -26,24 +51,19 @@ mod tests; /// 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 mut settings = Settings::default(); + let mut settings = syntax::Settings::default(); settings.optimize_priorities = false; settings.parse_binds = true; settings.parse_extern_interrupt = true; - let (app, analysis) = match rtic_syntax::parse(args, input, settings) { - Err(e) => return e.to_compile_error().into(), - Ok(x) => x, - }; - - let extra = match check::app(&app, &analysis) { + let (app, analysis) = match syntax::parse(args, input, settings) { Err(e) => return e.to_compile_error().into(), Ok(x) => x, }; let analysis = analyze::app(analysis, &app); - let ts = codegen::app(&app, &analysis, &extra); + let ts = codegen::app(&app, &analysis); // Default output path: /target/ let mut out_dir = Path::new("target"); @@ -52,22 +72,7 @@ pub fn app(args: TokenStream, input: TokenStream) -> TokenStream { // 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()); - // Assuming we are building for a thumbv* target - let target_triple_prefix = "thumbv"; - - // Check for special scenario where default target/ directory is not present - // - // This is configurable in .cargo/config: - // - // [build] - // target-dir = "target" - #[cfg(feature = "debugprint")] - println!("OUT_DIR\n{:#?}", out_str); - - if out_dir.exists() { - #[cfg(feature = "debugprint")] - println!("\ntarget/ exists\n"); - } else { + if !out_dir.exists() { // Set out_dir to OUT_DIR out_dir = Path::new(&out_str); @@ -81,16 +86,11 @@ pub fn app(args: TokenStream, input: TokenStream) -> TokenStream { // If no "target" directory is found, / is used for path in out_dir.ancestors() { if let Some(dir) = path.components().last() { - if dir - .as_os_str() - .to_str() - .unwrap() - .starts_with(target_triple_prefix) - { + 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; - #[cfg(feature = "debugprint")] - println!("{:#?}\n", out_dir); break; } // If no parent, just use it @@ -103,8 +103,6 @@ pub fn app(args: TokenStream, input: TokenStream) -> TokenStream { // Try to write the expanded code to disk if let Some(out_str) = out_dir.to_str() { - #[cfg(feature = "debugprint")] - println!("Write file:\n{}/rtic-expansion.rs\n", out_str); fs::write(format!("{}/rtic-expansion.rs", out_str), ts.to_string()).ok(); } diff --git a/macros/src/syntax.rs b/macros/src/syntax.rs new file mode 100644 index 0000000..11b92c1 --- /dev/null +++ b/macros/src/syntax.rs @@ -0,0 +1,158 @@ +#[allow(unused_extern_crates)] +extern crate proc_macro; + +use core::ops; +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 optimize; +mod parse; + +/// An ordered map keyed by identifier +pub type Map = IndexMap; + +/// An order set +pub type Set = IndexSet; + +/// Immutable pointer +pub struct P { + ptr: Box, +} + +impl P { + /// Boxes `x` making the value immutable + pub fn new(x: T) -> P { + P { ptr: Box::new(x) } + } +} + +impl ops::Deref for P { + type Target = T; + + fn deref(&self) -> &T { + &self.ptr + } +} + +/// Execution context +#[derive(Clone, Copy)] +pub enum Context<'a> { + /// The `idle` context + Idle, + + /// The `init`-ialization function + Init, + + /// A software task: `#[task]` + SoftwareTask(&'a Ident), + + /// A hardware task: `#[exception]` or `#[interrupt]` + 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() + } + } + } +} + +/// Parser and optimizer configuration +#[derive(Default)] +#[non_exhaustive] +pub struct Settings { + /// Whether to accept the `binds` argument in `#[task]` or not + pub parse_binds: bool, + /// Whether to parse `extern` interrupts (functions) or not + pub parse_extern_interrupt: bool, + /// Whether to "compress" priorities or not + pub optimize_priorities: bool, +} + +/// Parses the input of the `#[app]` attribute +pub fn parse( + args: TokenStream, + input: TokenStream, + settings: Settings, +) -> Result<(ast::App, analyze::Analysis), syn::parse::Error> { + parse2(args.into(), input.into(), settings) +} + +/// `proc_macro2::TokenStream` version of `parse` +pub fn parse2( + args: TokenStream2, + input: TokenStream2, + settings: Settings, +) -> Result<(ast::App, analyze::Analysis), syn::parse::Error> { + let mut app = parse::app(args, input, &settings)?; + check::app(&app)?; + optimize::app(&mut app, &settings); + + match analyze::app(&app) { + Err(e) => Err(e), + // If no errors, return the app and analysis results + Ok(analysis) => Ok((app, analysis)), + } +} + +enum Either { + Left(A), + Right(B), +} diff --git a/macros/src/syntax/.github/bors.toml b/macros/src/syntax/.github/bors.toml new file mode 100644 index 0000000..aee6042 --- /dev/null +++ b/macros/src/syntax/.github/bors.toml @@ -0,0 +1,3 @@ +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 new file mode 100644 index 0000000..29971b1 --- /dev/null +++ b/macros/src/syntax/.github/workflows/build.yml @@ -0,0 +1,213 @@ +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 new file mode 100644 index 0000000..ccf6eb9 --- /dev/null +++ b/macros/src/syntax/.github/workflows/changelog.yml @@ -0,0 +1,28 @@ +# 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 new file mode 100644 index 0000000..fd3eed3 --- /dev/null +++ b/macros/src/syntax/.github/workflows/properties/build.properties.json @@ -0,0 +1,6 @@ +{ + "name": "Build", + "description": "RTIC Test Suite", + "iconName": "rust", + "categories": ["Rust"] +} diff --git a/macros/src/syntax/.gitignore b/macros/src/syntax/.gitignore new file mode 100644 index 0000000..f8d7c8b --- /dev/null +++ b/macros/src/syntax/.gitignore @@ -0,0 +1,4 @@ +**/*.rs.bk +.#* +/target/ +Cargo.lock diff --git a/macros/src/syntax/.travis.yml b/macros/src/syntax/.travis.yml new file mode 100644 index 0000000..52d1ffd --- /dev/null +++ b/macros/src/syntax/.travis.yml @@ -0,0 +1,31 @@ +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 new file mode 100644 index 0000000..e75dde6 --- /dev/null +++ b/macros/src/syntax/accessors.rs @@ -0,0 +1,113 @@ +use syn::Ident; + +use crate::syntax::{ + analyze::Priority, + ast::{Access, App, Local, TaskLocal}, +}; + +impl App { + pub(crate) fn shared_resource_accesses( + &self, + ) -> impl Iterator, &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 { + 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 new file mode 100644 index 0000000..06b23f4 --- /dev/null +++ b/macros/src/syntax/analyze.rs @@ -0,0 +1,448 @@ +//! 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 { + // Collect all tasks into a vector + type TaskName = String; + type Priority = u8; + + // The task list is a Tuple (Name, Shared Resources, Local Resources, Priority, IsAsync) + let task_resources_list: Vec<(TaskName, Vec<&Ident>, &LocalResources, Priority, bool)> = + Some(&app.init) + .iter() + .map(|ht| { + ( + "init".to_string(), + Vec::new(), + &ht.args.local_resources, + 0, + false, + ) + }) + .chain(app.idle.iter().map(|ht| { + ( + "idle".to_string(), + ht.args + .shared_resources + .iter() + .map(|(v, _)| v) + .collect::>(), + &ht.args.local_resources, + 0, + false, + ) + })) + .chain(app.software_tasks.iter().map(|(name, ht)| { + ( + name.to_string(), + ht.args + .shared_resources + .iter() + .map(|(v, _)| v) + .collect::>(), + &ht.args.local_resources, + ht.args.priority, + ht.is_async, + ) + })) + .chain(app.hardware_tasks.iter().map(|(name, ht)| { + ( + name.to_string(), + ht.args + .shared_resources + .iter() + .map(|(v, _)| v) + .collect::>(), + &ht.args.local_resources, + ht.args.priority, + false, + ) + })) + .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, is_async) in task_resources_list.iter() { + for r in tr { + // Get all uses of resources annotated lock_free + if lf_res == r { + // lock_free resources are not allowed in async tasks + if *is_async { + error.push(syn::Error::new( + 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!( + "Software task {:?} has priority 0, but `#[idle]` is defined. 0-priority software tasks are only allowed if there is no `#[idle]`.", + name.to_string(), + ) + )); + } + + // 0-priority tasks must be async + if !task.is_async { + error.push(syn::Error::new( + name.span(), + format!( + "Software task {:?} has priority 0, but is not `async`. 0-priority software tasks must be `async`.", + 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` + let mut send_types = SendTypes::new(); + let owned_by_idle = Ownership::Owned { priority: 0 }; + for (name, res) in app.shared_resources.iter() { + // Handle not owned by idle + if ownerships + .get(name) + .map(|ownership| *ownership != owned_by_idle) + .unwrap_or(false) + { + send_types.insert(res.ty.clone()); + } + } + + // Most local resources need to be `Send` as well + for (name, res) in app.local_resources.iter() { + if let Some(idle) = &app.idle { + // Only Send if not in idle or not at idle prio + if idle.args.local_resources.get(name).is_none() + && !ownerships + .get(name) + .map(|ownership| *ownership != owned_by_idle) + .unwrap_or(false) + { + send_types.insert(res.ty.clone()); + } + } else { + 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()); + + if !spawnee.args.only_same_priority_spawn { + // Require `Send` if the task can be spawned from other priorities + spawnee.inputs.iter().for_each(|input| { + send_types.insert(input.ty.clone()); + }); + } + } + + // No channel should ever be empty + debug_assert!(channels.values().all(|channel| !channel.tasks.is_empty())); + + // Compute channel capacities + for channel in channels.values_mut() { + channel.capacity = channel + .tasks + .iter() + .map(|name| app.software_tasks[name].args.capacity) + .sum(); + } + + Ok(Analysis { + channels, + shared_resources: used_shared_resource, + local_resources: used_local_resource, + ownerships, + send_types, + sync_types, + }) +} + +/// Priority ceiling +pub type Ceiling = Option; + +/// 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; + +/// Location of all *used* shared resources +pub type UsedSharedResource = IndexSet; + +/// Location of all *used* local resources +pub type UsedLocalResource = IndexSet; + +/// Resource ownership +pub type Ownerships = IndexMap; + +/// These types must implement the `Send` trait +pub type SendTypes = Set>; + +/// These types must implement the `Sync` trait +pub type SyncTypes = Set>; + +/// 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, +} + +/// 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 new file mode 100644 index 0000000..0f2e36f --- /dev/null +++ b/macros/src/syntax/ast.rs @@ -0,0 +1,380 @@ +//! 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, + + /// Monotonic clocks + pub monotonics: Map, + + /// Resources shared between tasks defined in `#[shared]` + pub shared_resources: Map, + + /// Task local resources defined in `#[local]` + pub local_resources: Map, + + /// User imports + pub user_imports: Vec, + + /// User code + pub user_code: Vec, + + /// Hardware tasks: `#[task(binds = ..)]`s + pub hardware_tasks: Map, + + /// Software tasks: `#[task]` + pub software_tasks: Map, +} + +/// Interrupts used to dispatch software tasks +pub type Dispatchers = Map; + +/// 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, +} + +/// 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, + + /// The name of the `#[init]` function + pub name: Ident, + + /// The context argument + pub context: Box, + + /// The statements that make up this `init` function + pub stmts: Vec, + + /// 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, + + /// The name of the `#[idle]` function + pub name: Ident, + + /// The context argument + pub context: Box, + + /// The statements that make up this `idle` function + pub stmts: Vec, +} + +/// `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, + + /// `#[doc]` attributes like `/// this is a docstring` + pub docs: Vec, + + /// Attributes that will apply to this resource + pub attrs: Vec, + + /// The type of this resource + pub ty: Box, + + /// 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, + + /// `#[doc]` attributes like `/// this is a docstring` + pub docs: Vec, + + /// Attributes that will apply to this resource + pub attrs: Vec, + + /// The type of this resource + pub ty: Box, +} + +/// Monotonic +#[derive(Debug)] +#[non_exhaustive] +pub struct Monotonic { + /// `#[cfg]` attributes like `#[cfg(debug_assertions)]` + pub cfgs: Vec, + + /// The identifier of the monotonic + pub ident: Ident, + + /// The type of this monotonic + pub ty: Box, + + /// Monotonic args + pub args: MonotonicArgs, +} + +/// Monotonic metadata +#[derive(Debug)] +#[non_exhaustive] +pub struct MonotonicArgs { + /// The interrupt or exception that this monotonic is bound to + pub binds: Ident, + + /// The priority of this monotonic + pub priority: Option, + + /// If this is the default monotonic + pub default: bool, +} + +/// A software task +#[derive(Debug)] +#[non_exhaustive] +pub struct SoftwareTask { + /// Software task metadata + pub args: SoftwareTaskArgs, + + /// `#[cfg]` attributes like `#[cfg(debug_assertions)]` + pub cfgs: Vec, + + /// Attributes that will apply to this interrupt handler + pub attrs: Vec, + + /// The context argument + pub context: Box, + + /// The inputs of this software task + pub inputs: Vec, + + /// The statements that make up the task handler + pub stmts: Vec, + + /// The task is declared externally + pub is_extern: bool, + + /// If the task is marked as `async` + pub is_async: bool, +} + +/// Software task metadata +#[derive(Debug)] +#[non_exhaustive] +pub struct SoftwareTaskArgs { + /// The task capacity: the maximum number of pending messages that can be queued + pub capacity: u8, + + /// The priority of this task + pub priority: u8, + + /// 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, + + /// Only same priority tasks can spawn this task + pub only_same_priority_spawn: bool, +} + +impl Default for SoftwareTaskArgs { + fn default() -> Self { + Self { + capacity: 1, + priority: 1, + local_resources: LocalResources::new(), + shared_resources: SharedResources::new(), + only_same_priority_spawn: false, + } + } +} + +/// 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, + + /// Attributes that will apply to this interrupt handler + pub attrs: Vec, + + /// The context argument + pub context: Box, + + /// The statements that make up the task handler + pub stmts: Vec, + + /// 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, + + /// `#[cfg]` attributes like `#[cfg(debug_assertions)]` + pub cfgs: Vec, + + /// Type + pub ty: Box, + + /// Initial value + pub expr: Box, +} + +/// 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; + +/// Local resource access/declaration list in task attribute +pub type LocalResources = Map; diff --git a/macros/src/syntax/check.rs b/macros/src/syntax/check.rs new file mode 100644 index 0000000..989d418 --- /dev/null +++ b/macros/src/syntax/check.rs @@ -0,0 +1,66 @@ +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::>(); + 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 new file mode 100644 index 0000000..87a6258 --- /dev/null +++ b/macros/src/syntax/optimize.rs @@ -0,0 +1,36 @@ +use std::collections::{BTreeSet, HashMap}; + +use crate::syntax::{ast::App, Settings}; + +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::>(); + + let map = priorities + .iter() + .cloned() + .zip(1..) + .collect::>(); + + 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 new file mode 100644 index 0000000..74f94f2 --- /dev/null +++ b/macros/src/syntax/parse.rs @@ -0,0 +1,520 @@ +mod app; +mod hardware_task; +mod idle; +mod init; +mod monotonic; +mod resource; +mod software_task; +mod util; + +use proc_macro2::TokenStream as TokenStream2; +use syn::{ + braced, parenthesized, + parse::{self, Parse, ParseStream, Parser}, + token::{self, Brace}, + Ident, Item, LitBool, LitInt, Path, Token, +}; + +use crate::syntax::{ + ast::{ + App, AppArgs, HardwareTaskArgs, IdleArgs, InitArgs, MonotonicArgs, SoftwareTaskArgs, + TaskLocal, + }, + Either, Settings, +}; + +// Parse the app, both app arguments and body (input) +pub fn app(args: TokenStream2, input: TokenStream2, settings: &Settings) -> parse::Result { + let args = AppArgs::parse(args)?; + let input: Input = syn::parse2(input)?; + + App::parse(args, input, settings) +} + +pub(crate) struct Input { + _mod_token: Token![mod], + pub ident: Ident, + _brace_token: Brace, + pub items: Vec, +} + +impl Parse for Input { + fn parse(input: ParseStream<'_>) -> parse::Result { + fn parse_items(input: ParseStream<'_>) -> parse::Result> { + 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 { + (|input: ParseStream<'_>| -> parse::Result { + 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 { + (|input: ParseStream<'_>| -> parse::Result { + 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, + settings: &Settings, +) -> parse::Result> { + (|input: ParseStream<'_>| -> parse::Result> { + 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 mut only_same_priority_spawn = false; + let mut only_same_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(); + + if ident_s == "only_same_priority_spawn_please_fix_me" { + if only_same_priority_spawn { + return Err(parse::Error::new( + ident.span(), + "argument appears more than once", + )); + } + + only_same_priority_spawn = true; + only_same_prio_span = Some(ident.span()); + + if content.is_empty() { + break; + } + + // Handle comma: , + let _: Token![,] = content.parse()?; + + continue; + } + + // Handle equal sign + let _: Token![=] = content.parse()?; + + match &*ident_s { + "binds" if !settings.parse_binds => { + return Err(parse::Error::new( + ident.span(), + "Unexpected bind in task argument. Binds are only parsed if Settings::parse_binds is set.", + )); + } + + "binds" if settings.parse_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::().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::().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", + )); + } + + if only_same_priority_spawn { + return Err(parse::Error::new( + only_same_prio_span.unwrap(), + "hardware tasks are not allowed to be spawned, `only_same_priority_spawn_please_fix_me` is only for software tasks", + )); + } + + Either::Left(HardwareTaskArgs { + binds, + priority, + shared_resources, + local_resources, + }) + } else { + Either::Right(SoftwareTaskArgs { + capacity: capacity.unwrap_or(1), + priority, + shared_resources, + local_resources, + only_same_priority_spawn, + }) + }) + }) + .parse2(tokens) +} + +fn monotonic_args(path: Path, tokens: TokenStream2) -> parse::Result { + (|input: ParseStream<'_>| -> parse::Result { + let mut binds = None; + let mut priority = None; + let mut default = None; + + if !input.peek(token::Paren) { + return Err(parse::Error::new( + path.segments.first().unwrap().ident.span(), + "expected opening ( in #[monotonic( ... )]", + )); + } + + let content; + parenthesized!(content in input); + + if !content.is_empty() { + loop { + // Parse identifier name + let ident: Ident = content.parse()?; + // Handle equal sign + let _: Token![=] = content.parse()?; + + match &*ident.to_string() { + "binds" => { + if binds.is_some() { + return Err(parse::Error::new( + ident.span(), + "argument appears more than once", + )); + } + // Parse identifier name + let ident = content.parse()?; + + binds = Some(ident); + } + + "priority" => { + if priority.is_some() { + return Err(parse::Error::new( + ident.span(), + "argument appears more than once", + )); + } + + // #lit + let lit: LitInt = content.parse()?; + + if !lit.suffix().is_empty() { + return Err(parse::Error::new( + lit.span(), + "this literal must be unsuffixed", + )); + } + + let value = lit.base10_parse::().ok(); + if value.is_none() || value == Some(0) { + return Err(parse::Error::new( + lit.span(), + "this literal must be in the range 1...255", + )); + } + + priority = Some(value.unwrap()); + } + + "default" => { + if default.is_some() { + return Err(parse::Error::new( + ident.span(), + "argument appears more than once", + )); + } + + let lit: LitBool = content.parse()?; + default = Some(lit.value); + } + + _ => { + return Err(parse::Error::new(ident.span(), "unexpected argument")); + } + } + if content.is_empty() { + break; + } + + // Handle comma: , + let _: Token![,] = content.parse()?; + } + } + + let binds = if let Some(r) = binds { + r + } else { + return Err(parse::Error::new( + content.span(), + "`binds = ...` is missing", + )); + }; + let default = default.unwrap_or(false); + + Ok(MonotonicArgs { + binds, + priority, + default, + }) + }) + .parse2(tokens) +} diff --git a/macros/src/syntax/parse/app.rs b/macros/src/syntax/parse/app.rs new file mode 100644 index 0000000..7eb415d --- /dev/null +++ b/macros/src/syntax/parse/app.rs @@ -0,0 +1,539 @@ +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, Type, Visibility, +}; + +use super::Input; +use crate::syntax::{ + ast::{ + App, AppArgs, Dispatcher, Dispatchers, HardwareTask, Idle, IdleArgs, Init, InitArgs, + LocalResource, Monotonic, MonotonicArgs, SharedResource, SoftwareTask, + }, + parse::{self as syntax_parse, util}, + Either, Map, Set, Settings, +}; + +impl AppArgs { + pub(crate) fn parse(tokens: TokenStream2) -> parse::Result { + (|input: ParseStream<'_>| -> parse::Result { + 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::() { + 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::() { + 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::() { + 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, settings: &Settings) -> parse::Result { + 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 monotonics = 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::::new(); + let mut bindings = HashSet::::new(); + let mut monotonic_types = HashSet::::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(()) + }; + + let mut check_monotonic = |ty: &Type| { + if monotonic_types.contains(ty) { + return Err(parse::Error::new( + ty.span(), + "this type is already used by another monotonic", + )); + } else { + monotonic_types.insert(ty.clone()); + } + + Ok(()) + }; + + for mut item in input.items { + match item { + Item::Fn(mut item) => { + 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, settings)? { + 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, + settings, + )? { + 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()); + } + Item::Type(ref mut type_item) => { + // Match types with the attribute #[monotonic] + if let Some(pos) = type_item + .attrs + .iter() + .position(|attr| util::attr_eq(attr, "monotonic")) + { + let span = type_item.ident.span(); + + if monotonics.contains_key(&type_item.ident) { + return Err(parse::Error::new( + span, + "`#[monotonic(...)]` on a specific type must appear at most once", + )); + } + + if type_item.vis != Visibility::Inherited { + return Err(parse::Error::new( + type_item.span(), + "this item must have inherited / private visibility", + )); + } + + check_monotonic(&*type_item.ty)?; + + let m = type_item.attrs.remove(pos); + let args = MonotonicArgs::parse(m)?; + + check_binding(&args.binds)?; + + let monotonic = Monotonic::parse(args, type_item, span)?; + + monotonics.insert(type_item.ident.clone(), monotonic); + } + + // All types are passed on + user_code.push(item.clone()); + } + _ => { + // Anything else within the module should not make any difference + user_code.push(item.clone()); + } + } + } + + 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, + monotonics, + 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 new file mode 100644 index 0000000..304bfcd --- /dev/null +++ b/macros/src/syntax/parse/hardware_task.rs @@ -0,0 +1,96 @@ +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 { + 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 name == "init" || name == "idle" { + return Err(parse::Error::new( + span, + "tasks cannot be named `init` or `idle`", + )); + } + + 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({}::Context)`", + name + ), + )) + } +} + +impl HardwareTask { + pub(crate) fn parse_foreign( + args: HardwareTaskArgs, + item: ForeignItemFn, + ) -> parse::Result { + 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 name == "init" || name == "idle" { + return Err(parse::Error::new( + span, + "tasks cannot be named `init` or `idle`", + )); + } + + 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::::new(), + is_extern: true, + }); + } + } + } + + Err(parse::Error::new( + span, + &format!( + "this task handler must have type signature `fn({}::Context)`", + name + ), + )) + } +} diff --git a/macros/src/syntax/parse/idle.rs b/macros/src/syntax/parse/idle.rs new file mode 100644 index 0000000..d9f3a99 --- /dev/null +++ b/macros/src/syntax/parse/idle.rs @@ -0,0 +1,45 @@ +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 { + crate::syntax::parse::idle_args(tokens) + } +} + +impl Idle { + pub(crate) fn parse(args: IdleArgs, item: ItemFn) -> parse::Result { + 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({}::Context) -> !`", + name + ), + )) + } +} diff --git a/macros/src/syntax/parse/init.rs b/macros/src/syntax/parse/init.rs new file mode 100644 index 0000000..727ee20 --- /dev/null +++ b/macros/src/syntax/parse/init.rs @@ -0,0 +1,52 @@ +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 { + syntax_parse::init_args(tokens) + } +} + +impl Init { + pub(crate) fn parse(args: InitArgs, item: ItemFn) -> parse::Result { + 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, &name) + { + if let Some((context, Ok(rest))) = util::parse_inputs(item.sig.inputs, &name) { + if rest.is_empty() { + return Ok(Init { + args, + attrs: item.attrs, + context, + name: item.sig.ident, + stmts: item.block.stmts, + user_shared_struct, + user_local_struct, + }); + } + } + } + } + + Err(parse::Error::new( + span, + &format!( + "the `#[init]` function must have signature `fn({}::Context) -> (Shared resources struct, Local resources struct, {0}::Monotonics)`", + name + ), + )) + } +} diff --git a/macros/src/syntax/parse/monotonic.rs b/macros/src/syntax/parse/monotonic.rs new file mode 100644 index 0000000..0583233 --- /dev/null +++ b/macros/src/syntax/parse/monotonic.rs @@ -0,0 +1,42 @@ +use proc_macro2::Span; +use syn::Attribute; +use syn::{parse, spanned::Spanned, ItemType, Visibility}; + +use crate::syntax::parse::util::FilterAttrs; +use crate::syntax::{ + ast::{Monotonic, MonotonicArgs}, + parse::util, +}; + +impl MonotonicArgs { + pub(crate) fn parse(attr: Attribute) -> parse::Result { + crate::syntax::parse::monotonic_args(attr.path, attr.tokens) + } +} + +impl Monotonic { + pub(crate) fn parse(args: MonotonicArgs, item: &ItemType, span: Span) -> parse::Result { + if item.vis != Visibility::Inherited { + return Err(parse::Error::new( + span, + "this field must have inherited / private visibility", + )); + } + + let FilterAttrs { cfgs, attrs, .. } = util::filter_attributes(item.attrs.clone()); + + if !attrs.is_empty() { + return Err(parse::Error::new( + attrs[0].path.span(), + "Monotonic does not support attributes other than `#[cfg]`", + )); + } + + Ok(Monotonic { + cfgs, + ident: item.ident.clone(), + ty: item.ty.clone(), + args, + }) + } +} diff --git a/macros/src/syntax/parse/resource.rs b/macros/src/syntax/parse/resource.rs new file mode 100644 index 0000000..ff10057 --- /dev/null +++ b/macros/src/syntax/parse/resource.rs @@ -0,0 +1,55 @@ +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 { + 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 { + 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 new file mode 100644 index 0000000..2b1ac4a --- /dev/null +++ b/macros/src/syntax/parse/software_task.rs @@ -0,0 +1,86 @@ +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 { + let valid_signature = + util::check_fn_signature(&item, true) && util::type_is_unit(&item.sig.output); + + let span = item.sig.ident.span(); + + let name = item.sig.ident.to_string(); + + let is_async = item.sig.asyncness.is_some(); + + if valid_signature { + if let Some((context, Ok(inputs))) = util::parse_inputs(item.sig.inputs, &name) { + let FilterAttrs { cfgs, attrs, .. } = util::filter_attributes(item.attrs); + + return Ok(SoftwareTask { + args, + attrs, + cfgs, + context, + inputs, + stmts: item.block.stmts, + is_extern: false, + is_async, + }); + } + } + + Err(parse::Error::new( + span, + &format!( + "this task handler must have type signature `(async) fn({}::Context, ..)`", + name + ), + )) + } +} + +impl SoftwareTask { + pub(crate) fn parse_foreign( + args: SoftwareTaskArgs, + item: ForeignItemFn, + ) -> parse::Result { + let valid_signature = + util::check_foreign_fn_signature(&item, true) && util::type_is_unit(&item.sig.output); + + let span = item.sig.ident.span(); + + let name = item.sig.ident.to_string(); + + let is_async = item.sig.asyncness.is_some(); + + if valid_signature { + if let Some((context, Ok(inputs))) = util::parse_inputs(item.sig.inputs, &name) { + let FilterAttrs { cfgs, attrs, .. } = util::filter_attributes(item.attrs); + + return Ok(SoftwareTask { + args, + attrs, + cfgs, + context, + inputs, + stmts: Vec::::new(), + is_extern: true, + is_async, + }); + } + } + + Err(parse::Error::new( + span, + &format!( + "this task handler must have type signature `(async) fn({}::Context, ..)`", + name + ), + )) + } +} diff --git a/macros/src/syntax/parse/util.rs b/macros/src/syntax/parse/util.rs new file mode 100644 index 0000000..3fa51ef --- /dev/null +++ b/macros/src/syntax/parse/util.rs @@ -0,0 +1,338 @@ +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, + pub docs: Vec, + pub attrs: Vec, +} + +pub fn filter_attributes(input_attrs: Vec) -> 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) -> parse::Result { + 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 { + let inner; + bracketed!(inner in content); + + let mut resources = Map::new(); + for e in inner.call(Punctuated::::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 { + 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 { + let inner; + bracketed!(inner in content); + + let mut resources = Map::new(); + + for e in inner.call(Punctuated::::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, Result, FnArg>)>; + +pub fn parse_inputs(inputs: Punctuated, 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::, _>>(); + + 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 { + 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, name: &str) -> 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, {name}::Monotonics) + // + // We check the length and the last one here, analysis checks that the user + // provided structs are correct. + if t.elems.len() == 3 && type_is_path(&t.elems[2], &[name, "Monotonics"]) { + 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/tests/ui.rs b/macros/tests/ui.rs new file mode 100644 index 0000000..9fb88a1 --- /dev/null +++ b/macros/tests/ui.rs @@ -0,0 +1,7 @@ +use trybuild::TestCases; + +#[test] +fn ui() { + let t = TestCases::new(); + t.compile_fail("ui/*.rs"); +} diff --git a/macros/ui/async-local-resouces.rs b/macros/ui/async-local-resouces.rs new file mode 100644 index 0000000..1ba5865 --- /dev/null +++ b/macros/ui/async-local-resouces.rs @@ -0,0 +1,28 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[shared] + struct Shared { + #[lock_free] + e: u32, + } + + #[local] + struct Local {} + + #[init] + fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {} + + // e ok + #[task(priority = 1, shared = [e])] + fn uart0(cx: uart0::Context) {} + + // e ok + #[task(priority = 1, shared = [e])] + fn uart1(cx: uart1::Context) {} + + // e not ok + #[task(priority = 1, shared = [e])] + async fn async_task(cx: async_task::Context) {} +} diff --git a/macros/ui/async-local-resouces.stderr b/macros/ui/async-local-resouces.stderr new file mode 100644 index 0000000..7ce7517 --- /dev/null +++ b/macros/ui/async-local-resouces.stderr @@ -0,0 +1,5 @@ +error: Lock free shared resource "e" is used by an async tasks, which is forbidden + --> ui/async-local-resouces.rs:26:36 + | +26 | #[task(priority = 1, shared = [e])] + | ^ diff --git a/macros/ui/async-zero-prio-tasks.rs b/macros/ui/async-zero-prio-tasks.rs new file mode 100644 index 0000000..91e0990 --- /dev/null +++ b/macros/ui/async-zero-prio-tasks.rs @@ -0,0 +1,19 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[shared] + struct Shared {} + + #[local] + struct Local {} + + #[init] + fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {} + + #[task(priority = 0)] + fn foo(_: foo::Context) {} + + #[idle] + fn idle(_: idle::Context) -> ! {} +} diff --git a/macros/ui/async-zero-prio-tasks.stderr b/macros/ui/async-zero-prio-tasks.stderr new file mode 100644 index 0000000..d617feb --- /dev/null +++ b/macros/ui/async-zero-prio-tasks.stderr @@ -0,0 +1,11 @@ +error: Software task "foo" has priority 0, but `#[idle]` is defined. 0-priority software tasks are only allowed if there is no `#[idle]`. + --> ui/async-zero-prio-tasks.rs:15:8 + | +15 | fn foo(_: foo::Context) {} + | ^^^ + +error: Software task "foo" has priority 0, but is not `async`. 0-priority software tasks must be `async`. + --> ui/async-zero-prio-tasks.rs:15:8 + | +15 | fn foo(_: foo::Context) {} + | ^^^ diff --git a/macros/ui/extern-interrupt-used.rs b/macros/ui/extern-interrupt-used.rs new file mode 100644 index 0000000..71dc50f --- /dev/null +++ b/macros/ui/extern-interrupt-used.rs @@ -0,0 +1,16 @@ +#![no_main] + +#[rtic_macros::mock_app(parse_extern_interrupt, parse_binds, device = mock, dispatchers = [EXTI0])] +mod app { + #[shared] + struct Shared {} + + #[local] + struct Local {} + + #[init] + fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {} + + #[task(binds = EXTI0)] + fn foo(_: foo::Context) {} +} diff --git a/macros/ui/extern-interrupt-used.stderr b/macros/ui/extern-interrupt-used.stderr new file mode 100644 index 0000000..f9510d7 --- /dev/null +++ b/macros/ui/extern-interrupt-used.stderr @@ -0,0 +1,5 @@ +error: dispatcher interrupts can't be used as hardware tasks + --> $DIR/extern-interrupt-used.rs:14:20 + | +14 | #[task(binds = EXTI0)] + | ^^^^^ diff --git a/macros/ui/idle-double-local.rs b/macros/ui/idle-double-local.rs new file mode 100644 index 0000000..54e67d3 --- /dev/null +++ b/macros/ui/idle-double-local.rs @@ -0,0 +1,9 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[idle(local = [A], local = [B])] + fn idle(_: idle::Context) -> ! { + loop {} + } +} diff --git a/macros/ui/idle-double-local.stderr b/macros/ui/idle-double-local.stderr new file mode 100644 index 0000000..d3ba4ec --- /dev/null +++ b/macros/ui/idle-double-local.stderr @@ -0,0 +1,5 @@ +error: argument appears more than once + --> $DIR/idle-double-local.rs:5:25 + | +5 | #[idle(local = [A], local = [B])] + | ^^^^^ diff --git a/macros/ui/idle-double-shared.rs b/macros/ui/idle-double-shared.rs new file mode 100644 index 0000000..f66cb93 --- /dev/null +++ b/macros/ui/idle-double-shared.rs @@ -0,0 +1,9 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[idle(shared = [A], shared = [B])] + fn idle(_: idle::Context) -> ! { + loop {} + } +} diff --git a/macros/ui/idle-double-shared.stderr b/macros/ui/idle-double-shared.stderr new file mode 100644 index 0000000..84864a1 --- /dev/null +++ b/macros/ui/idle-double-shared.stderr @@ -0,0 +1,5 @@ +error: argument appears more than once + --> $DIR/idle-double-shared.rs:5:26 + | +5 | #[idle(shared = [A], shared = [B])] + | ^^^^^^ diff --git a/macros/ui/idle-input.rs b/macros/ui/idle-input.rs new file mode 100644 index 0000000..c896b1c --- /dev/null +++ b/macros/ui/idle-input.rs @@ -0,0 +1,9 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[idle] + fn idle(_: idle::Context, _undef: u32) -> ! { + loop {} + } +} diff --git a/macros/ui/idle-input.stderr b/macros/ui/idle-input.stderr new file mode 100644 index 0000000..34c38fc --- /dev/null +++ b/macros/ui/idle-input.stderr @@ -0,0 +1,5 @@ +error: this `#[idle]` function must have signature `fn(idle::Context) -> !` + --> ui/idle-input.rs:6:8 + | +6 | fn idle(_: idle::Context, _undef: u32) -> ! { + | ^^^^ diff --git a/macros/ui/idle-no-context.rs b/macros/ui/idle-no-context.rs new file mode 100644 index 0000000..bab4680 --- /dev/null +++ b/macros/ui/idle-no-context.rs @@ -0,0 +1,9 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[idle] + fn idle() -> ! { + loop {} + } +} diff --git a/macros/ui/idle-no-context.stderr b/macros/ui/idle-no-context.stderr new file mode 100644 index 0000000..c9f4b3d --- /dev/null +++ b/macros/ui/idle-no-context.stderr @@ -0,0 +1,5 @@ +error: this `#[idle]` function must have signature `fn(idle::Context) -> !` + --> ui/idle-no-context.rs:6:8 + | +6 | fn idle() -> ! { + | ^^^^ diff --git a/macros/ui/idle-not-divergent.rs b/macros/ui/idle-not-divergent.rs new file mode 100644 index 0000000..d1ae8b1 --- /dev/null +++ b/macros/ui/idle-not-divergent.rs @@ -0,0 +1,7 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[idle] + fn idle(_: idle::Context) {} +} diff --git a/macros/ui/idle-not-divergent.stderr b/macros/ui/idle-not-divergent.stderr new file mode 100644 index 0000000..e318f58 --- /dev/null +++ b/macros/ui/idle-not-divergent.stderr @@ -0,0 +1,5 @@ +error: this `#[idle]` function must have signature `fn(idle::Context) -> !` + --> ui/idle-not-divergent.rs:6:8 + | +6 | fn idle(_: idle::Context) {} + | ^^^^ diff --git a/macros/ui/idle-output.rs b/macros/ui/idle-output.rs new file mode 100644 index 0000000..1662157 --- /dev/null +++ b/macros/ui/idle-output.rs @@ -0,0 +1,9 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[idle] + fn idle(_: idle::Context) -> u32 { + 0 + } +} diff --git a/macros/ui/idle-output.stderr b/macros/ui/idle-output.stderr new file mode 100644 index 0000000..7070e25 --- /dev/null +++ b/macros/ui/idle-output.stderr @@ -0,0 +1,5 @@ +error: this `#[idle]` function must have signature `fn(idle::Context) -> !` + --> ui/idle-output.rs:6:8 + | +6 | fn idle(_: idle::Context) -> u32 { + | ^^^^ diff --git a/macros/ui/idle-pub.rs b/macros/ui/idle-pub.rs new file mode 100644 index 0000000..0d8dd01 --- /dev/null +++ b/macros/ui/idle-pub.rs @@ -0,0 +1,9 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[idle] + pub fn idle(_: idle::Context) -> ! { + loop {} + } +} diff --git a/macros/ui/idle-pub.stderr b/macros/ui/idle-pub.stderr new file mode 100644 index 0000000..aa46ac3 --- /dev/null +++ b/macros/ui/idle-pub.stderr @@ -0,0 +1,5 @@ +error: this `#[idle]` function must have signature `fn(idle::Context) -> !` + --> ui/idle-pub.rs:6:12 + | +6 | pub fn idle(_: idle::Context) -> ! { + | ^^^^ diff --git a/macros/ui/idle-unsafe.rs b/macros/ui/idle-unsafe.rs new file mode 100644 index 0000000..3422ef2 --- /dev/null +++ b/macros/ui/idle-unsafe.rs @@ -0,0 +1,9 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[idle] + unsafe fn idle(_: idle::Context) -> ! { + loop {} + } +} diff --git a/macros/ui/idle-unsafe.stderr b/macros/ui/idle-unsafe.stderr new file mode 100644 index 0000000..a416800 --- /dev/null +++ b/macros/ui/idle-unsafe.stderr @@ -0,0 +1,5 @@ +error: this `#[idle]` function must have signature `fn(idle::Context) -> !` + --> ui/idle-unsafe.rs:6:15 + | +6 | unsafe fn idle(_: idle::Context) -> ! { + | ^^^^ diff --git a/macros/ui/init-divergent.rs b/macros/ui/init-divergent.rs new file mode 100644 index 0000000..5e4e96a --- /dev/null +++ b/macros/ui/init-divergent.rs @@ -0,0 +1,13 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[shared] + struct Shared {} + + #[local] + struct Local {} + + #[init] + fn init(_: init::Context) -> ! {} +} diff --git a/macros/ui/init-divergent.stderr b/macros/ui/init-divergent.stderr new file mode 100644 index 0000000..2d5cc39 --- /dev/null +++ b/macros/ui/init-divergent.stderr @@ -0,0 +1,5 @@ +error: the `#[init]` function must have signature `fn(init::Context) -> (Shared resources struct, Local resources struct, init::Monotonics)` + --> $DIR/init-divergent.rs:12:8 + | +12 | fn init(_: init::Context) -> ! {} + | ^^^^ diff --git a/macros/ui/init-double-local.rs b/macros/ui/init-double-local.rs new file mode 100644 index 0000000..5f6d7ac --- /dev/null +++ b/macros/ui/init-double-local.rs @@ -0,0 +1,7 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[init(local = [A], local = [B])] + fn init(_: init::Context) {} +} diff --git a/macros/ui/init-double-local.stderr b/macros/ui/init-double-local.stderr new file mode 100644 index 0000000..5ffd2c1 --- /dev/null +++ b/macros/ui/init-double-local.stderr @@ -0,0 +1,5 @@ +error: argument appears more than once + --> $DIR/init-double-local.rs:5:25 + | +5 | #[init(local = [A], local = [B])] + | ^^^^^ diff --git a/macros/ui/init-double-shared.rs b/macros/ui/init-double-shared.rs new file mode 100644 index 0000000..4503c87 --- /dev/null +++ b/macros/ui/init-double-shared.rs @@ -0,0 +1,7 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[init(shared = [A], shared = [B])] + fn init(_: init::Context) {} +} diff --git a/macros/ui/init-double-shared.stderr b/macros/ui/init-double-shared.stderr new file mode 100644 index 0000000..b6b1f6d --- /dev/null +++ b/macros/ui/init-double-shared.stderr @@ -0,0 +1,5 @@ +error: unexpected argument + --> $DIR/init-double-shared.rs:5:12 + | +5 | #[init(shared = [A], shared = [B])] + | ^^^^^^ diff --git a/macros/ui/init-input.rs b/macros/ui/init-input.rs new file mode 100644 index 0000000..ac2a1bd --- /dev/null +++ b/macros/ui/init-input.rs @@ -0,0 +1,13 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[shared] + struct Shared {} + + #[local] + struct Local {} + + #[init] + fn init(_: init::Context, _undef: u32) -> (Shared, Local, init::Monotonics) {} +} diff --git a/macros/ui/init-input.stderr b/macros/ui/init-input.stderr new file mode 100644 index 0000000..983c469 --- /dev/null +++ b/macros/ui/init-input.stderr @@ -0,0 +1,5 @@ +error: the `#[init]` function must have signature `fn(init::Context) -> (Shared resources struct, Local resources struct, init::Monotonics)` + --> $DIR/init-input.rs:12:8 + | +12 | fn init(_: init::Context, _undef: u32) -> (Shared, Local, init::Monotonics) {} + | ^^^^ diff --git a/macros/ui/init-no-context.rs b/macros/ui/init-no-context.rs new file mode 100644 index 0000000..a74093a --- /dev/null +++ b/macros/ui/init-no-context.rs @@ -0,0 +1,13 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[shared] + struct Shared {} + + #[local] + struct Local {} + + #[init] + fn init() -> (Shared, Local, init::Monotonics) {} +} diff --git a/macros/ui/init-no-context.stderr b/macros/ui/init-no-context.stderr new file mode 100644 index 0000000..742e2ab --- /dev/null +++ b/macros/ui/init-no-context.stderr @@ -0,0 +1,5 @@ +error: the `#[init]` function must have signature `fn(init::Context) -> (Shared resources struct, Local resources struct, init::Monotonics)` + --> $DIR/init-no-context.rs:12:8 + | +12 | fn init() -> (Shared, Local, init::Monotonics) {} + | ^^^^ diff --git a/macros/ui/init-output.rs b/macros/ui/init-output.rs new file mode 100644 index 0000000..7057c95 --- /dev/null +++ b/macros/ui/init-output.rs @@ -0,0 +1,9 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[init] + fn init(_: init::Context) -> u32 { + 0 + } +} diff --git a/macros/ui/init-output.stderr b/macros/ui/init-output.stderr new file mode 100644 index 0000000..03e982c --- /dev/null +++ b/macros/ui/init-output.stderr @@ -0,0 +1,5 @@ +error: the `#[init]` function must have signature `fn(init::Context) -> (Shared resources struct, Local resources struct, init::Monotonics)` + --> $DIR/init-output.rs:6:8 + | +6 | fn init(_: init::Context) -> u32 { + | ^^^^ diff --git a/macros/ui/init-pub.rs b/macros/ui/init-pub.rs new file mode 100644 index 0000000..43375e4 --- /dev/null +++ b/macros/ui/init-pub.rs @@ -0,0 +1,13 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[shared] + struct Shared {} + + #[local] + struct Local {} + + #[init] + pub fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {} +} diff --git a/macros/ui/init-pub.stderr b/macros/ui/init-pub.stderr new file mode 100644 index 0000000..eb68e1e --- /dev/null +++ b/macros/ui/init-pub.stderr @@ -0,0 +1,5 @@ +error: the `#[init]` function must have signature `fn(init::Context) -> (Shared resources struct, Local resources struct, init::Monotonics)` + --> $DIR/init-pub.rs:12:12 + | +12 | pub fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {} + | ^^^^ diff --git a/macros/ui/init-unsafe.rs b/macros/ui/init-unsafe.rs new file mode 100644 index 0000000..b5d391d --- /dev/null +++ b/macros/ui/init-unsafe.rs @@ -0,0 +1,7 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[init] + unsafe fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {} +} diff --git a/macros/ui/init-unsafe.stderr b/macros/ui/init-unsafe.stderr new file mode 100644 index 0000000..2a48533 --- /dev/null +++ b/macros/ui/init-unsafe.stderr @@ -0,0 +1,5 @@ +error: the `#[init]` function must have signature `fn(init::Context) -> (Shared resources struct, Local resources struct, init::Monotonics)` + --> $DIR/init-unsafe.rs:6:15 + | +6 | unsafe fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {} + | ^^^^ diff --git a/macros/ui/interrupt-double.rs b/macros/ui/interrupt-double.rs new file mode 100644 index 0000000..1133c5c --- /dev/null +++ b/macros/ui/interrupt-double.rs @@ -0,0 +1,10 @@ +#![no_main] + +#[rtic_macros::mock_app(parse_binds, device = mock)] +mod app { + #[task(binds = UART0)] + fn foo(_: foo::Context) {} + + #[task(binds = UART0)] + fn bar(_: bar::Context) {} +} diff --git a/macros/ui/interrupt-double.stderr b/macros/ui/interrupt-double.stderr new file mode 100644 index 0000000..62b979b --- /dev/null +++ b/macros/ui/interrupt-double.stderr @@ -0,0 +1,5 @@ +error: this interrupt is already bound + --> $DIR/interrupt-double.rs:8:20 + | +8 | #[task(binds = UART0)] + | ^^^^^ diff --git a/macros/ui/local-collision-2.rs b/macros/ui/local-collision-2.rs new file mode 100644 index 0000000..7bd092c --- /dev/null +++ b/macros/ui/local-collision-2.rs @@ -0,0 +1,21 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[shared] + struct Shared {} + + #[local] + struct Local { + a: u32, + } + + fn bar(_: bar::Context) {} + + #[task(local = [a: u8 = 3])] + fn bar(_: bar::Context) {} + + #[init(local = [a: u16 = 2])] + fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {} +} + diff --git a/macros/ui/local-collision-2.stderr b/macros/ui/local-collision-2.stderr new file mode 100644 index 0000000..1e4c5fa --- /dev/null +++ b/macros/ui/local-collision-2.stderr @@ -0,0 +1,17 @@ +error: Local resource "a" is used by multiple tasks or collides with multiple definitions + --> $DIR/local-collision-2.rs:10:9 + | +10 | a: u32, + | ^ + +error: Local resource "a" is used by multiple tasks or collides with multiple definitions + --> $DIR/local-collision-2.rs:18:21 + | +18 | #[init(local = [a: u16 = 2])] + | ^ + +error: Local resource "a" is used by multiple tasks or collides with multiple definitions + --> $DIR/local-collision-2.rs:15:21 + | +15 | #[task(local = [a: u8 = 3])] + | ^ diff --git a/macros/ui/local-collision.rs b/macros/ui/local-collision.rs new file mode 100644 index 0000000..7dbe976 --- /dev/null +++ b/macros/ui/local-collision.rs @@ -0,0 +1,21 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[shared] + struct Shared {} + + #[local] + struct Local { + a: u32, + } + + #[task(local = [a])] + fn foo(_: foo::Context) {} + + #[task(local = [a: u8 = 3])] + fn bar(_: bar::Context) {} + + #[init] + fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {} +} diff --git a/macros/ui/local-collision.stderr b/macros/ui/local-collision.stderr new file mode 100644 index 0000000..1ba1da9 --- /dev/null +++ b/macros/ui/local-collision.stderr @@ -0,0 +1,11 @@ +error: Local resource "a" is used by multiple tasks or collides with multiple definitions + --> $DIR/local-collision.rs:10:9 + | +10 | a: u32, + | ^ + +error: Local resource "a" is used by multiple tasks or collides with multiple definitions + --> $DIR/local-collision.rs:16:21 + | +16 | #[task(local = [a: u8 = 3])] + | ^ diff --git a/macros/ui/local-malformed-1.rs b/macros/ui/local-malformed-1.rs new file mode 100644 index 0000000..7efcd9c --- /dev/null +++ b/macros/ui/local-malformed-1.rs @@ -0,0 +1,16 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[shared] + struct Shared {} + + #[local] + struct Local {} + + #[task(local = [a:])] + fn foo(_: foo::Context) {} + + #[init] + fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {} +} diff --git a/macros/ui/local-malformed-1.stderr b/macros/ui/local-malformed-1.stderr new file mode 100644 index 0000000..d15c324 --- /dev/null +++ b/macros/ui/local-malformed-1.stderr @@ -0,0 +1,5 @@ +error: unexpected end of input, expected one of: `for`, parentheses, `fn`, `unsafe`, `extern`, identifier, `::`, `<`, square brackets, `*`, `&`, `!`, `impl`, `_`, lifetime + --> ui/local-malformed-1.rs:11:23 + | +11 | #[task(local = [a:])] + | ^ diff --git a/macros/ui/local-malformed-2.rs b/macros/ui/local-malformed-2.rs new file mode 100644 index 0000000..ce5f891 --- /dev/null +++ b/macros/ui/local-malformed-2.rs @@ -0,0 +1,16 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[shared] + struct Shared {} + + #[local] + struct Local {} + + #[task(local = [a: u32])] + fn foo(_: foo::Context) {} + + #[init] + fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {} +} diff --git a/macros/ui/local-malformed-2.stderr b/macros/ui/local-malformed-2.stderr new file mode 100644 index 0000000..ceb0405 --- /dev/null +++ b/macros/ui/local-malformed-2.stderr @@ -0,0 +1,5 @@ +error: malformed, expected 'IDENT: TYPE = EXPR' + --> ui/local-malformed-2.rs:11:21 + | +11 | #[task(local = [a: u32])] + | ^ diff --git a/macros/ui/local-malformed-3.rs b/macros/ui/local-malformed-3.rs new file mode 100644 index 0000000..935dc2c --- /dev/null +++ b/macros/ui/local-malformed-3.rs @@ -0,0 +1,16 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[shared] + struct Shared {} + + #[local] + struct Local {} + + #[task(local = [a: u32 =])] + fn foo(_: foo::Context) {} + + #[init] + fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {} +} diff --git a/macros/ui/local-malformed-3.stderr b/macros/ui/local-malformed-3.stderr new file mode 100644 index 0000000..61af4f3 --- /dev/null +++ b/macros/ui/local-malformed-3.stderr @@ -0,0 +1,5 @@ +error: unexpected end of input, expected expression + --> ui/local-malformed-3.rs:11:29 + | +11 | #[task(local = [a: u32 =])] + | ^ diff --git a/macros/ui/local-malformed-4.rs b/macros/ui/local-malformed-4.rs new file mode 100644 index 0000000..49661b5 --- /dev/null +++ b/macros/ui/local-malformed-4.rs @@ -0,0 +1,16 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[shared] + struct Shared {} + + #[local] + struct Local {} + + #[task(local = [a = u32])] + fn foo(_: foo::Context) {} + + #[init] + fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {} +} diff --git a/macros/ui/local-malformed-4.stderr b/macros/ui/local-malformed-4.stderr new file mode 100644 index 0000000..0f7d9e7 --- /dev/null +++ b/macros/ui/local-malformed-4.stderr @@ -0,0 +1,5 @@ +error: malformed, expected a type + --> ui/local-malformed-4.rs:11:21 + | +11 | #[task(local = [a = u32])] + | ^ diff --git a/macros/ui/local-not-declared.rs b/macros/ui/local-not-declared.rs new file mode 100644 index 0000000..5a38b3d --- /dev/null +++ b/macros/ui/local-not-declared.rs @@ -0,0 +1,16 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[shared] + struct Shared {} + + #[local] + struct Local {} + + #[task(local = [A])] + fn foo(_: foo::Context) {} + + #[init] + fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {} +} diff --git a/macros/ui/local-not-declared.stderr b/macros/ui/local-not-declared.stderr new file mode 100644 index 0000000..540b4bb --- /dev/null +++ b/macros/ui/local-not-declared.stderr @@ -0,0 +1,5 @@ +error: this local resource has NOT been declared + --> $DIR/local-not-declared.rs:11:21 + | +11 | #[task(local = [A])] + | ^ diff --git a/macros/ui/local-pub.rs b/macros/ui/local-pub.rs new file mode 100644 index 0000000..8c51754 --- /dev/null +++ b/macros/ui/local-pub.rs @@ -0,0 +1,9 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[local] + struct Local { + pub x: u32, + } +} diff --git a/macros/ui/local-pub.stderr b/macros/ui/local-pub.stderr new file mode 100644 index 0000000..041bc59 --- /dev/null +++ b/macros/ui/local-pub.stderr @@ -0,0 +1,5 @@ +error: this field must have inherited / private visibility + --> $DIR/local-pub.rs:7:13 + | +7 | pub x: u32, + | ^ diff --git a/macros/ui/local-shared-attribute.rs b/macros/ui/local-shared-attribute.rs new file mode 100644 index 0000000..1ccce4a --- /dev/null +++ b/macros/ui/local-shared-attribute.rs @@ -0,0 +1,14 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[task(local = [ + #[test] + a: u32 = 0, // Ok + #[test] + b, // Error + ])] + fn foo(_: foo::Context) { + + } +} diff --git a/macros/ui/local-shared-attribute.stderr b/macros/ui/local-shared-attribute.stderr new file mode 100644 index 0000000..5c15fb5 --- /dev/null +++ b/macros/ui/local-shared-attribute.stderr @@ -0,0 +1,5 @@ +error: attributes are not supported here + --> $DIR/local-shared-attribute.rs:8:9 + | +8 | #[test] + | ^ diff --git a/macros/ui/local-shared.rs b/macros/ui/local-shared.rs new file mode 100644 index 0000000..f6fb491 --- /dev/null +++ b/macros/ui/local-shared.rs @@ -0,0 +1,28 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[shared] + struct Shared {} + + #[local] + struct Local { + l1: u32, + l2: u32, + } + + #[init] + fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {} + + // l2 ok + #[idle(local = [l2])] + fn idle(cx: idle::Context) -> ! {} + + // l1 rejected (not local) + #[task(priority = 1, local = [l1])] + fn uart0(cx: uart0::Context) {} + + // l1 rejected (not lock_free) + #[task(priority = 2, local = [l1])] + fn uart1(cx: uart1::Context) {} +} diff --git a/macros/ui/local-shared.stderr b/macros/ui/local-shared.stderr new file mode 100644 index 0000000..0d22db3 --- /dev/null +++ b/macros/ui/local-shared.stderr @@ -0,0 +1,11 @@ +error: Local resource "l1" is used by multiple tasks or collides with multiple definitions + --> $DIR/local-shared.rs:22:35 + | +22 | #[task(priority = 1, local = [l1])] + | ^^ + +error: Local resource "l1" is used by multiple tasks or collides with multiple definitions + --> $DIR/local-shared.rs:26:35 + | +26 | #[task(priority = 2, local = [l1])] + | ^^ diff --git a/macros/ui/monotonic-binds-collision-task.rs b/macros/ui/monotonic-binds-collision-task.rs new file mode 100644 index 0000000..57c59c1 --- /dev/null +++ b/macros/ui/monotonic-binds-collision-task.rs @@ -0,0 +1,10 @@ +#![no_main] + +#[rtic_macros::mock_app(parse_extern_interrupt, parse_binds, device = mock)] +mod app { + #[monotonic(binds = Tim1)] + type Fast1 = hal::Tim1Monotonic; + + #[task(binds = Tim1)] + fn foo(_: foo::Context) {} +} diff --git a/macros/ui/monotonic-binds-collision-task.stderr b/macros/ui/monotonic-binds-collision-task.stderr new file mode 100644 index 0000000..8f84986 --- /dev/null +++ b/macros/ui/monotonic-binds-collision-task.stderr @@ -0,0 +1,5 @@ +error: this interrupt is already bound + --> $DIR/monotonic-binds-collision-task.rs:8:20 + | +8 | #[task(binds = Tim1)] + | ^^^^ diff --git a/macros/ui/monotonic-binds-collision.rs b/macros/ui/monotonic-binds-collision.rs new file mode 100644 index 0000000..4e54814 --- /dev/null +++ b/macros/ui/monotonic-binds-collision.rs @@ -0,0 +1,10 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[monotonic(binds = Tim1)] + type Fast1 = hal::Tim1Monotonic; + + #[monotonic(binds = Tim1)] + type Fast2 = hal::Tim2Monotonic; +} diff --git a/macros/ui/monotonic-binds-collision.stderr b/macros/ui/monotonic-binds-collision.stderr new file mode 100644 index 0000000..62b764b --- /dev/null +++ b/macros/ui/monotonic-binds-collision.stderr @@ -0,0 +1,5 @@ +error: this interrupt is already bound + --> $DIR/monotonic-binds-collision.rs:8:25 + | +8 | #[monotonic(binds = Tim1)] + | ^^^^ diff --git a/macros/ui/monotonic-double-binds.rs b/macros/ui/monotonic-double-binds.rs new file mode 100644 index 0000000..1705dc4 --- /dev/null +++ b/macros/ui/monotonic-double-binds.rs @@ -0,0 +1,7 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[monotonic(binds = Tim1, binds = Tim2)] + type Fast = hal::Tim1Monotonic; +} diff --git a/macros/ui/monotonic-double-binds.stderr b/macros/ui/monotonic-double-binds.stderr new file mode 100644 index 0000000..c7313df --- /dev/null +++ b/macros/ui/monotonic-double-binds.stderr @@ -0,0 +1,5 @@ +error: argument appears more than once + --> $DIR/monotonic-double-binds.rs:5:31 + | +5 | #[monotonic(binds = Tim1, binds = Tim2)] + | ^^^^^ diff --git a/macros/ui/monotonic-double-default.rs b/macros/ui/monotonic-double-default.rs new file mode 100644 index 0000000..dc4eac6 --- /dev/null +++ b/macros/ui/monotonic-double-default.rs @@ -0,0 +1,7 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[monotonic(binds = Tim1, default = true, default = false)] + type Fast = hal::Tim1Monotonic; +} diff --git a/macros/ui/monotonic-double-default.stderr b/macros/ui/monotonic-double-default.stderr new file mode 100644 index 0000000..9819d04 --- /dev/null +++ b/macros/ui/monotonic-double-default.stderr @@ -0,0 +1,5 @@ +error: argument appears more than once + --> $DIR/monotonic-double-default.rs:5:47 + | +5 | #[monotonic(binds = Tim1, default = true, default = false)] + | ^^^^^^^ diff --git a/macros/ui/monotonic-double-prio.rs b/macros/ui/monotonic-double-prio.rs new file mode 100644 index 0000000..4330ddb --- /dev/null +++ b/macros/ui/monotonic-double-prio.rs @@ -0,0 +1,7 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[monotonic(binds = Tim1, priority = 1, priority = 2)] + type Fast = hal::Tim1Monotonic; +} diff --git a/macros/ui/monotonic-double-prio.stderr b/macros/ui/monotonic-double-prio.stderr new file mode 100644 index 0000000..fa888e2 --- /dev/null +++ b/macros/ui/monotonic-double-prio.stderr @@ -0,0 +1,5 @@ +error: argument appears more than once + --> $DIR/monotonic-double-prio.rs:5:45 + | +5 | #[monotonic(binds = Tim1, priority = 1, priority = 2)] + | ^^^^^^^^ diff --git a/macros/ui/monotonic-double.rs b/macros/ui/monotonic-double.rs new file mode 100644 index 0000000..3c43fae --- /dev/null +++ b/macros/ui/monotonic-double.rs @@ -0,0 +1,10 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[monotonic(binds = Tim1)] + type Fast = hal::Tim1Monotonic; + + #[monotonic(binds = Tim1)] + type Fast = hal::Tim1Monotonic; +} diff --git a/macros/ui/monotonic-double.stderr b/macros/ui/monotonic-double.stderr new file mode 100644 index 0000000..9fab84c --- /dev/null +++ b/macros/ui/monotonic-double.stderr @@ -0,0 +1,5 @@ +error: `#[monotonic(...)]` on a specific type must appear at most once + --> ui/monotonic-double.rs:9:10 + | +9 | type Fast = hal::Tim1Monotonic; + | ^^^^ diff --git a/macros/ui/monotonic-name-collision.rs b/macros/ui/monotonic-name-collision.rs new file mode 100644 index 0000000..d8d4431 --- /dev/null +++ b/macros/ui/monotonic-name-collision.rs @@ -0,0 +1,10 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[monotonic(binds = Tim1)] + type Fast1 = hal::Tim1Monotonic; + + #[monotonic(binds = Tim2)] + type Fast1 = hal::Tim2Monotonic; +} diff --git a/macros/ui/monotonic-name-collision.stderr b/macros/ui/monotonic-name-collision.stderr new file mode 100644 index 0000000..6557ee5 --- /dev/null +++ b/macros/ui/monotonic-name-collision.stderr @@ -0,0 +1,5 @@ +error: `#[monotonic(...)]` on a specific type must appear at most once + --> ui/monotonic-name-collision.rs:9:10 + | +9 | type Fast1 = hal::Tim2Monotonic; + | ^^^^^ diff --git a/macros/ui/monotonic-no-binds.rs b/macros/ui/monotonic-no-binds.rs new file mode 100644 index 0000000..462d73e --- /dev/null +++ b/macros/ui/monotonic-no-binds.rs @@ -0,0 +1,7 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[monotonic()] + type Fast = hal::Tim1Monotonic; +} diff --git a/macros/ui/monotonic-no-binds.stderr b/macros/ui/monotonic-no-binds.stderr new file mode 100644 index 0000000..0ef7b60 --- /dev/null +++ b/macros/ui/monotonic-no-binds.stderr @@ -0,0 +1,5 @@ +error: `binds = ...` is missing + --> $DIR/monotonic-no-binds.rs:5:17 + | +5 | #[monotonic()] + | ^ diff --git a/macros/ui/monotonic-no-paran.rs b/macros/ui/monotonic-no-paran.rs new file mode 100644 index 0000000..e294bc8 --- /dev/null +++ b/macros/ui/monotonic-no-paran.rs @@ -0,0 +1,8 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[monotonic] + type Fast = hal::Tim1Monotonic; +} + diff --git a/macros/ui/monotonic-no-paran.stderr b/macros/ui/monotonic-no-paran.stderr new file mode 100644 index 0000000..c2b32c5 --- /dev/null +++ b/macros/ui/monotonic-no-paran.stderr @@ -0,0 +1,5 @@ +error: expected opening ( in #[monotonic( ... )] + --> ui/monotonic-no-paran.rs:5:7 + | +5 | #[monotonic] + | ^^^^^^^^^ diff --git a/macros/ui/monotonic-timer-collision.rs b/macros/ui/monotonic-timer-collision.rs new file mode 100644 index 0000000..5663ad7 --- /dev/null +++ b/macros/ui/monotonic-timer-collision.rs @@ -0,0 +1,10 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[monotonic(binds = Tim1)] + type Fast1 = hal::Tim1Monotonic; + + #[monotonic(binds = Tim2)] + type Fast2 = hal::Tim1Monotonic; +} diff --git a/macros/ui/monotonic-timer-collision.stderr b/macros/ui/monotonic-timer-collision.stderr new file mode 100644 index 0000000..239b96b --- /dev/null +++ b/macros/ui/monotonic-timer-collision.stderr @@ -0,0 +1,5 @@ +error: this type is already used by another monotonic + --> $DIR/monotonic-timer-collision.rs:9:18 + | +9 | type Fast2 = hal::Tim1Monotonic; + | ^^^ diff --git a/macros/ui/monotonic-with-attrs.rs b/macros/ui/monotonic-with-attrs.rs new file mode 100644 index 0000000..7c63fbb --- /dev/null +++ b/macros/ui/monotonic-with-attrs.rs @@ -0,0 +1,8 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[no_mangle] + #[monotonic(binds = Tim1)] + type Fast = hal::Tim1Monotonic; +} diff --git a/macros/ui/monotonic-with-attrs.stderr b/macros/ui/monotonic-with-attrs.stderr new file mode 100644 index 0000000..62655d8 --- /dev/null +++ b/macros/ui/monotonic-with-attrs.stderr @@ -0,0 +1,5 @@ +error: Monotonic does not support attributes other than `#[cfg]` + --> $DIR/monotonic-with-attrs.rs:5:7 + | +5 | #[no_mangle] + | ^^^^^^^^^ diff --git a/macros/ui/pub-local.stderr b/macros/ui/pub-local.stderr new file mode 100644 index 0000000..dee818c --- /dev/null +++ b/macros/ui/pub-local.stderr @@ -0,0 +1,5 @@ +error: this field must have inherited / private visibility + --> $DIR/pub-local.rs:7:13 + | +7 | pub x: u32, + | ^ diff --git a/macros/ui/pub-shared.stderr b/macros/ui/pub-shared.stderr new file mode 100644 index 0000000..0fdb1ff --- /dev/null +++ b/macros/ui/pub-shared.stderr @@ -0,0 +1,5 @@ +error: this field must have inherited / private visibility + --> $DIR/pub-shared.rs:7:13 + | +7 | pub x: u32, + | ^ diff --git a/macros/ui/shared-lock-free.rs b/macros/ui/shared-lock-free.rs new file mode 100644 index 0000000..c7f8a16 --- /dev/null +++ b/macros/ui/shared-lock-free.rs @@ -0,0 +1,38 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[shared] + struct Shared { + // An exclusive, early resource + #[lock_free] + e1: u32, + + // An exclusive, late resource + #[lock_free] + e2: u32, + } + + #[local] + struct Local {} + + #[init] + fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {} + + // e2 ok + #[idle(shared = [e2])] + fn idle(cx: idle::Context) -> ! { + debug::exit(debug::EXIT_SUCCESS); + loop {} + } + + // e1 rejected (not lock_free) + #[task(priority = 1, shared = [e1])] + fn uart0(cx: uart0::Context) { + *cx.resources.e1 += 10; + } + + // e1 rejected (not lock_free) + #[task(priority = 2, shared = [e1])] + fn uart1(cx: uart1::Context) {} +} diff --git a/macros/ui/shared-lock-free.stderr b/macros/ui/shared-lock-free.stderr new file mode 100644 index 0000000..c6820e8 --- /dev/null +++ b/macros/ui/shared-lock-free.stderr @@ -0,0 +1,17 @@ +error: Lock free shared resource "e1" is used by tasks at different priorities + --> $DIR/shared-lock-free.rs:9:9 + | +9 | e1: u32, + | ^^ + +error: Shared resource "e1" is declared lock free but used by tasks at different priorities + --> $DIR/shared-lock-free.rs:30:36 + | +30 | #[task(priority = 1, shared = [e1])] + | ^^ + +error: Shared resource "e1" is declared lock free but used by tasks at different priorities + --> $DIR/shared-lock-free.rs:36:36 + | +36 | #[task(priority = 2, shared = [e1])] + | ^^ diff --git a/macros/ui/shared-not-declared.rs b/macros/ui/shared-not-declared.rs new file mode 100644 index 0000000..aca4178 --- /dev/null +++ b/macros/ui/shared-not-declared.rs @@ -0,0 +1,16 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[shared] + struct Shared {} + + #[local] + struct Local {} + + #[task(shared = [A])] + fn foo(_: foo::Context) {} + + #[init] + fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {} +} diff --git a/macros/ui/shared-not-declared.stderr b/macros/ui/shared-not-declared.stderr new file mode 100644 index 0000000..c174251 --- /dev/null +++ b/macros/ui/shared-not-declared.stderr @@ -0,0 +1,5 @@ +error: this shared resource has NOT been declared + --> $DIR/shared-not-declared.rs:11:22 + | +11 | #[task(shared = [A])] + | ^ diff --git a/macros/ui/shared-pub.rs b/macros/ui/shared-pub.rs new file mode 100644 index 0000000..10351fd --- /dev/null +++ b/macros/ui/shared-pub.rs @@ -0,0 +1,9 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[shared] + struct Shared { + pub x: u32, + } +} diff --git a/macros/ui/shared-pub.stderr b/macros/ui/shared-pub.stderr new file mode 100644 index 0000000..8f761c6 --- /dev/null +++ b/macros/ui/shared-pub.stderr @@ -0,0 +1,5 @@ +error: this field must have inherited / private visibility + --> $DIR/shared-pub.rs:7:13 + | +7 | pub x: u32, + | ^ diff --git a/macros/ui/task-bind.rs b/macros/ui/task-bind.rs new file mode 100644 index 0000000..de60524 --- /dev/null +++ b/macros/ui/task-bind.rs @@ -0,0 +1,7 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[task(binds = UART0)] + fn foo(_: foo::Context) {} +} diff --git a/macros/ui/task-bind.stderr b/macros/ui/task-bind.stderr new file mode 100644 index 0000000..60cfdc8 --- /dev/null +++ b/macros/ui/task-bind.stderr @@ -0,0 +1,5 @@ +error: Unexpected bind in task argument. Binds are only parsed if Settings::parse_binds is set. + --> $DIR/task-bind.rs:5:12 + | +5 | #[task(binds = UART0)] + | ^^^^^ diff --git a/macros/ui/task-divergent.rs b/macros/ui/task-divergent.rs new file mode 100644 index 0000000..5a471f3 --- /dev/null +++ b/macros/ui/task-divergent.rs @@ -0,0 +1,9 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[task] + fn foo(_: foo::Context) -> ! { + loop {} + } +} diff --git a/macros/ui/task-divergent.stderr b/macros/ui/task-divergent.stderr new file mode 100644 index 0000000..b25ca5d --- /dev/null +++ b/macros/ui/task-divergent.stderr @@ -0,0 +1,5 @@ +error: this task handler must have type signature `(async) fn(foo::Context, ..)` + --> ui/task-divergent.rs:6:8 + | +6 | fn foo(_: foo::Context) -> ! { + | ^^^ diff --git a/macros/ui/task-double-capacity.rs b/macros/ui/task-double-capacity.rs new file mode 100644 index 0000000..806d973 --- /dev/null +++ b/macros/ui/task-double-capacity.rs @@ -0,0 +1,7 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[task(capacity = 1, capacity = 2)] + fn foo(_: foo::Context) {} +} diff --git a/macros/ui/task-double-capacity.stderr b/macros/ui/task-double-capacity.stderr new file mode 100644 index 0000000..f73bca5 --- /dev/null +++ b/macros/ui/task-double-capacity.stderr @@ -0,0 +1,5 @@ +error: argument appears more than once + --> $DIR/task-double-capacity.rs:5:26 + | +5 | #[task(capacity = 1, capacity = 2)] + | ^^^^^^^^ diff --git a/macros/ui/task-double-local.rs b/macros/ui/task-double-local.rs new file mode 100644 index 0000000..2e465d7 --- /dev/null +++ b/macros/ui/task-double-local.rs @@ -0,0 +1,7 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[task(local = [A], local = [B])] + fn foo(_: foo::Context) {} +} diff --git a/macros/ui/task-double-local.stderr b/macros/ui/task-double-local.stderr new file mode 100644 index 0000000..654ed33 --- /dev/null +++ b/macros/ui/task-double-local.stderr @@ -0,0 +1,5 @@ +error: argument appears more than once + --> $DIR/task-double-local.rs:5:25 + | +5 | #[task(local = [A], local = [B])] + | ^^^^^ diff --git a/macros/ui/task-double-priority.rs b/macros/ui/task-double-priority.rs new file mode 100644 index 0000000..0a2ef0d --- /dev/null +++ b/macros/ui/task-double-priority.rs @@ -0,0 +1,7 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[task(priority = 1, priority = 2)] + fn foo(_: foo::Context) {} +} diff --git a/macros/ui/task-double-priority.stderr b/macros/ui/task-double-priority.stderr new file mode 100644 index 0000000..3d06dc6 --- /dev/null +++ b/macros/ui/task-double-priority.stderr @@ -0,0 +1,5 @@ +error: argument appears more than once + --> $DIR/task-double-priority.rs:5:26 + | +5 | #[task(priority = 1, priority = 2)] + | ^^^^^^^^ diff --git a/macros/ui/task-double-shared.rs b/macros/ui/task-double-shared.rs new file mode 100644 index 0000000..3b4d411 --- /dev/null +++ b/macros/ui/task-double-shared.rs @@ -0,0 +1,7 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[task(shared = [A], shared = [B])] + fn foo(_: foo::Context) {} +} diff --git a/macros/ui/task-double-shared.stderr b/macros/ui/task-double-shared.stderr new file mode 100644 index 0000000..6952f06 --- /dev/null +++ b/macros/ui/task-double-shared.stderr @@ -0,0 +1,5 @@ +error: argument appears more than once + --> $DIR/task-double-shared.rs:5:26 + | +5 | #[task(shared = [A], shared = [B])] + | ^^^^^^ diff --git a/macros/ui/task-idle.rs b/macros/ui/task-idle.rs new file mode 100644 index 0000000..3be6e28 --- /dev/null +++ b/macros/ui/task-idle.rs @@ -0,0 +1,13 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[idle] + fn foo(_: foo::Context) -> ! { + loop {} + } + + // name collides with `#[idle]` function + #[task] + fn foo(_: foo::Context) {} +} diff --git a/macros/ui/task-idle.stderr b/macros/ui/task-idle.stderr new file mode 100644 index 0000000..ba4fc94 --- /dev/null +++ b/macros/ui/task-idle.stderr @@ -0,0 +1,5 @@ +error: this identifier has already been used + --> $DIR/task-idle.rs:12:8 + | +12 | fn foo(_: foo::Context) {} + | ^^^ diff --git a/macros/ui/task-init.rs b/macros/ui/task-init.rs new file mode 100644 index 0000000..bab3805 --- /dev/null +++ b/macros/ui/task-init.rs @@ -0,0 +1,17 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[shared] + struct Shared {} + + #[local] + struct Local {} + + #[init] + fn foo(_: foo::Context) -> (Shared, Local, foo::Monotonics) {} + + // name collides with `#[idle]` function + #[task] + fn foo(_: foo::Context) {} +} diff --git a/macros/ui/task-init.stderr b/macros/ui/task-init.stderr new file mode 100644 index 0000000..911af37 --- /dev/null +++ b/macros/ui/task-init.stderr @@ -0,0 +1,5 @@ +error: this identifier has already been used + --> $DIR/task-init.rs:16:8 + | +16 | fn foo(_: foo::Context) {} + | ^^^ diff --git a/macros/ui/task-interrupt-same-prio-spawn.rs b/macros/ui/task-interrupt-same-prio-spawn.rs new file mode 100644 index 0000000..741e60e --- /dev/null +++ b/macros/ui/task-interrupt-same-prio-spawn.rs @@ -0,0 +1,7 @@ +#![no_main] + +#[rtic_macros::mock_app(parse_binds, device = mock)] +mod app { + #[task(binds = SysTick, only_same_priority_spawn_please_fix_me)] + fn foo(_: foo::Context) {} +} diff --git a/macros/ui/task-interrupt-same-prio-spawn.stderr b/macros/ui/task-interrupt-same-prio-spawn.stderr new file mode 100644 index 0000000..171b850 --- /dev/null +++ b/macros/ui/task-interrupt-same-prio-spawn.stderr @@ -0,0 +1,5 @@ +error: hardware tasks are not allowed to be spawned, `only_same_priority_spawn_please_fix_me` is only for software tasks + --> ui/task-interrupt-same-prio-spawn.rs:5:29 + | +5 | #[task(binds = SysTick, only_same_priority_spawn_please_fix_me)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/macros/ui/task-interrupt.rs b/macros/ui/task-interrupt.rs new file mode 100644 index 0000000..71fef9a --- /dev/null +++ b/macros/ui/task-interrupt.rs @@ -0,0 +1,10 @@ +#![no_main] + +#[rtic_macros::mock_app(parse_binds, device = mock)] +mod app { + #[task(binds = SysTick)] + fn foo(_: foo::Context) {} + + #[task] + fn foo(_: foo::Context) {} +} diff --git a/macros/ui/task-interrupt.stderr b/macros/ui/task-interrupt.stderr new file mode 100644 index 0000000..6efb0f9 --- /dev/null +++ b/macros/ui/task-interrupt.stderr @@ -0,0 +1,5 @@ +error: this task is defined multiple times + --> $DIR/task-interrupt.rs:9:8 + | +9 | fn foo(_: foo::Context) {} + | ^^^ diff --git a/macros/ui/task-no-context.rs b/macros/ui/task-no-context.rs new file mode 100644 index 0000000..e2da625 --- /dev/null +++ b/macros/ui/task-no-context.rs @@ -0,0 +1,7 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[task] + fn foo() {} +} diff --git a/macros/ui/task-no-context.stderr b/macros/ui/task-no-context.stderr new file mode 100644 index 0000000..8bf3438 --- /dev/null +++ b/macros/ui/task-no-context.stderr @@ -0,0 +1,5 @@ +error: this task handler must have type signature `(async) fn(foo::Context, ..)` + --> ui/task-no-context.rs:6:8 + | +6 | fn foo() {} + | ^^^ diff --git a/macros/ui/task-priority-too-high.rs b/macros/ui/task-priority-too-high.rs new file mode 100644 index 0000000..8c32beb --- /dev/null +++ b/macros/ui/task-priority-too-high.rs @@ -0,0 +1,7 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[task(priority = 256)] + fn foo(_: foo::Context) {} +} diff --git a/macros/ui/task-priority-too-high.stderr b/macros/ui/task-priority-too-high.stderr new file mode 100644 index 0000000..5790c88 --- /dev/null +++ b/macros/ui/task-priority-too-high.stderr @@ -0,0 +1,5 @@ +error: this literal must be in the range 0...255 + --> ui/task-priority-too-high.rs:5:23 + | +5 | #[task(priority = 256)] + | ^^^ diff --git a/macros/ui/task-priority-too-low.rs b/macros/ui/task-priority-too-low.rs new file mode 100644 index 0000000..beed4de --- /dev/null +++ b/macros/ui/task-priority-too-low.rs @@ -0,0 +1,7 @@ +#![no_main] + +#[rtic_macros::mock_app(parse_binds, device = mock)] +mod app { + #[task(binds = UART0, priority = 0)] + fn foo(_: foo::Context) {} +} diff --git a/macros/ui/task-priority-too-low.stderr b/macros/ui/task-priority-too-low.stderr new file mode 100644 index 0000000..85c8660 --- /dev/null +++ b/macros/ui/task-priority-too-low.stderr @@ -0,0 +1,5 @@ +error: hardware tasks are not allowed to be at priority 0 + --> ui/task-priority-too-low.rs:5:38 + | +5 | #[task(binds = UART0, priority = 0)] + | ^ diff --git a/macros/ui/task-pub.rs b/macros/ui/task-pub.rs new file mode 100644 index 0000000..3cbd523 --- /dev/null +++ b/macros/ui/task-pub.rs @@ -0,0 +1,7 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[task] + pub fn foo(_: foo::Context) {} +} diff --git a/macros/ui/task-pub.stderr b/macros/ui/task-pub.stderr new file mode 100644 index 0000000..56e09b1 --- /dev/null +++ b/macros/ui/task-pub.stderr @@ -0,0 +1,5 @@ +error: this task handler must have type signature `(async) fn(foo::Context, ..)` + --> ui/task-pub.rs:6:12 + | +6 | pub fn foo(_: foo::Context) {} + | ^^^ diff --git a/macros/ui/task-unsafe.rs b/macros/ui/task-unsafe.rs new file mode 100644 index 0000000..44255f0 --- /dev/null +++ b/macros/ui/task-unsafe.rs @@ -0,0 +1,7 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[task] + unsafe fn foo(_: foo::Context) {} +} diff --git a/macros/ui/task-unsafe.stderr b/macros/ui/task-unsafe.stderr new file mode 100644 index 0000000..424c5af --- /dev/null +++ b/macros/ui/task-unsafe.stderr @@ -0,0 +1,5 @@ +error: this task handler must have type signature `(async) fn(foo::Context, ..)` + --> ui/task-unsafe.rs:6:15 + | +6 | unsafe fn foo(_: foo::Context) {} + | ^^^ -- cgit v1.2.3 From 582c602912592ec7ebea3096aefa02aea99c2143 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Mon, 2 Jan 2023 14:34:05 +0100 Subject: Old xtask test pass --- macros/src/codegen/local_resources_struct.rs | 1 - macros/src/syntax.rs | 21 -------------- macros/src/syntax/analyze.rs | 42 ++++++++++++++-------------- 3 files changed, 21 insertions(+), 43 deletions(-) (limited to 'macros') diff --git a/macros/src/codegen/local_resources_struct.rs b/macros/src/codegen/local_resources_struct.rs index 309fd8d..6bcf4fa 100644 --- a/macros/src/codegen/local_resources_struct.rs +++ b/macros/src/codegen/local_resources_struct.rs @@ -37,7 +37,6 @@ pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2, (&r.cfgs, &r.ty, false) } TaskLocal::Declared(r) => (&r.cfgs, &r.ty, true), - _ => unreachable!(), }; has_cfgs |= !cfgs.is_empty(); diff --git a/macros/src/syntax.rs b/macros/src/syntax.rs index 11b92c1..09b2ab3 100644 --- a/macros/src/syntax.rs +++ b/macros/src/syntax.rs @@ -1,7 +1,6 @@ #[allow(unused_extern_crates)] extern crate proc_macro; -use core::ops; use proc_macro::TokenStream; use indexmap::{IndexMap, IndexSet}; @@ -23,26 +22,6 @@ pub type Map = IndexMap; /// An order set pub type Set = IndexSet; -/// Immutable pointer -pub struct P { - ptr: Box, -} - -impl P { - /// Boxes `x` making the value immutable - pub fn new(x: T) -> P { - P { ptr: Box::new(x) } - } -} - -impl ops::Deref for P { - type Target = T; - - fn deref(&self) -> &T { - &self.ptr - } -} - /// Execution context #[derive(Clone, Copy)] pub enum Context<'a> { diff --git a/macros/src/syntax/analyze.rs b/macros/src/syntax/analyze.rs index 06b23f4..44960b9 100644 --- a/macros/src/syntax/analyze.rs +++ b/macros/src/syntax/analyze.rs @@ -338,8 +338,8 @@ pub(crate) fn app(app: &App) -> Result { }) } -/// Priority ceiling -pub type Ceiling = Option; +// /// Priority ceiling +// pub type Ceiling = Option; /// Task priority pub type Priority = u8; @@ -427,22 +427,22 @@ pub enum Ownership { }, } -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 { .. }) - } -} +// 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 { .. }) +// } +// } -- cgit v1.2.3 From 9829d0ac07180967208403610bc9a25249b9fe85 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Mon, 2 Jan 2023 14:58:37 +0100 Subject: Add check again --- macros/src/check.rs | 28 ++++++---------------------- macros/src/lib.rs | 6 ++++++ 2 files changed, 12 insertions(+), 22 deletions(-) (limited to 'macros') diff --git a/macros/src/check.rs b/macros/src/check.rs index b0ad6f8..312b84d 100644 --- a/macros/src/check.rs +++ b/macros/src/check.rs @@ -1,18 +1,12 @@ use std::collections::HashSet; -use proc_macro2::Span; -use rtic_syntax::{analyze::Analysis, ast::App}; -use syn::{parse, Path}; +use crate::syntax::ast::App; +use syn::parse; -pub struct Extra { - pub device: Path, - pub peripherals: bool, -} - -pub fn app(app: &App, _analysis: &Analysis) -> parse::Result { +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.extern_interrupts.keys() { + for name in app.args.dispatchers.keys() { let name_s = name.to_string(); match &*name_s { @@ -41,7 +35,7 @@ pub fn app(app: &App, _analysis: &Analysis) -> parse::Result { .collect::>(); let need = priorities.len(); - let given = app.args.extern_interrupts.len(); + let given = app.args.dispatchers.len(); if need > given { let s = { format!( @@ -72,15 +66,5 @@ pub fn app(app: &App, _analysis: &Analysis) -> parse::Result { } } - if let Some(device) = app.args.device.clone() { - Ok(Extra { - device, - peripherals: app.args.peripherals, - }) - } else { - Err(parse::Error::new( - Span::call_site(), - "a `device` argument must be specified in `#[rtic::app]`", - )) - } + Ok(()) } diff --git a/macros/src/lib.rs b/macros/src/lib.rs index 7729dcb..1bda8d2 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -10,6 +10,7 @@ use std::{env, fs, path::Path}; mod analyze; mod bindings; +mod check; mod codegen; mod syntax; @@ -61,6 +62,11 @@ pub fn app(args: TokenStream, input: TokenStream) -> TokenStream { Ok(x) => x, }; + match check::app(&app) { + Err(e) => return e.to_compile_error().into(), + _ => {} + } + let analysis = analyze::app(analysis, &app); let ts = codegen::app(&app, &analysis); -- cgit v1.2.3 From d7ed7a8b9f78344f6855fa1c2655ae0d85e44068 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Tue, 3 Jan 2023 07:51:35 +0100 Subject: syntax: Remove parse settings struct --- macros/src/lib.rs | 27 ++------------------------- macros/src/syntax.rs | 24 ++++-------------------- macros/src/syntax/optimize.rs | 2 +- macros/src/syntax/parse.rs | 20 +++++--------------- macros/src/syntax/parse/app.rs | 11 ++++------- macros/ui/extern-interrupt-used.rs | 2 +- macros/ui/interrupt-double.rs | 2 +- macros/ui/monotonic-binds-collision-task.rs | 2 +- macros/ui/task-bind.rs | 7 ------- macros/ui/task-bind.stderr | 5 ----- macros/ui/task-interrupt-same-prio-spawn.rs | 2 +- macros/ui/task-interrupt.rs | 2 +- macros/ui/task-priority-too-low.rs | 2 +- 13 files changed, 22 insertions(+), 86 deletions(-) delete mode 100644 macros/ui/task-bind.rs delete mode 100644 macros/ui/task-bind.stderr (limited to 'macros') diff --git a/macros/src/lib.rs b/macros/src/lib.rs index 1bda8d2..34f2bb6 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -18,25 +18,7 @@ mod syntax; #[doc(hidden)] #[proc_macro_attribute] pub fn mock_app(args: TokenStream, input: TokenStream) -> TokenStream { - let mut settings = syntax::Settings::default(); - let mut rtic_args = vec![]; - for arg in args.to_string().split(',') { - if arg.trim() == "parse_binds" { - settings.parse_binds = true; - } else if arg.trim() == "parse_extern_interrupt" { - settings.parse_extern_interrupt = true; - } else { - rtic_args.push(arg.to_string()); - } - } - - // rtic_args.push("device = mock".into()); - - let args = rtic_args.join(", ").parse(); - - println!("args: {:?}", args); - - if let Err(e) = syntax::parse(args.unwrap(), input, settings) { + if let Err(e) = syntax::parse(args, input) { e.to_compile_error().into() } else { "fn main() {}".parse().unwrap() @@ -52,12 +34,7 @@ pub fn mock_app(args: TokenStream, input: TokenStream) -> TokenStream { /// 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 mut settings = syntax::Settings::default(); - settings.optimize_priorities = false; - settings.parse_binds = true; - settings.parse_extern_interrupt = true; - - let (app, analysis) = match syntax::parse(args, input, settings) { + let (app, analysis) = match syntax::parse(args, input) { Err(e) => return e.to_compile_error().into(), Ok(x) => x, }; diff --git a/macros/src/syntax.rs b/macros/src/syntax.rs index 09b2ab3..d6f5a47 100644 --- a/macros/src/syntax.rs +++ b/macros/src/syntax.rs @@ -13,7 +13,6 @@ mod accessors; pub mod analyze; pub mod ast; mod check; -mod optimize; mod parse; /// An ordered map keyed by identifier @@ -31,10 +30,10 @@ pub enum Context<'a> { /// The `init`-ialization function Init, - /// A software task: `#[task]` + /// A async software task SoftwareTask(&'a Ident), - /// A hardware task: `#[exception]` or `#[interrupt]` + /// A hardware task HardwareTask(&'a Ident), } @@ -93,36 +92,21 @@ impl<'a> Context<'a> { } } -/// Parser and optimizer configuration -#[derive(Default)] -#[non_exhaustive] -pub struct Settings { - /// Whether to accept the `binds` argument in `#[task]` or not - pub parse_binds: bool, - /// Whether to parse `extern` interrupts (functions) or not - pub parse_extern_interrupt: bool, - /// Whether to "compress" priorities or not - pub optimize_priorities: bool, -} - /// Parses the input of the `#[app]` attribute pub fn parse( args: TokenStream, input: TokenStream, - settings: Settings, ) -> Result<(ast::App, analyze::Analysis), syn::parse::Error> { - parse2(args.into(), input.into(), settings) + parse2(args.into(), input.into()) } /// `proc_macro2::TokenStream` version of `parse` pub fn parse2( args: TokenStream2, input: TokenStream2, - settings: Settings, ) -> Result<(ast::App, analyze::Analysis), syn::parse::Error> { - let mut app = parse::app(args, input, &settings)?; + let app = parse::app(args, input)?; check::app(&app)?; - optimize::app(&mut app, &settings); match analyze::app(&app) { Err(e) => Err(e), diff --git a/macros/src/syntax/optimize.rs b/macros/src/syntax/optimize.rs index 87a6258..e83ba31 100644 --- a/macros/src/syntax/optimize.rs +++ b/macros/src/syntax/optimize.rs @@ -1,6 +1,6 @@ use std::collections::{BTreeSet, HashMap}; -use crate::syntax::{ast::App, Settings}; +use crate::syntax::ast::App; pub fn app(app: &mut App, settings: &Settings) { // "compress" priorities diff --git a/macros/src/syntax/parse.rs b/macros/src/syntax/parse.rs index 74f94f2..ceedaa9 100644 --- a/macros/src/syntax/parse.rs +++ b/macros/src/syntax/parse.rs @@ -20,15 +20,15 @@ use crate::syntax::{ App, AppArgs, HardwareTaskArgs, IdleArgs, InitArgs, MonotonicArgs, SoftwareTaskArgs, TaskLocal, }, - Either, Settings, + Either, }; // Parse the app, both app arguments and body (input) -pub fn app(args: TokenStream2, input: TokenStream2, settings: &Settings) -> parse::Result { +pub fn app(args: TokenStream2, input: TokenStream2) -> parse::Result { let args = AppArgs::parse(args)?; let input: Input = syn::parse2(input)?; - App::parse(args, input, settings) + App::parse(args, input) } pub(crate) struct Input { @@ -188,10 +188,7 @@ fn idle_args(tokens: TokenStream2) -> parse::Result { .parse2(tokens) } -fn task_args( - tokens: TokenStream2, - settings: &Settings, -) -> parse::Result> { +fn task_args(tokens: TokenStream2) -> parse::Result> { (|input: ParseStream<'_>| -> parse::Result> { if input.is_empty() { return Ok(Either::Right(SoftwareTaskArgs::default())); @@ -242,14 +239,7 @@ fn task_args( let _: Token![=] = content.parse()?; match &*ident_s { - "binds" if !settings.parse_binds => { - return Err(parse::Error::new( - ident.span(), - "Unexpected bind in task argument. Binds are only parsed if Settings::parse_binds is set.", - )); - } - - "binds" if settings.parse_binds => { + "binds" => { if binds.is_some() { return Err(parse::Error::new( ident.span(), diff --git a/macros/src/syntax/parse/app.rs b/macros/src/syntax/parse/app.rs index 7eb415d..dd7c399 100644 --- a/macros/src/syntax/parse/app.rs +++ b/macros/src/syntax/parse/app.rs @@ -15,7 +15,7 @@ use crate::syntax::{ LocalResource, Monotonic, MonotonicArgs, SharedResource, SoftwareTask, }, parse::{self as syntax_parse, util}, - Either, Map, Set, Settings, + Either, Map, Set, }; impl AppArgs { @@ -142,7 +142,7 @@ impl AppArgs { } impl App { - pub(crate) fn parse(args: AppArgs, input: Input, settings: &Settings) -> parse::Result { + pub(crate) fn parse(args: AppArgs, input: Input) -> parse::Result { let mut init = None; let mut idle = None; @@ -253,7 +253,7 @@ impl App { )); } - match syntax_parse::task_args(item.attrs.remove(pos).tokens, settings)? { + match syntax_parse::task_args(item.attrs.remove(pos).tokens)? { Either::Left(args) => { check_binding(&args.binds)?; check_ident(&item.sig.ident)?; @@ -410,10 +410,7 @@ impl App { )); } - match syntax_parse::task_args( - item.attrs.remove(pos).tokens, - settings, - )? { + match syntax_parse::task_args(item.attrs.remove(pos).tokens)? { Either::Left(args) => { check_binding(&args.binds)?; check_ident(&item.sig.ident)?; diff --git a/macros/ui/extern-interrupt-used.rs b/macros/ui/extern-interrupt-used.rs index 71dc50f..a6e0b3b 100644 --- a/macros/ui/extern-interrupt-used.rs +++ b/macros/ui/extern-interrupt-used.rs @@ -1,6 +1,6 @@ #![no_main] -#[rtic_macros::mock_app(parse_extern_interrupt, parse_binds, device = mock, dispatchers = [EXTI0])] +#[rtic_macros::mock_app(device = mock, dispatchers = [EXTI0])] mod app { #[shared] struct Shared {} diff --git a/macros/ui/interrupt-double.rs b/macros/ui/interrupt-double.rs index 1133c5c..e2addc7 100644 --- a/macros/ui/interrupt-double.rs +++ b/macros/ui/interrupt-double.rs @@ -1,6 +1,6 @@ #![no_main] -#[rtic_macros::mock_app(parse_binds, device = mock)] +#[rtic_macros::mock_app(device = mock)] mod app { #[task(binds = UART0)] fn foo(_: foo::Context) {} diff --git a/macros/ui/monotonic-binds-collision-task.rs b/macros/ui/monotonic-binds-collision-task.rs index 57c59c1..1c58f9d 100644 --- a/macros/ui/monotonic-binds-collision-task.rs +++ b/macros/ui/monotonic-binds-collision-task.rs @@ -1,6 +1,6 @@ #![no_main] -#[rtic_macros::mock_app(parse_extern_interrupt, parse_binds, device = mock)] +#[rtic_macros::mock_app(device = mock)] mod app { #[monotonic(binds = Tim1)] type Fast1 = hal::Tim1Monotonic; diff --git a/macros/ui/task-bind.rs b/macros/ui/task-bind.rs deleted file mode 100644 index de60524..0000000 --- a/macros/ui/task-bind.rs +++ /dev/null @@ -1,7 +0,0 @@ -#![no_main] - -#[rtic_macros::mock_app(device = mock)] -mod app { - #[task(binds = UART0)] - fn foo(_: foo::Context) {} -} diff --git a/macros/ui/task-bind.stderr b/macros/ui/task-bind.stderr deleted file mode 100644 index 60cfdc8..0000000 --- a/macros/ui/task-bind.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: Unexpected bind in task argument. Binds are only parsed if Settings::parse_binds is set. - --> $DIR/task-bind.rs:5:12 - | -5 | #[task(binds = UART0)] - | ^^^^^ diff --git a/macros/ui/task-interrupt-same-prio-spawn.rs b/macros/ui/task-interrupt-same-prio-spawn.rs index 741e60e..1d5f1f8 100644 --- a/macros/ui/task-interrupt-same-prio-spawn.rs +++ b/macros/ui/task-interrupt-same-prio-spawn.rs @@ -1,6 +1,6 @@ #![no_main] -#[rtic_macros::mock_app(parse_binds, device = mock)] +#[rtic_macros::mock_app(device = mock)] mod app { #[task(binds = SysTick, only_same_priority_spawn_please_fix_me)] fn foo(_: foo::Context) {} diff --git a/macros/ui/task-interrupt.rs b/macros/ui/task-interrupt.rs index 71fef9a..5e063de 100644 --- a/macros/ui/task-interrupt.rs +++ b/macros/ui/task-interrupt.rs @@ -1,6 +1,6 @@ #![no_main] -#[rtic_macros::mock_app(parse_binds, device = mock)] +#[rtic_macros::mock_app(device = mock)] mod app { #[task(binds = SysTick)] fn foo(_: foo::Context) {} diff --git a/macros/ui/task-priority-too-low.rs b/macros/ui/task-priority-too-low.rs index beed4de..16e0557 100644 --- a/macros/ui/task-priority-too-low.rs +++ b/macros/ui/task-priority-too-low.rs @@ -1,6 +1,6 @@ #![no_main] -#[rtic_macros::mock_app(parse_binds, device = mock)] +#[rtic_macros::mock_app(device = mock)] mod app { #[task(binds = UART0, priority = 0)] fn foo(_: foo::Context) {} -- cgit v1.2.3 From f8352122a301c30db7c7851ebf50ad1608ebdad3 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Tue, 3 Jan 2023 15:10:59 +0100 Subject: Min codegen --- macros/src/analyze.rs | 23 +-- macros/src/codegen.rs | 29 +-- macros/src/codegen/assertions.rs | 5 - macros/src/codegen/async_dispatchers.rs | 66 +++---- macros/src/codegen/dispatchers.rs | 146 --------------- macros/src/codegen/module.rs | 300 ++----------------------------- macros/src/codegen/monotonic.rs | 280 ----------------------------- macros/src/codegen/post_init.rs | 20 +-- macros/src/codegen/pre_init.rs | 68 +------ macros/src/codegen/shared_resources.rs | 6 +- macros/src/codegen/software_tasks.rs | 179 ------------------ macros/src/codegen/timer_queue.rs | 170 ------------------ macros/src/codegen/util.rs | 111 +----------- macros/src/syntax/analyze.rs | 57 +----- macros/src/syntax/ast.rs | 50 +----- macros/src/syntax/parse.rs | 121 +------------ macros/src/syntax/parse/app.rs | 58 +----- macros/src/syntax/parse/hardware_task.rs | 44 +++-- macros/src/syntax/parse/idle.rs | 18 +- macros/src/syntax/parse/init.rs | 22 ++- macros/src/syntax/parse/software_task.rs | 26 ++- macros/src/syntax/parse/util.rs | 26 ++- 22 files changed, 130 insertions(+), 1695 deletions(-) delete mode 100644 macros/src/codegen/dispatchers.rs delete mode 100644 macros/src/codegen/monotonic.rs delete mode 100644 macros/src/codegen/software_tasks.rs delete mode 100644 macros/src/codegen/timer_queue.rs (limited to 'macros') diff --git a/macros/src/analyze.rs b/macros/src/analyze.rs index ec12cfb..cb42ad6 100644 --- a/macros/src/analyze.rs +++ b/macros/src/analyze.rs @@ -10,8 +10,7 @@ use syn::Ident; /// Extend the upstream `Analysis` struct with our field pub struct Analysis { parent: analyze::Analysis, - pub interrupts_normal: BTreeMap, - pub interrupts_async: BTreeMap, + pub interrupts: BTreeMap, } impl ops::Deref for Analysis { @@ -30,27 +29,12 @@ pub fn app(analysis: analyze::Analysis, app: &App) -> Analysis { let priorities = app .software_tasks .values() - .filter(|task| !task.is_async) - .map(|task| task.args.priority) - .collect::>(); - - let priorities_async = app - .software_tasks - .values() - .filter(|task| task.is_async) .map(|task| task.args.priority) .collect::>(); // map from priorities to interrupts (holding name and attributes) - let interrupts_normal: BTreeMap = priorities - .iter() - .copied() - .rev() - .map(|p| (p, available_interrupt.pop().expect("UNREACHABLE"))) - .collect(); - - let interrupts_async: BTreeMap = priorities_async + let interrupts: BTreeMap = priorities .iter() .copied() .rev() @@ -59,7 +43,6 @@ pub fn app(analysis: analyze::Analysis, app: &App) -> Analysis { Analysis { parent: analysis, - interrupts_normal, - interrupts_async, + interrupts, } } diff --git a/macros/src/codegen.rs b/macros/src/codegen.rs index ef81732..618d9f3 100644 --- a/macros/src/codegen.rs +++ b/macros/src/codegen.rs @@ -6,20 +6,20 @@ use crate::syntax::ast::App; mod assertions; mod async_dispatchers; -mod dispatchers; +// mod dispatchers; mod hardware_tasks; mod idle; mod init; mod local_resources; mod local_resources_struct; mod module; -mod monotonic; +// mod monotonic; mod post_init; mod pre_init; mod shared_resources; mod shared_resources_struct; -mod software_tasks; -mod timer_queue; +// mod software_tasks; +// mod timer_queue; mod util; #[allow(clippy::too_many_lines)] @@ -92,14 +92,7 @@ pub fn app(app: &App, analysis: &Analysis) -> TokenStream2 { let (mod_app_hardware_tasks, root_hardware_tasks, user_hardware_tasks) = hardware_tasks::codegen(app, analysis); - let (mod_app_software_tasks, root_software_tasks, user_software_tasks) = - software_tasks::codegen(app, analysis); - - let monotonics = monotonic::codegen(app, analysis); - - let mod_app_dispatchers = dispatchers::codegen(app, analysis); let mod_app_async_dispatchers = async_dispatchers::codegen(app, analysis); - let mod_app_timer_queue = timer_queue::codegen(app, analysis); let user_imports = &app.user_imports; let user_code = &app.user_code; let name = &app.name; @@ -113,8 +106,6 @@ pub fn app(app: &App, analysis: &Analysis) -> TokenStream2 { /// Always include the device crate which contains the vector table use #device as #rt_err; - #monotonics - #(#user_imports)* /// User code from within the module @@ -125,8 +116,6 @@ pub fn app(app: &App, analysis: &Analysis) -> TokenStream2 { #(#user_hardware_tasks)* - #(#user_software_tasks)* - #(#root)* #mod_shared_resources @@ -135,9 +124,7 @@ pub fn app(app: &App, analysis: &Analysis) -> TokenStream2 { #(#root_hardware_tasks)* - #(#root_software_tasks)* - - /// App module + /// app module #(#mod_app)* #(#mod_app_shared_resources)* @@ -146,14 +133,8 @@ pub fn app(app: &App, analysis: &Analysis) -> TokenStream2 { #(#mod_app_hardware_tasks)* - #(#mod_app_software_tasks)* - - #(#mod_app_dispatchers)* - #(#mod_app_async_dispatchers)* - #(#mod_app_timer_queue)* - #(#mains)* } ) diff --git a/macros/src/codegen/assertions.rs b/macros/src/codegen/assertions.rs index 0f8326c..dd94aa6 100644 --- a/macros/src/codegen/assertions.rs +++ b/macros/src/codegen/assertions.rs @@ -16,11 +16,6 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec { stmts.push(quote!(rtic::export::assert_sync::<#ty>();)); } - for (_, monotonic) in &app.monotonics { - let ty = &monotonic.ty; - stmts.push(quote!(rtic::export::assert_monotonic::<#ty>();)); - } - let device = &app.args.device; let chunks_name = util::priority_mask_chunks_ident(); let no_basepri_checks: Vec<_> = app diff --git a/macros/src/codegen/async_dispatchers.rs b/macros/src/codegen/async_dispatchers.rs index 8b0e928..aa854d7 100644 --- a/macros/src/codegen/async_dispatchers.rs +++ b/macros/src/codegen/async_dispatchers.rs @@ -7,65 +7,47 @@ use quote::quote; pub fn codegen(app: &App, analysis: &Analysis) -> Vec { let mut items = vec![]; - let interrupts = &analysis.interrupts_async; + let interrupts = &analysis.interrupts; // Generate executor definition and priority in global scope - for (name, task) in app.software_tasks.iter() { - if task.is_async { - let type_name = util::internal_task_ident(name, "F"); - let exec_name = util::internal_task_ident(name, "EXEC"); - let prio_name = util::internal_task_ident(name, "PRIORITY"); + for (name, _) in app.software_tasks.iter() { + let type_name = util::internal_task_ident(name, "F"); + let exec_name = util::internal_task_ident(name, "EXEC"); + let prio_name = util::internal_task_ident(name, "PRIORITY"); - items.push(quote!( - #[allow(non_camel_case_types)] - type #type_name = impl core::future::Future + 'static; - #[allow(non_upper_case_globals)] - static #exec_name: - rtic::RacyCell> = - rtic::RacyCell::new(rtic::export::executor::AsyncTaskExecutor::new()); - - // The executors priority, this can be any value - we will overwrite it when we - // start a task - #[allow(non_upper_case_globals)] - static #prio_name: rtic::RacyCell = - unsafe { rtic::RacyCell::new(rtic::export::Priority::new(0)) }; - )); - } + items.push(quote!( + #[allow(non_camel_case_types)] + type #type_name = impl core::future::Future + 'static; + #[allow(non_upper_case_globals)] + static #exec_name: + rtic::RacyCell> = + rtic::RacyCell::new(rtic::export::executor::AsyncTaskExecutor::new()); + + // The executors priority, this can be any value - we will overwrite it when we + // start a task + #[allow(non_upper_case_globals)] + static #prio_name: rtic::RacyCell = + unsafe { rtic::RacyCell::new(rtic::export::Priority::new(0)) }; + )); } for (&level, channel) in &analysis.channels { - if channel - .tasks - .iter() - .map(|task_name| !app.software_tasks[task_name].is_async) - .all(|is_not_async| is_not_async) - { - // check if all tasks are not async, if so don't generate this. - continue; - } - let mut stmts = vec![]; let device = &app.args.device; let enum_ = util::interrupt_ident(); let interrupt = util::suffixed(&interrupts[&level].0.to_string()); - for name in channel - .tasks - .iter() - .filter(|name| app.software_tasks[*name].is_async) - { + for name in channel.tasks.iter() { let exec_name = util::internal_task_ident(name, "EXEC"); let prio_name = util::internal_task_ident(name, "PRIORITY"); - let task = &app.software_tasks[name]; + // let task = &app.software_tasks[name]; // let cfgs = &task.cfgs; - let (_, tupled, pats, input_types) = util::regroup_inputs(&task.inputs); let executor_run_ident = util::executor_run_ident(name); - let n = util::capacity_literal(channel.capacity as usize + 1); let rq = util::rq_async_ident(name); let (rq_ty, rq_expr) = { ( - quote!(rtic::export::ASYNCRQ<#input_types, #n>), + quote!(rtic::export::ASYNCRQ<(), 2>), // TODO: This needs updating to a counter instead of a queue quote!(rtic::export::Queue::new()), ) }; @@ -79,13 +61,13 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec { stmts.push(quote!( if !(&*#exec_name.get()).is_running() { - if let Some(#tupled) = rtic::export::interrupt::free(|_| (&mut *#rq.get_mut()).dequeue()) { + if let Some(()) = rtic::export::interrupt::free(|_| (&mut *#rq.get_mut()).dequeue()) { // The async executor needs a static priority #prio_name.get_mut().write(rtic::export::Priority::new(PRIORITY)); let priority: &'static _ = &*#prio_name.get(); - (&mut *#exec_name.get_mut()).spawn(#name(#name::Context::new(priority) #(,#pats)*)); + (&mut *#exec_name.get_mut()).spawn(#name(#name::Context::new(priority))); #executor_run_ident.store(true, core::sync::atomic::Ordering::Relaxed); } } diff --git a/macros/src/codegen/dispatchers.rs b/macros/src/codegen/dispatchers.rs deleted file mode 100644 index 1a8b404..0000000 --- a/macros/src/codegen/dispatchers.rs +++ /dev/null @@ -1,146 +0,0 @@ -use crate::syntax::ast::App; -use crate::{analyze::Analysis, codegen::util}; -use proc_macro2::TokenStream as TokenStream2; -use quote::quote; - -/// Generates task dispatchers -pub fn codegen(app: &App, analysis: &Analysis) -> Vec { - let mut items = vec![]; - - let interrupts = &analysis.interrupts_normal; - - for (&level, channel) in &analysis.channels { - if channel - .tasks - .iter() - .map(|task_name| app.software_tasks[task_name].is_async) - .all(|is_async| is_async) - { - // check if all tasks are async, if so don't generate this. - continue; - } - - let mut stmts = vec![]; - - let variants = channel - .tasks - .iter() - .filter(|name| !app.software_tasks[*name].is_async) - .map(|name| { - let cfgs = &app.software_tasks[name].cfgs; - - quote!( - #(#cfgs)* - #name - ) - }) - .collect::>(); - - // For future use - // let doc = format!( - // "Software tasks to be dispatched at priority level {}", - // level, - // ); - let t = util::spawn_t_ident(level); - items.push(quote!( - #[allow(non_snake_case)] - #[allow(non_camel_case_types)] - #[derive(Clone, Copy)] - // #[doc = #doc] - #[doc(hidden)] - pub enum #t { - #(#variants,)* - } - )); - - let n = util::capacity_literal(channel.capacity as usize + 1); - let rq = util::rq_ident(level); - // let (_, _, _, input_ty) = util::regroup_inputs(inputs); - let (rq_ty, rq_expr) = { - ( - quote!(rtic::export::SCRQ<#t, #n>), - quote!(rtic::export::Queue::new()), - ) - }; - - // For future use - // let doc = format!( - // "Queue of tasks ready to be dispatched at priority level {}", - // level - // ); - items.push(quote!( - #[doc(hidden)] - #[allow(non_camel_case_types)] - #[allow(non_upper_case_globals)] - static #rq: rtic::RacyCell<#rq_ty> = rtic::RacyCell::new(#rq_expr); - )); - - let interrupt = util::suffixed( - &interrupts - .get(&level) - .expect("RTIC-ICE: Unable to get interrrupt") - .0 - .to_string(), - ); - let arms = channel - .tasks - .iter() - .map(|name| { - let task = &app.software_tasks[name]; - let cfgs = &task.cfgs; - let fq = util::fq_ident(name); - let inputs = util::inputs_ident(name); - let (_, tupled, pats, _) = util::regroup_inputs(&task.inputs); - - if !task.is_async { - quote!( - #(#cfgs)* - #t::#name => { - let #tupled = - (&*#inputs - .get()) - .get_unchecked(usize::from(index)) - .as_ptr() - .read(); - (&mut *#fq.get_mut()).split().0.enqueue_unchecked(index); - let priority = &rtic::export::Priority::new(PRIORITY); - #name( - #name::Context::new(priority) - #(,#pats)* - ) - } - ) - } else { - quote!() - } - }) - .collect::>(); - - stmts.push(quote!( - while let Some((task, index)) = (&mut *#rq.get_mut()).split().1.dequeue() { - match task { - #(#arms)* - } - } - )); - - let doc = format!("Interrupt handler to dispatch tasks at priority {}", level); - let attribute = &interrupts[&level].1.attrs; - items.push(quote!( - #[allow(non_snake_case)] - #[doc = #doc] - #[no_mangle] - #(#attribute)* - unsafe fn #interrupt() { - /// The priority of this interrupt handler - const PRIORITY: u8 = #level; - - rtic::export::run(PRIORITY, || { - #(#stmts)* - }); - } - )); - } - - items -} diff --git a/macros/src/codegen/module.rs b/macros/src/codegen/module.rs index 7ac06c5..eb0cb65 100644 --- a/macros/src/codegen/module.rs +++ b/macros/src/codegen/module.rs @@ -102,33 +102,6 @@ pub fn codegen( values.push(quote!(shared: #name::SharedResources::new(#priority))); } - if let Context::Init = ctxt { - let monotonic_types: Vec<_> = app - .monotonics - .iter() - .map(|(_, monotonic)| { - let mono = &monotonic.ty; - quote! {#mono} - }) - .collect(); - - let internal_monotonics_ident = util::mark_internal_name("Monotonics"); - - items.push(quote!( - /// Monotonics used by the system - #[allow(non_snake_case)] - #[allow(non_camel_case_types)] - pub struct #internal_monotonics_ident( - #(pub #monotonic_types),* - ); - )); - - module_items.push(quote!( - #[doc(inline)] - pub use super::#internal_monotonics_ident as Monotonics; - )); - } - let doc = match ctxt { Context::Idle => "Idle loop", Context::Init => "Initialization function", @@ -192,280 +165,45 @@ pub fn codegen( if let Context::SoftwareTask(..) = ctxt { let spawnee = &app.software_tasks[name]; let priority = spawnee.args.priority; - let t = util::spawn_t_ident(priority); let cfgs = &spawnee.cfgs; // Store a copy of the task cfgs task_cfgs = cfgs.clone(); - let (args, tupled, untupled, ty) = util::regroup_inputs(&spawnee.inputs); - let args = &args; - let tupled = &tupled; - let fq = util::fq_ident(name); - let rq = util::rq_ident(priority); - let inputs = util::inputs_ident(name); let device = &app.args.device; let enum_ = util::interrupt_ident(); - let interrupt = if spawnee.is_async { - &analysis - .interrupts_async - .get(&priority) - .expect("RTIC-ICE: interrupt identifer not found") - .0 - } else { - &analysis - .interrupts_normal - .get(&priority) - .expect("RTIC-ICE: interrupt identifer not found") - .0 - }; + let interrupt = &analysis + .interrupts + .get(&priority) + .expect("RTIC-ICE: interrupt identifer not found") + .0; let internal_spawn_ident = util::internal_task_ident(name, "spawn"); // Spawn caller - if spawnee.is_async { - let rq = util::rq_async_ident(name); - items.push(quote!( - - #(#cfgs)* - /// Spawns the task directly - #[allow(non_snake_case)] - #[doc(hidden)] - pub fn #internal_spawn_ident(#(#args,)*) -> Result<(), #ty> { - let input = #tupled; - - unsafe { - let r = rtic::export::interrupt::free(|_| (&mut *#rq.get_mut()).enqueue(input)); + let rq = util::rq_async_ident(name); + items.push(quote!( - if r.is_ok() { - rtic::pend(#device::#enum_::#interrupt); - } + #(#cfgs)* + /// Spawns the task directly + #[allow(non_snake_case)] + #[doc(hidden)] + pub fn #internal_spawn_ident() -> Result<(), ()> { + unsafe { + let r = rtic::export::interrupt::free(|_| (&mut *#rq.get_mut()).enqueue(())); - r + if r.is_ok() { + rtic::pend(#device::#enum_::#interrupt); } - })); - } else { - items.push(quote!( - #(#cfgs)* - /// Spawns the task directly - #[allow(non_snake_case)] - #[doc(hidden)] - pub fn #internal_spawn_ident(#(#args,)*) -> Result<(), #ty> { - let input = #tupled; - - unsafe { - if let Some(index) = rtic::export::interrupt::free(|_| (&mut *#fq.get_mut()).dequeue()) { - (&mut *#inputs - .get_mut()) - .get_unchecked_mut(usize::from(index)) - .as_mut_ptr() - .write(input); - - rtic::export::interrupt::free(|_| { - (&mut *#rq.get_mut()).enqueue_unchecked((#t::#name, index)); - }); - rtic::pend(#device::#enum_::#interrupt); - - Ok(()) - } else { - Err(input) - } - } - - })); - } + r + } + })); module_items.push(quote!( #(#cfgs)* #[doc(inline)] pub use super::#internal_spawn_ident as spawn; )); - - // Schedule caller - if !spawnee.is_async { - for (_, monotonic) in &app.monotonics { - let instants = util::monotonic_instants_ident(name, &monotonic.ident); - let monotonic_name = monotonic.ident.to_string(); - - let tq = util::tq_ident(&monotonic.ident.to_string()); - let t = util::schedule_t_ident(); - let m = &monotonic.ident; - let m_ident = util::monotonic_ident(&monotonic_name); - let m_isr = &monotonic.args.binds; - let enum_ = util::interrupt_ident(); - let spawn_handle_string = format!("{}::SpawnHandle", m); - - let (enable_interrupt, pend) = if &*m_isr.to_string() == "SysTick" { - ( - quote!(core::mem::transmute::<_, rtic::export::SYST>(()).enable_interrupt()), - quote!(rtic::export::SCB::set_pendst()), - ) - } else { - let rt_err = util::rt_err_ident(); - ( - quote!(rtic::export::NVIC::unmask(#rt_err::#enum_::#m_isr)), - quote!(rtic::pend(#rt_err::#enum_::#m_isr)), - ) - }; - - let tq_marker = &util::timer_queue_marker_ident(); - - let internal_spawn_handle_ident = - util::internal_monotonics_ident(name, m, "SpawnHandle"); - let internal_spawn_at_ident = util::internal_monotonics_ident(name, m, "spawn_at"); - let internal_spawn_after_ident = - util::internal_monotonics_ident(name, m, "spawn_after"); - - if monotonic.args.default { - module_items.push(quote!( - #[doc(inline)] - pub use #m::spawn_after; - #[doc(inline)] - pub use #m::spawn_at; - #[doc(inline)] - pub use #m::SpawnHandle; - )); - } - module_items.push(quote!( - pub mod #m { - #[doc(inline)] - pub use super::super::#internal_spawn_after_ident as spawn_after; - #[doc(inline)] - pub use super::super::#internal_spawn_at_ident as spawn_at; - #[doc(inline)] - pub use super::super::#internal_spawn_handle_ident as SpawnHandle; - } - )); - - items.push(quote!( - #(#cfgs)* - #[allow(non_snake_case)] - #[allow(non_camel_case_types)] - pub struct #internal_spawn_handle_ident { - #[doc(hidden)] - marker: u32, - } - - #(#cfgs)* - impl core::fmt::Debug for #internal_spawn_handle_ident { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - f.debug_struct(#spawn_handle_string).finish() - } - } - - #(#cfgs)* - impl #internal_spawn_handle_ident { - pub fn cancel(self) -> Result<#ty, ()> { - rtic::export::interrupt::free(|_| unsafe { - let tq = &mut *#tq.get_mut(); - if let Some((_task, index)) = tq.cancel_task_marker(self.marker) { - // Get the message - let msg = (&*#inputs - .get()) - .get_unchecked(usize::from(index)) - .as_ptr() - .read(); - // Return the index to the free queue - (&mut *#fq.get_mut()).split().0.enqueue_unchecked(index); - - Ok(msg) - } else { - Err(()) - } - }) - } - - #[inline] - pub fn reschedule_after( - self, - duration: <#m as rtic::Monotonic>::Duration - ) -> Result { - self.reschedule_at(monotonics::#m::now() + duration) - } - - pub fn reschedule_at( - self, - instant: <#m as rtic::Monotonic>::Instant - ) -> Result { - rtic::export::interrupt::free(|_| unsafe { - let marker = #tq_marker.get().read(); - #tq_marker.get_mut().write(marker.wrapping_add(1)); - - let tq = (&mut *#tq.get_mut()); - - tq.update_task_marker(self.marker, marker, instant, || #pend).map(|_| #name::#m::SpawnHandle { marker }) - }) - } - } - - - #(#cfgs)* - /// Spawns the task after a set duration relative to the current time - /// - /// This will use the time `Instant::new(0)` as baseline if called in `#[init]`, - /// so if you use a non-resetable timer use `spawn_at` when in `#[init]` - #[allow(non_snake_case)] - pub fn #internal_spawn_after_ident( - duration: <#m as rtic::Monotonic>::Duration - #(,#args)* - ) -> Result<#name::#m::SpawnHandle, #ty> - { - let instant = monotonics::#m::now(); - - #internal_spawn_at_ident(instant + duration #(,#untupled)*) - } - - #(#cfgs)* - /// Spawns the task at a fixed time instant - #[allow(non_snake_case)] - pub fn #internal_spawn_at_ident( - instant: <#m as rtic::Monotonic>::Instant - #(,#args)* - ) -> Result<#name::#m::SpawnHandle, #ty> { - unsafe { - let input = #tupled; - if let Some(index) = rtic::export::interrupt::free(|_| (&mut *#fq.get_mut()).dequeue()) { - (&mut *#inputs - .get_mut()) - .get_unchecked_mut(usize::from(index)) - .as_mut_ptr() - .write(input); - - (&mut *#instants - .get_mut()) - .get_unchecked_mut(usize::from(index)) - .as_mut_ptr() - .write(instant); - - rtic::export::interrupt::free(|_| { - let marker = #tq_marker.get().read(); - let nr = rtic::export::TaskNotReady { - task: #t::#name, - index, - instant, - marker, - }; - - #tq_marker.get_mut().write(#tq_marker.get().read().wrapping_add(1)); - - let tq = &mut *#tq.get_mut(); - - tq.enqueue_task_unchecked( - nr, - || #enable_interrupt, - || #pend, - (&mut *#m_ident.get_mut()).as_mut()); - - Ok(#name::#m::SpawnHandle { marker }) - }) - } else { - Err(input) - } - } - } - )); - } - } } if items.is_empty() { diff --git a/macros/src/codegen/monotonic.rs b/macros/src/codegen/monotonic.rs deleted file mode 100644 index 417a1d6..0000000 --- a/macros/src/codegen/monotonic.rs +++ /dev/null @@ -1,280 +0,0 @@ -use crate::syntax::ast::App; -use proc_macro2::TokenStream as TokenStream2; -use quote::quote; - -use crate::{analyze::Analysis, codegen::util}; - -/// Generates monotonic module dispatchers -pub fn codegen(app: &App, _analysis: &Analysis) -> TokenStream2 { - let mut monotonic_parts: Vec<_> = Vec::new(); - - let tq_marker = util::timer_queue_marker_ident(); - - for (_, monotonic) in &app.monotonics { - // let instants = util::monotonic_instants_ident(name, &monotonic.ident); - let monotonic_name = monotonic.ident.to_string(); - - let tq = util::tq_ident(&monotonic_name); - let m = &monotonic.ident; - let m_ident = util::monotonic_ident(&monotonic_name); - let m_isr = &monotonic.args.binds; - let enum_ = util::interrupt_ident(); - let name_str = &m.to_string(); - let ident = util::monotonic_ident(name_str); - let doc = &format!( - "This module holds the static implementation for `{}::now()`", - name_str - ); - - let (enable_interrupt, pend) = if &*m_isr.to_string() == "SysTick" { - ( - quote!(core::mem::transmute::<_, rtic::export::SYST>(()).enable_interrupt()), - quote!(rtic::export::SCB::set_pendst()), - ) - } else { - let rt_err = util::rt_err_ident(); - ( - quote!(rtic::export::NVIC::unmask(super::super::#rt_err::#enum_::#m_isr)), - quote!(rtic::pend(super::super::#rt_err::#enum_::#m_isr)), - ) - }; - - let default_monotonic = if monotonic.args.default { - quote!( - #[doc(inline)] - pub use #m::now; - #[doc(inline)] - pub use #m::delay; - #[doc(inline)] - pub use #m::delay_until; - #[doc(inline)] - pub use #m::timeout_at; - #[doc(inline)] - pub use #m::timeout_after; - ) - } else { - quote!() - }; - - monotonic_parts.push(quote! { - #default_monotonic - - #[doc = #doc] - #[allow(non_snake_case)] - pub mod #m { - /// Read the current time from this monotonic - pub fn now() -> ::Instant { - rtic::export::interrupt::free(|_| { - use rtic::Monotonic as _; - if let Some(m) = unsafe{ &mut *super::super::#ident.get_mut() } { - m.now() - } else { - ::zero() - } - }) - } - - /// Delay - #[inline(always)] - #[allow(non_snake_case)] - pub fn delay(duration: ::Duration) - -> DelayFuture { - let until = now() + duration; - DelayFuture { until, waker_storage: None } - } - - /// Delay until a specific time - #[inline(always)] - #[allow(non_snake_case)] - pub fn delay_until(instant: ::Instant) - -> DelayFuture { - let until = instant; - DelayFuture { until, waker_storage: None } - } - - /// Delay future. - #[allow(non_snake_case)] - #[allow(non_camel_case_types)] - pub struct DelayFuture { - until: ::Instant, - waker_storage: Option>>, - } - - impl Drop for DelayFuture { - fn drop(&mut self) { - if let Some(waker_storage) = &mut self.waker_storage { - rtic::export::interrupt::free(|_| unsafe { - let tq = &mut *super::super::#tq.get_mut(); - tq.cancel_waker_marker(waker_storage.val.marker); - }); - } - } - } - - impl core::future::Future for DelayFuture { - type Output = (); - - fn poll( - mut self: core::pin::Pin<&mut Self>, - cx: &mut core::task::Context<'_> - ) -> core::task::Poll { - let mut s = self.as_mut(); - let now = now(); - let until = s.until; - let is_ws_none = s.waker_storage.is_none(); - - if now >= until { - return core::task::Poll::Ready(()); - } else if is_ws_none { - rtic::export::interrupt::free(|_| unsafe { - let marker = super::super::#tq_marker.get().read(); - super::super::#tq_marker.get_mut().write(marker.wrapping_add(1)); - - let nr = s.waker_storage.insert(rtic::export::IntrusiveNode::new(rtic::export::WakerNotReady { - waker: cx.waker().clone(), - instant: until, - marker, - })); - - let tq = &mut *super::super::#tq.get_mut(); - - tq.enqueue_waker( - core::mem::transmute(nr), // Transmute the reference to static - || #enable_interrupt, - || #pend, - (&mut *super::super::#m_ident.get_mut()).as_mut()); - }); - } - - core::task::Poll::Pending - } - } - - /// Timeout future. - #[allow(non_snake_case)] - #[allow(non_camel_case_types)] - pub struct TimeoutFuture { - future: F, - until: ::Instant, - waker_storage: Option>>, - } - - impl Drop for TimeoutFuture { - fn drop(&mut self) { - if let Some(waker_storage) = &mut self.waker_storage { - rtic::export::interrupt::free(|_| unsafe { - let tq = &mut *super::super::#tq.get_mut(); - tq.cancel_waker_marker(waker_storage.val.marker); - }); - } - } - } - - /// Timeout after - #[allow(non_snake_case)] - #[inline(always)] - pub fn timeout_after( - future: F, - duration: ::Duration - ) -> TimeoutFuture { - let until = now() + duration; - TimeoutFuture { - future, - until, - waker_storage: None, - } - } - - /// Timeout at - #[allow(non_snake_case)] - #[inline(always)] - pub fn timeout_at( - future: F, - instant: ::Instant - ) -> TimeoutFuture { - TimeoutFuture { - future, - until: instant, - waker_storage: None, - } - } - - impl core::future::Future for TimeoutFuture - where - F: core::future::Future, - { - type Output = Result; - - fn poll( - self: core::pin::Pin<&mut Self>, - cx: &mut core::task::Context<'_> - ) -> core::task::Poll { - // SAFETY: We don't move the underlying pinned value. - let mut s = unsafe { self.get_unchecked_mut() }; - let future = unsafe { core::pin::Pin::new_unchecked(&mut s.future) }; - let now = now(); - let until = s.until; - let is_ws_none = s.waker_storage.is_none(); - - match future.poll(cx) { - core::task::Poll::Ready(r) => { - if let Some(waker_storage) = &mut s.waker_storage { - rtic::export::interrupt::free(|_| unsafe { - let tq = &mut *super::super::#tq.get_mut(); - tq.cancel_waker_marker(waker_storage.val.marker); - }); - } - - return core::task::Poll::Ready(Ok(r)); - } - core::task::Poll::Pending => { - if now >= until { - // Timeout - return core::task::Poll::Ready(Err(super::TimeoutError)); - } else if is_ws_none { - rtic::export::interrupt::free(|_| unsafe { - let marker = super::super::#tq_marker.get().read(); - super::super::#tq_marker.get_mut().write(marker.wrapping_add(1)); - - let nr = s.waker_storage.insert(rtic::export::IntrusiveNode::new(rtic::export::WakerNotReady { - waker: cx.waker().clone(), - instant: until, - marker, - })); - - let tq = &mut *super::super::#tq.get_mut(); - - tq.enqueue_waker( - core::mem::transmute(nr), // Transmute the reference to static - || #enable_interrupt, - || #pend, - (&mut *super::super::#m_ident.get_mut()).as_mut()); - }); - } - } - } - - core::task::Poll::Pending - } - } - } - }); - } - - if monotonic_parts.is_empty() { - quote!() - } else { - quote!( - pub use rtic::Monotonic as _; - - /// Holds static methods for each monotonic. - pub mod monotonics { - /// A timeout error. - #[derive(Debug)] - pub struct TimeoutError; - - #(#monotonic_parts)* - } - ) - } -} diff --git a/macros/src/codegen/post_init.rs b/macros/src/codegen/post_init.rs index df5daa1..e8183b9 100644 --- a/macros/src/codegen/post_init.rs +++ b/macros/src/codegen/post_init.rs @@ -1,7 +1,6 @@ use crate::syntax::ast::App; -use proc_macro2::{Span, TokenStream as TokenStream2}; +use proc_macro2::TokenStream as TokenStream2; use quote::quote; -use syn::Index; use crate::{analyze::Analysis, codegen::util}; @@ -43,23 +42,6 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec { } } - for (i, (monotonic, _)) in app.monotonics.iter().enumerate() { - // For future use - // let doc = format!(" RTIC internal: {}:{}", file!(), line!()); - // stmts.push(quote!(#[doc = #doc])); - - #[allow(clippy::cast_possible_truncation)] - let idx = Index { - index: i as u32, - span: Span::call_site(), - }; - stmts.push(quote!(monotonics.#idx.reset();)); - - // Store the monotonic - let name = util::monotonic_ident(&monotonic.to_string()); - stmts.push(quote!(#name.get_mut().write(Some(monotonics.#idx));)); - } - // Enable the interrupts -- this completes the `init`-ialization phase stmts.push(quote!(rtic::export::interrupt::enable();)); diff --git a/macros/src/codegen/pre_init.rs b/macros/src/codegen/pre_init.rs index ef3acba..1492688 100644 --- a/macros/src/codegen/pre_init.rs +++ b/macros/src/codegen/pre_init.rs @@ -13,20 +13,6 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec { // Disable interrupts -- `init` must run with interrupts disabled stmts.push(quote!(rtic::export::interrupt::disable();)); - // Populate the FreeQueue - for (name, task) in &app.software_tasks { - if task.is_async { - continue; - } - - let cap = task.args.capacity; - let fq_ident = util::fq_ident(name); - - stmts.push(quote!( - (0..#cap).for_each(|i| (&mut *#fq_ident.get_mut()).enqueue_unchecked(i)); - )); - } - stmts.push(quote!( // To set the variable in cortex_m so the peripherals cannot be taken multiple times let mut core: rtic::export::Peripherals = rtic::export::Peripherals::steal().into(); @@ -42,11 +28,7 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec { stmts.push(quote!(let _ = #rt_err::#interrupt::#name;)); } - let interrupt_ids = analysis - .interrupts_normal - .iter() - .map(|(p, (id, _))| (p, id)) - .chain(analysis.interrupts_async.iter().map(|(p, (id, _))| (p, id))); + let interrupt_ids = analysis.interrupts.iter().map(|(p, (id, _))| (p, id)); // Unmask interrupts and set their priorities for (&priority, name) in interrupt_ids.chain(app.hardware_tasks.values().filter_map(|task| { @@ -101,53 +83,5 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec { );)); } - // Initialize monotonic's interrupts - for (_, monotonic) in &app.monotonics { - let priority = if let Some(prio) = monotonic.args.priority { - quote! { #prio } - } else { - quote! { (1 << #nvic_prio_bits) } - }; - let binds = &monotonic.args.binds; - - let name = &monotonic.ident; - let es = format!( - "Maximum priority used by monotonic '{}' is more than supported by hardware", - name - ); - // Compile time assert that this priority is supported by the device - stmts.push(quote!( - const _: () = if (1 << #nvic_prio_bits) < #priority as usize { ::core::panic!(#es); }; - )); - - let mono_type = &monotonic.ty; - - if &*binds.to_string() == "SysTick" { - stmts.push(quote!( - core.SCB.set_priority( - rtic::export::SystemHandler::SysTick, - rtic::export::logical2hw(#priority, #nvic_prio_bits), - ); - - // Always enable monotonic interrupts if they should never be off - if !<#mono_type as rtic::Monotonic>::DISABLE_INTERRUPT_ON_EMPTY_QUEUE { - core::mem::transmute::<_, rtic::export::SYST>(()) - .enable_interrupt(); - } - )); - } else { - stmts.push(quote!( - core.NVIC.set_priority( - #rt_err::#interrupt::#binds, - rtic::export::logical2hw(#priority, #nvic_prio_bits), - ); - - // Always enable monotonic interrupts if they should never be off - if !<#mono_type as rtic::Monotonic>::DISABLE_INTERRUPT_ON_EMPTY_QUEUE { - rtic::export::NVIC::unmask(#rt_err::#interrupt::#binds); - } - )); - } - } stmts } diff --git a/macros/src/codegen/shared_resources.rs b/macros/src/codegen/shared_resources.rs index 66f3800..b63e743 100644 --- a/macros/src/codegen/shared_resources.rs +++ b/macros/src/codegen/shared_resources.rs @@ -111,11 +111,7 @@ pub fn codegen( }; // Computing mapping of used interrupts to masks - let interrupt_ids = analysis - .interrupts_normal - .iter() - .map(|(p, (id, _))| (p, id)) - .chain(analysis.interrupts_async.iter().map(|(p, (id, _))| (p, id))); + let interrupt_ids = analysis.interrupts.iter().map(|(p, (id, _))| (p, id)); let mut prio_to_masks = HashMap::new(); let device = &app.args.device; diff --git a/macros/src/codegen/software_tasks.rs b/macros/src/codegen/software_tasks.rs deleted file mode 100644 index f9247da..0000000 --- a/macros/src/codegen/software_tasks.rs +++ /dev/null @@ -1,179 +0,0 @@ -use crate::syntax::{ast::App, Context}; -use crate::{ - analyze::Analysis, - codegen::{local_resources_struct, module, shared_resources_struct, util}, -}; -use proc_macro2::TokenStream as TokenStream2; -use quote::quote; - -pub fn codegen( - app: &App, - analysis: &Analysis, -) -> ( - // mod_app_software_tasks -- free queues, buffers and `${task}Resources` constructors - Vec, - // root_software_tasks -- items that must be placed in the root of the crate: - // - `${task}Locals` structs - // - `${task}Resources` structs - // - `${task}` modules - Vec, - // user_software_tasks -- the `#[task]` functions written by the user - Vec, -) { - let mut mod_app = vec![]; - let mut root = vec![]; - let mut user_tasks = vec![]; - - // Any task - for (name, task) in app.software_tasks.iter() { - let inputs = &task.inputs; - let (_, _, _, input_ty) = util::regroup_inputs(inputs); - - let cap = task.args.capacity; - let cap_lit = util::capacity_literal(cap as usize); - let cap_lit_p1 = util::capacity_literal(cap as usize + 1); - - if !task.is_async { - // Create free queues and inputs / instants buffers - let fq = util::fq_ident(name); - - #[allow(clippy::redundant_closure)] - let (fq_ty, fq_expr, mk_uninit): (_, _, Box Option<_>>) = { - ( - quote!(rtic::export::SCFQ<#cap_lit_p1>), - quote!(rtic::export::Queue::new()), - Box::new(|| Some(util::link_section_uninit())), - ) - }; - - mod_app.push(quote!( - // /// Queue version of a free-list that keeps track of empty slots in - // /// the following buffers - #[allow(non_camel_case_types)] - #[allow(non_upper_case_globals)] - #[doc(hidden)] - static #fq: rtic::RacyCell<#fq_ty> = rtic::RacyCell::new(#fq_expr); - )); - - let elems = &(0..cap) - .map(|_| quote!(core::mem::MaybeUninit::uninit())) - .collect::>(); - - for (_, monotonic) in &app.monotonics { - let instants = util::monotonic_instants_ident(name, &monotonic.ident); - let mono_type = &monotonic.ty; - - let uninit = mk_uninit(); - // For future use - // let doc = format!(" RTIC internal: {}:{}", file!(), line!()); - mod_app.push(quote!( - #uninit - // /// Buffer that holds the instants associated to the inputs of a task - // #[doc = #doc] - #[allow(non_camel_case_types)] - #[allow(non_upper_case_globals)] - #[doc(hidden)] - static #instants: - rtic::RacyCell<[core::mem::MaybeUninit<<#mono_type as rtic::Monotonic>::Instant>; #cap_lit]> = - rtic::RacyCell::new([#(#elems,)*]); - )); - } - - let uninit = mk_uninit(); - let inputs_ident = util::inputs_ident(name); - - // Buffer that holds the inputs of a task - mod_app.push(quote!( - #uninit - #[allow(non_camel_case_types)] - #[allow(non_upper_case_globals)] - #[doc(hidden)] - static #inputs_ident: rtic::RacyCell<[core::mem::MaybeUninit<#input_ty>; #cap_lit]> = - rtic::RacyCell::new([#(#elems,)*]); - )); - } - - if task.is_async { - let executor_ident = util::executor_run_ident(name); - mod_app.push(quote!( - #[allow(non_camel_case_types)] - #[allow(non_upper_case_globals)] - #[doc(hidden)] - static #executor_ident: core::sync::atomic::AtomicBool = - core::sync::atomic::AtomicBool::new(false); - )); - } - - let inputs = &task.inputs; - - // `${task}Resources` - let mut shared_needs_lt = false; - let mut local_needs_lt = false; - - // `${task}Locals` - if !task.args.local_resources.is_empty() { - let (item, constructor) = local_resources_struct::codegen( - Context::SoftwareTask(name), - &mut local_needs_lt, - app, - ); - - root.push(item); - - mod_app.push(constructor); - } - - if !task.args.shared_resources.is_empty() { - let (item, constructor) = shared_resources_struct::codegen( - Context::SoftwareTask(name), - &mut shared_needs_lt, - app, - ); - - root.push(item); - - mod_app.push(constructor); - } - - if !&task.is_extern { - let context = &task.context; - let attrs = &task.attrs; - let cfgs = &task.cfgs; - let stmts = &task.stmts; - let (async_marker, context_lifetime) = if task.is_async { - ( - quote!(async), - if shared_needs_lt || local_needs_lt { - quote!(<'static>) - } else { - quote!() - }, - ) - } else { - (quote!(), quote!()) - }; - - user_tasks.push(quote!( - #(#attrs)* - #(#cfgs)* - #[allow(non_snake_case)] - #async_marker fn #name(#context: #name::Context #context_lifetime #(,#inputs)*) { - use rtic::Mutex as _; - use rtic::mutex::prelude::*; - - #(#stmts)* - } - )); - } - - root.push(module::codegen( - Context::SoftwareTask(name), - shared_needs_lt, - local_needs_lt, - app, - analysis, - )); - } - - (mod_app, root, user_tasks) -} diff --git a/macros/src/codegen/timer_queue.rs b/macros/src/codegen/timer_queue.rs deleted file mode 100644 index 281148d..0000000 --- a/macros/src/codegen/timer_queue.rs +++ /dev/null @@ -1,170 +0,0 @@ -use crate::syntax::ast::App; -use crate::{analyze::Analysis, codegen::util}; -use proc_macro2::TokenStream as TokenStream2; -use quote::quote; - -/// Generates timer queues and timer queue handlers -#[allow(clippy::too_many_lines)] -pub fn codegen(app: &App, analysis: &Analysis) -> Vec { - let mut items = vec![]; - - if !app.monotonics.is_empty() { - // Generate the marker counter used to track for `cancel` and `reschedule` - let tq_marker = util::timer_queue_marker_ident(); - items.push(quote!( - // #[doc = #doc] - #[doc(hidden)] - #[allow(non_camel_case_types)] - #[allow(non_upper_case_globals)] - static #tq_marker: rtic::RacyCell = rtic::RacyCell::new(0); - )); - - let t = util::schedule_t_ident(); - - // Enumeration of `schedule`-able tasks - { - let variants = app - .software_tasks - .iter() - .filter(|(_, task)| !task.is_async) - .map(|(name, task)| { - let cfgs = &task.cfgs; - - quote!( - #(#cfgs)* - #name - ) - }) - .collect::>(); - - // For future use - // let doc = "Tasks that can be scheduled".to_string(); - items.push(quote!( - // #[doc = #doc] - #[doc(hidden)] - #[allow(non_camel_case_types)] - #[derive(Clone, Copy)] - pub enum #t { - #(#variants,)* - } - )); - } - } - - for (_, monotonic) in &app.monotonics { - let monotonic_name = monotonic.ident.to_string(); - let tq = util::tq_ident(&monotonic_name); - let t = util::schedule_t_ident(); - let mono_type = &monotonic.ty; - let m_ident = util::monotonic_ident(&monotonic_name); - - // Static variables and resource proxy - { - // For future use - // let doc = &format!("Timer queue for {}", monotonic_name); - let cap: usize = app - .software_tasks - .iter() - .map(|(_name, task)| task.args.capacity as usize) - .sum(); - let n_task = util::capacity_literal(cap); - let tq_ty = quote!(rtic::export::TimerQueue<#mono_type, #t, #n_task>); - - // For future use - // let doc = format!(" RTIC internal: {}:{}", file!(), line!()); - items.push(quote!( - #[doc(hidden)] - #[allow(non_camel_case_types)] - #[allow(non_upper_case_globals)] - static #tq: rtic::RacyCell<#tq_ty> = rtic::RacyCell::new( - rtic::export::TimerQueue { - task_queue: rtic::export::SortedLinkedList::new_u16(), - waker_queue: rtic::export::IntrusiveSortedLinkedList::new(), - } - ); - )); - - let mono = util::monotonic_ident(&monotonic_name); - // For future use - // let doc = &format!("Storage for {}", monotonic_name); - - items.push(quote!( - #[doc(hidden)] - #[allow(non_camel_case_types)] - #[allow(non_upper_case_globals)] - static #mono: rtic::RacyCell> = rtic::RacyCell::new(None); - )); - } - - // Timer queue handler - { - let enum_ = util::interrupt_ident(); - let rt_err = util::rt_err_ident(); - - let arms = app - .software_tasks - .iter() - .filter(|(_, task)| !task.is_async) - .map(|(name, task)| { - let cfgs = &task.cfgs; - let priority = task.args.priority; - let rq = util::rq_ident(priority); - let rqt = util::spawn_t_ident(priority); - - // The interrupt that runs the task dispatcher - let interrupt = &analysis.interrupts_normal.get(&priority).expect("RTIC-ICE: interrupt not found").0; - - let pend = { - quote!( - rtic::pend(#rt_err::#enum_::#interrupt); - ) - }; - - quote!( - #(#cfgs)* - #t::#name => { - rtic::export::interrupt::free(|_| - (&mut *#rq.get_mut()).split().0.enqueue_unchecked((#rqt::#name, index)) - ); - - #pend - } - ) - }) - .collect::>(); - - let bound_interrupt = &monotonic.args.binds; - let disable_isr = if &*bound_interrupt.to_string() == "SysTick" { - quote!(core::mem::transmute::<_, rtic::export::SYST>(()).disable_interrupt()) - } else { - quote!(rtic::export::NVIC::mask(#rt_err::#enum_::#bound_interrupt)) - }; - - items.push(quote!( - #[no_mangle] - #[allow(non_snake_case)] - unsafe fn #bound_interrupt() { - while let Some((task, index)) = rtic::export::interrupt::free(|_| - if let Some(mono) = (&mut *#m_ident.get_mut()).as_mut() { - (&mut *#tq.get_mut()).dequeue(|| #disable_isr, mono) - } else { - // We can only use the timer queue if `init` has returned, and it - // writes the `Some(monotonic)` we are accessing here. - core::hint::unreachable_unchecked() - }) - { - match task { - #(#arms)* - } - } - - rtic::export::interrupt::free(|_| if let Some(mono) = (&mut *#m_ident.get_mut()).as_mut() { - mono.on_interrupt(); - }); - } - )); - } - } - - items -} diff --git a/macros/src/codegen/util.rs b/macros/src/codegen/util.rs index 151906d..61bde98 100644 --- a/macros/src/codegen/util.rs +++ b/macros/src/codegen/util.rs @@ -3,20 +3,10 @@ use core::sync::atomic::{AtomicUsize, Ordering}; use crate::syntax::{ast::App, Context}; use proc_macro2::{Span, TokenStream as TokenStream2}; use quote::quote; -use syn::{Attribute, Ident, LitInt, PatType}; +use syn::{Attribute, Ident}; const RTIC_INTERNAL: &str = "__rtic_internal"; -/// Turns `capacity` into an unsuffixed integer literal -pub fn capacity_literal(capacity: usize) -> LitInt { - LitInt::new(&capacity.to_string(), Span::call_site()) -} - -/// Identifier for the free queue -pub fn fq_ident(task: &Ident) -> Ident { - mark_internal_name(&format!("{}_FQ", task)) -} - /// Generates a `Mutex` implementation pub fn impl_mutex( app: &App, @@ -60,30 +50,16 @@ pub fn impl_mutex( ) } -/// Generates an identifier for the `INPUTS` buffer (`spawn` & `schedule` API) -pub fn inputs_ident(task: &Ident) -> Ident { - mark_internal_name(&format!("{}_INPUTS", task)) -} - /// Generates an identifier for the `EXECUTOR_RUN` atomics (`async` API) pub fn executor_run_ident(task: &Ident) -> Ident { mark_internal_name(&format!("{}_EXECUTOR_RUN", task)) } -/// Generates an identifier for the `INSTANTS` buffer (`schedule` API) -pub fn monotonic_instants_ident(task: &Ident, monotonic: &Ident) -> Ident { - mark_internal_name(&format!("{}_{}_INSTANTS", task, monotonic)) -} - pub fn interrupt_ident() -> Ident { let span = Span::call_site(); Ident::new("interrupt", span) } -pub fn timer_queue_marker_ident() -> Ident { - mark_internal_name("TIMER_QUEUE_MARKER") -} - /// Whether `name` is an exception with configurable priority pub fn is_exception(name: &Ident) -> bool { let s = name.to_string(); @@ -106,11 +82,6 @@ pub fn mark_internal_name(name: &str) -> Ident { Ident::new(&format!("{}_{}", RTIC_INTERNAL, name), Span::call_site()) } -/// Generate an internal identifier for monotonics -pub fn internal_monotonics_ident(task: &Ident, monotonic: &Ident, ident_name: &str) -> Ident { - mark_internal_name(&format!("{}_{}_{}", task, monotonic, ident_name,)) -} - /// Generate an internal identifier for tasks pub fn internal_task_ident(task: &Ident, ident_name: &str) -> Ident { mark_internal_name(&format!("{}_{}", task, ident_name)) @@ -129,55 +100,6 @@ pub fn link_section_uninit() -> TokenStream2 { quote!(#[link_section = #section]) } -// Regroups the inputs of a task -// -// `inputs` could be &[`input: Foo`] OR &[`mut x: i32`, `ref y: i64`] -pub fn regroup_inputs( - inputs: &[PatType], -) -> ( - // args e.g. &[`_0`], &[`_0: i32`, `_1: i64`] - Vec, - // tupled e.g. `_0`, `(_0, _1)` - TokenStream2, - // untupled e.g. &[`_0`], &[`_0`, `_1`] - Vec, - // ty e.g. `Foo`, `(i32, i64)` - TokenStream2, -) { - if inputs.len() == 1 { - let ty = &inputs[0].ty; - - ( - vec![quote!(_0: #ty)], - quote!(_0), - vec![quote!(_0)], - quote!(#ty), - ) - } else { - let mut args = vec![]; - let mut pats = vec![]; - let mut tys = vec![]; - - for (i, input) in inputs.iter().enumerate() { - let i = Ident::new(&format!("_{}", i), Span::call_site()); - let ty = &input.ty; - - args.push(quote!(#i: #ty)); - - pats.push(quote!(#i)); - - tys.push(quote!(#ty)); - } - - let tupled = { - let pats = pats.clone(); - quote!((#(#pats,)*)) - }; - let ty = quote!((#(#tys,)*)); - (args, tupled, pats, ty) - } -} - /// Get the ident for the name of the task pub fn get_task_name(ctxt: Context, app: &App) -> Ident { let s = match ctxt { @@ -230,48 +152,17 @@ pub fn local_resources_ident(ctxt: Context, app: &App) -> Ident { mark_internal_name(&s) } -/// Generates an identifier for a ready queue -/// -/// There may be several task dispatchers, one for each priority level. -/// The ready queues are SPSC queues -pub fn rq_ident(priority: u8) -> Ident { - mark_internal_name(&format!("P{}_RQ", priority)) -} - /// Generates an identifier for a ready queue, async task version pub fn rq_async_ident(async_task_name: &Ident) -> Ident { mark_internal_name(&format!("ASYNC_TACK_{}_RQ", async_task_name)) } -/// Generates an identifier for the `enum` of `schedule`-able tasks -pub fn schedule_t_ident() -> Ident { - mark_internal_name("SCHED_T") -} - -/// Generates an identifier for the `enum` of `spawn`-able tasks -/// -/// This identifier needs the same structure as the `RQ` identifier because there's one ready queue -/// for each of these `T` enums -pub fn spawn_t_ident(priority: u8) -> Ident { - mark_internal_name(&format!("P{}_T", priority)) -} - /// Suffixed identifier pub fn suffixed(name: &str) -> Ident { let span = Span::call_site(); Ident::new(name, span) } -/// Generates an identifier for a timer queue -pub fn tq_ident(name: &str) -> Ident { - mark_internal_name(&format!("TQ_{}", name)) -} - -/// Generates an identifier for monotonic timer storage -pub fn monotonic_ident(name: &str) -> Ident { - mark_internal_name(&format!("MONOTONIC_STORAGE_{}", name)) -} - pub fn static_shared_resource_ident(name: &Ident) -> Ident { mark_internal_name(&format!("shared_resource_{}", name)) } diff --git a/macros/src/syntax/analyze.rs b/macros/src/syntax/analyze.rs index 44960b9..ff0577d 100644 --- a/macros/src/syntax/analyze.rs +++ b/macros/src/syntax/analyze.rs @@ -16,19 +16,11 @@ pub(crate) fn app(app: &App) -> Result { type TaskName = String; type Priority = u8; - // The task list is a Tuple (Name, Shared Resources, Local Resources, Priority, IsAsync) - let task_resources_list: Vec<(TaskName, Vec<&Ident>, &LocalResources, Priority, bool)> = + // The task list is a Tuple (Name, Shared Resources, Local Resources, Priority) + let task_resources_list: Vec<(TaskName, Vec<&Ident>, &LocalResources, Priority)> = Some(&app.init) .iter() - .map(|ht| { - ( - "init".to_string(), - Vec::new(), - &ht.args.local_resources, - 0, - false, - ) - }) + .map(|ht| ("init".to_string(), Vec::new(), &ht.args.local_resources, 0)) .chain(app.idle.iter().map(|ht| { ( "idle".to_string(), @@ -39,7 +31,6 @@ pub(crate) fn app(app: &App) -> Result { .collect::>(), &ht.args.local_resources, 0, - false, ) })) .chain(app.software_tasks.iter().map(|(name, ht)| { @@ -52,7 +43,6 @@ pub(crate) fn app(app: &App) -> Result { .collect::>(), &ht.args.local_resources, ht.args.priority, - ht.is_async, ) })) .chain(app.hardware_tasks.iter().map(|(name, ht)| { @@ -65,7 +55,6 @@ pub(crate) fn app(app: &App) -> Result { .collect::>(), &ht.args.local_resources, ht.args.priority, - false, ) })) .collect(); @@ -84,21 +73,20 @@ pub(crate) fn app(app: &App) -> Result { // Check that lock_free resources are correct for lf_res in lock_free.iter() { - for (task, tr, _, priority, is_async) in task_resources_list.iter() { + for (task, tr, _, priority) in task_resources_list.iter() { for r in tr { // Get all uses of resources annotated lock_free if lf_res == r { // lock_free resources are not allowed in async tasks - if *is_async { - error.push(syn::Error::new( + error.push(syn::Error::new( r.span(), format!( "Lock free shared resource {:?} is used by an async tasks, which is forbidden", r.to_string(), ), )); - } + // TODO: Should this be removed? // HashMap returns the previous existing object if old.key == new.key if let Some(lf_res) = lf_hash.insert(r.to_string(), (task, r, priority)) { // Check if priority differ, if it does, append to @@ -150,7 +138,7 @@ pub(crate) fn app(app: &App) -> Result { // Check that local resources are not shared for lr in local { - for (task, _, local_resources, _, _) in task_resources_list.iter() { + for (task, _, local_resources, _) in task_resources_list.iter() { for (name, res) in local_resources.iter() { // Get all uses of resources annotated lock_free if lr == name { @@ -193,18 +181,7 @@ pub(crate) fn app(app: &App) -> Result { error.push(syn::Error::new( name.span(), format!( - "Software task {:?} has priority 0, but `#[idle]` is defined. 0-priority software tasks are only allowed if there is no `#[idle]`.", - name.to_string(), - ) - )); - } - - // 0-priority tasks must be async - if !task.is_async { - error.push(syn::Error::new( - name.span(), - format!( - "Software task {:?} has priority 0, but is not `async`. 0-priority software tasks must be `async`.", + "Async task {:?} has priority 0, but `#[idle]` is defined. 0-priority async tasks are only allowed if there is no `#[idle]`.", name.to_string(), ) )); @@ -263,7 +240,7 @@ pub(crate) fn app(app: &App) -> Result { // Create the list of used local resource Idents let mut used_local_resource = IndexSet::new(); - for (_, _, locals, _, _) in task_resources_list { + for (_, _, locals, _) in task_resources_list { for (local, _) in locals { used_local_resource.insert(local.clone()); } @@ -307,27 +284,11 @@ pub(crate) fn app(app: &App) -> Result { let channel = channels.entry(spawnee_prio).or_default(); channel.tasks.insert(name.clone()); - - if !spawnee.args.only_same_priority_spawn { - // Require `Send` if the task can be spawned from other priorities - spawnee.inputs.iter().for_each(|input| { - send_types.insert(input.ty.clone()); - }); - } } // No channel should ever be empty debug_assert!(channels.values().all(|channel| !channel.tasks.is_empty())); - // Compute channel capacities - for channel in channels.values_mut() { - channel.capacity = channel - .tasks - .iter() - .map(|name| app.software_tasks[name].args.capacity) - .sum(); - } - Ok(Analysis { channels, shared_resources: used_shared_resource, diff --git a/macros/src/syntax/ast.rs b/macros/src/syntax/ast.rs index 0f2e36f..ea6e402 100644 --- a/macros/src/syntax/ast.rs +++ b/macros/src/syntax/ast.rs @@ -1,6 +1,6 @@ //! Abstract Syntax Tree -use syn::{Attribute, Expr, Ident, Item, ItemUse, Pat, PatType, Path, Stmt, Type}; +use syn::{Attribute, Expr, Ident, Item, ItemUse, Pat, Path, Stmt, Type}; use crate::syntax::Map; @@ -20,9 +20,6 @@ pub struct App { /// The `#[idle]` function pub idle: Option, - /// Monotonic clocks - pub monotonics: Map, - /// Resources shared between tasks defined in `#[shared]` pub shared_resources: Map, @@ -38,7 +35,7 @@ pub struct App { /// Hardware tasks: `#[task(binds = ..)]`s pub hardware_tasks: Map, - /// Software tasks: `#[task]` + /// Async software tasks: `#[task]` pub software_tasks: Map, } @@ -192,38 +189,7 @@ pub struct LocalResource { pub ty: Box, } -/// Monotonic -#[derive(Debug)] -#[non_exhaustive] -pub struct Monotonic { - /// `#[cfg]` attributes like `#[cfg(debug_assertions)]` - pub cfgs: Vec, - - /// The identifier of the monotonic - pub ident: Ident, - - /// The type of this monotonic - pub ty: Box, - - /// Monotonic args - pub args: MonotonicArgs, -} - -/// Monotonic metadata -#[derive(Debug)] -#[non_exhaustive] -pub struct MonotonicArgs { - /// The interrupt or exception that this monotonic is bound to - pub binds: Ident, - - /// The priority of this monotonic - pub priority: Option, - - /// If this is the default monotonic - pub default: bool, -} - -/// A software task +/// An async software task #[derive(Debug)] #[non_exhaustive] pub struct SoftwareTask { @@ -239,26 +205,17 @@ pub struct SoftwareTask { /// The context argument pub context: Box, - /// The inputs of this software task - pub inputs: Vec, - /// The statements that make up the task handler pub stmts: Vec, /// The task is declared externally pub is_extern: bool, - - /// If the task is marked as `async` - pub is_async: bool, } /// Software task metadata #[derive(Debug)] #[non_exhaustive] pub struct SoftwareTaskArgs { - /// The task capacity: the maximum number of pending messages that can be queued - pub capacity: u8, - /// The priority of this task pub priority: u8, @@ -275,7 +232,6 @@ pub struct SoftwareTaskArgs { impl Default for SoftwareTaskArgs { fn default() -> Self { Self { - capacity: 1, priority: 1, local_resources: LocalResources::new(), shared_resources: SharedResources::new(), diff --git a/macros/src/syntax/parse.rs b/macros/src/syntax/parse.rs index ceedaa9..abdd677 100644 --- a/macros/src/syntax/parse.rs +++ b/macros/src/syntax/parse.rs @@ -2,7 +2,6 @@ mod app; mod hardware_task; mod idle; mod init; -mod monotonic; mod resource; mod software_task; mod util; @@ -11,15 +10,12 @@ use proc_macro2::TokenStream as TokenStream2; use syn::{ braced, parenthesized, parse::{self, Parse, ParseStream, Parser}, - token::{self, Brace}, - Ident, Item, LitBool, LitInt, Path, Token, + token::Brace, + Ident, Item, LitInt, Token, }; use crate::syntax::{ - ast::{ - App, AppArgs, HardwareTaskArgs, IdleArgs, InitArgs, MonotonicArgs, SoftwareTaskArgs, - TaskLocal, - }, + ast::{App, AppArgs, HardwareTaskArgs, IdleArgs, InitArgs, SoftwareTaskArgs, TaskLocal}, Either, }; @@ -388,7 +384,6 @@ fn task_args(tokens: TokenStream2) -> parse::Result parse::Result parse::Result { - (|input: ParseStream<'_>| -> parse::Result { - let mut binds = None; - let mut priority = None; - let mut default = None; - - if !input.peek(token::Paren) { - return Err(parse::Error::new( - path.segments.first().unwrap().ident.span(), - "expected opening ( in #[monotonic( ... )]", - )); - } - - let content; - parenthesized!(content in input); - - if !content.is_empty() { - loop { - // Parse identifier name - let ident: Ident = content.parse()?; - // Handle equal sign - let _: Token![=] = content.parse()?; - - match &*ident.to_string() { - "binds" => { - if binds.is_some() { - return Err(parse::Error::new( - ident.span(), - "argument appears more than once", - )); - } - // Parse identifier name - let ident = content.parse()?; - - binds = Some(ident); - } - - "priority" => { - if priority.is_some() { - return Err(parse::Error::new( - ident.span(), - "argument appears more than once", - )); - } - - // #lit - let lit: LitInt = content.parse()?; - - if !lit.suffix().is_empty() { - return Err(parse::Error::new( - lit.span(), - "this literal must be unsuffixed", - )); - } - - let value = lit.base10_parse::().ok(); - if value.is_none() || value == Some(0) { - return Err(parse::Error::new( - lit.span(), - "this literal must be in the range 1...255", - )); - } - - priority = Some(value.unwrap()); - } - - "default" => { - if default.is_some() { - return Err(parse::Error::new( - ident.span(), - "argument appears more than once", - )); - } - - let lit: LitBool = content.parse()?; - default = Some(lit.value); - } - - _ => { - return Err(parse::Error::new(ident.span(), "unexpected argument")); - } - } - if content.is_empty() { - break; - } - - // Handle comma: , - let _: Token![,] = content.parse()?; - } - } - - let binds = if let Some(r) = binds { - r - } else { - return Err(parse::Error::new( - content.span(), - "`binds = ...` is missing", - )); - }; - let default = default.unwrap_or(false); - - Ok(MonotonicArgs { - binds, - priority, - default, - }) - }) - .parse2(tokens) -} diff --git a/macros/src/syntax/parse/app.rs b/macros/src/syntax/parse/app.rs index dd7c399..8a9242e 100644 --- a/macros/src/syntax/parse/app.rs +++ b/macros/src/syntax/parse/app.rs @@ -5,14 +5,14 @@ use proc_macro2::TokenStream as TokenStream2; use syn::{ parse::{self, ParseStream, Parser}, spanned::Spanned, - Expr, ExprArray, Fields, ForeignItem, Ident, Item, LitBool, Path, Token, Type, Visibility, + Expr, ExprArray, Fields, ForeignItem, Ident, Item, LitBool, Path, Token, Visibility, }; use super::Input; use crate::syntax::{ ast::{ App, AppArgs, Dispatcher, Dispatchers, HardwareTask, Idle, IdleArgs, Init, InitArgs, - LocalResource, Monotonic, MonotonicArgs, SharedResource, SoftwareTask, + LocalResource, SharedResource, SoftwareTask, }, parse::{self as syntax_parse, util}, Either, Map, Set, @@ -150,7 +150,6 @@ impl App { let mut shared_resources = Map::new(); let mut local_resources_ident = None; let mut local_resources = Map::new(); - let mut monotonics = Map::new(); let mut hardware_tasks = Map::new(); let mut software_tasks = Map::new(); let mut user_imports = vec![]; @@ -158,7 +157,6 @@ impl App { let mut seen_idents = HashSet::::new(); let mut bindings = HashSet::::new(); - let mut monotonic_types = HashSet::::new(); let mut check_binding = |ident: &Ident| { if bindings.contains(ident) { @@ -186,19 +184,6 @@ impl App { Ok(()) }; - let mut check_monotonic = |ty: &Type| { - if monotonic_types.contains(ty) { - return Err(parse::Error::new( - ty.span(), - "this type is already used by another monotonic", - )); - } else { - monotonic_types.insert(ty.clone()); - } - - Ok(()) - }; - for mut item in input.items { match item { Item::Fn(mut item) => { @@ -448,44 +433,6 @@ impl App { // Store the user provided use-statements user_imports.push(itemuse_.clone()); } - Item::Type(ref mut type_item) => { - // Match types with the attribute #[monotonic] - if let Some(pos) = type_item - .attrs - .iter() - .position(|attr| util::attr_eq(attr, "monotonic")) - { - let span = type_item.ident.span(); - - if monotonics.contains_key(&type_item.ident) { - return Err(parse::Error::new( - span, - "`#[monotonic(...)]` on a specific type must appear at most once", - )); - } - - if type_item.vis != Visibility::Inherited { - return Err(parse::Error::new( - type_item.span(), - "this item must have inherited / private visibility", - )); - } - - check_monotonic(&*type_item.ty)?; - - let m = type_item.attrs.remove(pos); - let args = MonotonicArgs::parse(m)?; - - check_binding(&args.binds)?; - - let monotonic = Monotonic::parse(args, type_item, span)?; - - monotonics.insert(type_item.ident.clone(), monotonic); - } - - // All types are passed on - user_code.push(item.clone()); - } _ => { // Anything else within the module should not make any difference user_code.push(item.clone()); @@ -524,7 +471,6 @@ impl App { name: input.ident, init, idle, - monotonics, shared_resources, local_resources, user_imports, diff --git a/macros/src/syntax/parse/hardware_task.rs b/macros/src/syntax/parse/hardware_task.rs index 304bfcd..ff94bc5 100644 --- a/macros/src/syntax/parse/hardware_task.rs +++ b/macros/src/syntax/parse/hardware_task.rs @@ -23,19 +23,17 @@ impl HardwareTask { } if valid_signature { - if let Some((context, Ok(rest))) = util::parse_inputs(item.sig.inputs, &name) { - if rest.is_empty() { - let FilterAttrs { cfgs, attrs, .. } = util::filter_attributes(item.attrs); + if let Some(context) = util::parse_inputs(item.sig.inputs, &name) { + let FilterAttrs { cfgs, attrs, .. } = util::filter_attributes(item.attrs); - return Ok(HardwareTask { - args, - cfgs, - attrs, - context, - stmts: item.block.stmts, - is_extern: false, - }); - } + return Ok(HardwareTask { + args, + cfgs, + attrs, + context, + stmts: item.block.stmts, + is_extern: false, + }); } } @@ -69,19 +67,17 @@ impl HardwareTask { } if valid_signature { - if let Some((context, Ok(rest))) = util::parse_inputs(item.sig.inputs, &name) { - if rest.is_empty() { - let FilterAttrs { cfgs, attrs, .. } = util::filter_attributes(item.attrs); + if let Some(context) = util::parse_inputs(item.sig.inputs, &name) { + let FilterAttrs { cfgs, attrs, .. } = util::filter_attributes(item.attrs); - return Ok(HardwareTask { - args, - cfgs, - attrs, - context, - stmts: Vec::::new(), - is_extern: true, - }); - } + return Ok(HardwareTask { + args, + cfgs, + attrs, + context, + stmts: Vec::::new(), + is_extern: true, + }); } } diff --git a/macros/src/syntax/parse/idle.rs b/macros/src/syntax/parse/idle.rs index d9f3a99..ffec358 100644 --- a/macros/src/syntax/parse/idle.rs +++ b/macros/src/syntax/parse/idle.rs @@ -21,16 +21,14 @@ impl Idle { let name = item.sig.ident.to_string(); if valid_signature { - if let Some((context, Ok(rest))) = util::parse_inputs(item.sig.inputs, &name) { - if rest.is_empty() { - return Ok(Idle { - args, - attrs: item.attrs, - context, - name: item.sig.ident, - stmts: item.block.stmts, - }); - } + if let Some(context) = util::parse_inputs(item.sig.inputs, &name) { + return Ok(Idle { + args, + attrs: item.attrs, + context, + name: item.sig.ident, + stmts: item.block.stmts, + }); } } diff --git a/macros/src/syntax/parse/init.rs b/macros/src/syntax/parse/init.rs index 727ee20..5ec1aba 100644 --- a/macros/src/syntax/parse/init.rs +++ b/macros/src/syntax/parse/init.rs @@ -25,18 +25,16 @@ impl Init { if let Ok((user_shared_struct, user_local_struct)) = util::type_is_init_return(&item.sig.output, &name) { - if let Some((context, Ok(rest))) = util::parse_inputs(item.sig.inputs, &name) { - if rest.is_empty() { - return Ok(Init { - args, - attrs: item.attrs, - context, - name: item.sig.ident, - stmts: item.block.stmts, - user_shared_struct, - user_local_struct, - }); - } + if let Some(context) = util::parse_inputs(item.sig.inputs, &name) { + return Ok(Init { + args, + attrs: item.attrs, + context, + name: item.sig.ident, + stmts: item.block.stmts, + user_shared_struct, + user_local_struct, + }); } } } diff --git a/macros/src/syntax/parse/software_task.rs b/macros/src/syntax/parse/software_task.rs index 2b1ac4a..6be597e 100644 --- a/macros/src/syntax/parse/software_task.rs +++ b/macros/src/syntax/parse/software_task.rs @@ -8,17 +8,16 @@ use crate::syntax::{ impl SoftwareTask { pub(crate) fn parse(args: SoftwareTaskArgs, item: ItemFn) -> parse::Result { - let valid_signature = - util::check_fn_signature(&item, true) && util::type_is_unit(&item.sig.output); + let valid_signature = util::check_fn_signature(&item, true) + && util::type_is_unit(&item.sig.output) + && item.sig.asyncness.is_some(); let span = item.sig.ident.span(); let name = item.sig.ident.to_string(); - let is_async = item.sig.asyncness.is_some(); - if valid_signature { - if let Some((context, Ok(inputs))) = util::parse_inputs(item.sig.inputs, &name) { + if let Some(context) = util::parse_inputs(item.sig.inputs, &name) { let FilterAttrs { cfgs, attrs, .. } = util::filter_attributes(item.attrs); return Ok(SoftwareTask { @@ -26,10 +25,8 @@ impl SoftwareTask { attrs, cfgs, context, - inputs, stmts: item.block.stmts, is_extern: false, - is_async, }); } } @@ -37,7 +34,7 @@ impl SoftwareTask { Err(parse::Error::new( span, &format!( - "this task handler must have type signature `(async) fn({}::Context, ..)`", + "this task handler must have type signature `async fn({}::Context)`", name ), )) @@ -49,17 +46,16 @@ impl SoftwareTask { args: SoftwareTaskArgs, item: ForeignItemFn, ) -> parse::Result { - let valid_signature = - util::check_foreign_fn_signature(&item, true) && util::type_is_unit(&item.sig.output); + let valid_signature = util::check_foreign_fn_signature(&item, true) + && util::type_is_unit(&item.sig.output) + && item.sig.asyncness.is_some(); let span = item.sig.ident.span(); let name = item.sig.ident.to_string(); - let is_async = item.sig.asyncness.is_some(); - if valid_signature { - if let Some((context, Ok(inputs))) = util::parse_inputs(item.sig.inputs, &name) { + if let Some(context) = util::parse_inputs(item.sig.inputs, &name) { let FilterAttrs { cfgs, attrs, .. } = util::filter_attributes(item.attrs); return Ok(SoftwareTask { @@ -67,10 +63,8 @@ impl SoftwareTask { attrs, cfgs, context, - inputs, stmts: Vec::::new(), is_extern: true, - is_async, }); } } @@ -78,7 +72,7 @@ impl SoftwareTask { Err(parse::Error::new( span, &format!( - "this task handler must have type signature `(async) fn({}::Context, ..)`", + "this task handler must have type signature `async fn({}::Context)`", name ), )) diff --git a/macros/src/syntax/parse/util.rs b/macros/src/syntax/parse/util.rs index 3fa51ef..119129c 100644 --- a/macros/src/syntax/parse/util.rs +++ b/macros/src/syntax/parse/util.rs @@ -3,8 +3,8 @@ use syn::{ parse::{self, ParseStream}, punctuated::Punctuated, spanned::Spanned, - Abi, AttrStyle, Attribute, Expr, FnArg, ForeignItemFn, Ident, ItemFn, Pat, PatType, Path, - PathArguments, ReturnType, Token, Type, Visibility, + Abi, AttrStyle, Attribute, Expr, FnArg, ForeignItemFn, Ident, ItemFn, Pat, Path, PathArguments, + ReturnType, Token, Type, Visibility, }; use crate::syntax::{ @@ -231,29 +231,23 @@ pub fn parse_local_resources(content: ParseStream<'_>) -> parse::Result, Result, FnArg>)>; - -pub fn parse_inputs(inputs: Punctuated, name: &str) -> ParseInputResult { +pub fn parse_inputs(inputs: Punctuated, name: &str) -> Option> { let mut inputs = inputs.into_iter(); match inputs.next() { Some(FnArg::Typed(first)) => { if type_is_path(&first.ty, &[name, "Context"]) { - let rest = inputs - .map(|arg| match arg { - FnArg::Typed(arg) => Ok(arg), - _ => Err(arg), - }) - .collect::, _>>(); - - Some((first.pat, rest)) - } else { - None + // No more inputs + if inputs.next().is_none() { + return Some(first.pat); + } } } - _ => None, + _ => {} } + + None } pub fn type_is_bottom(ty: &ReturnType) -> bool { -- cgit v1.2.3 From d27d0fe33fdb54e6a11a1e9d09a7916f19e5c9ec Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Wed, 4 Jan 2023 20:01:05 +0100 Subject: Added software task codegen back --- macros/src/codegen.rs | 11 +++- macros/src/codegen/software_tasks.rs | 101 +++++++++++++++++++++++++++++++++++ 2 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 macros/src/codegen/software_tasks.rs (limited to 'macros') diff --git a/macros/src/codegen.rs b/macros/src/codegen.rs index 618d9f3..6460afe 100644 --- a/macros/src/codegen.rs +++ b/macros/src/codegen.rs @@ -18,7 +18,7 @@ mod post_init; mod pre_init; mod shared_resources; mod shared_resources_struct; -// mod software_tasks; +mod software_tasks; // mod timer_queue; mod util; @@ -92,6 +92,9 @@ pub fn app(app: &App, analysis: &Analysis) -> TokenStream2 { let (mod_app_hardware_tasks, root_hardware_tasks, user_hardware_tasks) = hardware_tasks::codegen(app, analysis); + let (mod_app_software_tasks, root_software_tasks, user_software_tasks) = + software_tasks::codegen(app, analysis); + let mod_app_async_dispatchers = async_dispatchers::codegen(app, analysis); let user_imports = &app.user_imports; let user_code = &app.user_code; @@ -116,6 +119,8 @@ pub fn app(app: &App, analysis: &Analysis) -> TokenStream2 { #(#user_hardware_tasks)* + #(#user_software_tasks)* + #(#root)* #mod_shared_resources @@ -124,6 +129,8 @@ pub fn app(app: &App, analysis: &Analysis) -> TokenStream2 { #(#root_hardware_tasks)* + #(#root_software_tasks)* + /// app module #(#mod_app)* @@ -133,6 +140,8 @@ pub fn app(app: &App, analysis: &Analysis) -> TokenStream2 { #(#mod_app_hardware_tasks)* + #(#mod_app_software_tasks)* + #(#mod_app_async_dispatchers)* #(#mains)* diff --git a/macros/src/codegen/software_tasks.rs b/macros/src/codegen/software_tasks.rs new file mode 100644 index 0000000..b2b468c --- /dev/null +++ b/macros/src/codegen/software_tasks.rs @@ -0,0 +1,101 @@ +use crate::syntax::{ast::App, Context}; +use crate::{ + analyze::Analysis, + codegen::{local_resources_struct, module, shared_resources_struct, util}, +}; +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; + +pub fn codegen( + app: &App, + analysis: &Analysis, +) -> ( + // mod_app_software_tasks -- free queues, buffers and `${task}Resources` constructors + Vec, + // root_software_tasks -- items that must be placed in the root of the crate: + // - `${task}Locals` structs + // - `${task}Resources` structs + // - `${task}` modules + Vec, + // user_software_tasks -- the `#[task]` functions written by the user + Vec, +) { + let mut mod_app = vec![]; + let mut root = vec![]; + let mut user_tasks = vec![]; + + // Any task + for (name, task) in app.software_tasks.iter() { + let executor_ident = util::executor_run_ident(name); + mod_app.push(quote!( + #[allow(non_camel_case_types)] + #[allow(non_upper_case_globals)] + #[doc(hidden)] + static #executor_ident: core::sync::atomic::AtomicBool = + core::sync::atomic::AtomicBool::new(false); + )); + + // `${task}Resources` + let mut shared_needs_lt = false; + let mut local_needs_lt = false; + + // `${task}Locals` + if !task.args.local_resources.is_empty() { + let (item, constructor) = local_resources_struct::codegen( + Context::SoftwareTask(name), + &mut local_needs_lt, + app, + ); + + root.push(item); + + mod_app.push(constructor); + } + + if !task.args.shared_resources.is_empty() { + let (item, constructor) = shared_resources_struct::codegen( + Context::SoftwareTask(name), + &mut shared_needs_lt, + app, + ); + + root.push(item); + + mod_app.push(constructor); + } + + if !&task.is_extern { + let context = &task.context; + let attrs = &task.attrs; + let cfgs = &task.cfgs; + let stmts = &task.stmts; + let context_lifetime = if shared_needs_lt || local_needs_lt { + quote!(<'static>) + } else { + quote!() + }; + + user_tasks.push(quote!( + #(#attrs)* + #(#cfgs)* + #[allow(non_snake_case)] + async fn #name(#context: #name::Context #context_lifetime) { + use rtic::Mutex as _; + use rtic::mutex::prelude::*; + + #(#stmts)* + } + )); + } + + root.push(module::codegen( + Context::SoftwareTask(name), + shared_needs_lt, + local_needs_lt, + app, + analysis, + )); + } + + (mod_app, root, user_tasks) +} -- cgit v1.2.3 From 5c3cedf69ad07cb8522a0b040bdbf1f9ca4cf37b Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Wed, 4 Jan 2023 20:29:16 +0100 Subject: Fix fences --- macros/src/codegen/async_dispatchers.rs | 5 +++++ macros/src/codegen/module.rs | 10 ++-------- 2 files changed, 7 insertions(+), 8 deletions(-) (limited to 'macros') diff --git a/macros/src/codegen/async_dispatchers.rs b/macros/src/codegen/async_dispatchers.rs index aa854d7..c811665 100644 --- a/macros/src/codegen/async_dispatchers.rs +++ b/macros/src/codegen/async_dispatchers.rs @@ -101,7 +101,12 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec { const PRIORITY: u8 = #level; rtic::export::run(PRIORITY, || { + // Have the acquire/release semantics outside the checks to no overdo it + core::sync::atomic::fence(core::sync::atomic::Ordering::Acquire); + #(#stmts)* + + core::sync::atomic::fence(core::sync::atomic::Ordering::Release); }); } )); diff --git a/macros/src/codegen/module.rs b/macros/src/codegen/module.rs index eb0cb65..b4ad68a 100644 --- a/macros/src/codegen/module.rs +++ b/macros/src/codegen/module.rs @@ -111,14 +111,8 @@ pub fn codegen( let v = Vec::new(); let cfgs = match ctxt { - Context::HardwareTask(t) => { - &app.hardware_tasks[t].cfgs - // ... - } - Context::SoftwareTask(t) => { - &app.software_tasks[t].cfgs - // ... - } + Context::HardwareTask(t) => &app.hardware_tasks[t].cfgs, + Context::SoftwareTask(t) => &app.software_tasks[t].cfgs, _ => &v, }; -- cgit v1.2.3 From 858320cbfc391a74bff6b9c8a0b3c7696a232b76 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Wed, 4 Jan 2023 21:08:44 +0100 Subject: Even more cleanup --- macros/src/codegen.rs | 3 --- macros/src/codegen/init.rs | 5 ++--- macros/src/syntax/parse/init.rs | 4 ++-- macros/src/syntax/parse/monotonic.rs | 42 ------------------------------------ macros/src/syntax/parse/util.rs | 6 +++--- 5 files changed, 7 insertions(+), 53 deletions(-) delete mode 100644 macros/src/syntax/parse/monotonic.rs (limited to 'macros') diff --git a/macros/src/codegen.rs b/macros/src/codegen.rs index 6460afe..0f68c34 100644 --- a/macros/src/codegen.rs +++ b/macros/src/codegen.rs @@ -6,20 +6,17 @@ use crate::syntax::ast::App; mod assertions; mod async_dispatchers; -// mod dispatchers; mod hardware_tasks; mod idle; mod init; mod local_resources; mod local_resources_struct; mod module; -// mod monotonic; mod post_init; mod pre_init; mod shared_resources; mod shared_resources_struct; mod software_tasks; -// mod timer_queue; mod util; #[allow(clippy::too_many_lines)] diff --git a/macros/src/codegen/init.rs b/macros/src/codegen/init.rs index 9a6fe2d..c7b8712 100644 --- a/macros/src/codegen/init.rs +++ b/macros/src/codegen/init.rs @@ -76,7 +76,7 @@ pub fn codegen(app: &App, analysis: &Analysis) -> CodegenResult { // let locals_pat = locals_pat.iter(); - let user_init_return = quote! {#shared, #local, #name::Monotonics}; + let user_init_return = quote! {#shared, #local}; let user_init = quote!( #(#attrs)* @@ -99,9 +99,8 @@ pub fn codegen(app: &App, analysis: &Analysis) -> CodegenResult { mod_app = Some(constructor); } - // let locals_new = locals_new.iter(); let call_init = quote! { - let (shared_resources, local_resources, mut monotonics) = #name(#name::Context::new(core.into())); + let (shared_resources, local_resources) = #name(#name::Context::new(core.into())); }; root_init.push(module::codegen( diff --git a/macros/src/syntax/parse/init.rs b/macros/src/syntax/parse/init.rs index 5ec1aba..61d3539 100644 --- a/macros/src/syntax/parse/init.rs +++ b/macros/src/syntax/parse/init.rs @@ -23,7 +23,7 @@ impl Init { if valid_signature { if let Ok((user_shared_struct, user_local_struct)) = - util::type_is_init_return(&item.sig.output, &name) + util::type_is_init_return(&item.sig.output) { if let Some(context) = util::parse_inputs(item.sig.inputs, &name) { return Ok(Init { @@ -42,7 +42,7 @@ impl Init { Err(parse::Error::new( span, &format!( - "the `#[init]` function must have signature `fn({}::Context) -> (Shared resources struct, Local resources struct, {0}::Monotonics)`", + "the `#[init]` function must have signature `fn({}::Context) -> (Shared resources struct, Local resources struct)`", name ), )) diff --git a/macros/src/syntax/parse/monotonic.rs b/macros/src/syntax/parse/monotonic.rs deleted file mode 100644 index 0583233..0000000 --- a/macros/src/syntax/parse/monotonic.rs +++ /dev/null @@ -1,42 +0,0 @@ -use proc_macro2::Span; -use syn::Attribute; -use syn::{parse, spanned::Spanned, ItemType, Visibility}; - -use crate::syntax::parse::util::FilterAttrs; -use crate::syntax::{ - ast::{Monotonic, MonotonicArgs}, - parse::util, -}; - -impl MonotonicArgs { - pub(crate) fn parse(attr: Attribute) -> parse::Result { - crate::syntax::parse::monotonic_args(attr.path, attr.tokens) - } -} - -impl Monotonic { - pub(crate) fn parse(args: MonotonicArgs, item: &ItemType, span: Span) -> parse::Result { - if item.vis != Visibility::Inherited { - return Err(parse::Error::new( - span, - "this field must have inherited / private visibility", - )); - } - - let FilterAttrs { cfgs, attrs, .. } = util::filter_attributes(item.attrs.clone()); - - if !attrs.is_empty() { - return Err(parse::Error::new( - attrs[0].path.span(), - "Monotonic does not support attributes other than `#[cfg]`", - )); - } - - Ok(Monotonic { - cfgs, - ident: item.ident.clone(), - ty: item.ty.clone(), - args, - }) - } -} diff --git a/macros/src/syntax/parse/util.rs b/macros/src/syntax/parse/util.rs index 119129c..28c3eac 100644 --- a/macros/src/syntax/parse/util.rs +++ b/macros/src/syntax/parse/util.rs @@ -277,18 +277,18 @@ fn extract_init_resource_name_ident(ty: Type) -> Result { } /// Checks Init's return type, return the user provided types for analysis -pub fn type_is_init_return(ty: &ReturnType, name: &str) -> Result<(Ident, Ident), ()> { +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, {name}::Monotonics) + // 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() == 3 && type_is_path(&t.elems[2], &[name, "Monotonics"]) { + 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())?, -- cgit v1.2.3 From 3b97531a5c40293e265999db543acec365c629df Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Wed, 4 Jan 2023 21:33:41 +0100 Subject: First example builds again --- macros/src/codegen/async_dispatchers.rs | 15 ++++++--------- macros/src/codegen/module.rs | 11 ++++++----- macros/src/codegen/util.rs | 2 +- 3 files changed, 13 insertions(+), 15 deletions(-) (limited to 'macros') diff --git a/macros/src/codegen/async_dispatchers.rs b/macros/src/codegen/async_dispatchers.rs index c811665..f428cef 100644 --- a/macros/src/codegen/async_dispatchers.rs +++ b/macros/src/codegen/async_dispatchers.rs @@ -45,23 +45,20 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec { let executor_run_ident = util::executor_run_ident(name); let rq = util::rq_async_ident(name); - let (rq_ty, rq_expr) = { - ( - quote!(rtic::export::ASYNCRQ<(), 2>), // TODO: This needs updating to a counter instead of a queue - quote!(rtic::export::Queue::new()), - ) - }; items.push(quote!( #[doc(hidden)] #[allow(non_camel_case_types)] #[allow(non_upper_case_globals)] - static #rq: rtic::RacyCell<#rq_ty> = rtic::RacyCell::new(#rq_expr); + static #rq: core::sync::atomic::AtomicBool = core::sync::atomic::AtomicBool::new(false); )); stmts.push(quote!( if !(&*#exec_name.get()).is_running() { - if let Some(()) = rtic::export::interrupt::free(|_| (&mut *#rq.get_mut()).dequeue()) { + // TODO Fix this to be compare and swap + if #rq.load(core::sync::atomic::Ordering::Relaxed) { + #rq.store(false, core::sync::atomic::Ordering::Relaxed); + // The async executor needs a static priority #prio_name.get_mut().write(rtic::export::Priority::new(PRIORITY)); @@ -77,7 +74,7 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec { if (&mut *#exec_name.get_mut()).poll(|| { #executor_run_ident.store(true, core::sync::atomic::Ordering::Release); rtic::pend(#device::#enum_::#interrupt); - }) && !rtic::export::interrupt::free(|_| (&*#rq.get_mut()).is_empty()) { + }) && #rq.load(core::sync::atomic::Ordering::Relaxed) { // If the ready queue is not empty and the executor finished, restart this // dispatch to check if the executor should be restarted. rtic::pend(#device::#enum_::#interrupt); diff --git a/macros/src/codegen/module.rs b/macros/src/codegen/module.rs index b4ad68a..7bbfdf3 100644 --- a/macros/src/codegen/module.rs +++ b/macros/src/codegen/module.rs @@ -183,13 +183,14 @@ pub fn codegen( #[doc(hidden)] pub fn #internal_spawn_ident() -> Result<(), ()> { unsafe { - let r = rtic::export::interrupt::free(|_| (&mut *#rq.get_mut()).enqueue(())); - - if r.is_ok() { + // TODO: Fix this to be compare and swap + if #rq.load(core::sync::atomic::Ordering::Acquire) { + Err(()) + } else { + #rq.store(true, core::sync::atomic::Ordering::Release); rtic::pend(#device::#enum_::#interrupt); + Ok(()) } - - r } })); diff --git a/macros/src/codegen/util.rs b/macros/src/codegen/util.rs index 61bde98..aa720c0 100644 --- a/macros/src/codegen/util.rs +++ b/macros/src/codegen/util.rs @@ -154,7 +154,7 @@ pub fn local_resources_ident(ctxt: Context, app: &App) -> Ident { /// Generates an identifier for a ready queue, async task version pub fn rq_async_ident(async_task_name: &Ident) -> Ident { - mark_internal_name(&format!("ASYNC_TACK_{}_RQ", async_task_name)) + mark_internal_name(&format!("ASYNC_TASK_{}_RQ", async_task_name)) } /// Suffixed identifier -- cgit v1.2.3 From 714020a624ca93c42d5da7ebe612e7fc668e1471 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Sat, 7 Jan 2023 11:24:13 +0100 Subject: Removed Priority, simplified lifetime handling --- macros/src/bindings.rs | 1 + macros/src/codegen.rs | 4 +++ macros/src/codegen/async_dispatchers.rs | 17 ++------- macros/src/codegen/hardware_tasks.rs | 31 ++++++----------- macros/src/codegen/idle.rs | 18 +++------- macros/src/codegen/init.rs | 11 ++---- macros/src/codegen/local_resources_struct.rs | 8 ++--- macros/src/codegen/module.rs | 50 ++++++--------------------- macros/src/codegen/shared_resources.rs | 13 ++----- macros/src/codegen/shared_resources_struct.rs | 13 ++----- macros/src/codegen/software_tasks.rs | 31 ++++------------- macros/src/codegen/util.rs | 7 ++-- 12 files changed, 52 insertions(+), 152 deletions(-) (limited to 'macros') diff --git a/macros/src/bindings.rs b/macros/src/bindings.rs index e69de29..8b13789 100644 --- a/macros/src/bindings.rs +++ b/macros/src/bindings.rs @@ -0,0 +1 @@ + diff --git a/macros/src/codegen.rs b/macros/src/codegen.rs index 0f68c34..b490d7a 100644 --- a/macros/src/codegen.rs +++ b/macros/src/codegen.rs @@ -19,6 +19,10 @@ mod shared_resources_struct; mod software_tasks; mod util; +// 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 { let mut mod_app = vec![]; diff --git a/macros/src/codegen/async_dispatchers.rs b/macros/src/codegen/async_dispatchers.rs index f428cef..d53d7b5 100644 --- a/macros/src/codegen/async_dispatchers.rs +++ b/macros/src/codegen/async_dispatchers.rs @@ -13,7 +13,6 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec { for (name, _) in app.software_tasks.iter() { let type_name = util::internal_task_ident(name, "F"); let exec_name = util::internal_task_ident(name, "EXEC"); - let prio_name = util::internal_task_ident(name, "PRIORITY"); items.push(quote!( #[allow(non_camel_case_types)] @@ -22,12 +21,6 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec { static #exec_name: rtic::RacyCell> = rtic::RacyCell::new(rtic::export::executor::AsyncTaskExecutor::new()); - - // The executors priority, this can be any value - we will overwrite it when we - // start a task - #[allow(non_upper_case_globals)] - static #prio_name: rtic::RacyCell = - unsafe { rtic::RacyCell::new(rtic::export::Priority::new(0)) }; )); } @@ -39,7 +32,6 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec { for name in channel.tasks.iter() { let exec_name = util::internal_task_ident(name, "EXEC"); - let prio_name = util::internal_task_ident(name, "PRIORITY"); // let task = &app.software_tasks[name]; // let cfgs = &task.cfgs; let executor_run_ident = util::executor_run_ident(name); @@ -57,14 +49,9 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec { if !(&*#exec_name.get()).is_running() { // TODO Fix this to be compare and swap if #rq.load(core::sync::atomic::Ordering::Relaxed) { - #rq.store(false, core::sync::atomic::Ordering::Relaxed); - - - // The async executor needs a static priority - #prio_name.get_mut().write(rtic::export::Priority::new(PRIORITY)); - let priority: &'static _ = &*#prio_name.get(); + #rq.store(false, core::sync::atomic::Ordering::Relaxed); - (&mut *#exec_name.get_mut()).spawn(#name(#name::Context::new(priority))); + (&mut *#exec_name.get_mut()).spawn(#name(#name::Context::new())); #executor_run_ident.store(true, core::sync::atomic::Ordering::Relaxed); } } diff --git a/macros/src/codegen/hardware_tasks.rs b/macros/src/codegen/hardware_tasks.rs index 2a81d9a..9ea5825 100644 --- a/macros/src/codegen/hardware_tasks.rs +++ b/macros/src/codegen/hardware_tasks.rs @@ -41,22 +41,16 @@ pub fn codegen( rtic::export::run(PRIORITY, || { #name( - #name::Context::new(&rtic::export::Priority::new(PRIORITY)) + #name::Context::new() ) }); } )); - let mut shared_needs_lt = false; - let mut local_needs_lt = false; - // `${task}Locals` if !task.args.local_resources.is_empty() { - let (item, constructor) = local_resources_struct::codegen( - Context::HardwareTask(name), - &mut local_needs_lt, - app, - ); + let (item, constructor) = + local_resources_struct::codegen(Context::HardwareTask(name), app); root.push(item); @@ -65,24 +59,19 @@ pub fn codegen( // `${task}Resources` if !task.args.shared_resources.is_empty() { - let (item, constructor) = shared_resources_struct::codegen( - Context::HardwareTask(name), - &mut shared_needs_lt, - app, - ); + let (item, constructor) = + shared_resources_struct::codegen(Context::HardwareTask(name), app); root.push(item); mod_app.push(constructor); } - root.push(module::codegen( - Context::HardwareTask(name), - shared_needs_lt, - local_needs_lt, - app, - analysis, - )); + // Module generation... + + root.push(module::codegen(Context::HardwareTask(name), app, analysis)); + + // End module generation if !task.is_extern { let attrs = &task.attrs; diff --git a/macros/src/codegen/idle.rs b/macros/src/codegen/idle.rs index 9867939..a4f6325 100644 --- a/macros/src/codegen/idle.rs +++ b/macros/src/codegen/idle.rs @@ -24,37 +24,27 @@ pub fn codegen( TokenStream2, ) { if let Some(idle) = &app.idle { - let mut shared_needs_lt = false; - let mut local_needs_lt = false; 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, &mut shared_needs_lt, app); + 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, &mut local_needs_lt, app); + 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, - shared_needs_lt, - local_needs_lt, - app, - analysis, - )); + root_idle.push(module::codegen(Context::Idle, app, analysis)); let attrs = &idle.attrs; let context = &idle.context; @@ -71,7 +61,7 @@ pub fn codegen( )); let call_idle = quote!(#name( - #name::Context::new(&rtic::export::Priority::new(0)) + #name::Context::new() )); (mod_app, root_idle, user_idle, call_idle) diff --git a/macros/src/codegen/init.rs b/macros/src/codegen/init.rs index c7b8712..bbde4f2 100644 --- a/macros/src/codegen/init.rs +++ b/macros/src/codegen/init.rs @@ -91,8 +91,7 @@ pub fn codegen(app: &App, analysis: &Analysis) -> CodegenResult { // `${task}Locals` if !init.args.local_resources.is_empty() { - let (item, constructor) = - local_resources_struct::codegen(Context::Init, &mut local_needs_lt, app); + let (item, constructor) = local_resources_struct::codegen(Context::Init, app); root_init.push(item); @@ -103,13 +102,7 @@ pub fn codegen(app: &App, analysis: &Analysis) -> CodegenResult { let (shared_resources, local_resources) = #name(#name::Context::new(core.into())); }; - root_init.push(module::codegen( - Context::Init, - false, - local_needs_lt, - app, - analysis, - )); + root_init.push(module::codegen(Context::Init, app, analysis)); (mod_app, root_init, user_init, call_init) } diff --git a/macros/src/codegen/local_resources_struct.rs b/macros/src/codegen/local_resources_struct.rs index 6bcf4fa..a0413f9 100644 --- a/macros/src/codegen/local_resources_struct.rs +++ b/macros/src/codegen/local_resources_struct.rs @@ -8,7 +8,7 @@ use quote::quote; use crate::codegen::util; /// Generates local resources structs -pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2, TokenStream2) { +pub fn codegen(ctxt: Context, app: &App) -> (TokenStream2, TokenStream2) { let mut lt = None; let resources = match ctxt { @@ -74,16 +74,14 @@ pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2, } if lt.is_some() { - *needs_lt = true; - // The struct could end up empty due to `cfg`s leading to an error due to `'a` being unused if has_cfgs { fields.push(quote!( #[doc(hidden)] - pub __marker__: core::marker::PhantomData<&'a ()> + pub __rtic_internal_marker: ::core::marker::PhantomData<&'a ()> )); - values.push(quote!(__marker__: core::marker::PhantomData)); + values.push(quote!(__rtic_internal_marker: ::core::marker::PhantomData)); } } diff --git a/macros/src/codegen/module.rs b/macros/src/codegen/module.rs index 7bbfdf3..a64abd8 100644 --- a/macros/src/codegen/module.rs +++ b/macros/src/codegen/module.rs @@ -4,13 +4,7 @@ use proc_macro2::TokenStream as TokenStream2; use quote::quote; #[allow(clippy::too_many_lines)] -pub fn codegen( - ctxt: Context, - shared_resources_tick: bool, - local_resources_tick: bool, - app: &App, - analysis: &Analysis, -) -> TokenStream2 { +pub fn codegen(ctxt: Context, app: &App, analysis: &Analysis) -> TokenStream2 { let mut items = vec![]; let mut module_items = vec![]; let mut fields = vec![]; @@ -20,7 +14,6 @@ pub fn codegen( let name = ctxt.ident(app); - let mut lt = None; match ctxt { Context::Init => { fields.push(quote!( @@ -39,10 +32,9 @@ pub fn codegen( values.push(quote!(device: #device::Peripherals::steal())); } - lt = Some(quote!('a)); fields.push(quote!( /// Critical section token for init - pub cs: rtic::export::CriticalSection<#lt> + pub cs: rtic::export::CriticalSection<'a> )); values.push(quote!(cs: rtic::export::CriticalSection::new())); @@ -55,12 +47,6 @@ pub fn codegen( if ctxt.has_local_resources(app) { let ident = util::local_resources_ident(ctxt, app); - let lt = if local_resources_tick { - lt = Some(quote!('a)); - Some(quote!('a)) - } else { - None - }; module_items.push(quote!( #[doc(inline)] @@ -69,7 +55,7 @@ pub fn codegen( fields.push(quote!( /// Local Resources this task has access to - pub local: #name::LocalResources<#lt> + pub local: #name::LocalResources<'a> )); values.push(quote!(local: #name::LocalResources::new())); @@ -77,12 +63,6 @@ pub fn codegen( if ctxt.has_shared_resources(app) { let ident = util::shared_resources_ident(ctxt, app); - let lt = if shared_resources_tick { - lt = Some(quote!('a)); - Some(quote!('a)) - } else { - None - }; module_items.push(quote!( #[doc(inline)] @@ -91,15 +71,10 @@ pub fn codegen( fields.push(quote!( /// Shared Resources this task has access to - pub shared: #name::SharedResources<#lt> + pub shared: #name::SharedResources<'a> )); - let priority = if ctxt.is_init() { - None - } else { - Some(quote!(priority)) - }; - values.push(quote!(shared: #name::SharedResources::new(#priority))); + values.push(quote!(shared: #name::SharedResources::new())); } let doc = match ctxt { @@ -122,12 +97,6 @@ pub fn codegen( None }; - let priority = if ctxt.is_init() { - None - } else { - Some(quote!(priority: &#lt rtic::export::Priority)) - }; - let internal_context_name = util::internal_task_ident(name, "Context"); items.push(quote!( @@ -135,15 +104,18 @@ pub fn codegen( /// Execution context #[allow(non_snake_case)] #[allow(non_camel_case_types)] - pub struct #internal_context_name<#lt> { + pub struct #internal_context_name<'a> { + #[doc(hidden)] + __rtic_internal_p: ::core::marker::PhantomData<&'a ()>, #(#fields,)* } #(#cfgs)* - impl<#lt> #internal_context_name<#lt> { + impl<'a> #internal_context_name<'a> { #[inline(always)] - pub unsafe fn new(#core #priority) -> Self { + pub unsafe fn new(#core) -> Self { #internal_context_name { + __rtic_internal_p: ::core::marker::PhantomData, #(#values,)* } } diff --git a/macros/src/codegen/shared_resources.rs b/macros/src/codegen/shared_resources.rs index b63e743..5c54fb9 100644 --- a/macros/src/codegen/shared_resources.rs +++ b/macros/src/codegen/shared_resources.rs @@ -57,19 +57,14 @@ pub fn codegen( #[allow(non_camel_case_types)] #(#cfgs)* pub struct #shared_name<'a> { - priority: &'a Priority, + __rtic_internal_p: ::core::marker::PhantomData<&'a ()>, } #(#cfgs)* impl<'a> #shared_name<'a> { #[inline(always)] - pub unsafe fn new(priority: &'a Priority) -> Self { - #shared_name { priority } - } - - #[inline(always)] - pub unsafe fn priority(&self) -> &Priority { - self.priority + pub unsafe fn new() -> Self { + #shared_name { __rtic_internal_p: ::core::marker::PhantomData } } } )); @@ -104,8 +99,6 @@ pub fn codegen( quote!() } else { quote!(mod shared_resources { - use rtic::export::Priority; - #(#mod_resources)* }) }; diff --git a/macros/src/codegen/shared_resources_struct.rs b/macros/src/codegen/shared_resources_struct.rs index 1d46aa4..de597ca 100644 --- a/macros/src/codegen/shared_resources_struct.rs +++ b/macros/src/codegen/shared_resources_struct.rs @@ -5,7 +5,7 @@ use quote::quote; use crate::codegen::util; /// Generate shared resources structs -pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2, TokenStream2) { +pub fn codegen(ctxt: Context, app: &App) -> (TokenStream2, TokenStream2) { let mut lt = None; let resources = match ctxt { @@ -72,7 +72,7 @@ pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2, values.push(quote!( #(#cfgs)* - #name: shared_resources::#shared_name::new(priority) + #name: shared_resources::#shared_name::new() )); @@ -93,8 +93,6 @@ pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2, } if lt.is_some() { - *needs_lt = true; - // The struct could end up empty due to `cfg`s leading to an error due to `'a` being unused if has_cfgs { fields.push(quote!( @@ -117,15 +115,10 @@ pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2, } ); - let arg = if ctxt.is_init() { - None - } else { - Some(quote!(priority: &#lt rtic::export::Priority)) - }; let constructor = quote!( impl<#lt> #ident<#lt> { #[inline(always)] - pub unsafe fn new(#arg) -> Self { + pub unsafe fn new() -> Self { #ident { #(#values,)* } diff --git a/macros/src/codegen/software_tasks.rs b/macros/src/codegen/software_tasks.rs index b2b468c..350c1e6 100644 --- a/macros/src/codegen/software_tasks.rs +++ b/macros/src/codegen/software_tasks.rs @@ -36,16 +36,11 @@ pub fn codegen( )); // `${task}Resources` - let mut shared_needs_lt = false; - let mut local_needs_lt = false; // `${task}Locals` if !task.args.local_resources.is_empty() { - let (item, constructor) = local_resources_struct::codegen( - Context::SoftwareTask(name), - &mut local_needs_lt, - app, - ); + let (item, constructor) = + local_resources_struct::codegen(Context::SoftwareTask(name), app); root.push(item); @@ -53,11 +48,8 @@ pub fn codegen( } if !task.args.shared_resources.is_empty() { - let (item, constructor) = shared_resources_struct::codegen( - Context::SoftwareTask(name), - &mut shared_needs_lt, - app, - ); + let (item, constructor) = + shared_resources_struct::codegen(Context::SoftwareTask(name), app); root.push(item); @@ -69,17 +61,12 @@ pub fn codegen( let attrs = &task.attrs; let cfgs = &task.cfgs; let stmts = &task.stmts; - let context_lifetime = if shared_needs_lt || local_needs_lt { - quote!(<'static>) - } else { - quote!() - }; user_tasks.push(quote!( #(#attrs)* #(#cfgs)* #[allow(non_snake_case)] - async fn #name(#context: #name::Context #context_lifetime) { + async fn #name(#context: #name::Context<'static>) { use rtic::Mutex as _; use rtic::mutex::prelude::*; @@ -88,13 +75,7 @@ pub fn codegen( )); } - root.push(module::codegen( - Context::SoftwareTask(name), - shared_needs_lt, - local_needs_lt, - app, - analysis, - )); + root.push(module::codegen(Context::SoftwareTask(name), app, analysis)); } (mod_app, root, user_tasks) diff --git a/macros/src/codegen/util.rs b/macros/src/codegen/util.rs index aa720c0..a071ca2 100644 --- a/macros/src/codegen/util.rs +++ b/macros/src/codegen/util.rs @@ -17,10 +17,10 @@ pub fn impl_mutex( ceiling: u8, ptr: &TokenStream2, ) -> TokenStream2 { - let (path, priority) = if resources_prefix { - (quote!(shared_resources::#name), quote!(self.priority())) + let path = if resources_prefix { + quote!(shared_resources::#name) } else { - (quote!(#name), quote!(self.priority)) + quote!(#name) }; let device = &app.args.device; @@ -38,7 +38,6 @@ pub fn impl_mutex( unsafe { rtic::export::lock( #ptr, - #priority, CEILING, #device::NVIC_PRIO_BITS, &#masks_name, -- cgit v1.2.3 From 511ff675b558812d65048ae737b6f1c53133e211 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Sat, 7 Jan 2023 11:36:32 +0100 Subject: Break codegen for 0-prio async --- macros/src/codegen/idle.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'macros') diff --git a/macros/src/codegen/idle.rs b/macros/src/codegen/idle.rs index a4f6325..c0eecbc 100644 --- a/macros/src/codegen/idle.rs +++ b/macros/src/codegen/idle.rs @@ -67,15 +67,15 @@ pub fn codegen( (mod_app, root_idle, user_idle, call_idle) } else { // TODO: No idle defined, check for 0-priority tasks and generate an executor if needed + unimplemented!(); - // - ( - vec![], - vec![], - None, - quote!(loop { - rtic::export::nop() - }), - ) + // ( + // vec![], + // vec![], + // None, + // quote!(loop { + // rtic::export::nop() + // }), + // ) } } -- cgit v1.2.3 From 2ad36a6efed5028e0e6bd991b82a50c045f825a8 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Sat, 7 Jan 2023 13:18:43 +0100 Subject: Lifetime cleanup --- macros/src/codegen/local_resources_struct.rs | 25 +++++++---------------- macros/src/codegen/shared_resources_struct.rs | 29 +++++++-------------------- 2 files changed, 14 insertions(+), 40 deletions(-) (limited to 'macros') diff --git a/macros/src/codegen/local_resources_struct.rs b/macros/src/codegen/local_resources_struct.rs index a0413f9..e268508 100644 --- a/macros/src/codegen/local_resources_struct.rs +++ b/macros/src/codegen/local_resources_struct.rs @@ -9,8 +9,6 @@ use crate::codegen::util; /// Generates local resources structs pub fn codegen(ctxt: Context, app: &App) -> (TokenStream2, TokenStream2) { - let mut lt = None; - let resources = match ctxt { Context::Init => &app.init.args.local_resources, Context::Idle => { @@ -28,7 +26,6 @@ pub fn codegen(ctxt: Context, app: &App) -> (TokenStream2, TokenStream2) { let mut fields = vec![]; let mut values = vec![]; - let mut has_cfgs = false; for (name, task_local) in resources { let (cfgs, ty, is_declared) = match task_local { @@ -39,12 +36,9 @@ pub fn codegen(ctxt: Context, app: &App) -> (TokenStream2, TokenStream2) { TaskLocal::Declared(r) => (&r.cfgs, &r.ty, true), }; - has_cfgs |= !cfgs.is_empty(); - let lt = if ctxt.runs_once() { quote!('static) } else { - lt = Some(quote!('a)); quote!('a) }; @@ -73,17 +67,12 @@ pub fn codegen(ctxt: Context, app: &App) -> (TokenStream2, TokenStream2) { )); } - if lt.is_some() { - // The struct could end up empty due to `cfg`s leading to an error due to `'a` being unused - if has_cfgs { - fields.push(quote!( - #[doc(hidden)] - pub __rtic_internal_marker: ::core::marker::PhantomData<&'a ()> - )); + fields.push(quote!( + #[doc(hidden)] + pub __rtic_internal_marker: ::core::marker::PhantomData<&'a ()> + )); - values.push(quote!(__rtic_internal_marker: ::core::marker::PhantomData)); - } - } + 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); @@ -91,13 +80,13 @@ pub fn codegen(ctxt: Context, app: &App) -> (TokenStream2, TokenStream2) { #[allow(non_snake_case)] #[allow(non_camel_case_types)] #[doc = #doc] - pub struct #ident<#lt> { + pub struct #ident<'a> { #(#fields,)* } ); let constructor = quote!( - impl<#lt> #ident<#lt> { + impl<'a> #ident<'a> { #[inline(always)] pub unsafe fn new() -> Self { #ident { diff --git a/macros/src/codegen/shared_resources_struct.rs b/macros/src/codegen/shared_resources_struct.rs index de597ca..24c93de 100644 --- a/macros/src/codegen/shared_resources_struct.rs +++ b/macros/src/codegen/shared_resources_struct.rs @@ -6,8 +6,6 @@ use crate::codegen::util; /// Generate shared resources structs pub fn codegen(ctxt: Context, app: &App) -> (TokenStream2, TokenStream2) { - let mut lt = None; - let resources = match ctxt { Context::Init => unreachable!("Tried to generate shared resources struct for init"), Context::Idle => { @@ -23,13 +21,11 @@ pub fn codegen(ctxt: Context, app: &App) -> (TokenStream2, TokenStream2) { let mut fields = vec![]; let mut values = vec![]; - let mut has_cfgs = false; for (name, access) in resources { let res = app.shared_resources.get(name).expect("UNREACHABLE"); let cfgs = &res.cfgs; - has_cfgs |= !cfgs.is_empty(); // access hold if the resource is [x] (exclusive) or [&x] (shared) let mut_ = if access.is_exclusive() { @@ -46,7 +42,6 @@ pub fn codegen(ctxt: Context, app: &App) -> (TokenStream2, TokenStream2) { let lt = if ctxt.runs_once() { quote!('static) } else { - lt = Some(quote!('a)); quote!('a) }; @@ -55,16 +50,11 @@ pub fn codegen(ctxt: Context, app: &App) -> (TokenStream2, TokenStream2) { pub #name: &#lt #mut_ #ty )); } else if access.is_shared() { - lt = Some(quote!('a)); - fields.push(quote!( #(#cfgs)* pub #name: &'a #ty )); } else { - // Resource proxy - lt = Some(quote!('a)); - fields.push(quote!( #(#cfgs)* pub #name: shared_resources::#shared_name<'a> @@ -92,17 +82,12 @@ pub fn codegen(ctxt: Context, app: &App) -> (TokenStream2, TokenStream2) { )); } - if lt.is_some() { - // The struct could end up empty due to `cfg`s leading to an error due to `'a` being unused - if has_cfgs { - fields.push(quote!( - #[doc(hidden)] - pub __marker__: core::marker::PhantomData<&'a ()> - )); + fields.push(quote!( + #[doc(hidden)] + pub __rtic_internal_marker: core::marker::PhantomData<&'a ()> + )); - values.push(quote!(__marker__: core::marker::PhantomData)); - } - } + 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); @@ -110,13 +95,13 @@ pub fn codegen(ctxt: Context, app: &App) -> (TokenStream2, TokenStream2) { #[allow(non_snake_case)] #[allow(non_camel_case_types)] #[doc = #doc] - pub struct #ident<#lt> { + pub struct #ident<'a> { #(#fields,)* } ); let constructor = quote!( - impl<#lt> #ident<#lt> { + impl<'a> #ident<'a> { #[inline(always)] pub unsafe fn new() -> Self { #ident { -- cgit v1.2.3 From fe2b5cc52ee634346bc8aecf5041b6af9fdea529 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Sat, 7 Jan 2023 13:23:20 +0100 Subject: Removed same prio spawn --- macros/src/codegen/init.rs | 1 - macros/src/syntax/ast.rs | 4 ---- macros/src/syntax/parse.rs | 32 ------------------------- macros/ui/task-interrupt-same-prio-spawn.rs | 7 ------ macros/ui/task-interrupt-same-prio-spawn.stderr | 5 ---- 5 files changed, 49 deletions(-) delete mode 100644 macros/ui/task-interrupt-same-prio-spawn.rs delete mode 100644 macros/ui/task-interrupt-same-prio-spawn.stderr (limited to 'macros') diff --git a/macros/src/codegen/init.rs b/macros/src/codegen/init.rs index bbde4f2..2aa8fb3 100644 --- a/macros/src/codegen/init.rs +++ b/macros/src/codegen/init.rs @@ -25,7 +25,6 @@ type CodegenResult = ( /// Generates support code for `#[init]` functions pub fn codegen(app: &App, analysis: &Analysis) -> CodegenResult { let init = &app.init; - let mut local_needs_lt = false; let name = &init.name; let mut root_init = vec![]; diff --git a/macros/src/syntax/ast.rs b/macros/src/syntax/ast.rs index ea6e402..da6016a 100644 --- a/macros/src/syntax/ast.rs +++ b/macros/src/syntax/ast.rs @@ -224,9 +224,6 @@ pub struct SoftwareTaskArgs { /// Shared resources that can be accessed from this context pub shared_resources: SharedResources, - - /// Only same priority tasks can spawn this task - pub only_same_priority_spawn: bool, } impl Default for SoftwareTaskArgs { @@ -235,7 +232,6 @@ impl Default for SoftwareTaskArgs { priority: 1, local_resources: LocalResources::new(), shared_resources: SharedResources::new(), - only_same_priority_spawn: false, } } } diff --git a/macros/src/syntax/parse.rs b/macros/src/syntax/parse.rs index abdd677..c78453a 100644 --- a/macros/src/syntax/parse.rs +++ b/macros/src/syntax/parse.rs @@ -196,8 +196,6 @@ fn task_args(tokens: TokenStream2) -> parse::Result parse::Result parse::Result { return Err(parse::Error::new(ident.span(), "unexpected argument")); } @@ -369,13 +345,6 @@ fn task_args(tokens: TokenStream2) -> parse::Result parse::Result ui/task-interrupt-same-prio-spawn.rs:5:29 - | -5 | #[task(binds = SysTick, only_same_priority_spawn_please_fix_me)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -- cgit v1.2.3 From 29228c47239f12794feb91ae5a81d748530c40dc Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Sat, 7 Jan 2023 14:06:11 +0100 Subject: Main in main codegen --- macros/src/codegen.rs | 43 +++++----------------------------- macros/src/codegen/idle.rs | 18 ++------------- macros/src/codegen/init.rs | 9 ++------ macros/src/codegen/main.rs | 51 +++++++++++++++++++++++++++++++++++++++++ macros/src/codegen/post_init.rs | 4 +--- 5 files changed, 62 insertions(+), 63 deletions(-) create mode 100644 macros/src/codegen/main.rs (limited to 'macros') diff --git a/macros/src/codegen.rs b/macros/src/codegen.rs index b490d7a..839b1cd 100644 --- a/macros/src/codegen.rs +++ b/macros/src/codegen.rs @@ -19,6 +19,8 @@ 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... @@ -26,20 +28,15 @@ mod util; #[allow(clippy::too_many_lines)] pub fn app(app: &App, analysis: &Analysis) -> TokenStream2 { let mut mod_app = vec![]; - let mut mains = vec![]; let mut root = vec![]; let mut user = vec![]; // Generate the `main` function - let assertion_stmts = assertions::codegen(app, analysis); - - let pre_init_stmts = pre_init::codegen(app, analysis); - - let (mod_app_init, root_init, user_init, call_init) = init::codegen(app, analysis); + let main = main::codegen(app, analysis); - let post_init_stmts = post_init::codegen(app, analysis); + let (mod_app_init, root_init, user_init) = init::codegen(app, analysis); - let (mod_app_idle, root_idle, user_idle, call_idle) = idle::codegen(app, analysis); + let (mod_app_idle, root_idle, user_idle) = idle::codegen(app, analysis); user.push(quote!( #user_init @@ -59,34 +56,6 @@ pub fn app(app: &App, analysis: &Analysis) -> TokenStream2 { #(#mod_app_idle)* )); - let main = util::suffixed("main"); - mains.push(quote!( - #[doc(hidden)] - mod rtic_ext { - use super::*; - #[no_mangle] - unsafe extern "C" fn #main() -> ! { - #(#assertion_stmts)* - - #(#pre_init_stmts)* - - #[inline(never)] - fn __rtic_init_resources(f: F) where F: FnOnce() { - f(); - } - - // Wrap late_init_stmts in a function to ensure that stack space is reclaimed. - __rtic_init_resources(||{ - #call_init - - #(#post_init_stmts)* - }); - - #call_idle - } - } - )); - let (mod_app_shared_resources, mod_shared_resources) = shared_resources::codegen(app, analysis); let (mod_app_local_resources, mod_local_resources) = local_resources::codegen(app, analysis); @@ -145,7 +114,7 @@ pub fn app(app: &App, analysis: &Analysis) -> TokenStream2 { #(#mod_app_async_dispatchers)* - #(#mains)* + #main } ) } diff --git a/macros/src/codegen/idle.rs b/macros/src/codegen/idle.rs index c0eecbc..1f05d12 100644 --- a/macros/src/codegen/idle.rs +++ b/macros/src/codegen/idle.rs @@ -20,8 +20,6 @@ pub fn codegen( Vec, // user_idle Option, - // call_idle - TokenStream2, ) { if let Some(idle) = &app.idle { let mut mod_app = vec![]; @@ -60,22 +58,10 @@ pub fn codegen( } )); - let call_idle = quote!(#name( - #name::Context::new() - )); - - (mod_app, root_idle, user_idle, call_idle) + (mod_app, root_idle, user_idle) } else { // TODO: No idle defined, check for 0-priority tasks and generate an executor if needed - unimplemented!(); - // ( - // vec![], - // vec![], - // None, - // quote!(loop { - // rtic::export::nop() - // }), - // ) + (vec![], vec![], None) } } diff --git a/macros/src/codegen/init.rs b/macros/src/codegen/init.rs index 2aa8fb3..3b2bcd4 100644 --- a/macros/src/codegen/init.rs +++ b/macros/src/codegen/init.rs @@ -18,8 +18,6 @@ type CodegenResult = ( Vec, // user_init -- the `#[init]` function written by the user TokenStream2, - // call_init -- the call to the user `#[init]` - TokenStream2, ); /// Generates support code for `#[init]` functions @@ -63,6 +61,7 @@ pub fn codegen(app: &App, analysis: &Analysis) -> CodegenResult { ) }) .collect(); + root_init.push(quote! { struct #shared { #(#shared_resources)* @@ -97,11 +96,7 @@ pub fn codegen(app: &App, analysis: &Analysis) -> CodegenResult { mod_app = Some(constructor); } - let call_init = quote! { - let (shared_resources, local_resources) = #name(#name::Context::new(core.into())); - }; - root_init.push(module::codegen(Context::Init, app, analysis)); - (mod_app, root_init, user_init, call_init) + (mod_app, root_init, user_init) } diff --git a/macros/src/codegen/main.rs b/macros/src/codegen/main.rs new file mode 100644 index 0000000..90f09ae --- /dev/null +++ b/macros/src/codegen/main.rs @@ -0,0 +1,51 @@ +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 { + // TODO: No idle defined, check for 0-priority tasks and generate an executor if needed + + 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) 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/post_init.rs b/macros/src/codegen/post_init.rs index e8183b9..c4e5383 100644 --- a/macros/src/codegen/post_init.rs +++ b/macros/src/codegen/post_init.rs @@ -1,9 +1,7 @@ -use crate::syntax::ast::App; +use crate::{analyze::Analysis, codegen::util, syntax::ast::App}; use proc_macro2::TokenStream as TokenStream2; use quote::quote; -use crate::{analyze::Analysis, codegen::util}; - /// Generates code that runs after `#[init]` returns pub fn codegen(app: &App, analysis: &Analysis) -> Vec { let mut stmts = vec![]; -- cgit v1.2.3 From b9b3ded5e21c40256163cf85f4fba2991c03a45c Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Sat, 7 Jan 2023 14:13:18 +0100 Subject: Cleanup weird locals in codegen --- macros/src/codegen.rs | 39 +++++++++++---------------------------- 1 file changed, 11 insertions(+), 28 deletions(-) (limited to 'macros') diff --git a/macros/src/codegen.rs b/macros/src/codegen.rs index 839b1cd..bb1028f 100644 --- a/macros/src/codegen.rs +++ b/macros/src/codegen.rs @@ -27,10 +27,6 @@ mod main; #[allow(clippy::too_many_lines)] pub fn app(app: &App, analysis: &Analysis) -> TokenStream2 { - let mut mod_app = vec![]; - let mut root = vec![]; - let mut user = vec![]; - // Generate the `main` function let main = main::codegen(app, analysis); @@ -38,24 +34,6 @@ pub fn app(app: &App, analysis: &Analysis) -> TokenStream2 { let (mod_app_idle, root_idle, user_idle) = idle::codegen(app, analysis); - user.push(quote!( - #user_init - - #user_idle - )); - - root.push(quote!( - #(#root_init)* - - #(#root_idle)* - )); - - mod_app.push(quote!( - #mod_app_init - - #(#mod_app_idle)* - )); - let (mod_app_shared_resources, mod_shared_resources) = shared_resources::codegen(app, analysis); let (mod_app_local_resources, mod_local_resources) = local_resources::codegen(app, analysis); @@ -85,13 +63,21 @@ pub fn app(app: &App, analysis: &Analysis) -> TokenStream2 { #(#user_code)* /// User code end - #(#user)* - #(#user_hardware_tasks)* #(#user_software_tasks)* - #(#root)* + #mod_app_init + + #(#root_init)* + + #user_init + + #(#mod_app_idle)* + + #(#root_idle)* + + #user_idle #mod_shared_resources @@ -101,9 +87,6 @@ pub fn app(app: &App, analysis: &Analysis) -> TokenStream2 { #(#root_software_tasks)* - /// app module - #(#mod_app)* - #(#mod_app_shared_resources)* #(#mod_app_local_resources)* -- cgit v1.2.3 From 76595b7aedd2a14aea8569b75fabe62120f93230 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Sat, 7 Jan 2023 14:26:55 +0100 Subject: All codegen is now explicit --- macros/src/codegen.rs | 56 +++++++++------------------------ macros/src/codegen/async_dispatchers.rs | 4 +-- macros/src/codegen/hardware_tasks.rs | 23 +++++--------- macros/src/codegen/idle.rs | 27 ++++++---------- macros/src/codegen/init.rs | 23 +++++--------- macros/src/codegen/local_resources.rs | 13 ++------ macros/src/codegen/shared_resources.rs | 16 ++++------ macros/src/codegen/software_tasks.rs | 23 +++++--------- 8 files changed, 57 insertions(+), 128 deletions(-) (limited to 'macros') diff --git a/macros/src/codegen.rs b/macros/src/codegen.rs index bb1028f..24e98ce 100644 --- a/macros/src/codegen.rs +++ b/macros/src/codegen.rs @@ -29,21 +29,14 @@ mod main; 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 (mod_app_init, root_init, user_init) = init::codegen(app, analysis); - - let (mod_app_idle, root_idle, user_idle) = idle::codegen(app, analysis); - - let (mod_app_shared_resources, mod_shared_resources) = shared_resources::codegen(app, analysis); - let (mod_app_local_resources, mod_local_resources) = local_resources::codegen(app, analysis); - - let (mod_app_hardware_tasks, root_hardware_tasks, user_hardware_tasks) = - hardware_tasks::codegen(app, analysis); - - let (mod_app_software_tasks, root_software_tasks, user_software_tasks) = - software_tasks::codegen(app, analysis); - - let mod_app_async_dispatchers = async_dispatchers::codegen(app, analysis); let user_imports = &app.user_imports; let user_code = &app.user_code; let name = &app.name; @@ -59,43 +52,22 @@ pub fn app(app: &App, analysis: &Analysis) -> TokenStream2 { #(#user_imports)* - /// User code from within the module #(#user_code)* /// User code end - #(#user_hardware_tasks)* - - #(#user_software_tasks)* - - #mod_app_init - - #(#root_init)* - - #user_init - - #(#mod_app_idle)* - - #(#root_idle)* - - #user_idle - - #mod_shared_resources - - #mod_local_resources - - #(#root_hardware_tasks)* + #init_codegen - #(#root_software_tasks)* + #idle_codegen - #(#mod_app_shared_resources)* + #hardware_tasks_codegen - #(#mod_app_local_resources)* + #software_tasks_codegen - #(#mod_app_hardware_tasks)* + #shared_resources_codegen - #(#mod_app_software_tasks)* + #local_resources_codegen - #(#mod_app_async_dispatchers)* + #async_dispatchers_codegen #main } diff --git a/macros/src/codegen/async_dispatchers.rs b/macros/src/codegen/async_dispatchers.rs index d53d7b5..62b17fe 100644 --- a/macros/src/codegen/async_dispatchers.rs +++ b/macros/src/codegen/async_dispatchers.rs @@ -4,7 +4,7 @@ use proc_macro2::TokenStream as TokenStream2; use quote::quote; /// Generates task dispatchers -pub fn codegen(app: &App, analysis: &Analysis) -> Vec { +pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 { let mut items = vec![]; let interrupts = &analysis.interrupts; @@ -96,5 +96,5 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec { )); } - items + quote!(#(#items)*) } diff --git a/macros/src/codegen/hardware_tasks.rs b/macros/src/codegen/hardware_tasks.rs index 9ea5825..8a5a8f6 100644 --- a/macros/src/codegen/hardware_tasks.rs +++ b/macros/src/codegen/hardware_tasks.rs @@ -7,20 +7,7 @@ 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, -) -> ( - // mod_app_hardware_tasks -- interrupt handlers and `${task}Resources` constructors - Vec, - // root_hardware_tasks -- items that must be placed in the root of the crate: - // - `${task}Locals` structs - // - `${task}Resources` structs - // - `${task}` modules - Vec, - // user_hardware_tasks -- the `#[task]` functions written by the user - Vec, -) { +pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 { let mut mod_app = vec![]; let mut root = vec![]; let mut user_tasks = vec![]; @@ -90,5 +77,11 @@ pub fn codegen( } } - (mod_app, root, user_tasks) + quote!( + #(#mod_app)* + + #(#root)* + + #(#user_tasks)* + ) } diff --git a/macros/src/codegen/idle.rs b/macros/src/codegen/idle.rs index 1f05d12..0c833ef 100644 --- a/macros/src/codegen/idle.rs +++ b/macros/src/codegen/idle.rs @@ -7,20 +7,7 @@ use proc_macro2::TokenStream as TokenStream2; use quote::quote; /// Generates support code for `#[idle]` functions -pub fn codegen( - app: &App, - analysis: &Analysis, -) -> ( - // mod_app_idle -- the `${idle}Resources` constructor - Vec, - // root_idle -- items that must be placed in the root of the crate: - // - the `${idle}Locals` struct - // - the `${idle}Resources` struct - // - the `${idle}` module, which contains types like `${idle}::Context` - Vec, - // user_idle - Option, -) { +pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 { if let Some(idle) = &app.idle { let mut mod_app = vec![]; let mut root_idle = vec![]; @@ -58,10 +45,14 @@ pub fn codegen( } )); - (mod_app, root_idle, user_idle) - } else { - // TODO: No idle defined, check for 0-priority tasks and generate an executor if needed + quote!( + #(#mod_app)* + + #(#root_idle)* - (vec![], vec![], None) + #user_idle + ) + } else { + quote!() } } diff --git a/macros/src/codegen/init.rs b/macros/src/codegen/init.rs index 3b2bcd4..6e1059f 100644 --- a/macros/src/codegen/init.rs +++ b/macros/src/codegen/init.rs @@ -7,21 +7,8 @@ use crate::{ syntax::{ast::App, Context}, }; -type CodegenResult = ( - // mod_app_idle -- the `${init}Resources` constructor - Option, - // root_init -- items that must be placed in the root of the crate: - // - the `${init}Locals` struct - // - the `${init}Resources` struct - // - the `${init}LateResources` struct - // - the `${init}` module, which contains types like `${init}::Context` - Vec, - // user_init -- the `#[init]` function written by the user - TokenStream2, -); - /// Generates support code for `#[init]` functions -pub fn codegen(app: &App, analysis: &Analysis) -> CodegenResult { +pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 { let init = &app.init; let name = &init.name; @@ -98,5 +85,11 @@ pub fn codegen(app: &App, analysis: &Analysis) -> CodegenResult { root_init.push(module::codegen(Context::Init, app, analysis)); - (mod_app, root_init, user_init) + quote!( + #mod_app + + #(#root_init)* + + #user_init + ) } diff --git a/macros/src/codegen/local_resources.rs b/macros/src/codegen/local_resources.rs index 6fc63cd..e6d1553 100644 --- a/macros/src/codegen/local_resources.rs +++ b/macros/src/codegen/local_resources.rs @@ -6,17 +6,8 @@ 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, -) -> ( - // mod_app -- the `static` variables behind the proxies - Vec, - // mod_resources -- the `resources` module - TokenStream2, -) { +pub fn codegen(app: &App, _analysis: &Analysis) -> TokenStream2 { let mut mod_app = vec![]; - // let mut mod_resources: _ = vec![]; // All local resources declared in the `#[local]' struct for (name, res) in &app.local_resources { @@ -70,5 +61,5 @@ pub fn codegen( )); } - (mod_app, TokenStream2::new()) + quote!(#(#mod_app)*) } diff --git a/macros/src/codegen/shared_resources.rs b/macros/src/codegen/shared_resources.rs index 5c54fb9..19fd13f 100644 --- a/macros/src/codegen/shared_resources.rs +++ b/macros/src/codegen/shared_resources.rs @@ -5,15 +5,7 @@ use quote::quote; use std::collections::HashMap; /// Generates `static` variables and shared resource proxies -pub fn codegen( - app: &App, - analysis: &Analysis, -) -> ( - // mod_app -- the `static` variables behind the proxies - Vec, - // mod_resources -- the `resources` module - TokenStream2, -) { +pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 { let mut mod_app = vec![]; let mut mod_resources = vec![]; @@ -183,5 +175,9 @@ pub fn codegen( )); } - (mod_app, mod_resources) + quote!( + #(#mod_app)* + + #mod_resources + ) } diff --git a/macros/src/codegen/software_tasks.rs b/macros/src/codegen/software_tasks.rs index 350c1e6..4cb1fa9 100644 --- a/macros/src/codegen/software_tasks.rs +++ b/macros/src/codegen/software_tasks.rs @@ -6,20 +6,7 @@ use crate::{ use proc_macro2::TokenStream as TokenStream2; use quote::quote; -pub fn codegen( - app: &App, - analysis: &Analysis, -) -> ( - // mod_app_software_tasks -- free queues, buffers and `${task}Resources` constructors - Vec, - // root_software_tasks -- items that must be placed in the root of the crate: - // - `${task}Locals` structs - // - `${task}Resources` structs - // - `${task}` modules - Vec, - // user_software_tasks -- the `#[task]` functions written by the user - Vec, -) { +pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 { let mut mod_app = vec![]; let mut root = vec![]; let mut user_tasks = vec![]; @@ -78,5 +65,11 @@ pub fn codegen( root.push(module::codegen(Context::SoftwareTask(name), app, analysis)); } - (mod_app, root, user_tasks) + quote!( + #(#mod_app)* + + #(#root)* + + #(#user_tasks)* + ) } -- cgit v1.2.3 From 584ac7e1b335411e3d923aeef92466efdc92ae50 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Sun, 8 Jan 2023 16:25:46 +0100 Subject: Update UI tests, 1 failing that needs fixing --- macros/ui/async-local-resouces.rs | 28 ------------------------- macros/ui/async-local-resouces.stderr | 5 ----- macros/ui/async-zero-prio-tasks.rs | 19 ----------------- macros/ui/async-zero-prio-tasks.stderr | 11 ---------- macros/ui/extern-interrupt-used.rs | 2 +- macros/ui/extern-interrupt-used.stderr | 2 +- macros/ui/idle-double-local.stderr | 2 +- macros/ui/idle-double-shared.stderr | 2 +- macros/ui/init-divergent.stderr | 4 ++-- macros/ui/init-double-local.stderr | 2 +- macros/ui/init-double-shared.stderr | 2 +- macros/ui/init-input.rs | 2 +- macros/ui/init-input.stderr | 6 +++--- macros/ui/init-no-context.rs | 2 +- macros/ui/init-no-context.stderr | 6 +++--- macros/ui/init-output.stderr | 4 ++-- macros/ui/init-pub.rs | 2 +- macros/ui/init-pub.stderr | 6 +++--- macros/ui/init-unsafe.rs | 2 +- macros/ui/init-unsafe.stderr | 6 +++--- macros/ui/interrupt-double.stderr | 2 +- macros/ui/local-collision-2.rs | 7 ++----- macros/ui/local-collision-2.stderr | 10 ++++----- macros/ui/local-collision.rs | 6 +++--- macros/ui/local-collision.stderr | 4 ++-- macros/ui/local-malformed-1.rs | 4 ++-- macros/ui/local-malformed-2.rs | 4 ++-- macros/ui/local-malformed-2.stderr | 2 +- macros/ui/local-malformed-3.rs | 4 ++-- macros/ui/local-malformed-4.rs | 4 ++-- macros/ui/local-not-declared.rs | 4 ++-- macros/ui/local-not-declared.stderr | 2 +- macros/ui/local-pub.rs | 6 ++++++ macros/ui/local-pub.stderr | 8 +++---- macros/ui/local-shared-attribute.rs | 13 +++++++++--- macros/ui/local-shared-attribute.stderr | 9 ++++---- macros/ui/local-shared.rs | 6 +++--- macros/ui/local-shared.stderr | 4 ++-- macros/ui/monotonic-binds-collision-task.rs | 10 --------- macros/ui/monotonic-binds-collision-task.stderr | 5 ----- macros/ui/monotonic-binds-collision.rs | 10 --------- macros/ui/monotonic-binds-collision.stderr | 5 ----- macros/ui/monotonic-double-binds.rs | 7 ------- macros/ui/monotonic-double-binds.stderr | 5 ----- macros/ui/monotonic-double-default.rs | 7 ------- macros/ui/monotonic-double-default.stderr | 5 ----- macros/ui/monotonic-double-prio.rs | 7 ------- macros/ui/monotonic-double-prio.stderr | 5 ----- macros/ui/monotonic-double.rs | 10 --------- macros/ui/monotonic-double.stderr | 5 ----- macros/ui/monotonic-name-collision.rs | 10 --------- macros/ui/monotonic-name-collision.stderr | 5 ----- macros/ui/monotonic-no-binds.rs | 7 ------- macros/ui/monotonic-no-binds.stderr | 5 ----- macros/ui/monotonic-no-paran.rs | 8 ------- macros/ui/monotonic-no-paran.stderr | 5 ----- macros/ui/monotonic-timer-collision.rs | 10 --------- macros/ui/monotonic-timer-collision.stderr | 5 ----- macros/ui/monotonic-with-attrs.rs | 8 ------- macros/ui/monotonic-with-attrs.stderr | 5 ----- macros/ui/pub-local.stderr | 5 ----- macros/ui/pub-shared.stderr | 5 ----- macros/ui/shared-lock-free.rs | 6 +++--- macros/ui/shared-lock-free.stderr | 17 --------------- macros/ui/shared-not-declared.rs | 4 ++-- macros/ui/shared-not-declared.stderr | 2 +- macros/ui/shared-pub.stderr | 2 +- macros/ui/task-divergent.rs | 2 +- macros/ui/task-divergent.stderr | 8 +++---- macros/ui/task-double-capacity.rs | 7 ------- macros/ui/task-double-capacity.stderr | 5 ----- macros/ui/task-double-local.rs | 2 +- macros/ui/task-double-local.stderr | 2 +- macros/ui/task-double-priority.rs | 2 +- macros/ui/task-double-priority.stderr | 2 +- macros/ui/task-double-shared.rs | 2 +- macros/ui/task-double-shared.stderr | 2 +- macros/ui/task-idle.rs | 2 +- macros/ui/task-idle.stderr | 6 +++--- macros/ui/task-init.rs | 4 ++-- macros/ui/task-init.stderr | 6 +++--- macros/ui/task-interrupt.rs | 2 +- macros/ui/task-interrupt.stderr | 6 +++--- macros/ui/task-no-context.rs | 2 +- macros/ui/task-no-context.stderr | 8 +++---- macros/ui/task-priority-too-high.rs | 2 +- macros/ui/task-pub.rs | 2 +- macros/ui/task-pub.stderr | 8 +++---- macros/ui/task-unsafe.rs | 2 +- macros/ui/task-unsafe.stderr | 8 +++---- macros/ui/task-zero-prio.rs | 19 +++++++++++++++++ macros/ui/task-zero-prio.stderr | 5 +++++ 92 files changed, 152 insertions(+), 368 deletions(-) delete mode 100644 macros/ui/async-local-resouces.rs delete mode 100644 macros/ui/async-local-resouces.stderr delete mode 100644 macros/ui/async-zero-prio-tasks.rs delete mode 100644 macros/ui/async-zero-prio-tasks.stderr delete mode 100644 macros/ui/monotonic-binds-collision-task.rs delete mode 100644 macros/ui/monotonic-binds-collision-task.stderr delete mode 100644 macros/ui/monotonic-binds-collision.rs delete mode 100644 macros/ui/monotonic-binds-collision.stderr delete mode 100644 macros/ui/monotonic-double-binds.rs delete mode 100644 macros/ui/monotonic-double-binds.stderr delete mode 100644 macros/ui/monotonic-double-default.rs delete mode 100644 macros/ui/monotonic-double-default.stderr delete mode 100644 macros/ui/monotonic-double-prio.rs delete mode 100644 macros/ui/monotonic-double-prio.stderr delete mode 100644 macros/ui/monotonic-double.rs delete mode 100644 macros/ui/monotonic-double.stderr delete mode 100644 macros/ui/monotonic-name-collision.rs delete mode 100644 macros/ui/monotonic-name-collision.stderr delete mode 100644 macros/ui/monotonic-no-binds.rs delete mode 100644 macros/ui/monotonic-no-binds.stderr delete mode 100644 macros/ui/monotonic-no-paran.rs delete mode 100644 macros/ui/monotonic-no-paran.stderr delete mode 100644 macros/ui/monotonic-timer-collision.rs delete mode 100644 macros/ui/monotonic-timer-collision.stderr delete mode 100644 macros/ui/monotonic-with-attrs.rs delete mode 100644 macros/ui/monotonic-with-attrs.stderr delete mode 100644 macros/ui/pub-local.stderr delete mode 100644 macros/ui/pub-shared.stderr delete mode 100644 macros/ui/shared-lock-free.stderr delete mode 100644 macros/ui/task-double-capacity.rs delete mode 100644 macros/ui/task-double-capacity.stderr create mode 100644 macros/ui/task-zero-prio.rs create mode 100644 macros/ui/task-zero-prio.stderr (limited to 'macros') diff --git a/macros/ui/async-local-resouces.rs b/macros/ui/async-local-resouces.rs deleted file mode 100644 index 1ba5865..0000000 --- a/macros/ui/async-local-resouces.rs +++ /dev/null @@ -1,28 +0,0 @@ -#![no_main] - -#[rtic_macros::mock_app(device = mock)] -mod app { - #[shared] - struct Shared { - #[lock_free] - e: u32, - } - - #[local] - struct Local {} - - #[init] - fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {} - - // e ok - #[task(priority = 1, shared = [e])] - fn uart0(cx: uart0::Context) {} - - // e ok - #[task(priority = 1, shared = [e])] - fn uart1(cx: uart1::Context) {} - - // e not ok - #[task(priority = 1, shared = [e])] - async fn async_task(cx: async_task::Context) {} -} diff --git a/macros/ui/async-local-resouces.stderr b/macros/ui/async-local-resouces.stderr deleted file mode 100644 index 7ce7517..0000000 --- a/macros/ui/async-local-resouces.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: Lock free shared resource "e" is used by an async tasks, which is forbidden - --> ui/async-local-resouces.rs:26:36 - | -26 | #[task(priority = 1, shared = [e])] - | ^ diff --git a/macros/ui/async-zero-prio-tasks.rs b/macros/ui/async-zero-prio-tasks.rs deleted file mode 100644 index 91e0990..0000000 --- a/macros/ui/async-zero-prio-tasks.rs +++ /dev/null @@ -1,19 +0,0 @@ -#![no_main] - -#[rtic_macros::mock_app(device = mock)] -mod app { - #[shared] - struct Shared {} - - #[local] - struct Local {} - - #[init] - fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {} - - #[task(priority = 0)] - fn foo(_: foo::Context) {} - - #[idle] - fn idle(_: idle::Context) -> ! {} -} diff --git a/macros/ui/async-zero-prio-tasks.stderr b/macros/ui/async-zero-prio-tasks.stderr deleted file mode 100644 index d617feb..0000000 --- a/macros/ui/async-zero-prio-tasks.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error: Software task "foo" has priority 0, but `#[idle]` is defined. 0-priority software tasks are only allowed if there is no `#[idle]`. - --> ui/async-zero-prio-tasks.rs:15:8 - | -15 | fn foo(_: foo::Context) {} - | ^^^ - -error: Software task "foo" has priority 0, but is not `async`. 0-priority software tasks must be `async`. - --> ui/async-zero-prio-tasks.rs:15:8 - | -15 | fn foo(_: foo::Context) {} - | ^^^ diff --git a/macros/ui/extern-interrupt-used.rs b/macros/ui/extern-interrupt-used.rs index a6e0b3b..6346a7d 100644 --- a/macros/ui/extern-interrupt-used.rs +++ b/macros/ui/extern-interrupt-used.rs @@ -9,7 +9,7 @@ mod app { struct Local {} #[init] - fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {} + fn init(_: init::Context) -> (Shared, Local) {} #[task(binds = EXTI0)] fn foo(_: foo::Context) {} diff --git a/macros/ui/extern-interrupt-used.stderr b/macros/ui/extern-interrupt-used.stderr index f9510d7..970d39b 100644 --- a/macros/ui/extern-interrupt-used.stderr +++ b/macros/ui/extern-interrupt-used.stderr @@ -1,5 +1,5 @@ error: dispatcher interrupts can't be used as hardware tasks - --> $DIR/extern-interrupt-used.rs:14:20 + --> ui/extern-interrupt-used.rs:14:20 | 14 | #[task(binds = EXTI0)] | ^^^^^ diff --git a/macros/ui/idle-double-local.stderr b/macros/ui/idle-double-local.stderr index d3ba4ec..b558136 100644 --- a/macros/ui/idle-double-local.stderr +++ b/macros/ui/idle-double-local.stderr @@ -1,5 +1,5 @@ error: argument appears more than once - --> $DIR/idle-double-local.rs:5:25 + --> ui/idle-double-local.rs:5:25 | 5 | #[idle(local = [A], local = [B])] | ^^^^^ diff --git a/macros/ui/idle-double-shared.stderr b/macros/ui/idle-double-shared.stderr index 84864a1..6f62ad2 100644 --- a/macros/ui/idle-double-shared.stderr +++ b/macros/ui/idle-double-shared.stderr @@ -1,5 +1,5 @@ error: argument appears more than once - --> $DIR/idle-double-shared.rs:5:26 + --> ui/idle-double-shared.rs:5:26 | 5 | #[idle(shared = [A], shared = [B])] | ^^^^^^ diff --git a/macros/ui/init-divergent.stderr b/macros/ui/init-divergent.stderr index 2d5cc39..9f6acf6 100644 --- a/macros/ui/init-divergent.stderr +++ b/macros/ui/init-divergent.stderr @@ -1,5 +1,5 @@ -error: the `#[init]` function must have signature `fn(init::Context) -> (Shared resources struct, Local resources struct, init::Monotonics)` - --> $DIR/init-divergent.rs:12:8 +error: the `#[init]` function must have signature `fn(init::Context) -> (Shared resources struct, Local resources struct)` + --> ui/init-divergent.rs:12:8 | 12 | fn init(_: init::Context) -> ! {} | ^^^^ diff --git a/macros/ui/init-double-local.stderr b/macros/ui/init-double-local.stderr index 5ffd2c1..07c3b50 100644 --- a/macros/ui/init-double-local.stderr +++ b/macros/ui/init-double-local.stderr @@ -1,5 +1,5 @@ error: argument appears more than once - --> $DIR/init-double-local.rs:5:25 + --> ui/init-double-local.rs:5:25 | 5 | #[init(local = [A], local = [B])] | ^^^^^ diff --git a/macros/ui/init-double-shared.stderr b/macros/ui/init-double-shared.stderr index b6b1f6d..af2a97b 100644 --- a/macros/ui/init-double-shared.stderr +++ b/macros/ui/init-double-shared.stderr @@ -1,5 +1,5 @@ error: unexpected argument - --> $DIR/init-double-shared.rs:5:12 + --> ui/init-double-shared.rs:5:12 | 5 | #[init(shared = [A], shared = [B])] | ^^^^^^ diff --git a/macros/ui/init-input.rs b/macros/ui/init-input.rs index ac2a1bd..d41a503 100644 --- a/macros/ui/init-input.rs +++ b/macros/ui/init-input.rs @@ -9,5 +9,5 @@ mod app { struct Local {} #[init] - fn init(_: init::Context, _undef: u32) -> (Shared, Local, init::Monotonics) {} + fn init(_: init::Context, _undef: u32) -> (Shared, Local) {} } diff --git a/macros/ui/init-input.stderr b/macros/ui/init-input.stderr index 983c469..e236043 100644 --- a/macros/ui/init-input.stderr +++ b/macros/ui/init-input.stderr @@ -1,5 +1,5 @@ -error: the `#[init]` function must have signature `fn(init::Context) -> (Shared resources struct, Local resources struct, init::Monotonics)` - --> $DIR/init-input.rs:12:8 +error: the `#[init]` function must have signature `fn(init::Context) -> (Shared resources struct, Local resources struct)` + --> ui/init-input.rs:12:8 | -12 | fn init(_: init::Context, _undef: u32) -> (Shared, Local, init::Monotonics) {} +12 | fn init(_: init::Context, _undef: u32) -> (Shared, Local) {} | ^^^^ diff --git a/macros/ui/init-no-context.rs b/macros/ui/init-no-context.rs index a74093a..cdce4c5 100644 --- a/macros/ui/init-no-context.rs +++ b/macros/ui/init-no-context.rs @@ -9,5 +9,5 @@ mod app { struct Local {} #[init] - fn init() -> (Shared, Local, init::Monotonics) {} + fn init() -> (Shared, Local) {} } diff --git a/macros/ui/init-no-context.stderr b/macros/ui/init-no-context.stderr index 742e2ab..28e1fd4 100644 --- a/macros/ui/init-no-context.stderr +++ b/macros/ui/init-no-context.stderr @@ -1,5 +1,5 @@ -error: the `#[init]` function must have signature `fn(init::Context) -> (Shared resources struct, Local resources struct, init::Monotonics)` - --> $DIR/init-no-context.rs:12:8 +error: the `#[init]` function must have signature `fn(init::Context) -> (Shared resources struct, Local resources struct)` + --> ui/init-no-context.rs:12:8 | -12 | fn init() -> (Shared, Local, init::Monotonics) {} +12 | fn init() -> (Shared, Local) {} | ^^^^ diff --git a/macros/ui/init-output.stderr b/macros/ui/init-output.stderr index 03e982c..8bc3c83 100644 --- a/macros/ui/init-output.stderr +++ b/macros/ui/init-output.stderr @@ -1,5 +1,5 @@ -error: the `#[init]` function must have signature `fn(init::Context) -> (Shared resources struct, Local resources struct, init::Monotonics)` - --> $DIR/init-output.rs:6:8 +error: the `#[init]` function must have signature `fn(init::Context) -> (Shared resources struct, Local resources struct)` + --> ui/init-output.rs:6:8 | 6 | fn init(_: init::Context) -> u32 { | ^^^^ diff --git a/macros/ui/init-pub.rs b/macros/ui/init-pub.rs index 43375e4..dd59aa1 100644 --- a/macros/ui/init-pub.rs +++ b/macros/ui/init-pub.rs @@ -9,5 +9,5 @@ mod app { struct Local {} #[init] - pub fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {} + pub fn init(_: init::Context) -> (Shared, Local) {} } diff --git a/macros/ui/init-pub.stderr b/macros/ui/init-pub.stderr index eb68e1e..b1610ed 100644 --- a/macros/ui/init-pub.stderr +++ b/macros/ui/init-pub.stderr @@ -1,5 +1,5 @@ -error: the `#[init]` function must have signature `fn(init::Context) -> (Shared resources struct, Local resources struct, init::Monotonics)` - --> $DIR/init-pub.rs:12:12 +error: the `#[init]` function must have signature `fn(init::Context) -> (Shared resources struct, Local resources struct)` + --> ui/init-pub.rs:12:12 | -12 | pub fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {} +12 | pub fn init(_: init::Context) -> (Shared, Local) {} | ^^^^ diff --git a/macros/ui/init-unsafe.rs b/macros/ui/init-unsafe.rs index b5d391d..4f89baf 100644 --- a/macros/ui/init-unsafe.rs +++ b/macros/ui/init-unsafe.rs @@ -3,5 +3,5 @@ #[rtic_macros::mock_app(device = mock)] mod app { #[init] - unsafe fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {} + unsafe fn init(_: init::Context) -> (Shared, Local) {} } diff --git a/macros/ui/init-unsafe.stderr b/macros/ui/init-unsafe.stderr index 2a48533..fd0b8f3 100644 --- a/macros/ui/init-unsafe.stderr +++ b/macros/ui/init-unsafe.stderr @@ -1,5 +1,5 @@ -error: the `#[init]` function must have signature `fn(init::Context) -> (Shared resources struct, Local resources struct, init::Monotonics)` - --> $DIR/init-unsafe.rs:6:15 +error: the `#[init]` function must have signature `fn(init::Context) -> (Shared resources struct, Local resources struct)` + --> ui/init-unsafe.rs:6:15 | -6 | unsafe fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {} +6 | unsafe fn init(_: init::Context) -> (Shared, Local) {} | ^^^^ diff --git a/macros/ui/interrupt-double.stderr b/macros/ui/interrupt-double.stderr index 62b979b..8db34e2 100644 --- a/macros/ui/interrupt-double.stderr +++ b/macros/ui/interrupt-double.stderr @@ -1,5 +1,5 @@ error: this interrupt is already bound - --> $DIR/interrupt-double.rs:8:20 + --> ui/interrupt-double.rs:8:20 | 8 | #[task(binds = UART0)] | ^^^^^ diff --git a/macros/ui/local-collision-2.rs b/macros/ui/local-collision-2.rs index 7bd092c..08bc8e5 100644 --- a/macros/ui/local-collision-2.rs +++ b/macros/ui/local-collision-2.rs @@ -10,12 +10,9 @@ mod app { a: u32, } - fn bar(_: bar::Context) {} - #[task(local = [a: u8 = 3])] - fn bar(_: bar::Context) {} + async fn bar(_: bar::Context) {} #[init(local = [a: u16 = 2])] - fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {} + fn init(_: init::Context) -> (Shared, Local) {} } - diff --git a/macros/ui/local-collision-2.stderr b/macros/ui/local-collision-2.stderr index 1e4c5fa..47dbbe3 100644 --- a/macros/ui/local-collision-2.stderr +++ b/macros/ui/local-collision-2.stderr @@ -1,17 +1,17 @@ error: Local resource "a" is used by multiple tasks or collides with multiple definitions - --> $DIR/local-collision-2.rs:10:9 + --> ui/local-collision-2.rs:10:9 | 10 | a: u32, | ^ error: Local resource "a" is used by multiple tasks or collides with multiple definitions - --> $DIR/local-collision-2.rs:18:21 + --> ui/local-collision-2.rs:16:21 | -18 | #[init(local = [a: u16 = 2])] +16 | #[init(local = [a: u16 = 2])] | ^ error: Local resource "a" is used by multiple tasks or collides with multiple definitions - --> $DIR/local-collision-2.rs:15:21 + --> ui/local-collision-2.rs:13:21 | -15 | #[task(local = [a: u8 = 3])] +13 | #[task(local = [a: u8 = 3])] | ^ diff --git a/macros/ui/local-collision.rs b/macros/ui/local-collision.rs index 7dbe976..0e4eef7 100644 --- a/macros/ui/local-collision.rs +++ b/macros/ui/local-collision.rs @@ -11,11 +11,11 @@ mod app { } #[task(local = [a])] - fn foo(_: foo::Context) {} + async fn foo(_: foo::Context) {} #[task(local = [a: u8 = 3])] - fn bar(_: bar::Context) {} + async fn bar(_: bar::Context) {} #[init] - fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {} + fn init(_: init::Context) -> (Shared, Local) {} } diff --git a/macros/ui/local-collision.stderr b/macros/ui/local-collision.stderr index 1ba1da9..47fbb6e 100644 --- a/macros/ui/local-collision.stderr +++ b/macros/ui/local-collision.stderr @@ -1,11 +1,11 @@ error: Local resource "a" is used by multiple tasks or collides with multiple definitions - --> $DIR/local-collision.rs:10:9 + --> ui/local-collision.rs:10:9 | 10 | a: u32, | ^ error: Local resource "a" is used by multiple tasks or collides with multiple definitions - --> $DIR/local-collision.rs:16:21 + --> ui/local-collision.rs:16:21 | 16 | #[task(local = [a: u8 = 3])] | ^ diff --git a/macros/ui/local-malformed-1.rs b/macros/ui/local-malformed-1.rs index 7efcd9c..219eef5 100644 --- a/macros/ui/local-malformed-1.rs +++ b/macros/ui/local-malformed-1.rs @@ -9,8 +9,8 @@ mod app { struct Local {} #[task(local = [a:])] - fn foo(_: foo::Context) {} + async fn foo(_: foo::Context) {} #[init] - fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {} + fn init(_: init::Context) -> (Shared, Local) {} } diff --git a/macros/ui/local-malformed-2.rs b/macros/ui/local-malformed-2.rs index ce5f891..d691453 100644 --- a/macros/ui/local-malformed-2.rs +++ b/macros/ui/local-malformed-2.rs @@ -9,8 +9,8 @@ mod app { struct Local {} #[task(local = [a: u32])] - fn foo(_: foo::Context) {} + async fn foo(_: foo::Context) {} #[init] - fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {} + fn init(_: init::Context) -> (Shared, Local) {} } diff --git a/macros/ui/local-malformed-2.stderr b/macros/ui/local-malformed-2.stderr index ceb0405..0b448f0 100644 --- a/macros/ui/local-malformed-2.stderr +++ b/macros/ui/local-malformed-2.stderr @@ -2,4 +2,4 @@ error: malformed, expected 'IDENT: TYPE = EXPR' --> ui/local-malformed-2.rs:11:21 | 11 | #[task(local = [a: u32])] - | ^ + | ^^^^^^ diff --git a/macros/ui/local-malformed-3.rs b/macros/ui/local-malformed-3.rs index 935dc2c..7eddfa4 100644 --- a/macros/ui/local-malformed-3.rs +++ b/macros/ui/local-malformed-3.rs @@ -9,8 +9,8 @@ mod app { struct Local {} #[task(local = [a: u32 =])] - fn foo(_: foo::Context) {} + async fn foo(_: foo::Context) {} #[init] - fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {} + fn init(_: init::Context) -> (Shared, Local) {} } diff --git a/macros/ui/local-malformed-4.rs b/macros/ui/local-malformed-4.rs index 49661b5..b913947 100644 --- a/macros/ui/local-malformed-4.rs +++ b/macros/ui/local-malformed-4.rs @@ -9,8 +9,8 @@ mod app { struct Local {} #[task(local = [a = u32])] - fn foo(_: foo::Context) {} + async fn foo(_: foo::Context) {} #[init] - fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {} + fn init(_: init::Context) -> (Shared, Local) {} } diff --git a/macros/ui/local-not-declared.rs b/macros/ui/local-not-declared.rs index 5a38b3d..7c087e4 100644 --- a/macros/ui/local-not-declared.rs +++ b/macros/ui/local-not-declared.rs @@ -9,8 +9,8 @@ mod app { struct Local {} #[task(local = [A])] - fn foo(_: foo::Context) {} + async fn foo(_: foo::Context) {} #[init] - fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {} + fn init(_: init::Context) -> (Shared, Local) {} } diff --git a/macros/ui/local-not-declared.stderr b/macros/ui/local-not-declared.stderr index 540b4bb..10d4b04 100644 --- a/macros/ui/local-not-declared.stderr +++ b/macros/ui/local-not-declared.stderr @@ -1,5 +1,5 @@ error: this local resource has NOT been declared - --> $DIR/local-not-declared.rs:11:21 + --> ui/local-not-declared.rs:11:21 | 11 | #[task(local = [A])] | ^ diff --git a/macros/ui/local-pub.rs b/macros/ui/local-pub.rs index 8c51754..42da4f4 100644 --- a/macros/ui/local-pub.rs +++ b/macros/ui/local-pub.rs @@ -2,8 +2,14 @@ #[rtic_macros::mock_app(device = mock)] mod app { + #[shared] + struct Shared {} + #[local] struct Local { pub x: u32, } + + #[init] + fn init(_: init::Context) -> (Shared, Local) {} } diff --git a/macros/ui/local-pub.stderr b/macros/ui/local-pub.stderr index 041bc59..e4814ca 100644 --- a/macros/ui/local-pub.stderr +++ b/macros/ui/local-pub.stderr @@ -1,5 +1,5 @@ error: this field must have inherited / private visibility - --> $DIR/local-pub.rs:7:13 - | -7 | pub x: u32, - | ^ + --> ui/local-pub.rs:10:13 + | +10 | pub x: u32, + | ^ diff --git a/macros/ui/local-shared-attribute.rs b/macros/ui/local-shared-attribute.rs index 1ccce4a..c594b5f 100644 --- a/macros/ui/local-shared-attribute.rs +++ b/macros/ui/local-shared-attribute.rs @@ -2,13 +2,20 @@ #[rtic_macros::mock_app(device = mock)] mod app { + #[shared] + struct Shared {} + + #[local] + struct Local {} + + #[init] + fn init(_: init::Context) -> (Shared, Local) {} + #[task(local = [ #[test] a: u32 = 0, // Ok #[test] b, // Error ])] - fn foo(_: foo::Context) { - - } + fn foo(_: foo::Context) {} } diff --git a/macros/ui/local-shared-attribute.stderr b/macros/ui/local-shared-attribute.stderr index 5c15fb5..a8130e8 100644 --- a/macros/ui/local-shared-attribute.stderr +++ b/macros/ui/local-shared-attribute.stderr @@ -1,5 +1,6 @@ error: attributes are not supported here - --> $DIR/local-shared-attribute.rs:8:9 - | -8 | #[test] - | ^ + --> ui/local-shared-attribute.rs:17:9 + | +17 | / #[test] +18 | | b, // Error + | |_________^ diff --git a/macros/ui/local-shared.rs b/macros/ui/local-shared.rs index f6fb491..4e8f9f4 100644 --- a/macros/ui/local-shared.rs +++ b/macros/ui/local-shared.rs @@ -12,7 +12,7 @@ mod app { } #[init] - fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {} + fn init(_: init::Context) -> (Shared, Local) {} // l2 ok #[idle(local = [l2])] @@ -20,9 +20,9 @@ mod app { // l1 rejected (not local) #[task(priority = 1, local = [l1])] - fn uart0(cx: uart0::Context) {} + async fn uart0(cx: uart0::Context) {} // l1 rejected (not lock_free) #[task(priority = 2, local = [l1])] - fn uart1(cx: uart1::Context) {} + async fn uart1(cx: uart1::Context) {} } diff --git a/macros/ui/local-shared.stderr b/macros/ui/local-shared.stderr index 0d22db3..fceb763 100644 --- a/macros/ui/local-shared.stderr +++ b/macros/ui/local-shared.stderr @@ -1,11 +1,11 @@ error: Local resource "l1" is used by multiple tasks or collides with multiple definitions - --> $DIR/local-shared.rs:22:35 + --> ui/local-shared.rs:22:35 | 22 | #[task(priority = 1, local = [l1])] | ^^ error: Local resource "l1" is used by multiple tasks or collides with multiple definitions - --> $DIR/local-shared.rs:26:35 + --> ui/local-shared.rs:26:35 | 26 | #[task(priority = 2, local = [l1])] | ^^ diff --git a/macros/ui/monotonic-binds-collision-task.rs b/macros/ui/monotonic-binds-collision-task.rs deleted file mode 100644 index 1c58f9d..0000000 --- a/macros/ui/monotonic-binds-collision-task.rs +++ /dev/null @@ -1,10 +0,0 @@ -#![no_main] - -#[rtic_macros::mock_app(device = mock)] -mod app { - #[monotonic(binds = Tim1)] - type Fast1 = hal::Tim1Monotonic; - - #[task(binds = Tim1)] - fn foo(_: foo::Context) {} -} diff --git a/macros/ui/monotonic-binds-collision-task.stderr b/macros/ui/monotonic-binds-collision-task.stderr deleted file mode 100644 index 8f84986..0000000 --- a/macros/ui/monotonic-binds-collision-task.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: this interrupt is already bound - --> $DIR/monotonic-binds-collision-task.rs:8:20 - | -8 | #[task(binds = Tim1)] - | ^^^^ diff --git a/macros/ui/monotonic-binds-collision.rs b/macros/ui/monotonic-binds-collision.rs deleted file mode 100644 index 4e54814..0000000 --- a/macros/ui/monotonic-binds-collision.rs +++ /dev/null @@ -1,10 +0,0 @@ -#![no_main] - -#[rtic_macros::mock_app(device = mock)] -mod app { - #[monotonic(binds = Tim1)] - type Fast1 = hal::Tim1Monotonic; - - #[monotonic(binds = Tim1)] - type Fast2 = hal::Tim2Monotonic; -} diff --git a/macros/ui/monotonic-binds-collision.stderr b/macros/ui/monotonic-binds-collision.stderr deleted file mode 100644 index 62b764b..0000000 --- a/macros/ui/monotonic-binds-collision.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: this interrupt is already bound - --> $DIR/monotonic-binds-collision.rs:8:25 - | -8 | #[monotonic(binds = Tim1)] - | ^^^^ diff --git a/macros/ui/monotonic-double-binds.rs b/macros/ui/monotonic-double-binds.rs deleted file mode 100644 index 1705dc4..0000000 --- a/macros/ui/monotonic-double-binds.rs +++ /dev/null @@ -1,7 +0,0 @@ -#![no_main] - -#[rtic_macros::mock_app(device = mock)] -mod app { - #[monotonic(binds = Tim1, binds = Tim2)] - type Fast = hal::Tim1Monotonic; -} diff --git a/macros/ui/monotonic-double-binds.stderr b/macros/ui/monotonic-double-binds.stderr deleted file mode 100644 index c7313df..0000000 --- a/macros/ui/monotonic-double-binds.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: argument appears more than once - --> $DIR/monotonic-double-binds.rs:5:31 - | -5 | #[monotonic(binds = Tim1, binds = Tim2)] - | ^^^^^ diff --git a/macros/ui/monotonic-double-default.rs b/macros/ui/monotonic-double-default.rs deleted file mode 100644 index dc4eac6..0000000 --- a/macros/ui/monotonic-double-default.rs +++ /dev/null @@ -1,7 +0,0 @@ -#![no_main] - -#[rtic_macros::mock_app(device = mock)] -mod app { - #[monotonic(binds = Tim1, default = true, default = false)] - type Fast = hal::Tim1Monotonic; -} diff --git a/macros/ui/monotonic-double-default.stderr b/macros/ui/monotonic-double-default.stderr deleted file mode 100644 index 9819d04..0000000 --- a/macros/ui/monotonic-double-default.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: argument appears more than once - --> $DIR/monotonic-double-default.rs:5:47 - | -5 | #[monotonic(binds = Tim1, default = true, default = false)] - | ^^^^^^^ diff --git a/macros/ui/monotonic-double-prio.rs b/macros/ui/monotonic-double-prio.rs deleted file mode 100644 index 4330ddb..0000000 --- a/macros/ui/monotonic-double-prio.rs +++ /dev/null @@ -1,7 +0,0 @@ -#![no_main] - -#[rtic_macros::mock_app(device = mock)] -mod app { - #[monotonic(binds = Tim1, priority = 1, priority = 2)] - type Fast = hal::Tim1Monotonic; -} diff --git a/macros/ui/monotonic-double-prio.stderr b/macros/ui/monotonic-double-prio.stderr deleted file mode 100644 index fa888e2..0000000 --- a/macros/ui/monotonic-double-prio.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: argument appears more than once - --> $DIR/monotonic-double-prio.rs:5:45 - | -5 | #[monotonic(binds = Tim1, priority = 1, priority = 2)] - | ^^^^^^^^ diff --git a/macros/ui/monotonic-double.rs b/macros/ui/monotonic-double.rs deleted file mode 100644 index 3c43fae..0000000 --- a/macros/ui/monotonic-double.rs +++ /dev/null @@ -1,10 +0,0 @@ -#![no_main] - -#[rtic_macros::mock_app(device = mock)] -mod app { - #[monotonic(binds = Tim1)] - type Fast = hal::Tim1Monotonic; - - #[monotonic(binds = Tim1)] - type Fast = hal::Tim1Monotonic; -} diff --git a/macros/ui/monotonic-double.stderr b/macros/ui/monotonic-double.stderr deleted file mode 100644 index 9fab84c..0000000 --- a/macros/ui/monotonic-double.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: `#[monotonic(...)]` on a specific type must appear at most once - --> ui/monotonic-double.rs:9:10 - | -9 | type Fast = hal::Tim1Monotonic; - | ^^^^ diff --git a/macros/ui/monotonic-name-collision.rs b/macros/ui/monotonic-name-collision.rs deleted file mode 100644 index d8d4431..0000000 --- a/macros/ui/monotonic-name-collision.rs +++ /dev/null @@ -1,10 +0,0 @@ -#![no_main] - -#[rtic_macros::mock_app(device = mock)] -mod app { - #[monotonic(binds = Tim1)] - type Fast1 = hal::Tim1Monotonic; - - #[monotonic(binds = Tim2)] - type Fast1 = hal::Tim2Monotonic; -} diff --git a/macros/ui/monotonic-name-collision.stderr b/macros/ui/monotonic-name-collision.stderr deleted file mode 100644 index 6557ee5..0000000 --- a/macros/ui/monotonic-name-collision.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: `#[monotonic(...)]` on a specific type must appear at most once - --> ui/monotonic-name-collision.rs:9:10 - | -9 | type Fast1 = hal::Tim2Monotonic; - | ^^^^^ diff --git a/macros/ui/monotonic-no-binds.rs b/macros/ui/monotonic-no-binds.rs deleted file mode 100644 index 462d73e..0000000 --- a/macros/ui/monotonic-no-binds.rs +++ /dev/null @@ -1,7 +0,0 @@ -#![no_main] - -#[rtic_macros::mock_app(device = mock)] -mod app { - #[monotonic()] - type Fast = hal::Tim1Monotonic; -} diff --git a/macros/ui/monotonic-no-binds.stderr b/macros/ui/monotonic-no-binds.stderr deleted file mode 100644 index 0ef7b60..0000000 --- a/macros/ui/monotonic-no-binds.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: `binds = ...` is missing - --> $DIR/monotonic-no-binds.rs:5:17 - | -5 | #[monotonic()] - | ^ diff --git a/macros/ui/monotonic-no-paran.rs b/macros/ui/monotonic-no-paran.rs deleted file mode 100644 index e294bc8..0000000 --- a/macros/ui/monotonic-no-paran.rs +++ /dev/null @@ -1,8 +0,0 @@ -#![no_main] - -#[rtic_macros::mock_app(device = mock)] -mod app { - #[monotonic] - type Fast = hal::Tim1Monotonic; -} - diff --git a/macros/ui/monotonic-no-paran.stderr b/macros/ui/monotonic-no-paran.stderr deleted file mode 100644 index c2b32c5..0000000 --- a/macros/ui/monotonic-no-paran.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: expected opening ( in #[monotonic( ... )] - --> ui/monotonic-no-paran.rs:5:7 - | -5 | #[monotonic] - | ^^^^^^^^^ diff --git a/macros/ui/monotonic-timer-collision.rs b/macros/ui/monotonic-timer-collision.rs deleted file mode 100644 index 5663ad7..0000000 --- a/macros/ui/monotonic-timer-collision.rs +++ /dev/null @@ -1,10 +0,0 @@ -#![no_main] - -#[rtic_macros::mock_app(device = mock)] -mod app { - #[monotonic(binds = Tim1)] - type Fast1 = hal::Tim1Monotonic; - - #[monotonic(binds = Tim2)] - type Fast2 = hal::Tim1Monotonic; -} diff --git a/macros/ui/monotonic-timer-collision.stderr b/macros/ui/monotonic-timer-collision.stderr deleted file mode 100644 index 239b96b..0000000 --- a/macros/ui/monotonic-timer-collision.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: this type is already used by another monotonic - --> $DIR/monotonic-timer-collision.rs:9:18 - | -9 | type Fast2 = hal::Tim1Monotonic; - | ^^^ diff --git a/macros/ui/monotonic-with-attrs.rs b/macros/ui/monotonic-with-attrs.rs deleted file mode 100644 index 7c63fbb..0000000 --- a/macros/ui/monotonic-with-attrs.rs +++ /dev/null @@ -1,8 +0,0 @@ -#![no_main] - -#[rtic_macros::mock_app(device = mock)] -mod app { - #[no_mangle] - #[monotonic(binds = Tim1)] - type Fast = hal::Tim1Monotonic; -} diff --git a/macros/ui/monotonic-with-attrs.stderr b/macros/ui/monotonic-with-attrs.stderr deleted file mode 100644 index 62655d8..0000000 --- a/macros/ui/monotonic-with-attrs.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: Monotonic does not support attributes other than `#[cfg]` - --> $DIR/monotonic-with-attrs.rs:5:7 - | -5 | #[no_mangle] - | ^^^^^^^^^ diff --git a/macros/ui/pub-local.stderr b/macros/ui/pub-local.stderr deleted file mode 100644 index dee818c..0000000 --- a/macros/ui/pub-local.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: this field must have inherited / private visibility - --> $DIR/pub-local.rs:7:13 - | -7 | pub x: u32, - | ^ diff --git a/macros/ui/pub-shared.stderr b/macros/ui/pub-shared.stderr deleted file mode 100644 index 0fdb1ff..0000000 --- a/macros/ui/pub-shared.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: this field must have inherited / private visibility - --> $DIR/pub-shared.rs:7:13 - | -7 | pub x: u32, - | ^ diff --git a/macros/ui/shared-lock-free.rs b/macros/ui/shared-lock-free.rs index c7f8a16..b3a4b9c 100644 --- a/macros/ui/shared-lock-free.rs +++ b/macros/ui/shared-lock-free.rs @@ -17,7 +17,7 @@ mod app { struct Local {} #[init] - fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {} + fn init(_: init::Context) -> (Shared, Local) {} // e2 ok #[idle(shared = [e2])] @@ -27,12 +27,12 @@ mod app { } // e1 rejected (not lock_free) - #[task(priority = 1, shared = [e1])] + #[task(binds = UART0, priority = 1, shared = [e1])] fn uart0(cx: uart0::Context) { *cx.resources.e1 += 10; } // e1 rejected (not lock_free) - #[task(priority = 2, shared = [e1])] + #[task(binds = UART1, priority = 2, shared = [e1])] fn uart1(cx: uart1::Context) {} } diff --git a/macros/ui/shared-lock-free.stderr b/macros/ui/shared-lock-free.stderr deleted file mode 100644 index c6820e8..0000000 --- a/macros/ui/shared-lock-free.stderr +++ /dev/null @@ -1,17 +0,0 @@ -error: Lock free shared resource "e1" is used by tasks at different priorities - --> $DIR/shared-lock-free.rs:9:9 - | -9 | e1: u32, - | ^^ - -error: Shared resource "e1" is declared lock free but used by tasks at different priorities - --> $DIR/shared-lock-free.rs:30:36 - | -30 | #[task(priority = 1, shared = [e1])] - | ^^ - -error: Shared resource "e1" is declared lock free but used by tasks at different priorities - --> $DIR/shared-lock-free.rs:36:36 - | -36 | #[task(priority = 2, shared = [e1])] - | ^^ diff --git a/macros/ui/shared-not-declared.rs b/macros/ui/shared-not-declared.rs index aca4178..5fef534 100644 --- a/macros/ui/shared-not-declared.rs +++ b/macros/ui/shared-not-declared.rs @@ -9,8 +9,8 @@ mod app { struct Local {} #[task(shared = [A])] - fn foo(_: foo::Context) {} + async fn foo(_: foo::Context) {} #[init] - fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {} + fn init(_: init::Context) -> (Shared, Local) {} } diff --git a/macros/ui/shared-not-declared.stderr b/macros/ui/shared-not-declared.stderr index c174251..7c5fb32 100644 --- a/macros/ui/shared-not-declared.stderr +++ b/macros/ui/shared-not-declared.stderr @@ -1,5 +1,5 @@ error: this shared resource has NOT been declared - --> $DIR/shared-not-declared.rs:11:22 + --> ui/shared-not-declared.rs:11:22 | 11 | #[task(shared = [A])] | ^ diff --git a/macros/ui/shared-pub.stderr b/macros/ui/shared-pub.stderr index 8f761c6..7148893 100644 --- a/macros/ui/shared-pub.stderr +++ b/macros/ui/shared-pub.stderr @@ -1,5 +1,5 @@ error: this field must have inherited / private visibility - --> $DIR/shared-pub.rs:7:13 + --> ui/shared-pub.rs:7:13 | 7 | pub x: u32, | ^ diff --git a/macros/ui/task-divergent.rs b/macros/ui/task-divergent.rs index 5a471f3..ffe2dc0 100644 --- a/macros/ui/task-divergent.rs +++ b/macros/ui/task-divergent.rs @@ -3,7 +3,7 @@ #[rtic_macros::mock_app(device = mock)] mod app { #[task] - fn foo(_: foo::Context) -> ! { + async fn foo(_: foo::Context) -> ! { loop {} } } diff --git a/macros/ui/task-divergent.stderr b/macros/ui/task-divergent.stderr index b25ca5d..bd22bd3 100644 --- a/macros/ui/task-divergent.stderr +++ b/macros/ui/task-divergent.stderr @@ -1,5 +1,5 @@ -error: this task handler must have type signature `(async) fn(foo::Context, ..)` - --> ui/task-divergent.rs:6:8 +error: this task handler must have type signature `async fn(foo::Context)` + --> ui/task-divergent.rs:6:14 | -6 | fn foo(_: foo::Context) -> ! { - | ^^^ +6 | async fn foo(_: foo::Context) -> ! { + | ^^^ diff --git a/macros/ui/task-double-capacity.rs b/macros/ui/task-double-capacity.rs deleted file mode 100644 index 806d973..0000000 --- a/macros/ui/task-double-capacity.rs +++ /dev/null @@ -1,7 +0,0 @@ -#![no_main] - -#[rtic_macros::mock_app(device = mock)] -mod app { - #[task(capacity = 1, capacity = 2)] - fn foo(_: foo::Context) {} -} diff --git a/macros/ui/task-double-capacity.stderr b/macros/ui/task-double-capacity.stderr deleted file mode 100644 index f73bca5..0000000 --- a/macros/ui/task-double-capacity.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: argument appears more than once - --> $DIR/task-double-capacity.rs:5:26 - | -5 | #[task(capacity = 1, capacity = 2)] - | ^^^^^^^^ diff --git a/macros/ui/task-double-local.rs b/macros/ui/task-double-local.rs index 2e465d7..c5277e2 100644 --- a/macros/ui/task-double-local.rs +++ b/macros/ui/task-double-local.rs @@ -3,5 +3,5 @@ #[rtic_macros::mock_app(device = mock)] mod app { #[task(local = [A], local = [B])] - fn foo(_: foo::Context) {} + async fn foo(_: foo::Context) {} } diff --git a/macros/ui/task-double-local.stderr b/macros/ui/task-double-local.stderr index 654ed33..91ed844 100644 --- a/macros/ui/task-double-local.stderr +++ b/macros/ui/task-double-local.stderr @@ -1,5 +1,5 @@ error: argument appears more than once - --> $DIR/task-double-local.rs:5:25 + --> ui/task-double-local.rs:5:25 | 5 | #[task(local = [A], local = [B])] | ^^^^^ diff --git a/macros/ui/task-double-priority.rs b/macros/ui/task-double-priority.rs index 0a2ef0d..5c8bd5b 100644 --- a/macros/ui/task-double-priority.rs +++ b/macros/ui/task-double-priority.rs @@ -3,5 +3,5 @@ #[rtic_macros::mock_app(device = mock)] mod app { #[task(priority = 1, priority = 2)] - fn foo(_: foo::Context) {} + async fn foo(_: foo::Context) {} } diff --git a/macros/ui/task-double-priority.stderr b/macros/ui/task-double-priority.stderr index 3d06dc6..b3c814a 100644 --- a/macros/ui/task-double-priority.stderr +++ b/macros/ui/task-double-priority.stderr @@ -1,5 +1,5 @@ error: argument appears more than once - --> $DIR/task-double-priority.rs:5:26 + --> ui/task-double-priority.rs:5:26 | 5 | #[task(priority = 1, priority = 2)] | ^^^^^^^^ diff --git a/macros/ui/task-double-shared.rs b/macros/ui/task-double-shared.rs index 3b4d411..f9812d3 100644 --- a/macros/ui/task-double-shared.rs +++ b/macros/ui/task-double-shared.rs @@ -3,5 +3,5 @@ #[rtic_macros::mock_app(device = mock)] mod app { #[task(shared = [A], shared = [B])] - fn foo(_: foo::Context) {} + async fn foo(_: foo::Context) {} } diff --git a/macros/ui/task-double-shared.stderr b/macros/ui/task-double-shared.stderr index 6952f06..bb90212 100644 --- a/macros/ui/task-double-shared.stderr +++ b/macros/ui/task-double-shared.stderr @@ -1,5 +1,5 @@ error: argument appears more than once - --> $DIR/task-double-shared.rs:5:26 + --> ui/task-double-shared.rs:5:26 | 5 | #[task(shared = [A], shared = [B])] | ^^^^^^ diff --git a/macros/ui/task-idle.rs b/macros/ui/task-idle.rs index 3be6e28..353c782 100644 --- a/macros/ui/task-idle.rs +++ b/macros/ui/task-idle.rs @@ -9,5 +9,5 @@ mod app { // name collides with `#[idle]` function #[task] - fn foo(_: foo::Context) {} + async fn foo(_: foo::Context) {} } diff --git a/macros/ui/task-idle.stderr b/macros/ui/task-idle.stderr index ba4fc94..4ccc113 100644 --- a/macros/ui/task-idle.stderr +++ b/macros/ui/task-idle.stderr @@ -1,5 +1,5 @@ error: this identifier has already been used - --> $DIR/task-idle.rs:12:8 + --> ui/task-idle.rs:12:14 | -12 | fn foo(_: foo::Context) {} - | ^^^ +12 | async fn foo(_: foo::Context) {} + | ^^^ diff --git a/macros/ui/task-init.rs b/macros/ui/task-init.rs index bab3805..e58fdce 100644 --- a/macros/ui/task-init.rs +++ b/macros/ui/task-init.rs @@ -9,9 +9,9 @@ mod app { struct Local {} #[init] - fn foo(_: foo::Context) -> (Shared, Local, foo::Monotonics) {} + fn foo(_: foo::Context) -> (Shared, Local) {} // name collides with `#[idle]` function #[task] - fn foo(_: foo::Context) {} + async fn foo(_: foo::Context) {} } diff --git a/macros/ui/task-init.stderr b/macros/ui/task-init.stderr index 911af37..161e194 100644 --- a/macros/ui/task-init.stderr +++ b/macros/ui/task-init.stderr @@ -1,5 +1,5 @@ error: this identifier has already been used - --> $DIR/task-init.rs:16:8 + --> ui/task-init.rs:16:14 | -16 | fn foo(_: foo::Context) {} - | ^^^ +16 | async fn foo(_: foo::Context) {} + | ^^^ diff --git a/macros/ui/task-interrupt.rs b/macros/ui/task-interrupt.rs index 5e063de..3d50bd8 100644 --- a/macros/ui/task-interrupt.rs +++ b/macros/ui/task-interrupt.rs @@ -6,5 +6,5 @@ mod app { fn foo(_: foo::Context) {} #[task] - fn foo(_: foo::Context) {} + async fn foo(_: foo::Context) {} } diff --git a/macros/ui/task-interrupt.stderr b/macros/ui/task-interrupt.stderr index 6efb0f9..087b6c6 100644 --- a/macros/ui/task-interrupt.stderr +++ b/macros/ui/task-interrupt.stderr @@ -1,5 +1,5 @@ error: this task is defined multiple times - --> $DIR/task-interrupt.rs:9:8 + --> ui/task-interrupt.rs:9:14 | -9 | fn foo(_: foo::Context) {} - | ^^^ +9 | async fn foo(_: foo::Context) {} + | ^^^ diff --git a/macros/ui/task-no-context.rs b/macros/ui/task-no-context.rs index e2da625..55e8c3b 100644 --- a/macros/ui/task-no-context.rs +++ b/macros/ui/task-no-context.rs @@ -3,5 +3,5 @@ #[rtic_macros::mock_app(device = mock)] mod app { #[task] - fn foo() {} + async fn foo() {} } diff --git a/macros/ui/task-no-context.stderr b/macros/ui/task-no-context.stderr index 8bf3438..91239a1 100644 --- a/macros/ui/task-no-context.stderr +++ b/macros/ui/task-no-context.stderr @@ -1,5 +1,5 @@ -error: this task handler must have type signature `(async) fn(foo::Context, ..)` - --> ui/task-no-context.rs:6:8 +error: this task handler must have type signature `async fn(foo::Context)` + --> ui/task-no-context.rs:6:14 | -6 | fn foo() {} - | ^^^ +6 | async fn foo() {} + | ^^^ diff --git a/macros/ui/task-priority-too-high.rs b/macros/ui/task-priority-too-high.rs index 8c32beb..f33ba56 100644 --- a/macros/ui/task-priority-too-high.rs +++ b/macros/ui/task-priority-too-high.rs @@ -3,5 +3,5 @@ #[rtic_macros::mock_app(device = mock)] mod app { #[task(priority = 256)] - fn foo(_: foo::Context) {} + async fn foo(_: foo::Context) {} } diff --git a/macros/ui/task-pub.rs b/macros/ui/task-pub.rs index 3cbd523..1ae533f 100644 --- a/macros/ui/task-pub.rs +++ b/macros/ui/task-pub.rs @@ -3,5 +3,5 @@ #[rtic_macros::mock_app(device = mock)] mod app { #[task] - pub fn foo(_: foo::Context) {} + pub async fn foo(_: foo::Context) {} } diff --git a/macros/ui/task-pub.stderr b/macros/ui/task-pub.stderr index 56e09b1..72c4e63 100644 --- a/macros/ui/task-pub.stderr +++ b/macros/ui/task-pub.stderr @@ -1,5 +1,5 @@ -error: this task handler must have type signature `(async) fn(foo::Context, ..)` - --> ui/task-pub.rs:6:12 +error: this task handler must have type signature `async fn(foo::Context)` + --> ui/task-pub.rs:6:18 | -6 | pub fn foo(_: foo::Context) {} - | ^^^ +6 | pub async fn foo(_: foo::Context) {} + | ^^^ diff --git a/macros/ui/task-unsafe.rs b/macros/ui/task-unsafe.rs index 44255f0..a8383ef 100644 --- a/macros/ui/task-unsafe.rs +++ b/macros/ui/task-unsafe.rs @@ -3,5 +3,5 @@ #[rtic_macros::mock_app(device = mock)] mod app { #[task] - unsafe fn foo(_: foo::Context) {} + async unsafe fn foo(_: foo::Context) {} } diff --git a/macros/ui/task-unsafe.stderr b/macros/ui/task-unsafe.stderr index 424c5af..4908481 100644 --- a/macros/ui/task-unsafe.stderr +++ b/macros/ui/task-unsafe.stderr @@ -1,5 +1,5 @@ -error: this task handler must have type signature `(async) fn(foo::Context, ..)` - --> ui/task-unsafe.rs:6:15 +error: this task handler must have type signature `async fn(foo::Context)` + --> ui/task-unsafe.rs:6:21 | -6 | unsafe fn foo(_: foo::Context) {} - | ^^^ +6 | async unsafe fn foo(_: foo::Context) {} + | ^^^ diff --git a/macros/ui/task-zero-prio.rs b/macros/ui/task-zero-prio.rs new file mode 100644 index 0000000..de3c86f --- /dev/null +++ b/macros/ui/task-zero-prio.rs @@ -0,0 +1,19 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[shared] + struct Shared {} + + #[local] + struct Local {} + + #[init] + fn init(_: init::Context) -> (Shared, Local) {} + + #[task(priority = 0)] + fn foo(_: foo::Context) {} + + #[idle] + fn idle(_: idle::Context) -> ! {} +} diff --git a/macros/ui/task-zero-prio.stderr b/macros/ui/task-zero-prio.stderr new file mode 100644 index 0000000..f2d8223 --- /dev/null +++ b/macros/ui/task-zero-prio.stderr @@ -0,0 +1,5 @@ +error: this task handler must have type signature `async fn(foo::Context)` + --> ui/task-zero-prio.rs:15:8 + | +15 | fn foo(_: foo::Context) {} + | ^^^ -- cgit v1.2.3 From cbe592688047e41ebfd0f15e7bf5799f81bfcd4a Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Sun, 8 Jan 2023 16:36:48 +0100 Subject: Fix failing UI test --- macros/src/syntax/analyze.rs | 18 ++++++++++-------- macros/ui/shared-lock-free.stderr | 17 +++++++++++++++++ 2 files changed, 27 insertions(+), 8 deletions(-) create mode 100644 macros/ui/shared-lock-free.stderr (limited to 'macros') diff --git a/macros/src/syntax/analyze.rs b/macros/src/syntax/analyze.rs index ff0577d..dd5a9b4 100644 --- a/macros/src/syntax/analyze.rs +++ b/macros/src/syntax/analyze.rs @@ -13,17 +13,17 @@ use crate::syntax::{ pub(crate) fn app(app: &App) -> Result { // Collect all tasks into a vector - type TaskName = String; + 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| ("init".to_string(), Vec::new(), &ht.args.local_resources, 0)) + .map(|ht| (ht.name.clone(), Vec::new(), &ht.args.local_resources, 0)) .chain(app.idle.iter().map(|ht| { ( - "idle".to_string(), + ht.name.clone(), ht.args .shared_resources .iter() @@ -35,7 +35,7 @@ pub(crate) fn app(app: &App) -> Result { })) .chain(app.software_tasks.iter().map(|(name, ht)| { ( - name.to_string(), + name.clone(), ht.args .shared_resources .iter() @@ -47,7 +47,7 @@ pub(crate) fn app(app: &App) -> Result { })) .chain(app.hardware_tasks.iter().map(|(name, ht)| { ( - name.to_string(), + name.clone(), ht.args .shared_resources .iter() @@ -77,16 +77,17 @@ pub(crate) fn app(app: &App) -> Result { for r in tr { // Get all uses of resources annotated lock_free if lf_res == r { - // lock_free resources are not allowed in async tasks - error.push(syn::Error::new( + // 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(), ), )); + } - // TODO: Should this be removed? // HashMap returns the previous existing object if old.key == new.key if let Some(lf_res) = lf_hash.insert(r.to_string(), (task, r, priority)) { // Check if priority differ, if it does, append to @@ -95,6 +96,7 @@ pub(crate) fn app(app: &App) -> Result { 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); diff --git a/macros/ui/shared-lock-free.stderr b/macros/ui/shared-lock-free.stderr new file mode 100644 index 0000000..51e99a0 --- /dev/null +++ b/macros/ui/shared-lock-free.stderr @@ -0,0 +1,17 @@ +error: Lock free shared resource "e1" is used by tasks at different priorities + --> ui/shared-lock-free.rs:9:9 + | +9 | e1: u32, + | ^^ + +error: Shared resource "e1" is declared lock free but used by tasks at different priorities + --> ui/shared-lock-free.rs:30:51 + | +30 | #[task(binds = UART0, priority = 1, shared = [e1])] + | ^^ + +error: Shared resource "e1" is declared lock free but used by tasks at different priorities + --> ui/shared-lock-free.rs:36:51 + | +36 | #[task(binds = UART1, priority = 2, shared = [e1])] + | ^^ -- cgit v1.2.3 From 9a67f00a30f14df3b9635913f728afd0b40c138d Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Sun, 8 Jan 2023 19:16:36 +0100 Subject: Fix typos --- macros/src/codegen/module.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'macros') diff --git a/macros/src/codegen/module.rs b/macros/src/codegen/module.rs index a64abd8..c6f7690 100644 --- a/macros/src/codegen/module.rs +++ b/macros/src/codegen/module.rs @@ -140,7 +140,7 @@ pub fn codegen(ctxt: Context, app: &App, analysis: &Analysis) -> TokenStream2 { let interrupt = &analysis .interrupts .get(&priority) - .expect("RTIC-ICE: interrupt identifer not found") + .expect("RTIC-ICE: interrupt identifier not found") .0; let internal_spawn_ident = util::internal_task_ident(name, "spawn"); -- cgit v1.2.3 From 6d252785e83218eeb5d080836281c90b86ca0e03 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Sun, 8 Jan 2023 21:10:06 +0100 Subject: Support 0 prio tasks --- macros/src/analyze.rs | 1 + macros/src/check.rs | 1 + macros/src/codegen/async_dispatchers.rs | 87 ++++++++++++++++++++++----------- macros/src/codegen/main.rs | 13 +++-- macros/src/codegen/module.rs | 17 ++++--- macros/src/codegen/util.rs | 4 ++ macros/src/syntax/analyze.rs | 33 +++++++------ 7 files changed, 98 insertions(+), 58 deletions(-) (limited to 'macros') diff --git a/macros/src/analyze.rs b/macros/src/analyze.rs index cb42ad6..65774f6 100644 --- a/macros/src/analyze.rs +++ b/macros/src/analyze.rs @@ -36,6 +36,7 @@ pub fn app(analysis: analyze::Analysis, app: &App) -> Analysis { let interrupts: BTreeMap = priorities .iter() + .filter(|prio| **prio > 0) // 0 prio tasks are run in main .copied() .rev() .map(|p| (p, available_interrupt.pop().expect("UNREACHABLE"))) diff --git a/macros/src/check.rs b/macros/src/check.rs index 312b84d..72d0a27 100644 --- a/macros/src/check.rs +++ b/macros/src/check.rs @@ -32,6 +32,7 @@ pub fn app(app: &App) -> parse::Result<()> { first = Some(name); task.args.priority }) + .filter(|prio| *prio > 0) .collect::>(); let need = priorities.len(); diff --git a/macros/src/codegen/async_dispatchers.rs b/macros/src/codegen/async_dispatchers.rs index 62b17fe..f6408e1 100644 --- a/macros/src/codegen/async_dispatchers.rs +++ b/macros/src/codegen/async_dispatchers.rs @@ -26,9 +26,22 @@ pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 { for (&level, channel) in &analysis.channels { let mut stmts = vec![]; - let device = &app.args.device; - let enum_ = util::interrupt_ident(); - let interrupt = util::suffixed(&interrupts[&level].0.to_string()); + + 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"); @@ -60,40 +73,56 @@ pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 { #executor_run_ident.store(false, core::sync::atomic::Ordering::Relaxed); if (&mut *#exec_name.get_mut()).poll(|| { #executor_run_ident.store(true, core::sync::atomic::Ordering::Release); - rtic::pend(#device::#enum_::#interrupt); + #pend_interrupt }) && #rq.load(core::sync::atomic::Ordering::Relaxed) { // If the ready queue is not empty and the executor finished, restart this // dispatch to check if the executor should be restarted. - rtic::pend(#device::#enum_::#interrupt); + #pend_interrupt } } )); } - let doc = format!( - "Interrupt handler to dispatch async tasks at priority {}", - level - ); - let attribute = &interrupts[&level].1.attrs; - items.push(quote!( - #[allow(non_snake_case)] - #[doc = #doc] - #[no_mangle] - #(#attribute)* - unsafe fn #interrupt() { - /// The priority of this interrupt handler - const PRIORITY: u8 = #level; - - rtic::export::run(PRIORITY, || { - // Have the acquire/release semantics outside the checks to no overdo it - core::sync::atomic::fence(core::sync::atomic::Ordering::Acquire); - - #(#stmts)* - - core::sync::atomic::fence(core::sync::atomic::Ordering::Release); - }); - } - )); + 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, || { + // Have the acquire/release semantics outside the checks to no overdo it + core::sync::atomic::fence(core::sync::atomic::Ordering::Acquire); + + #(#stmts)* + + core::sync::atomic::fence(core::sync::atomic::Ordering::Release); + }); + } + )); + } else { + items.push(quote!( + #[allow(non_snake_case)] + unsafe fn #dispatcher_name() -> ! { + loop { + // Have the acquire/release semantics outside the checks to no overdo it + core::sync::atomic::fence(core::sync::atomic::Ordering::Acquire); + + #(#stmts)* + + core::sync::atomic::fence(core::sync::atomic::Ordering::Release); + } + } + )); + } } quote!(#(#items)*) diff --git a/macros/src/codegen/main.rs b/macros/src/codegen/main.rs index 90f09ae..8e7138f 100644 --- a/macros/src/codegen/main.rs +++ b/macros/src/codegen/main.rs @@ -16,11 +16,14 @@ pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 { let name = &idle.name; quote!(#name(#name::Context::new())) } else { - // TODO: No idle defined, check for 0-priority tasks and generate an executor if needed - - quote!(loop { - rtic::export::nop() - }) + 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"); diff --git a/macros/src/codegen/module.rs b/macros/src/codegen/module.rs index c6f7690..70fbb5e 100644 --- a/macros/src/codegen/module.rs +++ b/macros/src/codegen/module.rs @@ -135,13 +135,14 @@ pub fn codegen(ctxt: Context, app: &App, analysis: &Analysis) -> TokenStream2 { // Store a copy of the task cfgs task_cfgs = cfgs.clone(); - let device = &app.args.device; - let enum_ = util::interrupt_ident(); - let interrupt = &analysis - .interrupts - .get(&priority) - .expect("RTIC-ICE: interrupt identifier not found") - .0; + 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"); @@ -160,7 +161,7 @@ pub fn codegen(ctxt: Context, app: &App, analysis: &Analysis) -> TokenStream2 { Err(()) } else { #rq.store(true, core::sync::atomic::Ordering::Release); - rtic::pend(#device::#enum_::#interrupt); + #pend_interrupt Ok(()) } } diff --git a/macros/src/codegen/util.rs b/macros/src/codegen/util.rs index a071ca2..6552839 100644 --- a/macros/src/codegen/util.rs +++ b/macros/src/codegen/util.rs @@ -187,6 +187,10 @@ pub fn need_to_lock_ident(name: &Ident) -> Ident { Ident::new(&format!("{}_that_needs_to_be_locked", name), 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( diff --git a/macros/src/syntax/analyze.rs b/macros/src/syntax/analyze.rs index dd5a9b4..b70ceb8 100644 --- a/macros/src/syntax/analyze.rs +++ b/macros/src/syntax/analyze.rs @@ -248,33 +248,34 @@ pub(crate) fn app(app: &App) -> Result { } } - // Most shared resources need to be `Send` + // Most shared resources need to be `Send`, only 0 prio does not need it let mut send_types = SendTypes::new(); - let owned_by_idle = Ownership::Owned { priority: 0 }; + for (name, res) in app.shared_resources.iter() { - // Handle not owned by idle if ownerships .get(name) - .map(|ownership| *ownership != owned_by_idle) + .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 + // 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 let Some(idle) = &app.idle { - // Only Send if not in idle or not at idle prio - if idle.args.local_resources.get(name).is_none() - && !ownerships - .get(name) - .map(|ownership| *ownership != owned_by_idle) - .unwrap_or(false) - { - send_types.insert(res.ty.clone()); - } - } else { + 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()); } } -- cgit v1.2.3 From c40c89bb4edc22c4a60d8677c660a9ab7eb47e92 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Sun, 8 Jan 2023 21:30:53 +0100 Subject: Clippy fixes --- macros/src/check.rs | 3 +-- macros/src/codegen/async_dispatchers.rs | 3 +-- macros/src/codegen/main.rs | 14 ++++++-------- macros/src/codegen/pre_init.rs | 6 ++---- macros/src/codegen/util.rs | 16 ++++++++-------- macros/src/lib.rs | 7 +++---- macros/src/syntax/parse/app.rs | 6 ++---- macros/src/syntax/parse/hardware_task.rs | 10 ++++------ macros/src/syntax/parse/idle.rs | 5 ++--- macros/src/syntax/parse/init.rs | 5 ++--- macros/src/syntax/parse/software_task.rs | 10 ++++------ macros/src/syntax/parse/util.rs | 14 +++++--------- 12 files changed, 40 insertions(+), 59 deletions(-) (limited to 'macros') diff --git a/macros/src/check.rs b/macros/src/check.rs index 72d0a27..a05c82e 100644 --- a/macros/src/check.rs +++ b/macros/src/check.rs @@ -41,8 +41,7 @@ pub fn app(app: &App) -> parse::Result<()> { let s = { format!( "not enough interrupts to dispatch \ - all software tasks (need: {}; given: {})", - need, given + all software tasks (need: {need}; given: {given})" ) }; diff --git a/macros/src/codegen/async_dispatchers.rs b/macros/src/codegen/async_dispatchers.rs index f6408e1..be02ad0 100644 --- a/macros/src/codegen/async_dispatchers.rs +++ b/macros/src/codegen/async_dispatchers.rs @@ -85,8 +85,7 @@ pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 { if level > 0 { let doc = format!( - "Interrupt handler to dispatch async tasks at priority {}", - level + "Interrupt handler to dispatch async tasks at priority {level}" ); let attribute = &interrupts.get(&level).expect("UNREACHABLE").1.attrs; items.push(quote!( diff --git a/macros/src/codegen/main.rs b/macros/src/codegen/main.rs index 8e7138f..2775d25 100644 --- a/macros/src/codegen/main.rs +++ b/macros/src/codegen/main.rs @@ -15,15 +15,13 @@ pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 { 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 { - if analysis.channels.get(&0).is_some() { - let dispatcher = util::zero_prio_dispatcher_ident(); - quote!(#dispatcher();) - } else { - quote!(loop { - rtic::export::nop() - }) - } + quote!(loop { + rtic::export::nop() + }) }; let main = util::suffixed("main"); diff --git a/macros/src/codegen/pre_init.rs b/macros/src/codegen/pre_init.rs index 1492688..28ba29c 100644 --- a/macros/src/codegen/pre_init.rs +++ b/macros/src/codegen/pre_init.rs @@ -40,8 +40,7 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec { } })) { let es = format!( - "Maximum priority used by interrupt vector '{}' is more than supported by hardware", - name + "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!( @@ -69,8 +68,7 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec { } }) { let es = format!( - "Maximum priority used by interrupt vector '{}' is more than supported by hardware", - name + "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!( diff --git a/macros/src/codegen/util.rs b/macros/src/codegen/util.rs index 6552839..a0caf0a 100644 --- a/macros/src/codegen/util.rs +++ b/macros/src/codegen/util.rs @@ -51,7 +51,7 @@ pub fn impl_mutex( /// Generates an identifier for the `EXECUTOR_RUN` atomics (`async` API) pub fn executor_run_ident(task: &Ident) -> Ident { - mark_internal_name(&format!("{}_EXECUTOR_RUN", task)) + mark_internal_name(&format!("{task}_EXECUTOR_RUN")) } pub fn interrupt_ident() -> Ident { @@ -78,12 +78,12 @@ pub fn is_exception(name: &Ident) -> bool { /// Mark a name as internal pub fn mark_internal_name(name: &str) -> Ident { - Ident::new(&format!("{}_{}", RTIC_INTERNAL, name), Span::call_site()) + 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)) + mark_internal_name(&format!("{task}_{ident_name}")) } fn link_section_index() -> usize { @@ -153,7 +153,7 @@ pub fn local_resources_ident(ctxt: Context, app: &App) -> Ident { /// Generates an identifier for a ready queue, async task version pub fn rq_async_ident(async_task_name: &Ident) -> Ident { - mark_internal_name(&format!("ASYNC_TASK_{}_RQ", async_task_name)) + mark_internal_name(&format!("ASYNC_TASK_{async_task_name}_RQ")) } /// Suffixed identifier @@ -163,7 +163,7 @@ pub fn suffixed(name: &str) -> Ident { } pub fn static_shared_resource_ident(name: &Ident) -> Ident { - mark_internal_name(&format!("shared_resource_{}", name)) + mark_internal_name(&format!("shared_resource_{name}")) } /// Generates an Ident for the number of 32 bit chunks used for Mask storage. @@ -176,15 +176,15 @@ pub fn priority_masks_ident() -> Ident { } pub fn static_local_resource_ident(name: &Ident) -> Ident { - mark_internal_name(&format!("local_resource_{}", name)) + 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)) + mark_internal_name(&format!("local_{task_name}_{name}")) } pub fn need_to_lock_ident(name: &Ident) -> Ident { - Ident::new(&format!("{}_that_needs_to_be_locked", name), name.span()) + Ident::new(&format!("{name}_that_needs_to_be_locked"), name.span()) } pub fn zero_prio_dispatcher_ident() -> Ident { diff --git a/macros/src/lib.rs b/macros/src/lib.rs index 34f2bb6..a8422d0 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -39,9 +39,8 @@ pub fn app(args: TokenStream, input: TokenStream) -> TokenStream { Ok(x) => x, }; - match check::app(&app) { - Err(e) => return e.to_compile_error().into(), - _ => {} + if let Err(e) = check::app(&app) { + return e.to_compile_error().into(); } let analysis = analyze::app(analysis, &app); @@ -86,7 +85,7 @@ pub fn app(args: TokenStream, input: TokenStream) -> TokenStream { // Try to write the expanded code to disk if let Some(out_str) = out_dir.to_str() { - fs::write(format!("{}/rtic-expansion.rs", out_str), ts.to_string()).ok(); + fs::write(format!("{out_str}/rtic-expansion.rs"), ts.to_string()).ok(); } ts.into() diff --git a/macros/src/syntax/parse/app.rs b/macros/src/syntax/parse/app.rs index 8a9242e..e797f75 100644 --- a/macros/src/syntax/parse/app.rs +++ b/macros/src/syntax/parse/app.rs @@ -450,8 +450,7 @@ impl App { 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 + "This name and the one defined on `#[shared]` are not the same. Should this be `{shared_resources_ident}`?" ), )); } @@ -460,8 +459,7 @@ impl App { 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 + "This name and the one defined on `#[local]` are not the same. Should this be `{local_resources_ident}`?" ), )); } diff --git a/macros/src/syntax/parse/hardware_task.rs b/macros/src/syntax/parse/hardware_task.rs index ff94bc5..6207e56 100644 --- a/macros/src/syntax/parse/hardware_task.rs +++ b/macros/src/syntax/parse/hardware_task.rs @@ -39,9 +39,8 @@ impl HardwareTask { Err(parse::Error::new( span, - &format!( - "this task handler must have type signature `fn({}::Context)`", - name + format!( + "this task handler must have type signature `fn({name}::Context)`" ), )) } @@ -83,9 +82,8 @@ impl HardwareTask { Err(parse::Error::new( span, - &format!( - "this task handler must have type signature `fn({}::Context)`", - name + 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 index ffec358..aa2ef5e 100644 --- a/macros/src/syntax/parse/idle.rs +++ b/macros/src/syntax/parse/idle.rs @@ -34,9 +34,8 @@ impl Idle { Err(parse::Error::new( item.sig.ident.span(), - &format!( - "this `#[idle]` function must have signature `fn({}::Context) -> !`", - name + 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 index 61d3539..23130c8 100644 --- a/macros/src/syntax/parse/init.rs +++ b/macros/src/syntax/parse/init.rs @@ -41,9 +41,8 @@ impl Init { Err(parse::Error::new( span, - &format!( - "the `#[init]` function must have signature `fn({}::Context) -> (Shared resources struct, Local resources struct)`", - name + format!( + "the `#[init]` function must have signature `fn({name}::Context) -> (Shared resources struct, Local resources struct)`" ), )) } diff --git a/macros/src/syntax/parse/software_task.rs b/macros/src/syntax/parse/software_task.rs index 6be597e..319620a 100644 --- a/macros/src/syntax/parse/software_task.rs +++ b/macros/src/syntax/parse/software_task.rs @@ -33,9 +33,8 @@ impl SoftwareTask { Err(parse::Error::new( span, - &format!( - "this task handler must have type signature `async fn({}::Context)`", - name + format!( + "this task handler must have type signature `async fn({name}::Context)`" ), )) } @@ -71,9 +70,8 @@ impl SoftwareTask { Err(parse::Error::new( span, - &format!( - "this task handler must have type signature `async fn({}::Context)`", - name + 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 index 28c3eac..900ef9d 100644 --- a/macros/src/syntax/parse/util.rs +++ b/macros/src/syntax/parse/util.rs @@ -234,17 +234,13 @@ pub fn parse_local_resources(content: ParseStream<'_>) -> parse::Result, name: &str) -> Option> { let mut inputs = inputs.into_iter(); - match inputs.next() { - Some(FnArg::Typed(first)) => { - if type_is_path(&first.ty, &[name, "Context"]) { - // No more inputs - if inputs.next().is_none() { - return Some(first.pat); - } + if let Some(FnArg::Typed(first)) = inputs.next() { + if type_is_path(&first.ty, &[name, "Context"]) { + // No more inputs + if inputs.next().is_none() { + return Some(first.pat); } } - - _ => {} } None -- cgit v1.2.3 From 95e494968053a17ac05a0c1cec9d8b2c7d450296 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Sun, 8 Jan 2023 21:33:44 +0100 Subject: Start CI, disable docs building --- macros/src/codegen/async_dispatchers.rs | 4 +--- macros/src/syntax/parse/hardware_task.rs | 8 ++------ macros/src/syntax/parse/idle.rs | 4 +--- macros/src/syntax/parse/software_task.rs | 8 ++------ 4 files changed, 6 insertions(+), 18 deletions(-) (limited to 'macros') diff --git a/macros/src/codegen/async_dispatchers.rs b/macros/src/codegen/async_dispatchers.rs index be02ad0..341f76f 100644 --- a/macros/src/codegen/async_dispatchers.rs +++ b/macros/src/codegen/async_dispatchers.rs @@ -84,9 +84,7 @@ pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 { } if level > 0 { - let doc = format!( - "Interrupt handler to dispatch async tasks at priority {level}" - ); + 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)] diff --git a/macros/src/syntax/parse/hardware_task.rs b/macros/src/syntax/parse/hardware_task.rs index 6207e56..c13426f 100644 --- a/macros/src/syntax/parse/hardware_task.rs +++ b/macros/src/syntax/parse/hardware_task.rs @@ -39,9 +39,7 @@ impl HardwareTask { Err(parse::Error::new( span, - format!( - "this task handler must have type signature `fn({name}::Context)`" - ), + format!("this task handler must have type signature `fn({name}::Context)`"), )) } } @@ -82,9 +80,7 @@ impl HardwareTask { Err(parse::Error::new( span, - format!( - "this task handler must have type signature `fn({name}::Context)`" - ), + 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 index aa2ef5e..f049cca 100644 --- a/macros/src/syntax/parse/idle.rs +++ b/macros/src/syntax/parse/idle.rs @@ -34,9 +34,7 @@ impl Idle { Err(parse::Error::new( item.sig.ident.span(), - format!( - "this `#[idle]` function must have signature `fn({name}::Context) -> !`" - ), + format!("this `#[idle]` function must have signature `fn({name}::Context) -> !`"), )) } } diff --git a/macros/src/syntax/parse/software_task.rs b/macros/src/syntax/parse/software_task.rs index 319620a..fb9b37c 100644 --- a/macros/src/syntax/parse/software_task.rs +++ b/macros/src/syntax/parse/software_task.rs @@ -33,9 +33,7 @@ impl SoftwareTask { Err(parse::Error::new( span, - format!( - "this task handler must have type signature `async fn({name}::Context)`" - ), + format!("this task handler must have type signature `async fn({name}::Context)`"), )) } } @@ -70,9 +68,7 @@ impl SoftwareTask { Err(parse::Error::new( span, - format!( - "this task handler must have type signature `async fn({name}::Context)`" - ), + format!("this task handler must have type signature `async fn({name}::Context)`"), )) } } -- cgit v1.2.3 From cd790a94286cdc307d399b7f7a43e305e90de5bf Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Mon, 9 Jan 2023 21:02:53 +0100 Subject: More work on new spawn/executor --- macros/src/codegen/async_dispatchers.rs | 50 +++++---------------------------- macros/src/codegen/module.rs | 29 ++++++++++--------- macros/src/codegen/software_tasks.rs | 14 +-------- macros/src/codegen/util.rs | 10 ------- 4 files changed, 23 insertions(+), 80 deletions(-) (limited to 'macros') diff --git a/macros/src/codegen/async_dispatchers.rs b/macros/src/codegen/async_dispatchers.rs index 341f76f..012bd61 100644 --- a/macros/src/codegen/async_dispatchers.rs +++ b/macros/src/codegen/async_dispatchers.rs @@ -16,11 +16,10 @@ pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 { items.push(quote!( #[allow(non_camel_case_types)] - type #type_name = impl core::future::Future + 'static; + type #type_name = impl core::future::Future; #[allow(non_upper_case_globals)] - static #exec_name: - rtic::RacyCell> = - rtic::RacyCell::new(rtic::export::executor::AsyncTaskExecutor::new()); + static #exec_name: rtic::export::executor::AsyncTaskExecutor<#type_name> = + rtic::export::executor::AsyncTaskExecutor::new(); )); } @@ -47,38 +46,13 @@ pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 { let exec_name = util::internal_task_ident(name, "EXEC"); // let task = &app.software_tasks[name]; // let cfgs = &task.cfgs; - let executor_run_ident = util::executor_run_ident(name); - - let rq = util::rq_async_ident(name); - - items.push(quote!( - #[doc(hidden)] - #[allow(non_camel_case_types)] - #[allow(non_upper_case_globals)] - static #rq: core::sync::atomic::AtomicBool = core::sync::atomic::AtomicBool::new(false); - )); stmts.push(quote!( - if !(&*#exec_name.get()).is_running() { - // TODO Fix this to be compare and swap - if #rq.load(core::sync::atomic::Ordering::Relaxed) { - #rq.store(false, core::sync::atomic::Ordering::Relaxed); - - (&mut *#exec_name.get_mut()).spawn(#name(#name::Context::new())); - #executor_run_ident.store(true, core::sync::atomic::Ordering::Relaxed); - } - } - - if #executor_run_ident.load(core::sync::atomic::Ordering::Relaxed) { - #executor_run_ident.store(false, core::sync::atomic::Ordering::Relaxed); - if (&mut *#exec_name.get_mut()).poll(|| { - #executor_run_ident.store(true, core::sync::atomic::Ordering::Release); + if #exec_name.check_and_clear_pending() { + #exec_name.poll(|| { + #exec_name.set_pending(); #pend_interrupt - }) && #rq.load(core::sync::atomic::Ordering::Relaxed) { - // If the ready queue is not empty and the executor finished, restart this - // dispatch to check if the executor should be restarted. - #pend_interrupt - } + }); } )); } @@ -96,12 +70,7 @@ pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 { const PRIORITY: u8 = #level; rtic::export::run(PRIORITY, || { - // Have the acquire/release semantics outside the checks to no overdo it - core::sync::atomic::fence(core::sync::atomic::Ordering::Acquire); - #(#stmts)* - - core::sync::atomic::fence(core::sync::atomic::Ordering::Release); }); } )); @@ -110,12 +79,7 @@ pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 { #[allow(non_snake_case)] unsafe fn #dispatcher_name() -> ! { loop { - // Have the acquire/release semantics outside the checks to no overdo it - core::sync::atomic::fence(core::sync::atomic::Ordering::Acquire); - #(#stmts)* - - core::sync::atomic::fence(core::sync::atomic::Ordering::Release); } } )); diff --git a/macros/src/codegen/module.rs b/macros/src/codegen/module.rs index 70fbb5e..19cf241 100644 --- a/macros/src/codegen/module.rs +++ b/macros/src/codegen/module.rs @@ -98,6 +98,7 @@ pub fn codegen(ctxt: Context, app: &App, analysis: &Analysis) -> TokenStream2 { }; let internal_context_name = util::internal_task_ident(name, "Context"); + let exec_name = util::internal_task_ident(name, "EXEC"); items.push(quote!( #(#cfgs)* @@ -147,25 +148,25 @@ pub fn codegen(ctxt: Context, app: &App, analysis: &Analysis) -> TokenStream2 { let internal_spawn_ident = util::internal_task_ident(name, "spawn"); // Spawn caller - let rq = util::rq_async_ident(name); items.push(quote!( - - #(#cfgs)* - /// Spawns the task directly - #[allow(non_snake_case)] - #[doc(hidden)] - pub fn #internal_spawn_ident() -> Result<(), ()> { - unsafe { - // TODO: Fix this to be compare and swap - if #rq.load(core::sync::atomic::Ordering::Acquire) { - Err(()) - } else { - #rq.store(true, core::sync::atomic::Ordering::Release); + #(#cfgs)* + /// Spawns the task directly + #[allow(non_snake_case)] + #[doc(hidden)] + pub fn #internal_spawn_ident() -> Result<(), ()> { + if #exec_name.try_reserve() { + unsafe { + // TODO: Add args here + #exec_name.spawn_unchecked(#name(#name::Context::new())); + } #pend_interrupt + Ok(()) + } else { + Err(()) } } - })); + )); module_items.push(quote!( #(#cfgs)* diff --git a/macros/src/codegen/software_tasks.rs b/macros/src/codegen/software_tasks.rs index 4cb1fa9..b923283 100644 --- a/macros/src/codegen/software_tasks.rs +++ b/macros/src/codegen/software_tasks.rs @@ -1,7 +1,7 @@ use crate::syntax::{ast::App, Context}; use crate::{ analyze::Analysis, - codegen::{local_resources_struct, module, shared_resources_struct, util}, + codegen::{local_resources_struct, module, shared_resources_struct}, }; use proc_macro2::TokenStream as TokenStream2; use quote::quote; @@ -13,18 +13,6 @@ pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 { // Any task for (name, task) in app.software_tasks.iter() { - let executor_ident = util::executor_run_ident(name); - mod_app.push(quote!( - #[allow(non_camel_case_types)] - #[allow(non_upper_case_globals)] - #[doc(hidden)] - static #executor_ident: core::sync::atomic::AtomicBool = - core::sync::atomic::AtomicBool::new(false); - )); - - // `${task}Resources` - - // `${task}Locals` if !task.args.local_resources.is_empty() { let (item, constructor) = local_resources_struct::codegen(Context::SoftwareTask(name), app); diff --git a/macros/src/codegen/util.rs b/macros/src/codegen/util.rs index a0caf0a..0558d9d 100644 --- a/macros/src/codegen/util.rs +++ b/macros/src/codegen/util.rs @@ -49,11 +49,6 @@ pub fn impl_mutex( ) } -/// Generates an identifier for the `EXECUTOR_RUN` atomics (`async` API) -pub fn executor_run_ident(task: &Ident) -> Ident { - mark_internal_name(&format!("{task}_EXECUTOR_RUN")) -} - pub fn interrupt_ident() -> Ident { let span = Span::call_site(); Ident::new("interrupt", span) @@ -151,11 +146,6 @@ pub fn local_resources_ident(ctxt: Context, app: &App) -> Ident { mark_internal_name(&s) } -/// Generates an identifier for a ready queue, async task version -pub fn rq_async_ident(async_task_name: &Ident) -> Ident { - mark_internal_name(&format!("ASYNC_TASK_{async_task_name}_RQ")) -} - /// Suffixed identifier pub fn suffixed(name: &str) -> Ident { let span = Span::call_site(); -- cgit v1.2.3 From d6d58b0eb88242cf63724e1420bd29f8a4489916 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Tue, 10 Jan 2023 21:03:10 +0100 Subject: Async tasks can now take arguments at spawn again --- macros/src/codegen/module.rs | 11 +++--- macros/src/codegen/software_tasks.rs | 3 +- macros/src/codegen/util.rs | 54 +++++++++++++++++++++++++++-- macros/src/syntax/analyze.rs | 5 +++ macros/src/syntax/ast.rs | 5 ++- macros/src/syntax/parse/hardware_task.rs | 58 +++++++++++++------------------- macros/src/syntax/parse/idle.rs | 18 +++++----- macros/src/syntax/parse/init.rs | 22 ++++++------ macros/src/syntax/parse/software_task.rs | 10 +++--- macros/src/syntax/parse/util.rs | 30 +++++++++++------ macros/ui/task-divergent.stderr | 2 +- macros/ui/task-no-context.stderr | 2 +- macros/ui/task-pub.stderr | 2 +- macros/ui/task-unsafe.stderr | 2 +- macros/ui/task-zero-prio.stderr | 2 +- 15 files changed, 146 insertions(+), 80 deletions(-) (limited to 'macros') diff --git a/macros/src/codegen/module.rs b/macros/src/codegen/module.rs index 19cf241..666bd04 100644 --- a/macros/src/codegen/module.rs +++ b/macros/src/codegen/module.rs @@ -146,6 +146,8 @@ pub fn codegen(ctxt: Context, app: &App, analysis: &Analysis) -> TokenStream2 { }; 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!( @@ -153,17 +155,18 @@ pub fn codegen(ctxt: Context, app: &App, analysis: &Analysis) -> TokenStream2 { /// Spawns the task directly #[allow(non_snake_case)] #[doc(hidden)] - pub fn #internal_spawn_ident() -> Result<(), ()> { + pub fn #internal_spawn_ident(#(#input_args,)*) -> Result<(), #input_ty> { if #exec_name.try_reserve() { + // This unsafe is protected by `try_reserve`, see its documentation for details unsafe { - // TODO: Add args here - #exec_name.spawn_unchecked(#name(#name::Context::new())); + #exec_name.spawn_unchecked(#name(#name::Context::new() #(,#input_untupled)*)); } + #pend_interrupt Ok(()) } else { - Err(()) + Err(#input_tupled) } } )); diff --git a/macros/src/codegen/software_tasks.rs b/macros/src/codegen/software_tasks.rs index b923283..34fc851 100644 --- a/macros/src/codegen/software_tasks.rs +++ b/macros/src/codegen/software_tasks.rs @@ -36,12 +36,13 @@ pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 { 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(#context: #name::Context<'static>) { + async fn #name<'a>(#context: #name::Context<'a> #(,#inputs)*) { use rtic::Mutex as _; use rtic::mutex::prelude::*; diff --git a/macros/src/codegen/util.rs b/macros/src/codegen/util.rs index 0558d9d..e121487 100644 --- a/macros/src/codegen/util.rs +++ b/macros/src/codegen/util.rs @@ -1,9 +1,8 @@ -use core::sync::atomic::{AtomicUsize, Ordering}; - 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}; +use syn::{Attribute, Ident, PatType}; const RTIC_INTERNAL: &str = "__rtic_internal"; @@ -94,6 +93,55 @@ pub fn link_section_uninit() -> TokenStream2 { quote!(#[link_section = #section]) } +/// Regroups the inputs of a task +/// +/// `inputs` could be &[`input: Foo`] OR &[`mut x: i32`, `ref y: i64`] +pub fn regroup_inputs( + inputs: &[PatType], +) -> ( + // args e.g. &[`_0`], &[`_0: i32`, `_1: i64`] + Vec, + // tupled e.g. `_0`, `(_0, _1)` + TokenStream2, + // untupled e.g. &[`_0`], &[`_0`, `_1`] + Vec, + // ty e.g. `Foo`, `(i32, i64)` + TokenStream2, +) { + if inputs.len() == 1 { + let ty = &inputs[0].ty; + + ( + vec![quote!(_0: #ty)], + quote!(_0), + vec![quote!(_0)], + quote!(#ty), + ) + } else { + let mut args = vec![]; + let mut pats = vec![]; + let mut tys = vec![]; + + for (i, input) in inputs.iter().enumerate() { + let i = Ident::new(&format!("_{}", i), Span::call_site()); + let ty = &input.ty; + + args.push(quote!(#i: #ty)); + + pats.push(quote!(#i)); + + tys.push(quote!(#ty)); + } + + let tupled = { + let pats = pats.clone(); + quote!((#(#pats,)*)) + }; + let ty = quote!((#(#tys,)*)); + (args, tupled, pats, ty) + } +} + /// Get the ident for the name of the task pub fn get_task_name(ctxt: Context, app: &App) -> Ident { let s = match ctxt { diff --git a/macros/src/syntax/analyze.rs b/macros/src/syntax/analyze.rs index b70ceb8..3ed1487 100644 --- a/macros/src/syntax/analyze.rs +++ b/macros/src/syntax/analyze.rs @@ -287,6 +287,11 @@ pub(crate) fn app(app: &App) -> Result { 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 diff --git a/macros/src/syntax/ast.rs b/macros/src/syntax/ast.rs index da6016a..27e6773 100644 --- a/macros/src/syntax/ast.rs +++ b/macros/src/syntax/ast.rs @@ -1,6 +1,6 @@ //! Abstract Syntax Tree -use syn::{Attribute, Expr, Ident, Item, ItemUse, Pat, Path, Stmt, Type}; +use syn::{Attribute, Expr, Ident, Item, ItemUse, Pat, PatType, Path, Stmt, Type}; use crate::syntax::Map; @@ -205,6 +205,9 @@ pub struct SoftwareTask { /// The context argument pub context: Box, + /// The inputs of this software task + pub inputs: Vec, + /// The statements that make up the task handler pub stmts: Vec, diff --git a/macros/src/syntax/parse/hardware_task.rs b/macros/src/syntax/parse/hardware_task.rs index c13426f..7f6dfbe 100644 --- a/macros/src/syntax/parse/hardware_task.rs +++ b/macros/src/syntax/parse/hardware_task.rs @@ -15,25 +15,20 @@ impl HardwareTask { let name = item.sig.ident.to_string(); - if name == "init" || name == "idle" { - return Err(parse::Error::new( - span, - "tasks cannot be named `init` or `idle`", - )); - } - if valid_signature { - if let Some(context) = util::parse_inputs(item.sig.inputs, &name) { - let FilterAttrs { cfgs, attrs, .. } = util::filter_attributes(item.attrs); + 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, - }); + return Ok(HardwareTask { + args, + cfgs, + attrs, + context, + stmts: item.block.stmts, + is_extern: false, + }); + } } } @@ -56,25 +51,20 @@ impl HardwareTask { let name = item.sig.ident.to_string(); - if name == "init" || name == "idle" { - return Err(parse::Error::new( - span, - "tasks cannot be named `init` or `idle`", - )); - } - if valid_signature { - if let Some(context) = util::parse_inputs(item.sig.inputs, &name) { - let FilterAttrs { cfgs, attrs, .. } = util::filter_attributes(item.attrs); + 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::::new(), - is_extern: true, - }); + return Ok(HardwareTask { + args, + cfgs, + attrs, + context, + stmts: Vec::::new(), + is_extern: true, + }); + } } } diff --git a/macros/src/syntax/parse/idle.rs b/macros/src/syntax/parse/idle.rs index f049cca..124c136 100644 --- a/macros/src/syntax/parse/idle.rs +++ b/macros/src/syntax/parse/idle.rs @@ -21,14 +21,16 @@ impl Idle { let name = item.sig.ident.to_string(); if valid_signature { - if let Some(context) = util::parse_inputs(item.sig.inputs, &name) { - return Ok(Idle { - args, - attrs: item.attrs, - context, - name: item.sig.ident, - stmts: item.block.stmts, - }); + 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, + }); + } } } diff --git a/macros/src/syntax/parse/init.rs b/macros/src/syntax/parse/init.rs index 23130c8..0aea20b 100644 --- a/macros/src/syntax/parse/init.rs +++ b/macros/src/syntax/parse/init.rs @@ -25,16 +25,18 @@ impl Init { if let Ok((user_shared_struct, user_local_struct)) = util::type_is_init_return(&item.sig.output) { - if let Some(context) = util::parse_inputs(item.sig.inputs, &name) { - return Ok(Init { - args, - attrs: item.attrs, - context, - name: item.sig.ident, - stmts: item.block.stmts, - user_shared_struct, - user_local_struct, - }); + 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, + }); + } } } } diff --git a/macros/src/syntax/parse/software_task.rs b/macros/src/syntax/parse/software_task.rs index fb9b37c..769aa65 100644 --- a/macros/src/syntax/parse/software_task.rs +++ b/macros/src/syntax/parse/software_task.rs @@ -17,7 +17,7 @@ impl SoftwareTask { let name = item.sig.ident.to_string(); if valid_signature { - if let Some(context) = util::parse_inputs(item.sig.inputs, &name) { + 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 { @@ -25,6 +25,7 @@ impl SoftwareTask { attrs, cfgs, context, + inputs, stmts: item.block.stmts, is_extern: false, }); @@ -33,7 +34,7 @@ impl SoftwareTask { Err(parse::Error::new( span, - format!("this task handler must have type signature `async fn({name}::Context)`"), + format!("this task handler must have type signature `async fn({name}::Context, ..)`"), )) } } @@ -52,7 +53,7 @@ impl SoftwareTask { let name = item.sig.ident.to_string(); if valid_signature { - if let Some(context) = util::parse_inputs(item.sig.inputs, &name) { + 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 { @@ -60,6 +61,7 @@ impl SoftwareTask { attrs, cfgs, context, + inputs, stmts: Vec::::new(), is_extern: true, }); @@ -68,7 +70,7 @@ impl SoftwareTask { Err(parse::Error::new( span, - format!("this task handler must have type signature `async fn({name}::Context)`"), + 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 index 900ef9d..5a5e0c0 100644 --- a/macros/src/syntax/parse/util.rs +++ b/macros/src/syntax/parse/util.rs @@ -3,8 +3,8 @@ use syn::{ parse::{self, ParseStream}, punctuated::Punctuated, spanned::Spanned, - Abi, AttrStyle, Attribute, Expr, FnArg, ForeignItemFn, Ident, ItemFn, Pat, Path, PathArguments, - ReturnType, Token, Type, Visibility, + Abi, AttrStyle, Attribute, Expr, FnArg, ForeignItemFn, Ident, ItemFn, Pat, PatType, Path, + PathArguments, ReturnType, Token, Type, Visibility, }; use crate::syntax::{ @@ -231,19 +231,29 @@ pub fn parse_local_resources(content: ParseStream<'_>) -> parse::Result, name: &str) -> Option> { +type ParseInputResult = Option<(Box, Result, FnArg>)>; + +pub fn parse_inputs(inputs: Punctuated, name: &str) -> ParseInputResult { let mut inputs = inputs.into_iter(); - if let Some(FnArg::Typed(first)) = inputs.next() { - if type_is_path(&first.ty, &[name, "Context"]) { - // No more inputs - if inputs.next().is_none() { - return Some(first.pat); + match inputs.next() { + Some(FnArg::Typed(first)) => { + if type_is_path(&first.ty, &[name, "Context"]) { + let rest = inputs + .map(|arg| match arg { + FnArg::Typed(arg) => Ok(arg), + _ => Err(arg), + }) + .collect::, _>>(); + + Some((first.pat, rest)) + } else { + None } } - } - None + _ => None, + } } pub fn type_is_bottom(ty: &ReturnType) -> bool { diff --git a/macros/ui/task-divergent.stderr b/macros/ui/task-divergent.stderr index bd22bd3..dd00208 100644 --- a/macros/ui/task-divergent.stderr +++ b/macros/ui/task-divergent.stderr @@ -1,4 +1,4 @@ -error: this task handler must have type signature `async fn(foo::Context)` +error: this task handler must have type signature `async fn(foo::Context, ..)` --> ui/task-divergent.rs:6:14 | 6 | async fn foo(_: foo::Context) -> ! { diff --git a/macros/ui/task-no-context.stderr b/macros/ui/task-no-context.stderr index 91239a1..62147aa 100644 --- a/macros/ui/task-no-context.stderr +++ b/macros/ui/task-no-context.stderr @@ -1,4 +1,4 @@ -error: this task handler must have type signature `async fn(foo::Context)` +error: this task handler must have type signature `async fn(foo::Context, ..)` --> ui/task-no-context.rs:6:14 | 6 | async fn foo() {} diff --git a/macros/ui/task-pub.stderr b/macros/ui/task-pub.stderr index 72c4e63..7b9813d 100644 --- a/macros/ui/task-pub.stderr +++ b/macros/ui/task-pub.stderr @@ -1,4 +1,4 @@ -error: this task handler must have type signature `async fn(foo::Context)` +error: this task handler must have type signature `async fn(foo::Context, ..)` --> ui/task-pub.rs:6:18 | 6 | pub async fn foo(_: foo::Context) {} diff --git a/macros/ui/task-unsafe.stderr b/macros/ui/task-unsafe.stderr index 4908481..90ac76f 100644 --- a/macros/ui/task-unsafe.stderr +++ b/macros/ui/task-unsafe.stderr @@ -1,4 +1,4 @@ -error: this task handler must have type signature `async fn(foo::Context)` +error: this task handler must have type signature `async fn(foo::Context, ..)` --> ui/task-unsafe.rs:6:21 | 6 | async unsafe fn foo(_: foo::Context) {} diff --git a/macros/ui/task-zero-prio.stderr b/macros/ui/task-zero-prio.stderr index f2d8223..1ab9aab 100644 --- a/macros/ui/task-zero-prio.stderr +++ b/macros/ui/task-zero-prio.stderr @@ -1,4 +1,4 @@ -error: this task handler must have type signature `async fn(foo::Context)` +error: this task handler must have type signature `async fn(foo::Context, ..)` --> ui/task-zero-prio.rs:15:8 | 15 | fn foo(_: foo::Context) {} -- cgit v1.2.3 From 5688a5d332cdaffaca64ade5b138a3676ac7cd32 Mon Sep 17 00:00:00 2001 From: Per Lindgren Date: Thu, 12 Jan 2023 08:50:12 +0100 Subject: executor update for less unsafe and more clear --- macros/src/codegen/async_dispatchers.rs | 11 +++++------ macros/src/codegen/module.rs | 8 +++----- 2 files changed, 8 insertions(+), 11 deletions(-) (limited to 'macros') diff --git a/macros/src/codegen/async_dispatchers.rs b/macros/src/codegen/async_dispatchers.rs index 012bd61..a12ad32 100644 --- a/macros/src/codegen/async_dispatchers.rs +++ b/macros/src/codegen/async_dispatchers.rs @@ -44,16 +44,15 @@ pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 { 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!( - if #exec_name.check_and_clear_pending() { - #exec_name.poll(|| { - #exec_name.set_pending(); - #pend_interrupt - }); - } + #exec_name.poll(|| { + #exec_name.set_pending(); + #pend_interrupt + }); )); } diff --git a/macros/src/codegen/module.rs b/macros/src/codegen/module.rs index 666bd04..f4c188a 100644 --- a/macros/src/codegen/module.rs +++ b/macros/src/codegen/module.rs @@ -156,11 +156,8 @@ pub fn codegen(ctxt: Context, app: &App, analysis: &Analysis) -> TokenStream2 { #[allow(non_snake_case)] #[doc(hidden)] pub fn #internal_spawn_ident(#(#input_args,)*) -> Result<(), #input_ty> { - if #exec_name.try_reserve() { - // This unsafe is protected by `try_reserve`, see its documentation for details - unsafe { - #exec_name.spawn_unchecked(#name(#name::Context::new() #(,#input_untupled)*)); - } + + if #exec_name.spawn(|| #name(unsafe { #name::Context::new() } #(,#input_untupled)*) ) { #pend_interrupt @@ -168,6 +165,7 @@ pub fn codegen(ctxt: Context, app: &App, analysis: &Analysis) -> TokenStream2 { } else { Err(#input_tupled) } + } )); -- cgit v1.2.3 From b8b881f446a226d6f3c4a7db7c9174590b47dbf6 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Thu, 19 Jan 2023 13:56:59 +0100 Subject: Fix so deny(missing_docs) work --- macros/src/codegen/local_resources_struct.rs | 2 ++ macros/src/codegen/module.rs | 1 + macros/src/codegen/shared_resources_struct.rs | 4 ++++ 3 files changed, 7 insertions(+) (limited to 'macros') diff --git a/macros/src/codegen/local_resources_struct.rs b/macros/src/codegen/local_resources_struct.rs index e268508..100c3eb 100644 --- a/macros/src/codegen/local_resources_struct.rs +++ b/macros/src/codegen/local_resources_struct.rs @@ -50,6 +50,7 @@ pub fn codegen(ctxt: Context, app: &App) -> (TokenStream2, TokenStream2) { fields.push(quote!( #(#cfgs)* + #[allow(missing_docs)] pub #name: &#lt mut #ty )); @@ -88,6 +89,7 @@ pub fn codegen(ctxt: Context, app: &App) -> (TokenStream2, TokenStream2) { let constructor = quote!( impl<'a> #ident<'a> { #[inline(always)] + #[allow(missing_docs)] pub unsafe fn new() -> Self { #ident { #(#values,)* diff --git a/macros/src/codegen/module.rs b/macros/src/codegen/module.rs index f4c188a..4725b9a 100644 --- a/macros/src/codegen/module.rs +++ b/macros/src/codegen/module.rs @@ -114,6 +114,7 @@ pub fn codegen(ctxt: Context, app: &App, analysis: &Analysis) -> TokenStream2 { #(#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, diff --git a/macros/src/codegen/shared_resources_struct.rs b/macros/src/codegen/shared_resources_struct.rs index 24c93de..fa6f0fc 100644 --- a/macros/src/codegen/shared_resources_struct.rs +++ b/macros/src/codegen/shared_resources_struct.rs @@ -47,16 +47,19 @@ pub fn codegen(ctxt: Context, app: &App) -> (TokenStream2, TokenStream2) { 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> )); @@ -103,6 +106,7 @@ pub fn codegen(ctxt: Context, app: &App) -> (TokenStream2, TokenStream2) { let constructor = quote!( impl<'a> #ident<'a> { #[inline(always)] + #[allow(missing_docs)] pub unsafe fn new() -> Self { #ident { #(#values,)* -- cgit v1.2.3 From 306aa47170fd59369b7a184924e287dc3706d64d Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Mon, 23 Jan 2023 20:05:47 +0100 Subject: Add rtic-timer (timerqueue + monotonic) and rtic-monotonics (systick-monotonic) --- macros/.gitignore | 2 - macros/Cargo.toml | 41 -- macros/src/analyze.rs | 49 --- macros/src/bindings.rs | 1 - macros/src/check.rs | 70 --- macros/src/codegen.rs | 75 ---- macros/src/codegen/assertions.rs | 53 --- macros/src/codegen/async_dispatchers.rs | 89 ---- macros/src/codegen/hardware_tasks.rs | 87 ---- macros/src/codegen/idle.rs | 58 --- macros/src/codegen/init.rs | 95 ---- macros/src/codegen/local_resources.rs | 65 --- macros/src/codegen/local_resources_struct.rs | 102 ----- macros/src/codegen/main.rs | 52 --- macros/src/codegen/module.rs | 194 --------- macros/src/codegen/post_init.rs | 47 -- macros/src/codegen/pre_init.rs | 85 ---- macros/src/codegen/shared_resources.rs | 183 -------- macros/src/codegen/shared_resources_struct.rs | 119 ----- macros/src/codegen/software_tasks.rs | 64 --- macros/src/codegen/util.rs | 238 ---------- macros/src/lib.rs | 92 ---- macros/src/syntax.rs | 121 ------ macros/src/syntax/.github/bors.toml | 3 - macros/src/syntax/.github/workflows/build.yml | 213 --------- macros/src/syntax/.github/workflows/changelog.yml | 28 -- .../workflows/properties/build.properties.json | 6 - macros/src/syntax/.gitignore | 4 - macros/src/syntax/.travis.yml | 31 -- macros/src/syntax/accessors.rs | 113 ----- macros/src/syntax/analyze.rs | 417 ------------------ macros/src/syntax/ast.rs | 335 -------------- macros/src/syntax/check.rs | 66 --- macros/src/syntax/optimize.rs | 36 -- macros/src/syntax/parse.rs | 363 ---------------- macros/src/syntax/parse/app.rs | 480 --------------------- macros/src/syntax/parse/hardware_task.rs | 76 ---- macros/src/syntax/parse/idle.rs | 42 -- macros/src/syntax/parse/init.rs | 51 --- macros/src/syntax/parse/resource.rs | 55 --- macros/src/syntax/parse/software_task.rs | 76 ---- macros/src/syntax/parse/util.rs | 338 --------------- macros/src/tests.rs | 4 - macros/src/tests/single.rs | 40 -- macros/tests/ui.rs | 7 - macros/ui/extern-interrupt-used.rs | 16 - macros/ui/extern-interrupt-used.stderr | 5 - macros/ui/idle-double-local.rs | 9 - macros/ui/idle-double-local.stderr | 5 - macros/ui/idle-double-shared.rs | 9 - macros/ui/idle-double-shared.stderr | 5 - macros/ui/idle-input.rs | 9 - macros/ui/idle-input.stderr | 5 - macros/ui/idle-no-context.rs | 9 - macros/ui/idle-no-context.stderr | 5 - macros/ui/idle-not-divergent.rs | 7 - macros/ui/idle-not-divergent.stderr | 5 - macros/ui/idle-output.rs | 9 - macros/ui/idle-output.stderr | 5 - macros/ui/idle-pub.rs | 9 - macros/ui/idle-pub.stderr | 5 - macros/ui/idle-unsafe.rs | 9 - macros/ui/idle-unsafe.stderr | 5 - macros/ui/init-divergent.rs | 13 - macros/ui/init-divergent.stderr | 5 - macros/ui/init-double-local.rs | 7 - macros/ui/init-double-local.stderr | 5 - macros/ui/init-double-shared.rs | 7 - macros/ui/init-double-shared.stderr | 5 - macros/ui/init-input.rs | 13 - macros/ui/init-input.stderr | 5 - macros/ui/init-no-context.rs | 13 - macros/ui/init-no-context.stderr | 5 - macros/ui/init-output.rs | 9 - macros/ui/init-output.stderr | 5 - macros/ui/init-pub.rs | 13 - macros/ui/init-pub.stderr | 5 - macros/ui/init-unsafe.rs | 7 - macros/ui/init-unsafe.stderr | 5 - macros/ui/interrupt-double.rs | 10 - macros/ui/interrupt-double.stderr | 5 - macros/ui/local-collision-2.rs | 18 - macros/ui/local-collision-2.stderr | 17 - macros/ui/local-collision.rs | 21 - macros/ui/local-collision.stderr | 11 - macros/ui/local-malformed-1.rs | 16 - macros/ui/local-malformed-1.stderr | 5 - macros/ui/local-malformed-2.rs | 16 - macros/ui/local-malformed-2.stderr | 5 - macros/ui/local-malformed-3.rs | 16 - macros/ui/local-malformed-3.stderr | 5 - macros/ui/local-malformed-4.rs | 16 - macros/ui/local-malformed-4.stderr | 5 - macros/ui/local-not-declared.rs | 16 - macros/ui/local-not-declared.stderr | 5 - macros/ui/local-pub.rs | 15 - macros/ui/local-pub.stderr | 5 - macros/ui/local-shared-attribute.rs | 21 - macros/ui/local-shared-attribute.stderr | 6 - macros/ui/local-shared.rs | 28 -- macros/ui/local-shared.stderr | 11 - macros/ui/shared-lock-free.rs | 38 -- macros/ui/shared-lock-free.stderr | 17 - macros/ui/shared-not-declared.rs | 16 - macros/ui/shared-not-declared.stderr | 5 - macros/ui/shared-pub.rs | 9 - macros/ui/shared-pub.stderr | 5 - macros/ui/task-divergent.rs | 9 - macros/ui/task-divergent.stderr | 5 - macros/ui/task-double-local.rs | 7 - macros/ui/task-double-local.stderr | 5 - macros/ui/task-double-priority.rs | 7 - macros/ui/task-double-priority.stderr | 5 - macros/ui/task-double-shared.rs | 7 - macros/ui/task-double-shared.stderr | 5 - macros/ui/task-idle.rs | 13 - macros/ui/task-idle.stderr | 5 - macros/ui/task-init.rs | 17 - macros/ui/task-init.stderr | 5 - macros/ui/task-interrupt.rs | 10 - macros/ui/task-interrupt.stderr | 5 - macros/ui/task-no-context.rs | 7 - macros/ui/task-no-context.stderr | 5 - macros/ui/task-priority-too-high.rs | 7 - macros/ui/task-priority-too-high.stderr | 5 - macros/ui/task-priority-too-low.rs | 7 - macros/ui/task-priority-too-low.stderr | 5 - macros/ui/task-pub.rs | 7 - macros/ui/task-pub.stderr | 5 - macros/ui/task-unsafe.rs | 7 - macros/ui/task-unsafe.stderr | 5 - macros/ui/task-zero-prio.rs | 19 - macros/ui/task-zero-prio.stderr | 5 - 133 files changed, 5571 deletions(-) delete mode 100644 macros/.gitignore delete mode 100644 macros/Cargo.toml delete mode 100644 macros/src/analyze.rs delete mode 100644 macros/src/bindings.rs delete mode 100644 macros/src/check.rs delete mode 100644 macros/src/codegen.rs delete mode 100644 macros/src/codegen/assertions.rs delete mode 100644 macros/src/codegen/async_dispatchers.rs delete mode 100644 macros/src/codegen/hardware_tasks.rs delete mode 100644 macros/src/codegen/idle.rs delete mode 100644 macros/src/codegen/init.rs delete mode 100644 macros/src/codegen/local_resources.rs delete mode 100644 macros/src/codegen/local_resources_struct.rs delete mode 100644 macros/src/codegen/main.rs delete mode 100644 macros/src/codegen/module.rs delete mode 100644 macros/src/codegen/post_init.rs delete mode 100644 macros/src/codegen/pre_init.rs delete mode 100644 macros/src/codegen/shared_resources.rs delete mode 100644 macros/src/codegen/shared_resources_struct.rs delete mode 100644 macros/src/codegen/software_tasks.rs delete mode 100644 macros/src/codegen/util.rs delete mode 100644 macros/src/lib.rs delete mode 100644 macros/src/syntax.rs delete mode 100644 macros/src/syntax/.github/bors.toml delete mode 100644 macros/src/syntax/.github/workflows/build.yml delete mode 100644 macros/src/syntax/.github/workflows/changelog.yml delete mode 100644 macros/src/syntax/.github/workflows/properties/build.properties.json delete mode 100644 macros/src/syntax/.gitignore delete mode 100644 macros/src/syntax/.travis.yml delete mode 100644 macros/src/syntax/accessors.rs delete mode 100644 macros/src/syntax/analyze.rs delete mode 100644 macros/src/syntax/ast.rs delete mode 100644 macros/src/syntax/check.rs delete mode 100644 macros/src/syntax/optimize.rs delete mode 100644 macros/src/syntax/parse.rs delete mode 100644 macros/src/syntax/parse/app.rs delete mode 100644 macros/src/syntax/parse/hardware_task.rs delete mode 100644 macros/src/syntax/parse/idle.rs delete mode 100644 macros/src/syntax/parse/init.rs delete mode 100644 macros/src/syntax/parse/resource.rs delete mode 100644 macros/src/syntax/parse/software_task.rs delete mode 100644 macros/src/syntax/parse/util.rs delete mode 100644 macros/src/tests.rs delete mode 100644 macros/src/tests/single.rs delete mode 100644 macros/tests/ui.rs delete mode 100644 macros/ui/extern-interrupt-used.rs delete mode 100644 macros/ui/extern-interrupt-used.stderr delete mode 100644 macros/ui/idle-double-local.rs delete mode 100644 macros/ui/idle-double-local.stderr delete mode 100644 macros/ui/idle-double-shared.rs delete mode 100644 macros/ui/idle-double-shared.stderr delete mode 100644 macros/ui/idle-input.rs delete mode 100644 macros/ui/idle-input.stderr delete mode 100644 macros/ui/idle-no-context.rs delete mode 100644 macros/ui/idle-no-context.stderr delete mode 100644 macros/ui/idle-not-divergent.rs delete mode 100644 macros/ui/idle-not-divergent.stderr delete mode 100644 macros/ui/idle-output.rs delete mode 100644 macros/ui/idle-output.stderr delete mode 100644 macros/ui/idle-pub.rs delete mode 100644 macros/ui/idle-pub.stderr delete mode 100644 macros/ui/idle-unsafe.rs delete mode 100644 macros/ui/idle-unsafe.stderr delete mode 100644 macros/ui/init-divergent.rs delete mode 100644 macros/ui/init-divergent.stderr delete mode 100644 macros/ui/init-double-local.rs delete mode 100644 macros/ui/init-double-local.stderr delete mode 100644 macros/ui/init-double-shared.rs delete mode 100644 macros/ui/init-double-shared.stderr delete mode 100644 macros/ui/init-input.rs delete mode 100644 macros/ui/init-input.stderr delete mode 100644 macros/ui/init-no-context.rs delete mode 100644 macros/ui/init-no-context.stderr delete mode 100644 macros/ui/init-output.rs delete mode 100644 macros/ui/init-output.stderr delete mode 100644 macros/ui/init-pub.rs delete mode 100644 macros/ui/init-pub.stderr delete mode 100644 macros/ui/init-unsafe.rs delete mode 100644 macros/ui/init-unsafe.stderr delete mode 100644 macros/ui/interrupt-double.rs delete mode 100644 macros/ui/interrupt-double.stderr delete mode 100644 macros/ui/local-collision-2.rs delete mode 100644 macros/ui/local-collision-2.stderr delete mode 100644 macros/ui/local-collision.rs delete mode 100644 macros/ui/local-collision.stderr delete mode 100644 macros/ui/local-malformed-1.rs delete mode 100644 macros/ui/local-malformed-1.stderr delete mode 100644 macros/ui/local-malformed-2.rs delete mode 100644 macros/ui/local-malformed-2.stderr delete mode 100644 macros/ui/local-malformed-3.rs delete mode 100644 macros/ui/local-malformed-3.stderr delete mode 100644 macros/ui/local-malformed-4.rs delete mode 100644 macros/ui/local-malformed-4.stderr delete mode 100644 macros/ui/local-not-declared.rs delete mode 100644 macros/ui/local-not-declared.stderr delete mode 100644 macros/ui/local-pub.rs delete mode 100644 macros/ui/local-pub.stderr delete mode 100644 macros/ui/local-shared-attribute.rs delete mode 100644 macros/ui/local-shared-attribute.stderr delete mode 100644 macros/ui/local-shared.rs delete mode 100644 macros/ui/local-shared.stderr delete mode 100644 macros/ui/shared-lock-free.rs delete mode 100644 macros/ui/shared-lock-free.stderr delete mode 100644 macros/ui/shared-not-declared.rs delete mode 100644 macros/ui/shared-not-declared.stderr delete mode 100644 macros/ui/shared-pub.rs delete mode 100644 macros/ui/shared-pub.stderr delete mode 100644 macros/ui/task-divergent.rs delete mode 100644 macros/ui/task-divergent.stderr delete mode 100644 macros/ui/task-double-local.rs delete mode 100644 macros/ui/task-double-local.stderr delete mode 100644 macros/ui/task-double-priority.rs delete mode 100644 macros/ui/task-double-priority.stderr delete mode 100644 macros/ui/task-double-shared.rs delete mode 100644 macros/ui/task-double-shared.stderr delete mode 100644 macros/ui/task-idle.rs delete mode 100644 macros/ui/task-idle.stderr delete mode 100644 macros/ui/task-init.rs delete mode 100644 macros/ui/task-init.stderr delete mode 100644 macros/ui/task-interrupt.rs delete mode 100644 macros/ui/task-interrupt.stderr delete mode 100644 macros/ui/task-no-context.rs delete mode 100644 macros/ui/task-no-context.stderr delete mode 100644 macros/ui/task-priority-too-high.rs delete mode 100644 macros/ui/task-priority-too-high.stderr delete mode 100644 macros/ui/task-priority-too-low.rs delete mode 100644 macros/ui/task-priority-too-low.stderr delete mode 100644 macros/ui/task-pub.rs delete mode 100644 macros/ui/task-pub.stderr delete mode 100644 macros/ui/task-unsafe.rs delete mode 100644 macros/ui/task-unsafe.stderr delete mode 100644 macros/ui/task-zero-prio.rs delete mode 100644 macros/ui/task-zero-prio.stderr (limited to 'macros') diff --git a/macros/.gitignore b/macros/.gitignore deleted file mode 100644 index 4fffb2f..0000000 --- a/macros/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/target -/Cargo.lock diff --git a/macros/Cargo.toml b/macros/Cargo.toml deleted file mode 100644 index 1cc9556..0000000 --- a/macros/Cargo.toml +++ /dev/null @@ -1,41 +0,0 @@ -[package] -authors = [ - "The Real-Time Interrupt-driven Concurrency developers", - "Emil Fresk ", - "Henrik Tjäder ", - "Jorge Aparicio ", - "Per Lindgren ", -] -categories = ["concurrency", "embedded", "no-std", "asynchronous"] -description = "Procedural macros, syntax parsing, and codegen of the RTIC crate" -documentation = "https://rtic-rs.github.io/rtic/api/rtic" -edition = "2021" -keywords = ["arm", "cortex-m", "risc-v", "embedded", "async", "runtime", "futures", "await", "no-std", "rtos", "bare-metal"] -license = "MIT OR Apache-2.0" -name = "rtic-macros" -readme = "../README.md" -repository = "https://github.com/rtic-rs/rtic" - -version = "2.0.0-alpha.0" - -[lib] -proc-macro = true - -[feature] -default = [] -debugprint = [] -# list of supported codegen backends -thumbv6 = [] -thumbv7 = [] -# riscv-clic = [] -# riscv-ch32 = [] - -[dependencies] -indexmap = "1.9.2" -proc-macro2 = "1.0.49" -proc-macro-error = "1.0.4" -quote = "1.0.23" -syn = { version = "1.0.107", features = ["extra-traits", "full"] } - -[dev-dependencies] -trybuild = "1.0.73" diff --git a/macros/src/analyze.rs b/macros/src/analyze.rs deleted file mode 100644 index 65774f6..0000000 --- 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, -} - -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::>(); - - // map from priorities to interrupts (holding name and attributes) - - let interrupts: BTreeMap = 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 8b13789..0000000 --- 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 a05c82e..0000000 --- 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::>(); - - 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 24e98ce..0000000 --- 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 dd94aa6..0000000 --- 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 { - 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 a12ad32..0000000 --- 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 8a5a8f6..0000000 --- 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 0c833ef..0000000 --- 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 6e1059f..0000000 --- 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 e6d1553..0000000 --- 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> = 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 100c3eb..0000000 --- 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 2775d25..0000000 --- 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) 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 4725b9a..0000000 --- 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 c4e5383..0000000 --- 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 { - 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> - // - `get_mut` to obtain a raw pointer to `MaybeUninit` - // - `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> - // - `get_mut` to obtain a raw pointer to `MaybeUninit` - // - `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 28ba29c..0000000 --- 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 { - 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 19fd13f..0000000 --- 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> = 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, - "e!(#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 fa6f0fc..0000000 --- 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 34fc851..0000000 --- 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 e121487..0000000 --- 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(&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, - // tupled e.g. `_0`, `(_0, _1)` - TokenStream2, - // untupled e.g. &[`_0`], &[`_0`, `_1`] - Vec, - // ty e.g. `Foo`, `(i32, i64)` - TokenStream2, -) { - if inputs.len() == 1 { - let ty = &inputs[0].ty; - - ( - vec![quote!(_0: #ty)], - quote!(_0), - vec![quote!(_0)], - quote!(#ty), - ) - } else { - let mut args = vec![]; - let mut pats = vec![]; - let mut tys = vec![]; - - for (i, input) in inputs.iter().enumerate() { - let i = Ident::new(&format!("_{}", i), Span::call_site()); - let ty = &input.ty; - - args.push(quote!(#i: #ty)); - - pats.push(quote!(#i)); - - tys.push(quote!(#ty)); - } - - let tupled = { - let pats = pats.clone(); - quote!((#(#pats,)*)) - }; - let ty = quote!((#(#tys,)*)); - (args, tupled, pats, ty) - } -} - -/// Get the ident for the name of the task -pub fn get_task_name(ctxt: Context, app: &App) -> Ident { - let s = match ctxt { - 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 a8422d0..0000000 --- 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: /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-/out/ - // ///debug/build/cortex-m-rtic-/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, / 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 d6f5a47..0000000 --- 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 = IndexMap; - -/// An order set -pub type Set = IndexSet; - -/// 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 { - 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 aee6042..0000000 --- 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 29971b1..0000000 --- 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 ccf6eb9..0000000 --- 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 fd3eed3..0000000 --- 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 f8d7c8b..0000000 --- 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 52d1ffd..0000000 --- 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 e75dde6..0000000 --- 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, &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 { - 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 3ed1487..0000000 --- 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 { - // 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::>(), - &ht.args.local_resources, - 0, - ) - })) - .chain(app.software_tasks.iter().map(|(name, ht)| { - ( - name.clone(), - ht.args - .shared_resources - .iter() - .map(|(v, _)| v) - .collect::>(), - &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::>(), - &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; - -/// 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; - -/// Location of all *used* shared resources -pub type UsedSharedResource = IndexSet; - -/// Location of all *used* local resources -pub type UsedLocalResource = IndexSet; - -/// Resource ownership -pub type Ownerships = IndexMap; - -/// These types must implement the `Send` trait -pub type SendTypes = Set>; - -/// These types must implement the `Sync` trait -pub type SyncTypes = Set>; - -/// 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, -} - -/// 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 27e6773..0000000 --- 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, - - /// Resources shared between tasks defined in `#[shared]` - pub shared_resources: Map, - - /// Task local resources defined in `#[local]` - pub local_resources: Map, - - /// User imports - pub user_imports: Vec, - - /// User code - pub user_code: Vec, - - /// Hardware tasks: `#[task(binds = ..)]`s - pub hardware_tasks: Map, - - /// Async software tasks: `#[task]` - pub software_tasks: Map, -} - -/// Interrupts used to dispatch software tasks -pub type Dispatchers = Map; - -/// 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, -} - -/// 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, - - /// The name of the `#[init]` function - pub name: Ident, - - /// The context argument - pub context: Box, - - /// The statements that make up this `init` function - pub stmts: Vec, - - /// 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, - - /// The name of the `#[idle]` function - pub name: Ident, - - /// The context argument - pub context: Box, - - /// The statements that make up this `idle` function - pub stmts: Vec, -} - -/// `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, - - /// `#[doc]` attributes like `/// this is a docstring` - pub docs: Vec, - - /// Attributes that will apply to this resource - pub attrs: Vec, - - /// The type of this resource - pub ty: Box, - - /// 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, - - /// `#[doc]` attributes like `/// this is a docstring` - pub docs: Vec, - - /// Attributes that will apply to this resource - pub attrs: Vec, - - /// The type of this resource - pub ty: Box, -} - -/// 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, - - /// Attributes that will apply to this interrupt handler - pub attrs: Vec, - - /// The context argument - pub context: Box, - - /// The inputs of this software task - pub inputs: Vec, - - /// The statements that make up the task handler - pub stmts: Vec, - - /// The task is declared externally - pub is_extern: bool, -} - -/// 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, - - /// Attributes that will apply to this interrupt handler - pub attrs: Vec, - - /// The context argument - pub context: Box, - - /// The statements that make up the task handler - pub stmts: Vec, - - /// 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, - - /// `#[cfg]` attributes like `#[cfg(debug_assertions)]` - pub cfgs: Vec, - - /// Type - pub ty: Box, - - /// Initial value - pub expr: Box, -} - -/// 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; - -/// Local resource access/declaration list in task attribute -pub type LocalResources = Map; diff --git a/macros/src/syntax/check.rs b/macros/src/syntax/check.rs deleted file mode 100644 index 989d418..0000000 --- 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::>(); - 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 e83ba31..0000000 --- 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::>(); - - let map = priorities - .iter() - .cloned() - .zip(1..) - .collect::>(); - - 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 c78453a..0000000 --- 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 { - 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, -} - -impl Parse for Input { - fn parse(input: ParseStream<'_>) -> parse::Result { - fn parse_items(input: ParseStream<'_>) -> parse::Result> { - 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 { - (|input: ParseStream<'_>| -> parse::Result { - 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 { - (|input: ParseStream<'_>| -> parse::Result { - 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> { - (|input: ParseStream<'_>| -> parse::Result> { - 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::().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::().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 e797f75..0000000 --- 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 { - (|input: ParseStream<'_>| -> parse::Result { - 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::() { - 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::() { - 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::() { - 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 { - 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::::new(); - let mut bindings = HashSet::::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 7f6dfbe..0000000 --- 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 { - 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 { - 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::::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 124c136..0000000 --- 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 { - crate::syntax::parse::idle_args(tokens) - } -} - -impl Idle { - pub(crate) fn parse(args: IdleArgs, item: ItemFn) -> parse::Result { - 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 0aea20b..0000000 --- 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 { - syntax_parse::init_args(tokens) - } -} - -impl Init { - pub(crate) fn parse(args: InitArgs, item: ItemFn) -> parse::Result { - 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 ff10057..0000000 --- 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 { - 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 { - 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 769aa65..0000000 --- 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 { - 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 { - 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::::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 5a5e0c0..0000000 --- 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, - pub docs: Vec, - pub attrs: Vec, -} - -pub fn filter_attributes(input_attrs: Vec) -> 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) -> parse::Result { - 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 { - let inner; - bracketed!(inner in content); - - let mut resources = Map::new(); - for e in inner.call(Punctuated::::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 { - 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 { - let inner; - bracketed!(inner in content); - - let mut resources = Map::new(); - - for e in inner.call(Punctuated::::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, Result, FnArg>)>; - -pub fn parse_inputs(inputs: Punctuated, 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::, _>>(); - - 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 { - 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 e9e3326..0000000 --- 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 f20c9cc..0000000 --- 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"); -} diff --git a/macros/tests/ui.rs b/macros/tests/ui.rs deleted file mode 100644 index 9fb88a1..0000000 --- a/macros/tests/ui.rs +++ /dev/null @@ -1,7 +0,0 @@ -use trybuild::TestCases; - -#[test] -fn ui() { - let t = TestCases::new(); - t.compile_fail("ui/*.rs"); -} diff --git a/macros/ui/extern-interrupt-used.rs b/macros/ui/extern-interrupt-used.rs deleted file mode 100644 index 6346a7d..0000000 --- a/macros/ui/extern-interrupt-used.rs +++ /dev/null @@ -1,16 +0,0 @@ -#![no_main] - -#[rtic_macros::mock_app(device = mock, dispatchers = [EXTI0])] -mod app { - #[shared] - struct Shared {} - - #[local] - struct Local {} - - #[init] - fn init(_: init::Context) -> (Shared, Local) {} - - #[task(binds = EXTI0)] - fn foo(_: foo::Context) {} -} diff --git a/macros/ui/extern-interrupt-used.stderr b/macros/ui/extern-interrupt-used.stderr deleted file mode 100644 index 970d39b..0000000 --- a/macros/ui/extern-interrupt-used.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: dispatcher interrupts can't be used as hardware tasks - --> ui/extern-interrupt-used.rs:14:20 - | -14 | #[task(binds = EXTI0)] - | ^^^^^ diff --git a/macros/ui/idle-double-local.rs b/macros/ui/idle-double-local.rs deleted file mode 100644 index 54e67d3..0000000 --- a/macros/ui/idle-double-local.rs +++ /dev/null @@ -1,9 +0,0 @@ -#![no_main] - -#[rtic_macros::mock_app(device = mock)] -mod app { - #[idle(local = [A], local = [B])] - fn idle(_: idle::Context) -> ! { - loop {} - } -} diff --git a/macros/ui/idle-double-local.stderr b/macros/ui/idle-double-local.stderr deleted file mode 100644 index b558136..0000000 --- a/macros/ui/idle-double-local.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: argument appears more than once - --> ui/idle-double-local.rs:5:25 - | -5 | #[idle(local = [A], local = [B])] - | ^^^^^ diff --git a/macros/ui/idle-double-shared.rs b/macros/ui/idle-double-shared.rs deleted file mode 100644 index f66cb93..0000000 --- a/macros/ui/idle-double-shared.rs +++ /dev/null @@ -1,9 +0,0 @@ -#![no_main] - -#[rtic_macros::mock_app(device = mock)] -mod app { - #[idle(shared = [A], shared = [B])] - fn idle(_: idle::Context) -> ! { - loop {} - } -} diff --git a/macros/ui/idle-double-shared.stderr b/macros/ui/idle-double-shared.stderr deleted file mode 100644 index 6f62ad2..0000000 --- a/macros/ui/idle-double-shared.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: argument appears more than once - --> ui/idle-double-shared.rs:5:26 - | -5 | #[idle(shared = [A], shared = [B])] - | ^^^^^^ diff --git a/macros/ui/idle-input.rs b/macros/ui/idle-input.rs deleted file mode 100644 index c896b1c..0000000 --- a/macros/ui/idle-input.rs +++ /dev/null @@ -1,9 +0,0 @@ -#![no_main] - -#[rtic_macros::mock_app(device = mock)] -mod app { - #[idle] - fn idle(_: idle::Context, _undef: u32) -> ! { - loop {} - } -} diff --git a/macros/ui/idle-input.stderr b/macros/ui/idle-input.stderr deleted file mode 100644 index 34c38fc..0000000 --- a/macros/ui/idle-input.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: this `#[idle]` function must have signature `fn(idle::Context) -> !` - --> ui/idle-input.rs:6:8 - | -6 | fn idle(_: idle::Context, _undef: u32) -> ! { - | ^^^^ diff --git a/macros/ui/idle-no-context.rs b/macros/ui/idle-no-context.rs deleted file mode 100644 index bab4680..0000000 --- a/macros/ui/idle-no-context.rs +++ /dev/null @@ -1,9 +0,0 @@ -#![no_main] - -#[rtic_macros::mock_app(device = mock)] -mod app { - #[idle] - fn idle() -> ! { - loop {} - } -} diff --git a/macros/ui/idle-no-context.stderr b/macros/ui/idle-no-context.stderr deleted file mode 100644 index c9f4b3d..0000000 --- a/macros/ui/idle-no-context.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: this `#[idle]` function must have signature `fn(idle::Context) -> !` - --> ui/idle-no-context.rs:6:8 - | -6 | fn idle() -> ! { - | ^^^^ diff --git a/macros/ui/idle-not-divergent.rs b/macros/ui/idle-not-divergent.rs deleted file mode 100644 index d1ae8b1..0000000 --- a/macros/ui/idle-not-divergent.rs +++ /dev/null @@ -1,7 +0,0 @@ -#![no_main] - -#[rtic_macros::mock_app(device = mock)] -mod app { - #[idle] - fn idle(_: idle::Context) {} -} diff --git a/macros/ui/idle-not-divergent.stderr b/macros/ui/idle-not-divergent.stderr deleted file mode 100644 index e318f58..0000000 --- a/macros/ui/idle-not-divergent.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: this `#[idle]` function must have signature `fn(idle::Context) -> !` - --> ui/idle-not-divergent.rs:6:8 - | -6 | fn idle(_: idle::Context) {} - | ^^^^ diff --git a/macros/ui/idle-output.rs b/macros/ui/idle-output.rs deleted file mode 100644 index 1662157..0000000 --- a/macros/ui/idle-output.rs +++ /dev/null @@ -1,9 +0,0 @@ -#![no_main] - -#[rtic_macros::mock_app(device = mock)] -mod app { - #[idle] - fn idle(_: idle::Context) -> u32 { - 0 - } -} diff --git a/macros/ui/idle-output.stderr b/macros/ui/idle-output.stderr deleted file mode 100644 index 7070e25..0000000 --- a/macros/ui/idle-output.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: this `#[idle]` function must have signature `fn(idle::Context) -> !` - --> ui/idle-output.rs:6:8 - | -6 | fn idle(_: idle::Context) -> u32 { - | ^^^^ diff --git a/macros/ui/idle-pub.rs b/macros/ui/idle-pub.rs deleted file mode 100644 index 0d8dd01..0000000 --- a/macros/ui/idle-pub.rs +++ /dev/null @@ -1,9 +0,0 @@ -#![no_main] - -#[rtic_macros::mock_app(device = mock)] -mod app { - #[idle] - pub fn idle(_: idle::Context) -> ! { - loop {} - } -} diff --git a/macros/ui/idle-pub.stderr b/macros/ui/idle-pub.stderr deleted file mode 100644 index aa46ac3..0000000 --- a/macros/ui/idle-pub.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: this `#[idle]` function must have signature `fn(idle::Context) -> !` - --> ui/idle-pub.rs:6:12 - | -6 | pub fn idle(_: idle::Context) -> ! { - | ^^^^ diff --git a/macros/ui/idle-unsafe.rs b/macros/ui/idle-unsafe.rs deleted file mode 100644 index 3422ef2..0000000 --- a/macros/ui/idle-unsafe.rs +++ /dev/null @@ -1,9 +0,0 @@ -#![no_main] - -#[rtic_macros::mock_app(device = mock)] -mod app { - #[idle] - unsafe fn idle(_: idle::Context) -> ! { - loop {} - } -} diff --git a/macros/ui/idle-unsafe.stderr b/macros/ui/idle-unsafe.stderr deleted file mode 100644 index a416800..0000000 --- a/macros/ui/idle-unsafe.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: this `#[idle]` function must have signature `fn(idle::Context) -> !` - --> ui/idle-unsafe.rs:6:15 - | -6 | unsafe fn idle(_: idle::Context) -> ! { - | ^^^^ diff --git a/macros/ui/init-divergent.rs b/macros/ui/init-divergent.rs deleted file mode 100644 index 5e4e96a..0000000 --- a/macros/ui/init-divergent.rs +++ /dev/null @@ -1,13 +0,0 @@ -#![no_main] - -#[rtic_macros::mock_app(device = mock)] -mod app { - #[shared] - struct Shared {} - - #[local] - struct Local {} - - #[init] - fn init(_: init::Context) -> ! {} -} diff --git a/macros/ui/init-divergent.stderr b/macros/ui/init-divergent.stderr deleted file mode 100644 index 9f6acf6..0000000 --- a/macros/ui/init-divergent.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: the `#[init]` function must have signature `fn(init::Context) -> (Shared resources struct, Local resources struct)` - --> ui/init-divergent.rs:12:8 - | -12 | fn init(_: init::Context) -> ! {} - | ^^^^ diff --git a/macros/ui/init-double-local.rs b/macros/ui/init-double-local.rs deleted file mode 100644 index 5f6d7ac..0000000 --- a/macros/ui/init-double-local.rs +++ /dev/null @@ -1,7 +0,0 @@ -#![no_main] - -#[rtic_macros::mock_app(device = mock)] -mod app { - #[init(local = [A], local = [B])] - fn init(_: init::Context) {} -} diff --git a/macros/ui/init-double-local.stderr b/macros/ui/init-double-local.stderr deleted file mode 100644 index 07c3b50..0000000 --- a/macros/ui/init-double-local.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: argument appears more than once - --> ui/init-double-local.rs:5:25 - | -5 | #[init(local = [A], local = [B])] - | ^^^^^ diff --git a/macros/ui/init-double-shared.rs b/macros/ui/init-double-shared.rs deleted file mode 100644 index 4503c87..0000000 --- a/macros/ui/init-double-shared.rs +++ /dev/null @@ -1,7 +0,0 @@ -#![no_main] - -#[rtic_macros::mock_app(device = mock)] -mod app { - #[init(shared = [A], shared = [B])] - fn init(_: init::Context) {} -} diff --git a/macros/ui/init-double-shared.stderr b/macros/ui/init-double-shared.stderr deleted file mode 100644 index af2a97b..0000000 --- a/macros/ui/init-double-shared.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: unexpected argument - --> ui/init-double-shared.rs:5:12 - | -5 | #[init(shared = [A], shared = [B])] - | ^^^^^^ diff --git a/macros/ui/init-input.rs b/macros/ui/init-input.rs deleted file mode 100644 index d41a503..0000000 --- a/macros/ui/init-input.rs +++ /dev/null @@ -1,13 +0,0 @@ -#![no_main] - -#[rtic_macros::mock_app(device = mock)] -mod app { - #[shared] - struct Shared {} - - #[local] - struct Local {} - - #[init] - fn init(_: init::Context, _undef: u32) -> (Shared, Local) {} -} diff --git a/macros/ui/init-input.stderr b/macros/ui/init-input.stderr deleted file mode 100644 index e236043..0000000 --- a/macros/ui/init-input.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: the `#[init]` function must have signature `fn(init::Context) -> (Shared resources struct, Local resources struct)` - --> ui/init-input.rs:12:8 - | -12 | fn init(_: init::Context, _undef: u32) -> (Shared, Local) {} - | ^^^^ diff --git a/macros/ui/init-no-context.rs b/macros/ui/init-no-context.rs deleted file mode 100644 index cdce4c5..0000000 --- a/macros/ui/init-no-context.rs +++ /dev/null @@ -1,13 +0,0 @@ -#![no_main] - -#[rtic_macros::mock_app(device = mock)] -mod app { - #[shared] - struct Shared {} - - #[local] - struct Local {} - - #[init] - fn init() -> (Shared, Local) {} -} diff --git a/macros/ui/init-no-context.stderr b/macros/ui/init-no-context.stderr deleted file mode 100644 index 28e1fd4..0000000 --- a/macros/ui/init-no-context.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: the `#[init]` function must have signature `fn(init::Context) -> (Shared resources struct, Local resources struct)` - --> ui/init-no-context.rs:12:8 - | -12 | fn init() -> (Shared, Local) {} - | ^^^^ diff --git a/macros/ui/init-output.rs b/macros/ui/init-output.rs deleted file mode 100644 index 7057c95..0000000 --- a/macros/ui/init-output.rs +++ /dev/null @@ -1,9 +0,0 @@ -#![no_main] - -#[rtic_macros::mock_app(device = mock)] -mod app { - #[init] - fn init(_: init::Context) -> u32 { - 0 - } -} diff --git a/macros/ui/init-output.stderr b/macros/ui/init-output.stderr deleted file mode 100644 index 8bc3c83..0000000 --- a/macros/ui/init-output.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: the `#[init]` function must have signature `fn(init::Context) -> (Shared resources struct, Local resources struct)` - --> ui/init-output.rs:6:8 - | -6 | fn init(_: init::Context) -> u32 { - | ^^^^ diff --git a/macros/ui/init-pub.rs b/macros/ui/init-pub.rs deleted file mode 100644 index dd59aa1..0000000 --- a/macros/ui/init-pub.rs +++ /dev/null @@ -1,13 +0,0 @@ -#![no_main] - -#[rtic_macros::mock_app(device = mock)] -mod app { - #[shared] - struct Shared {} - - #[local] - struct Local {} - - #[init] - pub fn init(_: init::Context) -> (Shared, Local) {} -} diff --git a/macros/ui/init-pub.stderr b/macros/ui/init-pub.stderr deleted file mode 100644 index b1610ed..0000000 --- a/macros/ui/init-pub.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: the `#[init]` function must have signature `fn(init::Context) -> (Shared resources struct, Local resources struct)` - --> ui/init-pub.rs:12:12 - | -12 | pub fn init(_: init::Context) -> (Shared, Local) {} - | ^^^^ diff --git a/macros/ui/init-unsafe.rs b/macros/ui/init-unsafe.rs deleted file mode 100644 index 4f89baf..0000000 --- a/macros/ui/init-unsafe.rs +++ /dev/null @@ -1,7 +0,0 @@ -#![no_main] - -#[rtic_macros::mock_app(device = mock)] -mod app { - #[init] - unsafe fn init(_: init::Context) -> (Shared, Local) {} -} diff --git a/macros/ui/init-unsafe.stderr b/macros/ui/init-unsafe.stderr deleted file mode 100644 index fd0b8f3..0000000 --- a/macros/ui/init-unsafe.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: the `#[init]` function must have signature `fn(init::Context) -> (Shared resources struct, Local resources struct)` - --> ui/init-unsafe.rs:6:15 - | -6 | unsafe fn init(_: init::Context) -> (Shared, Local) {} - | ^^^^ diff --git a/macros/ui/interrupt-double.rs b/macros/ui/interrupt-double.rs deleted file mode 100644 index e2addc7..0000000 --- a/macros/ui/interrupt-double.rs +++ /dev/null @@ -1,10 +0,0 @@ -#![no_main] - -#[rtic_macros::mock_app(device = mock)] -mod app { - #[task(binds = UART0)] - fn foo(_: foo::Context) {} - - #[task(binds = UART0)] - fn bar(_: bar::Context) {} -} diff --git a/macros/ui/interrupt-double.stderr b/macros/ui/interrupt-double.stderr deleted file mode 100644 index 8db34e2..0000000 --- a/macros/ui/interrupt-double.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: this interrupt is already bound - --> ui/interrupt-double.rs:8:20 - | -8 | #[task(binds = UART0)] - | ^^^^^ diff --git a/macros/ui/local-collision-2.rs b/macros/ui/local-collision-2.rs deleted file mode 100644 index 08bc8e5..0000000 --- a/macros/ui/local-collision-2.rs +++ /dev/null @@ -1,18 +0,0 @@ -#![no_main] - -#[rtic_macros::mock_app(device = mock)] -mod app { - #[shared] - struct Shared {} - - #[local] - struct Local { - a: u32, - } - - #[task(local = [a: u8 = 3])] - async fn bar(_: bar::Context) {} - - #[init(local = [a: u16 = 2])] - fn init(_: init::Context) -> (Shared, Local) {} -} diff --git a/macros/ui/local-collision-2.stderr b/macros/ui/local-collision-2.stderr deleted file mode 100644 index 47dbbe3..0000000 --- a/macros/ui/local-collision-2.stderr +++ /dev/null @@ -1,17 +0,0 @@ -error: Local resource "a" is used by multiple tasks or collides with multiple definitions - --> ui/local-collision-2.rs:10:9 - | -10 | a: u32, - | ^ - -error: Local resource "a" is used by multiple tasks or collides with multiple definitions - --> ui/local-collision-2.rs:16:21 - | -16 | #[init(local = [a: u16 = 2])] - | ^ - -error: Local resource "a" is used by multiple tasks or collides with multiple definitions - --> ui/local-collision-2.rs:13:21 - | -13 | #[task(local = [a: u8 = 3])] - | ^ diff --git a/macros/ui/local-collision.rs b/macros/ui/local-collision.rs deleted file mode 100644 index 0e4eef7..0000000 --- a/macros/ui/local-collision.rs +++ /dev/null @@ -1,21 +0,0 @@ -#![no_main] - -#[rtic_macros::mock_app(device = mock)] -mod app { - #[shared] - struct Shared {} - - #[local] - struct Local { - a: u32, - } - - #[task(local = [a])] - async fn foo(_: foo::Context) {} - - #[task(local = [a: u8 = 3])] - async fn bar(_: bar::Context) {} - - #[init] - fn init(_: init::Context) -> (Shared, Local) {} -} diff --git a/macros/ui/local-collision.stderr b/macros/ui/local-collision.stderr deleted file mode 100644 index 47fbb6e..0000000 --- a/macros/ui/local-collision.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error: Local resource "a" is used by multiple tasks or collides with multiple definitions - --> ui/local-collision.rs:10:9 - | -10 | a: u32, - | ^ - -error: Local resource "a" is used by multiple tasks or collides with multiple definitions - --> ui/local-collision.rs:16:21 - | -16 | #[task(local = [a: u8 = 3])] - | ^ diff --git a/macros/ui/local-malformed-1.rs b/macros/ui/local-malformed-1.rs deleted file mode 100644 index 219eef5..0000000 --- a/macros/ui/local-malformed-1.rs +++ /dev/null @@ -1,16 +0,0 @@ -#![no_main] - -#[rtic_macros::mock_app(device = mock)] -mod app { - #[shared] - struct Shared {} - - #[local] - struct Local {} - - #[task(local = [a:])] - async fn foo(_: foo::Context) {} - - #[init] - fn init(_: init::Context) -> (Shared, Local) {} -} diff --git a/macros/ui/local-malformed-1.stderr b/macros/ui/local-malformed-1.stderr deleted file mode 100644 index d15c324..0000000 --- a/macros/ui/local-malformed-1.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: unexpected end of input, expected one of: `for`, parentheses, `fn`, `unsafe`, `extern`, identifier, `::`, `<`, square brackets, `*`, `&`, `!`, `impl`, `_`, lifetime - --> ui/local-malformed-1.rs:11:23 - | -11 | #[task(local = [a:])] - | ^ diff --git a/macros/ui/local-malformed-2.rs b/macros/ui/local-malformed-2.rs deleted file mode 100644 index d691453..0000000 --- a/macros/ui/local-malformed-2.rs +++ /dev/null @@ -1,16 +0,0 @@ -#![no_main] - -#[rtic_macros::mock_app(device = mock)] -mod app { - #[shared] - struct Shared {} - - #[local] - struct Local {} - - #[task(local = [a: u32])] - async fn foo(_: foo::Context) {} - - #[init] - fn init(_: init::Context) -> (Shared, Local) {} -} diff --git a/macros/ui/local-malformed-2.stderr b/macros/ui/local-malformed-2.stderr deleted file mode 100644 index 0b448f0..0000000 --- a/macros/ui/local-malformed-2.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: malformed, expected 'IDENT: TYPE = EXPR' - --> ui/local-malformed-2.rs:11:21 - | -11 | #[task(local = [a: u32])] - | ^^^^^^ diff --git a/macros/ui/local-malformed-3.rs b/macros/ui/local-malformed-3.rs deleted file mode 100644 index 7eddfa4..0000000 --- a/macros/ui/local-malformed-3.rs +++ /dev/null @@ -1,16 +0,0 @@ -#![no_main] - -#[rtic_macros::mock_app(device = mock)] -mod app { - #[shared] - struct Shared {} - - #[local] - struct Local {} - - #[task(local = [a: u32 =])] - async fn foo(_: foo::Context) {} - - #[init] - fn init(_: init::Context) -> (Shared, Local) {} -} diff --git a/macros/ui/local-malformed-3.stderr b/macros/ui/local-malformed-3.stderr deleted file mode 100644 index 61af4f3..0000000 --- a/macros/ui/local-malformed-3.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: unexpected end of input, expected expression - --> ui/local-malformed-3.rs:11:29 - | -11 | #[task(local = [a: u32 =])] - | ^ diff --git a/macros/ui/local-malformed-4.rs b/macros/ui/local-malformed-4.rs deleted file mode 100644 index b913947..0000000 --- a/macros/ui/local-malformed-4.rs +++ /dev/null @@ -1,16 +0,0 @@ -#![no_main] - -#[rtic_macros::mock_app(device = mock)] -mod app { - #[shared] - struct Shared {} - - #[local] - struct Local {} - - #[task(local = [a = u32])] - async fn foo(_: foo::Context) {} - - #[init] - fn init(_: init::Context) -> (Shared, Local) {} -} diff --git a/macros/ui/local-malformed-4.stderr b/macros/ui/local-malformed-4.stderr deleted file mode 100644 index 0f7d9e7..0000000 --- a/macros/ui/local-malformed-4.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: malformed, expected a type - --> ui/local-malformed-4.rs:11:21 - | -11 | #[task(local = [a = u32])] - | ^ diff --git a/macros/ui/local-not-declared.rs b/macros/ui/local-not-declared.rs deleted file mode 100644 index 7c087e4..0000000 --- a/macros/ui/local-not-declared.rs +++ /dev/null @@ -1,16 +0,0 @@ -#![no_main] - -#[rtic_macros::mock_app(device = mock)] -mod app { - #[shared] - struct Shared {} - - #[local] - struct Local {} - - #[task(local = [A])] - async fn foo(_: foo::Context) {} - - #[init] - fn init(_: init::Context) -> (Shared, Local) {} -} diff --git a/macros/ui/local-not-declared.stderr b/macros/ui/local-not-declared.stderr deleted file mode 100644 index 10d4b04..0000000 --- a/macros/ui/local-not-declared.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: this local resource has NOT been declared - --> ui/local-not-declared.rs:11:21 - | -11 | #[task(local = [A])] - | ^ diff --git a/macros/ui/local-pub.rs b/macros/ui/local-pub.rs deleted file mode 100644 index 42da4f4..0000000 --- a/macros/ui/local-pub.rs +++ /dev/null @@ -1,15 +0,0 @@ -#![no_main] - -#[rtic_macros::mock_app(device = mock)] -mod app { - #[shared] - struct Shared {} - - #[local] - struct Local { - pub x: u32, - } - - #[init] - fn init(_: init::Context) -> (Shared, Local) {} -} diff --git a/macros/ui/local-pub.stderr b/macros/ui/local-pub.stderr deleted file mode 100644 index e4814ca..0000000 --- a/macros/ui/local-pub.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: this field must have inherited / private visibility - --> ui/local-pub.rs:10:13 - | -10 | pub x: u32, - | ^ diff --git a/macros/ui/local-shared-attribute.rs b/macros/ui/local-shared-attribute.rs deleted file mode 100644 index c594b5f..0000000 --- a/macros/ui/local-shared-attribute.rs +++ /dev/null @@ -1,21 +0,0 @@ -#![no_main] - -#[rtic_macros::mock_app(device = mock)] -mod app { - #[shared] - struct Shared {} - - #[local] - struct Local {} - - #[init] - fn init(_: init::Context) -> (Shared, Local) {} - - #[task(local = [ - #[test] - a: u32 = 0, // Ok - #[test] - b, // Error - ])] - fn foo(_: foo::Context) {} -} diff --git a/macros/ui/local-shared-attribute.stderr b/macros/ui/local-shared-attribute.stderr deleted file mode 100644 index a8130e8..0000000 --- a/macros/ui/local-shared-attribute.stderr +++ /dev/null @@ -1,6 +0,0 @@ -error: attributes are not supported here - --> ui/local-shared-attribute.rs:17:9 - | -17 | / #[test] -18 | | b, // Error - | |_________^ diff --git a/macros/ui/local-shared.rs b/macros/ui/local-shared.rs deleted file mode 100644 index 4e8f9f4..0000000 --- a/macros/ui/local-shared.rs +++ /dev/null @@ -1,28 +0,0 @@ -#![no_main] - -#[rtic_macros::mock_app(device = mock)] -mod app { - #[shared] - struct Shared {} - - #[local] - struct Local { - l1: u32, - l2: u32, - } - - #[init] - fn init(_: init::Context) -> (Shared, Local) {} - - // l2 ok - #[idle(local = [l2])] - fn idle(cx: idle::Context) -> ! {} - - // l1 rejected (not local) - #[task(priority = 1, local = [l1])] - async fn uart0(cx: uart0::Context) {} - - // l1 rejected (not lock_free) - #[task(priority = 2, local = [l1])] - async fn uart1(cx: uart1::Context) {} -} diff --git a/macros/ui/local-shared.stderr b/macros/ui/local-shared.stderr deleted file mode 100644 index fceb763..0000000 --- a/macros/ui/local-shared.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error: Local resource "l1" is used by multiple tasks or collides with multiple definitions - --> ui/local-shared.rs:22:35 - | -22 | #[task(priority = 1, local = [l1])] - | ^^ - -error: Local resource "l1" is used by multiple tasks or collides with multiple definitions - --> ui/local-shared.rs:26:35 - | -26 | #[task(priority = 2, local = [l1])] - | ^^ diff --git a/macros/ui/shared-lock-free.rs b/macros/ui/shared-lock-free.rs deleted file mode 100644 index b3a4b9c..0000000 --- a/macros/ui/shared-lock-free.rs +++ /dev/null @@ -1,38 +0,0 @@ -#![no_main] - -#[rtic_macros::mock_app(device = mock)] -mod app { - #[shared] - struct Shared { - // An exclusive, early resource - #[lock_free] - e1: u32, - - // An exclusive, late resource - #[lock_free] - e2: u32, - } - - #[local] - struct Local {} - - #[init] - fn init(_: init::Context) -> (Shared, Local) {} - - // e2 ok - #[idle(shared = [e2])] - fn idle(cx: idle::Context) -> ! { - debug::exit(debug::EXIT_SUCCESS); - loop {} - } - - // e1 rejected (not lock_free) - #[task(binds = UART0, priority = 1, shared = [e1])] - fn uart0(cx: uart0::Context) { - *cx.resources.e1 += 10; - } - - // e1 rejected (not lock_free) - #[task(binds = UART1, priority = 2, shared = [e1])] - fn uart1(cx: uart1::Context) {} -} diff --git a/macros/ui/shared-lock-free.stderr b/macros/ui/shared-lock-free.stderr deleted file mode 100644 index 51e99a0..0000000 --- a/macros/ui/shared-lock-free.stderr +++ /dev/null @@ -1,17 +0,0 @@ -error: Lock free shared resource "e1" is used by tasks at different priorities - --> ui/shared-lock-free.rs:9:9 - | -9 | e1: u32, - | ^^ - -error: Shared resource "e1" is declared lock free but used by tasks at different priorities - --> ui/shared-lock-free.rs:30:51 - | -30 | #[task(binds = UART0, priority = 1, shared = [e1])] - | ^^ - -error: Shared resource "e1" is declared lock free but used by tasks at different priorities - --> ui/shared-lock-free.rs:36:51 - | -36 | #[task(binds = UART1, priority = 2, shared = [e1])] - | ^^ diff --git a/macros/ui/shared-not-declared.rs b/macros/ui/shared-not-declared.rs deleted file mode 100644 index 5fef534..0000000 --- a/macros/ui/shared-not-declared.rs +++ /dev/null @@ -1,16 +0,0 @@ -#![no_main] - -#[rtic_macros::mock_app(device = mock)] -mod app { - #[shared] - struct Shared {} - - #[local] - struct Local {} - - #[task(shared = [A])] - async fn foo(_: foo::Context) {} - - #[init] - fn init(_: init::Context) -> (Shared, Local) {} -} diff --git a/macros/ui/shared-not-declared.stderr b/macros/ui/shared-not-declared.stderr deleted file mode 100644 index 7c5fb32..0000000 --- a/macros/ui/shared-not-declared.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: this shared resource has NOT been declared - --> ui/shared-not-declared.rs:11:22 - | -11 | #[task(shared = [A])] - | ^ diff --git a/macros/ui/shared-pub.rs b/macros/ui/shared-pub.rs deleted file mode 100644 index 10351fd..0000000 --- a/macros/ui/shared-pub.rs +++ /dev/null @@ -1,9 +0,0 @@ -#![no_main] - -#[rtic_macros::mock_app(device = mock)] -mod app { - #[shared] - struct Shared { - pub x: u32, - } -} diff --git a/macros/ui/shared-pub.stderr b/macros/ui/shared-pub.stderr deleted file mode 100644 index 7148893..0000000 --- a/macros/ui/shared-pub.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: this field must have inherited / private visibility - --> ui/shared-pub.rs:7:13 - | -7 | pub x: u32, - | ^ diff --git a/macros/ui/task-divergent.rs b/macros/ui/task-divergent.rs deleted file mode 100644 index ffe2dc0..0000000 --- a/macros/ui/task-divergent.rs +++ /dev/null @@ -1,9 +0,0 @@ -#![no_main] - -#[rtic_macros::mock_app(device = mock)] -mod app { - #[task] - async fn foo(_: foo::Context) -> ! { - loop {} - } -} diff --git a/macros/ui/task-divergent.stderr b/macros/ui/task-divergent.stderr deleted file mode 100644 index dd00208..0000000 --- a/macros/ui/task-divergent.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: this task handler must have type signature `async fn(foo::Context, ..)` - --> ui/task-divergent.rs:6:14 - | -6 | async fn foo(_: foo::Context) -> ! { - | ^^^ diff --git a/macros/ui/task-double-local.rs b/macros/ui/task-double-local.rs deleted file mode 100644 index c5277e2..0000000 --- a/macros/ui/task-double-local.rs +++ /dev/null @@ -1,7 +0,0 @@ -#![no_main] - -#[rtic_macros::mock_app(device = mock)] -mod app { - #[task(local = [A], local = [B])] - async fn foo(_: foo::Context) {} -} diff --git a/macros/ui/task-double-local.stderr b/macros/ui/task-double-local.stderr deleted file mode 100644 index 91ed844..0000000 --- a/macros/ui/task-double-local.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: argument appears more than once - --> ui/task-double-local.rs:5:25 - | -5 | #[task(local = [A], local = [B])] - | ^^^^^ diff --git a/macros/ui/task-double-priority.rs b/macros/ui/task-double-priority.rs deleted file mode 100644 index 5c8bd5b..0000000 --- a/macros/ui/task-double-priority.rs +++ /dev/null @@ -1,7 +0,0 @@ -#![no_main] - -#[rtic_macros::mock_app(device = mock)] -mod app { - #[task(priority = 1, priority = 2)] - async fn foo(_: foo::Context) {} -} diff --git a/macros/ui/task-double-priority.stderr b/macros/ui/task-double-priority.stderr deleted file mode 100644 index b3c814a..0000000 --- a/macros/ui/task-double-priority.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: argument appears more than once - --> ui/task-double-priority.rs:5:26 - | -5 | #[task(priority = 1, priority = 2)] - | ^^^^^^^^ diff --git a/macros/ui/task-double-shared.rs b/macros/ui/task-double-shared.rs deleted file mode 100644 index f9812d3..0000000 --- a/macros/ui/task-double-shared.rs +++ /dev/null @@ -1,7 +0,0 @@ -#![no_main] - -#[rtic_macros::mock_app(device = mock)] -mod app { - #[task(shared = [A], shared = [B])] - async fn foo(_: foo::Context) {} -} diff --git a/macros/ui/task-double-shared.stderr b/macros/ui/task-double-shared.stderr deleted file mode 100644 index bb90212..0000000 --- a/macros/ui/task-double-shared.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: argument appears more than once - --> ui/task-double-shared.rs:5:26 - | -5 | #[task(shared = [A], shared = [B])] - | ^^^^^^ diff --git a/macros/ui/task-idle.rs b/macros/ui/task-idle.rs deleted file mode 100644 index 353c782..0000000 --- a/macros/ui/task-idle.rs +++ /dev/null @@ -1,13 +0,0 @@ -#![no_main] - -#[rtic_macros::mock_app(device = mock)] -mod app { - #[idle] - fn foo(_: foo::Context) -> ! { - loop {} - } - - // name collides with `#[idle]` function - #[task] - async fn foo(_: foo::Context) {} -} diff --git a/macros/ui/task-idle.stderr b/macros/ui/task-idle.stderr deleted file mode 100644 index 4ccc113..0000000 --- a/macros/ui/task-idle.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: this identifier has already been used - --> ui/task-idle.rs:12:14 - | -12 | async fn foo(_: foo::Context) {} - | ^^^ diff --git a/macros/ui/task-init.rs b/macros/ui/task-init.rs deleted file mode 100644 index e58fdce..0000000 --- a/macros/ui/task-init.rs +++ /dev/null @@ -1,17 +0,0 @@ -#![no_main] - -#[rtic_macros::mock_app(device = mock)] -mod app { - #[shared] - struct Shared {} - - #[local] - struct Local {} - - #[init] - fn foo(_: foo::Context) -> (Shared, Local) {} - - // name collides with `#[idle]` function - #[task] - async fn foo(_: foo::Context) {} -} diff --git a/macros/ui/task-init.stderr b/macros/ui/task-init.stderr deleted file mode 100644 index 161e194..0000000 --- a/macros/ui/task-init.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: this identifier has already been used - --> ui/task-init.rs:16:14 - | -16 | async fn foo(_: foo::Context) {} - | ^^^ diff --git a/macros/ui/task-interrupt.rs b/macros/ui/task-interrupt.rs deleted file mode 100644 index 3d50bd8..0000000 --- a/macros/ui/task-interrupt.rs +++ /dev/null @@ -1,10 +0,0 @@ -#![no_main] - -#[rtic_macros::mock_app(device = mock)] -mod app { - #[task(binds = SysTick)] - fn foo(_: foo::Context) {} - - #[task] - async fn foo(_: foo::Context) {} -} diff --git a/macros/ui/task-interrupt.stderr b/macros/ui/task-interrupt.stderr deleted file mode 100644 index 087b6c6..0000000 --- a/macros/ui/task-interrupt.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: this task is defined multiple times - --> ui/task-interrupt.rs:9:14 - | -9 | async fn foo(_: foo::Context) {} - | ^^^ diff --git a/macros/ui/task-no-context.rs b/macros/ui/task-no-context.rs deleted file mode 100644 index 55e8c3b..0000000 --- a/macros/ui/task-no-context.rs +++ /dev/null @@ -1,7 +0,0 @@ -#![no_main] - -#[rtic_macros::mock_app(device = mock)] -mod app { - #[task] - async fn foo() {} -} diff --git a/macros/ui/task-no-context.stderr b/macros/ui/task-no-context.stderr deleted file mode 100644 index 62147aa..0000000 --- a/macros/ui/task-no-context.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: this task handler must have type signature `async fn(foo::Context, ..)` - --> ui/task-no-context.rs:6:14 - | -6 | async fn foo() {} - | ^^^ diff --git a/macros/ui/task-priority-too-high.rs b/macros/ui/task-priority-too-high.rs deleted file mode 100644 index f33ba56..0000000 --- a/macros/ui/task-priority-too-high.rs +++ /dev/null @@ -1,7 +0,0 @@ -#![no_main] - -#[rtic_macros::mock_app(device = mock)] -mod app { - #[task(priority = 256)] - async fn foo(_: foo::Context) {} -} diff --git a/macros/ui/task-priority-too-high.stderr b/macros/ui/task-priority-too-high.stderr deleted file mode 100644 index 5790c88..0000000 --- a/macros/ui/task-priority-too-high.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: this literal must be in the range 0...255 - --> ui/task-priority-too-high.rs:5:23 - | -5 | #[task(priority = 256)] - | ^^^ diff --git a/macros/ui/task-priority-too-low.rs b/macros/ui/task-priority-too-low.rs deleted file mode 100644 index 16e0557..0000000 --- a/macros/ui/task-priority-too-low.rs +++ /dev/null @@ -1,7 +0,0 @@ -#![no_main] - -#[rtic_macros::mock_app(device = mock)] -mod app { - #[task(binds = UART0, priority = 0)] - fn foo(_: foo::Context) {} -} diff --git a/macros/ui/task-priority-too-low.stderr b/macros/ui/task-priority-too-low.stderr deleted file mode 100644 index 85c8660..0000000 --- a/macros/ui/task-priority-too-low.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: hardware tasks are not allowed to be at priority 0 - --> ui/task-priority-too-low.rs:5:38 - | -5 | #[task(binds = UART0, priority = 0)] - | ^ diff --git a/macros/ui/task-pub.rs b/macros/ui/task-pub.rs deleted file mode 100644 index 1ae533f..0000000 --- a/macros/ui/task-pub.rs +++ /dev/null @@ -1,7 +0,0 @@ -#![no_main] - -#[rtic_macros::mock_app(device = mock)] -mod app { - #[task] - pub async fn foo(_: foo::Context) {} -} diff --git a/macros/ui/task-pub.stderr b/macros/ui/task-pub.stderr deleted file mode 100644 index 7b9813d..0000000 --- a/macros/ui/task-pub.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: this task handler must have type signature `async fn(foo::Context, ..)` - --> ui/task-pub.rs:6:18 - | -6 | pub async fn foo(_: foo::Context) {} - | ^^^ diff --git a/macros/ui/task-unsafe.rs b/macros/ui/task-unsafe.rs deleted file mode 100644 index a8383ef..0000000 --- a/macros/ui/task-unsafe.rs +++ /dev/null @@ -1,7 +0,0 @@ -#![no_main] - -#[rtic_macros::mock_app(device = mock)] -mod app { - #[task] - async unsafe fn foo(_: foo::Context) {} -} diff --git a/macros/ui/task-unsafe.stderr b/macros/ui/task-unsafe.stderr deleted file mode 100644 index 90ac76f..0000000 --- a/macros/ui/task-unsafe.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: this task handler must have type signature `async fn(foo::Context, ..)` - --> ui/task-unsafe.rs:6:21 - | -6 | async unsafe fn foo(_: foo::Context) {} - | ^^^ diff --git a/macros/ui/task-zero-prio.rs b/macros/ui/task-zero-prio.rs deleted file mode 100644 index de3c86f..0000000 --- a/macros/ui/task-zero-prio.rs +++ /dev/null @@ -1,19 +0,0 @@ -#![no_main] - -#[rtic_macros::mock_app(device = mock)] -mod app { - #[shared] - struct Shared {} - - #[local] - struct Local {} - - #[init] - fn init(_: init::Context) -> (Shared, Local) {} - - #[task(priority = 0)] - fn foo(_: foo::Context) {} - - #[idle] - fn idle(_: idle::Context) -> ! {} -} diff --git a/macros/ui/task-zero-prio.stderr b/macros/ui/task-zero-prio.stderr deleted file mode 100644 index 1ab9aab..0000000 --- a/macros/ui/task-zero-prio.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: this task handler must have type signature `async fn(foo::Context, ..)` - --> ui/task-zero-prio.rs:15:8 - | -15 | fn foo(_: foo::Context) {} - | ^^^ -- cgit v1.2.3