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
|
@ -1257,7 +1257,7 @@ impl<'tcx> InstantiatedPredicates<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, HashStable, TyEncodable, TyDecodable, Lift)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, HashStable, TyEncodable, TyDecodable, Lift)]
|
||||
#[derive(TypeFoldable, TypeVisitable)]
|
||||
pub struct OpaqueTypeKey<'tcx> {
|
||||
pub def_id: LocalDefId,
|
||||
|
@ -1332,6 +1332,9 @@ impl<'tcx> OpaqueHiddenType<'tcx> {
|
|||
let id_substs = InternalSubsts::identity_for_item(tcx, def_id.to_def_id());
|
||||
debug!(?id_substs);
|
||||
|
||||
// This zip may have several times the same lifetime in `substs` paired with a different
|
||||
// lifetime from `id_substs`. Simply `collect`ing the iterator is the correct behaviour:
|
||||
// it will pick the last one, which is the one we introduced in the impl-trait desugaring.
|
||||
let map = substs.iter().zip(id_substs);
|
||||
|
||||
let map: FxHashMap<GenericArg<'tcx>, GenericArg<'tcx>> = match origin {
|
||||
|
@ -1345,61 +1348,13 @@ impl<'tcx> OpaqueHiddenType<'tcx> {
|
|||
// type Foo<'a, 'b, 'c> = impl Trait<'a> + 'b;
|
||||
// ```
|
||||
// we may not use `'c` in the hidden type.
|
||||
struct OpaqueTypeLifetimeCollector<'tcx> {
|
||||
lifetimes: FxHashSet<ty::Region<'tcx>>,
|
||||
}
|
||||
let variances = tcx.variances_of(def_id);
|
||||
debug!(?variances);
|
||||
|
||||
impl<'tcx> ty::TypeVisitor<'tcx> for OpaqueTypeLifetimeCollector<'tcx> {
|
||||
fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
|
||||
self.lifetimes.insert(r);
|
||||
r.super_visit_with(self)
|
||||
}
|
||||
}
|
||||
|
||||
let mut collector = OpaqueTypeLifetimeCollector { lifetimes: Default::default() };
|
||||
|
||||
for pred in tcx.bound_explicit_item_bounds(def_id.to_def_id()).transpose_iter() {
|
||||
let pred = pred.map_bound(|(pred, _)| *pred).subst(tcx, id_substs);
|
||||
|
||||
trace!(pred=?pred.kind());
|
||||
|
||||
// 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(TraitPredicate {
|
||||
trait_ref: ty::TraitRef { def_id: _, substs },
|
||||
constness: _,
|
||||
polarity: _,
|
||||
}) => {
|
||||
trace!(?substs);
|
||||
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);
|
||||
}
|
||||
_ => {
|
||||
pred.visit_with(&mut collector);
|
||||
}
|
||||
}
|
||||
}
|
||||
let lifetimes = collector.lifetimes;
|
||||
trace!(?lifetimes);
|
||||
map.filter(|(_, v)| {
|
||||
let ty::GenericArgKind::Lifetime(lt) = v.unpack() else {
|
||||
return true;
|
||||
};
|
||||
lifetimes.contains(<)
|
||||
let ty::GenericArgKind::Lifetime(lt) = v.unpack() else { return true };
|
||||
let ty::ReEarlyBound(ebr) = lt.kind() else { bug!() };
|
||||
variances[ebr.index as usize] == ty::Variance::Invariant
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
|
|
@ -60,7 +60,7 @@ pub trait TypeRelation<'tcx>: Sized {
|
|||
|
||||
let tcx = self.tcx();
|
||||
let opt_variances = tcx.variances_of(item_def_id);
|
||||
relate_substs_with_variances(self, item_def_id, opt_variances, a_subst, b_subst)
|
||||
relate_substs_with_variances(self, item_def_id, opt_variances, a_subst, b_subst, true)
|
||||
}
|
||||
|
||||
/// Switch variance for the purpose of relating `a` and `b`.
|
||||
|
@ -151,13 +151,14 @@ pub fn relate_substs_with_variances<'tcx, R: TypeRelation<'tcx>>(
|
|||
variances: &[ty::Variance],
|
||||
a_subst: SubstsRef<'tcx>,
|
||||
b_subst: SubstsRef<'tcx>,
|
||||
fetch_ty_for_diag: bool,
|
||||
) -> RelateResult<'tcx, SubstsRef<'tcx>> {
|
||||
let tcx = relation.tcx();
|
||||
|
||||
let mut cached_ty = None;
|
||||
let params = iter::zip(a_subst, b_subst).enumerate().map(|(i, (a, b))| {
|
||||
let variance = variances[i];
|
||||
let variance_info = if variance == ty::Invariant {
|
||||
let variance_info = if variance == ty::Invariant && fetch_ty_for_diag {
|
||||
let ty =
|
||||
*cached_ty.get_or_insert_with(|| tcx.bound_type_of(ty_def_id).subst(tcx, a_subst));
|
||||
ty::VarianceDiagInfo::Invariant { ty, param_index: i.try_into().unwrap() }
|
||||
|
@ -561,7 +562,15 @@ pub fn super_relate_tys<'tcx, R: TypeRelation<'tcx>>(
|
|||
(&ty::Opaque(a_def_id, a_substs), &ty::Opaque(b_def_id, b_substs))
|
||||
if a_def_id == b_def_id =>
|
||||
{
|
||||
let substs = relate_substs(relation, a_substs, b_substs)?;
|
||||
let opt_variances = tcx.variances_of(a_def_id);
|
||||
let substs = relate_substs_with_variances(
|
||||
relation,
|
||||
a_def_id,
|
||||
opt_variances,
|
||||
a_substs,
|
||||
b_substs,
|
||||
false, // do not fetch `type_of(a_def_id)`, as it will cause a cycle
|
||||
)?;
|
||||
Ok(tcx.mk_opaque(a_def_id, substs))
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue