-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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 vs static #246
Const vs static #246
Conversation
Clarify taking addresses of globals in globals
We have been wrestling with the best way to represent globals for some | ||
times. There are number of interrelated issues: | ||
|
||
- *Significant addresses and inlining:* For optimization purposes, 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.
Insignificant addresses (unnamed_addr
) don't have any implications on mutability. Marking two mutable globals with the same initializer as unnamed_addr
does not mean the compiler can merge them without first proving that they are read-only/write-only or some other invariant guaranteeing that they are not used independently.
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.
@thestinger You seem to be responding to some text other than the one the comment is attached to, since that text doesn't mention mutability. Can you clarify what you mean? (That is, what concrete change does that imply for the proposal? Is there a restriction you think we ought to lift?)
However, I also want to point out that the semantics we choose here are not governed by what LLVM does -- that is, we could decide to inline or duplicate constants more aggressively (or less aggressively) than LLVM permits itself to 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.
The address being insignificant only means that converting it to an integer and performing comparisons isn't meaningful. A constant can be inlined without giving it an insignificant address by using available_externally
and a mutable global can be given an insignificant address without breaking anything.
In general this seems like the right approach to me! Obvious bikeshed is obvious: we currently use the |
} | ||
|
||
This would allow us to make the `value` field on `UnsafeCell` private, | ||
among other things. |
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 is similar to C++'s constexpr
. See also my related thoughts here (s/static
/const
/g). In particular C++ also originally started out, in C++11, with only allowing constexpr
functions to consist of a single expression (although with recursion); then in C++14 they greatly generalized this to allow various imperative constructs as well, as long as they can be computed at compile time. (I haven't looked at what the precise rules are; they're probably complicated.) In Rust, though, we have the advantage that all code barring unsafe
has a precisely-defined meaning. This means that all Rust code which doesn't involve unsafe
can be safely evaluated at compile time! (We can't prove termination, but I don't think we make any attempt to prove it for e.g. impl
lookup or macro expansion either.)
@glaebhoerl true, but in practice "all rust code that doesn't use unsafe" is a fairly small subset, given that library things like |
an `UnsafeCell` in its interior, the compiler may place it in | ||
read-only memory, but otherwise it must be placed in mutable memory. | ||
|
||
`mut` statics may have any type. All access is considered unsafe. |
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.
Is a "static mut" of type T any different from a non-mut static of type UnsafeCell in this proposal? (other than syntax)
It seems not, and in this case it seems redundant to provide static muts.
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.
On Thu, Sep 18, 2014 at 03:05:03PM -0700, bill-myers wrote:
Is a "static mut" of type T any different from a non-mut static of type UnsafeCell in this proposal? (other than syntax)
...
It seems not, and in this case it seems redundant to provide static muts.
I believe you are correct that there is little added value. They are
there for convenience, for analogy with "let mut", primarily, and
perhaps partly for interfacing with C code (that is, extern static
mut?). The latter point seems important, although the memory layout of
Unsafe<T>
and T
are identical, so perhaps it is not. That said, it
makes the proposal simpler to exclude them. This is roughly what PR
#228 proposed (well, not quite, but very similar).
This RFC looks like a great idea. |
I wonder if
Type inference would work out the type of an untyped |
@p1start Pretty sure that can be accomplished with a macro that takes no arguments. |
This seems like a nice clan separation of concerns between truly constant rvalues on the one hand, and global variable lvalues on the other hand. One aspect I didn't see mentioned in the RFC is the case of associated statics: Their proposed semantic only fits I think the rules around borrowing
That is, |
I like this. It neatly steps around the ridiculous |
have generic parameters. For example, the following constant is legal: | ||
|
||
struct WrappedOption<T> { value: Option<T> } | ||
const NONE<T> = WrappedOption { value: None } |
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.
Curious! I had something similar in rust-lang/rust#10798 (I'm kinda surprised the issue is still open).
(As I noted above we do already have the |
Not super happy with the way &CONSTANT works.
let x = &module::UPPER_SNAKE_CASE; it's not clear what the lifetime of the reference is. If we have a static then it's 'static and otherwise it's the lifetime of the current block. Afaik, taking a reference of an identifier always creates a reference with a lifetime as long as the lifetime of the identifier (unless the lifetime of the reference is artificially restricted.)
The word
I don't see why this wouldn't work. Why not reserve the memory after monomorphization?
const X = 1i;
const Y = &X; // static lifetime
let z = &X; // block lifetime This sounds dangerous. Consider the following alternative:
const BAR<T> = &FOO<T>; How would this even work if you can't create such a static? |
In todays Rust,
|
@Kimundi Those are not written in UPPER_SNAKE_CASE. |
The casing style is just convention. Granted, useful convention to see at a glance how something will likely behave, which is what you're meaning, but nevertheless the language does not enforce that unit structs and enum variants are always written in camel caps, nor does it enforce that constants are upper snake case. Besides, I think not knowing whether a reference to a constant/static has Lets assume we switch to separate |
On Sun, Sep 21, 2014 at 09:19:10AM -0700, mahkoh wrote:
Thanks for your comment. Let me try to lay out what led me here.
Actually, I think the lifetime would only be less clear if we had more Interior mutability is the key point here. The main reason to make
It's not that we can't do it; it just means that a single static The
I am not sure what you mean by "dangerous". I interpret it as "unsafe"
It's not that we can't do it, but that awkward questions get raised. Note that if
|
Just so the option is considered: what if we don't add the
magic, and require manually writing the intermediate
instead? Would this meaningfully reduce the amount of complication around interior mutability? If so, how annoying would it be? (If not, it's obviously not worth thinking about further.) |
I was thinking about cases where a wrong lifetime can cause crashes, e.g., transmute and FFI. But after @Kimundi's comment I think this is not really an issue. |
On Tue, Sep 23, 2014 at 05:30:34AM -0700, Gábor Lehel wrote:
In some sense yes. I think the only rule that is directly tied to However, we changed the plan because we wanted to ensure that people |
We've decided to accept this at the last meeting: |
This change is an implementation of [RFC 69][rfc] which adds a third kind of global to the language, `const`. This global is most similar to what the old `static` was, and if you're unsure about what to use then you should use a `const`. The semantics of these three kinds of globals are: * A `const` does not represent a memory location, but only a value. Constants are translated as rvalues, which means that their values are directly inlined at usage location (similar to a #define in C/C++). Constant values are, well, constant, and can not be modified. Any "modification" is actually a modification to a local value on the stack rather than the actual constant itself. Almost all values are allowed inside constants, whether they have interior mutability or not. There are a few minor restrictions listed in the RFC, but they should in general not come up too often. * A `static` now always represents a memory location (unconditionally). Any references to the same `static` are actually a reference to the same memory location. Only values whose types ascribe to `Sync` are allowed in a `static`. This restriction is in place because many threads may access a `static` concurrently. Lifting this restriction (and allowing unsafe access) is a future extension not implemented at this time. * A `static mut` continues to always represent a memory location. All references to a `static mut` continue to be `unsafe`. This is a large breaking change, and many programs will need to be updated accordingly. A summary of the breaking changes is: * Statics may no longer be used in patterns. Statics now always represent a memory location, which can sometimes be modified. To fix code, repurpose the matched-on-`static` to a `const`. static FOO: uint = 4; match n { FOO => { /* ... */ } _ => { /* ... */ } } change this code to: const FOO: uint = 4; match n { FOO => { /* ... */ } _ => { /* ... */ } } * Statics may no longer refer to other statics by value. Due to statics being able to change at runtime, allowing them to reference one another could possibly lead to confusing semantics. If you are in this situation, use a constant initializer instead. Note, however, that statics may reference other statics by address, however. * Statics may no longer be used in constant expressions, such as array lengths. This is due to the same restrictions as listed above. Use a `const` instead. [breaking-change] [rfc]: rust-lang/rfcs#246
This change is an implementation of [RFC 69][rfc] which adds a third kind of global to the language, `const`. This global is most similar to what the old `static` was, and if you're unsure about what to use then you should use a `const`. The semantics of these three kinds of globals are: * A `const` does not represent a memory location, but only a value. Constants are translated as rvalues, which means that their values are directly inlined at usage location (similar to a #define in C/C++). Constant values are, well, constant, and can not be modified. Any "modification" is actually a modification to a local value on the stack rather than the actual constant itself. Almost all values are allowed inside constants, whether they have interior mutability or not. There are a few minor restrictions listed in the RFC, but they should in general not come up too often. * A `static` now always represents a memory location (unconditionally). Any references to the same `static` are actually a reference to the same memory location. Only values whose types ascribe to `Sync` are allowed in a `static`. This restriction is in place because many threads may access a `static` concurrently. Lifting this restriction (and allowing unsafe access) is a future extension not implemented at this time. * A `static mut` continues to always represent a memory location. All references to a `static mut` continue to be `unsafe`. This is a large breaking change, and many programs will need to be updated accordingly. A summary of the breaking changes is: * Statics may no longer be used in patterns. Statics now always represent a memory location, which can sometimes be modified. To fix code, repurpose the matched-on-`static` to a `const`. static FOO: uint = 4; match n { FOO => { /* ... */ } _ => { /* ... */ } } change this code to: const FOO: uint = 4; match n { FOO => { /* ... */ } _ => { /* ... */ } } * Statics may no longer refer to other statics by value. Due to statics being able to change at runtime, allowing them to reference one another could possibly lead to confusing semantics. If you are in this situation, use a constant initializer instead. Note, however, that statics may reference other statics by address, however. * Statics may no longer be used in constant expressions, such as array lengths. This is due to the same restrictions as listed above. Use a `const` instead. [breaking-change] Closes #17718 [rfc]: rust-lang/rfcs#246
Change according to Rust RFC 246 [1], implemented in [2]. [1] rust-lang/rfcs#246 [2] rust-lang/rust@f9fc49c
This was a recent change integrated into the rust compiler as per RFC #246 rust-lang/rfcs#246
This change is an implementation of [RFC 69][rfc] which adds a third kind of global to the language, `const`. This global is most similar to what the old `static` was, and if you're unsure about what to use then you should use a `const`. The semantics of these three kinds of globals are: * A `const` does not represent a memory location, but only a value. Constants are translated as rvalues, which means that their values are directly inlined at usage location (similar to a #define in C/C++). Constant values are, well, constant, and can not be modified. Any "modification" is actually a modification to a local value on the stack rather than the actual constant itself. Almost all values are allowed inside constants, whether they have interior mutability or not. There are a few minor restrictions listed in the RFC, but they should in general not come up too often. * A `static` now always represents a memory location (unconditionally). Any references to the same `static` are actually a reference to the same memory location. Only values whose types ascribe to `Sync` are allowed in a `static`. This restriction is in place because many threads may access a `static` concurrently. Lifting this restriction (and allowing unsafe access) is a future extension not implemented at this time. * A `static mut` continues to always represent a memory location. All references to a `static mut` continue to be `unsafe`. This is a large breaking change, and many programs will need to be updated accordingly. A summary of the breaking changes is: * Statics may no longer be used in patterns. Statics now always represent a memory location, which can sometimes be modified. To fix code, repurpose the matched-on-`static` to a `const`. static FOO: uint = 4; match n { FOO => { /* ... */ } _ => { /* ... */ } } change this code to: const FOO: uint = 4; match n { FOO => { /* ... */ } _ => { /* ... */ } } * Statics may no longer refer to other statics by value. Due to statics being able to change at runtime, allowing them to reference one another could possibly lead to confusing semantics. If you are in this situation, use a constant initializer instead. Note, however, that statics may reference other statics by address, however. * Statics may no longer be used in constant expressions, such as array lengths. This is due to the same restrictions as listed above. Use a `const` instead. [breaking-change] Closes #17718 [rfc]: rust-lang/rfcs#246
This change is an implementation of [RFC 69][rfc] which adds a third kind of global to the language, `const`. This global is most similar to what the old `static` was, and if you're unsure about what to use then you should use a `const`. The semantics of these three kinds of globals are: * A `const` does not represent a memory location, but only a value. Constants are translated as rvalues, which means that their values are directly inlined at usage location (similar to a #define in C/C++). Constant values are, well, constant, and can not be modified. Any "modification" is actually a modification to a local value on the stack rather than the actual constant itself. Almost all values are allowed inside constants, whether they have interior mutability or not. There are a few minor restrictions listed in the RFC, but they should in general not come up too often. * A `static` now always represents a memory location (unconditionally). Any references to the same `static` are actually a reference to the same memory location. Only values whose types ascribe to `Sync` are allowed in a `static`. This restriction is in place because many threads may access a `static` concurrently. Lifting this restriction (and allowing unsafe access) is a future extension not implemented at this time. * A `static mut` continues to always represent a memory location. All references to a `static mut` continue to be `unsafe`. This is a large breaking change, and many programs will need to be updated accordingly. A summary of the breaking changes is: * Statics may no longer be used in patterns. Statics now always represent a memory location, which can sometimes be modified. To fix code, repurpose the matched-on-`static` to a `const`. static FOO: uint = 4; match n { FOO => { /* ... */ } _ => { /* ... */ } } change this code to: const FOO: uint = 4; match n { FOO => { /* ... */ } _ => { /* ... */ } } * Statics may no longer refer to other statics by value. Due to statics being able to change at runtime, allowing them to reference one another could possibly lead to confusing semantics. If you are in this situation, use a constant initializer instead. Note, however, that statics may reference other statics by address, however. * Statics may no longer be used in constant expressions, such as array lengths. This is due to the same restrictions as listed above. Use a `const` instead. [breaking-change] Closes #17718 [rfc]: rust-lang/rfcs#246
A proposal to extend statics/constants so that
static
always declares a global variable (memory location) with an address andconst
declares global values.const
is what we expect users to use most of the time,static
is only intended to be used in narrow circumstances.