Skip to content
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 expression can borrow static items #1610

Merged
merged 1 commit into from
Dec 2, 2024

Conversation

dingxiangfei2009
Copy link
Contributor

@dingxiangfei2009 dingxiangfei2009 commented Sep 11, 2024

Tracked by rust-lang/rust#119618

Stabilization report: rust-lang/rust#128183

Fix #1690

This PR updates the documentation to mention that now static items can be used for borrowing, explicitly or implicitly, which permits general use of them in const context.

@ehuss ehuss added the S-waiting-on-stabilization Waiting for a stabilization PR to be merged in the main Rust repository label Sep 11, 2024
@dingxiangfei2009 dingxiangfei2009 changed the title const expression can borrow static items const expression can borrow static items Sep 11, 2024
Copy link
Contributor

@ehuss ehuss left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you also update https://github.com/rust-lang/reference/blob/master/src/items/static-items.md which mentions this isn't possible?

I'm wondering if this might be confusion (and perhaps my understanding is missing) with regards to what counts as a borrow. IIUC, statics have an implied &, is that correct? For example, if you see const C: i32 = SOME_STATIC;, there's no obvious borrow there.

@dingxiangfei2009 dingxiangfei2009 force-pushed the const-ref-to-static branch 2 times, most recently from 3d0233b to 75faa87 Compare September 11, 2024 17:32
@dingxiangfei2009
Copy link
Contributor Author

@ehuss I updated the wording to include simply "use of statics".

I have updated static-items page as well with tweaks on wording.

@traviscross
Copy link
Contributor

We probably want to update the constant-items page that currently says:

Constants may refer to the address of other constants...

Since it'd now make sense for that to also address the fact that constants may refer to statics with some limitations.

@traviscross
Copy link
Contributor

Also in const_eval.md, where it says:

Paths to statics. These are only allowed within the initializer of a static.

...that seems to need to change also.

@traviscross
Copy link
Contributor

traviscross commented Sep 17, 2024

More generally, it seems what we need to do here is to loosen the various places in the Reference that have these restrictions, and in loosening them, add the necessary caveats, e.g. with respect to Freeze as described in rust-lang/rust#128183.

@RalfJung
Copy link
Member

RalfJung commented Sep 21, 2024

For example, if you see const C: i32 = SOME_STATIC;, there's no obvious borrow there.

There's no borrow at all there, obvious or otherwise.

MIR happens to represent SOME_STATIC as a pointer, making this look like *SOME_STATIC_PTR -- so even there, there's no borrow. Also that is an implementation detail and should not affect the reference at all.

## Use and reference to `static` items

When a constant item or constant block is defined, [`static` items] can be used, borrowed or taken address of.
By extension, you are allowed to call methods that immutably borrows the `static` items as receivers.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah this should definitely explain that it is not allowed to read from or write to any mutable static (static mut and static !Freeze).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we already have a link into the reference to explain the static !Freeze items? It makes sense to me to make a reference here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh this is largely missing. I guess I have to expand this section in const_eval.md. I can do that.

@RalfJung
Copy link
Member

RalfJung commented Sep 21, 2024

Constants may refer to the address of other constants...

That's a somewhat nonsensical statement as constants are values, they don't have an address.

@RalfJung
Copy link
Member

I opened #1624 to fix the part about "address of a constant".

@dingxiangfei2009
Copy link
Contributor Author

dingxiangfei2009 commented Sep 22, 2024

@rustbot ready

  • I sieved through the document and I believe these are the places where use of static is mentioned.
  • I managed to add some sort of document about Freeze without mentioning it, given that it is not stabilized. In case [RFC] core::marker::Freeze in bounds rfcs#3633 lands and stabilization of the trait actually happens, I will follow up with the edits.

@rustbot rustbot added the S-waiting-on-review Status: The marked PR is awaiting review from a maintainer label Sep 22, 2024
Copy link
Contributor

@ehuss ehuss left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there anywhere that mentions the extern static restrictions mentioned in the stabilization report?

src/const_eval.md Outdated Show resolved Hide resolved
src/const_eval.md Outdated Show resolved Hide resolved
src/const_eval.md Outdated Show resolved Hide resolved
src/const_eval.md Outdated Show resolved Hide resolved
@dingxiangfei2009
Copy link
Contributor Author

@ehuss I added a section on extern { static }.

@ehuss
Copy link
Contributor

ehuss commented Oct 9, 2024

I pushed a commit to simplify things. After reading more carefully, I was feeling like this was duplicating content that is already specified in the const_eval chapter. I'm reluctant to duplicate things since it makes it more challenging to maintain. I'm also a little reluctant to add deeper "exploration" style content which is essentially restating other rules. There is more specific reasoning in the commit message. If you disagree or think there is something that is now not documented, please let me know!

I realize this removes a lot of content from this PR, and I apologize for that. I probably should have been paying more attention earlier on and provided some guidance.

As for having more examples that illustrate specific const-eval limitations, I expect those to be added later over time (perhaps through the test suite links).

@traviscross Would you be willing to give this a final look? I think this is ready to go from my perspective.

Copy link
Contributor

@ehuss ehuss left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! ❤️

* The type must have the `Sync` trait bound to allow thread-safe access.
* Constants cannot refer to statics.
All access to a static is safe,
provided that the type must have the `Sync` trait bound to allow thread-safe access.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes it sound like a !Sync static would be unsafe to access. But actually it's just rejected.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! I have rewritten this to use more direct language. I'm not sure why the original was written to say "it is safe to ...", since there isn't anything to imply that it was unsafe.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I fixed the wording, to mention that a type with !Sync is rejected.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I picked the changeset from @ehuss actually. I think those wordings are better.

@@ -23,7 +23,8 @@ to be run.
* [Const parameters].
* [Paths] to [functions] and [constants].
Recursively defining constants is not allowed.
* Paths to [statics]. These are only allowed within the initializer of a static.
* Paths to immutable [statics].
* Reads of [`extern` statics] are not allowed.
Copy link
Member

@RalfJung RalfJung Oct 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reads from and writes to static mut and static with interior mutability are also not allowed.

And then there are restrictions on references that escape into the final value of the const/static. More precisely, this is about expressions that have been subject to lifetime extension in the top-level scope of a const/static initializer. Those references must also be of type &T and point to a value without an UnsafeCell.

And finally, specifically for const there also can't be any references to anything mutable in the final value.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, I'm trying to unpack what you are saying here and align that with the current rules.

Reads from and writes to static mut

Isn't this implied by the fact that you are not allowed to refer to static mut at all by the rule that says paths only to immutable statics are allowed?

Reads from and writes to [..] static with interior mutability are also not allowed.

Thanks, I have added that.

And then there are restrictions on references that escape into the final value of the const/static.

Isn't this specified in the later rule that limits borrows of UnsafeCell that are transient? (That was my reading of it, and why I didn't say it explicitly.)

And finally, specifically for const there also can't be any references to anything mutable in the final value.

Thanks, I have added that to the constant-items chapter.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I actually have found the points mentioned down in the list here, in a bullet point starting with All forms of [borrow]s, including raw borrows, with one limitation .... I think we have it covered.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this implied by the fact that you are not allowed to refer to static mut at all by the rule that says paths only to immutable statics are allowed?

That's not correct, referring to static mut is permitted.
https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=7519fded08041a618e0b57c710ab6c25

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this specified in the later rule that limits borrows of UnsafeCell that are transient? (That was my reading of it, and why I didn't say it explicitly.)

Ah right, I forgot that those are already documented.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's not correct, referring to static mut is permitted.

OK, then I go back to my comment earlier about why the word "immutable" is being added here.

I would think this PR shouldn't add the word immutable, and the edit @dingxiangfei2009 just added about not being allowed to read or write from static mut should cover everything?

(Because as written now, it says you can only do paths to immutable statics, but that does not seem to be true based on the playground above.)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah you are right, immutable should not be mentioned here.

Copy link
Contributor

@ehuss ehuss left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should be the last edit, do you agree?

@@ -23,7 +23,10 @@ to be run.
* [Const parameters].
* [Paths] to [functions] and [constants].
Recursively defining constants is not allowed.
* Paths to [statics]. These are only allowed within the initializer of a static.
* Paths to immutable [statics] with these exception with these restrictions.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* Paths to immutable [statics] with these exception with these restrictions.
* Paths to [statics] with the following restrictions:

* Paths to immutable [statics] with these exception with these restrictions.
* Reads out of and writes into [`extern` statics] are not allowed.
* Reads out of and writes into either a `static` with data equipped with interior mutability,
or a whole or parts of `static mut`, are not allowed.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am confused by the wording here. Why does static mut emphasize this "whole or part of", but interior mutable static does not? The two are treated exactly the same so they should also be described the same.

The actual underlying check is simply "is this global mutable memory -- if yes, reject read", and "if this memory global -- if yes, reject write".

Copy link
Contributor

@chorman0773 chorman0773 Oct 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this code accepted?

static FOO: (u8, UnsafeCell<u8>) = (0, UnsafeCell::new(1));

const BAR: u8 = FOO.0;

Edit: Checked and indeed it is not. So yeah, the text shouldn't differ in wording between the two.

@rustbot rustbot added the S-waiting-on-author Status: The marked PR is awaiting some action (such as code changes) from the PR author. label Oct 22, 2024
@@ -23,7 +23,10 @@ to be run.
* [Const parameters].
* [Paths] to [functions] and [constants].
Recursively defining constants is not allowed.
* Paths to [statics]. These are only allowed within the initializer of a static.
* Paths to immutable [statics] with these restrictions and observations.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this might still need to be updated, since it allows static mut.

Suggested change
* Paths to immutable [statics] with these restrictions and observations.
* Paths to [statics] with these restrictions and observations:

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Applied

@dingxiangfei2009
Copy link
Contributor Author

@rustbot ready

  • Rebased with merge conflict resolutions
  • Clarify the accepted use of mutable static items.

@rustbot rustbot added S-waiting-on-review Status: The marked PR is awaiting review from a maintainer and removed S-waiting-on-author Status: The marked PR is awaiting some action (such as code changes) from the PR author. labels Oct 23, 2024
@ehuss
Copy link
Contributor

ehuss commented Oct 25, 2024

@RalfJung How do you feel about the final wording here?

@@ -42,7 +42,10 @@ r[const-eval.const-expr.path-item]
Recursively defining constants is not allowed.

r[const-eval.const-expr.path-static]
* Paths to [statics]. These are only allowed within the initializer of a static.
* Paths to [statics] with these restrictions and observations.
* In particular, reads and writes to any `static`, `static mut` or [`extern` statics] is not allowed.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reads from immutable statics are allowed.

Comment on lines 47 to 48
* Immutable borrows and pointers into immutable part of a `static` are allowed and observes the same restriction
on all other forms of [borrow]s as mentioned below.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't quite parse for me. I'm also not sure what the point of this bullet is -- is there any reason to not just entirely remove it?

@@ -42,7 +42,8 @@ r[const-eval.const-expr.path-item]
Recursively defining constants is not allowed.

r[const-eval.const-expr.path-static]
* Paths to [statics]. These are only allowed within the initializer of a static.
* Paths to [statics] with these restrictions and observations.
* In particular, reads and writes to any `static mut` or [`extern` statics] is not allowed.
Copy link
Contributor

@traviscross traviscross Nov 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So over in #1657, there is proposed language:

Static initializers may refer to and even read from other statics.
When reading from mutable statics, they read the initial value of that static.

The language in this PR seems contradictory to the language in that one, no? Or is there a subtle distinction we're making? Do we need some carve-out here?

(We talked this through on the lang-docs call and couldn't immediately work it out.)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@RalfJung Can you write down exactly what we should say here? It would be helpful since we don't know exactly what the rules are. From looking at the implementation, I'm guessing, is it the CanAccessMutGlobal setting, which appears to be set only when in a static initializer? Does this need to say that you cannot read from static mut unless inside a static initializer, and that you can never write to a static mut, and that reading and writing extern statics always disallowed?

Copy link
Member

@RalfJung RalfJung Nov 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes:

  • reads from mutable statics (static mut, and interior mutable static) are forbidden except when evaluating the initializer expression of a static/static mut (so to be clear, the read can be syntactically inside a const fn or not, what matters is the const context, i.e. whether this is computing the initial value of a static or not)
  • writes to mutable statics are forbidden everywhere
  • reads from and writes to extern static are forbidden everywhere

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dingxiangfei2009 could you update the text accordingly? This is about to hit stable, would be good to get at least the nightly reference updated properly.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have updated the text just now, so that the cases for the two evaluation contexts are clearer.

@@ -48,6 +48,8 @@ const BITS_N_STRINGS: BitsNStrings<'static> = BitsNStrings {
};
```

The final value of a `const` item cannot contain references to anything mutable.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably get some r label.

Suggested change
The final value of a `const` item cannot contain references to anything mutable.
r[items.const.final-value-immutable]
The final value of a `const` item cannot contain references to anything mutable.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Applied

@@ -42,7 +42,9 @@ r[const-eval.const-expr.path-item]
Recursively defining constants is not allowed.

r[const-eval.const-expr.path-static]
* Paths to [statics]. These are only allowed within the initializer of a static.
* Paths to [statics] with these restrictions and observations.
* If the evaluation is carried out in an initializer of a `const` item, or inside a `const` block, reads and writes to any `static mut` or [`extern` statics] are not allowed.
Copy link
Member

@RalfJung RalfJung Nov 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is missing some things, like array lengths or enum discriminants. I wouldn't try to list them all. Instead, I would say "If the evaluation is not carried out in an initializer of a static item, ...".

Also, saying static mut is wrong since it fails to mention interior mutability. By summary carefully mentioned interior mutability. Please ensure your text matches my summary.

Suggested change
* If the evaluation is carried out in an initializer of a `const` item, or inside a `const` block, reads and writes to any `static mut` or [`extern` statics] are not allowed.
* If the evaluation is *not* carried out in an initializer of a `static` item, then reads and writes to any mutable `static` (`static mut` or `static` with an interior mutable type) or [`extern` statics] are not allowed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Applied with edits. I am also reordering the bullets around.

* Paths to [statics]. These are only allowed within the initializer of a static.
* Paths to [statics] with these restrictions and observations.
* If the evaluation is carried out in an initializer of a `const` item, or inside a `const` block, reads and writes to any `static mut` or [`extern` statics] are not allowed.
* If the evaluation is carried out in an initializer of a `static` item, reads into a `static`, or inside `unsafe` blocks reads into `static mut` are allowed. Reads and writes to `extern` statics are still not allowed.
Copy link
Member

@RalfJung RalfJung Nov 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* If the evaluation is carried out in an initializer of a `static` item, reads into a `static`, or inside `unsafe` blocks reads into `static mut` are allowed. Reads and writes to `extern` statics are still not allowed.
* If the evaluation is carried out in an initializer of a `static` item, then *writes* to a mutable `static` are not allowed, but reads are allowed. Reads and writes to `extern` statics are still not allowed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Applied

* Paths to [statics] with these restrictions and observations.
* Reads and writes to `extern` statics are not allowed in any constant evaluation context.
* If the evaluation is carried out in an initializer of a `static` item, then *writes* into mutable `static` are not allowed, such as `static mut`s and `static`s containing an interior-mutable type.
* If the evaluation is *not* carried out in an initializer of a `static` item, then reads and writes to any mutable `static`, such as `static mut`s and `static`s containing an interior-mutable type.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something (a verb?) seems to have been lost in this sentence. Maybe this?

Suggested change
* If the evaluation is *not* carried out in an initializer of a `static` item, then reads and writes to any mutable `static`, such as `static mut`s and `static`s containing an interior-mutable type.
* If the evaluation is *not* carried out in an initializer of a `static` item, then reads and writes to any mutable `static`, such as `static mut`s and `static`s containing an interior-mutable type, are not allowed.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With writes being handled separately by my suggestion above, this can just say "reads from any mutable `static".

Comment on lines 46 to 47
* Reads and writes to `extern` statics are not allowed in any constant evaluation context.
* If the evaluation is carried out in an initializer of a `static` item, then *writes* into mutable `static` are not allowed, such as `static mut`s and `static`s containing an interior-mutable type.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* Reads and writes to `extern` statics are not allowed in any constant evaluation context.
* If the evaluation is carried out in an initializer of a `static` item, then *writes* into mutable `static` are not allowed, such as `static mut`s and `static`s containing an interior-mutable type.
* Writes to `static` items are not allowed in any constant evaluation context.
* Reads from `extern` statics are not allowed in any constant evaluation context.

I think that is simpler than what you have.

Comment on lines 48 to 50
* If the evaluation is *not* carried out in an initializer of a `static` item,
then reads from any mutable `static` are not allowed.
By mutable `static` it includes `static mut`s and `static`s containing an interior-mutable type.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* If the evaluation is *not* carried out in an initializer of a `static` item,
then reads from any mutable `static` are not allowed.
By mutable `static` it includes `static mut`s and `static`s containing an interior-mutable type.
* If the evaluation is *not* carried out in an initializer of a `static` item,
then reads from any mutable `static` are not allowed.
A mutable `static` is a `static mut` item, or a `static` item with an interior-mutable type.

src/const_eval.md Outdated Show resolved Hide resolved
src/const_eval.md Outdated Show resolved Hide resolved
@traviscross traviscross added this pull request to the merge queue Dec 2, 2024
Merged via the queue into rust-lang:master with commit 270c2b9 Dec 2, 2024
5 checks passed
@RalfJung
Copy link
Member

RalfJung commented Dec 2, 2024 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-review Status: The marked PR is awaiting review from a maintainer
Projects
None yet
Development

Successfully merging this pull request may close these issues.

The reference is wrong about constants referencing statics since v1.83.0
6 participants