Skip to content

Commit 4b4e01f

Browse files
Define promotion in terms of "context" and "promotability"
1 parent 7231ee7 commit 4b4e01f

File tree

1 file changed

+109
-3
lines changed

1 file changed

+109
-3
lines changed

promotion.md

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

3-
["(Implicit) Promotion"][promotion-rfc] is a mechanism that affects code like `&3`:
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`:
414
Instead of putting it on the stack, the `3` is allocated in global static memory
515
and a reference with lifetime `'static` is provided. This is essentially an
616
automatic transformation turning `&EXPR` into
@@ -10,15 +20,111 @@ 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

23+
Lifetime extension is described in [RFC 1414][promotion-rfc]. The RFC uses the
24+
word "promotion" to refer exclusively to lifetime extension, since this was the
25+
first context where promotion was done.
26+
27+
[promotion-rfc]: https://github.com/rust-lang/rfcs/blob/master/text/1414-rvalue_static_promotion.md
28+
29+
### Non-`Copy` array initialization
30+
31+
Another promotion context was introduced in [RFC
32+
2203](https://github.com/rust-rfcs/const-eval/blob/master/const.md). In this
33+
case, we try to promote the initializer in expressions like `[Vec::new(); 32]`,
34+
which allows non-`Copy` types to be used as array initializers.
35+
36+
### `#[rustc_args_required_const(...)]`
37+
38+
Additionally, some platform intrinsics require certain operations to be
39+
immediates (known at compile-time). We use the `#[rustc_args_required_const]`
40+
attribute, introduced in
41+
[rust-lang/rust#48018](https://github.com/rust-lang/rust/pull/48018), to
42+
specify these parameters and (aggressively, see below) try to promote the
43+
corresponding arguments.
44+
45+
### Implicit and explicit contexts
46+
1347
On top of what applies to [consts](const.md), promoteds suffer from the additional issue that *the user did not ask for them to be evaluated at compile-time*.
1448
Thus, if CTFE fails but the code would have worked fine at run-time, we broke the user's code for no good reason.
1549
Even if we are sure we found an error in the user's code, we are only allowed to [emit a warning, not a hard error][warn-rfc].
1650
That's why we have to be very conservative with what can and cannot be promoted.
1751

18-
[promotion-rfc]: https://github.com/rust-lang/rfcs/blob/master/text/1414-rvalue_static_promotion.md
52+
For example, users might be surprised to learn that whenever they take a
53+
reference to a temporary, that temporary may be promoted away and never
54+
actually put on the stack. In this way, lifetime extension is an "implicit
55+
promotion context": the user did not ask for the value to be promoted.
56+
57+
On the other hand, when a user passes an expression to a function with
58+
`#[rustc_args_required_const]`, they are explicitly asking for that expression
59+
to be evaluated at compile-time even though they have not written it in a
60+
`const` declaration. We call this an "explicit promotion context".
61+
62+
Currently, non-`Copy` array initialization is treated as an implicit context.
63+
64+
The distinction between these two determines whether calls to arbitrary `const
65+
fn`s (those without `#[rustc_promotable]`) are promotable (see below). See
66+
[rust-rfcs/const-eval#19](https://github.com/rust-rfcs/const-eval/issues/19)
67+
for a thorough discussion of this. At present, this is the only difference
68+
between implicit and explicit contexts. The requirements for promotion in an
69+
implicit context are a superset of the ones in an explicit context.
70+
1971
[warn-rfc]: https://github.com/rust-lang/rfcs/blob/master/text/1229-compile-time-asserts.md
2072

21-
## Rules
73+
### Lifetime extension in `const` and `static`
74+
75+
We defined above that promotion guarantees that code in a non-const context
76+
will be executed at compile-time. However, lifetime extension is useful
77+
*inside* `const`s and `static`s as well (unlike the other promotion contexts).
78+
Strictly speaking, lifetime extension in a const-context is not promotion; it
79+
does not create `promoted`s in the MIR. However the same rules for
80+
promotability apply inside a const-context as outside.
81+
82+
It's an open question whether lifetime extension within a `const` or `static`
83+
should be an implicit or explicit context. Currently it is treated as an
84+
implicit one.
85+
86+
## Promotability
87+
88+
We have defined when it is desirable to promote expressions but have not yet
89+
defined which expressions can actually be promoted. We refer to such
90+
expressions as "promotable".
91+
92+
### Named locals
93+
94+
Promotable expressions cannot refer to named locals. This is not a technical
95+
limitation with the CTFE engine. While writing `let x = {expr}` outside of a
96+
const context, the user likely expects that `x` will live on the stack and be
97+
initialized at run-time. Although this is not (to my knowledge) guaranteed by
98+
the language, we do not wish to violate the user's expectations here.
99+
100+
### Single assignment
101+
102+
We only promote temporaries that are assigned to exactly once. For example, the
103+
lifetime of the temporary whose reference is assigned to `x` below will not be
104+
extended.
105+
106+
```rust
107+
let x: &'static i32 = &if cfg!(windows) { 0 } else { 1 };
108+
```
109+
110+
Once again, this is not a fundamental limitation in the CTFE engine; we are
111+
perfectly capable of evaluating such expressions at compile time. However,
112+
determining the promotability of complex expressions would require more
113+
resources for little benefit.
114+
115+
### Access to a `const` or `static`
116+
117+
Accesses to `const`s are always promotable, regardless of the body of the
118+
`const`. For instance, while the previous example was not legal, the
119+
following would be:
120+
121+
```rust
122+
const NOT_WINDOWS: i32 = if cfg!(windows) { 0 } else { 1 };
123+
let x: &'static i32 = &NOT_WINDOWS;
124+
```
125+
126+
However, an access to a `static` is only promotable within the initializer of
127+
another `static`.
22128

23129
### Panics
24130

0 commit comments

Comments
 (0)