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

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.