| # Effects and effect checking |
| |
| Note: all of this describes the implementation of the unstable `effects` and |
| `const_trait_impl` features. None of this implementation is usable or visible from |
| stable Rust. |
| |
| The implementation of const traits and `~const` bounds is a limited effect system. |
| It is used to allow trait bounds on `const fn` to be used within the `const fn` for |
| method calls. Within the function, in order to know whether a method on a trait |
| bound is `const`, we need to know whether there is a `~const` bound for the trait. |
| In order to know whether we can instantiate a `~const` bound on a `const fn`, we |
| need to know whether there is a `const_trait` impl for the type and trait being |
| used (or whether the `const fn` is used at runtime, then any type implementing the |
| trait is ok, just like with other bounds). |
| |
| We perform these checks via a const generic boolean that gets attached to all |
| `const fn` and `const trait`. The following sections will explain the desugarings |
| and the way we perform the checks at call sites. |
| |
| The const generic boolean is inverted to the meaning of `const`. In the compiler |
| it is called `host`, because it enables "host APIs" like `static` items, network |
| access, disk access, random numbers and everything else that isn't available in |
| `const` contexts. So `false` means "const", `true` means "not const" and if it's |
| a generic parameter, it means "maybe const" (meaning we're in a const fn or const |
| trait). |
| |
| ## `const fn` |
| |
| All `const fn` have a `#[rustc_host] const host: bool` generic parameter that is |
| hidden from users. Any `~const Trait` bounds in the generics list or `where` bounds |
| of a `const fn` get converted to `Trait<host> + Trait<true>` bounds. The `Trait<true>` |
| exists so that associated types of the generic param can be used from projections |
| like `<T as Trait>::Assoc`, because there are no `<T as ~const Trait>` projections for now. |
| |
| ## `#[const_trait] trait`s |
| |
| The `#[const_trait]` attribute gives the marked trait a `#[rustc_host] const host: bool` |
| generic parameter. All functions of the trait "inherit" this generic parameter, just like |
| they have all the regular generic parameters of the trait. Any `~const Trait` super-trait |
| bounds get desugared to `Trait<host> + Trait<true>` in order to allow using associated |
| types and consts of the super traits in the trait declaration. This is necessary, because |
| `<Self as SuperTrait>::Assoc` is always `<Self as SuperTrait<true>>::Assoc` as there is |
| no `<Self as ~const SuperTrait>` syntax. |
| |
| ## `typeck` performing method and function call checks. |
| |
| When generic parameters are instantiated for any items, the `host` generic parameter |
| is always instantiated as an inference variable. This is a special kind of inference var |
| that is not part of the type or const inference variables, similar to how we have |
| special inference variables for type variables that we know to be an integer, but not |
| yet which one. These separate inference variables fall back to `true` at |
| the end of typeck (in `fallback_effects`) to ensure that `let _ = some_fn_item_name;` |
| will keep compiling. |
| |
| All actually used (in function calls, casts, or anywhere else) function items, will |
| have the `enforce_context_effects` method invoked. |
| It trivially returns if the function being called has no `host` generic parameter. |
| |
| In order to error if a non-const function is called in a const context, we have not |
| yet disabled the const-check logic that happens on MIR, because |
| `enforce_context_effects` does not yet perform this check. |
| |
| The function call's `host` parameter is then equated to the context's `host` value, |
| which almost always trivially succeeds, as it was an inference var. If the inference |
| var has already been bound (since the function item is invoked twice), the second |
| invocation checks it against the first. |