Closed Centril closed 3 years ago
and we need to document this feature
The link to the stabilization guide on Forge is broken in the top-level issue. Is there a new reference for writing feature documentation?
@abonander Currently, the stabilization guide is here: https://rustc-dev-guide.rust-lang.org/stabilization_guide.html
DISCLAIMER: there are active discussions about where we want this kind of content to live long-term...
I think that the answer to the question is: yes, there should be an error message or an explanation that "frames" the error as a panic that happened in a constant. But preferably something more lightweight than what the current message looks like.
Anything wrong with just switching the message placement? E.g. from this:
error[E0080]: could not evaluate static initializer
--> src/main.rs:3:21
|
3 | static FOO: usize = panic!("Hello, world!");
| ^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'Hello, world!', src/main.rs:3:21
|
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
To this:
error[E0080]: Hello, world!
--> src/main.rs:3:21
|
3 | static FOO: usize = panic!("Hello, world!");
| ^^^^^^^^^^^^^^^^^^^^^^^ panic in static initializer
|
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
I don't think there's any ambiguity here. The error is formatted as a compiler-error and the context message explains why it was a compiler error.
I like it. It's radical but powerful. It will just be fun to watch how ppl will use it to emit fake compiler errors by inserting newlines and color characters in their error messages, causing complete confusion (they could have done so with the way we emit messages currently, too, so this is nothing new, they can just do it more conveniently).
Yeah, error[E0080]: could not evaluate static initializer
is not a terribly useful error "title". IMO we should change that for non-panic errors, too, and say more about what went wrong. In Miri, I changed errors to start like
error: unsupported operation: `clock_gettime` not available when isolation is enabled
I like it. It's radical but powerful. It will just be fun to watch how ppl will use it to emit fake compiler errors by inserting newlines and color characters in their error messages, causing complete confusion (they could have done so with the way we emit messages currently, too, so this is nothing new, they can just do it more conveniently).
@oli-obk is that potentially a concern, if people begin to rely on exactly where the compiler emits their const panic message? I guess we can easily disclaim that: "the exact rendering of the panic message is not considered stable" or something like that.
That seems like an unreasonable fear. People can use many parts of the language poorly, and we trust users to avoid crates that give them a bad experience.
But also, yes, the exact rendering should be explicitly classified as a "debug message" and thus subject to potential change without it being a "breaking change".
Is there a reason not to guarantee that panics in constants will display this way? It would be kind of awesome to have a way for people to generate custom error messages...
I would like to not guarantee a specific display because I would like rustc to one day be able to detect color change ansi control codes and strip them out of the output when color output is turned off or automatically detected to be off.
And in general, specifying the exact nature of a debug message just isn't the usual for Rust.
I think saying "the panic message is shown to the user in some way as a compile error" is specific enough.
Even if we don't want to lock ourselves into anything, giving a reasonable default, like the one proposed by @abonander, seems like a good idea.
Could the format string allow specifying the names of the printed variables like in RFC 2795 ? That way one can use specific variables in scope,and put them in any order. This is all assuming that variables in scope can be printed with the panicking macros, as implied by the guide level explanation of the const-panic RFC.
Currently const-panics only allow string constants, no format string at all. That is still way off at this point I am afraid.
The format string syntax will be exactly the same as for run-time formatting.
Should the error include a stack trace? Otherwise trouble shooting panics that happened deep inside a const fn might be difficult to diagnose. (I don't think this is needed at release, but should be considered as a later enhancement)
We already emit a stack trace: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=b0f5cc58e32119df1f116595783db08e
8 | [()][x]
| ^^^^^^^
| |
| index out of bounds: the len is 1 but the index is 4
| inside `foo` at src/main.rs:8:5
| inside `bar` at src/main.rs:4:5
| inside `FOO` at src/main.rs:1:17
There is a workaround for this issue now in stable 1.46 that indexes outside of an array when we want to panic:
// reduced example for my usecase that involves wrappers around `NonZeroUsize`
pub const fn bw(width: usize) -> NonZeroUsize {
["Tried to construct an invalid BitWidth of 0 using the `apint::bw` function"][(width == 0) as usize];
unsafe {NonZeroUsize::new_unchecked(width)}
}
When executed at compile time, it will show the message in the string, but unfortunately the runtime error is unhelpful.
@AaronKutch But this compiles in Rust 1.33 https://rust.godbolt.org/z/fThezn
use std::num::NonZeroUsize;
// reduced example for my usecase that involves wrappers around `NonZeroUsize`
const fn bw(width: usize) -> NonZeroUsize {
["Tried to construct an invalid BitWidth of 0 using the `apint::bw` function"][(width == 0) as usize];
unsafe {NonZeroUsize::new_unchecked(width)}
}
pub static FOO: NonZeroUsize = bw(30);
Also, replacing the 30
with a 0
errors with index out of bounds as expected.
I should have stated that it probably compiled before the current stable, but I did not know it would compile that long ago.
I noticed that one is required to pass a string literal to core::panic
, needing to use std::panic
to panic with a const S: &str = ....;
, is this going to be fixed soon?
I made a crate for doing compile-time formatting, and find it kind of annoying that the assertc
macro for compile-time assertions won't be usable with #![no_std]
crates once it's released, because of that limitation in core::panic
.
https://github.com/rodrimati1992/const_format_crates/tree/assertions#formatted-const-panics
@rodrimati1992 if you are asking whether it will be possible to use format strings for const panics, the answer is -- not for a long while. Format strings are extremely complicated. The const-eval skill tree show the dependency chain to get panics with formatting to work.
Or maybe you are asking something else (I am confused by your emphasis on core::panic
vs std::panic
), in which case it would help if you had a self-contained example that you think should work.
I am asking about panicking in a #[no_std]
crate with a &'static str
const
ant.
Example:
const POINT: Point = Point{x: 3, y: 5};
const _: () = {
const S: &str = const_format::formatc!("The point is misaligned: {:?}", POINT);
if POINT.x%2==0 { panic!(S) }
};
This is the kind of code that I can write today with std
but not in #![no_std]
,
probably because nobody anticipated that I would make my own formatting machinery that works before const allocations or traits in const fns do.
probably because nobody anticipated that I would make my own formatting machinery that works before const allocations or traits in const fns do.
Yeah, probably.^^ I was not aware this is a thing, impressive.
But, what do you need to make this work no_std
? Again, a self-contained example would help understand the limitation here. Something that doesn't depend on your crate, but demonstrates the problem.
Well, a self contained example would be:
#![feature(const_panic)]
//#![no_std]
use core::mem;
// Let's assume that FOO and BAR come from a dependency
const FOO: (usize, &str) = (mem::size_of::<usize>(), "foo is smaller than bar");
const BAR: (usize, &str) = (mem::size_of::<u32>(), "bar is smaller than foo");
const _: () = {
if FOO.0 < BAR.0 {
panic!(FOO.1)
} else if FOO.0 > BAR.0 {
panic!(BAR.1)
}
};
Uncomment the #![no_std]
line and you'll get errors about how it tries to do something non-const.
This is because the different expansions of panic! in std and core.
In std, panic! is expanded to ::std::rt::begin_panic
, in core it is ::core::panicking::panic_fmt
.
You might want to use core::panicking::panic
instead of panic!.
It's not panic_fmt
though since no format string is used. The problem is ::core::panicking::panic
.
Probably this line needs an exception for that function:
It's not
panic_fmt
though since no format string is used
It is what I get from expanded
: https://rust.godbolt.org/z/rznTzh
Oh right, these are not string literals... I am actually surprised this works in std::panic!
.
Also what is this:
https://github.com/rust-lang/rust/blob/c3364780d2cfddfe329f62a3ec138fd4f9a60e27/library/core/src/macros/mod.rs#L9
I never saw literal
as a macro argument class before.^^
Okay so this works with both the core and std macro:
#![feature(const_panic)]
const FOO:() = std::panic!("hello");
const BAR:() = core::panic!("hello");
AFAIK that is the only part that is intended to work. The fact that std::panic!
works with more arguments is, I think, an accident that might be hard to replicate for core::panic!
... Cc @oli-obk
Oh, this was actually broken by https://github.com/rust-lang/rust/pull/74056. Before that PR, core::panic!
with one argument expanded to ::panicking::panic
and that worked during const-time. It seems we didn't have a test like this though:
#![feature(const_panic)]
const MSG: &str = "hello";
const FOO:() = std::panic!(MSG);
const BAR:() = core::panic!(MSG);
So the regression went unnoticed. That said, it is still unclear to me whether this was ever supposed to work -- and it looks really hard to make this work again after #74056.
Since we're talking about const panics on non-literals; this currently gives an ICE:
#![feature(const_panic)]
const _: () = panic!(123);
Edit: Ah, this was already reported as #66693. Sorry for the noise.
Since we're talking about const panics on non-literals; this currently gives an ICE:
Yup, that's https://github.com/rust-lang/rust/issues/66693.
Okay so this works with both the core and std macro:
#![feature(const_panic)]
const FOO:() = std::panic!("hello");
const BAR:() = core::panic!("hello");
Curious how hard it would be to stabilize this much. It's really all I need for now. I've gone ahead and used the array hack to achieve the same effect, but it'd be nice to have something which provides a reasonable panic message when used in non-const
contexts as well.
@rodrimati1992
I noticed that one is required to pass a string literal to core::panic, needing to use std::panic to panic with a const S: &str = ....;, is this going to be fixed soon?
So after having done the investigation here and at https://github.com/rust-lang/rust/issues/67984#issuecomment-687611718, this is very far from an easy issue to fix. I see no good way to resolve the problem. This is technically regression, but of a nightly-only feature, so probably not a strong enough argument to revert anything, given the arguments in favor of the change that caused the regression.
@tarcieri One blocking problem is adjusting the const checker to ensure that the argument is indeed a string literal, thus avoiding the ICE. I'm afraid that will break @rodrimati1992's approach; we could still allow that under a separate flag.
Once that's done, I think there was still some discussion around how exactly to print the panic message to the user, but I see no major blockers. I'd prefer to have at least somewhat settled promotion before we stabilize more ways in which CTFE can fail (Cc https://github.com/rust-lang/const-eval/issues/53), but that might be asking too much.
The const non-literal core::panic can be fixed by turning that case also into a lang item. Working demo/patch: https://github.com/rust-lang/rust/compare/master...fusion-engineering-forks:core-const-panic-str
(Not sure if adding a lang item just for this is worth it though. Let me know if you want me to submit this as a PR.)
Edit: Sent as PR now: https://github.com/rust-lang/rust/pull/78069
Ah good point, we can separate that case from the more general panic_fmt
.
The reason I am not using core::panicking::panic
is that It's not even unstably const, so I don't know if it's only intended to be used at compile-time by the core::panic
macro.
@rodrimati1992 that function is an unstable implementation detail that no external crates should use.
I ran into the following weird situation:
The following code only needs #![feature(const_panic)]
:
const A: () = assert!(1 == 1);
const B: () = panic!("Oh noes");
but this code needs #![feature(const_fn)]
and #![feature(const_panic)]
:
const fn foo() {
assert!(1 == 1);
}
const fn bar() {
panic!("hi");
}
This occurs both with core
and std
, but with different error messages.
Is this intentional, or is it related to the https://github.com/rust-lang/rust/pull/74056 issue?
@josephlr Looks like min_const_fn
does not accept panics even with const_panic
enabled. Not sure if that is deliberate -- @oli-obk @ecstatic-morse do you know?
I thought core::panicing::panic
needed to be marked with rustc_const_unstable
, but that didn't work.
The panic functions are non-const. They are subject to a special exception in the const checks, and then during const-evaluation the interpreter notices when these functions are called, and stops interpretation (so their bodies do not even matter).
@RalfJung https://github.com/rust-lang/rust/pull/76602 fixes the issue, thanks for the tips!
but this code needs
#![feature(const_fn)]
and#![feature(const_panic)]
:const fn foo() { assert!(1 == 1); } const fn bar() { panic!("hi"); }
That looks expected, since foo
uses PartialEq
, and trait methods aren't stable in CTFE (https://github.com/rust-lang/rust/issues/67792).
Just bar
alone compiles fine just with #![feature(const_panic)]
.
Both of these are fine with just const_panic
, because ==
on primitive integers does not go through PartialEq
. You may have been thinking of assert_eq
, which has a whole host of problems like Debug
impls and such
Ah, right
I would love to see this move toward stabilization. If the issue description is up-to-date, we should be ready to do so:
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?
Given that there is no stability guarantee regarding error messages, this should not preclude stabilization. Nevertheless, I would suggest that a panic!()
evaluated in a const
context act just like a compile_error!()
in a macro
context, for consistency.
This change becomes really useful if
Result::unwrap
andOption::unwrap
becomeconst fn
, doing both in one go might be a good idea.
This is a nice idea in theory but is orthogonal to stabilizing this feature.
Blockers:
- ICE: #66693
This, the only real blocker, is now resolved.
Given that there is no stability guarantee regarding error messages, this should not preclude stabilization. Nevertheless, I would suggest that a panic!() evaluated in a const context act just like a compile_error!() in a macro context, for consistency.
There was a lot of discussion around this at https://github.com/rust-lang/rust/pull/79695.
However, I agree that this does not have to be a blocker.
Any opposition to stabilizing this as-is? Sounds like all blockers have been resolved, and as mentioned the exact formatting of the error message is not guaranteed to remain unchanged.
Any opposition to stabilizing this as-is?
Not from my side.
Nominating for an upcoming @rust-lang/lang meeting to discuss potential stabilization. I would encourage however that if someone wants to see this stabilized, the next step is to write a stabilization report.
Discussed briefly in lang team meeting - generally positive, looking forward to reviewing a stabilization report.
This is a tracking issue for the RFC "Allow panicking in constants" (rust-lang/rfcs#2345).
Steps:
Unresolved questions:
[ ] 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.Blockers: