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 ` :
414Instead of putting it on the stack, the ` 3 ` is allocated in global static memory
515and a reference with lifetime ` 'static ` is provided. This is essentially an
616automatic transformation turning ` &EXPR ` into
@@ -10,17 +20,142 @@ Note that promotion happens on the MIR, not on surface-level syntax. This is
1020relevant when discussing e.g. handling of panics caused by overflowing
1121arithmetic.
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, the initializer of an array expression, was
32+ introduced in [ RFC 2203] [ ] . Here, promotion allows arrays of
33+ non-` Copy ` types to be initialized idiomatically, for example
34+ ` [Option::<Box<i32>>::None; 32] ` .
35+
36+ [ RFC 2203 ] : https://github.com/rust-lang/rfcs/blob/master/text/2203-const-repeat-expr.md
37+
38+ ### ` #[rustc_args_required_const(...)] `
39+
40+ Additionally, some platform intrinsics require certain parameters to be
41+ immediates (known at compile-time). We use the ` #[rustc_args_required_const] `
42+ attribute, introduced in
43+ [ rust-lang/rust #48018 ] ( https://github.com/rust-lang/rust/pull/48018 ) , to
44+ specify these parameters and (aggressively, see below) try to promote the
45+ corresponding arguments.
46+
47+ ### Implicit and explicit contexts
48+
1349On 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* .
1450Thus, if CTFE fails but the code would have worked fine at run-time, we broke the user's code for no good reason.
1551Even 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 ] .
1652That's why we have to be very conservative with what can and cannot be promoted.
1753
18- [ promotion-rfc ] : https://github.com/rust-lang/rfcs/blob/master/text/1414-rvalue_static_promotion.md
54+ For example, users might be surprised to learn that whenever they take a
55+ reference to a temporary, that temporary may be promoted away and never
56+ actually put on the stack. In this way, lifetime extension is an "implicit
57+ promotion context": the user did not ask for the value to be promoted.
58+
59+ On the other hand, when a user passes an expression to a function with
60+ ` #[rustc_args_required_const] ` , they are explicitly asking for that expression
61+ to be evaluated at compile-time even though they have not written it in a
62+ ` const ` declaration. We call this an "explicit promotion context".
63+
64+ Currently, non-` Copy ` array initialization is treated as an implicit context.
65+
66+ The distinction between these two determines whether calls to arbitrary `const
67+ fn` s (those without ` #[ rustc_promotable] `) are promotable (see below). See
68+ [ rust-rfcs/const-eval #19 ] ( https://github.com/rust-rfcs/const-eval/issues/19 )
69+ for a thorough discussion of this. At present, this is the only difference
70+ between implicit and explicit contexts. The requirements for promotion in an
71+ implicit context are a superset of the ones in an explicit context.
72+
1973[ warn-rfc ] : https://github.com/rust-lang/rfcs/blob/master/text/1229-compile-time-asserts.md
2074
21- ## Rules
75+ ### Promotion contexts inside ` const ` and ` static `
76+
77+ Lifetime extension is also responsible for making code like this work:
78+
79+ ``` rust
80+ const FOO : & 'static i32 = {
81+ let x = & 13 ;
82+ x
83+ };
84+ ```
85+
86+ We defined above that promotion guarantees that code in a non-const context
87+ will be executed at compile-time. The above example illustrates that lifetime
88+ extension and non-` Copy ` array initialization are useful features * inside*
89+ ` const ` s and ` static ` s as well. Strictly speaking, the transformation used to
90+ enable these features inside a const-context is not promotion; no ` promoted ` s
91+ are created in the MIR. However the same rules for promotability are used with
92+ one modification: Because the user has already requested that this code run at
93+ compile time, all contexts are treated as explicit.
94+
95+ Notice that some code involving ` & ` * looks* like it relies on lifetime
96+ extension but actually does not:
97+
98+ ``` rust
99+ const EMPTY_BYTES : & Vec <u8 > = & Vec :: new (); // Ok without lifetime extension
100+ ```
101+
102+ As we have seen above, ` Vec::new() ` does not get promoted. And yet this
103+ compiles. Why that? The reason is that the reference obtains the lifetime of
104+ the "enclosing scope", similar to how ` let x = &mut x; ` creates a reference
105+ whose lifetime lasts for the enclosing scope. This is decided during MIR
106+ building already, and does not involve lifetime extension.
107+
108+ ## Promotability
109+
110+ We have described the circumstances where promotion is desirable, but what
111+ expressions are actually eligible for promotion? We refer to eligible
112+ expressions as "promotable" and describe the restrictions on such expressions
113+ below.
114+
115+ ### Named locals
116+
117+ Promotable expressions cannot refer to named locals. This is not a technical
118+ limitation with the CTFE engine. While writing ` let x = {expr} ` outside of a
119+ const context, the user likely expects that ` x ` will live on the stack and be
120+ initialized at run-time. Although this is not (to my knowledge) guaranteed by
121+ the language, we do not wish to violate the user's expectations here.
122+
123+ ### Single assignment
124+
125+ We only promote temporaries that are assigned to exactly once. For example, the
126+ lifetime of the temporary whose reference is assigned to ` x ` below will not be
127+ extended.
128+
129+ ``` rust
130+ let x : & 'static i32 = & if cfg! (windows ) { 0 } else { 1 };
131+ ```
132+
133+ Once again, this is not a fundamental limitation in the CTFE engine; we are
134+ perfectly capable of evaluating such expressions at compile time. However,
135+ determining the promotability of complex expressions would require more
136+ resources for little benefit.
137+
138+ ### Access to a ` const ` or ` static `
139+
140+ When accessing a ` const ` in a promotable context, the restrictions on single
141+ assignment and named locals do not apply to the body of the ` const ` . All other
142+ restrictions, notably that the result of the ` const ` cannot be ` Drop ` or mutable
143+ through a reference still apply. For instance, while the previous example was
144+ not legal, the following would be:
145+
146+ ``` rust
147+ const BOOL : i32 = {
148+ let ret = if cfg! (windows ) { 0 } else { 1 };
149+ ret
150+ };
151+
152+ let x : & 'static i32 = & BOOL ;
153+ ```
154+
155+ An access to a ` static ` is only promotable within the initializer of
156+ another ` static ` .
22157
23- ### 1. Panics
158+ ### Panics
24159
25160Promotion is not allowed to throw away side effects. This includes panicking.
26161Let us look at what happens when we promote ` &(0_usize - 1) ` in a debug build:
@@ -56,7 +191,7 @@ could not panic!) at run-time leads to a compile-time CTFE error.
56191* Dynamic check.* The Miri engine already dynamically detects panics, but the
57192main point of promoteds is ruling them out statically.
58193
59- ### 2. Const safety
194+ ### Const safety
60195
61196We have explained what happens when evaluating a promoted panics, but what about
62197other kinds of failure -- what about hitting an unsupported operation or
@@ -105,7 +240,7 @@ For this reason, only `const fn` that were explicitly marked with the
105240* Dynamic check.* The Miri engine already dynamically detects const safety
106241violations, but the main point of promoteds is ruling them out statically.
107242
108- ### 3. Drop
243+ ### Drop
109244
110245Expressions returning "needs drop" types can never be promoted. If such an
111246expression were promoted, the ` Drop ` impl would never get called on the value,
@@ -124,52 +259,6 @@ or `const` item and refer to that.
124259* Dynamic check.* The Miri engine could dynamically check this by ensuring that
125260the result of computing a promoted is a value that does not need dropping.
126261
127- ## ` & ` in ` const ` and ` static `
128-
129- Promotion is also responsible for making code like this work:
130-
131- ``` rust
132- const FOO : & 'static i32 = {
133- let x = & 13 ;
134- x
135- };
136- ```
137-
138- However, since this is in explicit const context, we are less strict about
139- promotion in this situation: all function calls are promoted, not just
140- ` #[rustc_promotable] ` functions:
141-
142- ``` rust
143- const fn bar () -> i32 { 42 }
144-
145- const FOO : & 'static i32 = {
146- let x = & bar (); // this gets promoted
147- x
148- };
149- ```
150-
151- However, we still do not promote * everything* ; e.g., drop-checking still applies:
152-
153- ``` rust
154- const DROP : & 'static Vec <u8 > = {
155- let x = & Vec :: new (); // ~ ERROR: temporary value dropped while borrowed
156- x
157- };
158- ```
159-
160- Notice that some code involving ` & ` * looks* like it relies on promotion but
161- actually does not:
162-
163- ``` rust
164- const EMPTY_BYTES : & Vec <u8 > = & Vec :: new (); // Ok without promotion
165- ```
166-
167- As we have seen above, ` Vec::new() ` does not get promoted. And yet this
168- compiles. Why that? The reason is that the reference obtains the lifetime of
169- the "enclosing scope", similar to how ` let x = &mut x; ` creates a reference
170- whose lifetime lasts for the enclosing scope. This is decided during MIR
171- building already, and does not involve promotion.
172-
173262## Open questions
174263
175264* There is a fourth kind of CTFE failure -- resource exhaustion. What do we do
0 commit comments