rust-lang / rust

Empowering everyone to build reliable and efficient software.
https://www.rust-lang.org
Other
98.58k stars 12.74k forks source link

Tracking issue for RFC 2345, "Allow panicking in constants" (const_panic) #51999

Closed Centril closed 3 years ago

Centril commented 6 years ago

This is a tracking issue for the RFC "Allow panicking in constants" (rust-lang/rfcs#2345).

Steps:

Unresolved questions:

Blockers:

abonander commented 4 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?

mark-i-m commented 4 years ago

@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...

abonander commented 4 years ago

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.

oli-obk commented 4 years ago

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).

RalfJung commented 4 years ago

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
abonander commented 4 years ago

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.

Lokathor commented 4 years ago

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".

mark-i-m commented 4 years ago

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...

Lokathor commented 4 years ago

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.

oli-obk commented 4 years ago

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.

rodrimati1992 commented 4 years ago

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.

RalfJung commented 4 years ago

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.

CodesInChaos commented 4 years ago

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)

oli-obk commented 4 years ago

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
AaronKutch commented 4 years ago

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.

rodrimati1992 commented 4 years ago

@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.

AaronKutch commented 4 years ago

I should have stated that it probably compiled before the current stable, but I did not know it would compile that long ago.

rodrimati1992 commented 4 years 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

RalfJung commented 4 years ago

@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.

rodrimati1992 commented 4 years ago

I am asking about panicking in a #[no_std] crate with a &'static str constant. 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.

RalfJung commented 4 years ago

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.

rodrimati1992 commented 4 years ago

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.

tesuji commented 4 years ago

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!.

RalfJung commented 4 years ago

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:

https://github.com/rust-lang/rust/blob/c3364780d2cfddfe329f62a3ec138fd4f9a60e27/compiler/rustc_mir/src/transform/check_consts/mod.rs#L56

tesuji commented 4 years ago

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

RalfJung commented 4 years ago

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.^^

RalfJung commented 4 years ago

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

RalfJung commented 4 years ago

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.

m-ou-se commented 4 years ago

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.

RalfJung commented 4 years ago

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.

tarcieri commented 4 years ago

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.

RalfJung commented 4 years ago

@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.

m-ou-se commented 4 years ago

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

RalfJung commented 4 years ago

Ah good point, we can separate that case from the more general panic_fmt.

rodrimati1992 commented 4 years ago

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.

RalfJung commented 4 years ago

@rodrimati1992 that function is an unstable implementation detail that no external crates should use.

josephlr commented 4 years ago

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?

RalfJung commented 4 years ago

@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?

josephlr commented 4 years ago

I thought core::panicing::panic needed to be marked with rustc_const_unstable, but that didn't work.

RalfJung commented 4 years ago

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).

josephlr commented 4 years ago

@RalfJung https://github.com/rust-lang/rust/pull/76602 fixes the issue, thanks for the tips!

jonas-schievink commented 3 years ago

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)].

oli-obk commented 3 years ago

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

jonas-schievink commented 3 years ago

Ah, right

SergioBenitez commented 3 years ago

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 and Option::unwrap become const 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.

RalfJung commented 3 years ago

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.

jhpratt commented 3 years ago

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.

RalfJung commented 3 years ago

Any opposition to stabilizing this as-is?

Not from my side.

nikomatsakis commented 3 years ago

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.

Mark-Simulacrum commented 3 years ago

Discussed briefly in lang team meeting - generally positive, looking forward to reviewing a stabilization report.