pub struct AutoTraitFinder<'tcx> {
tcx: TyCtxt<'tcx>,
}Fields§
§tcx: TyCtxt<'tcx>Implementations§
Source§impl<'tcx> AutoTraitFinder<'tcx>
impl<'tcx> AutoTraitFinder<'tcx>
pub fn new(tcx: TyCtxt<'tcx>) -> Self
Sourcepub fn find_auto_trait_generics<A>(
&self,
ty: Ty<'tcx>,
typing_env: TypingEnv<'tcx>,
trait_did: DefId,
auto_trait_callback: impl FnMut(AutoTraitInfo<'tcx>) -> A,
) -> AutoTraitResult<A>
pub fn find_auto_trait_generics<A>( &self, ty: Ty<'tcx>, typing_env: TypingEnv<'tcx>, trait_did: DefId, auto_trait_callback: impl FnMut(AutoTraitInfo<'tcx>) -> A, ) -> AutoTraitResult<A>
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.
Sourcefn evaluate_predicates(
&self,
infcx: &InferCtxt<'tcx>,
trait_did: DefId,
ty: Ty<'tcx>,
param_env: ParamEnv<'tcx>,
user_env: ParamEnv<'tcx>,
fresh_preds: &mut FxIndexSet<Predicate<'tcx>>,
) -> Option<(ParamEnv<'tcx>, ParamEnv<'tcx>)>
fn evaluate_predicates( &self, infcx: &InferCtxt<'tcx>, trait_did: DefId, ty: Ty<'tcx>, param_env: ParamEnv<'tcx>, user_env: ParamEnv<'tcx>, fresh_preds: &mut FxIndexSet<Predicate<'tcx>>, ) -> Option<(ParamEnv<'tcx>, ParamEnv<'tcx>)>
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: IntoIteratorWhile 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, 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.
Sourcefn add_user_pred(
&self,
user_computed_preds: &mut FxIndexSet<Predicate<'tcx>>,
new_pred: Predicate<'tcx>,
)
fn add_user_pred( &self, user_computed_preds: &mut FxIndexSet<Predicate<'tcx>>, new_pred: Predicate<'tcx>, )
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).
Sourcefn map_vid_to_region<'cx>(
&self,
regions: &RegionConstraintData<'cx>,
) -> FxIndexMap<RegionVid, Region<'cx>>
fn map_vid_to_region<'cx>( &self, regions: &RegionConstraintData<'cx>, ) -> FxIndexMap<RegionVid, Region<'cx>>
This is very similar to handle_lifetimes. However, instead of matching ty::Regions
to each other, we match ty::RegionVids to ty::Regions.
fn is_param_no_infer(&self, args: GenericArgsRef<'tcx>) -> bool
pub fn is_of_param(&self, ty: Ty<'tcx>) -> bool
fn is_self_referential_projection( &self, p: PolyProjectionPredicate<'tcx>, ) -> bool
fn evaluate_nested_obligations( &self, ty: Ty<'_>, nested: impl Iterator<Item = PredicateObligation<'tcx>>, computed_preds: &mut FxIndexSet<Predicate<'tcx>>, fresh_preds: &mut FxIndexSet<Predicate<'tcx>>, predicates: &mut VecDeque<PolyTraitPredicate<'tcx>>, selcx: &mut SelectionContext<'_, 'tcx>, ) -> bool
pub fn clean_pred( &self, infcx: &InferCtxt<'tcx>, p: Predicate<'tcx>, ) -> Predicate<'tcx>
Auto Trait Implementations§
impl<'tcx> DynSend for AutoTraitFinder<'tcx>
impl<'tcx> DynSync for AutoTraitFinder<'tcx>
impl<'tcx> Freeze for AutoTraitFinder<'tcx>
impl<'tcx> !RefUnwindSafe for AutoTraitFinder<'tcx>
impl<'tcx> !Send for AutoTraitFinder<'tcx>
impl<'tcx> !Sync for AutoTraitFinder<'tcx>
impl<'tcx> Unpin for AutoTraitFinder<'tcx>
impl<'tcx> !UnwindSafe for AutoTraitFinder<'tcx>
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T, R> CollectAndApply<T, R> for T
impl<T, R> CollectAndApply<T, R> for T
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§impl<P> IntoQueryParam<P> for P
impl<P> IntoQueryParam<P> for P
fn into_query_param(self) -> P
Source§impl<T> MaybeResult<T> for T
impl<T> MaybeResult<T> for T
Source§impl<T> Pointable for T
impl<T> Pointable for T
Source§impl<I, T, U> Upcast<I, U> for Twhere
U: UpcastFrom<I, T>,
impl<I, T, U> Upcast<I, U> for Twhere
U: UpcastFrom<I, T>,
Source§impl<I, T> UpcastFrom<I, T> for T
impl<I, T> UpcastFrom<I, T> for T
fn upcast_from(from: T, _tcx: I) -> T
Source§impl<Tcx, T> Value<Tcx> for Twhere
Tcx: DepContext,
impl<Tcx, T> Value<Tcx> for Twhere
Tcx: DepContext,
default fn from_cycle_error( tcx: Tcx, cycle_error: &CycleError, _guar: ErrorGuaranteed, ) -> T
Source§impl<T> WithSubscriber for T
impl<T> WithSubscriber for T
Source§fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
Source§fn with_current_subscriber(self) -> WithDispatch<Self>
fn with_current_subscriber(self) -> WithDispatch<Self>
impl<T> ErasedDestructor for Twhere
T: 'static,
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