-
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
Towards dynamic const-qualify #27
Conversation
Also Cc @ecstatic-morse |
promotion.md
Outdated
In `EMPTY_BYTES`, the reference obtains the lifetime of the "enclosing scope", | ||
similar to how `let x = &mut x;` creates a reference whose lifetime lasts for | ||
the enclosing scope. This is decided during MIR building already, and does not | ||
involve promotion. |
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 "enclosing scope" rule doesn't make sense to me. I don't know how it should extend to arbitrary cases. Is it the braces that cause the problem or the assignment to x
? It also might be good to mention #[rustc_promotable]
here, since without it calls to const fn
s can't get promoted.
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 don't know how it should extend to arbitrary cases. Is it the braces that cause the problem or the assignment to x?
I don't know. I am paraphrasing @eddyb here.
@eddyb are these rules explained anywhere?
It also might be good to mention #[rustc_promotable] here, since without it calls to const fns can't get promoted.
Good point, will do.
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.
Hm I guess I should edit this in the light of our unresolved terminology.
Is the entire "Promotion is not involved" incorrect, or is there something there that makes sense? I was trying to explain why const EMPTY_BYTES: &Vec<u8> = &Vec::new();
works even though this stuff does not get promoted. @ecstatic-morse since my explanation confused you, could you try to explain this in your own words?
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.
See my latest comment for what is actually confusing me here. The "enclosing scope" rule makes sense intuitively, but the details are still not clear to me. That's my problem though, not this PR's.
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 have done some edits. @ecstatic-morse does this resolve your confusion?
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 enclosing scope rule is the same one that makes these two different:
{
let r = &foo(); // compiles
bar(r);
}
{
let r = id(&foo()); // doesn't compile
bar(r);
}
Except instead of let
it's const
/static
and instead of "enclosing block" it's 'static
.
I don't remember the exact name of these semantics, maybe "rvalue/temporary lifetime/scope"? @nikomatsakis would be more helpful here, sorry.
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 call these "temporary lifetimes" -- I've been meaning to writeup some text about them for the rust reference.
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 call these "temporary lifetimes" -- I've been meaning to writeup some text about them for the rust reference.
promotion.md
Outdated
}; | ||
``` | ||
|
||
However, since this is in explicit const context, we could be less strict about |
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.
We are less strict. You can promote any const fn call inside a 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.
Ah, good to know! Are there any other differences? We could in principle be even more aggressive.
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.
iirc we are running just the regular const checks on promoteds inside constants and const fns. So there's no third system, it's exactly as if you wrote an explicit 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.
Isnt promotion also about identifying which parts of the MIR to promote, and then actually patch the MIR to replace uses of the data by a promoted? Somehow this still needs to do something extra inside constants I think, or will it just trigger at every &
?
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.
So implementation wise the difference is IsPromotable
vs IsImplicitlyPromotable
, where iirc IsImplicitlyPromotable
only makes a difference in non-const fns
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.
So IsPromotable
is not about the kind of promotion I am talking about here? :(
What is it about then?
I asked @eddyb about this terminology but must have misunderstood what he said.
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.
So IsPromotable is not about the kind of promotion I am talking about here? :(
I'm confused.
So IsPromotable
and IsImplicitlyPromotable
are required in normal functions, while const fn
and constants only check IsPromotable
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 guess I am confused by the "implicit" part. The promotion to a separate static is also implicit inside a const body.
If the thing inside a normal fn
is "implicit promotion", I would expect the thing inside a const body to be "explicit promotion", but that does not make sense, does it?
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.
yea... it's not the best naming scheme... Maybe we should call the thing in non-const fns "restricted promotion" and the other cases just "promotion" or "unrestricted promotion"
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.
That doesn't convey why it is restricted, though. "implicit" did that nicely, but was not precise enough.
"promotion of run-time code" is verbose but informative. "run-time promotion" is somewhat misleading... "run-time-code promotion"?
So I was using the term for the mechanism, and you were using for the effect. That would explain the confusion. I think "promotion" makes more sense as the name for the mechanism of extracting part of the MIR out -- and that's what Overall we are looking for three terms:
Finally I feel like we are making progress on this minefield of three different things that are all referred to by the same name.^^ Strawman proposals for the three names:
|
Nuking The reason I was abusing the word "promotion" is mostly historical: the lifting was "merely" the way it was implemented in MIR, but both "qualification" and "promotion" predate MIR, and they were referring to AST expressions: "qualification" was the analysis, and "promotion" was the effect of having The way it was done back then made even something as simple as I should've changed the terminology when I implemented the MIR side, but I guess I was trying to make them as close as possible to avoid introducing any bugs (since the old borrowck would still use the information from the AST pass, until we switched to the MIR borrowck, and only recently are we getting close to getting rid of the old one). For the MIR, I think "const checking" and "const promotion" are pretty okay terms. One thing your nomenclature seems to avoid is that the analysis used to nuke The reason you can be more relaxed is the code must run at compile-time anyway, so there is no risk of a |
The mechanism is called promotion because it is what does the action of promoting. The unsuspecting-runtime-code-gets-const-evaluated thing should probably focus on the fact that const evaluating arbitrary runtime code will fail, so we need to be careful about what we guarantee. Maybe "guaranteed" is the right keyword? I like "lifetime extension", because it makes it so much clearer that the type system process could just as well extend the lifetime on |
Fair. The focus in this PR anyway is to document current behavior and clarify terminology.
Right, so those would be two different "modes" of the analysis, whatever we end up calling that one.
That doesn't exactly explain which of the multiple possible meanings of this verb you mean. ;) Like, do you consider nuking I think my preferred proposal is to use the term "promotion" in the same sense as Not sure if a separate name for the analysis still makes sense though if we end up always using the "messing with MIR" mechanism for everything? Still, seems nice to be able to talk about them separately. It's just that someone would have to rename everything promotion-analysis-related in rustc... |
FWIW, I've been using the following nomenclature in my head while working on rust-lang/rust#64470 and friends.
Using that terminology, the three jobs of the current qualifcation pass would be:
The last step, promotion, has two different subroutines depending on whether the current item is a I suppose I should be referring to the last two steps together as "lifetime extension", although I mostly just say "promotion" when writing about them. Also, I think "lifetime extension" might already have a specific meaning in the context of the borrow checker? |
@ecstatic-morse thanks a lot, that is very helpful!
Does that attribute have the same effect as being inside the body of a
What exactly is a "temporary" here? Is it just any MIR-level
Call them "promotion" unfortunately overlaps with the third step which is also called "promotion". But what I meant by "lifetime extension" was just what you call "promotability (analysis)". Maybe using the same name here for both is okay, as long as everyone is aware of the more refined terminology and we switch to it when it seems like confusion is arising. |
Ah, that's a good catch. I think this is the place where they diverge? Basically, promotion of Said another way, when it comes to
A temporary is a MIR
I like "promotability analysis" (I didn't have a good noun for the second step). The hierarchy currently looks like this in my head:
|
* The compiler checks that statics are `Sync`, justifying sharing their address across threads. | ||
* [Constants](const.md) and [promoteds](promotion.md) are not allowed to read from statics, | ||
so their final value does not have have to be [const-valid](const_safety.md#const-safety-check-on-values) in any meaningful way. | ||
As of 2019-08, we do check them for validity anyway, to be conservative; and indeed constants could be allowed to read from frozen statics. |
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 think this would be nice personally; less times to invent an ephemeral constant just to please the check and you also get to use exported statics from other crates.
Any future compat hazards with some new feature?
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.
Discovered a problem... If we want to have a scheme like rust-lang/rfcs#2492 but centered around static
s instead, allowing static
s in const contexts will give us all the problems re. separate compilation.
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.
@Centril sorry, I do not follow... why is it harder for separate compilation to support const
referring to static
than const
referring to const
(and the latter we already support)?
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.
In this case it would be a static
for which the value isn't known yet; that is something like pub extern static Foo: u8;
<-- no value. That would be similar to rust-lang/rfcs#2492 in that it is matched up with something in the crate graph. If you can then refer to Foo
in types then that will create all the same problems as with extern existential type
.
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.
As long as whatever instantiates the static
checks them for validity, we are sill good. The important part is the invariant that all statics have been checked. The order doesn't matter.
Question: what is the difference between |
no difference for the caller except in syntax. The function body can't use the argument as const, so you can't forward it internally, so const generics are more powerful there. |
I see. That sounds quite hacky. But on the other hand we probably do want the same checks for both cases (as in both cases it is not locally syntactically apparent that things are evaluated at const-time). So maybe this is a hint that the analysis (shared by promotion-in-runtime-fn and Maybe it makes more sense then to to speak of "explicit-const validation" and "implicit-const validation" or "explicit-const checking" and "implicit-const checking" (I think I prefer the latter) to refer to the analysis that checks if code is amenable to const-eval. And then both |
It seems like we are mostly bikeshedding terminology here at this point, which IMO does not have to block this PR. So, maybe we can move that to an issue? Is there anything else blocking the PR? |
I'm still confused about the first part of this comment. Other than that I think it's ready. |
promotion.md
Outdated
However, since this is in explicit const context, we are less strict about | ||
promotion in this situation: all function calls are promoted, not just | ||
`#[rustc_promotable]` functions. |
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.
@RalfJung I think my confusion really occurs because of this paragraph. It seems to apply that both const
s should compile in the following example, which is not true.
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.
Ah yes. That's because of the Drop
check, which still applies.
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.
It also fails on types which are not Missing a Drop
. I think @oli-obk's comment above was incorrect?const
. Ignore me plz.
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.
That example works if you make new
a const fn
.
r=me with @ecstatic-morse's confusion resolved |
My confusion has been resolved (at least far as this PR is concerned). |
Great, thanks. :) |
Note to self: review this (eventually). |
I finally came around to look at #17 again, and updated the documents with what I learned on the way.
@oli-obk @eddyb please check if this all makes sense. :)