Join GitHub today
GitHub is home to over 31 million developers working together to host and review code, manage projects, and build software together.
Sign upturn statically known erroneous code into a warning and continue normal code-generation #1229
Conversation
oli-obk
referenced this pull request
Jul 30, 2015
Merged
allow constant evaluation of function calls #26848
alexcrichton
added
the
T-lang
label
Jul 30, 2015
pnkfelix
reviewed
Jul 30, 2015
|
|
||
| # Unresolved questions | ||
|
|
||
| How to implement this? |
This comment has been minimized.
This comment has been minimized.
pnkfelix
Jul 30, 2015
Member
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
This comment has been minimized.
This comment has been minimized.
oli-obk
Jul 30, 2015
Author
Contributor
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
This comment has been minimized.
This comment has been minimized.
pnkfelix
added
T-compiler
T-lang
and removed
T-lang
labels
Jul 30, 2015
This comment has been minimized.
This comment has been minimized.
|
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. |
This comment has been minimized.
This comment has been minimized.
|
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 |
This comment has been minimized.
This comment has been minimized.
I mostly agree. Rust has |
This comment has been minimized.
This comment has been minimized.
|
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.) |
This comment has been minimized.
This comment has been minimized.
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". |
oli-obk
added some commits
Jul 31, 2015
This comment has been minimized.
This comment has been minimized.
done
added an unresolved question about |
eefriedman
reviewed
Jul 31, 2015
|
|
||
| - 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]` |
This comment has been minimized.
This comment has been minimized.
eefriedman
Jul 31, 2015
Contributor
This isn't a complete list... off the top of my head, patterns and enum discriminants are also required to be constant.
This comment has been minimized.
This comment has been minimized.
nikomatsakis
assigned
pnkfelix
Jul 31, 2015
pnkfelix
reviewed
Jul 31, 2015
|
|
||
| ## 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. |
This comment has been minimized.
This comment has been minimized.
pnkfelix
Jul 31, 2015
Member
(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 comment has been minimized.
This comment has been minimized.
|
This is also relevant for our erroring on transmute. |
This comment has been minimized.
This comment has been minimized.
|
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 |
This comment has been minimized.
This comment has been minimized.
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 comment has been minimized.
This comment has been minimized.
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 :) |
This comment has been minimized.
This comment has been minimized.
|
@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.) |
This comment has been minimized.
This comment has been minimized.
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 |
This comment has been minimized.
This comment has been minimized.
|
updated PR, should I squash? |
This comment has been minimized.
This comment has been minimized.
|
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!
|
oli-obk
changed the title
turn statically known erroneous code into a warning and an unconditional panic
turn statically known erroneous code into a warning and continue normal code-generation
Aug 7, 2015
This comment has been minimized.
This comment has been minimized.
|
updated title and message at the top |
nikomatsakis
added
the
final-comment-period
label
Aug 14, 2015
This comment has been minimized.
This comment has been minimized.
|
Hear ye, hear ye. This RFC is entering final comment period. |
This comment has been minimized.
This comment has been minimized.
|
These warnings should always be displayed. I don't see a reason to ever |
This comment has been minimized.
This comment has been minimized.
ssokolow
commented
Aug 17, 2015
|
@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. |
This comment has been minimized.
This comment has been minimized.
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). |
This comment has been minimized.
This comment has been minimized.
|
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. |
This comment has been minimized.
This comment has been minimized.
|
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. |
This comment has been minimized.
This comment has been minimized.
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. |
nikomatsakis
referenced this pull request
Sep 4, 2015
Open
Tracking issue for constant prop not causing add'l errors (RFC #1229) #28238
nikomatsakis
merged commit 61d1436
into
rust-lang:master
Sep 4, 2015
nikomatsakis
added a commit
that referenced
this pull request
Sep 4, 2015
This comment has been minimized.
This comment has been minimized.
|
Huzzah! The language design subteam has decided to accept this RFC. Tracking issue is rust-lang/rust#28238 |
oli-obk commentedJul 30, 2015
•
edited by pnkfelix
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)