-
Couldn't load subscription status.
- Fork 557
Add caveats about mutable references in consts #2058
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
base: master
Are you sure you want to change the base?
Conversation
| ``` | ||
|
|
||
| > [!NOTE] | ||
| > This is allowed as, for a value of a zero-sized type, no bytes can actually be mutated. We must accept this as `&mut []` is [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 believe that this is the first time in the reference that we document that mutable references to zero-length arrays can be promoted.
Note that this only works with zero-length arrays. Also, this promotion does not seem to always happen, see rust-lang/rust#140126
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.
Note that this only works with zero-length arrays. Also, this promotion does not seem to always happen, see rust-lang/rust#140126
Look at the bottom note in https://doc.rust-lang.org/nightly/reference/const_eval.html#constant-expressions and rust-lang/rust#143129.
src/items/constant-items.md
Outdated
| > This is allowed as it's separately not allowed to read from an external static during constant evaluation. See [const-eval.const-expr.path-static]. | ||
| > [!NOTE] | ||
| > We also disallow, in the final value, shared references to mutable statics created in the initializer for a separate reason. Consider: |
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 preexisting note seems incorrect. It talks about mutable statics, and then shows an example with a reference to an interior-mutable temporary. Such temporaries do not live for 'static. And even if they were to, they would be a const-promoted thing, which is subtly different from 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.
In an initializer such as,
const _: &AtomicU8 = &AtomicU8::new(0);the rules for "extending based on expressions" apply as follows:
- The initializer is an extending expression.
- The borrow expression is therefore an extending expression, so the operand of the borrow is an extending expression.
The "operand of an extending borrow expression has its temporary scope extended". I.e., we extend the temporary scope of AtomicU8::new(0).
How far do we extend it? "To the end of the program":
Lifetime extension also applies to static and const items, where it makes temporaries live until the end of the program.
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 also specify that interior mutable values are not subject to constant promotion.
https://doc.rust-lang.org/nightly/reference/destructors.html#constant-promotion
Promotion of a value expression to a
'staticslot occurs when the expression could be written in a constant and borrowed, and that borrow could be dereferenced where the expression was originally written, without changing the runtime behavior. That is, the promoted expression can be evaluated at compile-time and the resulting value does not contain interior mutability or destructors (these properties are determined based on the value where possible, e.g.&Nonealways has the type&'static Option<_>, as it contains nothing disallowed).
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 is still confusing to call this a "mutable static", which one could reasonably interpret as referring to a static mut or at least some sort of static item with mutability.
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.
(Edit: GH didn't show me @RalfJung's comment; this was posted in parallel to it:)
All that said, I agree the wording was imperfect. I've pushed a commit to clarify the point.
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 is still confusing to call this a "mutable static", which one could reasonably interpret as referring to a
static mutor at least some sort ofstaticitem with mutability.
Yes, agreed; this is avoided in the revision.
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.
Should we give the failing examples a rationale, too? The passing ones have rationales, but we don't explain why e.g. we don't want to allow creating references to interior mutable data from a const unless the reference is to a static item
As it turns out, the story that "the final value of a constant cannot contain any mutable references" has some nuance to it. Let's describe these caveats. Thanks to theemathas for noticing these and to RalfJ for filling in context.
We have a note that describes why, perhaps surprisingly, it's OK to have a reference to a static with an interior mutable value in the final value of a constant item but it's not OK if the interior mutable value is a temporary from the initializer. There was room to improve how this was worded, so let's take it.
2e7cb57 to
7acafcd
Compare
|
This PR was rebased onto a different master commit. Here's a range-diff highlighting what actually changed. Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers. |
Sure. I've added some rationales. I'm curious, though, what would you say for this one? # #![allow(static_mut_refs)]
static mut S: u8 = 0;
const _: &dyn Send = &unsafe { &mut S }; // ERROR.
// ^^^^^^
// Not allowed as the mutable reference appears in the final value,
// even though type erasure occurs.(It's easier to say, for this sort of thing, why we allow something rather than why we don't, as we're conservatively only allowing the things that we have good reason to allow and where we can prove that it's OK to allow those things.) |
7acafcd to
5cad100
Compare
We had described the design rationale for why certain values of constant items were accepted, but for the values that are not accepted, while we had described mechanically the reasons for this, we had not described the rationale for it. This can be a bit tricky, because often the real rationale is that "we're being conservative and only allowing the cases where we have a good reason to allow them and where we can prove that allowing them is OK". So it is easier to describe why we allow something than why we don't. But still, let's try to describe some reasons why we don't allow some things yet.
5cad100 to
486075d
Compare
|
Yea that case is just a limitation of the few provenance checks happening in CTFE. We do allow use std::sync::atomic::AtomicBool;
static S: AtomicBool = AtomicBool::new(false);
const _: &dyn Send = &unsafe { &S }; |
As it turns out, the story that "the final value of a constant cannot contain any mutable references" has some nuance to it. Let's describe these caveats.
Thanks to theemathas for noticing these and to RalfJ for filling in context.
cc @RalfJung @oli-obk @theemathas @tshepang @ehuss