Auto merge of #103491 - cjgillot:self-rpit, r=oli-obk
Support using `Self` or projections inside an RPIT/async fn I reuse the same idea as https://github.com/rust-lang/rust/pull/103449 to use variances to encode whether a lifetime parameter is captured by impl-trait. The current implementation of async and RPIT replace all lifetimes from the parent generics by `'static`. This PR changes the scheme ```rust impl<'a> Foo<'a> { fn foo<'b, T>() -> impl Into<Self> + 'b { ... } } opaque Foo::<'_a>::foo::<'_b, T>::opaque<'b>: Into<Foo<'_a>> + 'b; impl<'a> Foo<'a> { // OLD fn foo<'b, T>() -> Foo::<'static>::foo::<'static, T>::opaque::<'b> { ... } ^^^^^^^ the `Self` becomes `Foo<'static>` // NEW fn foo<'b, T>() -> Foo::<'a>::foo::<'b, T>::opaque::<'b> { ... } ^^ the `Self` stays `Foo<'a>` } ``` There is the same issue with projections. In the example, substitute `Self` by `<T as Trait<'b>>::Assoc` in the sugared version, and `Foo<'_a>` by `<T as Trait<'_b>>::Assoc` in the desugared one. This allows to support `Self` in impl-trait, since we do not replace lifetimes by `'static` any more. The same trick allows to use projections like `T::Assoc` where `Self` is allowed. The feature is gated behind a `impl_trait_projections` feature gate. The implementation relies on 2 tweaking rules for opaques in 2 places: - we only relate substs that correspond to captured lifetimes during TypeRelation; - we only list captured lifetimes in choice region computation. For simplicity, I encoded the "capturedness" of lifetimes as a variance, `Bivariant` vs `Invariant` for unused vs captured lifetimes. The `variances_of` query used to ICE for opaques. Impl-trait that do not reference `Self` or projections will have their variances as: - `o` (invariant) for each parent type or const; - `*` (bivariant) for each parent lifetime --> will not participate in borrowck; - `o` (invariant) for each own lifetime. Impl-trait that does reference `Self` and/or projections will have some parent lifetimes marked as `o` (as the example above), and participate in type relation and borrowck. In the example above, `variances_of(opaque) = ['_a: o, '_b: *, T: o, 'b: o]`. r? types cc `@compiler-errors` , as you asked about the issue with `Self` and projections.
This commit is contained in:
commit
7fe6f36224
33 changed files with 570 additions and 341 deletions
|
@ -2777,35 +2777,11 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
let substs = InternalSubsts::for_item(tcx, def_id, |param, _| {
|
||||
if let Some(i) = (param.index as usize).checked_sub(generics.parent_count) {
|
||||
// Our own parameters are the resolved lifetimes.
|
||||
if let GenericParamDefKind::Lifetime = param.kind {
|
||||
if let hir::GenericArg::Lifetime(lifetime) = &lifetimes[i] {
|
||||
self.ast_region_to_region(lifetime, None).into()
|
||||
} else {
|
||||
bug!()
|
||||
}
|
||||
} else {
|
||||
bug!()
|
||||
}
|
||||
let GenericParamDefKind::Lifetime { .. } = param.kind else { bug!() };
|
||||
let hir::GenericArg::Lifetime(lifetime) = &lifetimes[i] else { bug!() };
|
||||
self.ast_region_to_region(lifetime, None).into()
|
||||
} else {
|
||||
match param.kind {
|
||||
// For RPIT (return position impl trait), only lifetimes
|
||||
// mentioned in the impl Trait predicate are captured by
|
||||
// the opaque type, so the lifetime parameters from the
|
||||
// parent item need to be replaced with `'static`.
|
||||
//
|
||||
// For `impl Trait` in the types of statics, constants,
|
||||
// locals and type aliases. These capture all parent
|
||||
// lifetimes, so they can use their identity subst.
|
||||
GenericParamDefKind::Lifetime
|
||||
if matches!(
|
||||
origin,
|
||||
hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..)
|
||||
) =>
|
||||
{
|
||||
tcx.lifetimes.re_static.into()
|
||||
}
|
||||
_ => tcx.mk_param_from_def(param),
|
||||
}
|
||||
tcx.mk_param_from_def(param)
|
||||
}
|
||||
});
|
||||
debug!("impl_trait_ty_to_ty: substs={:?}", substs);
|
||||
|
@ -2982,6 +2958,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
Some(tcx.liberate_late_bound_regions(fn_hir_id.expect_owner().to_def_id(), ty))
|
||||
}
|
||||
|
||||
#[instrument(level = "trace", skip(self, generate_err))]
|
||||
fn validate_late_bound_regions(
|
||||
&self,
|
||||
constrained_regions: FxHashSet<ty::BoundRegionKind>,
|
||||
|
|
|
@ -10,6 +10,7 @@ use rustc_hir::def::{CtorKind, DefKind, Res};
|
|||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_hir::intravisit::Visitor;
|
||||
use rustc_hir::{ItemKind, Node, PathSegment};
|
||||
use rustc_infer::infer::opaque_types::ConstrainOpaqueTypeRegionVisitor;
|
||||
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
|
||||
use rustc_infer::infer::{DefiningAnchor, RegionVariableOrigin, TyCtxtInferExt};
|
||||
use rustc_infer::traits::Obligation;
|
||||
|
@ -229,7 +230,9 @@ fn check_opaque<'tcx>(tcx: TyCtxt<'tcx>, id: hir::ItemId) {
|
|||
let substs = InternalSubsts::identity_for_item(tcx, item.owner_id.to_def_id());
|
||||
let span = tcx.def_span(item.owner_id.def_id);
|
||||
|
||||
check_opaque_for_inheriting_lifetimes(tcx, item.owner_id.def_id, span);
|
||||
if !tcx.features().impl_trait_projections {
|
||||
check_opaque_for_inheriting_lifetimes(tcx, item.owner_id.def_id, span);
|
||||
}
|
||||
if tcx.type_of(item.owner_id.def_id).references_error() {
|
||||
return;
|
||||
}
|
||||
|
@ -238,6 +241,7 @@ fn check_opaque<'tcx>(tcx: TyCtxt<'tcx>, id: hir::ItemId) {
|
|||
}
|
||||
check_opaque_meets_bounds(tcx, item.owner_id.def_id, substs, span, &origin);
|
||||
}
|
||||
|
||||
/// Checks that an opaque type does not use `Self` or `T::Foo` projections that would result
|
||||
/// in "inheriting lifetimes".
|
||||
#[instrument(level = "debug", skip(tcx, span))]
|
||||
|
@ -249,39 +253,11 @@ pub(super) fn check_opaque_for_inheriting_lifetimes<'tcx>(
|
|||
let item = tcx.hir().expect_item(def_id);
|
||||
debug!(?item, ?span);
|
||||
|
||||
struct FoundParentLifetime;
|
||||
struct FindParentLifetimeVisitor<'tcx>(&'tcx ty::Generics);
|
||||
impl<'tcx> ty::visit::TypeVisitor<'tcx> for FindParentLifetimeVisitor<'tcx> {
|
||||
type BreakTy = FoundParentLifetime;
|
||||
|
||||
fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
|
||||
debug!("FindParentLifetimeVisitor: r={:?}", r);
|
||||
if let ty::ReEarlyBound(ty::EarlyBoundRegion { index, .. }) = *r {
|
||||
if index < self.0.parent_count as u32 {
|
||||
return ControlFlow::Break(FoundParentLifetime);
|
||||
} else {
|
||||
return ControlFlow::CONTINUE;
|
||||
}
|
||||
}
|
||||
|
||||
r.super_visit_with(self)
|
||||
}
|
||||
|
||||
fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
|
||||
if let ty::ConstKind::Unevaluated(..) = c.kind() {
|
||||
// FIXME(#72219) We currently don't detect lifetimes within substs
|
||||
// which would violate this check. Even though the particular substitution is not used
|
||||
// within the const, this should still be fixed.
|
||||
return ControlFlow::CONTINUE;
|
||||
}
|
||||
c.super_visit_with(self)
|
||||
}
|
||||
}
|
||||
|
||||
struct ProhibitOpaqueVisitor<'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
opaque_identity_ty: Ty<'tcx>,
|
||||
generics: &'tcx ty::Generics,
|
||||
parent_count: u32,
|
||||
references_parent_regions: bool,
|
||||
selftys: Vec<(Span, Option<String>)>,
|
||||
}
|
||||
|
||||
|
@ -289,12 +265,25 @@ pub(super) fn check_opaque_for_inheriting_lifetimes<'tcx>(
|
|||
type BreakTy = Ty<'tcx>;
|
||||
|
||||
fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
|
||||
debug!("check_opaque_for_inheriting_lifetimes: (visit_ty) t={:?}", t);
|
||||
debug!(?t, "root_visit_ty");
|
||||
if t == self.opaque_identity_ty {
|
||||
ControlFlow::CONTINUE
|
||||
} else {
|
||||
t.super_visit_with(&mut FindParentLifetimeVisitor(self.generics))
|
||||
.map_break(|FoundParentLifetime| t)
|
||||
t.visit_with(&mut ConstrainOpaqueTypeRegionVisitor {
|
||||
tcx: self.tcx,
|
||||
op: |region| {
|
||||
if let ty::ReEarlyBound(ty::EarlyBoundRegion { index, .. }) = *region
|
||||
&& index < self.parent_count
|
||||
{
|
||||
self.references_parent_regions= true;
|
||||
}
|
||||
},
|
||||
});
|
||||
if self.references_parent_regions {
|
||||
ControlFlow::Break(t)
|
||||
} else {
|
||||
ControlFlow::CONTINUE
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -327,15 +316,20 @@ pub(super) fn check_opaque_for_inheriting_lifetimes<'tcx>(
|
|||
|
||||
if let ItemKind::OpaqueTy(hir::OpaqueTy {
|
||||
origin: hir::OpaqueTyOrigin::AsyncFn(..) | hir::OpaqueTyOrigin::FnReturn(..),
|
||||
in_trait,
|
||||
..
|
||||
}) = item.kind
|
||||
{
|
||||
let substs = InternalSubsts::identity_for_item(tcx, def_id.to_def_id());
|
||||
let opaque_identity_ty = if in_trait {
|
||||
tcx.mk_projection(def_id.to_def_id(), substs)
|
||||
} else {
|
||||
tcx.mk_opaque(def_id.to_def_id(), substs)
|
||||
};
|
||||
let mut visitor = ProhibitOpaqueVisitor {
|
||||
opaque_identity_ty: tcx.mk_opaque(
|
||||
def_id.to_def_id(),
|
||||
InternalSubsts::identity_for_item(tcx, def_id.to_def_id()),
|
||||
),
|
||||
generics: tcx.generics_of(def_id),
|
||||
opaque_identity_ty,
|
||||
parent_count: tcx.generics_of(def_id).parent_count as u32,
|
||||
references_parent_regions: false,
|
||||
tcx,
|
||||
selftys: vec![],
|
||||
};
|
||||
|
@ -343,10 +337,6 @@ pub(super) fn check_opaque_for_inheriting_lifetimes<'tcx>(
|
|||
.explicit_item_bounds(def_id)
|
||||
.iter()
|
||||
.try_for_each(|(predicate, _)| predicate.visit_with(&mut visitor));
|
||||
debug!(
|
||||
"check_opaque_for_inheriting_lifetimes: prohibit_opaque={:?}, visitor.opaque_identity_ty={:?}, visitor.generics={:?}",
|
||||
prohibit_opaque, visitor.opaque_identity_ty, visitor.generics
|
||||
);
|
||||
|
||||
if let Some(ty) = prohibit_opaque.break_value() {
|
||||
visitor.visit_item(&item);
|
||||
|
@ -357,15 +347,16 @@ pub(super) fn check_opaque_for_inheriting_lifetimes<'tcx>(
|
|||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let mut err = struct_span_err!(
|
||||
tcx.sess,
|
||||
let mut err = feature_err(
|
||||
&tcx.sess.parse_sess,
|
||||
sym::impl_trait_projections,
|
||||
span,
|
||||
E0760,
|
||||
"`{}` return type cannot contain a projection or `Self` that references lifetimes from \
|
||||
a parent scope",
|
||||
if is_async { "async fn" } else { "impl Trait" },
|
||||
&format!(
|
||||
"`{}` return type cannot contain a projection or `Self` that references \
|
||||
lifetimes from a parent scope",
|
||||
if is_async { "async fn" } else { "impl Trait" },
|
||||
),
|
||||
);
|
||||
|
||||
for (span, name) in visitor.selftys {
|
||||
err.span_suggestion(
|
||||
span,
|
||||
|
|
|
@ -1539,7 +1539,6 @@ fn check_fn_or_method<'tcx>(
|
|||
|
||||
check_return_position_impl_trait_in_trait_bounds(
|
||||
tcx,
|
||||
wfcx,
|
||||
def_id,
|
||||
sig.output(),
|
||||
hir_decl.output.span(),
|
||||
|
@ -1575,9 +1574,9 @@ fn check_fn_or_method<'tcx>(
|
|||
|
||||
/// Basically `check_associated_type_bounds`, but separated for now and should be
|
||||
/// deduplicated when RPITITs get lowered into real associated items.
|
||||
#[tracing::instrument(level = "trace", skip(tcx))]
|
||||
fn check_return_position_impl_trait_in_trait_bounds<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
wfcx: &WfCheckingCtxt<'_, 'tcx>,
|
||||
fn_def_id: LocalDefId,
|
||||
fn_output: Ty<'tcx>,
|
||||
span: Span,
|
||||
|
@ -1591,18 +1590,22 @@ fn check_return_position_impl_trait_in_trait_bounds<'tcx>(
|
|||
&& tcx.def_kind(proj.item_def_id) == DefKind::ImplTraitPlaceholder
|
||||
&& tcx.impl_trait_in_trait_parent(proj.item_def_id) == fn_def_id.to_def_id()
|
||||
{
|
||||
let bounds = wfcx.tcx().explicit_item_bounds(proj.item_def_id);
|
||||
let wf_obligations = bounds.iter().flat_map(|&(bound, bound_span)| {
|
||||
let normalized_bound = wfcx.normalize(span, None, bound);
|
||||
traits::wf::predicate_obligations(
|
||||
wfcx.infcx,
|
||||
wfcx.param_env,
|
||||
wfcx.body_id,
|
||||
normalized_bound,
|
||||
bound_span,
|
||||
)
|
||||
// Create a new context, since we want the opaque's ParamEnv and not the parent's.
|
||||
let span = tcx.def_span(proj.item_def_id);
|
||||
enter_wf_checking_ctxt(tcx, span, proj.item_def_id.expect_local(), |wfcx| {
|
||||
let bounds = wfcx.tcx().explicit_item_bounds(proj.item_def_id);
|
||||
let wf_obligations = bounds.iter().flat_map(|&(bound, bound_span)| {
|
||||
let normalized_bound = wfcx.normalize(span, None, bound);
|
||||
traits::wf::predicate_obligations(
|
||||
wfcx.infcx,
|
||||
wfcx.param_env,
|
||||
wfcx.body_id,
|
||||
normalized_bound,
|
||||
bound_span,
|
||||
)
|
||||
});
|
||||
wfcx.register_obligations(wf_obligations);
|
||||
});
|
||||
wfcx.register_obligations(wf_obligations);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -84,60 +84,30 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP
|
|||
|
||||
Node::ImplItem(item) => item.generics,
|
||||
|
||||
Node::Item(item) => {
|
||||
match item.kind {
|
||||
ItemKind::Impl(ref impl_) => {
|
||||
if impl_.defaultness.is_default() {
|
||||
is_default_impl_trait = tcx.impl_trait_ref(def_id).map(ty::Binder::dummy);
|
||||
}
|
||||
&impl_.generics
|
||||
Node::Item(item) => match item.kind {
|
||||
ItemKind::Impl(ref impl_) => {
|
||||
if impl_.defaultness.is_default() {
|
||||
is_default_impl_trait = tcx.impl_trait_ref(def_id).map(ty::Binder::dummy);
|
||||
}
|
||||
ItemKind::Fn(.., ref generics, _)
|
||||
| ItemKind::TyAlias(_, ref generics)
|
||||
| ItemKind::Enum(_, ref generics)
|
||||
| ItemKind::Struct(_, ref generics)
|
||||
| ItemKind::Union(_, ref generics) => *generics,
|
||||
|
||||
ItemKind::Trait(_, _, ref generics, ..) => {
|
||||
is_trait = Some(ty::TraitRef::identity(tcx, def_id));
|
||||
*generics
|
||||
}
|
||||
ItemKind::TraitAlias(ref generics, _) => {
|
||||
is_trait = Some(ty::TraitRef::identity(tcx, def_id));
|
||||
*generics
|
||||
}
|
||||
ItemKind::OpaqueTy(OpaqueTy {
|
||||
origin: hir::OpaqueTyOrigin::AsyncFn(..) | hir::OpaqueTyOrigin::FnReturn(..),
|
||||
..
|
||||
}) => {
|
||||
// return-position impl trait
|
||||
//
|
||||
// We don't inherit predicates from the parent here:
|
||||
// If we have, say `fn f<'a, T: 'a>() -> impl Sized {}`
|
||||
// then the return type is `f::<'static, T>::{{opaque}}`.
|
||||
//
|
||||
// If we inherited the predicates of `f` then we would
|
||||
// require that `T: 'static` to show that the return
|
||||
// type is well-formed.
|
||||
//
|
||||
// The only way to have something with this opaque type
|
||||
// is from the return type of the containing function,
|
||||
// which will ensure that the function's predicates
|
||||
// hold.
|
||||
return ty::GenericPredicates { parent: None, predicates: &[] };
|
||||
}
|
||||
ItemKind::OpaqueTy(OpaqueTy {
|
||||
ref generics,
|
||||
origin: hir::OpaqueTyOrigin::TyAlias,
|
||||
..
|
||||
}) => {
|
||||
// type-alias impl trait
|
||||
generics
|
||||
}
|
||||
|
||||
_ => NO_GENERICS,
|
||||
&impl_.generics
|
||||
}
|
||||
}
|
||||
ItemKind::Fn(.., ref generics, _)
|
||||
| ItemKind::TyAlias(_, ref generics)
|
||||
| ItemKind::Enum(_, ref generics)
|
||||
| ItemKind::Struct(_, ref generics)
|
||||
| ItemKind::Union(_, ref generics) => *generics,
|
||||
|
||||
ItemKind::Trait(_, _, ref generics, ..) => {
|
||||
is_trait = Some(ty::TraitRef::identity(tcx, def_id));
|
||||
*generics
|
||||
}
|
||||
ItemKind::TraitAlias(ref generics, _) => {
|
||||
is_trait = Some(ty::TraitRef::identity(tcx, def_id));
|
||||
*generics
|
||||
}
|
||||
ItemKind::OpaqueTy(OpaqueTy { ref generics, .. }) => generics,
|
||||
_ => NO_GENERICS,
|
||||
},
|
||||
|
||||
Node::ForeignItem(item) => match item.kind {
|
||||
ForeignItemKind::Static(..) => NO_GENERICS,
|
||||
|
@ -181,6 +151,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP
|
|||
|
||||
trace!(?predicates);
|
||||
trace!(?ast_generics);
|
||||
trace!(?generics);
|
||||
|
||||
// Collect the predicates that were written inline by the user on each
|
||||
// type parameter (e.g., `<T: Foo>`).
|
||||
|
@ -299,6 +270,54 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP
|
|||
);
|
||||
}
|
||||
|
||||
// Opaque types duplicate some of their generic parameters.
|
||||
// We create bi-directional Outlives predicates between the original
|
||||
// and the duplicated parameter, to ensure that they do not get out of sync.
|
||||
if let Node::Item(&Item { kind: ItemKind::OpaqueTy(..), .. }) = node {
|
||||
let opaque_ty_id = tcx.hir().get_parent_node(hir_id);
|
||||
let opaque_ty_node = tcx.hir().get(opaque_ty_id);
|
||||
let Node::Ty(&Ty { kind: TyKind::OpaqueDef(_, lifetimes, _), .. }) = opaque_ty_node else {
|
||||
bug!("unexpected {opaque_ty_node:?}")
|
||||
};
|
||||
debug!(?lifetimes);
|
||||
for (arg, duplicate) in std::iter::zip(lifetimes, ast_generics.params) {
|
||||
let hir::GenericArg::Lifetime(arg) = arg else { bug!() };
|
||||
let orig_region = <dyn AstConv<'_>>::ast_region_to_region(&icx, &arg, None);
|
||||
if !matches!(orig_region.kind(), ty::ReEarlyBound(..)) {
|
||||
// Only early-bound regions can point to the original generic parameter.
|
||||
continue;
|
||||
}
|
||||
|
||||
let hir::GenericParamKind::Lifetime { .. } = duplicate.kind else { continue };
|
||||
let dup_def = tcx.hir().local_def_id(duplicate.hir_id).to_def_id();
|
||||
|
||||
let Some(dup_index) = generics.param_def_id_to_index(tcx, dup_def) else { bug!() };
|
||||
|
||||
let dup_region = tcx.mk_region(ty::ReEarlyBound(ty::EarlyBoundRegion {
|
||||
def_id: dup_def,
|
||||
index: dup_index,
|
||||
name: duplicate.name.ident().name,
|
||||
}));
|
||||
predicates.push((
|
||||
ty::Binder::dummy(ty::PredicateKind::RegionOutlives(ty::OutlivesPredicate(
|
||||
orig_region,
|
||||
dup_region,
|
||||
)))
|
||||
.to_predicate(icx.tcx),
|
||||
duplicate.span,
|
||||
));
|
||||
predicates.push((
|
||||
ty::Binder::dummy(ty::PredicateKind::RegionOutlives(ty::OutlivesPredicate(
|
||||
dup_region,
|
||||
orig_region,
|
||||
)))
|
||||
.to_predicate(icx.tcx),
|
||||
duplicate.span,
|
||||
));
|
||||
}
|
||||
debug!(?predicates);
|
||||
}
|
||||
|
||||
ty::GenericPredicates {
|
||||
parent: generics.parent,
|
||||
predicates: tcx.arena.alloc_from_iter(predicates),
|
||||
|
|
|
@ -5,9 +5,10 @@
|
|||
|
||||
use rustc_arena::DroplessArena;
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_middle::ty::query::Providers;
|
||||
use rustc_middle::ty::{self, CrateVariancesMap, TyCtxt};
|
||||
use rustc_middle::ty::{self, CrateVariancesMap, TyCtxt, TypeSuperVisitable, TypeVisitable};
|
||||
use std::ops::ControlFlow;
|
||||
|
||||
/// Defines the `TermsContext` basically houses an arena where we can
|
||||
/// allocate terms.
|
||||
|
@ -50,6 +51,9 @@ fn variances_of(tcx: TyCtxt<'_>, item_def_id: DefId) -> &[ty::Variance] {
|
|||
| DefKind::Union
|
||||
| DefKind::Variant
|
||||
| DefKind::Ctor(..) => {}
|
||||
DefKind::OpaqueTy | DefKind::ImplTraitPlaceholder => {
|
||||
return variance_of_opaque(tcx, item_def_id.expect_local());
|
||||
}
|
||||
_ => {
|
||||
// Variance not relevant.
|
||||
span_bug!(tcx.def_span(item_def_id), "asked to compute variance for wrong kind of item")
|
||||
|
@ -61,3 +65,89 @@ fn variances_of(tcx: TyCtxt<'_>, item_def_id: DefId) -> &[ty::Variance] {
|
|||
let crate_map = tcx.crate_variances(());
|
||||
crate_map.variances.get(&item_def_id).copied().unwrap_or(&[])
|
||||
}
|
||||
|
||||
#[instrument(level = "trace", skip(tcx), ret)]
|
||||
fn variance_of_opaque(tcx: TyCtxt<'_>, item_def_id: LocalDefId) -> &[ty::Variance] {
|
||||
let generics = tcx.generics_of(item_def_id);
|
||||
|
||||
// Opaque types may only use regions that are bound. So for
|
||||
// ```rust
|
||||
// type Foo<'a, 'b, 'c> = impl Trait<'a> + 'b;
|
||||
// ```
|
||||
// we may not use `'c` in the hidden type.
|
||||
struct OpaqueTypeLifetimeCollector {
|
||||
variances: Vec<ty::Variance>,
|
||||
}
|
||||
|
||||
impl<'tcx> ty::TypeVisitor<'tcx> for OpaqueTypeLifetimeCollector {
|
||||
#[instrument(level = "trace", skip(self), ret)]
|
||||
fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
|
||||
if let ty::RegionKind::ReEarlyBound(ebr) = r.kind() {
|
||||
self.variances[ebr.index as usize] = ty::Invariant;
|
||||
}
|
||||
r.super_visit_with(self)
|
||||
}
|
||||
}
|
||||
|
||||
// By default, RPIT are invariant wrt type and const generics, but they are bivariant wrt
|
||||
// lifetime generics.
|
||||
let mut variances: Vec<_> = std::iter::repeat(ty::Invariant).take(generics.count()).collect();
|
||||
|
||||
// Mark all lifetimes from parent generics as unused (Bivariant).
|
||||
// This will be overridden later if required.
|
||||
{
|
||||
let mut generics = generics;
|
||||
while let Some(def_id) = generics.parent {
|
||||
generics = tcx.generics_of(def_id);
|
||||
for param in &generics.params {
|
||||
match param.kind {
|
||||
ty::GenericParamDefKind::Lifetime => {
|
||||
variances[param.index as usize] = ty::Bivariant;
|
||||
}
|
||||
ty::GenericParamDefKind::Type { .. }
|
||||
| ty::GenericParamDefKind::Const { .. } => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut collector = OpaqueTypeLifetimeCollector { variances };
|
||||
let id_substs = ty::InternalSubsts::identity_for_item(tcx, item_def_id.to_def_id());
|
||||
for pred in tcx.bound_explicit_item_bounds(item_def_id.to_def_id()).transpose_iter() {
|
||||
let pred = pred.map_bound(|(pred, _)| *pred).subst(tcx, id_substs);
|
||||
debug!(?pred);
|
||||
|
||||
// We only ignore opaque type substs if the opaque type is the outermost type.
|
||||
// The opaque type may be nested within itself via recursion in e.g.
|
||||
// type Foo<'a> = impl PartialEq<Foo<'a>>;
|
||||
// which thus mentions `'a` and should thus accept hidden types that borrow 'a
|
||||
// instead of requiring an additional `+ 'a`.
|
||||
match pred.kind().skip_binder() {
|
||||
ty::PredicateKind::Trait(ty::TraitPredicate {
|
||||
trait_ref: ty::TraitRef { def_id: _, substs },
|
||||
constness: _,
|
||||
polarity: _,
|
||||
}) => {
|
||||
for subst in &substs[1..] {
|
||||
subst.visit_with(&mut collector);
|
||||
}
|
||||
}
|
||||
ty::PredicateKind::Projection(ty::ProjectionPredicate {
|
||||
projection_ty: ty::ProjectionTy { substs, item_def_id: _ },
|
||||
term,
|
||||
}) => {
|
||||
for subst in &substs[1..] {
|
||||
subst.visit_with(&mut collector);
|
||||
}
|
||||
term.visit_with(&mut collector);
|
||||
}
|
||||
ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(_, region)) => {
|
||||
region.visit_with(&mut collector);
|
||||
}
|
||||
_ => {
|
||||
pred.visit_with(&mut collector);
|
||||
}
|
||||
}
|
||||
}
|
||||
tcx.arena.alloc_from_iter(collector.variances.into_iter())
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue