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 upallow constant evaluation of function calls #26848
Conversation
rust-highfive
assigned
pnkfelix
Jul 7, 2015
This comment has been minimized.
This comment has been minimized.
|
r? @pnkfelix (rust_highfive has picked a reviewer for you, use r? to override) |
oli-obk
force-pushed the
oli-obk:const_fn_const_eval
branch
from
ef3593b
to
6d1e92a
Jul 7, 2015
eddyb
reviewed
Jul 7, 2015
| @@ -271,6 +271,7 @@ pub enum ConstVal { | |||
| Bool(bool), | |||
| Struct(ast::NodeId), | |||
| Tuple(ast::NodeId), | |||
| Function(ast::DefId), | |||
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
oli-obk
Jul 7, 2015
Author
Contributor
I was just following the Struct/Tuple definition. And it made things easier with Expr::Call
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
|
|
eddyb
reviewed
Jul 7, 2015
| } | ||
| }, | ||
| Some(def::DefFn(id, _)) => return Ok(Function(id)), | ||
| // FIXME: implement const methods? |
This comment has been minimized.
This comment has been minimized.
eddyb
Jul 7, 2015
Member
This should at least check that the function is const fn - check_const runs long after type collection - and type collection is what triggers the evaluation of array length expressions.
This comment has been minimized.
This comment has been minimized.
oli-obk
Jul 7, 2015
Author
Contributor
that is unfortunate... but for all practical purposes irrelevant, since the function is simply interpreted as const fn and if it hits a non-const value anywhere const_eval bails out anyway. I'd rather keep const eval for all functions, so regular statements calling non-const-fn-but-could-be-const-fn functions with const arguments can also be const evaluated
This comment has been minimized.
This comment has been minimized.
pnkfelix
Jul 29, 2015
Member
what does your PR do if it attempts to eval a call to a non const fn whose body is more complex than a single expression?
From my initial reading of the PR, it seems like it is assuming that it is always given a const fn and thus can assume that the block.expr.as_ref().unwrap() is all it needs to look at in the body, in particular it is not (I think) double-checking that the block has no statements...
Update: oh wait, let me double check, but now I'm thinking that all such functions should have already been marked as NOT_CONST from the check_const run.
Update 2: But wait, as eddyb already said, check_const runs long after type collection ... so it still seems like you could mistakenly think you are evaluating a non-const fn correctly but in fact should have flagged the fn as non-const on-the-fly...
This comment has been minimized.
This comment has been minimized.
oli-obk
Jul 29, 2015
Author
Contributor
yea, I misunderstood. I will force const-fn here. This should also un-break all the unit tests :D
This comment has been minimized.
This comment has been minimized.
|
Maybe it might be a good idea to introduce a |
This comment has been minimized.
This comment has been minimized.
|
Is there any plan to fix rustc's usage of const_eval so it doesn't try to evaluate expressions that haven't gone through type-checking first? Fixing that would massively simplify const_eval, and make changes like this a lot easier to reason about. (See also #26683.) For the tests, probably should just implement "id" as a store+load from a static mut variable, which is basically guaranteed to be impossible to const-evaluate. |
This comment has been minimized.
This comment has been minimized.
yea, I had similar thoughts, but then got too lazy to introduce a PR before this one, but if preferred I can do that once @eefriedman gets his hint PR through |
This comment has been minimized.
This comment has been minimized.
|
|
oli-obk
force-pushed the
oli-obk:const_fn_const_eval
branch
from
6d1e92a
to
0e2388d
Jul 22, 2015
This comment has been minimized.
This comment has been minimized.
|
|
This comment has been minimized.
This comment has been minimized.
|
What's the status on this? Just needs review? Blocked on @eefriedman's PR? |
oli-obk
force-pushed the
oli-obk:const_fn_const_eval
branch
3 times, most recently
from
e41da4c
to
2e750e6
Jul 27, 2015
oli-obk
reviewed
Jul 27, 2015
| unsafety, | ||
| abi, | ||
| block, | ||
| ) = match try!(eval_const_expr_partial(tcx, callee, UncheckedExprNoHint, fn_args)) { |
This comment has been minimized.
This comment has been minimized.
oli-obk
Jul 27, 2015
Author
Contributor
@eefriedman this eval_const_expr_partial gets me the DefId of the function I'm calling. I'm not sure if anything but UncheckedExprNoHint makes sense here.
oli-obk
reviewed
Jul 27, 2015
| } | ||
| let result = block.expr.as_ref().unwrap(); | ||
| debug!("const call({:?})", call_args); | ||
| try!(eval_const_expr_partial(tcx, &**result, ty_hint, Some(&call_args))) |
This comment has been minimized.
This comment has been minimized.
oli-obk
Jul 27, 2015
Author
Contributor
this ty_hint is just forwarded, as the type of the call expression is the same as the type of the functions body-expression
oli-obk
reviewed
Jul 27, 2015
| let arg_val = try!(eval_const_expr_partial( | ||
| tcx, | ||
| arg_expr, | ||
| UncheckedExprNoHint, |
This comment has been minimized.
This comment has been minimized.
oli-obk
Jul 27, 2015
Author
Contributor
I only know about the ast-type, not the actual type-checked type. I could of course check the appropriate tables if the type is known.
This comment has been minimized.
This comment has been minimized.
|
I'll look at this more carefully tomorrow, but the way hints work inside of eval_const_expr_partial is that if the argument is ExprTypeChecked, all recursive calls should generally also be ExprTypeChecked. This limits the use of weird hinting semantics to places where it's absolutely necessary. |
This comment has been minimized.
This comment has been minimized.
Am I understanding correctly that this doesn't magically turn every |
This comment has been minimized.
This comment has been minimized.
|
Looking over this more carefully, I'm pretty sure this approach is going to exacerbate the existing problems with overflow in constant evaluation in type-checking. You simply can't correctly evaluate a const fn correctly in general without knowing the declared argument and result types. For example: const fn bmax() -> u8 { !0 }
const fn smax() -> u16 { !0 }
let c: [u8; bmax() as usize] = [0; 255];
let c: [u8; smax() as usize] = [0; 65535]; |
This comment has been minimized.
This comment has been minimized.
|
I will add that and similar functions with function arguments as test cases. For now I will raise an error in case the type is not yet in the ast_ty_to_ty map. I wonder how this plays with generics... |
This comment has been minimized.
This comment has been minimized.
|
|
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
can you elaborate on what you mean here? Is this specifically because you were treating arbitrary fns as const fns? |
This comment has been minimized.
This comment has been minimized.
|
@oli-obk wrote:
@nikomatsakis asked:
Niko and I then reviewed the situation further, and determined the following:
If you follow that policy (of emitting code to panic at runtime -- which I guess you could emulate by just not const-evaluating the overflowing cases), then the hypothesis no longer holds: This would no longer be a breaking-change, and it would re-enforce a precedent that improvements to the const-evaluator should not be breaking changes. |
This comment has been minimized.
This comment has been minimized.
yes, but it applies also to my other branch about constant evaluation of indexing operations or any further operations or constant values that are added (e.g. Ranges).
I'll create an RFC to make this the standard procedure for compiler-improvements that detect an error at compile-time, but insert an unconditional runtime-panic to not be a breaking change.
This would require 2 things
|
This comment has been minimized.
This comment has been minimized.
|
RFC: rust-lang/rfcs#1229 |
This comment has been minimized.
This comment has been minimized.
|
This RFC was merged, any chance the PR can get rebased? |
This comment has been minimized.
This comment has been minimized.
|
I implemented the RFC, there'll be a PR for it soon, after that, I'll rebase this PR over the RFC-PR and add the appropriate regression tests. |
This comment has been minimized.
This comment has been minimized.
|
Great! On Fri, Oct 2, 2015 at 5:08 AM, Oliver Schneider notifications@github.com
|
oli-obk
force-pushed the
oli-obk:const_fn_const_eval
branch
2 times, most recently
from
0c978cb
to
084c171
Oct 14, 2015
This comment has been minimized.
This comment has been minimized.
|
done and rebased depends on PR #28845 (rfc 1229) |
This comment has been minimized.
This comment has been minimized.
|
|
oli-obk
added some commits
Oct 14, 2015
oli-obk
force-pushed the
oli-obk:const_fn_const_eval
branch
from
084c171
to
72f42f1
Oct 19, 2015
This comment has been minimized.
This comment has been minimized.
|
now that #28845 (RFC 1229) has landed, we can merge this without causing breaking changes |
oli-obk
reviewed
Oct 27, 2015
| }, | ||
| _ => signal!(e, NonConstPath), | ||
| }; | ||
| if let ExprTypeChecked = ty_hint { |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
pnkfelix
reviewed
Oct 27, 2015
| @@ -13,9 +13,6 @@ | |||
| // Check that we do *not* overflow on a number of edge cases. | |||
| // (compare with test/run-fail/overflowing-{lsh,rsh}*.rs) | |||
|
|
|||
| // (Work around constant-evaluation) | |||
| fn id<T>(x: T) -> T { x } | |||
This comment has been minimized.
This comment has been minimized.
pnkfelix
Oct 27, 2015
Member
(I'm not sure if this was a good idea. The point of this test was to exercise the code generated for execution at runtime, but with this change, you are heavily encouraging it to be evaluated at compile time. I would probably have preferred to just leave this as it was, perhaps even modifying the fn id further to try to ensure that it would not be flagged as a const fn by future aggressive analyses...)
This comment has been minimized.
This comment has been minimized.
oli-obk
Oct 28, 2015
Author
Contributor
Yes, I was overzealous here.
The problem is, that this test gets silently turned into a compile-time test whenever we improve the const evaluator. I think if we made the id function in a way that it modifies a static on every call, there's no way this can ever be evaluated at compile-time in rustc. LLVM will probably still end up optimizing everything out. So this test also needs to guarantee that optimizations are off (are they even on in tests? if they are not, ever activating them will again silently turn this test into a compile-time test).
An alternative is to add a text file from which the values are read. This way optimizations and the const evaluator cannot ever influence the meaning of this test.
This comment has been minimized.
This comment has been minimized.
pnkfelix
Oct 28, 2015
Member
I don't care as much about LLVM optimizing everything out (my main intent is to test the rust codegen paths) -- but having said that, you are probably right that it would be good to prevent LLVM as well.
Still, would marking the fn as '#[inline(never)]` not suffice here?
This comment has been minimized.
This comment has been minimized.
oli-obk
Oct 28, 2015
Author
Contributor
oh right, we have that. I'll create a new workaround function and submit a PR.
This comment has been minimized.
This comment has been minimized.
|
Despite my misgivings about the change to the I would nonetheless be interested in hearing @oli-obk 's opinion on whether that change to the test should stand, or if a followup PR should perhaps return it to its former state (or something similar to that). |
This comment has been minimized.
This comment has been minimized.
|
@bors r+ |
This comment has been minimized.
This comment has been minimized.
|
|
bors
added a commit
that referenced
this pull request
Oct 27, 2015
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
bors
merged commit 2b000fe
into
rust-lang:master
Oct 27, 2015
oli-obk
deleted the
oli-obk:const_fn_const_eval
branch
Oct 28, 2015
dtwood
reviewed
Nov 2, 2015
| } else { | ||
| // we don't know much about the function, so we force it to be a const fn | ||
| // so compilation will fail later in case the const fn's body is not const | ||
| assert_eq!(constness, hir::Constness::Const) |
This comment has been minimized.
This comment has been minimized.
dtwood
Nov 2, 2015
I'm getting a ICE here with the current nightly on playpen:
thread 'rustc' panicked at 'assertion failed: `(left == right)` (left: `NotConst`, right: `Const`)', ../src/librustc/middle/const_eval.rs:106
in this simple test program:
fn f(x: usize) -> usize {
x
}
const Y: usize = f(2);
fn main() {
let z = [0; X];
}
Curiously if you move the const Y... line to the top, then it at least doesn't ICE, although I'd expect to get E0015 as an error, rather than E0307 (you can then get the E0015 by removing the let z = ... line)
This comment has been minimized.
This comment has been minimized.
eefriedman
Nov 3, 2015
Contributor
This isn't the right place to file a bug report; try https://github.com/rust-lang/rust/issues/new . (You can link to the PR from there if you think it's useful.)
This comment has been minimized.
This comment has been minimized.
dtwood
Nov 4, 2015
I've just been playing on my phone on playpen on the bus, and won't have a laptop for at least a week, so was just hoping to put this somewhere that someone would spot it, and they can triage it (otherwise I'll just forget)
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
dtwood
Nov 4, 2015
Great, thanks
On Wed, 4 Nov 2015 at 20:46 eefriedman notifications@github.com wrote:
In src/librustc/middle/const_eval.rs
#26848 (comment):
},_ => signal!(e, NonConstPath),}} else {signal!(e, NonConstPath)},_ => signal!(e, NonConstPath),};if let ExprTypeChecked = ty_hint {// no need to check for constness... either check_const// already forbids this or we const eval over whatever// we want} else {// we don't know much about the function, so we force it to be a const fn// so compilation will fail later in case the const fn's body is not constassert_eq!(constness, hir::Constness::Const)—
Reply to this email directly or view it on GitHub
https://github.com/rust-lang/rust/pull/26848/files#r43936838.
This comment has been minimized.
This comment has been minimized.
|
Won't this introduce a backcompat hazard? Functions which are const but not marked as such may become non-const in the future, breaking libs which depend on this. |
This comment has been minimized.
This comment has been minimized.
|
@Manishearth it's impossible to use non-const functions in a const environment |
This comment has been minimized.
This comment has been minimized.
|
I'm talking about
|
This comment has been minimized.
This comment has been minimized.
|
yes, normal code can be const evaluated. But in case the const evaluation fails, it falls back to normal code generation anyway. I had an RFC for that and implemented it a while back. |
This comment has been minimized.
This comment has been minimized.
|
as you can see in #29587, there's actually an assertion preventing the use of non-const functions in a true const environment. |
oli-obk commentedJul 7, 2015
this has the funky side-effect of also allowing constant evaluation of function calls to functions that are not
const fnas long ascheck_constdidn't mark that functionNOT_CONSTIt's still not possible to call a normal function from a
const fn, but let statements' initialization value can get const evaluated (this caused the fallout in the overflowing tests)we can now do this:
also added a test for destructuring in const fn args
This is a [breaking change], since it turns some runtime panics into compile-time errors. This statement is true for ANY improvement to the const evaluator.