From 7fdf16eab948ea04c1e56fdb5a704ed88780f5c6 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Sun, 8 Apr 2018 18:23:27 +0200 Subject: [PATCH] update parser closes #69 this doesn't change functionality per se but improves diagnostics in some cases. Some hard errors have becomes warnings, for example: when `resources` is empty, or when `idle.path` is set to the default `idle` path. --- ci/install.sh | 9 +-- ci/script.sh | 8 +-- macros/Cargo.toml | 9 +-- macros/src/analyze.rs | 2 +- macros/src/check.rs | 91 +++---------------------- macros/src/lib.rs | 21 +++--- macros/src/trans.rs | 46 +++++++------ tests/cfail/init-resource-share-idle.rs | 2 +- tests/cfail/init-resource-share-task.rs | 2 +- tests/cfail/interrupt.rs | 4 +- tests/cfail/priority-too-high.rs | 1 + tests/cfail/priority-too-low.rs | 1 + tests/cfail/resource-not-send-sync.rs | 4 +- 13 files changed, 61 insertions(+), 139 deletions(-) diff --git a/ci/install.sh b/ci/install.sh index dd7de9e19b..8bc6a4708a 100644 --- a/ci/install.sh +++ b/ci/install.sh @@ -1,14 +1,7 @@ set -euxo pipefail main() { - case $TARGET in - thumbv*-none-eabi*) - cargo install --list | grep 'xargo v0.3.8' || \ - cargo install xargo --vers 0.3.8 - rustup component list | grep 'rust-src.*installed' || \ - rustup component add rust-src - ;; - esac + . } main diff --git a/ci/script.sh b/ci/script.sh index 222a1e0263..9e91aad5e8 100644 --- a/ci/script.sh +++ b/ci/script.sh @@ -9,13 +9,13 @@ main() { case $TARGET in thumbv7em-none-eabi*) - xargo check --target $TARGET --features cm7-r0p1 - xargo check --target $TARGET --features cm7-r0p1 --examples + cargo check --target $TARGET --features cm7-r0p1 + cargo check --target $TARGET --features cm7-r0p1 --examples ;; esac - xargo check --target $TARGET - xargo check --target $TARGET --examples + cargo check --target $TARGET + cargo check --target $TARGET --examples } main diff --git a/macros/Cargo.toml b/macros/Cargo.toml index 254b7bd1b4..977933e44c 100644 --- a/macros/Cargo.toml +++ b/macros/Cargo.toml @@ -10,10 +10,11 @@ repository = "https://github.com/japaric/cortex-m-rtfm" version = "0.3.0" [dependencies] -error-chain = "0.10.0" -quote = "0.3.15" -rtfm-syntax = "0.2.1" -syn = "0.11.11" +failure = "0.1.1" +proc-macro2 = "0.3.6" +quote = "0.5.1" +rtfm-syntax = { git = "https://github.com/japaric/rtfm-syntax", branch = "syn-up" } +syn = "0.13.1" [lib] proc-macro = true diff --git a/macros/src/analyze.rs b/macros/src/analyze.rs index 65d98e6960..666dc306a3 100644 --- a/macros/src/analyze.rs +++ b/macros/src/analyze.rs @@ -40,7 +40,7 @@ pub fn app(app: &App) -> Ownerships { } for task in app.tasks.values() { - for resource in &task.resources { + for resource in task.resources.iter() { if let Some(ownership) = ownerships.get_mut(resource) { match *ownership { Ownership::Owned { priority } => { diff --git a/macros/src/check.rs b/macros/src/check.rs index f6fd9cc609..4defb46d7d 100644 --- a/macros/src/check.rs +++ b/macros/src/check.rs @@ -1,10 +1,8 @@ use std::collections::HashMap; use syn::{Ident, Path}; -use syntax::check::{self, Idle, Init}; -use syntax::{self, Resources, Statics}; - -use syntax::error::*; +use syntax::check::{self, Idents, Idle, Init, Statics}; +use syntax::{self, Result}; pub struct App { pub device: Path, @@ -51,7 +49,7 @@ pub struct Task { pub kind: Kind, pub path: Path, pub priority: u8, - pub resources: Resources, + pub resources: Idents, } pub fn app(app: check::App) -> Result { @@ -63,80 +61,16 @@ pub fn app(app: check::App) -> Result { tasks: app.tasks .into_iter() .map(|(k, v)| { - let v = - ::check::task(k.as_ref(), v).chain_err(|| format!("checking task `{}`", k))?; + let v = ::check::task(k.as_ref(), v)?; Ok((k, v)) }) .collect::>()?, }; - ::check::resources(&app).chain_err(|| "checking `resources`")?; - Ok(app) } -fn resources(app: &App) -> Result<()> { - for name in &app.init.resources { - if let Some(resource) = app.resources.get(name) { - ensure!( - resource.expr.is_some(), - "resource `{}`, allocated to `init`, must have an initial value", - name - ); - } else { - bail!( - "resource `{}`, allocated to `init`, must be a data resource", - name - ); - } - - ensure!( - !app.idle.resources.contains(name), - "resources assigned to `init` can't be shared with `idle`" - ); - - ensure!( - app.tasks - .iter() - .all(|(_, task)| !task.resources.contains(name)), - "resources assigned to `init` can't be shared with tasks" - ) - } - - for resource in app.resources.keys() { - if app.init.resources.contains(resource) { - continue; - } - - if app.idle.resources.contains(resource) { - continue; - } - - if app.tasks - .values() - .any(|task| task.resources.contains(resource)) - { - continue; - } - - bail!("resource `{}` is unused", resource); - } - - for (name, task) in &app.tasks { - for resource in &task.resources { - ensure!( - app.resources.contains_key(&resource), - "task {} contains an undeclared resource with name {}", - name, - resource - ); - } - } - - Ok(()) -} - fn task(name: &str, task: syntax::check::Task) -> Result { let kind = match Exception::from(name) { Some(e) => { @@ -147,23 +81,14 @@ fn task(name: &str, task: syntax::check::Task) -> Result { Kind::Exception(e) } - None => { - if task.enabled == Some(true) { - bail!( - "`enabled: true` is the default value; this line can be \ - omitted" - ); - } - - Kind::Interrupt { - enabled: task.enabled.unwrap_or(true), - } - } + None => Kind::Interrupt { + enabled: task.enabled.unwrap_or(true), + }, }; Ok(Task { kind, - path: task.path.ok_or("`path` field is missing")?, + path: task.path, priority: task.priority.unwrap_or(1), resources: task.resources, }) diff --git a/macros/src/lib.rs b/macros/src/lib.rs index c45646c236..728e6133ec 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -1,19 +1,19 @@ //! Procedural macros of the `cortex-m-rtfm` crate -#![deny(warnings)] +// #![deny(warnings)] #![feature(proc_macro)] #![recursion_limit = "128"] #[macro_use] -extern crate error_chain; +extern crate failure; extern crate proc_macro; +extern crate proc_macro2; +extern crate syn; #[macro_use] extern crate quote; extern crate rtfm_syntax as syntax; -extern crate syn; use proc_macro::TokenStream; -use syntax::App; -use syntax::error::*; +use syntax::{App, Result}; mod analyze; mod check; @@ -170,22 +170,17 @@ mod trans; #[proc_macro] pub fn app(ts: TokenStream) -> TokenStream { match run(ts) { - Err(e) => panic!("{}", error_chain::ChainedError::display(&e)), + Err(e) => panic!("error: {}", e), Ok(ts) => ts, } } fn run(ts: TokenStream) -> Result { - let input = format!("{}", ts); - - let app = App::parse(&input).chain_err(|| "parsing")?; - let app = syntax::check::app(app).chain_err(|| "checking the AST")?; + let app = App::parse(ts)?.check()?; let app = check::app(app)?; let ownerships = analyze::app(&app); let tokens = trans::app(&app, &ownerships); - Ok(format!("{}", tokens) - .parse() - .map_err(|_| "BUG: error parsing the generated code")?) + Ok(tokens.into()) } diff --git a/macros/src/trans.rs b/macros/src/trans.rs index 42f7487d06..964b1a3081 100644 --- a/macros/src/trans.rs +++ b/macros/src/trans.rs @@ -1,5 +1,6 @@ -use quote::{Ident, Tokens}; -use syn::{Lit, StrStyle}; +use proc_macro2::Span; +use quote::Tokens; +use syn::{Ident, LitStr}; use analyze::Ownerships; use check::{App, Kind}; @@ -10,12 +11,12 @@ fn krate() -> Ident { pub fn app(app: &App, ownerships: &Ownerships) -> Tokens { let mut root = vec![]; - let mut main = vec![]; + let mut main = vec![quote!(#![allow(path_statements)])]; + ::trans::tasks(app, ownerships, &mut root, &mut main); ::trans::init(app, &mut main, &mut root); ::trans::idle(app, ownerships, &mut main, &mut root); ::trans::resources(app, ownerships, &mut root); - ::trans::tasks(app, ownerships, &mut root); root.push(quote! { #[allow(unsafe_code)] @@ -53,7 +54,7 @@ fn idle(app: &App, ownerships: &Ownerships, main: &mut Vec, root: &mut V let super_ = if needs_reexport { None } else { - Some(Ident::new("super")) + Some(Ident::from("super")) }; let mut rexprs = vec![]; let mut rfields = vec![]; @@ -69,7 +70,7 @@ fn idle(app: &App, ownerships: &Ownerships, main: &mut Vec, root: &mut V pub #name: &'static mut #ty, }); - let _name = Ident::new(format!("_{}", name.as_ref())); + let _name = Ident::from(format!("_{}", name.as_ref())); rexprs.push(if resource.expr.is_some() { quote! { #name: &mut #super_::#_name, @@ -132,10 +133,10 @@ fn idle(app: &App, ownerships: &Ownerships, main: &mut Vec, root: &mut V // owned resource if ceiling == 0 { - continue + continue; } - let _name = Ident::new(format!("_{}", name.as_ref())); + let _name = Ident::from(format!("_{}", name.as_ref())); let resource = app.resources .get(name) .expect(&format!("BUG: resource {} has no definition", name)); @@ -262,7 +263,7 @@ fn init(app: &App, main: &mut Vec, root: &mut Vec) { &mut #name },)); } else { - let _name = Ident::new(format!("_{}", name.as_ref())); + let _name = Ident::from(format!("_{}", name.as_ref())); lifetime = Some(quote!('a)); fields.push(quote! { @@ -309,7 +310,7 @@ fn init(app: &App, main: &mut Vec, root: &mut Vec) { let mut fields = vec![]; for (name, resource) in late_resources { - let _name = Ident::new(format!("_{}", name.as_ref())); + let _name = Ident::from(format!("_{}", name.as_ref())); let ty = &resource.ty; @@ -373,6 +374,8 @@ fn init(app: &App, main: &mut Vec, root: &mut Vec) { // Interrupt. These are enabled / disabled through the NVIC if interrupts.is_empty() { interrupts.push(quote! { + use #device::Interrupt; + let mut nvic: #device::NVIC = core::mem::transmute(()); }); } @@ -381,16 +384,16 @@ fn init(app: &App, main: &mut Vec, root: &mut Vec) { interrupts.push(quote! { let prio_bits = #device::NVIC_PRIO_BITS; let hw = ((1 << prio_bits) - #priority) << (8 - prio_bits); - nvic.set_priority(#device::Interrupt::#name, hw); + nvic.set_priority(Interrupt::#name, hw); }); if enabled { interrupts.push(quote! { - nvic.enable(#device::Interrupt::#name); + nvic.enable(Interrupt::#name); }); } else { interrupts.push(quote! { - nvic.disable(#device::Interrupt::#name); + nvic.disable(Interrupt::#name); }); } } @@ -416,7 +419,7 @@ fn resources(app: &App, ownerships: &Ownerships, root: &mut Vec) { let krate = krate(); for name in ownerships.keys() { - let _name = Ident::new(format!("_{}", name.as_ref())); + let _name = Ident::from(format!("_{}", name.as_ref())); // Declare the static that holds the resource let resource = app.resources @@ -439,7 +442,7 @@ fn resources(app: &App, ownerships: &Ownerships, root: &mut Vec) { } } -fn tasks(app: &App, ownerships: &Ownerships, root: &mut Vec) { +fn tasks(app: &App, ownerships: &Ownerships, root: &mut Vec, main: &mut Vec) { let device = &app.device; let krate = krate(); @@ -453,7 +456,7 @@ fn tasks(app: &App, ownerships: &Ownerships, root: &mut Vec) { if has_resources { for rname in &task.resources { let ceiling = ownerships[rname].ceiling(); - let _rname = Ident::new(format!("_{}", rname.as_ref())); + let _rname = Ident::from(format!("_{}", rname.as_ref())); let resource = app.resources .get(rname) .expect(&format!("BUG: resource {} has no definition", rname)); @@ -591,8 +594,8 @@ fn tasks(app: &App, ownerships: &Ownerships, root: &mut Vec) { } let path = &task.path; - let _tname = Ident::new(format!("_{}", tname)); - let export_name = Lit::Str(tname.as_ref().to_owned(), StrStyle::Cooked); + let _tname = Ident::from(format!("_{}", tname)); + let export_name = LitStr::new(tname.as_ref(), Span::call_site()); root.push(quote! { #[allow(non_snake_case)] #[allow(unsafe_code)] @@ -608,11 +611,12 @@ fn tasks(app: &App, ownerships: &Ownerships, root: &mut Vec) { #[allow(non_snake_case)] #[allow(unsafe_code)] mod #tname { + #[allow(unused_imports)] use core::marker::PhantomData; #[allow(dead_code)] #[deny(const_err)] - const CHECK_PRIORITY: (u8, u8) = ( + pub const CHECK_PRIORITY: (u8, u8) = ( #priority - 1, (1 << ::#device::NVIC_PRIO_BITS) - #priority, ); @@ -620,5 +624,9 @@ fn tasks(app: &App, ownerships: &Ownerships, root: &mut Vec) { #(#items)* } }); + + // after miri landed (?) rustc won't analyze `const` items unless they are used so we force + // evaluation with this path statement + main.push(quote!(#tname::CHECK_PRIORITY;)); } } diff --git a/tests/cfail/init-resource-share-idle.rs b/tests/cfail/init-resource-share-idle.rs index d8332469d8..4e2ed4aa84 100644 --- a/tests/cfail/init-resource-share-idle.rs +++ b/tests/cfail/init-resource-share-idle.rs @@ -19,8 +19,8 @@ app! { //~ proc macro panicked }, idle: { - // ERROR resources assigned to `init` can't be shared with `idle` resources: [BUFFER], + //~^ error: this resource is owned by `init` and can't be shared }, } diff --git a/tests/cfail/init-resource-share-task.rs b/tests/cfail/init-resource-share-task.rs index 8fe688993e..391c543d2c 100644 --- a/tests/cfail/init-resource-share-task.rs +++ b/tests/cfail/init-resource-share-task.rs @@ -21,8 +21,8 @@ app! { //~ proc macro panicked tasks: { SYS_TICK: { path: sys_tick, - // ERROR resources assigned to `init` can't be shared with tasks resources: [BUFFER], + //~^ error: this resource is owned by `init` and can't be shared }, }, } diff --git a/tests/cfail/interrupt.rs b/tests/cfail/interrupt.rs index e3ef2e8ffd..7c345a11e3 100644 --- a/tests/cfail/interrupt.rs +++ b/tests/cfail/interrupt.rs @@ -8,12 +8,10 @@ extern crate stm32f103xx; use rtfm::app; -app! { - //~^ error no variant named `EXTI33` found for type +app! { //~ error no variant named `EXTI33` found for type device: stm32f103xx, tasks: { - // ERROR this interrupt doesn't exist EXTI33: { path: exti33, }, diff --git a/tests/cfail/priority-too-high.rs b/tests/cfail/priority-too-high.rs index 5c353770d2..15f6b7a86c 100644 --- a/tests/cfail/priority-too-high.rs +++ b/tests/cfail/priority-too-high.rs @@ -9,6 +9,7 @@ extern crate stm32f103xx; use rtfm::app; app! { //~ error attempt to subtract with overflow + //~^ error constant evaluation error device: stm32f103xx, tasks: { diff --git a/tests/cfail/priority-too-low.rs b/tests/cfail/priority-too-low.rs index 2be2254deb..e87951121f 100644 --- a/tests/cfail/priority-too-low.rs +++ b/tests/cfail/priority-too-low.rs @@ -9,6 +9,7 @@ extern crate stm32f103xx; use rtfm::app; app! { //~ error attempt to subtract with overflow + //~^ error constant evaluation error device: stm32f103xx, tasks: { diff --git a/tests/cfail/resource-not-send-sync.rs b/tests/cfail/resource-not-send-sync.rs index 775c78ae07..60a20db1aa 100644 --- a/tests/cfail/resource-not-send-sync.rs +++ b/tests/cfail/resource-not-send-sync.rs @@ -7,7 +7,7 @@ extern crate cortex_m_rtfm as rtfm; extern crate stm32f103xx; -use rtfm::{app, Resource, Threshold}; +use rtfm::{app, Threshold}; app! { device: stm32f103xx, @@ -43,7 +43,7 @@ fn is_sync(_: &T) where T: Sync {} fn exti0(_t: &mut Threshold, r: EXTI0::Resources) { // ERROR resource proxies can't be shared between tasks is_sync(&r.SHARED); - //~^ error the trait bound `*const (): core::marker::Sync` is not satisfied + //~^ error `*const ()` cannot be shared between threads safely // ERROR resource proxies are not `Send`able across tasks is_send(&r.SHARED);