-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
turn statically known erroneous code into a warning and continue normal code-generation #1229
Conversation
|
||
# Unresolved questions | ||
|
||
How to implement this? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, one easy way to implement it is: If you detect the error situation, then emit the warning, and then abandon the constant folding for that expression (that is, continue to emit the code that will run the expression at runtime, thus hitting the error condition when/if it runs).
Right?
Update: Oh, I didn't read carefully enough -- my strategy above (which I think is already included in the Alteratives section) differs from that of the RFC in that the above strategy will only work for -C debug-assertions mode builds, not release builds
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you also need to detect that you are not in a true const environment, where you definitely want this to happen and there can't be breakage since the feature wasn't there before
cc @rust-lang/compiler |
I certainly agree with the sentiment of this RFC, but I think I prefer the "constant evaluation does not influence code generation" alternative. That is, I think the fact that we sometimes constant fold expressions is not something which should be observable to an end user (except via a warning) -- it's just a runtime optimization for better performance. |
It's probably worth adding to the RFC a definition of "constant evaluation" vs "runtime evaluation" contexts. I think that the language has a pretty clear distinction, though:
The body of a |
I mostly agree. Rust has |
The overflow RFC states that, when overflow checking is not enabled (i.e., in release builds), "erroneous operations will produce a value". I do not believe there is any provision in the RFC for us to cause a panic to occur unless overflow checks have been enabled. (Earlier drafts did explicitly permit this, however.) My main concern here is causing code to break that used to work (because it relied on the integer overflow fallback semantics). I realize that such code is "officially" buggy, but it costs us nothing (in terms of maintenance burden, etc) to simply keep producing the same code we would have produced had the expression not been constant, and it will probably prevent some code from randomly panic'ing after a rustup. (Issuing a warning is nice, though.) |
These two statements sound to me as if it were legal to actually cause a compiler-error when such a bug is encountered. While this would be my preferred handling of such situations, I can see (and accept without argument) how such breaking changes should not happen suddenly after a minor compiler-upgrade. My issue is that a library user needs to opt-in to all of the overflow panics or none when compiling the library, while the default could be "all statically proven overflow panics". |
done
added an unresolved question about |
|
||
- the initializer of a constant `const foo: ty = EXPR` or `static foo: ty = EXPR` | ||
- the size of an array `[T; EXPR]` | ||
- the length of a repeat expression `[VAL; LEN_EXPR]` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't a complete list... off the top of my head, patterns and enum discriminants are also required to be constant.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
|
||
## Const-eval the body of `const fn` that are never used in a constant environment | ||
|
||
Currently a `const fn` that is called in non-const code is treated just like a normal function. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(brief discussion leads me to think that the context of a body of a const fn
is inherited from the context of the call-site. (This is mostly a note to myself; I'll try to write something more coherent later.)
This is also relevant for our erroring on transmute. |
Pre-execution constants (array lengths, enum variant discriminants, The more interesting case is panics/UB in ordinary code. If the code isn't actually reached at runtime, this can be completely fine, and may occur as a result of macros or potentially generics, e.g. fn elts_per_page<T>() -> usize {
if mem::size_of::<T>() == 0 {
usize::MAX
} else {
4096 / mem::size_of::<T>()
}
} or a similar version of using macros. It would be very annoying if overly-eager evaluation makes that a compile-time error. Note that this is what currently happens with |
it wouldn't occur, If we enable if/else in the constant evaluator, only the true branch will be evaluated. if you wrapped the else branch in a const-fn that would be a different issue though, as that function should also be evaluated on its own. |
This is of course specific to @arielb1's example. There can easily be conditions which are always false, though we cannot know it statically. But regardless the intent of this RFC is that such code would be accepted, though it would get a warning, right? UPDATE: adding the ", right?" at the end :) |
@oli-obk can you adjust the RFC to the alternative of "do not influence codegen"? I still think that is the preferred alternative. (After some discussion with others on the @rust-lang/lang team, I believe they are largely in agreement, though I'm not 100% sure -- if others disagree, please say so.) |
The code would get a warning currently (if there were a const fn sizeof), but an improved const evaluator (that can evaluate
sure thing, if the preference ever changes, it'll be as trivial as turning the lint into |
Update: ugh so sorry, I was looking at a cached page. Update 2: wait, I don't seem to be seeing an update that actually adopts the alternative... Raar github!
|
updated title and message at the top |
Hear ye, hear ye. This RFC is entering final comment period. |
These warnings should always be displayed. I don't see a reason to ever |
@oli-obk: I'd be careful going down that road. When I was younger and dumber, my response to a warning I couldn't silence wasn't "Hmm. Why shouldn't this be silenced?" but, instead, a burst of annoyance and, in a burst of semi-spiteful productivity, a wrapper which added At the very least, I'd make it very clear that it's some kind of deprecation warning and, thus, it can't be silenced because it's really an error that's just cutting you some slack by giving you some catch-up time. |
I'll make sure to add to the warning message that this is in fact an erroneous but well-defined situation (see #1237 for details on the well-defined-ness). |
I just tried to implement this RFC... And hit a curious issue. There's actually no const evaluation happening until the lint-passes run: https://github.com/rust-lang/rust/blob/master/src/librustc_lint/builtin.rs#L174-L178 Is it really a breaking change to improve a lint? IIRC #1105 says that if you could have prevented the situation by changing a lint-level or using UFCs, the breaking change is fine. The more we improve the const evaluator, the more time this lint will end up using, especially in the non-error case. It would be better to actually remove the lint and fold those const evaluable expressions so llvm has to do less work. A few things could be done here, but the only true way forward is to wait for MIR, port const_eval to MIR, fold all constant expressions in MIR and report errors/warnings during the folding, and finally remove the lint as it is now duplication. |
The thing here is that various places in rustc evaluate const-exprs, and the first place that fails reports the error. The first place that evaluates const-exprs is typeck in type-level expressions (I am using fn main() {
let x: Option<[u8; 1/0]> = None;
} Error:
The second place is const_check, which evaluates divisions: fn main() {
let x = (0u8-1u8)/1;
} Error:
Third in line is the fn main() {
let _x = 1<<(0u8-1u8);
let _y = 1<<64;
} Error:
The last place that does constant evaluation is trans: fn main() {
let _ = 127u8+129u8;
} Error:
I think we should consolidate the const_check & type_limits passes. The trans pass will be harder when we get generic constants through. |
Improving a lint is fine; see #1193 etc. For now, it would probably be good enough to just discard any constant evaluation warnings which would be triggered by a lint. Don't worry too much about the eventual model; a lot of our constant-handling code is probably going to have to be rewritten to work on MIR anyway. |
Huzzah! The language design subteam has decided to accept this RFC. Tracking issue is rust-lang/rust#28238 |
This PR turns statically known erroneous code (e.g. numeric overflow) into a warning and continues normal code-generation to emit the same code that would have been generated without `check_const` detecting that the result can be computed at compile-time. <del>It's not done yet, as I don't know how to properly emit a lint from trans. I can't seem to extract the real lint level of the item the erroneous expression is in.</del> It's an unconditional warning now. r? @pnkfelix cc @nikomatsakis * [RFC 1229 text](https://github.com/rust-lang/rfcs/blob/master/text/1229-compile-time-asserts.md) * RFC PR: rust-lang/rfcs#1229 * tracking issue: #28238
This RFC has changed course. The original intent is still noted at the bottom of this post.
If the constant evaluator encounters erronous code during the evaluation of
an expression that is not part of a true constant evaluation context a warning
must be emitted and the expression needs to be translated normally.
Currently the compiler errors in some situations like
let x = 5 << 42;
but not in others likelet x = 5 << some_function_returning_42();
There are three levels of breaking changes (in the order of my preference and severity) that could be applied here:
Variant 3 is the one now suggested by this RFC.
Old RFC: To prevent breaking changes, the compiler should turn statically known erroneous code (that previously wasn't a compile-time error) into an unconditional runtime-panic and issue a warning.
Rendered (dead link)
Rendered (official repo)