-
Notifications
You must be signed in to change notification settings - Fork 12.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Segfault or wrong values from const FOO: u32 = 0 - 1; println!("{}", FOO);
#50814
Comments
The constant evaluation error is being flagged, but only as a warning, so the code is still executed. cc @oli-obk |
@eddyb the way promotion works we can't back out of promoting in case of const eval failure, can we? The named const case can be fixed. But I'm not sure what do do on the promotion case |
@oli-obk Ah, this is where we disagree - overflow is not an evaluation error. But there is a more general issue, where if a |
Ok so this is super easy to fix. Just remove the check in https://github.com/rust-lang/rust/blob/master/src/librustc_mir/interpret/eval_context.rs#L533 The const fn part is "fine" right now, because users can't write any const fns and the const fns that libstd offers are safe to use. |
@oli-obk I would not rely on assumptions about libstd, IMO, it's too fragile. We have code to emit an |
Ok. some irc discussion has shown that this is a horrible situation we're in. Summary: Let's use a strawman union for demonstration purposes, but it's not necessary, there are other ways to fail the const evaluator, they just aren't short examples. /// Helper for casting `&'static u32` into `usize`
union Union {
a: &'static u32,
b: usize,
}
|
Here's a problematic case with depending on a generic function's args: trait C {
const BOO: usize;
}
trait Foo<T> {
const BAR: usize;
}
struct A<T>(T);
impl<T: C> Foo<T> for A<T> {
const BAR: usize = [5, 6, 7][T::BOO];
}
fn foo<T: C>() -> &'static usize {
&<A<T> as Foo<T>>::BAR
}
impl C for () {
const BOO: usize = 42;
}
impl C for u32 {
const BOO: usize = 1;
}
fn main() {
println!("{:x}", foo::<()>() as *const usize as usize);
println!("{:x}", foo::<u32>() as *const usize as usize);
println!("{:x}", foo::<()>());
println!("{:x}", foo::<u32>());
} You cannot implement this with |
Looks like I ran into a variant of this as well:
Unlike the OP's examples, this still fails on rustc 1.28.0-nightly (cb20f68 2018-05-21), and doesn't produce any compile time warnings or errors (it also fails on 1.26.0). |
For future reference: we're discussing this issue on Discord with the conversation GUID: 5fd923d1-f8ea-4a3a-a85b-67dbe3557544 (you can search for it =) |
Notes from that discussion are available in this gist https://gist.github.com/nikomatsakis/eee212d7c4a49ae75e19765c1e06bf49 |
What would such "strict limitations" on const fns in promotion look like? Also: Could we simply not allow any const fn's in promoted expressions? This would mean some loss of expressivity in promotion, but seems like a simpler mental model. |
I'm still trying to understand what the problem is here, so let me try to rephrase and please tell me if that makes any sense: What happens with promotion is that the compiler identifies some expression to be a promotion candidate. Such expressions than are evaluated at compile time using miri's CTFE. If that succeeds, all is well and the Either way, there is the problem that the check if something should be promoted has to be very conservative because if it claims something is promotable but, but then that code errors at CTFE time, we broke compilation for no good reason (as this code would have compiled just fine if we didn't decide to promote). Does that summarize the problem? I see two separate things to do/discuss here, though they are obviously related. One is whether we pick (a) or (b). (The current situation of emitting "undef" is clearly a bug, right?) I don't see any good reason to to (b) -- silently (or with a warning?) emit code that will definitely panic at run-time seems just bad. Also, @oli-obk mentioned that it's not even easy to do because The notes @nikomatsakis linked above mention (a), but only as an alternative to "forbid unions in promotion", which I don't understand how that's an alternative. We still have to do something if miri errors, right? Secondly, how can we avoid the problem of trying to promote something that's not promoteable? This ties into the discussions around CTFE soundess/correctness at #24111 (comment). @oli-obk was convinced that we should have a "const mode typesystem" that makes things like raw pointer comparisons or ptr-to-int-casts unsafe. Based on that, shouldn't it be enough to check that the thing to be promoted is entirely "const well-typed" ignoring unsafe blocks, i.e., everything in there would have to be called in safe const code. That would rule out union field access. There is still some possible issue if a |
It was intended for errors from things like out-of-bounds indexing and division by zero, where the original |
I have addressed the |
Refactor the const eval diagnostic API * no longer report "const eval error" for things that have typeck errors * errors and lints have saner spans and messages * unified the diagnostic logic (const eval errors were slightly different depending on where they were reported, and there was also code duplication between the different reporters) * report errors if an erroneous constant is used inside a promoted (fixes most of rust-lang#50814)
Refactor the const eval diagnostic API * no longer report "const eval error" for things that have typeck errors * errors and lints have saner spans and messages * unified the diagnostic logic (const eval errors were slightly different depending on where they were reported, and there was also code duplication between the different reporters) * report errors if an erroneous constant is used inside a promoted (fixes most of #50814)
There is a full fix in #51570, but it's overzealous and will error on completely fine code. The correct solution is outlined in #51570 (comment) but that requires some refactorings that @TheDarkula is currently working on. After their refactorings are done I'll update the PR to implement the fix properly. |
Thanks to #52571 (which got backported to beta and hence will be in the next stable), at least similar bugs can no longer create UB. Instead, the program will abort. Not good but better.^^ That means this is no longer I-unsound. |
closing at the actual soundness holes have been plugged (by codegening abort code instead, as @RalfJung described.) A new issue will hopefully be opened somewhere soon to track the follow-on work to do better static checking in such cases rather than falling back on dynamic aborts. |
I did a (short) writeup with a possible solution in rust-lang/const-eval#2 |
The following programs produce a binary that will either segfault or produce a nonsense value seemingly depending on whether optimizations are enabled or not.
On 1.25.0 they behave as expected (the first doesn't even compile) but 1.26.0 and newer are broken.
The text was updated successfully, but these errors were encountered: