|
1 | 1 | # Const promotion
|
2 | 2 |
|
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. |
8 | 18 |
|
9 | 19 | Note that promotion happens on the MIR, not on surface-level syntax. This is
|
10 | 20 | relevant when discussing e.g. handling of panics caused by overflowing
|
11 | 21 | arithmetic.
|
12 | 22 |
|
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`. |
14 | 121 |
|
15 | 122 | ### Panics
|
16 | 123 |
|
|
0 commit comments