-
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
Const indexing and friends #25570
Const indexing and friends #25570
Conversation
cc @eddyb |
Given that we're in the post-1.0 world. We need to either make this not a breaking change or justify how this is effectively just fixing a bug (we'd also need to measure the impact of the change in this case). So I don't think we can land this as is. I'm afraid I don't understand either of the two proposals for fixing the breaking change - could you explain them in a bit more detail please? |
everything in this comment is outdated and probably wrong. It is left since other comments refer to it rebase and fix of #25370
enum E { V1(isize), V0 }
const C: &'static [E] = &[E::V0, E::V1(0xDEADBEE)];
static C1: E = C[1];
static C0: E = C[0]; The failure
Explanation of changes:Array expressions, Repeat expressions This requires a few tricks:
I see now that only Solution 1. is viable in this PR, as there shouldn't be any potential for a breaking change even if mostly no code would break. For Solution 2 I'll add another PR in the future, for now I'll just fix this by reintroducing the original fallback |
ccc582e
to
8ce7fdb
Compare
@@ -877,12 +962,57 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &ty::ctxt<'tcx>, | |||
ast::ExprBlock(ref block) => { | |||
match block.expr { | |||
Some(ref expr) => try!(eval_const_expr_partial(tcx, &**expr, ety)), | |||
None => const_int(0) | |||
None => const_int(0) // huh??? |
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.
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.
oh sorry, should have been researching better: #14183 (comment)
hold this off until #25609 is merged, I'll rebase then. |
@@ -566,12 +610,20 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, | |||
} | |||
|
|||
ast::ExprIndex(ref base, ref index) => { | |||
match const_eval::eval_const_expr_partial(cx.tcx(), &e, None) { |
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.
an alternative would be to check if base
points to a static or a constant and then decide on what to do. Any thoughts?
yea, that confused me quite a bit, too. As I read the docs and book, it means that if you use a constant in between, you again loose all guarantees of memory locations and uniqueness of values. Although the compiler actually evaluates all constants with intermediate statics as statics EDIT: apparently the comment was deleted |
@oli-obk I realized the code path is for the value with which a Also, the error |
oh wow, I got everything confused here... I was so sure statics were simply evaluated as constants if called from a constant... So I guess my "try const eval, if fails try static eval" is fine? |
☔ The latest upstream changes (presumably #25609) made this pull request unmergeable. Please resolve the merge conflicts. |
It's trying to tell you you should replace the static with a constant, and make the static simply be a |
rebased, it was only a minor change (forwarding a function parameter). |
@oli-obk that seems very contrived, in practice you either replace the referenced |
I'd rather remove the error completely and have constant evaluation treat statics as constants. But then my "try const eval, else fallback to static eval" method fails horribly.
|
@oli-obk No, no, no: statics are not constant. |
now that is something i can do! useless static-lint is on my TODO list |
rebased and removed the const references This PR now only addresses const arrays, repeats and indexing into them. |
NotOnFloat, | ||
NotOnString, | ||
NotOnBinary, | ||
NotOnStruct, | ||
NotOnTuple, | ||
NotOnArray, | ||
NotOnRepeat, |
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 feels like it should be a nested enum or something (is it done like this just for flattening? no, that doesn't make sense, below there's (u64, u64)
payloads).
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.
(I'm okay with not trying to fold that particular change into this PR -- i.e. switching to a nested enum might make sense but also seems like it is a broader refactoring that we could develop later, no?)
I just checked and it seems that many of these cases just cause the compiler to abort (with SIGILL and no error message), for example (playpen): fn main() {
static A: i32 = 0;
static B: i32 = *&A;
println!("{:?}", B);
} Though it only happens when the statics are inside a function, so I'm a bit confused as to what's going on. IMO references to statics should not be dereferenced for any other purpose than to offset them (similar to Is it even possible to deny such uses after 1.0? |
so... I came to the conclusion that indexing into and derefing statics never makes sense in a static environment. except when taking the address directly again, but that's apparently not ok:
taking either of these by value also makes no sense... so i tried to forbid that, but this also breaks constants since there's no difference between a situations this is happening in the test-suite: |
☔ The latest upstream changes (presumably #26071) made this pull request unmergeable. Please resolve the merge conflicts. |
☔ The latest upstream changes (presumably #29781) made this pull request unmergeable. Please resolve the merge conflicts. |
The PR's message will go into the git log when this is merged. I think it's best to make sure it reflects the merged version, and I guess it's best to remove the outdated text? |
18fda60
to
ed3ed84
Compare
rebased. @bluss, I moved it to my second comment |
@oli-obk Thank you, that's neater! |
@@ -416,6 +424,12 @@ pub enum ErrKind { | |||
ExpectedConstTuple, | |||
ExpectedConstStruct, | |||
TupleIndexOutOfBounds, | |||
IndexedNonVec, | |||
IndexNotNatural, |
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.
Nit: I feel like I vaguely remember what "natural numbers" are from grade-school, but wouldn't "positive" be easier?
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.
Positive doesn't include zero. (Usually the naturals start at zero.)
You could do IndexNotNonNegative
, or just IndexNegative
if you like. :)
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.
(though perhaps this error is also issued when the index is a floating point number, i.e. a non-integer? That's the other reason one would say "Natural" ...)
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.
The non-integer case seems to be handed by the NotInt
variant below?
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.
I like IndexNegative
, I'll go with that.
Natural numbers only include integers.
So this PR looks good to me. One question is whether we should feature-gate these changes? Otherwise it does represent an implicit commitment. OTOH, it seems like something we'll obviously want to support. I feel like we've chosen to feature-gate in other similar instances. But it seems like we need to make better procedure for moving forward with UNGATING. I know that @aturon made a bunch of issues, which is a first-step, we need to start reviewing them regularly as well, I imagine. Thoughts @rust-lang/lang? |
@nikomatsakis I suppose the broader question is what criteria we should use in deciding whether to impose a feature gate. I see three potential reasons for a feature gate:
Does that sounds about right? In this case, I think the answers are: (1.) we want this (but maybe others would disagree), (2.) any possible semantics we might choose is going to include what is outlined here, I think (i.e. this probably occupies a low point in the partial ordering of potential semantics that I alluded to above). So the only question mark for me is (3.): is the implementation sufficiently low-risk that we can side-step a gate? And I don't have a confident answer there. |
We already allow indexing into constants anywhere but a true constant (e.g. array lengths). It's just computed by llvm's constant evaluator in So imo it's only a question about (3.) |
ed3ed84
to
6683fa4
Compare
addressed nits and added a test |
So let me be clear on my concern. In general, I think that constant expression evaluation is a kind of "ad-hoc, best-effort" mess whose limits are not well documented and are only understood by a relatively small set. The very fact that we have two constant evaluators that handle two distinct subsets -- and slightly differently, at times, though of course those are "just bugs" -- is sort of what I'm talking about. I would very much like to refactor things so that we have ONE constant evaluator that is following a relatively documented, comprehensible process. And I'm wary of accidentally enabling evaluation of expressions that turns out to work most of the time, but have rough edges. Let me give you another concrete example: we currently allow you to "dereference" an Now, indexing and dereferences are basically the same thing, so an example of something I'd like to be careful about is: what happens if you try to index into the content of a All this is not to say we need a feature-gate, but just to say that there is evidence that there is reason to think that @pnkfelix's condition #2 may apply here:
Basically, I think it might be prudent to place a kind of feature gate around ALL expansions of constants until we have something a bit more disciplined (at least a unified evaluator, ideally an RFC laying out what is expected to work). |
I just want to be clear though: I'm also open to the idea that I'm being excessively cautious. Part of my problem I think is that I personally don't feel like I deeply understand the limits of the current system, and that makes me feel uneasy. Perhaps those more intimately familiar feel more comfortable. |
you get an error, because a static is not a true constant and
This is the target I've been doing all these PRs for. Once
I'm perfectly fine with a feature gate. And I just found #29914 in |
tracking issue is rust-lang#29947
added a feature gate. make check passes. |
📌 Commit 6405122 has been approved by |
This PR allows the constant evaluation of index operations on constant arrays and repeat expressions. This allows index expressions to appear in the expression path of the length expression of a repeat expression or an array type. An example is ```rust const ARR: [usize; 5] = [1, 2, 3, 4, 5]; const ARR2: [usize; ARR[1]] = [42, 99]; ``` In most other locations llvm's const evaluator figures it out already. This is not specific to index expressions and could be remedied in the future.
This PR allows the constant evaluation of index operations on constant arrays and repeat expressions. This allows index expressions to appear in the expression path of the length expression of a repeat expression or an array type.
An example is
In most other locations llvm's const evaluator figures it out already. This is not specific to index expressions and could be remedied in the future.