|
| 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