| # Effects, const traits, and const condition checking | |
| ## The `HostEffect` predicate | |
| [`HostEffectPredicate`]s are a kind of predicate from `[const] Tr` or `const Tr` bounds. | |
| It has a trait reference, and a `constness` which could be `Maybe` or | |
| `Const` depending on the bound. | |
| Because `[const] Tr`, or rather `Maybe` bounds | |
| apply differently based on whichever contexts they are in, they have different | |
| behavior than normal bounds. | |
| Where normal trait bounds on a function such as | |
| `T: Tr` are collected within the [`clauses_of`] query to be proven when a | |
| function is called and to be assumed within the function, bounds such as | |
| `T: [const] Tr` will behave as a normal trait bound and add `T: Tr` to the result | |
| from `clauses_of`, but also adds a `HostEffectPredicate` to the [`const_conditions`] query. | |
| On the other hand, `T: const Tr` bounds do not change meaning across contexts, | |
| therefore they will result in `HostEffect(T: Tr, const)` being added to | |
| `clauses_of`, and not `const_conditions`. | |
| [`HostEffectPredicate`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_type_ir/predicate/struct.HostEffectPredicate.html | |
| [`clauses_of`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TyCtxt.html#method.clauses_of | |
| [`const_conditions`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TyCtxt.html#method.const_conditions | |
| ## The `const_conditions` query | |
| `clauses_of` represents a set of clauses that need to be proven to use an item. | |
| For example, to use `foo` in the example below: | |
| ```rust | |
| fn foo<T>() where T: Default {} | |
| ``` | |
| We must be able to prove that `T` implements `Default`. | |
| In a similar vein, | |
| `const_conditions` represents a set of predicates that need to be proven to use | |
| an item *in const contexts*. If we adjust the example above to use `const` trait bounds: | |
| ```rust | |
| const fn foo<T>() where T: [const] Default {} | |
| ``` | |
| Then `foo` would get a `HostEffect(T: Default, maybe)` in the `const_conditions` | |
| query, suggesting that in order to call `foo` from const contexts, one must | |
| prove that `T` has a const implementation of `Default`. | |
| ## Enforcement of `const_conditions` | |
| `const_conditions` are currently checked in various places. | |
| Every call in HIR from a const context (which includes `const fn` and `const` | |
| items) will check that `const_conditions` of the function we are calling hold. | |
| This is done in [`FnCtxt::enforce_context_effects`]. | |
| Note that we don't check | |
| if the function is only referred to but not called, as the following code needs to compile: | |
| ```rust | |
| const fn hi<T: [const] Default>() -> T { | |
| T::default() | |
| } | |
| const X: fn() -> u32 = hi::<u32>; | |
| ``` | |
| For a trait `impl` to be well-formed, we must be able to prove the | |
| `const_conditions` of the trait from the `impl`'s environment. | |
| This is checked in [`wfcheck::check_impl`]. | |
| Here's an example: | |
| ```rust | |
| const trait Bar {} | |
| const trait Foo: [const] Bar {} | |
| // `const_conditions` contains `HostEffect(Self: Bar, maybe)` | |
| impl const Bar for () {} | |
| impl const Foo for () {} | |
| // ^ here we check `const_conditions` for the impl to be well-formed | |
| ``` | |
| Methods of trait impls must not have stricter bounds than the method of the | |
| trait that they are implementing. | |
| To check that the methods are compatible, a | |
| hybrid environment is constructed with the predicates of the `impl` plus the | |
| predicates of the trait method, and we attempt to prove the predicates of the impl method. | |
| We do the same for `const_conditions`: | |
| ```rust | |
| const trait Foo { | |
| fn hi<T: [const] Default>(); | |
| } | |
| impl<T: [const] Clone> Foo for Vec<T> { | |
| fn hi<T: [const] PartialEq>(); | |
| // ^ we can't prove `T: [const] PartialEq` given `T: [const] Clone` and | |
| // `T: [const] Default`, therefore we know that the method on the impl | |
| // is stricter than the method on the trait. | |
| } | |
| ``` | |
| These checks are done in [`compare_method_clause_entailment`]. | |
| A similar function that does the same check for associated types is called | |
| [`compare_type_clause_entailment`]. | |
| Both of these need to consider `const_conditions` when in const contexts. | |
| In MIR, as part of const checking, `const_conditions` of items that are called | |
| are revalidated again in [`Checker::revalidate_conditional_constness`]. | |
| [`compare_method_clause_entailment`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_analysis/check/compare_impl_item/fn.compare_method_clause_entailment.html | |
| [`compare_type_clause_entailment`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_analysis/check/compare_impl_item/fn.compare_type_clause_entailment.html | |
| [`FnCtxt::enforce_context_effects`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_typeck/fn_ctxt/struct.FnCtxt.html#method.enforce_context_effects | |
| [`wfcheck::check_impl`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_analysis/check/wfcheck/fn.check_impl.html | |
| [`Checker::revalidate_conditional_constness`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_const_eval/check_consts/check/struct.Checker.html#method.revalidate_conditional_constness | |
| ## `explicit_implied_const_bounds` on associated types and traits | |
| Bounds on associated types, opaque types, and supertraits such as the following | |
| have their bounds represented differently: | |
| ```rust | |
| trait Foo: [const] PartialEq { | |
| type X: [const] PartialEq; | |
| } | |
| fn foo() -> impl [const] PartialEq { | |
| // ^ unimplemented syntax | |
| } | |
| ``` | |
| Unlike `const_conditions`, which need to be proved for callers, | |
| and can be assumed inside the definition (e.g. trait | |
| bounds on functions), these bounds need to be proved at definition (at the impl, | |
| or when returning the opaque) but can be assumed for callers. | |
| The non-const equivalent of these bounds are called [`explicit_item_bounds`]. | |
| These bounds are checked in [`compare_impl_item::check_type_bounds`] for HIR | |
| typeck, [`evaluate_host_effect_from_item_bounds`] in the old solver and | |
| [`consider_additional_alias_assumptions`] in the new solver. | |
| [`explicit_item_bounds`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TyCtxt.html#method.explicit_item_bounds | |
| [`compare_impl_item::check_type_bounds`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_analysis/check/compare_impl_item/fn.check_type_bounds.html | |
| [`evaluate_host_effect_from_item_bounds`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_trait_selection/traits/effects/fn.evaluate_host_effect_from_item_bounds.html | |
| [`consider_additional_alias_assumptions`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_next_trait_solver/solve/assembly/trait.GoalKind.html#tymethod.consider_additional_alias_assumptions | |
| ## Proving `HostEffectPredicate`s | |
| `HostEffectPredicate`s are implemented both in the [old solver] and the [new trait solver]. | |
| In general, we can prove a `HostEffect` predicate when either of these conditions are met: | |
| * The predicate can be assumed from caller bounds; | |
| * The type has a `const` `impl` for the trait, *and* that const conditions on | |
| the impl holds, *and* that the `explicit_implied_const_bounds` on the trait holds; or | |
| * The type has a built-in implementation for the trait in const contexts. | |
| For example, `Fn` may be implemented by function items if their const conditions | |
| are satisfied, or `Destruct` is implemented in const contexts if the type can | |
| be dropped at compile time. | |
| [old solver]: https://doc.rust-lang.org/nightly/nightly-rustc/src/rustc_trait_selection/traits/effects.rs.html | |
| [new trait solver]: https://doc.rust-lang.org/nightly/nightly-rustc/src/rustc_next_trait_solver/solve/effect_goals.rs.html | |
| ## More on const traits | |
| To be expanded later. | |
| ### The `#[rustc_non_const_trait_method]` attribute | |
| This is intended for internal (standard library) usage only. | |
| With this attribute applied to a trait method, the compiler will not check the default body of this | |
| method for ability to run in compile time. | |
| Users of the trait will also not be allowed to use this trait method in const contexts. | |
| This attribute is primarily | |
| used for constifying large traits such as `Iterator` without having to make all | |
| its methods `const` at the same time. | |
| This attribute should not be present while stabilizing the trait as `const`. |