|
| 1 | +- Feature Name: `const_panic` |
| 2 | +- Start Date: 2018-02-22 |
| 3 | +- RFC PR: [rust-lang/rfcs#2345](https://github.com/rust-lang/rfcs/pull/2345) |
| 4 | +- Rust Issue: [rust-lang/rust#51999](https://github.com/rust-lang/rust/issues/51999) |
| 5 | + |
| 6 | +# Summary |
| 7 | +[summary]: #summary |
| 8 | + |
| 9 | +Allow the use of `panic!`, `assert!` and `assert_eq!` within constants and |
| 10 | +report their evaluation as a compile-time error. |
| 11 | + |
| 12 | +# Motivation |
| 13 | +[motivation]: #motivation |
| 14 | + |
| 15 | +It can often be desirable to terminate a constant evaluation due to invalid |
| 16 | +arguments. Currently there's no way to do this other than to use `Result` to |
| 17 | +produce an `Err` in case of errors. Unfortunately this will end up as a runtime |
| 18 | +problem and not abort compilation, even though the problem has been detected at |
| 19 | +compile-time. There are already ways to abort compilation, e.g. by invoking |
| 20 | +`["some assert failed"][42]` within a constant, which will abort with a |
| 21 | +compile-time error pointing at the span of the index operation. But this hack is |
| 22 | +not very convenient to use and produces the wrong error message. |
| 23 | + |
| 24 | +# Guide-level explanation |
| 25 | +[guide-level-explanation]: #guide-level-explanation |
| 26 | + |
| 27 | +You can now use `panic!` and `assert!` within `const fn`s. This means that when |
| 28 | +the const fn is invoked at runtime, you will get a regular panic, but if it is |
| 29 | +invoked at compile-time, the panic message will show up as an error message. |
| 30 | + |
| 31 | +As an example, imagine a function that converts strings to their corresponding |
| 32 | +booleans. |
| 33 | + |
| 34 | +```rust |
| 35 | +const fn parse_bool(s: &str) -> bool { |
| 36 | + match s { |
| 37 | + "true" => true, |
| 38 | + "false" => false, |
| 39 | + other => panic!("`{}` is not a valid bool"), |
| 40 | + } |
| 41 | +} |
| 42 | +parse_bool("true"); |
| 43 | +parse_bool("false"); |
| 44 | +parse_bool("foo"); |
| 45 | +``` |
| 46 | + |
| 47 | +will produce an error with your custom error message: |
| 48 | + |
| 49 | +``` |
| 50 | +error[E0080]: `foo` is not a valid bool |
| 51 | + --> src/main.rs: 5:25 |
| 52 | + | |
| 53 | +5 | other => panic!("`{}` is not a valid bool"), |
| 54 | + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
| 55 | +note: during the evaluation of |
| 56 | + | |
| 57 | +10 | parse_bool("foo"); |
| 58 | + | ^^^^^^^^^^^^^^^^^ |
| 59 | +``` |
| 60 | + |
| 61 | +# Reference-level explanation |
| 62 | +[reference-level-explanation]: #reference-level-explanation |
| 63 | + |
| 64 | +MIR interpretation gets a special case for the panic machinery (which isn't |
| 65 | +const fn). If the `panic` lang item is entered, instead of producing an error |
| 66 | +about it not being const fn, we produce a specialized error with the panic's |
| 67 | +message. This panic reporting machinery is already present in the mir |
| 68 | +interpreter, but needs the lang item detection in order to work. |
| 69 | + |
| 70 | +Note that this internal machinery is inherently unstable and thus never |
| 71 | +invoked directly by users. Users will use the `panic!` macro as an entry point. |
| 72 | +The internal details of the panic handling might change in the future, but always |
| 73 | +in a way that will keep allowing MIR interpretation to evaluate it. All future |
| 74 | +changes will have to address this directly and regression tests should ensure |
| 75 | +that we never break the const evaluability. |
| 76 | + |
| 77 | +# Drawbacks |
| 78 | +[drawbacks]: #drawbacks |
| 79 | + |
| 80 | +We have to implement some magic around processing `fmt::Arguments` objects and |
| 81 | +producing the panic message from that. |
| 82 | + |
| 83 | +# Rationale and alternatives |
| 84 | +[alternatives]: #alternatives |
| 85 | + |
| 86 | +* We could add a special constant error reporting mechanism. This has the |
| 87 | + disadvantage of widening the gap between const eval and runtime execution. |
| 88 | +* We could make `String` and formatting const enough to allow the panic |
| 89 | + formatting machinery to be interpreted and made const fn |
| 90 | +* Don't produce a good error message, just say "const eval encountered an error" |
| 91 | + and point the user to the panic location. This already works out of the box |
| 92 | + right now. We can improve the error message in the future with the `String` + |
| 93 | + formatting alternative. This is the most minimalistic alternative to this RFC |
| 94 | + |
| 95 | +# Unresolved questions |
| 96 | +[unresolved]: #unresolved-questions |
| 97 | + |
| 98 | +* Should there be some additional message in the error about this being a panic |
| 99 | + turned error? Or do we just produce the exact message the panic would produce? |
| 100 | + |
| 101 | +* This change becomes really useful if `Result::unwrap` and `Option::unwrap` |
| 102 | + become const fn, doing both in one go might be a good idea |
0 commit comments