Skip to content

Commit 6ca6f59

Browse files
committed
update promotion docs
1 parent 213b96f commit 6ca6f59

File tree

1 file changed

+44
-47
lines changed

1 file changed

+44
-47
lines changed

promotion.md

+44-47
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
# Const promotion
22

3-
"Promotion" is the act of guaranteeing that code *not* written in an (explicit)
4-
const context will be run at compile-time. Explicit const contexts include the
5-
initializer of a `const` or `static`, or an array length expression.
3+
"Promotion" is the act of splicing a part of a MIR computation out into a
4+
separate self-contained MIR body which is evaluated at compile-time like a
5+
constant.
66

77
## Promotion contexts
88

@@ -35,7 +35,7 @@ non-`Copy` types to be initialized idiomatically, for example
3535

3636
[RFC 2203]: https://github.com/rust-lang/rfcs/blob/master/text/2203-const-repeat-expr.md
3737

38-
### `#[rustc_args_required_const(...)]`
38+
### `#[rustc_args_required_const(...)]` and inline assembly `const` operands
3939

4040
Additionally, some platform intrinsics require certain parameters to be
4141
immediates (known at compile-time). We use the `#[rustc_args_required_const]`
@@ -44,59 +44,46 @@ attribute, introduced in
4444
specify these parameters and (aggressively, see below) try to promote the
4545
corresponding arguments.
4646

47-
### Implicit and explicit contexts
47+
Similarly, inline assembly has `const` operands, which are treated the same way
48+
as `rustc_args_required_const` arguments.
49+
50+
## Implicit and explicit promotion
4851

4952
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*.
5053
Thus, if CTFE fails but the code would have worked fine at run-time, we broke the user's code for no good reason.
5154
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].
52-
That's why we have to be very conservative with what can and cannot be promoted.
55+
We call this *implicit* promotion, and we have to be very conservative with what can and cannot be implicitly promoted.
5356

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.
57+
CTFE of implicitly promoted code must never fail to evaluate except if the
58+
run-time code also would have failed. This means we cannot permit calling
59+
arbitrary `const fn`, as discussed in detail in
60+
[rust-lang/const-eval#19](https://github.com/rust-lang/const-eval/issues/19).
61+
Thus, only functions marked `#[rustc_promotable]` are implicitly promotable (see
62+
below).
5863

5964
On the other hand, when a user passes an expression to a function with
60-
`#[rustc_args_required_const]`, the only way for this code to compile is to promote it.
61-
In that sense, the user is explicitly asking for that expression
62-
to be evaluated at compile-time even though they have not written it in a
63-
`const` declaration. We call this an "explicit promotion context".
64-
65-
Currently, non-`Copy` array initialization is treated as an implicit context,
66-
because the code could compile even without promotion (namely, if the result
67-
type is `Copy`).
65+
`#[rustc_args_required_const]`, the only way for this code to compile is to
66+
promote it. In that sense, the user is explicitly asking for that expression to
67+
be evaluated at compile-time even though they have not written it in a `const`
68+
declaration. We can thus be less conservative. This is called *explicit*
69+
promotion.
6870

69-
CTFE of implicitly promoted code must never fail to evaluate except if the
70-
run-time code also would have failed. This means we cannot permit calling
71-
arbitrary `const fn`, as we cannot predict if they are going to perform an
72-
["unconst" operation](const_safety.md). Thus, only functions marked
73-
`#[rustc_promotable]` are implicitly promotable (see below). See
74-
[rust-lang/const-eval#19](https://github.com/rust-lang/const-eval/issues/19) for
75-
a thorough discussion of this. At present, this is the only difference between
76-
implicit and explicit contexts. The requirements for promotion in an implicit
77-
context are a superset of the ones in an explicit context.
71+
Currently, the following are considered explicit promotion contexts:
72+
* `#[rustc_args_required_const]` arguments and inline assembly `const` operands everywhere.
73+
* Everything inside the bodies of `const` and `static` items. (Note: this is handled separately from "explicit contexts" in promotion analysis, but the effect is the same.)
7874

79-
[warn-rfc]: https://github.com/rust-lang/rfcs/blob/master/text/1229-compile-time-asserts.md
75+
In these contexts, we promote calls to arbitrary `const fn`.
8076

81-
### Promotion contexts inside `const` and `static`
77+
There is one further special case for the bodies of `const` and `static` items; here we additionally promote union field accesses.
78+
Both of these special cases can lead to promoting things that can fail to evaluate.
79+
Currently, this works out because it just leads to a warning, but longer-term it would be desirable to turn evaluation failures into hard errors, which for these promoteds means we have to guarantee that we only evaluate them on-demand.
8280

83-
Lifetime extension is also responsible for making code like this work:
81+
[See below][static access] for another special case in promotion analysis:
82+
accesses and references to statics are only promoted inside other statics.
8483

85-
```rust
86-
const FOO: &'static i32 = {
87-
let x = &13;
88-
x
89-
};
90-
```
84+
[warn-rfc]: https://github.com/rust-lang/rfcs/blob/master/text/1229-compile-time-asserts.md
9185

92-
We defined above that promotion guarantees that code in a non-const context
93-
will be executed at compile-time. The above example illustrates that lifetime
94-
extension and non-`Copy` array initialization are useful features *inside*
95-
`const`s and `static`s as well. Strictly speaking, the transformation used to
96-
enable these features inside a const-context is not promotion; no `promoted`s
97-
are created in the MIR. However the same rules for promotability are used with
98-
one modification: Because the user has already requested that this code run at
99-
compile time, all contexts are treated as explicit.
86+
## "enclosing scope" rule
10087

10188
Notice that some code involving `&` *looks* like it relies on lifetime
10289
extension but actually does not:
@@ -105,12 +92,20 @@ extension but actually does not:
10592
const EMPTY_BYTES: &Vec<u8> = &Vec::new(); // Ok without lifetime extension
10693
```
10794

108-
As we have seen above, `Vec::new()` does not get promoted. And yet this
95+
`Vec::new()` cannot get promoted because it needs dropping. And yet this
10996
compiles. Why that? The reason is that the reference obtains the lifetime of
11097
the "enclosing scope", similar to how `let x = &mut x;` creates a reference
11198
whose lifetime lasts for the enclosing scope. This is decided during MIR
11299
building already, and does not involve lifetime extension.
113100

101+
In contrast, this does not compile:
102+
103+
```rust
104+
const OPT_EMPTY_BYTES: Option<&Vec<u8>> = Some(&Vec::new());
105+
```
106+
107+
The "enclosing scope" rule only fires for outermost `&`, just like in `fn` bodies.
108+
114109
## Promotability
115110

116111
We have described the circumstances where promotion is desirable, but what
@@ -230,6 +225,7 @@ or `const` item and refer to that.
230225
the result of computing a promoted is a value that does not need dropping.
231226

232227
### Access to a `const` or `static`
228+
[access-static]: #access-to-a-const-or-static
233229

234230
When accessing a `const` in a promotable context, its value gets computed
235231
at compile-time anyway, so we do not have to check the initializer. However, the
@@ -250,8 +246,9 @@ const ANSWER: i32 = {
250246
let x: &'static i32 = &ANSWER;
251247
```
252248

253-
An access to a `static` is only promotable within the initializer of another
254-
`static`. This is for the same reason that `const` initializers
249+
An access to a `static`, including just taking references to a `static`, is only
250+
promotable within the initializer of another `static`. This is for the same
251+
reason that `const` initializers
255252
[cannot access statics](const.md#reading-statics).
256253

257254
Crucially, however, the following is *not* legal:

0 commit comments

Comments
 (0)