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+ [ "(Implicit) Promotion"] [ rfc ] is a mechanism that affects code like ` &3 ` :
4+ Instead of putting it on the stack, the ` 3 ` is allocated in global static memory
5+ and a reference with lifetime ` 'static ` is provided. This is essentially an
6+ automatic transformation turning ` &EXPR ` into `{ const _ PROMOTED = &EXPR; EXPR
7+ } ` , but only if ` EXPR` qualifies.
88
99Note that promotion happens on the MIR, not on surface-level syntax. This is
1010relevant when discussing e.g. handling of panics caused by overflowing
1111arithmetic.
1212
13+ 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* .
14+ Thus, if CTFE fails but the code would have worked fine at run-time, we broke the user's code for no good reason.
15+ That's why we have to be very conservative with what can and cannot be promoted.
16+
17+ [ rfc ] : https://github.com/rust-lang/rfcs/blob/master/text/1414-rvalue_static_promotion.md
18+
1319## Rules
1420
1521### 1. Panics
1622
1723Promotion is not allowed to throw away side effects. This includes panicking.
1824Let us look at what happens when we promote ` &(0_usize - 1) ` in a debug build:
1925We have to avoid erroring at compile-time, because that would be promotion
20- breaking compilation (the code would have compiled just fine if we hadn't
21- promoted), but we must be sure to error correctly at run-time. In the MIR, this
22- looks roughly like
26+ breaking compilation, but we must be sure to error correctly at run-time. In
27+ the MIR, this looks roughly like
2328
2429```
2530_tmp1 = CheckedSub (const 0usize) (const 1usize)
@@ -89,18 +94,12 @@ but to abort compilation of a program that would have compiled fine if we would
8994not have decided to promote. It is the responsibility of ` foo ` to not fail this
9095way when working with const-safe arguments.
9196
92- ### 3. Constraints on constants
93-
94- All the [ extra restrictions for constants] ( const.md ) beyond const safety also
95- apply to promoteds, for the same reason: Evaluating the expression at
96- compile-time instead of run-time should not alter program behavior.
97-
98- ### 4. Drop
97+ ### 3. Drop
9998
100- Expressions containing "needs drop" types
101- can never be promoted. If such an expression were promoted, the ` Drop ` impl would
102- never get called on the value, even though the user did not explicitly request such
103- behavior by using an explicit ` const ` or ` static ` item.
99+ Expressions returning "needs drop" types can never be promoted. If such an
100+ expression were promoted, the ` Drop ` impl would never get called on the value,
101+ even though the user did not explicitly request such behavior by using an
102+ explicit ` const ` or ` static ` item.
104103
105104As expression promotion is essentially the silent insertion of a ` static ` item, and
106105` static ` items never have their ` Drop ` impl called, the ` Drop ` impl of the promoted
@@ -111,6 +110,38 @@ it is unlikely to be the desired behavior in most cases and very likey to be con
111110to the user. If such behavior is desired, the user can still use an explicit ` static `
112111or ` const ` item and refer to that.
113112
113+ ## ` & ` in ` const ` and ` static `
114+
115+ Promotion is also responsible for making code like this work:
116+
117+ ``` rust
118+ const FOO : & 'static i32 = {
119+ let x = & 13 ;
120+ x
121+ };
122+ ```
123+
124+ However, since this is in explicit const context, we could be less strict about
125+ promotion in this situation.
126+
127+ Promotion is * not* involved in something like this:
128+
129+ ``` rust
130+ #![feature(const_vec_new)]
131+ const EMPTY_BYTES : & Vec <u8 > = & Vec :: new ();
132+
133+ const NESTED : & 'static Vec <u8 > = {
134+ // This does not work when we have an inner scope:
135+ let x = & Vec :: new (); // ~ ERROR: temporary value dropped while borrowed
136+ x
137+ };
138+ ```
139+
140+ In ` EMPTY_BYTES ` , the reference obtains the lifetime of the "enclosing scope",
141+ similar to how ` let x = &mut x; ` creates a reference whose lifetime lasts for
142+ the enclosing scope. This is decided during MIR building already, and does not
143+ involve promotion.
144+
114145## Open questions
115146
116147* There is a fourth kind of CTFE failure -- resource exhaustion. What do we do
0 commit comments