Skip to content

Commit 06c13df

Browse files
committed
stable mechanism to specify the behavior of panic! in no-std applications.
1 parent a099f17 commit 06c13df

File tree

1 file changed

+238
-0
lines changed

1 file changed

+238
-0
lines changed

text/0000-panic-implementation.md

+238
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
1+
- Feature Name: panic_implementation
2+
- Start Date: 2017-07-19
3+
- RFC PR: (leave this empty)
4+
- Rust Issue: (leave this empty)
5+
6+
# Summary
7+
[summary]: #summary
8+
9+
Provide a stable mechanism to specify the behavior of `panic!` in no-std
10+
applications.
11+
12+
# Motivation
13+
[motivation]: #motivation
14+
15+
The `#![no_std]` attribute was stabilized some time ago and it made possible to
16+
build no-std libraries on stable. However, to this day no-std applications
17+
still require a nightly compiler to be built. The main cause of this is that
18+
the behavior of `panic!` is left undefined in no-std context, and the only way
19+
to specify a panicking behavior is through the unstable `panic_fmt` [language
20+
item].
21+
22+
[language item]: https://doc.rust-lang.org/unstable-book/language-features/lang-items.html
23+
24+
This document proposes a stable mechanism to specify the behavior of `panic!` in
25+
no-std context. This would be a step towards enabling development of no-std
26+
applications like device firmware, kernels and operating systems on the stable
27+
channel.
28+
29+
# Detailed design
30+
[design]: #detailed-design
31+
32+
## Constraints
33+
34+
`panic!` in no-std environments must continue to be free of memory allocations
35+
and [its API] can only be changed in a backward compatible way.
36+
37+
[its API]: https://doc.rust-lang.org/core/macro.panic.html
38+
39+
Although not a hard constraint, the cognitive load of the mechanism would be
40+
greatly reduced if it mimicked the existing [custom panic hook] mechanism as
41+
much as possible.
42+
43+
[custom panic hook]: https://doc.rust-lang.org/std/panic/fn.set_hook.html
44+
45+
## `PanicInfo`
46+
47+
The types [`std::panic::PanicInfo`] and [`std::panic::Location`] will be moved
48+
into the `core` crate, and `PanicInfo` will gain a new method:
49+
50+
[`std::panic::PanicInfo`]: https://doc.rust-lang.org/std/panic/struct.PanicInfo.html
51+
[`std::panic::Location`]: https://doc.rust-lang.org/std/panic/struct.Location.html
52+
53+
``` rust
54+
impl PanicInfo {
55+
pub fn message(&self) -> Option<&fmt::Arguments> { .. }
56+
}
57+
```
58+
59+
This method returns `Some` if the `panic!` invocation needs to do any formatting
60+
like `panic!("{}: {}", key , value)` does.
61+
62+
### `fmt::Display`
63+
64+
For convenience, `PanicInfo` will gain an implementation of the `fmt::Display`
65+
trait that produces a message very similar to the one that the standard `panic!`
66+
hook produces. For instance, this program:
67+
68+
``` rust
69+
use std::panic::{self, PanicInfo};
70+
71+
fn panic_handler(pi: &PanicInfo) {
72+
println!("the application {}", pi);
73+
}
74+
75+
fn main() {
76+
panic::set_hook(Box::new(panic_handler));
77+
78+
panic!("Hello, {}!", "world");
79+
}
80+
```
81+
82+
Would print:
83+
84+
``` console
85+
$ cargo run
86+
the application panicked at 'Hello, world!', src/main.rs:27:4
87+
```
88+
89+
## `#[panic_implementation]`
90+
91+
A `#[panic_implementation]` attribute will be added to the language. This
92+
attribute can be used to specify the behavior of `panic!` in no-std context.
93+
Only functions with signature `fn(&PanicInfo) -> !` can be annotated with this
94+
attribute, and only one item can be annotated with this attribute in the whole
95+
dependency graph of a crate.
96+
97+
Here's an example of how to replicate the panic messages one gets on std
98+
programs on a no-std program:
99+
100+
``` rust
101+
use core::fmt;
102+
use core::panic::PanicInfo;
103+
104+
// prints: "program panicked at 'reason', src/main.rs:27:4"
105+
#[panic_implementation]
106+
fn my_panic(pi: &PanicInfo) -> ! {
107+
let _ = writeln!(&MY_STDERR, "program {}", pi);
108+
109+
abort()
110+
}
111+
```
112+
113+
The `#[panic_implementation]` item will roughly expand to:
114+
115+
``` rust
116+
fn my_panic(pi: &PanicInfo) -> ! {
117+
// same as before
118+
}
119+
120+
// Generated by the compiler
121+
// This will always use the correct ABI and will work on the stable channel
122+
#[lang = "panic_fmt"]
123+
#[no_mangle]
124+
pub extern fn rust_begin_panic(msg: ::core::fmt::Arguments,
125+
file: &'static str,
126+
line: u32,
127+
col: u32) -> ! {
128+
my_panic(&PanicInfo::__private_unstable_constructor(msg, file, line, col))
129+
}
130+
```
131+
132+
## Payload
133+
134+
The `core` version of the `panic!` macro will gain support for *payloads*, as in
135+
`panic!(42)`. When invoked with a payload `PanicInfo.payload()` will return the
136+
payload as an `&Any` trait object just like it does in std context with custom
137+
panic hooks.
138+
139+
When using `core::panic!` with formatting, e.g. `panic!("{}", 42)`, the payload
140+
will be uninspectable: it won't be downcastable to any known type. This is where
141+
`core::panic!` diverges from `std::panic!`. The latter returns a `String`,
142+
behind the `&Any` trait object, from the `payload()` method in this situation.
143+
144+
## Feature gate
145+
146+
The initial implementation of the `#[panic_implementation]` mechanism as well as
147+
the `core::panic::Location` and `core::panic::PanicInfo` types will be feature
148+
gated. `std::panic::Location` and `std::panic::PanicInfo` will continue to be
149+
stable except for the new `PanicInfo.message` method.
150+
151+
## Unwinding
152+
153+
The `#[panic_implementation]` mechanism can only be used with no-std
154+
applications compiled with `-C panic=abort`. Applications compiled with `-C
155+
panic=unwind` additionally require the `eh_personality` language item which this
156+
proposal doesn't cover.
157+
158+
## `std::panic!`
159+
160+
This proposal doesn't affect how the selection of the panic runtime in `std`
161+
applications works (`panic_abort`, `panic_unwind`, etc.). Using
162+
`#[panic_implementation]` in `std` programs will cause a compiler error.
163+
164+
# How We Teach This
165+
[how-we-teach-this]: #how-we-teach-this
166+
167+
Currently, no-std applications are only possible on nightly so there's not much
168+
official documentation on this topic given its dependency on several unstable
169+
features. Hopefully once no-std applications are minimally possible on stable we
170+
can have a detailed chapter on the topic in ["The Rust Programming Language"]
171+
book. In the meantime, this feature can be documented in [the unstable book].
172+
173+
["The Rust Programming Language"]: https://doc.rust-lang.org/book/second-edition/
174+
[the unstable book]: https://doc.rust-lang.org/unstable-book/
175+
176+
# Drawbacks
177+
[drawbacks]: #drawbacks
178+
179+
## Slight deviation from std
180+
181+
Although both `#[panic_implementation]` (no-std) and custom panic hooks (std)
182+
use the same `PanicInfo` type. The behavior of the `PanicInfo.payload()` method
183+
changes depending on which context it is used: given `panic!("{}", 42)`,
184+
`payload()` will return a `String`, behind an `Any` trait object, in std context
185+
but it will return an opaque `Any` trait object in no-std context.
186+
187+
# Alternatives
188+
[alternatives]: #alternatives
189+
190+
## Not doing this
191+
192+
Not providing a stable alternative to the `panic_fmt` language item means that
193+
no-std applications will continue to be tied to the nightly channel.
194+
195+
## Two `PanicInfo` types
196+
197+
An alternative design is to have two different `PanicInfo` types, one in `core`
198+
and one in `std`. The difference between these two types would be in their APIs:
199+
200+
``` rust
201+
// core
202+
impl PanicInfo {
203+
pub fn location(&self) -> Option<Location> { .. }
204+
pub fn message(&self) -> Option<&fmt::Arguments> { .. }
205+
206+
// Not available
207+
// pub fn payload(&self) -> &(Any + Send) { .. }
208+
}
209+
210+
// std
211+
impl PanicInfo {
212+
pub fn location(&self) -> Option<Location> { .. }
213+
pub fn message(&self) -> Option<&fmt::Arguments> { .. }
214+
pub fn payload(&self) -> &(Any + Send) { .. }
215+
}
216+
```
217+
218+
In this alternative design the signature of the `#[panic_implementation]`
219+
function would be enforced to be `fn(&core::panic::PanicInfo) -> !`. Custom
220+
panic hooks will continue to use the `std::panic::PanicInfo` type.
221+
222+
This design precludes supporting payloads in `core::panic!` but also eliminates
223+
the difference between `core::PanicInfo.payload()` in no-std vs std by
224+
eliminating the method in the former context.
225+
226+
# Unresolved questions
227+
[unresolved]: #unresolved-questions
228+
229+
## `fmt::Display`
230+
231+
Should the `Display` of `PanicInfo` format the panic information as `"panicked
232+
at 'reason', src/main.rs:27:4"`, as `"'reason', src/main.rs:27:4"`, or simply as
233+
`"reason"`.
234+
235+
## Unwinding in no-std
236+
237+
Is this design compatible, or can it be extended to work, with unwinding
238+
implementations for no-std environments?

0 commit comments

Comments
 (0)