Skip to content

Commit e6d1254

Browse files
Define promotion in terms of "context" and "promotability"
1 parent 039b0f2 commit e6d1254

File tree

1 file changed

+113
-6
lines changed

1 file changed

+113
-6
lines changed

promotion.md

+113-6
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,123 @@
11
# Const promotion
22

3-
"Promotion" is a mechanism that affects code like `&3`: Instead of putting it on
4-
the stack, the `3` is allocated in global static memory and a reference with
5-
lifetime `'static` is provided. This is essentially an automatic transformation
6-
turning `&EXPR` into `{ const _PROMOTED = &EXPR; EXPR }`, but only if `EXPR`
7-
qualifies.
3+
"Promotion" is the act of guaranteeing that code not written in a const context
4+
(e.g. initalizer of a `const` or `static`, or an array length expression) will
5+
be run at compile-time.
6+
7+
## Promotion contexts
8+
9+
There are a few different contexts where promotion is beneficial.
10+
11+
### Lifetime extension
12+
13+
"Lifetime extension" is a mechanism that affects code like `&3`: Instead of
14+
putting it on the stack, the `3` is allocated in global static memory and a
15+
reference with lifetime `'static` is provided. This is essentially an
16+
automatic transformation turning `&EXPR` into `{ const _PROMOTED = &EXPR; EXPR
17+
}`, but only if `EXPR` qualifies.
818

919
Note that promotion happens on the MIR, not on surface-level syntax. This is
1020
relevant when discussing e.g. handling of panics caused by overflowing
1121
arithmetic.
1222

13-
## Rules
23+
Lifetime extension is described in [RFC
24+
1414](https://github.com/rust-lang/rfcs/pull/1414). Note that the PR uses the
25+
word "promotion" to refer exclusively to lifetime extension, since this was the
26+
first context where promotion was done.
27+
28+
### Non-`Copy` array initialization
29+
30+
Another promotion context was introduced in [RFC
31+
2203](https://github.com/rust-rfcs/const-eval/blob/master/const.md). In this
32+
case, we try to promote the initializer in expressions like `[Vec::new(); 32]`,
33+
which allows non-`Copy` types to be used as array initializers.
34+
35+
### `#[rustc_args_required_const(...)]`
36+
37+
Additionally, some platform intrinsics require certain operations to be
38+
immediates (known at compile-time). We use the `#[rustc_args_required_const]`
39+
attribute, introduced in
40+
[rust-lang/rust#48018](https://github.com/rust-lang/rust/pull/48018), to
41+
specify these parameters and (aggressively, see below) try to promote the
42+
corresponding arguments.
43+
44+
### Implicit and explicit contexts
45+
46+
We take great care that promotion does not cause user code to behave in
47+
unexpected ways. Users might be surprised to learn that whenever they take a
48+
reference to a temporary, that temporary may be promoted away and never
49+
actually put on the stack. In this way, lifetime extension is an "implicit
50+
promotion context": the user did not ask for the value to be promoted.
51+
52+
On the other hand, when a user passes an expression to a function with
53+
`#[rustc_args_required_const]`, they are explicitly asking for that expression
54+
to be evaluated at compile-time even though they have not written it in a
55+
`const` declaration. We call this an "explicit promotion context".
56+
57+
Currently, non-`Copy` array initialization is treated as an implicit context.
58+
59+
The distinction between these two determines whether calls to arbitrary `const
60+
fn`s (those without `#[rustc_promotable]`) are promotable (see below). See
61+
[rust-rfcs/const-eval#19](https://github.com/rust-rfcs/const-eval/issues/19)
62+
for a thorough discussion of this. At present, this is the only difference
63+
between implicit and explicit contexts. The requirements for promotion in an
64+
implicit context are a superset of the ones in an explicit context.
65+
66+
### Lifetime extension in `const` and `static`
67+
68+
We defined above that promotion guarantees that code in a non-const context
69+
will be executed at compile-time. However, lifetime extension is useful
70+
*inside* `const`s and `static`s as well (unlike the other promotion contexts).
71+
Strictly speaking, lifetime extension in a const-context is not promotion; it
72+
does not create `promoted`s in the MIR. However the same rules for
73+
promotability apply inside a const-context as outside.
74+
75+
It's an open question whether lifetime extension within a `const` or `static`
76+
should be an implicit or explicit context. Currently it is treated as an
77+
implicit one.
78+
79+
## Promotability
80+
81+
We have defined when it is desirable to promote expressions but have not yet
82+
defined which expressions can actually be promoted. We refer to such
83+
expressions as "promotable".
84+
85+
### Named locals
86+
87+
Promotable expressions cannot refer to named locals. This is not a technical
88+
limitation with the CTFE engine. While writing `let x = {expr}` outside of a
89+
const context, the user likely expects that `x` will live on the stack and be
90+
initialized at run-time. Although this is not (to my knowledge) guaranteed by
91+
the language, we do not wish to violate the user's expectations here.
92+
93+
### Single assignment
94+
95+
We only promote temporaries that are assigned to exactly once. For example, the
96+
lifetime of the temporary whose reference is assigned to `x` below will not be
97+
extended.
98+
99+
```rust
100+
let x: &'static i32 = &if cfg!(windows) { 0 } else { 1 };
101+
```
102+
103+
Once again, this is not a fundamental limitation in the CTFE engine; we are
104+
perfectly capable of evaluating such expressions at compile time. However,
105+
determining the promotability of complex expressions would require more
106+
resources for little benefit.
107+
108+
### Access to a `const` or `static`
109+
110+
Accesses to `const`s are always promotable, regardless of the body of the
111+
`const`. For instance, while the previous example was not legal, the
112+
following would be:
113+
114+
```rust
115+
const NOT_WINDOWS: i32 = if cfg!(windows) { 0 } else { 1 };
116+
let x: &'static i32 = &NOT_WINDOWS;
117+
```
118+
119+
However, an access to a `static` is only promotable within the initializer of
120+
another `static`.
14121

15122
### Panics
16123

0 commit comments

Comments
 (0)