pub struct AutoTraitFinder<'tcx> {
    tcx: TyCtxt<'tcx>,
}

Fields§

§tcx: TyCtxt<'tcx>

Implementations§

Makes a best effort to determine whether and under which conditions an auto trait is implemented for a type. For example, if you have

struct Foo<T> { data: Box<T> }

then this might return that Foo<T>: Send if T: Send (encoded in the AutoTraitResult type). The analysis attempts to account for custom impls as well as other complex cases. This result is intended for use by rustdoc and other such consumers.

(Note that due to the coinductive nature of Send, the full and correct result is actually quite simple to generate. That is, when a type has no custom impl, it is Send iff its field types are all Send. So, in our example, we might have that Foo<T>: Send if Box<T>: Send. But this is often not the best way to present to the user.)

Warning: The API should be considered highly unstable, and it may be refactored or removed in the future.

The core logic responsible for computing the bounds for our synthesized impl.

To calculate the bounds, we call SelectionContext.select in a loop. Like FulfillmentContext, we recursively select the nested obligations of predicates we encounter. However, whenever we encounter an UnimplementedError involving a type parameter, we add it to our ParamEnv. Since our goal is to determine when a particular type implements an auto trait, Unimplemented errors tell us what conditions need to be met.

This method ends up working somewhat similarly to FulfillmentContext, but with a few key differences. FulfillmentContext works under the assumption that it’s dealing with concrete user code. According, it considers all possible ways that a Predicate could be met, which isn’t always what we want for a synthesized impl. For example, given the predicate T: Iterator, FulfillmentContext can end up reporting an Unimplemented error for T: IntoIterator – since there’s an implementation of Iterator where T: IntoIterator, FulfillmentContext will drive SelectionContext to consider that impl before giving up. If we were to rely on FulfillmentContexts decision, we might end up synthesizing an impl like this:

impl<T> Send for Foo<T> where T: IntoIterator

While it might be technically true that Foo implements Send where T: IntoIterator, the bound is overly restrictive - it’s really only necessary that T: Iterator.

For this reason, evaluate_predicates handles predicates with type variables specially. When we encounter an Unimplemented error for a bound such as T: Iterator, we immediately add it to our ParamEnv, and add it to our stack for recursive evaluation. When we later select it, we’ll pick up any nested bounds, without ever inferring that T: IntoIterator needs to hold.

One additional consideration is supertrait bounds. Normally, a ParamEnv is only ever constructed once for a given type. As part of the construction process, the ParamEnv will have any supertrait bounds normalized – e.g., if we have a type struct Foo<T: Copy>, the ParamEnv will contain T: Copy and T: Clone, since Copy: Clone. When we construct our own ParamEnv, we need to do this ourselves, through traits::elaborate_predicates, or else SelectionContext will choke on the missing predicates. However, this should never show up in the final synthesized generics: we don’t want our generated docs page to contain something like T: Copy + Clone, as that’s redundant. Therefore, we keep track of a separate user_env, which only holds the predicates that will actually be displayed to the user.

This method is designed to work around the following issue: When we compute auto trait bounds, we repeatedly call SelectionContext.select, progressively building a ParamEnv based on the results we get. However, our usage of SelectionContext differs from its normal use within the compiler, in that we capture and re-reprocess predicates from Unimplemented errors.

This can lead to a corner case when dealing with region parameters. During our selection loop in evaluate_predicates, we might end up with two trait predicates that differ only in their region parameters: one containing a HRTB lifetime parameter, and one containing a ‘normal’ lifetime parameter. For example:

T as MyTrait<'a>
T as MyTrait<'static>

If we put both of these predicates in our computed ParamEnv, we’ll confuse SelectionContext, since it will (correctly) view both as being applicable.

To solve this, we pick the ‘more strict’ lifetime bound – i.e., the HRTB Our end goal is to generate a user-visible description of the conditions under which a type implements an auto trait. A trait predicate involving a HRTB means that the type needs to work with any choice of lifetime, not just one specific lifetime (e.g., 'static).

This is very similar to handle_lifetimes. However, instead of matching ty::Regions to each other, we match ty::RegionVids to ty::Regions.

Auto Trait Implementations§

Blanket Implementations§

Gets the TypeId of self. Read more
Immutably borrows from an owned value. Read more
Mutably borrows from an owned value. Read more

Returns the argument unchanged.

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

The type returned in the event of a conversion error.
Performs the conversion.
The type returned in the event of a conversion error.
Performs the conversion.

Layout§

Note: Most layout information is completely unstable and may even differ between compilations. The only exception is types with certain repr(...) attributes. Please see the Rust Reference’s “Type Layout” chapter for details on type layout guarantees.

Size: 8 bytes