Struct rustc_trait_selection::traits::auto_trait::AutoTraitFinder
source · 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>,
orig_env: ParamEnv<'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>, orig_env: ParamEnv<'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.
source§impl<'tcx> AutoTraitFinder<'tcx>
impl<'tcx> AutoTraitFinder<'tcx>
sourcefn evaluate_predicates(
&self,
infcx: &InferCtxt<'tcx>,
trait_did: DefId,
ty: Ty<'tcx>,
param_env: ParamEnv<'tcx>,
user_env: ParamEnv<'tcx>,
fresh_preds: &mut FxHashSet<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 FxHashSet<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 FulfillmentContext
s 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
, 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>
) -> FxHashMap<RegionVid, Region<'cx>>
fn map_vid_to_region<'cx>( &self, regions: &RegionConstraintData<'cx> ) -> FxHashMap<RegionVid, Region<'cx>>
This is very similar to handle_lifetimes
. However, instead of matching ty::Region
s
to each other, we match ty::RegionVid
s to ty::Region
s.
fn is_param_no_infer(&self, args: GenericArgsRef<'_>) -> bool
pub fn is_of_param(&self, ty: Ty<'_>) -> bool
fn is_self_referential_projection(&self, p: PolyProjectionPredicate<'_>) -> bool
fn evaluate_nested_obligations( &self, ty: Ty<'_>, nested: impl Iterator<Item = PredicateObligation<'tcx>>, computed_preds: &mut FxIndexSet<Predicate<'tcx>>, fresh_preds: &mut FxHashSet<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> !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
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