Are consts really allocations?

The standard library documentation says:

Examples of allocations include heap allocations, stack-allocated variables, statics, and consts.

Consts aren't allocations... are they? Each use of one is, I guess, in the sense that all temporaries are allocations.

1 Like

Agreed, it seems inaccurate. Consts don't have an address, for one.

1 Like

Read the definition they're using carefully. This is the new name for "allocated object", and is distinct from the other common meaning of "allocation" that only means the alloc in std::alloc - Rust kind of allocation and excludes local variables.

(For example, "non-allocating" in things like Vec<T>VecDeque<T> conversions means no calls to the runtime allocator. They obviously don't mean "no local variables".)

1 Like

But still, how are consts allocations? They do not actually exist anywhere in program memory as independent entities, only as values of other allocated objects.

Right, it's clear they don't mean only heap allocations from their use of "heap allocations" and "stack-allocated" in the quote. The question is meant to ask if consts are allocations in that sense.

No, I don't believe consts are allocations. However, each usage of the constant can become an allocation through static promotion.

This definitely begs for more clarity, I think.

That's helpful. There's a section on constant promotion in the Rust Reference. It says, in its entirety:

[destructors.scope.const-promotion]

Constant promotion

Promotion of a value expression to a 'static slot 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. &None always has the type &'static Option<_>, as it contains nothing disallowed).

The vibes suggest this was written a while ago, and is due for a rewrite now that constant evaluation is better understood.

This is the only place the word "slot" appears in the Reference.

After reading the first sentence a few times, I think all constant expressions qualify (except for interior mutability and destructors). But it is definitely not the case that all such expressions are stored in the static segment of the executable. Maybe this just means all such expressions have the 'static lifetime, and maybe the same address each time they're evaluated. But even that seems too strong a statement to be true; the compiler isn't even required to evaluate all constant expressions at compile time.

The phrase "without changing the runtime behavior" also seems wrong. Without promotion, lots of programs wouldn't even compile, since promotion changes the value's lifetime. For those programs, you have to assume static promotion to get any runtime behavior at all! And then the behavior could depend on the fact that static promotion happened, since references are pointers.

In the section on constants, the Reference says:

[items.const.static-temporary] A reference to a constant will have 'static lifetime if the constant value is eligible for promotion; otherwise, a temporary will be created.

Hmm. The fact that this is here suggests there's a reason for specifying it. But the spec previously said that "Constants are essentially inlined wherever they are used". Apart from whatever "essentially" is supposed to convey, it seems like that should settle this question, among many others.

In summary, I'm still pretty confused.

Having been reading up on optimizing compilers recently (specifically "Global value numbering") the very idea of "allocation" now seems very floaty and ambiguous to me in practice. If you don't take the address of something, there's really not much reason for the compiler to actually put it anywhere until it runs out of registers. I'm sure this will clear up later, probably with alias analysis or something.

1 Like

I suspect the use of a const becomes an allocation, though exactly when that happens is probably underspecified. In MIR, at least, the Rvalue for taking the address of something depends on having a local.

I guess that might mean that, in practice, the const itself isn't exactly an allocation, just the local into which the const was copied. Or maybe any & triggers the rvalue-static-promotion so the allocation is the static not the const.

And of course if you don't take the address then there's no way for the "offset needs to be inside the allocation" rule to actually come up.

Oh, it's not so bad. The compiler can often optimize away an allocation in the same way it can optimize away a redundant read, an unused variable, or anything else. The rule for the compiler is a bit fuzzy, but it's the same for all optimizations:

After the optimization, the program's "observable behavior" (whatever that means) must still conform to the spec, for all possible executions.

Or shorter:

Change whatever you want. Just don't get caught.

I mean, sure, it's just that GVN, and really SSA-like approaches in general, makes it harder to figure out what the "observable behavior" of an allocation actually is. If the compiler is free to merge values and doesn't care at all about variable declarations past them being a way to connect expressions, then the whole concept of local allocations seems to start looking pretty shaky. As mentioned, I suspect aliasing is where it does start kicking back in, an "allocation" is probably pretty tightly connected to an aliasing class / identity from what I've seen so far.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.