-
Notifications
You must be signed in to change notification settings - Fork 85
turn macros into attributes #90
Conversation
These attributes can be used on the latest nightly without any feature gate ( This is a breaking change because it removes the macros by example. Instead of directly breaking the rt crate we could have an intermediate test phase where we first publish the rt-macros crate. Then people would be able to test out the attributes by changing their programs in the following way: #![no_main]
#![no_std]
-#[macro_use(entry)]
-extern crate cortex_m_rt as rt;
+extern crate cortex_m_rt_macros as rt;
extern crate panic_semihosting;
-entry!(main);
+use rt::entry;
+#[entry]
fn main() -> ! {
loop {}
} Though I'm not sure if there's any advantage to doing the release that way. |
macros/src/lib.rs
Outdated
pub fn #ident() { | ||
extern crate cortex_m_rt; | ||
|
||
cortex_m_rt::Exception::#name; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@adamgreig this is reason why an hypothetical #[interrupt]
attribute would need the device crate name in its argument. We want to check that the interrupt exists and emit a compiler error if it doesn't and for that we need the full path to the Interrupt
variant: e.g. stm32f103xx::Interrupt::EXTI0
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense. I guess we could require that whatever is passed in (to some future #[interrupt]
) is resolvable to an Interrupt
, so it could be either a full path, or the user could use stm32f103::Interrupt::*
and then just call #[interrupt(EXTI0)]
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the user could use stm32f103::Interrupt::* and then just call #[interrupt(EXTI0)]?
If we omit the full path that would kind of defeat the purpose of the check. For example, this would compile:
struct InterruptThatDoesntExist;
#[interrupt(InterruptThatDoesntExist)]
fn handler() { /* .. */ }
Though thinking about it more having the user enter the name of the device crate makes it possible to nullify the check. For example,
extern crate stm32f103xx; // real device crate
mod stm32f1337 {
enum Interrupt {
InterruptThatDoesntExist;
}
}
#[interrupt(stm32f1337, InterruptThatDoesntExist)]
fn handler() { /* .. */ }
examples/state.rs
Outdated
|
||
fn sys_tick(state: &mut u32) { | ||
*state += 1; | ||
#[exception(SysTick, static STATE: u32 = 0)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
An alternative syntax would be to have the second argument be a string. i.e.
#[exception(SysTick, "static STATE: u32 = 0")]
fn sys_tick() {}
But the current syntax seems to parse without problems and have better spans for error messages. e.g.
#[exception(SysTick, static STATE: u32 = true)]
// ~~~~ error: expected `u32`, found `bool`
fn sys_tick() {}
@japaric Lovely, I like it! |
Error messages are terrible by default because all the span information is lost. By this I mean that any compile error caused by the body of e.g. an exception function will be reported with the span of the attribute. Example: #[entry]
// ~~~~~ error: mismatched types, expected `!`, found `()`
fn main() -> ! {
asm::bkpt()
} To improve you can opt into the # Cargo.toml
[build-dependencies.proc-macro2]
version = "0.4"
features = ["nightly"] But, as the name of the feature implies, this requires a nightly compiler. The error message then becomes what you would expect: #[entry]
fn main() -> ! {
asm::bkpt()
// ~~~~~~~~~~~ error: mismatched types, expected `!`, found `()`
} I don't know if it's planned to stabilize these span improvements for the edition release. |
I have rebased this PR. @rust-embedded/cortex-m Please vote on RFC #82 by reviewing (approving) this PR. Amendments to #82
#[exception(SysTick, static STATE: u32 = 0)]
fn sys_tick() { // <- note that the function has no argument
*STATE += 1; // but that `STATE` can be referenced only within this function
} Also a reminder that both the |
I just thought of an alternative syntax for adding state that may feel less strange: #[exception(SysTick)]
fn sys_tick() {
static mut STATE: u32 = 0;
// user code
*STATE += 1;
} The attribute will magically make #[export_name = "SysTick"]
pub fn sys_tick() {
// <expansion>
static mut STATE_: u32 = 0;
let STATE: &mut u32 = unsafe { &mut STATE_ };
// </expansion>
// user code
*STATE += 1;
} |
And now I'm seeing a problem with the current implementation: it allows data races in safe code. For example: #[exception(SysTick)]
fn sys_tick() {
static mut STATE: u64 = 0;
// user code
*STATE += 1;
}
#[entry]
fn main() -> ! {
loop {
sys_tick();
}
} Here The fix is to either (a) make #[exception]
fn SysTick() {
static mut STATE: u32 = 0;
// user code
*STATE += 1;
}
#[entry]
fn main() -> ! {
loop {
SysTick(); //~ error: this function doesn't exist
}
} We can use the function name as the exception name; that way we don't need an argument. The above code would expand to: #[export_name = "SysTick"]
pub fn cf1dglqbt3jr7uiu() { // some randomly generated string that the user can't rely on
// <expansion>
static mut STATE_: u32 = 0;
let STATE: &mut u32 = unsafe { &mut STATE_ };
// </expansion>
// user code
*STATE += 1;
}
#[entry]
fn main() -> ! {
SysTick(); // error: this function doesn't exist
} |
I'm not sure I'm a fan of "magically" making statics safe, but I also do not have any good input for a better solution, though the original idea was quite nice on the eyes and properly conveys that there is a state. |
@korken89 with the With the static inside the function it's immediately clear that it can't be referred to from outside the function. I believe that making it safe to access it's not surprising exactly because it's local to the function. |
I see, and agree. Having the static in the function would indeed be cleaner. |
@japaric Yes, please! The local state definition for the interrupt handlers was very weird in the past and caused lots of weird syntactic problems with non-integral types which should be all gone with the new syntax. This is very similar to something a C/C++ programmer might do, too, so that's also a big plus. And last but not least this reduces the boilerplating quite a bit and makes things safer (by preventing calling an interrupt handler from another function) so goodies all around! |
OK, I have made the exception handlers uncallable and also change the syntax of |
Also, we can use the |
extend / update documentation
latest commit makes it safe to access local @therealprof @adamgreig @korken89 in #82 it sounded like you were in favor of this change. Could you confirm? @thejpster @ithinuel I'd love to get your input on RFC #82 and this implementation. Are you in favor of that RFC or do you have concerns about it? |
I really like the new no-argument attributes and the new syntax for state. Having mutable statics work in 👍 from me! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fantastic! 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good, great work!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM :) The macros are definitely fancier.
My only "concern" would be about the stability of proc-macro that I don't know. Other than that it looks great !
The proc macros I'm using here are planned to be stable by the 2018 edition; they don't require feature gates in nightly; and they won't require features gates in RC1 (next beta) and RC2 (next next beta). Alright, thanks for the input everyone. We have majority so this is officially accepted. I'm going to add some compile fail tests then any of you can review and land this. |
we'll test on beta when 1.30-beta is out
they are no longer required due to rust-lang/rust#52467
de35ebb
This should be ready to land, but while adding the compile-fail tests I noticed that we could relax some constraints. I'll leave some inline comments about that. |
|
||
#[exception] //~ ERROR custom attribute panicked | ||
//~^ HELP `DefaultHandler` exception must have signature `fn(i16)` | ||
unsafe fn DefaultHandler(_irqn: i16) {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we want to allow this? I don't see a problem with letting the user state this is unsafe
because this function can't be called from software anyways.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 from me, no reason to not permit this, and I wouldn't be surprised if a lot of unsafe code in embedded projects is in the ISRs.
|
||
#[exception] //~ ERROR custom attribute panicked | ||
//~^ HELP `DefaultHandler` exception must have signature `fn(i16)` | ||
fn DefaultHandler(_irqn: i16) -> ! { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we want to allow this? I see no problem with letting this be a divergent function.
|
||
#[entry] //~ ERROR custom attribute panicked | ||
//~^ HELP `#[entry]` function must have signature `fn() -> !` | ||
unsafe fn foo() -> ! { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we want to allow this? Same rationale as allowing unsafe
DefaultHandler
.
|
||
#[exception] //~ ERROR custom attribute panicked | ||
//~^ HELP `#[exception]` functions other than `DefaultHandler` and `HardFault` must have signature `fn()` | ||
unsafe fn SysTick() {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we want to allow this? Same rationale as unsafe
DefaultHandler
|
||
#[exception] //~ ERROR custom attribute panicked | ||
//~^ HELP `#[exception]` functions other than `DefaultHandler` and `HardFault` must have signature `fn()` | ||
fn SysTick() -> ! { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we want to allow this? Same rationale as divergent DefaultHandler
|
||
#[exception] //~ ERROR custom attribute panicked | ||
//~^ HELP `HardFault` exception must have signature `fn(&ExceptionFrame) -> !` | ||
unsafe fn HardFault(_ef: &ExceptionFrame) -> ! { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we want to allow this? Same rationale as unsafe
DefaultHandler
Rather than commenting inline on each one: I'm happy to allow all of those unsafe/divergent functions. There's no safety reasons not to, so I'd rather let the user decide. |
since we can't statically check that it holds
Ok, I have relaxed the signature checks and added more compile-fail tests and examples (which I'm using as compile-pass tests). I have also made the reachability requirement more prominent in the docs. |
Looks great! I've gone through the new code and also tested this branch on a few small examples on hardware with exceptions and pre_init and everything seems to work as expected. bors r+ |
90: turn macros into attributes r=adamgreig a=japaric This is a PoC implementation of RFC #82. Assuming that we are OK with this implementation we should add some compile fail tests (e.g. using a function with the wrong signature as the entry point) before landing this. Look at the diff of the examples to get an overview of the changes. cc @rust-embedded/cortex-m Co-authored-by: Jorge Aparicio <jorge@japaric.io>
Build succeeded |
This is a PoC implementation of RFC #82. Assuming that we are OK with this implementation we should
add some compile fail tests (e.g. using a function with the wrong signature as the entry point)
before landing this.
Look at the diff of the examples to get an overview of the changes.
cc @rust-embedded/cortex-m