Expand description

Minimal Specialization

This module contains the checks for sound specialization used when the min_specialization feature is enabled. This requires that the impl is always applicable.

If impl1 specializes impl2 then impl1 is always applicable if we know that all the bounds of impl2 are satisfied, and all of the bounds of impl1 are satisfied for some choice of lifetimes then we know that impl1 applies for any choice of lifetimes.

Basic approach

To enforce this requirement on specializations we take the following approach:

  1. Match up the substs for impl2 so that the implemented trait and self-type match those for impl1.
  2. Check for any direct use of 'static in the substs of impl2.
  3. Check that all of the generic parameters of impl1 occur at most once in the unconstrained substs for impl2. A parameter is constrained if its value is completely determined by an associated type projection predicate.
  4. Check that all predicates on impl1 either exist on impl2 (after matching substs), or are well-formed predicates for the trait’s type arguments.

Example

Suppose we have the following always applicable impl:

impl<T> SpecExtend<T> for std::vec::IntoIter<T> { /* specialized impl */ }
impl<T, I: Iterator<Item=T>> SpecExtend<T> for I { /* default impl */ }

We get that the subst for impl2 are [T, std::vec::IntoIter<T>]. T is constrained to be <I as Iterator>::Item, so we check only std::vec::IntoIter<T> for repeated parameters, which it doesn’t have. The predicates of impl1 are only T: Sized, which is also a predicate of impl2. So this specialization is sound.

Extensions

Unfortunately not all specializations in the standard library are allowed by this. So there are two extensions to these rules that allow specializing on some traits: that is, using them as bounds on the specializing impl, even when they don’t occur in the base impl.

rustc_specialization_trait

If a trait is always applicable, then it’s sound to specialize on it. We check trait is always applicable in the same way as impls, except that step 4 is now “all predicates on impl1 are always applicable”. We require that specialization or min_specialization is enabled to implement these traits.

rustc_unsafe_specialization_marker

There are also some specialization on traits with no methods, including the stable FusedIterator trait. We allow marking marker traits with an unstable attribute that means we ignore them in point 3 of the checks above. This is unsound, in the sense that the specialized impl may be used when it doesn’t apply, but we allow it in the short term since it can’t cause use after frees with purely safe code in the same way as specializing on traits with methods can.

Functions

Check that impl1 is a sound specialization
Check that the specializing impl impl1 is at least as const as the base impl impl2
Check that parameters of the derived impl don’t occur more than once in the equated substs of the base impl.
Check whether predicates on the specializing impl (impl1) are allowed.
Check that 'static lifetimes are not introduced by the specializing impl.
Given a specializing impl impl1, and the base impl impl2, returns two substitutions (S1, S2) that equate their trait references. The returned types are expressed in terms of the generics of impl1.
Checks if some predicate on the specializing impl (predicate1) is the same as some predicate on the base impl (predicate2).
Returns a list of all of the unconstrained subst of the given impl.