Skip to content

Commit 266a7ad

Browse files
committed
add document on consts in patterns
1 parent 9924d2c commit 266a7ad

File tree

2 files changed

+64
-0
lines changed

2 files changed

+64
-0
lines changed

const.md

+2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ To make this work, we need to ensure [const safety](const_safety.md).
1212
Based on this requirement, we allow other constants and [promoteds](promotion.md) to read from constants.
1313
This is why the value of a `const` is subject to validity checks.
1414

15+
Constants can also be [used in patterns](patterns.md), which brings its own soundness concerns.
16+
1517
## References
1618

1719
One issue is constants of reference type:

patterns.md

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# Constants in patterns
2+
3+
Constants that pass the [structural equality check](https://github.com/rust-lang/rust/issues/74446) can be used in patterns:
4+
```rust
5+
const FOO: i32 = 13;
6+
7+
fn is_foo(x: i32) -> bool {
8+
match x {
9+
FOO => true,
10+
_ => false,
11+
}
12+
}
13+
```
14+
However, that check has some loopholes, so e.g. `&T` can be used in a pattern no matter the `T`.
15+
16+
A const-pattern is compiled by calling `PartialEq::eq` to compare the subject of the `match` with the constant,
17+
except for TODO where the constant is treated as if it was inlined as a pattern (and the usual `match` tree is constructed).
18+
19+
## Soundness concerns
20+
21+
Most of the time there are no extra soundness concerns due to const-patterns; except for surprising behavior nothing can go wrong when the structural equality check gives a wrong answer.
22+
However, there is one exception: some constants participate in exhaustiveness checking.
23+
If a pattern is incorrectly considered exhaustive, that leads to a critical soundness bug.
24+
25+
Exhaustiveness checking is done for constants of type TODO, as well as `&[u8]` and `&str` (but no other types with references).
26+
This means we can write:
27+
```rust
28+
#![feature(exclusive_range_pattern)]
29+
#![feature(half_open_range_patterns)]
30+
const PAT: &[u8] = &[0];
31+
32+
pub fn test(x: &[u8]) -> bool {
33+
match x {
34+
PAT => true,
35+
&[] => false,
36+
&[1..] => false,
37+
&[_, _, ..] => false
38+
}
39+
}
40+
```
41+
42+
To ensure soundness of exhaustiveness checking, it is crucial that all data considered this check is fully immutable.
43+
In particular, for constants of reference type, it is important that they only point to immutable data.
44+
For this reason, the static const checks reject references to `static` items.
45+
This is a new soundness concern that otherwise does not come up during CTFE (where *reading from* a mutable `static` during const initialization is a problem, but just referencing a mutable `static` is not).
46+
A more precise check could be possible, but is non-trivial: even an immutable `static` could point to a mutable `static`; that would have to be excluded.
47+
(We could, in principle, also rely on it being UB to have a shared reference to mutable memory, but for now we prefer not to rely on the aliasing model like that---aliasing of global pointers is a tricky subject.)
48+
49+
*Dynamic check.*
50+
To dynamically ensure constants only point to read-only data, we maintain a global invariant:
51+
pointers to a `static` (including memory that was created as part of interning a `static`) may never "leak" to a non-`static`.
52+
All memory outside of `static`s is marked as immutable.
53+
As a consequence, non-`static`s recursively only point to immutable memory.
54+
55+
This check is uncomfortably close to the static checks, and fragile due to its reliance on a global invariant.
56+
Longer-term, it would be better to move to a more local check, by making validation check if consts are recursively read-only.
57+
This check (unlike our current validation) would have to descend through pointers to `static`.
58+
59+
An alternative could be to wait for the [value tree proposal](https://github.com/rust-lang/compiler-team/issues/323) to be implemented.
60+
Then we could check during value tree construction that all memory is read-only (if we use the CTFE machine in `const`-mode, that will happen implicitly), and thus we know for sure we can rely on the value tree to be sound to use for exhaustiveness checking.
61+
How exactly this looks like depends on how we resolve [pattern matching allowing many things that do not fit a value tree](https://github.com/rust-lang/rust/issues/74446#issuecomment-663439899).
62+
However, if only the well-behaved part of the value tree is used for exhaustiveness checking, handling these extra patterns should not interact closely with the soundness concerns discussed here.

0 commit comments

Comments
 (0)