-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
stable mechanism to specify the behavior of panic! in no-std applications #2070
Merged
Merged
Changes from 1 commit
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,238 @@ | ||
- Feature Name: panic_implementation | ||
- Start Date: 2017-07-19 | ||
- RFC PR: (leave this empty) | ||
- Rust Issue: (leave this empty) | ||
|
||
# Summary | ||
[summary]: #summary | ||
|
||
Provide a stable mechanism to specify the behavior of `panic!` in no-std | ||
applications. | ||
|
||
# Motivation | ||
[motivation]: #motivation | ||
|
||
The `#![no_std]` attribute was stabilized some time ago and it made possible to | ||
build no-std libraries on stable. However, to this day no-std applications | ||
still require a nightly compiler to be built. The main cause of this is that | ||
the behavior of `panic!` is left undefined in no-std context, and the only way | ||
to specify a panicking behavior is through the unstable `panic_fmt` [language | ||
item]. | ||
|
||
[language item]: https://doc.rust-lang.org/unstable-book/language-features/lang-items.html | ||
|
||
This document proposes a stable mechanism to specify the behavior of `panic!` in | ||
no-std context. This would be a step towards enabling development of no-std | ||
applications like device firmware, kernels and operating systems on the stable | ||
channel. | ||
|
||
# Detailed design | ||
[design]: #detailed-design | ||
|
||
## Constraints | ||
|
||
`panic!` in no-std environments must continue to be free of memory allocations | ||
and [its API] can only be changed in a backward compatible way. | ||
|
||
[its API]: https://doc.rust-lang.org/core/macro.panic.html | ||
|
||
Although not a hard constraint, the cognitive load of the mechanism would be | ||
greatly reduced if it mimicked the existing [custom panic hook] mechanism as | ||
much as possible. | ||
|
||
[custom panic hook]: https://doc.rust-lang.org/std/panic/fn.set_hook.html | ||
|
||
## `PanicInfo` | ||
|
||
The types [`std::panic::PanicInfo`] and [`std::panic::Location`] will be moved | ||
into the `core` crate, and `PanicInfo` will gain a new method: | ||
|
||
[`std::panic::PanicInfo`]: https://doc.rust-lang.org/std/panic/struct.PanicInfo.html | ||
[`std::panic::Location`]: https://doc.rust-lang.org/std/panic/struct.Location.html | ||
|
||
``` rust | ||
impl PanicInfo { | ||
pub fn message(&self) -> Option<&fmt::Arguments> { .. } | ||
} | ||
``` | ||
|
||
This method returns `Some` if the `panic!` invocation needs to do any formatting | ||
like `panic!("{}: {}", key , value)` does. | ||
|
||
### `fmt::Display` | ||
|
||
For convenience, `PanicInfo` will gain an implementation of the `fmt::Display` | ||
trait that produces a message very similar to the one that the standard `panic!` | ||
hook produces. For instance, this program: | ||
|
||
``` rust | ||
use std::panic::{self, PanicInfo}; | ||
|
||
fn panic_handler(pi: &PanicInfo) { | ||
println!("the application {}", pi); | ||
} | ||
|
||
fn main() { | ||
panic::set_hook(Box::new(panic_handler)); | ||
|
||
panic!("Hello, {}!", "world"); | ||
} | ||
``` | ||
|
||
Would print: | ||
|
||
``` console | ||
$ cargo run | ||
the application panicked at 'Hello, world!', src/main.rs:27:4 | ||
``` | ||
|
||
## `#[panic_implementation]` | ||
|
||
A `#[panic_implementation]` attribute will be added to the language. This | ||
attribute can be used to specify the behavior of `panic!` in no-std context. | ||
Only functions with signature `fn(&PanicInfo) -> !` can be annotated with this | ||
attribute, and only one item can be annotated with this attribute in the whole | ||
dependency graph of a crate. | ||
|
||
Here's an example of how to replicate the panic messages one gets on std | ||
programs on a no-std program: | ||
|
||
``` rust | ||
use core::fmt; | ||
use core::panic::PanicInfo; | ||
|
||
// prints: "program panicked at 'reason', src/main.rs:27:4" | ||
#[panic_implementation] | ||
fn my_panic(pi: &PanicInfo) -> ! { | ||
let _ = writeln!(&MY_STDERR, "program {}", pi); | ||
|
||
abort() | ||
} | ||
``` | ||
|
||
The `#[panic_implementation]` item will roughly expand to: | ||
|
||
``` rust | ||
fn my_panic(pi: &PanicInfo) -> ! { | ||
// same as before | ||
} | ||
|
||
// Generated by the compiler | ||
// This will always use the correct ABI and will work on the stable channel | ||
#[lang = "panic_fmt"] | ||
#[no_mangle] | ||
pub extern fn rust_begin_panic(msg: ::core::fmt::Arguments, | ||
file: &'static str, | ||
line: u32, | ||
col: u32) -> ! { | ||
my_panic(&PanicInfo::__private_unstable_constructor(msg, file, line, col)) | ||
} | ||
``` | ||
|
||
## Payload | ||
|
||
The `core` version of the `panic!` macro will gain support for *payloads*, as in | ||
`panic!(42)`. When invoked with a payload `PanicInfo.payload()` will return the | ||
payload as an `&Any` trait object just like it does in std context with custom | ||
panic hooks. | ||
|
||
When using `core::panic!` with formatting, e.g. `panic!("{}", 42)`, the payload | ||
will be uninspectable: it won't be downcastable to any known type. This is where | ||
`core::panic!` diverges from `std::panic!`. The latter returns a `String`, | ||
behind the `&Any` trait object, from the `payload()` method in this situation. | ||
|
||
## Feature gate | ||
|
||
The initial implementation of the `#[panic_implementation]` mechanism as well as | ||
the `core::panic::Location` and `core::panic::PanicInfo` types will be feature | ||
gated. `std::panic::Location` and `std::panic::PanicInfo` will continue to be | ||
stable except for the new `PanicInfo.message` method. | ||
|
||
## Unwinding | ||
|
||
The `#[panic_implementation]` mechanism can only be used with no-std | ||
applications compiled with `-C panic=abort`. Applications compiled with `-C | ||
panic=unwind` additionally require the `eh_personality` language item which this | ||
proposal doesn't cover. | ||
|
||
## `std::panic!` | ||
|
||
This proposal doesn't affect how the selection of the panic runtime in `std` | ||
applications works (`panic_abort`, `panic_unwind`, etc.). Using | ||
`#[panic_implementation]` in `std` programs will cause a compiler error. | ||
|
||
# How We Teach This | ||
[how-we-teach-this]: #how-we-teach-this | ||
|
||
Currently, no-std applications are only possible on nightly so there's not much | ||
official documentation on this topic given its dependency on several unstable | ||
features. Hopefully once no-std applications are minimally possible on stable we | ||
can have a detailed chapter on the topic in ["The Rust Programming Language"] | ||
book. In the meantime, this feature can be documented in [the unstable book]. | ||
|
||
["The Rust Programming Language"]: https://doc.rust-lang.org/book/second-edition/ | ||
[the unstable book]: https://doc.rust-lang.org/unstable-book/ | ||
|
||
# Drawbacks | ||
[drawbacks]: #drawbacks | ||
|
||
## Slight deviation from std | ||
|
||
Although both `#[panic_implementation]` (no-std) and custom panic hooks (std) | ||
use the same `PanicInfo` type. The behavior of the `PanicInfo.payload()` method | ||
changes depending on which context it is used: given `panic!("{}", 42)`, | ||
`payload()` will return a `String`, behind an `Any` trait object, in std context | ||
but it will return an opaque `Any` trait object in no-std context. | ||
|
||
# Alternatives | ||
[alternatives]: #alternatives | ||
|
||
## Not doing this | ||
|
||
Not providing a stable alternative to the `panic_fmt` language item means that | ||
no-std applications will continue to be tied to the nightly channel. | ||
|
||
## Two `PanicInfo` types | ||
|
||
An alternative design is to have two different `PanicInfo` types, one in `core` | ||
and one in `std`. The difference between these two types would be in their APIs: | ||
|
||
``` rust | ||
// core | ||
impl PanicInfo { | ||
pub fn location(&self) -> Option<Location> { .. } | ||
pub fn message(&self) -> Option<&fmt::Arguments> { .. } | ||
|
||
// Not available | ||
// pub fn payload(&self) -> &(Any + Send) { .. } | ||
} | ||
|
||
// std | ||
impl PanicInfo { | ||
pub fn location(&self) -> Option<Location> { .. } | ||
pub fn message(&self) -> Option<&fmt::Arguments> { .. } | ||
pub fn payload(&self) -> &(Any + Send) { .. } | ||
} | ||
``` | ||
|
||
In this alternative design the signature of the `#[panic_implementation]` | ||
function would be enforced to be `fn(&core::panic::PanicInfo) -> !`. Custom | ||
panic hooks will continue to use the `std::panic::PanicInfo` type. | ||
|
||
This design precludes supporting payloads in `core::panic!` but also eliminates | ||
the difference between `core::PanicInfo.payload()` in no-std vs std by | ||
eliminating the method in the former context. | ||
|
||
# Unresolved questions | ||
[unresolved]: #unresolved-questions | ||
|
||
## `fmt::Display` | ||
|
||
Should the `Display` of `PanicInfo` format the panic information as `"panicked | ||
at 'reason', src/main.rs:27:4"`, as `"'reason', src/main.rs:27:4"`, or simply as | ||
`"reason"`. | ||
|
||
## Unwinding in no-std | ||
|
||
Is this design compatible, or can it be extended to work, with unwinding | ||
implementations for no-std environments? |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
Why is that important?
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.
We don't want to require allocations for
no_std
targets