|
| 1 | +- Feature Name: panic_implementation |
| 2 | +- Start Date: 2017-07-19 |
| 3 | +- RFC PR: https://github.com/rust-lang/rfcs/pull/2070 |
| 4 | +- Rust Issue: https://github.com/rust-lang/rust/issues/44489 |
| 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