Skip to content

Commit 752a055

Browse files
authored
Merge pull request #13 from RalfJung/const-refs
Consts cannot read from (mutable) statics
2 parents 9308f3a + aa4de5b commit 752a055

File tree

5 files changed

+85
-69
lines changed

5 files changed

+85
-69
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ which sort of is a virtual machine using `MIR` as "bytecode".
2121
## Table of Contents
2222

2323
* [Const Safety](const_safety.md)
24-
* [Constant References](const_refs.md)
24+
* [Constants](const.md)
2525
* [Promotion](promotion.md)
2626

2727
## Related RFCs

const.md

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
# Further restrictions for constants
2+
3+
The [const safety](const_safety.md) concerns apply to all computations happening
4+
at compile-time, `static` and `const` alike. However, there are some additional
5+
considerations about `const` specifically. These arise from the idea that
6+
```rust
7+
const CONST: T = EXPR;
8+
```
9+
is supposed to behave as-if `EXPR` was written at every use site of `CONST`.
10+
11+
## References
12+
13+
One issue is constants of reference type:
14+
```rust
15+
const REF: &u32 = &EXPR;
16+
```
17+
Instead of creating a new allocation for storing the result of `EXPR` on every
18+
use site, we just have a single global "static" allocation and every use of
19+
`REF` uses its address. It's as if we had written:
20+
```rust
21+
const REF: &u32 = { const _VAL = EXPR; static _STATIC = EXPR; &_STATIC };
22+
```
23+
(`EXPR` is assigned to a `const` first to make it subject to the restrictions
24+
discussed in this document.)
25+
26+
There are three reasons why this could be an issue.
27+
28+
### Pointer equality
29+
30+
We effectively "deduplicate" all the allocations that would otherwise locally be
31+
created at each use site of `REF`. This is observable when the program compares
32+
these pointers for equality. We consider this okay, i.e., programs may not rely
33+
on such constants all getting distinct addresses. They may not rely on them all
34+
getting the same address either.
35+
36+
### Interior mutability
37+
38+
If the reference has type `&Cell<i32>` it is quite clear that the program can
39+
easily observe whether two references point to the same memory even without
40+
comparing their address: Changes through one reference will affect reads through
41+
the other. So, we cannot allow constant references to types that have interior
42+
mutability (types that are not `Freeze`).
43+
44+
However, we can do better than that: Even if a *type* is not `Freeze`, it can
45+
have *values* that do not exhibit any interior mutability. For example, `&None`
46+
at type `&Option<Cell<i32>>` would be rejected by the naive analysis above, but
47+
is actually accepted by the compiler because we know that there is no
48+
`UnsafeCell` here that would permit interior mutability.
49+
50+
### `Sync`
51+
52+
Finally, the same constant reference is actually shared across threads. This is
53+
very similar to multiple threads having a shared reference to the same `static`,
54+
which is why `static` must be `Sync`. So it seems like we should reject
55+
non-`Sync` types, conforming with the desugaring described above.
56+
57+
However, this does not currently happen, and there are several crates across the
58+
ecosystem that would break if we just started enforcing this now. See
59+
[this issue](https://github.com/rust-lang/rust/issues/49206) and the
60+
[PR attempting to fix this](https://github.com/rust-lang/rust/pull/54424/).
61+
62+
## Reading statics
63+
64+
Beyond values of reference type, we have to be careful that *computing* a
65+
`const` cannot read from a static that could get mutated (because it is `static
66+
mut`, or because it has interior mutability). That would lead to the constant
67+
expression (being computed at compile-time) not having the same value any more
68+
when evaluated at run-time.
69+
70+
This is distinct to the concern about interior mutability above: That concern
71+
was about first computing a `&Cell<i32>` and then using it at run-time (and
72+
observing the fact that it has been "deduplicated"), this here is about using
73+
such a value at compile-time even though it might be changed at run-time.

const_refs.md

Lines changed: 0 additions & 52 deletions
This file was deleted.

const_safety.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@ four possible ways:
99
* The program causes undefined behavior (e.g., dereferencing an out-of-bounds
1010
pointer).
1111
* The program panics (e.g., a failed bounds check).
12-
* The program loops forever, and this is detected by the loop detector. Note
13-
that this detection happens on a best-effort basis only.
12+
* The program exhausts its resources: It might overflow the stack, allocation
13+
too much memory or loops forever. Note that detecting these conditions
14+
happens on a best-effort basis only.
1415

1516
Just like panics and non-termination are acceptable in safe run-time Rust code,
1617
we also consider these acceptable in safe compile-time Rust code. However, we

promotion.md

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -89,23 +89,17 @@ but to abort compilation of a program that would have compiled fine if we would
8989
not have decided to promote. It is the responsibility of `foo` to not fail this
9090
way when working with const-safe arguments.
9191

92-
### 3. Drop
92+
### 3. Constraints on constants
9393

94-
TODO: Fill this with information.
95-
96-
### 4. Reference constraints
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.
9797

98-
[Constant references](const_refs.md) impose some restrictions on the data they
99-
point to; the same restrictions apply to promoteds.
98+
### 4. Drop
10099

101-
### 5. Accessing statics
102-
103-
Since the promoted code is evaluated at compile-time, we must make sure that it
104-
does not access any mutable statics (including safe `static` with interior
105-
mutability), not even read from them. Their value could have changed at
106-
run-time, so we wouldn't be producing the correct result.
100+
TODO: Fill this with information.
107101

108102
## Open questions
109103

110-
* There is a fourth kind of CTFE failure -- and endless loop being detected.
111-
What do we do when that happens while evaluating a promoted?
104+
* There is a fourth kind of CTFE failure -- resource exhaustion. What do we do
105+
when that happens while evaluating a promoted?

0 commit comments

Comments
 (0)