Skip to content

Externally defined Init/Idle #71

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
### Added

- CI changelog entry enforcer
- Support for externally defined #[init] and #[idle]

## [v1.0.0] - 2021-12-25

Expand Down
40 changes: 40 additions & 0 deletions examples/extern_idle.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
//! examples/extern_idle

#[mock::app(parse_binds, dispatchers = [UART1])]
mod app {
// idle externally implemented
use crate::{bar, foo};

#[shared]
struct Shared {
a: u32,
}

#[local]
struct Local {}

#[init]
fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {}

extern "Rust" {
// Software task
#[task(shared = [a], priority = 2)]
fn foo(_: foo::Context, _: u32);

// Hardware task
#[task(binds = UART0, shared = [a], priority = 2)]
// #[inline(always)] // would be rejected
fn bar(_: bar::Context);

// Externally defined idle task
#[idle()]
fn idle(_: idle::Context) -> !;
}
}

// The actual functions to dispatch are
// defined outside of the mod `app`.
//
// fn foo(_: foo::Context, _: u32) {}
// fn bar(_: bar::Context, _: u32) {}
// fn idle(_: idle::Context) -> ! {}
41 changes: 41 additions & 0 deletions examples/extern_init.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//! examples/extern_init

#[mock::app(parse_binds, dispatchers = [UART1])]
mod app {
// init externally implemented
use crate::{bar, foo, idle, init};

#[shared]
struct Shared {
a: u32,
}

#[local]
struct Local {}

#[idle]
fn idle(_: idle::Context) -> ! {}

extern "Rust" {

// Externally defined init
#[init]
fn init(_: init::Context) -> (Shared, Local, init::Monotonics);

// Software task
#[task(shared = [a], priority = 2)]
fn foo(_: foo::Context, _: u32);

// Hardware task
#[task(binds = UART0, shared = [a], priority = 2)]
//#[inline(always)] // would be rejected
fn bar(_: bar::Context);
}
}

// The actual functions to dispatch are
// defined outside of the mod `app`.
//
// fn foo(_: foo::Context, _: u32) {}
// fn bar(_: bar::Context, _: u32) {}
// fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {}
6 changes: 6 additions & 0 deletions src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,9 @@ pub struct Init {

/// The name of the user provided local resources struct
pub user_local_struct: Ident,

/// The task is declared externally
pub is_extern: bool,
}

/// `init` context metadata
Expand Down Expand Up @@ -126,6 +129,9 @@ pub struct Idle {

/// The statements that make up this `idle` function
pub stmts: Vec<Stmt>,

/// The task is declared externally
pub is_extern: bool,
}

/// `idle` context metadata
Expand Down
49 changes: 42 additions & 7 deletions src/parse/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,46 @@ impl App {
for item in mod_.items {
if let ForeignItem::Fn(mut item) = item {
let span = item.sig.ident.span();
// Find externally defined #[init] tasks
if let Some(pos) = item
.attrs
.iter()
.position(|attr| util::attr_eq(attr, "init"))
{
let args = InitArgs::parse(item.attrs.remove(pos).tokens)?;

// If an init function already exists, error
if init.is_some() {
return Err(parse::Error::new(
span,
"`#[init]` function must appear at most once",
));
}

check_ident(&item.sig.ident)?;

init = Some(Init::parse_foreign(args, item.clone())?);

// Find externally defined #[idle] tasks
} else if let Some(pos) = item
.attrs
.iter()
.position(|attr| util::attr_eq(attr, "idle"))
{
let args = IdleArgs::parse(item.attrs.remove(pos).tokens)?;

// If an idle function already exists, error
if idle.is_some() {
return Err(parse::Error::new(
span,
"`#[idle]` function must appear at most once",
));
}

check_ident(&item.sig.ident)?;

idle = Some(Idle::parse_foreign(args, item.clone())?);
} else if let Some(pos) = item
.attrs
.iter()
.position(|attr| util::attr_eq(attr, "task"))
Expand All @@ -400,7 +439,7 @@ impl App {
if item.attrs.len() != 1 {
return Err(parse::Error::new(
span,
"`extern` task required `#[task(..)]` attribute",
"`extern` tasks only supports one attribute: `#[task(..)]`",
));
}

Expand Down Expand Up @@ -430,14 +469,10 @@ impl App {
} else {
return Err(parse::Error::new(
span,
"`extern` task required `#[task(..)]` attribute",
"`extern` task, init or idle must have either `#[task(..)]`,
`#[init(..)]` or `#[idle(..)]` attribute",
));
}
} else {
return Err(parse::Error::new(
item.span(),
"this item must live outside the `#[app]` module",
));
}
}
}
Expand Down
3 changes: 0 additions & 3 deletions src/parse/hardware_task.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,6 @@ impl HardwareTask {
),
))
}
}

impl HardwareTask {
pub(crate) fn parse_foreign(
args: HardwareTaskArgs,
item: ForeignItemFn,
Expand Down
33 changes: 32 additions & 1 deletion src/parse/idle.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use proc_macro2::TokenStream as TokenStream2;
use syn::{parse, ItemFn};
use syn::{parse, ForeignItemFn, ItemFn, Stmt};

use crate::{
ast::{Idle, IdleArgs},
Expand Down Expand Up @@ -29,6 +29,37 @@ impl Idle {
context,
name: item.sig.ident,
stmts: item.block.stmts,
is_extern: false,
});
}
}
}

Err(parse::Error::new(
item.sig.ident.span(),
&format!(
"this `#[idle]` function must have signature `fn({}::Context) -> !`",
name
),
))
}
pub(crate) fn parse_foreign(args: IdleArgs, item: ForeignItemFn) -> parse::Result<Self> {
let valid_signature = util::check_foreign_fn_signature(&item)
&& item.sig.inputs.len() == 1
&& util::type_is_bottom(&item.sig.output);

let name = item.sig.ident.to_string();

if valid_signature {
if let Some((context, Ok(rest))) = util::parse_inputs(item.sig.inputs, &name) {
if rest.is_empty() {
return Ok(Idle {
args,
attrs: item.attrs,
context,
name: item.sig.ident,
stmts: Vec::<Stmt>::new(),
is_extern: true,
});
}
}
Expand Down
39 changes: 38 additions & 1 deletion src/parse/init.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use proc_macro2::TokenStream as TokenStream2;

use syn::{parse, ItemFn};
use syn::{parse, ForeignItemFn, ItemFn, Stmt};

use crate::{
ast::{Init, InitArgs},
Expand Down Expand Up @@ -35,6 +35,43 @@ impl Init {
stmts: item.block.stmts,
user_shared_struct,
user_local_struct,
is_extern: false,
});
}
}
}
}

Err(parse::Error::new(
span,
&format!(
"the `#[init]` function must have signature `fn({}::Context) -> (Shared resources struct, Local resources struct, {0}::Monotonics)`",
name
),
))
}
pub(crate) fn parse_foreign(args: InitArgs, item: ForeignItemFn) -> parse::Result<Self> {
let valid_signature = util::check_foreign_fn_signature(&item) && item.sig.inputs.len() == 1;

let span = item.sig.ident.span();

let name = item.sig.ident.to_string();

if valid_signature {
if let Ok((user_shared_struct, user_local_struct)) =
util::type_is_init_return(&item.sig.output, &name)
{
if let Some((context, Ok(rest))) = util::parse_inputs(item.sig.inputs, &name) {
if rest.is_empty() {
return Ok(Init {
args,
attrs: item.attrs,
context,
name: item.sig.ident,
stmts: Vec::<Stmt>::new(),
user_shared_struct,
user_local_struct,
is_extern: true,
});
}
}
Expand Down