Skip to content

Commit 99157af

Browse files
authored
Start a chapter about the evolving const effect system (#1808)
* Start a chapter about the evolving const effect system * Address review comments
1 parent 2c65ca9 commit 99157af

File tree

3 files changed

+68
-0
lines changed

3 files changed

+68
-0
lines changed

src/SUMMARY.md

+1
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@
136136
- [Opaque Types](./opaque-types-type-alias-impl-trait.md)
137137
- [Inference details](./opaque-types-impl-trait-inference.md)
138138
- [Return Position Impl Trait In Trait](./return-position-impl-trait-in-trait.md)
139+
- [Effect checking](./effects.md)
139140
- [Pattern and Exhaustiveness Checking](./pat-exhaustive-checking.md)
140141
- [MIR dataflow](./mir/dataflow.md)
141142
- [Drop elaboration](./mir/drop-elaboration.md)

src/appendix/glossary.md

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ Term | Meaning
2525
<span id="drop-glue">drop glue</span> &nbsp; | (internal) compiler-generated instructions that handle calling the destructors (`Drop`) for data types.
2626
<span id="dst">DST</span> &nbsp; | Short for Dynamically-Sized Type, this is a type for which the compiler cannot statically know the size in memory (e.g. `str` or `[u8]`). Such types don't implement `Sized` and cannot be allocated on the stack. They can only occur as the last field in a struct. They can only be used behind a pointer (e.g. `&str` or `&[u8]`).
2727
<span id="ebl">early-bound lifetime</span> &nbsp; | A lifetime region that is substituted at its definition site. Bound in an item's `Generics` and substituted using a `GenericArgs`. Contrast with **late-bound lifetime**. ([see more](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_type_ir/sty/enum.RegionKind.html#bound-regions))
28+
<span id="effect">effects</span> &nbsp; | Right now only means const traits and `~const` bounds. ([see more](../effects.md))
2829
<span id="empty-type">empty type</span> &nbsp; | see "uninhabited type".
2930
<span id="fat-ptr">fat pointer</span> &nbsp; | A two word value carrying the address of some value, along with some further information necessary to put the value to use. Rust includes two kinds of "fat pointers": references to slices, and trait objects. A reference to a slice carries the starting address of the slice and its length. A trait object carries a value's address and a pointer to the trait's implementation appropriate to that value. "Fat pointers" are also known as "wide pointers", and "double pointers".
3031
<span id="free-var">free variable</span> &nbsp; | A "free variable" is one that is not bound within an expression or term; see [the background chapter for more](./background.md#free-vs-bound)

src/effects.md

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# Effects and effect checking
2+
3+
Note: all of this describes the implementation of the unstable `effects` and
4+
`const_trait_impl` features. None of this implementation is usable or visible from
5+
stable Rust.
6+
7+
The implementation of const traits and `~const` bounds is a limited effect system.
8+
It is used to allow trait bounds on `const fn` to be used within the `const fn` for
9+
method calls. Within the function, in order to know whether a method on a trait
10+
bound is `const`, we need to know whether there is a `~const` bound for the trait.
11+
In order to know whether we can instantiate a `~const` bound on a `const fn`, we
12+
need to know whether there is a `const_trait` impl for the type and trait being
13+
used (or whether the `const fn` is used at runtime, then any type implementing the
14+
trait is ok, just like with other bounds).
15+
16+
We perform these checks via a const generic boolean that gets attached to all
17+
`const fn` and `const trait`. The following sections will explain the desugarings
18+
and the way we perform the checks at call sites.
19+
20+
The const generic boolean is inverted to the meaning of `const`. In the compiler
21+
it is called `host`, because it enables "host APIs" like `static` items, network
22+
access, disk access, random numbers and everything else that isn't available in
23+
`const` contexts. So `false` means "const", `true` means "not const" and if it's
24+
a generic parameter, it means "maybe const" (meaning we're in a const fn or const
25+
trait).
26+
27+
## `const fn`
28+
29+
All `const fn` have a `#[rustc_host] const host: bool` generic parameter that is
30+
hidden from users. Any `~const Trait` bounds in the generics list or `where` bounds
31+
of a `const fn` get converted to `Trait<host> + Trait<true>` bounds. The `Trait<true>`
32+
exists so that associated types of the generic param can be used from projections
33+
like `<T as Trait>::Assoc`, because there are no `<T as ~const Trait>` projections for now.
34+
35+
## `#[const_trait] trait`s
36+
37+
The `#[const_trait]` attribute gives the marked trait a `#[rustc_host] const host: bool`
38+
generic parameter. All functions of the trait "inherit" this generic parameter, just like
39+
they have all the regular generic parameters of the trait. Any `~const Trait` super-trait
40+
bounds get desugared to `Trait<host> + Trait<true>` in order to allow using associated
41+
types and consts of the super traits in the trait declaration. This is necessary, because
42+
`<Self as SuperTrait>::Assoc` is always `<Self as SuperTrait<true>>::Assoc` as there is
43+
no `<Self as ~const SuperTrait>` syntax.
44+
45+
## `typeck` performing method and function call checks.
46+
47+
When generic parameters are instantiated for any items, the `host` generic parameter
48+
is always instantiated as an inference variable. This is a special kind of inference var
49+
that is not part of the type or const inference variables, similar to how we have
50+
special inference variables for type variables that we know to be an integer, but not
51+
yet which one. These separate inference variables fall back to `true` at
52+
the end of typeck (in `fallback_effects`) to ensure that `let _ = some_fn_item_name;`
53+
will keep compiling.
54+
55+
All actually used (in function calls, casts, or anywhere else) function items, will
56+
have the `enforce_context_effects` method invoked.
57+
It trivially returns if the function being called has no `host` generic parameter.
58+
59+
In order to error if a non-const function is called in a const context, we have not
60+
yet disabled the const-check logic that happens on MIR, because
61+
`enforce_context_effects` does not yet perform this check.
62+
63+
The function call's `host` parameter is then equated to the context's `host` value,
64+
which almost always trivially succeeds, as it was an inference var. If the inference
65+
var has already been bound (since the function item is invoked twice), the second
66+
invocation checks it against the first.

0 commit comments

Comments
 (0)