-
Notifications
You must be signed in to change notification settings - Fork 17
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
Solving the problem with promotion #53
Comments
One way that satisfies just the "only promote things that we can successfully evaluate" would be to
This will still be a breaking change, but it will be much less of one (and also not cause post-monomorphization errors) |
Yes that would be like a fallback solution. The reason I like it less is that it is "too smart" -- that makes it really hard to predict what will happen, and now the lifetime of something can depend on subtle details of const evaluation. I would hope that at least in implicit promotion contexts, we can do better, since we promote less. (EDIT: the following is outdated, see my next post) But in explicit contexts, we have all the same problems! They can have dead code, too: const FOO: i32 = {
if false { &promotable }
} So all the concerns about creating promoteds that fail const evaluation apply here, too, I think? All the time we were concerned about changing behavior due to suddenly evaluating things at compile-time that used to be run-time-evaluated, but I now feel like the worse part is the one where we have consts (with separate const-eval queries) that fail to evaluate. Note that another cause of const-eval queries are nullary functions. But those don't cause any problems I think. I wonder why? |
I think I overreacted re: explicit promotion contexts. The property that I was going for all along here is: When a CTFE query fails, that's a hard error. Promoteds in implicit contexts are always evaluated during codegen, even in dead code, so this implies that evaluation of an implicit promoted must never fail. But for explicit promoteds, we either already hard-error when they fail as codegen needs their result (e.g. |
We already check that explicit promotion succeeds or halts compilation: What we do not check yet, I think, is that evaluating the explicit promoted succeeds or compilation halts. That is a property we would need for this plan to work out. |
But not proactively, i.e., only when the promoted is actually used. IOW, this should only lint on implicit promoteds: Can we store the information whether a promoted is implicit or explicit? That code currently checks the |
I think the only place we can store it reasonably is in the |
Turns out we have yet another huge problem with promotion caused by their "design" (if you want to call it that) never having been written down and discussed at a high level. |
A bit off-topic, but this can't hold in general due to the const-propagation transform. Maybe you just mean for |
Ah sure, ConstProp can just bail out and be like "I'm not touching this" on errors, that's fair. (Though if all consts reaching codegen must successfully evaluate, I think the only bad consts ConstProp might encounter are promoteds in explicit promotion context in dead code.) |
This comment has been minimized.
This comment has been minimized.
Actually ConstProp should be easy -- we just make The only exception of course are const/static bodies, as they are not codegen'd. We can just not ConstProp them either... though we have some lints in ConstProp that we would miss then. Well, this is another argument for not doing promotion for those, but instead doing lifetime extension the old way ( |
I have collected my current thoughts on this in a hackmd. Please feel free to expand that with missing information! |
Removing const _: () = {
loop {
let _x: &'static i32 = &5;
}
} |
@ecstatic-morse that's a good point, dang. So, uh, what can we do? I don't suppose there is any hope of restricting lifetime extension in const/static bodies to just infallible code? The only other way then to still achieve "all CTFE failures are hard errors" is to make sure we only evaluate lifetime extended promoteds that we actually need -- doing it on-demand instead of eagerly. This affects MIR optimizations, they must be careful not to trigger evaluation of these promoteds. Also, doesn't this entirely break promotion of mutable data? What if we have @lcnr, thanks for your comment! However, I don't think hacmd makes any sense for discussion, it only makes sense for documenting the result of a discussion. So let me move your comment here:
However, the example you give has nothing to do with promotion. See the In the light of what @ecstatic-morse noted, since we have to evaluate lifetime extension promoteds lazily in const/static bodies, we might as well do the same with other promoteds... @oli-obk same for your comment
Yes we have to. It is impossible to determine all dead code -- see "halting problem". We could remove some dead code, but then we get an inconsistent mess, not a better system. (Btw hackmd also has a dedicated comment mechanism, we could also try that. But then we'd have two threads of discussion and I do not know if hackmd sends out any notifications. Either way, using a free-form text editor as a discussion forum is a rather poor substitute IMO.^^) |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
stop promoting union field accesses in 'const' Turns out that promotion of union field accesses is the only difference between "promotion in `const`/`static` bodies" and "explicit promotion". So if we can remove this, we have finally achieved what I thought to already be the case -- that the bodies of `const`/`static` initializers behave the same as explicit promotion contexts. The reason we do not want to promote union field accesses is that they can introduce UB, i.e., they can go wrong. We want to [minimize the ways promoteds can fail to evaluate](rust-lang/const-eval#53). Also this change makes things more consistent overall, removing a special case that was added without much consideration (as far as I can tell). Cc `@rust-lang/wg-const-eval`
RFC 3072 got accepted, so most of the rest of the work here is tracked at rust-lang/rust#80619. |
Over the years, promotion has caused many problems. The point of this issue is to collect those cases, which hopefully serves as a useful argument when we start to deprecate some of those mechanisms or when we reject proposals to promote more things.
What is promotion?
The short version is "it makes
&expr
have static lifetime for someexpr
". For more details, see here.What are the problems?
The earliest critical problem I am aware of is rust-lang/rust#50814, where I made Miri detect arithmetic overflows even in release mode and promotion made that unsound because it lead to unexpected const-eval failures. We later plugged that kind of soundness hole for good but it can still lead to unexpected program aborts, which is not great.
The high-level summary is that we have a problem when the promoted constant fails to evaluate: it might be in dead code, so we cannot just make compilation fail (that would mean that our decision to promote caused otherwise valid code to fail to compile). But we have to do something. All other constants stop compilation when they fail to evaluate, so this requires special cases all over the place (we have checks for "is this a promoted" in const error reporting, in codegen, in Miri, and probably more places; also see rust-lang/rust#75461, rust-lang/rust#71800).
Most recently, we realized that our
required_consts
scheme, which is supposed to ensure that we do not compile functions that use ill-formed consts, also needs a special case as we can indeed have ill-formed promoteds.@oli-obk collected some more problems specifically around promoting
const fn
calls in #19.Other "fun" cases of promotion requiring a special case:
What could be the solution?
First of all we tried hard to limit promotion and even de-promoted some functions (rust-lang/rust#67531, rust-lang/rust#71796). We distinguish between "implicit" and "explicit" promotion context (see here, and are less careful in "explicit" contexts such as
const
initializers. However, even those can have dead code, so all the problems caused by promoteds that fail to evaluate still apply.But what is the endgame here, what is a subset of promotable expressions that actually avoids all these problems? The largest possible subset is the set of expressions that cannot fail to evaluate. This covers almost all of the above problems; if we consider validation failures (such as invalid results of unsafe code or unexpected interior mutability) to also be evaluation failures, then I think this indeed covers all the problems.
See this hackmd for the details of that possibility.
So the least thing would be to forbid all of fallible operations. Another choice, perhaps easier to explain, would be to say that we only promote things that would also be legal as a pattern. That would mean de-promoting all arithmetic and logical operations as well -- but it is not clear that we can get away with that, so we might have to settle for infallible operations.
The intended replacement for code that wants things to have static lifetime is to explicitly request that lifetime via rust-lang/rfcs#2920. Compilation fails when that const block fails to evaluate, which means that promotion itself cannot fail, thus satisfying the infallibility condition.
The text was updated successfully, but these errors were encountered: