From 91962d21fea9be901125883f2eacbcc426873103 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Tue, 12 Feb 2019 11:04:54 +0100 Subject: [PATCH 1/8] (ru) fix includes in the preface --- book/ru/src/preface.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/book/ru/src/preface.md b/book/ru/src/preface.md index ee01d10134..0f9bd67ed6 100644 --- a/book/ru/src/preface.md +++ b/book/ru/src/preface.md @@ -7,6 +7,6 @@ Эта книга содержит документацию уровня пользователя фреймворком Real Time For the Masses (RTFM). Описание API можно найти [здесь](../api/rtfm/index.html). -{{#include ../..ADME_RU.md:5:54}} +{{#include README_RU.md:5:44}} -{{#include ../..ADME_RU.md:60:}} +{{#include README_RU.md:50:}} From 557a51ede11b39acd37dcadb5bd3e12c4e7e7b28 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Tue, 12 Feb 2019 11:07:15 +0100 Subject: [PATCH 2/8] forbid early returns in init --- macros/src/check.rs | 247 +++++++++++++++++++++++++++++++++- tests/cfail/early-return-2.rs | 29 ++++ tests/cfail/early-return.rs | 32 +++++ 3 files changed, 307 insertions(+), 1 deletion(-) create mode 100644 tests/cfail/early-return-2.rs create mode 100644 tests/cfail/early-return.rs diff --git a/macros/src/check.rs b/macros/src/check.rs index ae2262a859..0bc31c5ff9 100644 --- a/macros/src/check.rs +++ b/macros/src/check.rs @@ -1,7 +1,7 @@ use std::{collections::HashSet, iter}; use proc_macro2::Span; -use syn::parse; +use syn::{parse, spanned::Spanned, Block, Expr, Stmt}; use crate::syntax::App; @@ -112,5 +112,250 @@ pub fn app(app: &App) -> parse::Result<()> { } } + // Check that `init` contains no early returns *if* late resources exist + if app.resources.values().any(|res| res.expr.is_none()) { + for stmt in &app.init.stmts { + noreturn_stmt(stmt)?; + } + } + + Ok(()) +} + +// checks that the given block contains no instance of `return` +fn noreturn_block(block: &Block) -> Result<(), parse::Error> { + for stmt in &block.stmts { + noreturn_stmt(stmt)?; + } + + Ok(()) +} + +// checks that the given statement contains no instance of `return` +fn noreturn_stmt(stmt: &Stmt) -> Result<(), parse::Error> { + match stmt { + // `let x = ..` -- this may contain a return in the RHS + Stmt::Local(local) => { + if let Some(ref init) = local.init { + noreturn_expr(&init.1)? + } + } + + // items have no effect on control flow + Stmt::Item(..) => {} + + Stmt::Expr(expr) => noreturn_expr(expr)?, + + Stmt::Semi(expr, ..) => noreturn_expr(expr)?, + } + + Ok(()) +} + +// checks that the given expression contains no `return` +fn noreturn_expr(expr: &Expr) -> Result<(), parse::Error> { + match expr { + Expr::Box(b) => noreturn_expr(&b.expr)?, + + Expr::InPlace(ip) => { + noreturn_expr(&ip.place)?; + noreturn_expr(&ip.value)?; + } + + Expr::Array(a) => { + for elem in &a.elems { + noreturn_expr(elem)?; + } + } + + Expr::Call(c) => { + noreturn_expr(&c.func)?; + + for arg in &c.args { + noreturn_expr(arg)?; + } + } + + Expr::MethodCall(mc) => { + noreturn_expr(&mc.receiver)?; + + for arg in &mc.args { + noreturn_expr(arg)?; + } + } + + Expr::Tuple(t) => { + for elem in &t.elems { + noreturn_expr(elem)?; + } + } + + Expr::Binary(b) => { + noreturn_expr(&b.left)?; + noreturn_expr(&b.right)?; + } + + Expr::Unary(u) => { + noreturn_expr(&u.expr)?; + } + + Expr::Lit(..) => {} + + Expr::Cast(c) => { + noreturn_expr(&c.expr)?; + } + + Expr::Type(t) => { + noreturn_expr(&t.expr)?; + } + + Expr::Let(l) => { + noreturn_expr(&l.expr)?; + } + + Expr::If(i) => { + noreturn_expr(&i.cond)?; + + noreturn_block(&i.then_branch)?; + + if let Some(ref e) = i.else_branch { + noreturn_expr(&e.1)?; + } + } + + Expr::While(w) => { + noreturn_expr(&w.cond)?; + noreturn_block(&w.body)?; + } + + Expr::ForLoop(fl) => { + noreturn_expr(&fl.expr)?; + noreturn_block(&fl.body)?; + } + + Expr::Loop(l) => { + noreturn_block(&l.body)?; + } + + Expr::Match(m) => { + noreturn_expr(&m.expr)?; + + for arm in &m.arms { + if let Some(g) = &arm.guard { + noreturn_expr(&g.1)?; + } + + noreturn_expr(&arm.body)?; + } + } + + // we don't care about `return`s inside closures + Expr::Closure(..) => {} + + Expr::Unsafe(u) => { + noreturn_block(&u.block)?; + } + + Expr::Block(b) => { + noreturn_block(&b.block)?; + } + + Expr::Assign(a) => { + noreturn_expr(&a.left)?; + noreturn_expr(&a.right)?; + } + + Expr::AssignOp(ao) => { + noreturn_expr(&ao.left)?; + noreturn_expr(&ao.right)?; + } + + Expr::Field(f) => { + noreturn_expr(&f.base)?; + } + + Expr::Index(i) => { + noreturn_expr(&i.expr)?; + noreturn_expr(&i.index)?; + } + + Expr::Range(r) => { + if let Some(ref f) = r.from { + noreturn_expr(f)?; + } + + if let Some(ref t) = r.to { + noreturn_expr(t)?; + } + } + + Expr::Path(..) => {} + + Expr::Reference(r) => { + noreturn_expr(&r.expr)?; + } + + Expr::Break(b) => { + if let Some(ref e) = b.expr { + noreturn_expr(e)?; + } + } + + Expr::Continue(..) => {} + + Expr::Return(r) => { + return Err(parse::Error::new( + r.span(), + "`init` is *not* allowed to early return", + )); + } + + // we can not analyze this + Expr::Macro(..) => {} + + Expr::Struct(s) => { + for field in &s.fields { + noreturn_expr(&field.expr)?; + } + + if let Some(ref rest) = s.rest { + noreturn_expr(rest)?; + } + } + + Expr::Repeat(r) => { + noreturn_expr(&r.expr)?; + noreturn_expr(&r.len)?; + } + + Expr::Paren(p) => { + noreturn_expr(&p.expr)?; + } + + Expr::Group(g) => { + noreturn_expr(&g.expr)?; + } + + Expr::Try(t) => { + noreturn_expr(&t.expr)?; + } + + // we don't care about `return`s inside async blocks + Expr::Async(..) => {} + + Expr::TryBlock(tb) => { + noreturn_block(&tb.block)?; + } + + Expr::Yield(y) => { + if let Some(expr) = &y.expr { + noreturn_expr(expr)?; + } + } + + // we can not analyze this + Expr::Verbatim(..) => {} + } + Ok(()) } diff --git a/tests/cfail/early-return-2.rs b/tests/cfail/early-return-2.rs new file mode 100644 index 0000000000..bf867e0720 --- /dev/null +++ b/tests/cfail/early-return-2.rs @@ -0,0 +1,29 @@ +#![no_main] +#![no_std] + +extern crate lm3s6965; +extern crate panic_halt; +extern crate rtfm; + +use rtfm::app; + +#[app(device = lm3s6965)] +const APP: () = { + static mut UNINITIALIZED: bool = (); + + #[init] + fn init() { + if false { + return; //~ ERROR `init` is *not* allowed to early return + } + + UNINITIALIZED = true; + } + + #[interrupt(resources = [UNINITIALIZED])] + fn UART0() { + if resources.UNINITIALIZED { + // UB + } + } +}; diff --git a/tests/cfail/early-return.rs b/tests/cfail/early-return.rs new file mode 100644 index 0000000000..fb695aac97 --- /dev/null +++ b/tests/cfail/early-return.rs @@ -0,0 +1,32 @@ +#![no_main] +#![no_std] + +extern crate lm3s6965; +extern crate panic_halt; +extern crate rtfm; + +use rtfm::app; + +#[app(device = lm3s6965)] +const APP: () = { + static mut UNINITIALIZED: bool = (); + + #[init] + fn init() { + let x = || { + // this is OK + return 0; + }; + + return; //~ ERROR `init` is *not* allowed to early return + + UNINITIALIZED = true; + } + + #[interrupt(resources = [UNINITIALIZED])] + fn UART0() { + if resources.UNINITIALIZED { + // UB + } + } +}; From 1ba03b9f00611dbc5f79a128bd7306e57195edb3 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Tue, 12 Feb 2019 11:08:39 +0100 Subject: [PATCH 3/8] document MSRV and SemVer policy --- src/lib.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 9dc5175685..9914aaf430 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,6 +14,20 @@ //! //! [`#[app]`]: ../cortex_m_rtfm_macros/attr.app.html //! +//! # Minimum Supported Rust Version (MSRV) +//! +//! This crate is guaranteed to compile on stable Rust 1.31 (2018 edition) and up. It *might* +//! compile on older versions but that may change in any new patch release. +//! +//! # Semantic Versioning +//! +//! Like the Rust project, this crate adheres to [SemVer]: breaking changes in the API and semantics +//! require a *semver bump* (a new minor version release), with the exception of breaking changes +//! that fix soundness issues -- those are considered bug fixes and can be landed in a new patch +//! release. +//! +//! [SemVer]: https://semver.org/spec/v2.0.0.html +//! //! # Cargo features //! //! - `timer-queue`. This opt-in feature enables the `schedule` API which can be used to schedule From 8890f10e1c386b5f408f30174f0a83bb45166823 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Tue, 12 Feb 2019 11:28:34 +0100 Subject: [PATCH 4/8] v0.4.1 --- CHANGELOG.md | 26 ++++++++++++++++++++++++-- Cargo.toml | 2 +- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e83b7d16b..b780873ec2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,10 +5,31 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +## [v0.4.1] - 2019-02-12 + +### Added + +- The RTFM book has been translated to Russian. You can find the translation + online at https://japaric.github.io/cortex-m-rtfm/book/ru/ + +- `Duration` now implements the `Default` trait. + +### Changed + +- [breaking-change][] [soundness-fix] `init` can not contain any early return as + that would result in late resources not being initialized and thus undefined + behavior. + - Use an absolute link to the book so it works when landing from crates.io documentation page -## [v0.4.0] - 2018-11-03 +### Fixed + +- `#[interrupt]` and `#[exception]` no longer produce warnings on recent nightlies. + +## [v0.4.0] - 2018-11-03 - YANKED + +Yanked due to a soundness issue in `init`; the issue has been mostly fixed in v0.4.1. ### Changed @@ -150,7 +171,8 @@ This project adheres to [Semantic Versioning](http://semver.org/). - Initial release -[Unreleased]: https://github.com/japaric/cortex-m-rtfm/compare/v0.4.0...HEAD +[Unreleased]: https://github.com/japaric/cortex-m-rtfm/compare/v0.4.1...HEAD +[v0.4.1]: https://github.com/japaric/cortex-m-rtfm/compare/v0.4.0...v0.4.1 [v0.4.0]: https://github.com/japaric/cortex-m-rtfm/compare/v0.3.4...v0.4.0 [v0.3.4]: https://github.com/japaric/cortex-m-rtfm/compare/v0.3.3...v0.3.4 [v0.3.3]: https://github.com/japaric/cortex-m-rtfm/compare/v0.3.2...v0.3.3 diff --git a/Cargo.toml b/Cargo.toml index 55d3fa85a3..79f9430fd8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ license = "MIT OR Apache-2.0" name = "cortex-m-rtfm" readme = "README.md" repository = "https://github.com/japaric/cortex-m-rtfm" -version = "0.4.0" +version = "0.4.1" [lib] name = "rtfm" From 88599780e0eba38d9e543b7809f586479f6956bd Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Tue, 12 Feb 2019 14:53:49 +0100 Subject: [PATCH 5/8] accept `init: fn() -> init::LateResources` --- macros/src/check.rs | 39 +++++++++------ macros/src/codegen.rs | 107 +++++++++++++++++++++++++++++++++++------- macros/src/syntax.rs | 64 +++++++++++++++++++++++-- 3 files changed, 175 insertions(+), 35 deletions(-) diff --git a/macros/src/check.rs b/macros/src/check.rs index 0bc31c5ff9..045d152f8c 100644 --- a/macros/src/check.rs +++ b/macros/src/check.rs @@ -35,17 +35,20 @@ pub fn app(app: &App) -> parse::Result<()> { } } - // Check that all late resources have been initialized in `#[init]` - for res in app - .resources - .iter() - .filter_map(|(name, res)| if res.expr.is_none() { Some(name) } else { None }) - { - if app.init.assigns.iter().all(|assign| assign.left != *res) { - return Err(parse::Error::new( - res.span(), - "late resources MUST be initialized at the end of `init`", - )); + // Check that all late resources have been initialized in `#[init]` if `init` has signature + // `fn()` + if !app.init.returns_late_resources { + for res in app + .resources + .iter() + .filter_map(|(name, res)| if res.expr.is_none() { Some(name) } else { None }) + { + if app.init.assigns.iter().all(|assign| assign.left != *res) { + return Err(parse::Error::new( + res.span(), + "late resources MUST be initialized at the end of `init`", + )); + } } } @@ -112,11 +115,19 @@ pub fn app(app: &App) -> parse::Result<()> { } } - // Check that `init` contains no early returns *if* late resources exist + // Check that `init` contains no early returns *if* late resources exist and `init` signature is + // `fn()` if app.resources.values().any(|res| res.expr.is_none()) { - for stmt in &app.init.stmts { - noreturn_stmt(stmt)?; + if !app.init.returns_late_resources { + for stmt in &app.init.stmts { + noreturn_stmt(stmt)?; + } } + } else if app.init.returns_late_resources { + return Err(parse::Error::new( + Span::call_site(), + "`init` signature must be `[unsafe] fn()` if there are no late resources", + )); } Ok(()) diff --git a/macros/src/codegen.rs b/macros/src/codegen.rs index 6c1baeca1e..c7adbd67ea 100644 --- a/macros/src/codegen.rs +++ b/macros/src/codegen.rs @@ -94,7 +94,7 @@ pub fn app(app: &App, analysis: &Analysis) -> TokenStream { let (dispatchers_data, dispatchers) = dispatchers(&mut ctxt, &app, analysis); - let init_fn = init(&mut ctxt, &app, analysis); + let (init_fn, has_late_resources) = init(&mut ctxt, &app, analysis); let init_arg = if cfg!(feature = "timer-queue") { quote!(rtfm::Peripherals { CBP: p.CBP, @@ -123,6 +123,30 @@ pub fn app(app: &App, analysis: &Analysis) -> TokenStream { }) }; + let init = &ctxt.init; + let init_phase = if has_late_resources { + let assigns = app + .resources + .iter() + .filter_map(|(name, res)| { + if res.expr.is_none() { + let alias = &ctxt.statics[name]; + + Some(quote!(#alias.set(res.#name);)) + } else { + None + } + }) + .collect::>(); + + quote!( + let res = #init(#init_arg); + #(#assigns)* + ) + } else { + quote!(#init(#init_arg);) + }; + let post_init = post_init(&ctxt, &app, analysis); let (idle_fn, idle_expr) = idle(&mut ctxt, &app, analysis); @@ -147,7 +171,6 @@ pub fn app(app: &App, analysis: &Analysis) -> TokenStream { let assertions = assertions(app, analysis); let main = mk_ident(None); - let init = &ctxt.init; quote!( #resources @@ -185,7 +208,7 @@ pub fn app(app: &App, analysis: &Analysis) -> TokenStream { #pre_init - #init(#init_arg); + #init_phase #post_init @@ -290,10 +313,11 @@ fn resources(ctxt: &mut Context, app: &App, analysis: &Analysis) -> proc_macro2: quote!(#(#items)*) } -fn init(ctxt: &mut Context, app: &App, analysis: &Analysis) -> proc_macro2::TokenStream { +fn init(ctxt: &mut Context, app: &App, analysis: &Analysis) -> (proc_macro2::TokenStream, bool) { let attrs = &app.init.attrs; let locals = mk_locals(&app.init.statics, true); let stmts = &app.init.stmts; + // TODO remove in v0.5.x let assigns = app .init .assigns @@ -334,12 +358,47 @@ fn init(ctxt: &mut Context, app: &App, analysis: &Analysis) -> proc_macro2::Toke analysis, ); + let (late_resources, late_resources_ident, ret) = if app.init.returns_late_resources { + // create `LateResources` struct in the root of the crate + let ident = mk_ident(None); + + let fields = app + .resources + .iter() + .filter_map(|(name, res)| { + if res.expr.is_none() { + let ty = &res.ty; + Some(quote!(pub #name: #ty)) + } else { + None + } + }) + .collect::>(); + + let late_resources = quote!( + #[allow(non_snake_case)] + pub struct #ident { + #(#fields),* + } + ); + + ( + Some(late_resources), + Some(ident), + Some(quote!(-> init::LateResources)), + ) + } else { + (None, None, None) + }; + let has_late_resources = late_resources.is_some(); + let module = module( ctxt, Kind::Init, !app.init.args.schedule.is_empty(), !app.init.args.spawn.is_empty(), app, + late_resources_ident, ); #[cfg(feature = "timer-queue")] @@ -365,25 +424,30 @@ fn init(ctxt: &mut Context, app: &App, analysis: &Analysis) -> proc_macro2::Toke let unsafety = &app.init.unsafety; let device = &app.args.device; let init = &ctxt.init; - quote!( - #module + ( + quote!( + #late_resources - #(#attrs)* - #unsafety fn #init(mut core: rtfm::Peripherals) { - #(#locals)* + #module - #baseline_let + #(#attrs)* + #unsafety fn #init(mut core: rtfm::Peripherals) #ret { + #(#locals)* - #prelude + #baseline_let - let mut device = unsafe { #device::Peripherals::steal() }; + #prelude - #start_let + let mut device = unsafe { #device::Peripherals::steal() }; - #(#stmts)* + #start_let - #(#assigns)* - } + #(#stmts)* + + #(#assigns)* + } + ), + has_late_resources, ) } @@ -440,6 +504,7 @@ fn module( schedule: bool, spawn: bool, app: &App, + late_resources: Option, ) -> proc_macro2::TokenStream { let mut items = vec![]; let mut fields = vec![]; @@ -572,6 +637,12 @@ fn module( Kind::Task(_) => "Software task", }; + if let Some(late_resources) = late_resources { + items.push(quote!( + pub use super::#late_resources as LateResources; + )); + } + quote!( #root @@ -950,6 +1021,7 @@ fn idle( !idle.args.schedule.is_empty(), !idle.args.spawn.is_empty(), app, + None, ); let unsafety = &idle.unsafety; @@ -1004,6 +1076,7 @@ fn exceptions(ctxt: &mut Context, app: &App, analysis: &Analysis) -> Vec proc_macro2::Tok !task.args.schedule.is_empty(), !task.args.spawn.is_empty(), app, + None, )); let attrs = &task.attrs; diff --git a/macros/src/syntax.rs b/macros/src/syntax.rs index 85f3caaa90..ad7d8bde34 100644 --- a/macros/src/syntax.rs +++ b/macros/src/syntax.rs @@ -596,6 +596,7 @@ impl Parse for InitArgs { } } +// TODO remove in v0.5.x pub struct Assign { pub attrs: Vec, pub left: Ident, @@ -608,32 +609,83 @@ pub struct Init { pub unsafety: Option, pub statics: HashMap, pub stmts: Vec, + // TODO remove in v0.5.x pub assigns: Vec, + pub returns_late_resources: bool, } impl Init { fn check(args: InitArgs, item: ItemFn) -> parse::Result { - let valid_signature = item.vis == Visibility::Inherited + let mut valid_signature = item.vis == Visibility::Inherited && item.constness.is_none() && item.asyncness.is_none() && item.abi.is_none() && item.decl.generics.params.is_empty() && item.decl.generics.where_clause.is_none() && item.decl.inputs.is_empty() - && item.decl.variadic.is_none() - && is_unit(&item.decl.output); + && item.decl.variadic.is_none(); + + let returns_late_resources = match &item.decl.output { + ReturnType::Default => false, + ReturnType::Type(_, ty) => { + match &**ty { + Type::Tuple(t) => { + if t.elems.is_empty() { + // -> () + true + } else { + valid_signature = false; + + false // don't care + } + } + + Type::Path(p) => { + let mut segments = p.path.segments.iter(); + if p.qself.is_none() + && p.path.leading_colon.is_none() + && p.path.segments.len() == 2 + && segments.next().map(|s| { + s.arguments == PathArguments::None && s.ident.to_string() == "init" + }) == Some(true) + && segments.next().map(|s| { + s.arguments == PathArguments::None + && s.ident.to_string() == "LateResources" + }) == Some(true) + { + // -> init::LateResources + true + } else { + valid_signature = false; + + false // don't care + } + } + + _ => { + valid_signature = false; + + false // don't care + } + } + } + }; let span = item.span(); if !valid_signature { return Err(parse::Error::new( span, - "`init` must have type signature `[unsafe] fn()`", + "`init` must have type signature `[unsafe] fn() [-> init::LateResources]`", )); } let (statics, stmts) = extract_statics(item.block.stmts); - let (stmts, assigns) = extract_assignments(stmts); + let (stmts, assigns) = if returns_late_resources { + (stmts, vec![]) + } else { + extract_assignments(stmts) + }; Ok(Init { args, @@ -642,6 +694,7 @@ impl Init { statics: Static::parse(statics)?, stmts, assigns, + returns_late_resources, }) } } @@ -1207,6 +1260,7 @@ fn extract_statics(stmts: Vec) -> (Statics, Vec) { (statics, stmts) } +// TODO remove in v0.5.x fn extract_assignments(stmts: Vec) -> (Vec, Vec) { let mut istmts = stmts.into_iter().rev(); From 89c922079eaefc748febdb62aeccfff598a07c69 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Tue, 12 Feb 2019 15:08:46 +0100 Subject: [PATCH 6/8] update examples and tests --- examples/late.rs | 5 ++--- examples/singleton.rs | 6 ++++-- examples/static.rs | 4 ++-- tests/cfail/init-divergent.rs | 2 +- tests/cfail/init-input.rs | 2 +- tests/cfail/init-output.rs | 2 +- tests/cfail/late-not-send.rs | 6 ++++-- tests/cfail/late-uninit.rs | 2 ++ tests/cpass/late-not-send.rs | 6 ++++-- tests/cpass/late-resource.rs | 5 ++--- 10 files changed, 23 insertions(+), 17 deletions(-) diff --git a/examples/late.rs b/examples/late.rs index be656408cf..622008a7fe 100644 --- a/examples/late.rs +++ b/examples/late.rs @@ -22,7 +22,7 @@ const APP: () = { static mut C: Consumer<'static, u32, U4> = (); #[init] - fn init() { + fn init() -> init::LateResources { // NOTE: we use `Option` here to work around the lack of // a stable `const` constructor static mut Q: Option> = None; @@ -31,8 +31,7 @@ const APP: () = { let (p, c) = Q.as_mut().unwrap().split(); // Initialization of late resources - P = p; - C = c; + init::LateResources { P: p, C: c } } #[idle(resources = [C])] diff --git a/examples/singleton.rs b/examples/singleton.rs index 79815e8852..9e48e54121 100644 --- a/examples/singleton.rs +++ b/examples/singleton.rs @@ -20,10 +20,12 @@ const APP: () = { static mut P: Pool = (); #[init(resources = [M])] - fn init() { + fn init() -> init::LateResources { rtfm::pend(Interrupt::I2C0); - P = Pool::new(resources.M); + init::LateResources { + P: Pool::new(resources.M), + } } #[interrupt( diff --git a/examples/static.rs b/examples/static.rs index d40fdb1a6d..0309b68163 100644 --- a/examples/static.rs +++ b/examples/static.rs @@ -16,11 +16,11 @@ const APP: () = { static KEY: u32 = (); #[init] - fn init() { + fn init() -> init::LateResources { rtfm::pend(Interrupt::UART0); rtfm::pend(Interrupt::UART1); - KEY = 0xdeadbeef; + init::LateResources { KEY: 0xdeadbeef } } #[interrupt(resources = [KEY])] diff --git a/tests/cfail/init-divergent.rs b/tests/cfail/init-divergent.rs index 400c805e91..54813d479f 100644 --- a/tests/cfail/init-divergent.rs +++ b/tests/cfail/init-divergent.rs @@ -11,7 +11,7 @@ use rtfm::app; const APP: () = { #[init] fn init() -> ! { - //~^ ERROR `init` must have type signature `[unsafe] fn()` + //~^ ERROR `init` must have type signature `[unsafe] fn() [-> init::LateResources]` loop {} } }; diff --git a/tests/cfail/init-input.rs b/tests/cfail/init-input.rs index fa79099c35..3bf0cadf13 100644 --- a/tests/cfail/init-input.rs +++ b/tests/cfail/init-input.rs @@ -11,6 +11,6 @@ use rtfm::app; const APP: () = { #[init] fn init(undef: u32) { - //~^ ERROR `init` must have type signature `[unsafe] fn()` + //~^ ERROR `init` must have type signature `[unsafe] fn() [-> init::LateResources]` } }; diff --git a/tests/cfail/init-output.rs b/tests/cfail/init-output.rs index 1200aca774..414a35a83a 100644 --- a/tests/cfail/init-output.rs +++ b/tests/cfail/init-output.rs @@ -11,7 +11,7 @@ use rtfm::app; const APP: () = { #[init] fn init() -> u32 { - //~^ ERROR `init` must have type signature `[unsafe] fn()` + //~^ ERROR `init` must have type signature `[unsafe] fn() [-> init::LateResources]` 0 } }; diff --git a/tests/cfail/late-not-send.rs b/tests/cfail/late-not-send.rs index b9180fed9d..eb3048d90f 100644 --- a/tests/cfail/late-not-send.rs +++ b/tests/cfail/late-not-send.rs @@ -22,8 +22,10 @@ const APP: () = { static mut X: NotSend = (); #[init] - fn init() { - X = NotSend { _0: PhantomData }; + fn init() -> init::LateResources { + init::LateResources { + X: NotSend { _0: PhantomData }, + } } #[interrupt(resources = [X])] diff --git a/tests/cfail/late-uninit.rs b/tests/cfail/late-uninit.rs index eeb9bd4191..55122ed7e4 100644 --- a/tests/cfail/late-uninit.rs +++ b/tests/cfail/late-uninit.rs @@ -1,3 +1,5 @@ +// TODO remove in v0.5.x + #![no_main] #![no_std] diff --git a/tests/cpass/late-not-send.rs b/tests/cpass/late-not-send.rs index 06d376bdf1..5b278ab5c3 100644 --- a/tests/cpass/late-not-send.rs +++ b/tests/cpass/late-not-send.rs @@ -19,10 +19,12 @@ const APP: () = { static mut Y: Option = None; #[init(resources = [Y])] - fn init() { + fn init() -> init::LateResources { *resources.Y = Some(NotSend { _0: PhantomData }); - X = NotSend { _0: PhantomData }; + init::LateResources { + X: NotSend { _0: PhantomData }, + } } #[idle(resources = [X, Y])] diff --git a/tests/cpass/late-resource.rs b/tests/cpass/late-resource.rs index 94ec8c9676..0dec4cbee0 100644 --- a/tests/cpass/late-resource.rs +++ b/tests/cpass/late-resource.rs @@ -14,8 +14,7 @@ const APP: () = { static Y: u32 = (); #[init] - fn init() { - X = 0; - Y = 1; + fn init() -> init::LateResources { + init::LateResources { X: 0, Y: 1 } } }; From 1e9058cab2d29979da9856a8198884b50176ccbc Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Tue, 12 Feb 2019 15:12:39 +0100 Subject: [PATCH 7/8] (en) update the text related to late resources cc @burrbull --- book/en/src/by-example/resources.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/book/en/src/by-example/resources.md b/book/en/src/by-example/resources.md index efdeaa2d9f..46d04a74c2 100644 --- a/book/en/src/by-example/resources.md +++ b/book/en/src/by-example/resources.md @@ -78,8 +78,8 @@ runtime initialized resources as *late resources*. Late resources are useful for interrupt and exception handlers. Late resources are declared like normal resources but that are given an initial -value of `()` (the unit value). Late resources must be initialized at the end of -the `init` function using plain assignments (e.g. `FOO = 1`). +value of `()` (the unit value). `init` must return the initial values of all +late resources packed in a `struct` of type `init::LateResources`. The example below uses late resources to stablish a lockless, one-way channel between the `UART0` interrupt handler and the `idle` function. A single producer From 519d7ca0569c25a23b1fab383351eb4d8344cdb0 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Tue, 12 Feb 2019 15:27:08 +0100 Subject: [PATCH 8/8] update CHANGELOG with alt init syntax --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b780873ec2..189e02b831 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,10 @@ This project adheres to [Semantic Versioning](http://semver.org/). - Use an absolute link to the book so it works when landing from crates.io documentation page +- The initialization function can now be written as `fn init() -> + init::LateResources` when late resources are used. This is preferred over the + old `fn init()` form. + ### Fixed - `#[interrupt]` and `#[exception]` no longer produce warnings on recent nightlies.