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

Open
wants to merge 10 commits into
base: master
Choose a base branch
from

Conversation

dingxiangfei2009
Copy link

@dingxiangfei2009 dingxiangfei2009 commented Sep 11, 2024

Tracked by rust-lang/rust#119618

Blocked on rust-lang/rust#129759

Stabilization report: rust-lang/rust#128183

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:/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
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
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
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
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 the PR author. 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
Author

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

dingxiangfei2009 and others added 6 commits October 9, 2024 06:43
Co-authored-by: Eric Huss <[email protected]>
This removes some content for const_refs_to_static which seems to
duplicate content elsewhere. In particular:

* Remove the examples of allowed use of `static` in `const_eval.md`. We
  don't show examples for any of the other permitted uses. Additionally,
  I believe the other points in the list already cover concerns such as
  interior mutability (and possibly more precise way, since the existing
  content mentions things like transient places).
* I think a single statement that reads of `extern` statics is
  sufficient to follow the pattern of all the other allowed behaviors.
* Removed the examples from the `constant-items.md` chapter about
  allowing use of `static`s. Again, we generally don't duplicate what
  is allowed unless there is some explicit statement indicating that
  it is not. Also, I am reluctant to duplicate what is allowed between
  `const_eval.md` and `constant-items.md`.
@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
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
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
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.

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 the PR author. S-waiting-on-stabilization Waiting for a stabilization PR to be merged in the main Rust repository
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants