Effects 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 predicates_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 predicates_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
predicates_of
, and not const_conditions
.
The const_conditions
query
predicates_of
represents a set of predicates that need to be proven to use an
item. For example, to use foo
in the example below:
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:
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:
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:
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
:
These checks are done in compare_method_predicate_entailment
. A similar
function that does the same check for associated types is called
compare_type_predicate_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
.
explicit_implied_const_bounds
on associated types and traits
Bounds on associated types, opaque types, and supertraits such as
Have their bounds represented differently. 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.
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 theexplicit_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, orDestruct
is implemented in const contexts if the type can be dropped at compile time.