Skip to content

Commit e9c3add

Browse files
authored
Merge pull request #31 from RalfJung/promotion
promotion tweaks
2 parents e1bd11d + 45d867e commit e9c3add

File tree

2 files changed

+88
-47
lines changed

2 files changed

+88
-47
lines changed

const.md

+5
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,11 @@ note the FIXME added [by this PR](https://github.com/rust-lang/rust/pull/63955):
6262
for untyped data in a constant, we currently just *make* it immutable, instead
6363
of checking properly.
6464

65+
Note that a constant *referring to* some already existing mutable memory is
66+
fine: inlining that reference everywhere has the same behavior as computing a
67+
new reference each time. In both cases, there exists exactly one instance of
68+
the mutable memory that everything references.
69+
6570
### 3. `Sync`
6671

6772
Finally, the same constant reference is actually shared across threads. This is

promotion.md

+83-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 a const context
4-
(e.g. initalizer of a `const` or `static`, or an array length expression) will
5-
be run at compile-time.
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.
66

77
## Promotion contexts
88

@@ -57,11 +57,14 @@ actually put on the stack. In this way, lifetime extension is an "implicit
5757
promotion context": the user did not ask for the value to be promoted.
5858

5959
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
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
6162
to be evaluated at compile-time even though they have not written it in a
6263
`const` declaration. We call this an "explicit promotion context".
6364

64-
Currently, non-`Copy` array initialization is treated as an implicit context.
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`).
6568

6669
The distinction between these two determines whether calls to arbitrary `const
6770
fn`s (those without `#[rustc_promotable]`) are promotable (see below). See
@@ -112,48 +115,10 @@ expressions are actually eligible for promotion? We refer to eligible
112115
expressions as "promotable" and describe the restrictions on such expressions
113116
below.
114117

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`.
118+
First of all, expressions have to be [allowed in constants](const.md). The
119+
restrictions described there are needed because we want `const` to behave the
120+
same as copying the `const` initializer everywhere the constant is used; we need
121+
the same property when promoting expressions. But we need more.
157122

158123
### Panics
159124

@@ -259,6 +224,77 @@ or `const` item and refer to that.
259224
*Dynamic check.* The Miri engine could dynamically check this by ensuring that
260225
the result of computing a promoted is a value that does not need dropping.
261226

227+
### Access to a `const` or `static`
228+
229+
When accessing a `const` in a promotable context, its value gets computed
230+
at compile-time anyway, so we do not have to check the initializer. However, the
231+
restrictions described above still apply for the *result* of the promoted
232+
computation: in particular, it must be a valid `const` (i.e., it cannot
233+
introduce interior mutability) and it must not require dropping.
234+
235+
For instance the following would be legal even though calls to `do_it` are not
236+
eligible for implicit promotion:
237+
238+
```rust
239+
const fn do_it(x: i32) -> i32 { 2*x }
240+
const ANSWER: i32 = {
241+
let ret = do_it(21);
242+
ret
243+
};
244+
245+
let x: &'static i32 = &ANSWER;
246+
```
247+
248+
An access to a `static` is only promotable within the initializer of another
249+
`static`. This is for the same reason that `const` initializers
250+
[cannot access statics](const.md#reading-statics).
251+
252+
Crucially, however, the following is *not* legal:
253+
254+
```rust
255+
const X: Cell<i32> = Cell::new(5); // ok
256+
const XREF: &Cell<i32> = &X; // not ok
257+
fn main() {
258+
let x: &'static _ = &X; // not ok
259+
}
260+
```
261+
262+
Just like allowing `XREF` would be a problem because, by the inlining semantics,
263+
every user of `XREF` should get their own `Cell`; it would also be a problem to
264+
promote here because if that code gets executed multiple times (e.g. inside a
265+
loop), it should get a new `Cell` each time.
266+
267+
### Named locals
268+
269+
Promotable expressions cannot refer to named locals. This is not a technical
270+
limitation with the CTFE engine. While writing `let x = {expr}` outside of a
271+
const context, the user likely expects that `x` will live on the stack and be
272+
initialized at run-time. Although this is not (to my knowledge) guaranteed by
273+
the language, we do not wish to violate the user's expectations here.
274+
275+
Note that constant-folding still applies: the optimizer may compute `x` at
276+
compile-time and even inline it everywhere if it can show that this does not
277+
observably alter program behavior. Promotion is very different from
278+
constant-folding as promotion can introduce observable differences in behavior
279+
(if const-evaluation fails) and as it is *guaranteed* to happen in some cases
280+
(and thus exploited by the borrow checker). This is reflected in the fact that
281+
promotion affects lifetimes, but constant folding does not.
282+
283+
### Single assignment
284+
285+
We only promote temporaries that are assigned to exactly once. For example, the
286+
lifetime of the temporary whose reference is assigned to `x` below will not be
287+
extended.
288+
289+
```rust
290+
let x: &'static i32 = &if cfg!(windows) { 0 } else { 1 };
291+
```
292+
293+
Once again, this is not a fundamental limitation in the CTFE engine; we are
294+
perfectly capable of evaluating such expressions at compile time. However,
295+
determining the promotability of complex expressions would require more
296+
resources for little benefit.
297+
262298
## Open questions
263299

264300
* There is a fourth kind of CTFE failure -- resource exhaustion. What do we do

0 commit comments

Comments
 (0)