From 4345c105963cee061acf26bec207fab2859fb164 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Sun, 16 Dec 2018 18:37:36 +0100 Subject: [PATCH 1/4] properly handle #[cfg] (conditional compilation) on resources --- macros/Cargo.toml | 6 +- macros/src/codegen.rs | 234 +++++++++++++++++++++++++---------- macros/src/lib.rs | 4 +- macros/src/syntax.rs | 24 ++-- src/export.rs | 1 - tests/cfail/cfg-resources.rs | 64 ++++++++++ tests/cfail/cfg-static.rs | 57 +++++++++ tests/cpass/cfg.rs | 49 ++++++++ 8 files changed, 362 insertions(+), 77 deletions(-) create mode 100644 tests/cfail/cfg-resources.rs create mode 100644 tests/cfail/cfg-static.rs create mode 100644 tests/cpass/cfg.rs diff --git a/macros/Cargo.toml b/macros/Cargo.toml index 1b45fc8bc8..dd5fb546b8 100644 --- a/macros/Cargo.toml +++ b/macros/Cargo.toml @@ -14,12 +14,12 @@ version = "0.4.0-beta.3" proc-macro = true [dependencies] -quote = "0.6.8" -proc-macro2 = "0.4.20" +quote = "0.6.10" +proc-macro2 = "0.4.24" [dependencies.syn] features = ["extra-traits", "full"] -version = "0.15.6" +version = "0.15.23" [dependencies.rand] default-features = false diff --git a/macros/src/codegen.rs b/macros/src/codegen.rs index 71c6090723..bae61bfc11 100644 --- a/macros/src/codegen.rs +++ b/macros/src/codegen.rs @@ -10,7 +10,7 @@ use std::{ use proc_macro2::Span; use quote::quote; use rand::{Rng, SeedableRng}; -use syn::{ArgCaptured, Ident, IntSuffix, LitInt}; +use syn::{parse_quote, ArgCaptured, Attribute, Ident, IntSuffix, LitInt}; use analyze::{Analysis, Ownership}; use syntax::{App, Idents, Static}; @@ -86,8 +86,6 @@ struct Resources { pub fn app(app: &App, analysis: &Analysis) -> TokenStream { let mut ctxt = Context::default(); - let device = &app.args.device; - let resources = resources(&mut ctxt, &app, analysis); let tasks = tasks(&mut ctxt, &app, analysis); @@ -146,6 +144,7 @@ pub fn app(app: &App, analysis: &Analysis) -> TokenStream { let assertions = assertions(app, analysis); + let main = mk_ident(None); let init = &ctxt.init; quote!( #resources @@ -162,11 +161,7 @@ pub fn app(app: &App, analysis: &Analysis) -> TokenStream { #root_interrupts - // We put these items into a pseudo-module to avoid a collision between the `interrupt` - // import and user code const APP: () = { - use #device::interrupt; - #scoped_interrupts #(#dispatchers)* @@ -178,10 +173,10 @@ pub fn app(app: &App, analysis: &Analysis) -> TokenStream { #idle_fn + #[export_name = "main"] #[allow(unsafe_code)] - #[rtfm::export::entry] #[doc(hidden)] - unsafe fn main() -> ! { + unsafe fn #main() -> ! { #assertions rtfm::export::interrupt::disable(); @@ -204,6 +199,7 @@ fn resources(ctxt: &mut Context, app: &App, analysis: &Analysis) -> proc_macro2: let mut items = vec![]; let mut module = vec![]; for (name, res) in &app.resources { + let cfgs = &res.cfgs; let attrs = &res.attrs; let mut_ = &res.mutability; let ty = &res.ty; @@ -219,6 +215,7 @@ fn resources(ctxt: &mut Context, app: &App, analysis: &Analysis) -> proc_macro2: if let Some(Ownership::Shared { ceiling }) = analysis.ownerships.get(name) { items.push(mk_resource( ctxt, + cfgs, name, quote!(#name), *ceiling, @@ -238,6 +235,7 @@ fn resources(ctxt: &mut Context, app: &App, analysis: &Analysis) -> proc_macro2: .map(|expr| { quote!( #(#attrs)* + #(#cfgs)* #[doc = #symbol] static mut #alias: #ty = #expr; ) @@ -245,6 +243,7 @@ fn resources(ctxt: &mut Context, app: &App, analysis: &Analysis) -> proc_macro2: .unwrap_or_else(|| { quote!( #(#attrs)* + #(#cfgs)* #[doc = #symbol] static mut #alias: rtfm::export::MaybeUninit<#ty> = rtfm::export::MaybeUninit::uninitialized(); @@ -262,6 +261,7 @@ fn resources(ctxt: &mut Context, app: &App, analysis: &Analysis) -> proc_macro2: items.push(mk_resource( ctxt, + cfgs, name, quote!(#ty), *ceiling, @@ -297,6 +297,7 @@ fn init(ctxt: &mut Context, app: &App, analysis: &Analysis) -> proc_macro2::Toke .assigns .iter() .map(|assign| { + let attrs = &assign.attrs; if app .resources .get(&assign.left) @@ -305,11 +306,17 @@ fn init(ctxt: &mut Context, app: &App, analysis: &Analysis) -> proc_macro2::Toke { let alias = &ctxt.statics[&assign.left]; let expr = &assign.right; - quote!(unsafe { #alias.set(#expr); }) + quote!( + #(#attrs)* + unsafe { #alias.set(#expr); } + ) } else { let left = &assign.left; let right = &assign.right; - quote!(#left = #right;) + quote!( + #(#attrs)* + #left = #right; + ) } }) .collect::>(); @@ -607,12 +614,14 @@ fn prelude( // NOTE This field is just to avoid unused type parameter errors around `'a` defs.push(quote!(#[allow(dead_code)] #priority: &'a core::cell::Cell)); - exprs.push(quote!(#priority)); + exprs.push(parse_quote!(#priority)); let mut may_call_lock = false; let mut needs_unsafe = false; for name in resources { let res = &app.resources[name]; + let cfgs = &res.cfgs; + let initialized = res.expr.is_some(); let singleton = res.singleton; let mut_ = res.mutability; @@ -624,23 +633,40 @@ fn prelude( // owned by Init if singleton { needs_unsafe = true; - defs.push(quote!(pub #name: #name)); - exprs.push(quote!(#name: <#name as owned_singleton::Singleton>::new())); + defs.push(quote!( + #(#cfgs)* + pub #name: #name + )); + exprs.push(quote!( + #(#cfgs)* + #name: <#name as owned_singleton::Singleton>::new() + )); continue; } else { - defs.push(quote!(pub #name: &'static #mut_ #ty)); + defs.push(quote!( + #(#cfgs)* + pub #name: &'static #mut_ #ty + )); } } else { // owned by someone else if singleton { needs_unsafe = true; - defs.push(quote!(pub #name: &'a mut #name)); - exprs - .push(quote!(#name: &mut <#name as owned_singleton::Singleton>::new())); + defs.push(quote!( + #(#cfgs)* + pub #name: &'a mut #name + )); + exprs.push(quote!( + #(#cfgs)* + #name: &mut <#name as owned_singleton::Singleton>::new() + )); continue; } else { force_mut = true; - defs.push(quote!(pub #name: &'a mut #ty)); + defs.push(quote!( + #(#cfgs)* + pub #name: &'a mut #ty + )); } } @@ -648,9 +674,15 @@ fn prelude( // Resources assigned to init are always const initialized needs_unsafe = true; if force_mut { - exprs.push(quote!(#name: &mut #alias)); + exprs.push(quote!( + #(#cfgs)* + #name: &mut #alias + )); } else { - exprs.push(quote!(#name: &#mut_ #alias)); + exprs.push(quote!( + #(#cfgs)* + #name: &#mut_ #alias + )); } } else { let ownership = &analysis.ownerships[name]; @@ -661,23 +693,43 @@ fn prelude( if singleton { if mut_.is_none() { needs_unsafe = true; - defs.push(quote!(pub #name: &'a #name)); - exprs - .push(quote!(#name: &<#name as owned_singleton::Singleton>::new())); + defs.push(quote!( + #(#cfgs)* + pub #name: &'a #name + )); + exprs.push(quote!( + #(#cfgs)* + #name: &<#name as owned_singleton::Singleton>::new() + )); continue; } else { // Generate a resource proxy - defs.push(quote!(pub #name: resources::#name<'a>)); - exprs.push(quote!(#name: resources::#name { #priority })); + defs.push(quote!( + #(#cfgs)* + pub #name: resources::#name<'a> + )); + exprs.push(quote!( + #(#cfgs)* + #name: resources::#name { #priority } + )); continue; } } else { if mut_.is_none() { - defs.push(quote!(pub #name: &'a #ty)); + defs.push(quote!( + #(#cfgs)* + pub #name: &'a #ty + )); } else { // Generate a resource proxy - defs.push(quote!(pub #name: resources::#name<'a>)); - exprs.push(quote!(#name: resources::#name { #priority })); + defs.push(quote!( + #(#cfgs)* + pub #name: resources::#name<'a> + )); + exprs.push(quote!( + #(#cfgs)* + #name: resources::#name { #priority } + )); continue; } } @@ -685,29 +737,47 @@ fn prelude( if singleton { if kind.runs_once() { needs_unsafe = true; - defs.push(quote!(pub #name: #name)); - exprs.push(quote!(#name: <#name as owned_singleton::Singleton>::new())); + defs.push(quote!( + #(#cfgs)* + pub #name: #name + )); + exprs.push(quote!( + #(#cfgs)* + #name: <#name as owned_singleton::Singleton>::new() + )); } else { needs_unsafe = true; if ownership.is_owned() || mut_.is_none() { - defs.push(quote!(pub #name: &'a #mut_ #name)); - let alias = mk_ident(None); - items.push(quote!( - let #mut_ #alias = unsafe { - <#name as owned_singleton::Singleton>::new() - }; + defs.push(quote!( + #(#cfgs)* + pub #name: &'a #mut_ #name )); - exprs.push(quote!(#name: &#mut_ #alias)); - } else { - may_call_lock = true; - defs.push(quote!(pub #name: rtfm::Exclusive<'a, #name>)); let alias = mk_ident(None); items.push(quote!( + #(#cfgs)* let #mut_ #alias = unsafe { <#name as owned_singleton::Singleton>::new() }; )); exprs.push(quote!( + #(#cfgs)* + #name: &#mut_ #alias + )); + } else { + may_call_lock = true; + defs.push(quote!( + #(#cfgs)* + pub #name: rtfm::Exclusive<'a, #name> + )); + let alias = mk_ident(None); + items.push(quote!( + #(#cfgs)* + let #mut_ #alias = unsafe { + <#name as owned_singleton::Singleton>::new() + }; + )); + exprs.push(quote!( + #(#cfgs)* #name: rtfm::Exclusive(&mut #alias) )); } @@ -715,11 +785,17 @@ fn prelude( continue; } else { if ownership.is_owned() || mut_.is_none() { - defs.push(quote!(pub #name: &#lt #mut_ #ty)); + defs.push(quote!( + #(#cfgs)* + pub #name: &#lt #mut_ #ty + )); } else { exclusive = true; may_call_lock = true; - defs.push(quote!(pub #name: rtfm::Exclusive<#lt, #ty>)); + defs.push(quote!( + #(#cfgs)* + pub #name: rtfm::Exclusive<#lt, #ty> + )); } } } @@ -728,9 +804,15 @@ fn prelude( needs_unsafe = true; if initialized { if exclusive { - exprs.push(quote!(#name: rtfm::Exclusive(&mut #alias))); + exprs.push(quote!( + #(#cfgs)* + #name: rtfm::Exclusive(&mut #alias) + )); } else { - exprs.push(quote!(#name: &#mut_ #alias)); + exprs.push(quote!( + #(#cfgs)* + #name: &#mut_ #alias + )); } } else { let method = if mut_.is_some() { @@ -740,9 +822,15 @@ fn prelude( }; if exclusive { - exprs.push(quote!(#name: rtfm::Exclusive(#alias.#method()) )); + exprs.push(quote!( + #(#cfgs)* + #name: rtfm::Exclusive(#alias.#method()) + )); } else { - exprs.push(quote!(#name: #alias.#method() )); + exprs.push(quote!( + #(#cfgs)* + #name: #alias.#method() + )); } } } @@ -755,6 +843,7 @@ fn prelude( None }; + let defs = &defs; let doc = format!("`{}::Resources`", kind.ident().to_string()); let decl = quote!( #[doc = #doc] @@ -893,7 +982,6 @@ fn exceptions(ctxt: &mut Context, app: &App, analysis: &Analysis) -> Vec Vec quote!(), }; + let locals = mk_locals(&exception.statics, false); + let symbol = ident.to_string(); + let alias = mk_ident(None); let unsafety = &exception.unsafety; quote!( #module - #[rtfm::export::exception] #[doc(hidden)] + #[export_name = #symbol] #(#attrs)* - #unsafety fn #ident() { - #(#statics)* + #unsafety fn #alias() { + #(#locals)* #baseline_let @@ -967,9 +1058,9 @@ fn interrupts( let mut root = vec![]; let mut scoped = vec![]; + let device = &app.args.device; for (ident, interrupt) in &app.interrupts { let attrs = &interrupt.attrs; - let statics = &interrupt.statics; let stmts = &interrupt.stmts; let prelude = prelude( @@ -1010,12 +1101,18 @@ fn interrupts( () => quote!(), }; + let locals = mk_locals(&interrupt.statics, false); + let alias = mk_ident(None); + let symbol = ident.to_string(); let unsafety = &interrupt.unsafety; scoped.push(quote!( - #[interrupt] #(#attrs)* - #unsafety fn #ident() { - #(#statics)* + #[export_name = #symbol] + #unsafety fn #alias() { + // check that this interrupt exists + let _ = #device::interrupt::#ident; + + #(#locals)* #baseline_let @@ -1053,6 +1150,7 @@ fn tasks(ctxt: &mut Context, app: &App, analysis: &Analysis) -> proc_macro2::Tok let resource = mk_resource( ctxt, + &[], &free_alias, quote!(rtfm::export::FreeQueue<#capacity_ty>), *analysis.free_queues.get(name).unwrap_or(&0), @@ -1182,6 +1280,7 @@ fn dispatchers( let mut data = vec![]; let mut dispatchers = vec![]; + let device = &app.args.device; for (level, dispatcher) in &analysis.dispatchers { let ready_alias = mk_ident(None); let enum_alias = mk_ident(None); @@ -1194,6 +1293,7 @@ fn dispatchers( let ceiling = *analysis.ready_queues.get(&level).unwrap_or(&0); let resource = mk_resource( ctxt, + &[], &ready_alias, ty.clone(), ceiling, @@ -1212,8 +1312,6 @@ fn dispatchers( #resource )); - let interrupt = &dispatcher.interrupt; - let arms = dispatcher .tasks .iter() @@ -1254,12 +1352,18 @@ fn dispatchers( .collect::>(); let attrs = &dispatcher.attrs; + let interrupt = &dispatcher.interrupt; + let symbol = interrupt.to_string(); + let alias = mk_ident(None); dispatchers.push(quote!( #(#attrs)* - #[interrupt] - unsafe fn #interrupt() { + #[export_name = #symbol] + unsafe fn #alias() { use core::ptr; + // check that this interrupt exists + let _ = #device::interrupt::#interrupt; + rtfm::export::run(|| { while let Some((task, index)) = #ready_alias.get_mut().split().1.dequeue() { match task { @@ -1519,6 +1623,7 @@ fn timer_queue(ctxt: &Context, app: &App, analysis: &Analysis) -> proc_macro2::T items.push(mk_resource( ctxt, + &[], tq, quote!(rtfm::export::TimerQueue<#enum_, #cap>), analysis.timer_queue.ceiling, @@ -1551,10 +1656,11 @@ fn timer_queue(ctxt: &Context, app: &App, analysis: &Analysis) -> proc_macro2::T .collect::>(); let logical_prio = analysis.timer_queue.priority; + let alias = mk_ident(None); items.push(quote!( - #[rtfm::export::exception] + #[export_name = "SysTick"] #[doc(hidden)] - unsafe fn SysTick() { + unsafe fn #alias() { use rtfm::Mutex; let ref #priority = core::cell::Cell::new(#logical_prio); @@ -1680,6 +1786,7 @@ fn assertions(app: &App, analysis: &Analysis) -> proc_macro2::TokenStream { fn mk_resource( ctxt: &Context, + cfgs: &[Attribute], struct_: &Ident, ty: proc_macro2::TokenStream, ceiling: u8, @@ -1696,6 +1803,7 @@ fn mk_resource( let doc = format!("`{}`", ty); module.push(quote!( #[doc = #doc] + #(#cfgs)* pub struct #struct_<'a> { #[doc(hidden)] pub #priority: &'a core::cell::Cell, @@ -1705,6 +1813,7 @@ fn mk_resource( quote!(resources::#struct_) } else { items.push(quote!( + #(#cfgs)* struct #struct_<'a> { #priority: &'a core::cell::Cell, } @@ -1714,6 +1823,7 @@ fn mk_resource( }; items.push(quote!( + #(#cfgs)* impl<'a> rtfm::Mutex for #path<'a> { type T = #ty; @@ -1852,7 +1962,7 @@ fn tuple_ty(inputs: &[ArgCaptured]) -> proc_macro2::TokenStream { } } -#[derive(Clone, Eq, Hash, PartialEq)] +#[derive(Clone, Debug, Eq, Hash, PartialEq)] enum Kind { Exception(Ident), Idle, diff --git a/macros/src/lib.rs b/macros/src/lib.rs index e382b410dd..2e32da6585 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -1,4 +1,4 @@ -// #![deny(warnings)] +#![deny(warnings)] #![recursion_limit = "128"] extern crate proc_macro; @@ -308,5 +308,5 @@ pub fn app(args: TokenStream, input: TokenStream) -> TokenStream { let analysis = analyze::app(&app); // Code generation - codegen::app(&app, &analysis) + codegen::app(&app, &analysis).into() } diff --git a/macros/src/syntax.rs b/macros/src/syntax.rs index c1b0d5d279..0e6c606b36 100644 --- a/macros/src/syntax.rs +++ b/macros/src/syntax.rs @@ -47,7 +47,7 @@ impl Parse for AppArgs { return Err(parse::Error::new( ident.span(), "expected `device`; other keys are not accepted", - )) + )); } } @@ -228,7 +228,7 @@ impl App { return Err(parse::Error::new( item.span(), "this item must live outside the `#[app]` module", - )) + )); } } } @@ -526,7 +526,7 @@ impl Parse for InitArgs { return Err(parse::Error::new( ident.span(), "expected one of: resources, schedule or spawn", - )) + )); } } @@ -597,6 +597,7 @@ impl Parse for InitArgs { } pub struct Assign { + pub attrs: Vec, pub left: Ident, pub right: Box, } @@ -649,7 +650,7 @@ pub struct Exception { pub args: ExceptionArgs, pub attrs: Vec, pub unsafety: Option, - pub statics: Statics, + pub statics: HashMap, pub stmts: Vec, } @@ -727,7 +728,7 @@ impl Exception { args, attrs: item.attrs, unsafety: item.unsafety, - statics, + statics: Static::parse(statics)?, stmts, }) } @@ -737,7 +738,7 @@ pub struct Interrupt { pub args: InterruptArgs, pub attrs: Vec, pub unsafety: Option, - pub statics: Statics, + pub statics: HashMap, pub stmts: Vec, } @@ -780,7 +781,7 @@ impl Interrupt { args, attrs: item.attrs, unsafety: item.unsafety, - statics, + statics: Static::parse(statics)?, stmts, }) } @@ -788,6 +789,7 @@ impl Interrupt { pub struct Resource { pub singleton: bool, + pub cfgs: Vec, pub attrs: Vec, pub mutability: Option, pub ty: Box, @@ -817,9 +819,12 @@ impl Resource { ); } + let (cfgs, attrs) = extract_cfgs(item.attrs); + Ok(Resource { singleton: pos.is_some(), - attrs: item.attrs, + cfgs, + attrs, mutability: item.mutability, ty: item.ty, expr: if uninitialized { None } else { Some(item.expr) }, @@ -981,7 +986,7 @@ fn parse_args(input: ParseStream, accept_capacity: bool) -> parse::Result) -> (Vec, Vec) { if let Expr::Path(ref expr) = *assign.left { if expr.path.segments.len() == 1 { assigns.push(Assign { + attrs: assign.attrs, left: expr.path.segments[0].ident.clone(), right: assign.right, }); diff --git a/src/export.rs b/src/export.rs index c1eeb87bac..ed40b62164 100644 --- a/src/export.rs +++ b/src/export.rs @@ -10,7 +10,6 @@ pub use cortex_m::{ asm::wfi, interrupt, peripheral::scb::SystemHandler, peripheral::syst::SystClkSource, peripheral::Peripherals, }; -pub use cortex_m_rt::{entry, exception}; pub use heapless::consts; use heapless::spsc::{Queue, SingleCore}; diff --git a/tests/cfail/cfg-resources.rs b/tests/cfail/cfg-resources.rs new file mode 100644 index 0000000000..dee1485b8e --- /dev/null +++ b/tests/cfail/cfg-resources.rs @@ -0,0 +1,64 @@ +#![no_main] +#![no_std] + +extern crate lm3s6965; +extern crate panic_halt; +extern crate rtfm; + +use rtfm::app; + +#[app(device = lm3s6965)] +const APP: () = { + #[cfg(never)] + static mut O1: u32 = 0; // init + #[cfg(never)] + static mut O2: u32 = 0; // idle + #[cfg(never)] + static mut O3: u32 = 0; // EXTI0 + #[cfg(never)] + static O4: u32 = 0; // idle + #[cfg(never)] + static O5: u32 = 0; // EXTI1 + #[cfg(never)] + static O6: u32 = 0; // init + + #[cfg(never)] + static mut S1: u32 = 0; // idle & EXTI0 + #[cfg(never)] + static mut S2: u32 = 0; // EXTI0 & EXTI1 + #[cfg(never)] + static S3: u32 = 0; + + #[init(resources = [O1, O4, O5, O6, S3])] + fn init() { + resources.O1; //~ ERROR no field `O1` + resources.O4; //~ ERROR no field `O4` + resources.O5; //~ ERROR no field `O5` + resources.O6; //~ ERROR no field `O6` + resources.S3; //~ ERROR no field `S3` + } + + #[idle(resources = [O2, O4, S1, S3])] + fn idle() -> ! { + resources.O2; //~ ERROR no field `O2` + resources.O4; //~ ERROR no field `O4` + resources.S1; //~ ERROR no field `S1` + resources.S3; //~ ERROR no field `S3` + + loop {} + } + + #[interrupt(resources = [O3, S1, S2, S3])] + fn UART0() { + resources.O3; //~ ERROR no field `O3` + resources.S1; //~ ERROR no field `S1` + resources.S2; //~ ERROR no field `S2` + resources.S3; //~ ERROR no field `S3` + } + + #[interrupt(resources = [S2, O5])] + fn UART1() { + resources.S2; //~ ERROR no field `S2` + resources.O5; //~ ERROR no field `O5` + } +}; diff --git a/tests/cfail/cfg-static.rs b/tests/cfail/cfg-static.rs new file mode 100644 index 0000000000..0d27e53398 --- /dev/null +++ b/tests/cfail/cfg-static.rs @@ -0,0 +1,57 @@ +#![no_main] +#![no_std] + +extern crate lm3s6965; +extern crate panic_halt; +extern crate rtfm; + +use rtfm::app; + +#[app(device = lm3s6965)] +const APP: () = { + #[init] + fn init() { + #[cfg(never)] + static mut FOO: u32 = 0; + + FOO; //~ ERROR cannot find value `FOO` in this scope + } + + #[idle] + fn idle() -> ! { + #[cfg(never)] + static mut FOO: u32 = 0; + + FOO; //~ ERROR cannot find value `FOO` in this scope + + loop {} + } + + #[exception] + fn SVCall() { + #[cfg(never)] + static mut FOO: u32 = 0; + + FOO; //~ ERROR cannot find value `FOO` in this scope + } + + #[interrupt] + fn UART0() { + #[cfg(never)] + static mut FOO: u32 = 0; + + FOO; //~ ERROR cannot find value `FOO` in this scope + } + + #[task] + fn foo() { + #[cfg(never)] + static mut FOO: u32 = 0; + + FOO; //~ ERROR cannot find value `FOO` in this scope + } + + extern "C" { + fn UART1(); + } +}; diff --git a/tests/cpass/cfg.rs b/tests/cpass/cfg.rs new file mode 100644 index 0000000000..e2ff984359 --- /dev/null +++ b/tests/cpass/cfg.rs @@ -0,0 +1,49 @@ +//! Compile-pass test that checks that `#[cfg]` attributes are respected + +#![deny(unsafe_code)] +#![deny(warnings)] +#![no_main] +#![no_std] + +extern crate lm3s6965; +extern crate panic_semihosting; +extern crate rtfm; + +use rtfm::app; + +#[app(device = lm3s6965)] +const APP: () = { + #[cfg(never)] + static mut FOO: u32 = 0; + + #[init] + fn init() { + #[cfg(never)] + static mut BAR: u32 = 0; + } + + #[idle] + fn idle() -> ! { + #[cfg(never)] + static mut BAR: u32 = 0; + + loop {} + } + + #[task(resources = [FOO])] + fn foo() { + #[cfg(never)] + static mut BAR: u32 = 0; + } + + #[task(priority = 3, resources = [FOO])] + fn bar() { + #[cfg(never)] + static mut BAR: u32 = 0; + } + + extern "C" { + fn UART0(); + fn UART1(); + } +}; From 8e9a91d0b09313eee0f7fa44cc827dced0ea1806 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Sun, 16 Dec 2018 19:10:36 +0100 Subject: [PATCH 2/4] properly handle `#[cfg]` (conditional compilation) on tasks --- macros/src/check.rs | 3 +- macros/src/codegen.rs | 76 ++++++++++++++++++++++++++++++++++--------- macros/src/syntax.rs | 5 ++- tests/cpass/cfg.rs | 8 +++-- 4 files changed, 72 insertions(+), 20 deletions(-) diff --git a/macros/src/check.rs b/macros/src/check.rs index f28322074e..85184596be 100644 --- a/macros/src/check.rs +++ b/macros/src/check.rs @@ -73,7 +73,8 @@ pub fn app(app: &App) -> parse::Result<()> { app.tasks .values() .flat_map(|t| t.args.schedule.iter().chain(&t.args.spawn)), - ) { + ) + { if !app.tasks.contains_key(task) { return Err(parse::Error::new( task.span(), diff --git a/macros/src/codegen.rs b/macros/src/codegen.rs index bae61bfc11..eafea945c3 100644 --- a/macros/src/codegen.rs +++ b/macros/src/codegen.rs @@ -1210,7 +1210,6 @@ fn tasks(ctxt: &mut Context, app: &App, analysis: &Analysis) -> proc_macro2::Tok // second pass to generate the actual task function for (name, task) in &app.tasks { - let attrs = &task.attrs; let inputs = &task.inputs; let locals = mk_locals(&task.statics, false); let stmts = &task.stmts; @@ -1245,6 +1244,8 @@ fn tasks(ctxt: &mut Context, app: &App, analysis: &Analysis) -> proc_macro2::Tok app, )); + let attrs = &task.attrs; + let cfgs = &task.cfgs; let task_alias = &ctxt.tasks[name].alias; let baseline_arg = match () { #[cfg(feature = "timer-queue")] @@ -1257,6 +1258,7 @@ fn tasks(ctxt: &mut Context, app: &App, analysis: &Analysis) -> proc_macro2::Tok }; items.push(quote!( #(#attrs)* + #(#cfgs)* #unsafety fn #task_alias(#baseline_arg #(#inputs,)*) { #(#locals)* @@ -1284,9 +1286,21 @@ fn dispatchers( for (level, dispatcher) in &analysis.dispatchers { let ready_alias = mk_ident(None); let enum_alias = mk_ident(None); - let tasks = &dispatcher.tasks; let capacity = mk_typenum_capacity(dispatcher.capacity, true); + let variants = dispatcher + .tasks + .iter() + .map(|task| { + let task_ = &app.tasks[task]; + let cfgs = &task_.cfgs; + + quote!( + #(#cfgs)* + #task + ) + }) + .collect::>(); let symbol = format!("P{}::READY_QUEUE::{}", level, ready_alias); let e = quote!(rtfm::export); let ty = quote!(#e::ReadyQueue<#enum_alias, #capacity>); @@ -1304,7 +1318,7 @@ fn dispatchers( data.push(quote!( #[allow(dead_code)] #[allow(non_camel_case_types)] - enum #enum_alias { #(#tasks,)* } + enum #enum_alias { #(#variants,)* } #[doc = #symbol] static mut #ready_alias: #e::MaybeUninit<#ty> = #e::MaybeUninit::uninitialized(); @@ -1319,9 +1333,12 @@ fn dispatchers( let task_ = &ctxt.tasks[task]; let inputs = &task_.inputs; let free = &task_.free_queue; - let pats = tuple_pat(&app.tasks[task].inputs); let alias = &task_.alias; + let task__ = &app.tasks[task]; + let pats = tuple_pat(&task__.inputs); + let cfgs = &task__.cfgs; + let baseline_let; let call; match () { @@ -1341,13 +1358,16 @@ fn dispatchers( } }; - quote!(#enum_alias::#task => { - #baseline_let - let input = ptr::read(#inputs.get_ref().get_unchecked(usize::from(index))); - #free.get_mut().split().0.enqueue_unchecked(index); - let (#pats) = input; - #call - }) + quote!( + #(#cfgs)* + #enum_alias::#task => { + #baseline_let + let input = ptr::read(#inputs.get_ref().get_unchecked(usize::from(index))); + #free.get_mut().split().0.enqueue_unchecked(index); + let (#pats) = input; + #call + } + ) }) .collect::>(); @@ -1397,6 +1417,7 @@ fn spawn(ctxt: &Context, app: &App, analysis: &Analysis) -> proc_macro2::TokenSt for (name, task) in &ctxt.tasks { let alias = &task.spawn_fn; let task_ = &app.tasks[name]; + let cfgs = &task_.cfgs; let free = &task.free_queue; let level = task_.args.priority; let dispatcher = &ctxt.dispatchers[&level]; @@ -1432,6 +1453,7 @@ fn spawn(ctxt: &Context, app: &App, analysis: &Analysis) -> proc_macro2::TokenSt items.push(quote!( #[inline(always)] + #(#cfgs)* unsafe fn #alias( #baseline_arg #priority: &core::cell::Cell, @@ -1470,8 +1492,10 @@ fn spawn(ctxt: &Context, app: &App, analysis: &Analysis) -> proc_macro2::TokenSt let mut methods = vec![]; for task in spawn { + let task_ = &app.tasks[task]; let alias = &ctxt.tasks[task].spawn_fn; - let inputs = &app.tasks[task].inputs; + let inputs = &task_.inputs; + let cfgs = &task_.cfgs; let ty = tuple_ty(inputs); let pats = tuple_pat(inputs); @@ -1490,6 +1514,7 @@ fn spawn(ctxt: &Context, app: &App, analysis: &Analysis) -> proc_macro2::TokenSt methods.push(quote!( #[allow(unsafe_code)] #[inline] + #(#cfgs)* pub fn #task(&self, #(#inputs,)*) -> Result<(), #ty> { unsafe { #alias(#instant &self.#priority, #pats) } } @@ -1519,12 +1544,15 @@ fn schedule(ctxt: &Context, app: &App) -> proc_macro2::TokenStream { let enum_ = &ctxt.schedule_enum; let inputs = &task_.inputs; let scheduleds = &task_.scheduleds; - let args = &app.tasks[task].inputs; + let task__ = &app.tasks[task]; + let args = &task__.inputs; + let cfgs = &task__.cfgs; let ty = tuple_ty(args); let pats = tuple_pat(args); items.push(quote!( #[inline(always)] + #(#cfgs)* unsafe fn #alias( #priority: &core::cell::Cell, instant: rtfm::Instant, @@ -1568,12 +1596,15 @@ fn schedule(ctxt: &Context, app: &App) -> proc_macro2::TokenStream { let mut methods = vec![]; for task in schedule { let alias = &ctxt.schedule_fn[task]; - let inputs = &app.tasks[task].inputs; + let task_ = &app.tasks[task]; + let inputs = &task_.inputs; + let cfgs = &task_.cfgs; let ty = tuple_ty(inputs); let pats = tuple_pat(inputs); methods.push(quote!( #[inline] + #(#cfgs)* pub fn #task( &self, instant: rtfm::Instant, @@ -1603,12 +1634,22 @@ fn timer_queue(ctxt: &Context, app: &App, analysis: &Analysis) -> proc_macro2::T let mut items = vec![]; + let variants = tasks + .iter() + .map(|task| { + let cfgs = &app.tasks[task].cfgs; + quote!( + #(#cfgs)* + #task + ) + }) + .collect::>(); let enum_ = &ctxt.schedule_enum; items.push(quote!( #[allow(dead_code)] #[allow(non_camel_case_types)] #[derive(Clone, Copy)] - enum #enum_ { #(#tasks,)* } + enum #enum_ { #(#variants,)* } )); let cap = mk_typenum_capacity(analysis.timer_queue.capacity, false); @@ -1637,13 +1678,16 @@ fn timer_queue(ctxt: &Context, app: &App, analysis: &Analysis) -> proc_macro2::T let arms = tasks .iter() .map(|task| { - let level = app.tasks[task].args.priority; + let task_ = &app.tasks[task]; + let level = task_.args.priority; + let cfgs = &task_.cfgs; let dispatcher_ = &ctxt.dispatchers[&level]; let tenum = &dispatcher_.enum_; let ready = &dispatcher_.ready_queue; let dispatcher = &analysis.dispatchers[&level].interrupt; quote!( + #(#cfgs)* #enum_::#task => { (#ready { #priority }).lock(|rq| { rq.split().0.enqueue_unchecked((#tenum::#task, index)) diff --git a/macros/src/syntax.rs b/macros/src/syntax.rs index 0e6c606b36..b9424fbe16 100644 --- a/macros/src/syntax.rs +++ b/macros/src/syntax.rs @@ -1047,6 +1047,7 @@ impl Static { pub struct Task { pub args: TaskArgs, + pub cfgs: Vec, pub attrs: Vec, pub unsafety: Option, pub inputs: Vec, @@ -1098,9 +1099,11 @@ impl Task { _ => {} } + let (cfgs, attrs) = extract_cfgs(item.attrs); Ok(Task { args, - attrs: item.attrs, + cfgs, + attrs, unsafety: item.unsafety, inputs, statics: Static::parse(statics)?, diff --git a/tests/cpass/cfg.rs b/tests/cpass/cfg.rs index e2ff984359..c91ab60465 100644 --- a/tests/cpass/cfg.rs +++ b/tests/cpass/cfg.rs @@ -30,18 +30,22 @@ const APP: () = { loop {} } - #[task(resources = [FOO])] + #[task(resources = [FOO], schedule = [quux], spawn = [quux])] fn foo() { #[cfg(never)] static mut BAR: u32 = 0; } - #[task(priority = 3, resources = [FOO])] + #[task(priority = 3, resources = [FOO], schedule = [quux], spawn = [quux])] fn bar() { #[cfg(never)] static mut BAR: u32 = 0; } + #[cfg(never)] + #[task] + fn quux() {} + extern "C" { fn UART0(); fn UART1(); From 56d09a12dd645166af7d6def6b95bf71ae7962bd Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Sun, 16 Dec 2018 19:13:22 +0100 Subject: [PATCH 3/4] move macros crate to the 2018 edition --- macros/Cargo.toml | 1 + macros/src/analyze.rs | 2 +- macros/src/check.rs | 6 +++--- macros/src/codegen.rs | 6 ++++-- macros/src/lib.rs | 4 ---- macros/src/syntax.rs | 24 ++++++++++++------------ 6 files changed, 21 insertions(+), 22 deletions(-) diff --git a/macros/Cargo.toml b/macros/Cargo.toml index dd5fb546b8..4c4b734b23 100644 --- a/macros/Cargo.toml +++ b/macros/Cargo.toml @@ -3,6 +3,7 @@ authors = ["Jorge Aparicio "] categories = ["concurrency", "embedded", "no-std"] description = "Procedural macros of the cortex-m-rtfm crate" documentation = "https://japaric.github.io/cortex-m-rtfm/api/cortex_m_rtfm" +edition = "2018" keywords = ["arm", "cortex-m"] license = "MIT OR Apache-2.0" name = "cortex-m-rtfm-macros" diff --git a/macros/src/analyze.rs b/macros/src/analyze.rs index 869b5d2060..19575b7728 100644 --- a/macros/src/analyze.rs +++ b/macros/src/analyze.rs @@ -5,7 +5,7 @@ use std::{ use syn::{Attribute, Ident, Type}; -use syntax::{App, Idents}; +use crate::syntax::{App, Idents}; pub type Ownerships = HashMap; diff --git a/macros/src/check.rs b/macros/src/check.rs index 85184596be..ae2262a859 100644 --- a/macros/src/check.rs +++ b/macros/src/check.rs @@ -3,14 +3,14 @@ use std::{collections::HashSet, iter}; use proc_macro2::Span; use syn::parse; -use syntax::App; +use crate::syntax::App; pub fn app(app: &App) -> parse::Result<()> { // Check that all referenced resources have been declared for res in app .idle .as_ref() - .map(|idle| -> Box> { Box::new(idle.args.resources.iter()) }) + .map(|idle| -> Box> { Box::new(idle.args.resources.iter()) }) .unwrap_or_else(|| Box::new(iter::empty())) .chain(&app.init.args.resources) .chain(app.exceptions.values().flat_map(|e| &e.args.resources)) @@ -53,7 +53,7 @@ pub fn app(app: &App) -> parse::Result<()> { for task in app .idle .as_ref() - .map(|idle| -> Box> { + .map(|idle| -> Box> { Box::new(idle.args.schedule.iter().chain(&idle.args.spawn)) }) .unwrap_or_else(|| Box::new(iter::empty())) diff --git a/macros/src/codegen.rs b/macros/src/codegen.rs index eafea945c3..45c3e263f7 100644 --- a/macros/src/codegen.rs +++ b/macros/src/codegen.rs @@ -12,8 +12,10 @@ use quote::quote; use rand::{Rng, SeedableRng}; use syn::{parse_quote, ArgCaptured, Attribute, Ident, IntSuffix, LitInt}; -use analyze::{Analysis, Ownership}; -use syntax::{App, Idents, Static}; +use crate::{ + analyze::{Analysis, Ownership}, + syntax::{App, Idents, Static}, +}; // NOTE to avoid polluting the user namespaces we map some identifiers to pseudo-hygienic names. // In some instances we also use the pseudo-hygienic names for safety, for example the user should diff --git a/macros/src/lib.rs b/macros/src/lib.rs index 2e32da6585..c8d9fee190 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -2,10 +2,6 @@ #![recursion_limit = "128"] extern crate proc_macro; -extern crate proc_macro2; -extern crate quote; -extern crate rand; -extern crate syn; use proc_macro::TokenStream; use syn::parse_macro_input; diff --git a/macros/src/syntax.rs b/macros/src/syntax.rs index b9424fbe16..85f3caaa90 100644 --- a/macros/src/syntax.rs +++ b/macros/src/syntax.rs @@ -20,7 +20,7 @@ pub struct AppArgs { } impl Parse for AppArgs { - fn parse(input: ParseStream) -> parse::Result { + fn parse(input: ParseStream<'_>) -> parse::Result { let mut device = None; loop { if input.is_empty() { @@ -80,8 +80,8 @@ pub struct Input { } impl Parse for Input { - fn parse(input: ParseStream) -> parse::Result { - fn parse_items(input: ParseStream) -> parse::Result> { + fn parse(input: ParseStream<'_>) -> parse::Result { + fn parse_items(input: ParseStream<'_>) -> parse::Result> { let mut items = vec![]; while !input.is_empty() { @@ -254,7 +254,7 @@ impl App { pub fn resource_accesses(&self) -> impl Iterator { self.idle .as_ref() - .map(|idle| -> Box> { + .map(|idle| -> Box> { Box::new(idle.args.resources.iter().map(|res| (0, res))) }) .unwrap_or_else(|| Box::new(iter::empty())) @@ -293,7 +293,7 @@ impl App { .chain( self.idle .as_ref() - .map(|idle| -> Box> { + .map(|idle| -> Box> { Box::new(idle.args.spawn.iter().map(|s| (Some(0), s))) }) .unwrap_or_else(|| Box::new(iter::empty())), @@ -329,7 +329,7 @@ impl App { .chain( self.idle .as_ref() - .map(|idle| -> Box> { + .map(|idle| -> Box> { Box::new(idle.args.schedule.iter().map(|s| (Some(0), s))) }) .unwrap_or_else(|| Box::new(iter::empty())), @@ -358,7 +358,7 @@ impl App { pub fn schedule_callers(&self) -> impl Iterator { self.idle .as_ref() - .map(|idle| -> Box> { + .map(|idle| -> Box> { Box::new(iter::once(( Ident::new("idle", Span::call_site()), &idle.args.schedule, @@ -389,7 +389,7 @@ impl App { pub fn spawn_callers(&self) -> impl Iterator { self.idle .as_ref() - .map(|idle| -> Box> { + .map(|idle| -> Box> { Box::new(iter::once(( Ident::new("idle", Span::call_site()), &idle.args.spawn, @@ -492,7 +492,7 @@ impl Default for InitArgs { } impl Parse for InitArgs { - fn parse(input: ParseStream) -> parse::Result { + fn parse(input: ParseStream<'_>) -> parse::Result { if input.is_empty() { return Ok(InitArgs::default()); } @@ -662,7 +662,7 @@ pub struct ExceptionArgs { } impl Parse for ExceptionArgs { - fn parse(input: ParseStream) -> parse::Result { + fn parse(input: ParseStream<'_>) -> parse::Result { parse_args(input, false).map( |TaskArgs { priority, @@ -853,13 +853,13 @@ impl Default for TaskArgs { } impl Parse for TaskArgs { - fn parse(input: ParseStream) -> parse::Result { + fn parse(input: ParseStream<'_>) -> parse::Result { parse_args(input, true) } } // Parser shared by TaskArgs and ExceptionArgs / InterruptArgs -fn parse_args(input: ParseStream, accept_capacity: bool) -> parse::Result { +fn parse_args(input: ParseStream<'_>, accept_capacity: bool) -> parse::Result { if input.is_empty() { return Ok(TaskArgs::default()); } From d35f5bc0b0453c2e98b8398d36bd2ba553edce45 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Sun, 16 Dec 2018 19:16:19 +0100 Subject: [PATCH 4/4] use edition idioms in the top crate --- src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 943413fd01..213037b7e5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -295,7 +295,7 @@ impl<'a, T> fmt::Debug for Exclusive<'a, T> where T: fmt::Debug, { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { (**self).fmt(f) } } @@ -304,7 +304,7 @@ impl<'a, T> fmt::Display for Exclusive<'a, T> where T: fmt::Display, { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { (**self).fmt(f) } }