├── src
├── SUMMARY.md
└── skill_tree.md
├── skill-tree-finetuning.css
├── .gitignore
├── book.toml
├── LICENSE-MIT
├── .github
└── workflows
│ └── deploy_skill_tree.yml
├── static.md
├── const_checks.md
├── README.md
├── patterns.md
├── CODE_OF_CONDUCT.md
├── const.md
├── const_safety.md
├── LICENSE-APACHE
└── promotion.md
/src/SUMMARY.md:
--------------------------------------------------------------------------------
1 | # Summary
2 |
3 | - [Feature Skill Tree](./skill_tree.md)
4 |
--------------------------------------------------------------------------------
/skill-tree-finetuning.css:
--------------------------------------------------------------------------------
1 |
2 | :root {
3 | --content-max-width: 2000px;
4 | }
5 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *~
2 | book
3 | full.render.js
4 | skill-tree.js
5 | viz.js
6 | skill-tree.css
7 |
--------------------------------------------------------------------------------
/book.toml:
--------------------------------------------------------------------------------
1 | [book]
2 | authors = ["Oliver Scherer"]
3 | language = "en"
4 | multilingual = false
5 | src = "src"
6 | title = "const-eval"
7 | [preprocessor.skill-tree]
8 | command = "mdbook-skill-tree"
9 | [output.html]
10 | additional-js =["viz.js", "full.render.js", "skill-tree.js"]
11 | additional-css =["skill-tree.css", "skill-tree-finetuning.css"]
12 |
--------------------------------------------------------------------------------
/LICENSE-MIT:
--------------------------------------------------------------------------------
1 | Permission is hereby granted, free of charge, to any
2 | person obtaining a copy of this software and associated
3 | documentation files (the "Software"), to deal in the
4 | Software without restriction, including without
5 | limitation the rights to use, copy, modify, merge,
6 | publish, distribute, sublicense, and/or sell copies of
7 | the Software, and to permit persons to whom the Software
8 | is furnished to do so, subject to the following
9 | conditions:
10 |
11 | The above copyright notice and this permission notice
12 | shall be included in all copies or substantial portions
13 | of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
17 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
18 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
19 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
22 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23 | DEALINGS IN THE SOFTWARE.
24 |
--------------------------------------------------------------------------------
/.github/workflows/deploy_skill_tree.yml:
--------------------------------------------------------------------------------
1 | name: github pages
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 |
8 | jobs:
9 | deploy:
10 | runs-on: ubuntu-18.04
11 | steps:
12 | - uses: actions/checkout@v2
13 |
14 | - name: Setup mdBook
15 | uses: peaceiris/actions-mdbook@v1
16 | with:
17 | mdbook-version: '0.4.5'
18 | # mdbook-version: 'latest'
19 |
20 | - name: Cache multiple paths
21 | uses: actions/cache@v2
22 | with:
23 | path: |
24 | ~/.cargo/bin
25 | key: ${{ runner.os }}
26 |
27 | - name: Install mdbook-skill-tree
28 | run: cargo install --git https://github.com/nikomatsakis/skill-tree.git mdbook-skill-tree
29 | continue-on-error: true # we don't care if this is already cached
30 |
31 | - name: Install skill tree files
32 | run: mdbook-skill-tree install
33 |
34 | - run: mdbook build
35 |
36 | - name: Deploy
37 | uses: peaceiris/actions-gh-pages@v3
38 | with:
39 | github_token: ${{ secrets.GITHUB_TOKEN }}
40 | publish_dir: ./book
41 |
--------------------------------------------------------------------------------
/static.md:
--------------------------------------------------------------------------------
1 | # Statics
2 |
3 | Statics (`static`, `static mut`) are the simplest kind of compile-time evaluated data:
4 | * The user explicitly requested them to be evaluated at compile-time,
5 | so evaluation errors from computing the initial value of a static are no concern
6 | (in other words, [const safety](const_safety.md) is mostly not an issue).
7 | * They observably get evaluated *once*, with the result being put at some address known at run-time,
8 | so there are no fundamental restrictions on what statics can do.
9 | * The compiler checks that statics are `Sync`, justifying sharing their address across threads.
10 | * [Constants](const.md) and [promoteds](promotion.md) are not allowed to read from statics,
11 | so their final value does not have have to be [const-valid](const_safety.md#const-safety-check-on-values) in any meaningful way.
12 | As of 2019-08, we do check them for validity anyway, to be conservative; and indeed constants could be allowed to read from frozen statics.
13 |
14 | ## `Drop`
15 |
16 | The compiler rejects intermediate values (created and discarded during the computation of a static initializer) that implement `Drop`.
17 | The reason for this is simply that the `Drop` implementation might be non-`const fn`
18 | (so really, this is just a special case of static initializers not being able to call non-`const` functions).
19 | This restriction can be lifted once `const impl Drop for Type` (or something similar) is supported.
20 |
21 | ```rust
22 | struct Foo;
23 |
24 | impl Drop for Foo {
25 | fn drop(&mut self) {
26 | println!("foo dropped");
27 | }
28 | }
29 |
30 | static FOOO: Foo = Foo; // Ok, drop is never run
31 |
32 | // Not ok, cannot run `Foo::drop` because it's not a const fn
33 | static BAR: i32 = (Foo, 42).1;
34 | ```
35 |
36 | *Dynamic check.* The Miri engine dynamically checks that this is done correctly
37 | by not permitting calls of non-`const` functions.
38 |
--------------------------------------------------------------------------------
/const_checks.md:
--------------------------------------------------------------------------------
1 | # Static and dynamic checks
2 |
3 | This repository describes a set of rules that various forms of compile-time evaluated code needs to satisfy.
4 | The compiler contains two kinds of checks that guard against violations of these rules: static checks and dynamic checks.
5 |
6 | ## Dynamic checks
7 |
8 | Dynamic checks are conceptually very simple: when evaluating the compile-time code, we look at what it does, and if what it does violates the rules, we halt evaluation.
9 | Thus, a dynamic check generally makes it very clear what is being protected against.
10 |
11 | The main disadvantage of dynamic checks is that they can only run when the compile-time code is being evaluated, which is after monomorphization.
12 | We generally try to avoid post-monomorphization errors as they inherently make for a bad user experience.
13 | While there are technical aspects that could be improved here, the main problem is that the site where the error is reported is disconnected from the site where the root cause is.
14 | Such problems can be observed when creating an associated constant that uses associated constants from generic parameters.
15 | These generic parameters are unknown, so the usage of these associated constants may cause errors depending on the *value* of the generic parameter's associated constants.
16 |
17 | [Promotion analysis](promotion.md) also makes little sense dynamically as it is about code transformation.
18 | All we can do is check after the transformation if the generated code makes sense.
19 |
20 | ## Static checks
21 |
22 | Static checks work by "looking" at the code.
23 | (This is "static" as in [static analysis](https://en.wikipedia.org/wiki/Static_analysis), not to be confused with Rust's `static` keyword.)
24 | The idea is to predict what the code will do without actually executing it.
25 | That means we can even analyze code that we cannot run pre-monomorphization.
26 | However, static checks are necessarily less precise than dynamic checks (this is the famous halting problem), which means they will reject code that the dynamic checks would accept.
27 |
28 | The key property is that the static check is *sound*, which means that everything accepted by the static checks will also be accepted by the dynamic checks.
29 | It might seem like this makes the dynamic checks unnecessary, but they are still tremendously useful.
30 | On the one hand, the dynamic checks serve as a safety net for when we have bugs in the static checks (dynamic checks are much easier to get right, typically).
31 | On the other hand, having the dynamic checks forces us through the helpful exercise of figuring out what the dynamic property we are enforcing actually *is*, which is crucial when we want to adjust the static check to be more precise.
32 | The dynamic check serves as the "ground truth" that the static check approximates, and we can improve that approximation over time.
33 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Constant Evaluation
2 |
3 | Constant evaluation is the process of running Rust code at compile-time
4 | in order to use the result e.g. to set the discriminant of enum variants
5 | or as an array length.
6 |
7 | Examples:
8 |
9 | ```rust
10 | enum Foo {
11 | A = 5,
12 | B = 7 - 3,
13 | }
14 | type NineStrings = [&str, 3 * 3];
15 | ```
16 |
17 | The Rust compiler runs the [MIR](https://rustc-dev-guide.rust-lang.org/mir/index.html)
18 | in the [`MIR` interpreter (miri)](https://rustc-dev-guide.rust-lang.org/const-eval),
19 | which sort of is a virtual machine using `MIR` as "bytecode".
20 |
21 | ## Table of Contents
22 |
23 | * [Unstable Feature Skill Tree](https://rust-lang.github.io/const-eval/)
24 | * [Const Safety](const_safety.md)
25 | * [Static and dynamic checks](const_checks.md)
26 | * The three "kinds" of compile-time evaluated data:
27 | * [Statics](static.md) (`static`, `static mut`)
28 | * [Constants](const.md) (`const`, array sizes, non-`Copy` array initializers)
29 | * [Promoteds](promotion.md) (rvalue promotion)
30 |
31 | ## Related RFCs
32 |
33 | ### Const Promotion
34 |
35 | [RFC 1414](https://github.com/rust-lang/rfcs/pull/1414) injects a hidden static for any
36 | `&foo` expression as long as `foo` follows a certain set of rules.
37 | These rules are discussed [here](promotion.md)
38 |
39 | ### Drop types in constants/statics
40 |
41 | [RFC 1440](https://github.com/rust-lang/rfcs/pull/1440) allows using types that implement
42 | `Drop` (or have fields that do so) inside constants and statics. It is guaranteed that the
43 | `Drop::drop` method will never be called on the static/const object.
44 |
45 | ### Constants in Repeat expressions
46 |
47 | [RFC 2203](https://github.com/rust-lang/rfcs/pull/2203) allows the use `!Copy` types for the
48 | initializer of repeat expressions, as long as that value is constant.
49 |
50 | This permits e.g. `[Vec::new(); 42]`.
51 |
52 | ### Statically known bugs and panics in runtime code are warnings
53 |
54 | [RFC 1229](https://github.com/rust-lang/rfcs/pull/1229) formalized the concept that
55 |
56 | ```rust
57 | let x: usize = 1 - 2;
58 | ```
59 |
60 | is allowed to produce a `lint` but not an `error`. This allows us to make these analyses
61 | more powerful without suddenly breaking compilation. The corresponding lint is the `const_err`
62 | lint, which is `deny` by default and will thus break compilation of the crate currently being built,
63 | even if it does not break the compilation of the current crate's dependencies.
64 |
65 | ### Various new const-eval features
66 |
67 | * [`loop`](https://github.com/rust-lang/rfcs/pull/2344)
68 | * [`if` and `match`](https://github.com/rust-lang/rfcs/pull/2342)
69 | * [`panic!`](https://github.com/rust-lang/rfcs/pull/2345)
70 | * [`locals and destructuring`](https://github.com/rust-lang/rfcs/pull/2341)
71 |
72 | Some of these features interact. E.g.
73 |
74 | * `match` + `loop` yields `while`
75 | * `panic!` + `if` + `locals` yields `assert!`
76 |
--------------------------------------------------------------------------------
/patterns.md:
--------------------------------------------------------------------------------
1 | # Constants in patterns
2 |
3 | Constants that pass the [structural equality check][struct-eq] 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 | The way this works is that the constant is converted to a [THIR pattern](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_build/thir/pattern/enum.PatKind.html).
17 | This proceeds recursively through fields of the constant until we hit a reference.
18 | Then, `&[u8]` and `&str` are treated specially, while everything else becomes just a `PatKind::Constant`.
19 |
20 | For further compilation, `Constant` are translated to [`PartialEq::eq`][compile-partial-eq], whereas everything else is translated as if the constant was inlined as a pattern.
21 | [RFC 1445](https://github.com/rust-lang/rfcs/blob/master/text/1445-restrict-constants-in-patterns.md) lays the groundwork for changing this in the future to always using `PartialEq::eq`, but no decision either way has been made yet.
22 |
23 | [struct-eq]: https://github.com/rust-lang/rust/blob/2c28244cf0fc9868f55070e55b8f332d196eaf3f/src/librustc_mir_build/hair/pattern/const_to_pat.rs#L121
24 | [compile-partial-eq]: https://github.com/rust-lang/rust/blob/2c28244cf0fc9868f55070e55b8f332d196eaf3f/src/librustc_mir_build/build/matches/test.rs#L355
25 |
26 | ## Soundness concerns
27 |
28 | 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.
29 | However, there is one exception: some constants participate in exhaustiveness checking.
30 | If a pattern is incorrectly considered exhaustive, that leads to a critical soundness bug.
31 |
32 | Exhaustiveness checking is done based on the THIR pattern tree, with `PatKind::Constant` being ignored.
33 | This means we can write:
34 | ```rust
35 | #![feature(exclusive_range_pattern)]
36 | #![feature(half_open_range_patterns)]
37 | const PAT: &[u8] = &[0];
38 |
39 | pub fn test(x: &[u8]) -> bool {
40 | match x {
41 | PAT => true,
42 | &[] => false,
43 | &[1..] => false,
44 | &[_, _, ..] => false
45 | }
46 | }
47 | ```
48 |
49 | To ensure soundness of exhaustiveness checking, it is crucial that all data considered this check is fully immutable.
50 | In particular, for constants of reference type, it is important that they only point to immutable data.
51 | For this reason, the static const checks reject references to `static` items.
52 | This is a new soundness concern that otherwise does not come up during CTFE.
53 | Note that this is orthogonal to the concern of [*reading from* a mutable `static`](const.md#reading-statics) during const initialization, which is a problem for all `const` (even when they are not used in patterns) because it breaks the "inlining" property.
54 | 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.
55 | (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.)
56 |
57 | *Dynamic check.*
58 | To dynamically ensure constants only point to read-only data, we maintain a global invariant:
59 | pointers to a `static` (including memory that was created as part of interning a `static`) may never "leak" to a non-`static`.
60 | All memory outside of `static`s is marked as immutable.
61 | As a consequence, non-`static`s recursively only point to immutable memory.
62 |
63 | This check is uncomfortably close to the static checks, and fragile due to its reliance on a global invariant.
64 | Longer-term, it would be better to move to a more local check, by making validation check if consts are recursively read-only.
65 | This check (unlike our current validation) would have to descend through pointers to `static`.
66 |
67 | An alternative could be to wait for the [value tree proposal](https://github.com/rust-lang/compiler-team/issues/323) to be implemented.
68 | 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.
69 | 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).
70 | 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.
71 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # The Rust Code of Conduct
2 |
3 | A version of this document [can be found online](https://www.rust-lang.org/conduct.html).
4 |
5 | ## Conduct
6 |
7 | **Contact**: [rust-mods@rust-lang.org](mailto:rust-mods@rust-lang.org)
8 |
9 | * We are committed to providing a friendly, safe and welcoming environment for all, regardless of level of experience, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, nationality, or other similar characteristic.
10 | * On IRC, please avoid using overtly sexual nicknames or other nicknames that might detract from a friendly, safe and welcoming environment for all.
11 | * Please be kind and courteous. There's no need to be mean or rude.
12 | * Respect that people have differences of opinion and that every design or implementation choice carries a trade-off and numerous costs. There is seldom a right answer.
13 | * Please keep unstructured critique to a minimum. If you have solid ideas you want to experiment with, make a fork and see how it works.
14 | * We will exclude you from interaction if you insult, demean or harass anyone. That is not welcome behavior. We interpret the term "harassment" as including the definition in the Citizen Code of Conduct; if you have any lack of clarity about what might be included in that concept, please read their definition. In particular, we don't tolerate behavior that excludes people in socially marginalized groups.
15 | * Private harassment is also unacceptable. No matter who you are, if you feel you have been or are being harassed or made uncomfortable by a community member, please contact one of the channel ops or any of the [Rust moderation team][mod_team] immediately. Whether you're a regular contributor or a newcomer, we care about making this community a safe place for you and we've got your back.
16 | * Likewise any spamming, trolling, flaming, baiting or other attention-stealing behavior is not welcome.
17 |
18 | ## Moderation
19 |
20 |
21 | These are the policies for upholding our community's standards of conduct. If you feel that a thread needs moderation, please contact the [Rust moderation team][mod_team].
22 |
23 | 1. Remarks that violate the Rust standards of conduct, including hateful, hurtful, oppressive, or exclusionary remarks, are not allowed. (Cursing is allowed, but never targeting another user, and never in a hateful manner.)
24 | 2. Remarks that moderators find inappropriate, whether listed in the code of conduct or not, are also not allowed.
25 | 3. Moderators will first respond to such remarks with a warning.
26 | 4. If the warning is unheeded, the user will be "kicked," i.e., kicked out of the communication channel to cool off.
27 | 5. If the user comes back and continues to make trouble, they will be banned, i.e., indefinitely excluded.
28 | 6. Moderators may choose at their discretion to un-ban the user if it was a first offense and they offer the offended party a genuine apology.
29 | 7. If a moderator bans someone and you think it was unjustified, please take it up with that moderator, or with a different moderator, **in private**. Complaints about bans in-channel are not allowed.
30 | 8. Moderators are held to a higher standard than other community members. If a moderator creates an inappropriate situation, they should expect less leeway than others.
31 |
32 | In the Rust community we strive to go the extra step to look out for each other. Don't just aim to be technically unimpeachable, try to be your best self. In particular, avoid flirting with offensive or sensitive issues, particularly if they're off-topic; this all too often leads to unnecessary fights, hurt feelings, and damaged trust; worse, it can drive people away from the community entirely.
33 |
34 | And if someone takes issue with something you said or did, resist the urge to be defensive. Just stop doing what it was they complained about and apologize. Even if you feel you were misinterpreted or unfairly accused, chances are good there was something you could've communicated better — remember that it's your responsibility to make your fellow Rustaceans comfortable. Everyone wants to get along and we are all here first and foremost because we want to talk about cool technology. You will find that people will be eager to assume good intent and forgive as long as you earn their trust.
35 |
36 | The enforcement policies listed above apply to all official Rust venues; including official IRC channels (#rust, #rust-internals, #rust-tools, #rust-libs, #rustc, #rust-beginners, #rust-docs, #rust-community, #rust-lang, and #cargo); GitHub repositories under rust-lang, rust-lang-nursery, and rust-lang-deprecated; and all forums under rust-lang.org (users.rust-lang.org, internals.rust-lang.org). For other projects adopting the Rust Code of Conduct, please contact the maintainers of those projects for enforcement. If you wish to use this code of conduct for your own project, consider explicitly mentioning your moderation policy or making a copy with your own moderation policy so as to avoid confusion.
37 |
38 | *Adapted from the [Node.js Policy on Trolling](http://blog.izs.me/post/30036893703/policy-on-trolling) as well as the [Contributor Covenant v1.3.0](https://www.contributor-covenant.org/version/1/3/0/).*
39 |
40 | [mod_team]: https://www.rust-lang.org/team.html#Moderation-team
41 |
--------------------------------------------------------------------------------
/const.md:
--------------------------------------------------------------------------------
1 | # Constants
2 |
3 | "Constants" in this document refers to `const` bodies, array sizes, and non-`Copy` array initializers.
4 | On top of what applies to [statics](static.md), they are subject to an additional constraint: In code like
5 | ```rust
6 | const CONST: T = EXPR;
7 | ```
8 | is supposed to behave as-if `EXPR` was written at every use site of `CONST`.
9 | However, in current versions of rustc, `EXPR` is just computed once and the result is bit-wise copied each time `CONST` is used.
10 | To make this work, we need to ensure [const safety](const_safety.md).
11 |
12 | Based on this requirement, we allow other constants and [promoteds](promotion.md) to read from constants.
13 | This is why the value of a `const` is subject to validity checks.
14 |
15 | Constants can also be [used in patterns](patterns.md), which brings its own soundness concerns.
16 |
17 | ## References
18 |
19 | One issue is constants of reference type:
20 | ```rust
21 | const REF: &u32 = &EXPR;
22 | ```
23 | Instead of creating a new allocation for storing the result of `EXPR` on every
24 | use site, we just have a single global "static" allocation and every use of
25 | `REF` uses its address. It's as if we had written:
26 | ```rust
27 | const REF: &u32 = { const _VAL = EXPR; static _STATIC = EXPR; &_STATIC };
28 | ```
29 | (`EXPR` is assigned to a `const` first to make it subject to the restrictions
30 | discussed in this document.)
31 |
32 | There are various reasons why this could be an issue.
33 |
34 | ### 1. Pointer equality
35 |
36 | We effectively "deduplicate" all the allocations that would otherwise locally be
37 | created at each use site of `REF`. This is observable when the program compares
38 | these pointers for equality. We consider this okay, i.e., programs may not rely
39 | on such constants all getting distinct addresses. They may not rely on them all
40 | getting the same address either.
41 |
42 | ### 2. Interior mutability
43 |
44 | If the reference has type `&Cell` it is quite clear that the program can
45 | easily observe whether two references point to the same memory even without
46 | comparing their address: Changes through one reference will affect reads through
47 | the other. So, we cannot allow constant references to types that have interior
48 | mutability (types that are not `Freeze`):
49 |
50 | ```rust
51 | const BAD: &Cell = &Cell::new(42);
52 | // Inlining `BAD` everywhere clearly is not the same as them all pointing to the same thing.
53 | ```
54 |
55 | However, we can do better than that: Even if a *type* is not `Freeze`, it can
56 | have *values* that do not exhibit any interior mutability. For example, `&None`
57 | at type `&Option| >` would be rejected by the naive analysis above, but
58 | is actually accepted by the compiler because we know that there is no
59 | `UnsafeCell` here that would permit interior mutability.
60 |
61 | *Dynamic check.* The Miri engine enforces this dynamically by ensuring that the
62 | new data that is interned for a constant is all marked as immutable.
63 | (Except for
64 | [data inside promoteds](https://github.com/rust-lang/rust/blob/d538b80ad77949e46989cd355cdec193b574f052/src/librustc_mir/interpret/intern.rs#L363-L367)
65 | which cannot easily be checked, and is thus just *marked* as immutable because
66 | it descends from a shared reference subject to the strict syntactic checks of
67 | lifetime extension.)
68 |
69 | Note that a constant *referring to* some already existing mutable memory is
70 | fine: inlining that reference everywhere has the same behavior as computing a
71 | new reference each time. In both cases, there exists exactly one instance of
72 | the mutable memory that everything references.
73 |
74 | ### 3. `Send`
75 |
76 | Finally, the same constant value is actually shared across threads. This is
77 | very similar to sending the same value across threads, so it seems like we
78 | should reject non-`Send` types. For shared references, this means the pointee
79 | type ought to be `Sync`. That is already required for `static` items, so this
80 | is consistent with the desugaring described above.
81 |
82 | However, this does not currently happen, and there are several crates across the
83 | ecosystem that would break if we just started enforcing this now. See
84 | [this issue](https://github.com/rust-lang/rust/issues/49206) and the
85 | [PR attempting to fix this](https://github.com/rust-lang/rust/pull/54424/).
86 |
87 | One could make the argument that the value does not have to be `Send` because it
88 | is not actually sent to other threads; instead, conceptually, each thread
89 | re-does the same computation. But we know they will all come to the same
90 | result. This works, except when we consider address identity: with references
91 | in the `const`, all threads will get the same address, unlike in case of a
92 | per-thread recomputation which would lead to different addresses. As a
93 | consequence, non-`Send` `const` without references are fine, but once references
94 | and thus address identity comes into play, we have a problem.
95 |
96 | *Dynamic check.* It is unclear how the Miri engine could dynamically check this.
97 |
98 | ### 4. Drop
99 |
100 | `Drop` is actually not an issue, at least not more so than for statics:
101 |
102 | ```rust
103 | struct Foo;
104 |
105 | impl Drop for Foo {
106 | fn drop(&mut self) {
107 | println!("foo dropped");
108 | }
109 | }
110 |
111 | const FOO: Foo = Foo; // Ok, drop is run at each use site in runtime code
112 |
113 | // Not ok, cannot run `Foo::drop` because it's not a const fn
114 | const BAR: i32 = (Foo, 42).1;
115 | ```
116 |
117 | ## Reading statics
118 |
119 | Beyond values of reference type, we have to be careful that *computing* a
120 | `const` cannot read from a static that could get mutated (because it is `static
121 | mut`, or because it has interior mutability). That would lead to the constant
122 | expression (being computed at compile-time) not having the same value any more
123 | when evaluated at run-time.
124 |
125 | This is distinct to the concern about interior mutability above: That concern
126 | was about first computing a `&Cell` and then using it at run-time (and
127 | observing the fact that it has been "deduplicated"), this here is about using
128 | such a value at compile-time even though it might be changed at run-time.
129 |
130 | *Dynamic check.* The Miri engine checks this dynamically by refusing to access
131 | global mutable memory, and refusing to dereference any pointer to a static, when
132 | computing a const.
133 |
--------------------------------------------------------------------------------
/const_safety.md:
--------------------------------------------------------------------------------
1 | # Const safety
2 |
3 | The miri engine, which is used to execute code at compile time, can fail in
4 | four possible ways:
5 |
6 | * The program performs an unsupported operation (e.g., calling an unimplemented
7 | intrinsics, or doing an operation that would observe the integer address of a
8 | pointer).
9 | * The program causes undefined behavior (e.g., dereferencing an out-of-bounds
10 | pointer).
11 | * The program panics (e.g., a failed bounds check).
12 | * The program exhausts its resources: It might overflow the stack, allocate
13 | too much memory or loop forever. Note that detecting these conditions
14 | happens on a best-effort basis only.
15 |
16 | Just like panics and non-termination are acceptable in safe run-time Rust code,
17 | we also consider these acceptable in safe compile-time Rust code. However, we
18 | would like to rule out the first two kinds of failures in safe code. Following
19 | the terminology in [this blog post], we call a program that does not fail in the
20 | first two ways *const safe*.
21 |
22 | [this blog post]: https://www.ralfj.de/blog/2018/07/19/const.html
23 |
24 | The goal of the const safety check, then, is to ensure that a program is const
25 | safe. What makes this tricky is that there are some operations that are safe as
26 | far as run-time Rust is concerned, but unsupported in the miri engine and hence
27 | not const safe (they fall in the first category of failures above). We call these operations *unconst*. The purpose
28 | of the following section is to explain this in more detail, before proceeding
29 | with the main definitions.
30 |
31 | ## Miri background
32 |
33 | A very simple example of an unconst operation is
34 | ```rust
35 | static S:i32 = 0;
36 | const BAD:bool = (&S as *const i32 as usize) % 16 == 0;
37 | ```
38 | The modulo operation here is not supported by the miri engine because evaluating
39 | it requires knowing the actual integer address of `S`.
40 |
41 | The way miri handles this is by treating pointer and integer values separately.
42 | The most primitive kind of value in miri is a `Scalar`, and a scalar is *either*
43 | a pointer (`Scalar::Ptr`) or a bunch of bits representing an integer
44 | (`Scalar::Bits`). Every value of a variable of primitive type is stored as a
45 | `Scalar`. In the code above, casting the pointer `&S` to `*const i32` and then
46 | to `usize` does not actually change the value -- we end up with a local variable
47 | of type `usize` whose value is a `Scalar::Ptr`. This is not a problem in
48 | itself, but then executing `%` on this *pointer value* is unsupported.
49 |
50 | However, it does not seem appropriate to blame the `%` operation above for this
51 | failure. `%` on "normal" `usize` values (`Scalar::Bits`) is perfectly fine, just using it on
52 | values computed from pointers is an issue. Essentially, `&i32 as *const i32 as
53 | usize` is a "safe" `usize` at run-time (meaning that applying safe operations to
54 | this `usize` cannot lead to misbehavior, following terminology [suggested here])
55 | -- but the same value is *not* "safe" at compile-time, because we can cause a
56 | const safety violation by applying a safe operation (namely, `%`).
57 |
58 | [suggested here]: https://www.ralfj.de/blog/2018/08/22/two-kinds-of-invariants.html
59 |
60 | ## Const safety check on values
61 |
62 | The result of any const computation (`const`, `static`, promoteds) is subject to
63 | a "sanity check" which enforces const safety. (A sanity check is already
64 | happening, but it is not exactly checking const safety currently.) Const safety
65 | is defined as follows:
66 |
67 | * Integer and floating point types are const-safe if they are a `Scalar::Bits`.
68 | This makes sure that we can run `%` and other operations without violating
69 | const safety. In particular, the value must *not* be uninitialized.
70 | * References are const-safe if they are `Scalar::Ptr` into allocated memory, and
71 | the data stored there is const-safe. (Technically, we would also like to
72 | require `&mut` to be unique and `&` to not be mutable unless there is an
73 | `UnsafeCell`, but it seems infeasible to check that.) For fat pointers, the
74 | length of a slice must be a valid `usize` and the vtable of a `dyn Trait` must
75 | be a valid vtable.
76 | * `bool` is const-safe if it is `Scalar::Bits` with a value of `0` or `1`.
77 | * `char` is const-safe if it is a valid unicode codepoint.
78 | * `()` is always const-safe.
79 | * `!` is never const-safe.
80 | * Tuples, structs, arrays and slices are const-safe if all their fields are
81 | const-safe.
82 | * Enums are const-safe if they have a valid discriminant and the fields of the
83 | active variant are const-safe.
84 | * Unions are always const-safe; the data does not matter.
85 | * `dyn Trait` is const-safe if the value is const-safe at the type indicated by
86 | the vtable.
87 | * Function pointers are const-safe if they point to an actual function. A
88 | `const fn` pointer (when/if we have those) must point to a `const fn`.
89 |
90 | For example:
91 | ```rust
92 | static S: i32 = 0;
93 | const BAD: usize = &S as *const i32 as usize;
94 | ```
95 | Here, `S` is const-safe because `0` is a `Scalar::Bits`. However, `BAD` is *not* const-safe because it is a `Scalar::Ptr`.
96 |
97 | ## Const safety check on code
98 |
99 | The purpose of the const safety check on code is to prohibit construction of
100 | non-const-safe values in safe code. We can allow *almost* all safe operations,
101 | except for unconst operations -- which are all related to raw pointers:
102 | Comparing raw pointers for (in)equality, converting them to integers, hashing
103 | them (including hashing references) and so on must be prohibited. Basically, we
104 | should not permit any raw pointer operations to begin with, and carefully
105 | evaluate any that we permit to make sure they are fully supported by miri and do
106 | not permit constructing non-const-safe values.
107 |
108 | There should also be a mechanism akin to `unsafe` blocks to opt-in to using
109 | unconst operations. At this point, it becomes the responsibility of the
110 | programmer to preserve const safety. In particular, a *safe* `const fn` must
111 | always execute const-safely when called with const-safe arguments, and produce a
112 | const-safe result. For example, the following function is const-safe (after
113 | some extensions of the miri engine that are already implemented in miri) even
114 | though it uses raw pointer operations:
115 | ```rust
116 | const fn slice_eq(x: &[u32], y: &[u32]) -> bool {
117 | if x.len() != y.len() {
118 | return false;
119 | }
120 | // equal length and address -> memory must be equal, too
121 | if unconst { x as *const [u32] as *const u32 == y as *const [u32] as *const u32 } {
122 | return true;
123 | }
124 | // assume the following is legal const code for the purpose of this function
125 | x.iter().eq(y.iter())
126 | }
127 | ```
128 | On the other hand, the following function is *not* const-safe and hence it is considered a bug to mark it as such:
129 | ```
130 | const fn ptr_eq(x: &T, y: &T) -> bool {
131 | unconst { x as *const T == y as *const T }
132 | }
133 | ```
134 |
135 | If the function were invoked as `ptr_eq(&42, &42)` the result depends on the potential
136 | deduplication of the memory of the `42`s.
137 |
138 | ## Open questions
139 |
140 | * Do we allow unconst operations in `unsafe` blocks, or do we have some other
141 | mechanism for opting in to them (like `unconst` blocks)?
142 |
143 | * How do we communicate that the rules for safe `const fn` using unsafe code are
144 | different than the ones for "runtime" functions? The good news here is that
145 | violating the rules, at worst, leads to a compile-time error in a dependency.
146 | No UB can arise. However, thanks to [promotion](promotion.md), compile-time
147 | errors can arise even if no `const` or `static` is involved.
148 |
--------------------------------------------------------------------------------
/src/skill_tree.md:
--------------------------------------------------------------------------------
1 | # Skill tree for const eval features
2 |
3 | ```skill-tree
4 | [[group]]
5 | name = "cell_ref"
6 | label = "references to interior mutability\nfeature:const_cell_refs"
7 | href = "https://github.com/rust-lang/rust/issues/79703"
8 | items = []
9 |
10 | [[group]]
11 | name = "mut_ref"
12 | label = "mutable references in\nconst fn\nfeature:const_mut_refs"
13 | href = "https://github.com/rust-lang/rust/issues/57349"
14 | items = []
15 |
16 | [[group]]
17 | name = "const_mut_ref"
18 | label = "mutable references in\ninitializers of const items"
19 | href = "https://github.com/rust-lang/rust/issues/71212"
20 | requires = ["mut_ref"]
21 | items = []
22 |
23 | [[group]]
24 | name = "file_output"
25 | label = "write to files\nfrom constants"
26 | href = "https://github.com/rust-lang/const-eval/issues/25"
27 | items = []
28 |
29 | [[group]]
30 | name = "final_heap"
31 | label = "heap allocations\nin the final value of constants"
32 | requires = ["heap"]
33 | href = "https://github.com/rust-lang/const-eval/issues/20"
34 | items = []
35 |
36 | [[group]]
37 | name = "heap"
38 | label = "heap allocations"
39 | requires = ["mut_ref", "cell_ref", "trait_impl"]
40 | items = []
41 | href = "https://github.com/rust-lang/const-eval/issues/20"
42 |
43 | [[group]]
44 | name = "iterators"
45 | label = "iterators"
46 | requires = ["mut_ref", "trait_impl"]
47 | items = []
48 |
49 | [[group]]
50 | name = "for"
51 | label = "for loops"
52 | requires = ["trait_impl", "mut_ref"]
53 | items = []
54 |
55 | [[group]]
56 | name = "slice_eq"
57 | label = "[T]::eq"
58 | requires = ["for", "fuzzy-ptr-comparisons", "trait_impl"]
59 | items = []
60 |
61 | [[group]]
62 | name = "box_new"
63 | label = "Box::new"
64 | requires = ["heap", "trait_bound_opt_out", "drop"]
65 | items = []
66 |
67 | [[group]]
68 | name = "ptr-is-null"
69 | label = "<*T>::is_null\nfeature:const_ptr_is_null"
70 | requires = ["fuzzy-ptr-comparisons"]
71 | href = "https://github.com/rust-lang/rust/issues/74939"
72 | items = []
73 |
74 | [[group]]
75 | name = "fuzzy-ptr-comparisons"
76 | label = "guaranteed_eq and\nguaranteed_ne\nfeature:const_compare_raw_pointers"
77 | href = "https://github.com/rust-lang/rust/issues/53020"
78 | requires = []
79 | items = []
80 |
81 | [[group]]
82 | name = "unconst_rules"
83 | label = "unconst operations"
84 | href = "https://github.com/rust-lang/const-eval/issues/14"
85 | items = []
86 |
87 | [[group]]
88 | name = "raw_ptr_deref"
89 | label = "feature:const_raw_ptr_deref"
90 | items = []
91 | href = "https://github.com/rust-lang/rust/issues/51911"
92 |
93 | [[group]]
94 | name = "offset_of"
95 | label = "offset_of"
96 | items = []
97 | requires = [
98 | "raw_ptr_deref",
99 | "raw_ref_macros",
100 | "maybe_uninit_as_ptr",
101 | "offset_from",
102 | ]
103 |
104 | [[group]]
105 | name = "offset"
106 | label = "offset\nfeature:const_ptr_offset"
107 | href = "https://github.com/rust-lang/rust/issues/71499"
108 | items = []
109 |
110 | [[group]]
111 | name = "offset_from"
112 | label = "offset_from\nfeature:ptr_offset_from"
113 | href = "https://github.com/rust-lang/rust/issues/41079"
114 | items = []
115 |
116 | [[group]]
117 | name = "raw_ref_macros"
118 | label = "raw_ref maros\nfeature:raw_ref_macros"
119 | href = "https://github.com/rust-lang/rust/issues/73394"
120 | items = []
121 |
122 | [[group]]
123 | name = "maybe_uninit_as_ptr"
124 | label = "MaybeUninit::as_ptr\nfeature:const_maybe_uninit_as_ptr"
125 | href = "https://github.com/rust-lang/rust/issues/75251"
126 | items = []
127 |
128 | [[group]]
129 | name = "question_mark"
130 | label = "using ? in const"
131 | href = "https://github.com/rust-lang/rust/issues/74935"
132 | requires = ["trait_impl"]
133 | items = []
134 |
135 | [[group]]
136 | name = "mutex_new"
137 | label = "Mutex::new"
138 | href = "https://github.com/rust-lang/rust/issues/66806"
139 | items = []
140 | requires = ["parking_lot"]
141 |
142 | [[group]]
143 | name = "parking_lot"
144 | label = "`parking_lot` in `std`"
145 | href = "https://github.com/rust-lang/rust/issues/73714"
146 | items = []
147 |
148 | [[group]]
149 | name = "const_fn_in_patterns"
150 | label = "const fn callable in patterns"
151 | href = "https://github.com/rust-lang/rust/issues/57240"
152 | requires = ["const-blocks"]
153 | items = []
154 |
155 | [[group]]
156 | name = "from_str"
157 | label = "FromStr"
158 | href = "https://github.com/rust-lang/rust/issues/59133"
159 | requires = ["trait_impl"]
160 | items = []
161 |
162 | [[group]]
163 | name = "float"
164 | label = "floats in const fn"
165 | href = "https://github.com/rust-lang/rust/issues/57241"
166 | items = [
167 | { label = "from_bits" },
168 | { label = "to_bits" },
169 | { label = "float math, arguments and return types" },
170 | ]
171 |
172 | [[group]]
173 | name = "float_classify"
174 | label = "feature:const_float_classify"
175 | href = "https://github.com/rust-lang/rust/issues/72505"
176 | items = []
177 | requires = ["float_bits_conv"]
178 |
179 | [[group]]
180 | name = "float_bits_conv"
181 | label = "feature:const_float_bits_conv"
182 | href = "https://github.com/rust-lang/rust/issues/72447"
183 | items = []
184 | requires = ["float"]
185 |
186 | [[group]]
187 | name = "const-assert-eq"
188 | label = "assert_eq!"
189 | requires = ["trait_impl", "panic_fmt"]
190 | href = "https://github.com/rust-lang/rust/issues/74925"
191 | items = []
192 |
193 | [[group]]
194 | name = "const-blocks"
195 | label = "inline const"
196 | href = "https://github.com/rust-lang/rust/issues/76001"
197 | items = []
198 |
199 | [[group]]
200 | label = "panic! with formatting"
201 | name = "panic_fmt"
202 | requires = ["format_args"]
203 | href = "https://github.com/rust-lang/rust/issues/51999"
204 | items = []
205 |
206 | [[group]]
207 | label = "feature:const_discriminant"
208 | name = "discriminant"
209 | href = "https://github.com/rust-lang/rust/pull/69825"
210 | items = []
211 |
212 | [[group]]
213 | label = "feature:const_trait_bound_opt_out"
214 | name = "trait_bound_opt_out"
215 | href = "https://github.com/rust-lang/rust/issues/67794"
216 | items = []
217 |
218 | [[group]]
219 | label = "feature:const_trait_impl"
220 | name = "trait_impl"
221 | href="https://github.com/rust-lang/rust/issues/67792"
222 | items = []
223 | requires = ["trait_bound_opt_out"]
224 |
225 | [[group]]
226 | label = "feature:const_raw_ptr_to_usize_cast"
227 | name = "raw_ptr_to_usize_cast"
228 | href="https://github.com/rust-lang/rust/issues/51910"
229 | items = []
230 | requires = ["unconst_rules"]
231 |
232 | [[group]]
233 | label = "feature:const_extern_fn"
234 | name = "extern_const_fn"
235 | href = "https://github.com/rust-lang/rust/issues/64926"
236 | items = []
237 |
238 | [[group]]
239 | label = "const function pointers"
240 | name = "const_fn_ptr"
241 | href = "https://github.com/rust-lang/rust/issues/63997"
242 | items = []
243 | requires = ["trait_impl", "trait_bound_opt_out"]
244 |
245 | [[group]]
246 | label = "format!"
247 | name = "format"
248 | items = []
249 | requires = ["string", "format_args"]
250 |
251 | [[group]]
252 | label = "format_args!"
253 | name = "format_args"
254 | items = []
255 | requires = ["trait_impl", "fuzzy-ptr-comparisons"]
256 |
257 | [[group]]
258 | label = "String operations"
259 | name = "string"
260 | items = []
261 | requires = ["vec"]
262 |
263 | [[group]]
264 | label = "Vec operations"
265 | name = "vec"
266 | items = []
267 | requires = ["mut_ref", "heap", "trait_impl", "drop", "raw_ptr_deref"]
268 |
269 | [[group]]
270 | label = "Drop"
271 | name = "drop"
272 | items = []
273 | requires = ["mut_ref", "trait_impl"]
274 |
275 | [[group]]
276 | label = "ptr::copy_nonoverlapping\nfeature:const_intrinsic_copy"
277 | name = "copy_nonoverlapping"
278 | items = []
279 | href = "https://github.com/rust-lang/rust/issues/80697"
280 | requires = ["raw_ptr_deref", "mut_ref"]
281 |
282 | [[group]]
283 | label = "async functions\nand blocks"
284 | name = "async"
285 | items = []
286 | href = "https://github.com/rust-lang/rust/issues/69431"
287 | requires = ["trait_impl"]
288 | ```
289 |
--------------------------------------------------------------------------------
/LICENSE-APACHE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/promotion.md:
--------------------------------------------------------------------------------
1 | # Const promotion
2 |
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. This mechanism has been introduced by [RFC 1414][promotion-rfc] with
6 | the goal of equipping some references-to-temporaries with a `'static` lifetime,
7 | which is sometimes called "lifetime extension".
8 |
9 | [promotion-rfc]: https://github.com/rust-lang/rfcs/blob/master/text/1414-rvalue_static_promotion.md
10 |
11 | Promotion / lifetime extension affects code like `&3`: Instead of putting it on
12 | the stack, the `3` is allocated in global static memory and a reference with
13 | lifetime `'static` is provided. This is essentially an automatic transformation
14 | turning `&EXPR` into `{ const _PROMOTED = &EXPR; _PROMOTED }`, but only if
15 | `EXPR` qualifies. Topmost projections are not promoted, so `&EXPR.proj1.proj2`
16 | turns into `{ const _PROMOTED = &EXPR; &(*_PROMOTED).proj1.proj2 }`.
17 |
18 | Note that promotion happens on the MIR, not on surface-level syntax. This is
19 | relevant when discussing e.g. handling of panics caused by overflowing
20 | arithmetic.
21 |
22 | ## Promotion and fallability of const-evaluation
23 |
24 | On top of what applies to [consts](const.md), promoteds suffer from the
25 | additional issue that *the user did not ask for them to be evaluated at
26 | compile-time*. Thus, if CTFE fails but the code would have worked fine at
27 | run-time, we broke the user's code for no good reason. Even if we are sure we
28 | found an error in the user's code, we are only allowed to
29 | [emit a warning, not a hard error][warn-rfc].
30 |
31 | For example:
32 | ```rust
33 | fn foo() {
34 | if false {
35 | let x = &(1/0);
36 | }
37 | }
38 | ```
39 | If we performed promotion here, this would turn into
40 | ```rust
41 | fn foo() {
42 | if false {
43 | const _PROMOTED = &(1/0);
44 | let x = _PROMOTED;
45 | }
46 | }
47 | ```
48 | When compiling this function, we have to evaluate all constants that occur
49 | inside the function body, even if they might only be used in dead code. This
50 | means we have to evaluate `_PROMOTED`, which will error -- and now what, should
51 | we halt compilation? That would be wrong since there is no problem with this
52 | code, the "bad" division never actually happens as it occurs in dead code.
53 | (Note that the considerations would be the same even if `foo` were a `const
54 | fn`.)
55 |
56 | As a consequence, we only promote code that can never fail to evaluate (see
57 | [RFC 3027]). This ensures that even if promotion happens inside dead code, this
58 | will not turn a "runtime error in dead code" (which is not an error at all) into
59 | a compile-time error. In particular, we cannot promote calls to arbitrary `const
60 | fn`, as discussed in detail in
61 | [rust-lang/const-eval#19](https://github.com/rust-lang/const-eval/issues/19).
62 | Thus, only functions marked `#[rustc_promotable]` are promotable.
63 |
64 | [RFC 3027]: https://rust-lang.github.io/rfcs/3027-infallible-promotion.html
65 |
66 | There is one exception to this rule: the bodies of `const`/`static`
67 | initializers. This code is never compiled, so we do not actually have to
68 | evaluate constants that occur in dead code. If we are careful enough during
69 | compilation, we can ensure that only constants whose value is *actually needed*
70 | are evaluated. We thus can be more relaxed about promotion; in practice, what
71 | this means is that we will promote calls to arbitrary `const fn`, not just those
72 | marked `#[rustc_promotable]`.
73 |
74 | [See below][access-static] for another special case in promotion analysis:
75 | accesses and references to statics are only promoted inside other statics.
76 |
77 | [warn-rfc]: https://github.com/rust-lang/rfcs/blob/master/text/1229-compile-time-asserts.md
78 |
79 | ## "enclosing scope" rule
80 |
81 | Notice that some code involving `&` *looks* like it relies on promotion /
82 | lifetime extension but actually does not:
83 |
84 | ```rust
85 | const EMPTY_BYTES: &Vec = &Vec::new(); // Ok without lifetime extension
86 | ```
87 |
88 | `Vec::new()` cannot get promoted because it needs dropping. And yet this
89 | compiles. Why that? The reason is that the reference obtains the lifetime of
90 | the "enclosing scope", similar to how `let x = &mut x;` creates a reference
91 | whose lifetime lasts for the enclosing scope. This is decided during MIR
92 | building already, and does not involve lifetime extension.
93 |
94 | In contrast, this does not compile:
95 |
96 | ```rust
97 | const OPT_EMPTY_BYTES: Option<&Vec> = Some(&Vec::new());
98 | ```
99 |
100 | The "enclosing scope" rule only fires for outermost `&`, just like in `fn` bodies.
101 |
102 | ## Promotability
103 |
104 | We have described the circumstances where promotion is desirable, but what
105 | expressions are actually eligible for promotion? We refer to eligible
106 | expressions as "promotable" and describe the restrictions on such expressions
107 | below.
108 |
109 | First of all, expressions have to be [allowed in constants](const.md). The
110 | restrictions described there are needed because we want `const` to behave the
111 | same as copying the `const` initializer everywhere the constant is used; we need
112 | the same property when promoting expressions. But we need more.
113 |
114 | Note that there is no point in doing additional dynamic checks to ensure that we
115 | do get these restrictions right. The entire point of the promotion restrictions
116 | is to avoid failing compilation for code that would have been fine without
117 | promotion. The best a dynamic check could do is tell us after the fact that we
118 | should not have promoted something, but then it is already too late -- and the
119 | dynamic checks for that are exactly the ones we are already doing for constants
120 | and statics.
121 |
122 | ### Panics, overflow and bounds checks
123 |
124 | Let us look at what happens when we promote `&(0_usize - 1)` in a debug build.
125 | This code is promoted even though we cannot promote code that could fail, and
126 | this code will fail with an overflow error! What is happening? We have to look
127 | at the underlying MIR representation of this code to explain what happens:
128 |
129 | ```
130 | _tmp1 = CheckedSub (const 0usize) (const 1usize)
131 | assert(!_tmp1.1) -> [success: bb2; unwind: ..]
132 |
133 | bb2:
134 | _tmp2 = tmp1.0
135 | _res = &_tmp2
136 | ```
137 |
138 | Both `_tmp1` and `_tmp2` are promoted. `_tmp1` evaluates to `(~0, true)`, so
139 | the assertion will always fail at run-time. Computing `_tmp2` evaluates to `~0`.
140 |
141 | In other words, the actually failing check is not promoted, only the computation
142 | that serves as input to the check is promoted.
143 |
144 | An earlier version of Miri used to error on arithmetic overflow even in release
145 | mode. This breaks promotion, because now promoting code like `_tmp1` would
146 | introduce promotes that fail to evaluate, which is not acceptable as explained
147 | above!
148 |
149 | Something similar but more subtle happens when promoting array accesses: the
150 | bounds check is not promoted, but the array access is. However, before accepting
151 | a temporary for promotion, we ensure that array accesses are definitely
152 | in-bounds. This leads to MIR without bounds checks, but we know the array access
153 | will always succeed.
154 |
155 | ### Const safety
156 |
157 | We have explained how we ensure that evaluating a promoted does not panic, but
158 | what about other kinds of failure -- what about hitting an unsupported operation
159 | or undefined behavior? To make sure this does not happen, only const safe code
160 | gets promoted. The exact details for `const safety` are discussed in
161 | [here](const_safety.md).
162 |
163 | An example of this would be `&(&1 as *const i32 as usize % 16 == 0)`. The actual
164 | location is not known at compile-time, so we cannot promote this. Generally, we
165 | can guarantee const-safety by not promoting when an unsafe or unconst operation
166 | is performed -- if our const safety checker is correct, that has to cover
167 | everything, so the only possible remaining failure are panics.
168 |
169 | However, things get more tricky when `const` and `const fn` are involved.
170 |
171 | For `const`, based on the const safety check described [here](const_safety.md#const-safety-check-on-values),
172 | we can rely on there not being const-unsafe values in the `const`, so we should
173 | be able to promote freely. For example:
174 |
175 | ```rust
176 | union Foo { x: &'static i32, y: usize }
177 | const A: usize = unsafe { Foo { x: &1 }.y };
178 | const B: usize = unsafe { Foo { x: &2 }.y };
179 | let x: &bool = &(A < B);
180 | ```
181 |
182 | Promoting `x` would lead to a compile failure because we cannot compare pointer
183 | addresses. However, we do not even get there -- computing `A` or `B` fails with
184 | a const safety check error because these are values of type `usize` that contain
185 | a `Scalar::Ptr`.
186 |
187 | For `const fn`, however, there is no way to check anything in advance. We can
188 | either just not promote, or we can move responsibility to the `const fn` and
189 | promote *if* all function arguments pass the const safety check. So,
190 | `foo(42usize)` would get promoted, but `foo(&1 as *const i32 as usize)` would
191 | not. When this call panics, compilation proceeds and we just hard-code a panic
192 | to happen as well at run-time. However, when const evaluation fails with
193 | another error (unsupported operation or undefined behavior), we have no choice
194 | but to abort compilation of a program that would have compiled fine if we would
195 | not have decided to promote. It is the responsibility of `foo` to not fail this
196 | way when working with const-safe arguments.
197 |
198 | For this reason, only `const fn` that were explicitly marked with the
199 | `#[rustc_promotable]` attribute are subject to promotion. Those functions must
200 | be manually reviewed to never raise CTFE errors.
201 |
202 | ### Drop
203 |
204 | Expressions returning "needs drop" types can never be promoted. If such an
205 | expression were promoted, the `Drop` impl would never get called on the value,
206 | even though the user did not explicitly request such behavior by using an
207 | explicit `const` or `static` item.
208 |
209 | As expression promotion is essentially the silent insertion of a `static` item, and
210 | `static` items never have their `Drop` impl called, the `Drop` impl of the promoted
211 | value would never get called.
212 |
213 | While it is sound to `std::mem::forget` any value and thus not call its `Drop` impl,
214 | it is unlikely to be the desired behavior in most cases and very likey to be confusing
215 | to the user. If such behavior is desired, the user can still use an explicit `static`
216 | or `const` item and refer to that.
217 |
218 | *Dynamic check.* The Miri engine could dynamically check this by ensuring that
219 | the result of computing a promoted is a value that does not need dropping.
220 |
221 | ### Access to a `const` or `static`
222 | [access-static]: #access-to-a-const-or-static
223 |
224 | When accessing a `const` in a promotable context, its value gets computed
225 | at compile-time anyway, so we do not have to check the initializer. However, the
226 | restrictions described above still apply for the *result* of the promoted
227 | computation: in particular, it must be a valid `const` (i.e., it cannot
228 | introduce interior mutability) and it must not require dropping.
229 |
230 | For instance the following would be legal even though calls to `do_it` are not
231 | eligible for implicit promotion:
232 |
233 | ```rust
234 | const fn do_it(x: i32) -> i32 { 2*x }
235 | const ANSWER: i32 = {
236 | let ret = do_it(21);
237 | ret
238 | };
239 |
240 | let x: &'static i32 = &ANSWER;
241 | ```
242 |
243 | An access to a `static`, including just taking references to a `static`, is only
244 | promotable within the initializer of another `static`. This is for the same
245 | reason that `const` initializers
246 | [cannot access statics](const.md#reading-statics).
247 |
248 | Crucially, however, the following is *not* legal:
249 |
250 | ```rust
251 | const X: Cell = Cell::new(5); // ok
252 | const XREF: &Cell = &X; // not ok
253 | fn main() {
254 | let x: &'static _ = &X; // not ok
255 | }
256 | ```
257 |
258 | Just like allowing `XREF` would be a problem because, by the inlining semantics,
259 | every user of `XREF` should get their own `Cell`; it would also be a problem to
260 | promote here because if that code gets executed multiple times (e.g. inside a
261 | loop), it should get a new `Cell` each time.
262 |
263 | ### Named locals
264 |
265 | Promotable expressions cannot refer to named locals. This is not a technical
266 | limitation with the CTFE engine. While writing `let x = {expr}` outside of a
267 | const context, the user likely expects that `x` will live on the stack and be
268 | initialized at run-time. Although this is not (to my knowledge) guaranteed by
269 | the language, we do not wish to violate the user's expectations here.
270 |
271 | Note that constant-folding still applies: the optimizer may compute `x` at
272 | compile-time and even inline it everywhere if it can show that this does not
273 | observably alter program behavior. Promotion is very different from
274 | constant-folding as promotion can introduce observable differences in behavior
275 | (if const-evaluation fails) and as it is *guaranteed* to happen in some cases
276 | (and thus exploited by the borrow checker). This is reflected in the fact that
277 | promotion affects lifetimes, but constant folding does not.
278 |
279 | ### Single assignment
280 |
281 | We only promote temporaries that are assigned to exactly once. For example, the
282 | lifetime of the temporary whose reference is assigned to `x` below will not be
283 | extended.
284 |
285 | ```rust
286 | let x: &'static i32 = &if cfg!(windows) { 0 } else { 1 };
287 | ```
288 |
289 | Once again, this is not a fundamental limitation in the CTFE engine; we are
290 | perfectly capable of evaluating such expressions at compile time. However,
291 | determining the promotability of complex expressions would require more
292 | resources for little benefit.
293 |
294 | ## Open questions
295 |
296 | * There is a fourth kind of CTFE failure -- resource exhaustion. What do we do
297 | when that happens while evaluating a promoted?
298 |
--------------------------------------------------------------------------------
|