aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md30
-rw-r--r--Cargo.toml2
-rw-r--r--book/en/src/by-example/resources.md4
-rw-r--r--book/ru/src/preface.md4
-rw-r--r--examples/late.rs5
-rw-r--r--examples/singleton.rs6
-rw-r--r--examples/static.rs4
-rw-r--r--macros/src/check.rs280
-rw-r--r--macros/src/codegen.rs107
-rw-r--r--macros/src/syntax.rs64
-rw-r--r--src/lib.rs14
-rw-r--r--tests/cfail/early-return-2.rs29
-rw-r--r--tests/cfail/early-return.rs32
-rw-r--r--tests/cfail/init-divergent.rs2
-rw-r--r--tests/cfail/init-input.rs2
-rw-r--r--tests/cfail/init-output.rs2
-rw-r--r--tests/cfail/late-not-send.rs6
-rw-r--r--tests/cfail/late-uninit.rs2
-rw-r--r--tests/cpass/late-not-send.rs6
-rw-r--r--tests/cpass/late-resource.rs5
20 files changed, 549 insertions, 57 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7e83b7d1..189e02b8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,10 +5,35 @@ 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
+- 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.
+
+## [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 +175,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 55d3fa85..79f9430f 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"
diff --git a/book/en/src/by-example/resources.md b/book/en/src/by-example/resources.md
index efdeaa2d..46d04a74 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
diff --git a/book/ru/src/preface.md b/book/ru/src/preface.md
index ee01d101..0f9bd67e 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:}}
diff --git a/examples/late.rs b/examples/late.rs
index be656408..622008a7 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<Queue<u32, U4>> = 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 79815e88..9e48e541 100644
--- a/examples/singleton.rs
+++ b/examples/singleton.rs
@@ -20,10 +20,12 @@ const APP: () = {
static mut P: Pool<M> = ();
#[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 d40fdb1a..0309b681 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/macros/src/check.rs b/macros/src/check.rs
index ae2262a8..045d152f 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;
@@ -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,5 +115,258 @@ pub fn app(app: &App) -> parse::Result<()> {
}
}
+ // 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()) {
+ 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(())
+}
+
+// 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/macros/src/codegen.rs b/macros/src/codegen.rs
index 6c1baeca..c7adbd67 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::<Vec<_>>();
+
+ 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::<Vec<_>>();
+
+ 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<Ident>,
) -> 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_ma
!exception.args.schedule.is_empty(),
!exception.args.spawn.is_empty(),
app,
+ None,
);
#[cfg(feature = "timer-queue")]
@@ -1083,6 +1156,7 @@ fn interrupts(
!interrupt.args.schedule.is_empty(),
!interrupt.args.spawn.is_empty(),
app,
+ None,
));
#[cfg(feature = "timer-queue")]
@@ -1245,6 +1319,7 @@ fn tasks(ctxt: &mut Context, app: &App, analysis: &Analysis) -> 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 85f3caaa..ad7d8bde 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<Attribute>,
pub left: Ident,
@@ -608,32 +609,83 @@ pub struct Init {
pub unsafety: Option<Token![unsafe]>,
pub statics: HashMap<Ident, Static>,
pub stmts: Vec<Stmt>,
+ // TODO remove in v0.5.x
pub assigns: Vec<Assign>,
+ pub returns_late_resources: bool,
}
impl Init {
fn check(args: InitArgs, item: ItemFn) -> parse::Result<Self> {
- 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<Stmt>) -> (Statics, Vec<Stmt>) {
(statics, stmts)
}
+// TODO remove in v0.5.x
fn extract_assignments(stmts: Vec<Stmt>) -> (Vec<Stmt>, Vec<Assign>) {
let mut istmts = stmts.into_iter().rev();
diff --git a/src/lib.rs b/src/lib.rs
index 9dc51756..9914aaf4 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
diff --git a/tests/cfail/early-return-2.rs b/tests/cfail/early-return-2.rs
new file mode 100644
index 00000000..bf867e07
--- /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 00000000..fb695aac
--- /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
+ }
+ }
+};
diff --git a/tests/cfail/init-divergent.rs b/tests/cfail/init-divergent.rs
index 400c805e..54813d47 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 fa79099c..3bf0cadf 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 1200aca7..414a35a8 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 b9180fed..eb3048d9 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 eeb9bd41..55122ed7 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 06d376bd..5b278ab5 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<NotSend> = 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 94ec8c96..0dec4cbe 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 }
}
};