Stabilize RFC 2345: Allow panicking in constants · Issue #89006 · rust-lang/rust...
source link: https://github.com/rust-lang/rust/issues/89006
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Stabilization report
Summary
This feature allows panicking within constants and reporting a custom message as a compile-time error.
The following code will become valid on stable:
const fn foo(val: u8) { assert!(val == 1, "val is not 1"); } // can be also computed by manually creating a `&[u8]` and // from_utf8_unchecked. const MSG: &str = "custom message"; const _: () = std::panic!("{}", MSG); const _: () = panic!("custom message");
The message looks like:
error: any use of this value will cause an error
--> src/main.rs:4:15
|
4 | const _: () = std::panic!("{}", MSG);
| --------------^^^^^^^^^^^^^^^^^^^^^^-
| |
| the evaluated program panicked at 'custom message', src/main.rs:4:15
|
= note: `#[deny(const_err)]` on by default
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800>
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
Motivation for this RFC can be found here: https://rust-lang.github.io/rfcs/2345-const-panic.html#motivation
More examples can be found here: https://github.com/rust-lang/rust/blob/673d0db5e393e9c64897005b470bfeb6d5aec61b/src/test/ui/consts/const-eval/const_panic.rs
Edge cases & tests
Some questions which should be resolved in the future
We have some "unresolved questions" but they aren't an actual blocker.
- Should there be some additional message in the error about this being a panic turned error?
Or do we just produce the exact message the panic would produce? - This change becomes really useful if
Result::unwrap
andOption::unwrap
becomeconst fn
, doing both in one go might be a good idea.
See the brief summary comment here: #51999 (comment)
Previous attempt: #85194 (This was blocked because it was not possible to use a generated panic message, only literal strings were allowed, which has been resolved in #88954)
Originally posted by @JohnTitor in #51999 (comment)
Should there be some additional message in the error about this being a panic turned error?
Or do we just produce the exact message the panic would produce?
My perspective as someone who is frequently trying to make good error messages:
- Yes, there should be some indication that the message text originated from user code — otherwise you'll get confused users asking “what does this compiler error mean” when using a library's
const fn
s. - On the other hand, it is very helpful to readability if the message is presented as a message in itself rather than a quoted fragment with both leading and trailing text inside a larger error message. Quotation with content escapes, and line wrapping, also harm scannability, and make it less likely that a user will understand that this is the relevant information. (Rust's run-time panics unfortunately already have this problem.)
For example, the existing example
4 | const _: () = std::panic!("{}", MSG); | --------------^^^^^^^^^^^^^^^^^^^^^^- | | | the evaluated program panicked at 'custom message', src/main.rs:4:15
would in my opinion be better formatted as
4 | const _: () = std::panic!("{}", MSG); | --------------^^^^^^^^^^^^^^^^^^^^^^- | | | the evaluated program panicked at src/main.rs:4:15 with: | custom message
The first sample has the virtue of more closely matching runtime behavior; but I think there should be generally higher expectations for readability from messages during compilation than those which must be formatted by handler code embedded in the final binary.
would in my opinion be better formatted as
4 | const _: () = std::panic!("{}", MSG); | --------------^^^^^^^^^^^^^^^^^^^^^^- | | | the evaluated program panicked at src/main.rs:4:15 with: | custom message
I agree. I'd also expect multiline panic messages to stay indented, so that they can be easily distinguished from the following compilation errors.
I would prefer if both const panic
s and compile_error
were consistent in that, and change this:
error:
hello
world
--> src/main.rs:1:1
|
1 | / compile_error!{"
2 | | hello
3 | | world
4 | | "}
| |__^
error: could not compile `playground` due to previous error
to the indented style you're proposing.
There was some long discussion about error formatting in a previous PR as well, more than a year ago... maybe someone can dig up the link.
I think const panic messages should be consistent with other kinds of errors that stop const-evaluation. So, I am not a fan of special-casing const panic error formatting. But I am open to improving const-eval error rendering in general.
Seems reasonable; let's get consensus:
@rfcbot merge
Team member @joshtriplett has proposed to merge this. The next step is review by the rest of the tagged team members:
No concerns currently listed.
Once a majority of reviewers approve (and at most 2 approvals are outstanding), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up!
See this document for info about what commands tagged team members can give me.
Important to note: This will make assert!(1 == 2, "...")
work in const, but assert_eq!(1, 2, "...")
will still not work, since it uses more complicated formatting.
It is not the scope of this issue but I would like to chime to draw the attention of also considering "constifying" Result::unwrap()
and Option::unwrap()
. Without them, people will probably write
const FOO: () = { let _ = match SOME_OPERATION { Err(_err) => panic!("SOME ERROR"), Ok(elem) => elem }; };
instead of the much simpler
const FOO: () = { let _ = SOME_OPERATION.unwrap(); };
Result::unwrap
is way out of reach for now since it formats the Err
value.
But Option::unwrap
and Option::expect
would be reasonable to stabilize I think -- they are basically forms of assert
.
@rfcbot reviewed
Copy link
rfcbot commented 19 days ago
This is now entering its final comment period, as per the review above.
I just checked: Option::unwrap
and Option::expect
can not be stabilized.
Ignoring the message that const_panic
is unstable, the compiler still complains:
error[E0493]: destructors cannot be evaluated at compile-time
--> library/core/src/option.rs:733:25
|
733 | pub const fn unwrap(self) -> T {
| ^^^^ constant functions cannot evaluate destructors
...
738 | }
| - value is dropped here
with the same error for expect
. Until impl const Drop
is a thing, it'll have to wait for stdlib. However, it would be possible to define a macro that does this. That is obviously not for stdlib to do.
With regard to Result
methods, even complex const-panic doesn't unblock anything. I have a full list of blockers up at #82814. Pretty much everything needs impl const Trait
.
@jhpratt Option::unwrap
doesn't only work with const Trait
bounds, it also works with precise drop tracking(which allows the compiler to figure out that no dropping actually happens), can't tell you which is likely to be stabilized earlier.
#![feature(const_panic)] #![feature(const_precise_live_drops)] pub const fn unwrap<T>(this: Option<T>) -> T { match this { Some(val) => val, None => panic!("called `Option::unwrap()` on a `None` value"), } }
Certainly. I suspected that was the error I was going to run into, actually. I just didn't bother to keep adding features once I confirmed my suspicion that it wouldn't compile with just const_panic
.
Copy link
Nugine commented 17 days ago
It's possible to make char::from_u32_unchecked
a const fn if we have const_panic
stablized.
^^ that would require const unwrap when written as-is
If there are the appropriate MIRI checks in place (I don't know if there are) it could just be a straight transmute, which is already stabilized. It could theoretically introduce UB in debug builds, but that's the caller's problem (don't use unchecked if it's not valid).
Copy link
Nugine commented 17 days ago
I found it quite simple to constify from_u32_unchecked
.
#![feature(const_panic)] pub const fn from_u32(i: u32) -> Option<char> { if (i > '\u{10ffff}' as u32) || (i >= 0xD800 && i <= 0xDFFF) { None } else { // SAFETY: checked that it's a legal unicode value Some(unsafe { core::mem::transmute(i) }) } } pub const unsafe fn from_u32_unchecked(i: u32) -> char { if cfg!(debug_assertions) { match from_u32(i) { Some(val) => val, None => panic!("illegal unicode value"), } } else { // SAFETY: checked that it's a legal unicode value core::mem::transmute(i) } }
Result::unwrap
is way out of reach for now since it formats theErr
value.But
Option::unwrap
andOption::expect
would be reasonable to stabilize I think -- they are basically forms ofassert
.
Thanks for the explanation, @RalfJung !
Although not yet in the scope of this issue, may I suggest also "constifying" Resull::ok
? In the absence of const Result::unwrap
, such method would be very useful.
const FOO: () = { let _ = SOME_RSLT.ok().unwrap(); };
Although not yet in the scope of this issue, may I suggest also "constifying" Resull::ok?
That will require const drop too. Result::ok()
drops the error variant.
Copy link
rfcbot commented 7 days ago
The final comment period, with a disposition to merge, as per the review above, is now complete.
As the automated representative of the governance process, I would like to thank the author for their work and everyone else who contributed.
This will be merged soon.
Just to avoid conflicts with others, I have a commit locally that stabilizes some things only blocked by this. Planning on opening PRs later tonight or tomorrow.
Just to avoid conflicts with others, I have a commit locally that stabilizes some things only blocked by this. Planning on opening PRs later tonight or tomorrow.
Are you planning a PR to stablizes const_panic
or just features that depend on const_panic
? This is a stablisation issue not a PR.
The latter; I was only planning on stabilizing things that depend on const_panic
(plus one that was blocked process-wise, not technically). I can submit the actual stabilization of const_panic
if desired, though.
Edit: Working on stabilizing the const_panic
feature first. May or may not be done tonight.
Copy link
tarcieri commented 6 days ago
With 9866b09 having landed, it looks like this can be closed?
Ah dang, missed the opportunity to link it in like I did the tracking issue. Yes, this can be closed.
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK