From 4925b57782bafc2ae26568154b5f085d75b5792c Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Wed, 21 Jun 2023 19:09:18 -0300 Subject: [PATCH] Add bidirectional where clauses on RPITIT synthesized GATs --- compiler/rustc_ast_lowering/src/lib.rs | 50 +++++++++------- compiler/rustc_hir/src/hir.rs | 4 ++ .../src/collect/predicates_of.rs | 58 ++++++++++++++++++- compiler/rustc_ty_utils/src/assoc.rs | 12 ---- .../in-trait/async-lifetimes-and-bounds.rs | 2 + .../async-await/in-trait/async-lifetimes.rs | 2 + 6 files changed, 92 insertions(+), 36 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 78e5d5e8f40..84fd99e399c 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -1568,14 +1568,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // This creates HIR lifetime arguments as `hir::GenericArg`, in the given example `type // TestReturn<'a, T, 'x> = impl Debug + 'x`, it creates a collection containing `&['x]`. - let lifetimes: Vec<_> = collected_lifetimes + let lifetime_mapping: Vec<_> = collected_lifetimes .iter() - .map(|(_, lifetime)| { + .map(|(node_id, lifetime)| { let id = self.next_node_id(); - self.new_named_lifetime(lifetime.id, id, lifetime.ident) + let lifetime = self.new_named_lifetime(lifetime.id, id, lifetime.ident); + let def_id = self.local_def_id(*node_id); + (lifetime, def_id) }) .collect(); - debug!(?lifetimes); + debug!(?lifetime_mapping); self.with_hir_id_owner(opaque_ty_node_id, |lctx| { // Install the remapping from old to new (if any): @@ -1626,6 +1628,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { }), bounds: hir_bounds, origin, + lifetime_mapping: self.arena.alloc_from_iter( + lifetime_mapping.iter().map(|(lifetime, def_id)| (**lifetime, *def_id)), + ), in_trait, }; debug!(?opaque_ty_item); @@ -1634,17 +1639,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { }) }); - // This creates HIR lifetime arguments as `hir::GenericArg`, in the given example `type - // TestReturn<'a, T, 'x> = impl Debug + 'x`, it creates a collection containing `&['x]`. - let lifetimes = self.arena.alloc_from_iter( - lifetimes.into_iter().map(|lifetime| hir::GenericArg::Lifetime(lifetime)), - ); - debug!(?lifetimes); - // `impl Trait` now just becomes `Foo<'a, 'b, ..>`. hir::TyKind::OpaqueDef( hir::ItemId { owner_id: hir::OwnerId { def_id: opaque_ty_def_id } }, - lifetimes, + self.arena.alloc_from_iter( + lifetime_mapping.iter().map(|(lifetime, _)| hir::GenericArg::Lifetime(*lifetime)), + ), in_trait, ) } @@ -1986,7 +1986,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let lifetime = Lifetime { id: outer_node_id, ident }; collected_lifetimes.push((inner_node_id, lifetime, Some(inner_res))); } - debug!(?collected_lifetimes); // We only want to capture the lifetimes that appear in the bounds. So visit the bounds to @@ -2007,19 +2006,23 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { debug!(?collected_lifetimes); debug!(?new_remapping); - // This creates HIR lifetime arguments as `hir::GenericArg`, in the given example `type - // TestReturn<'a, T, 'x> = impl Debug + 'x`, it creates a collection containing `&['x]`. - let lifetimes: Vec<_> = collected_lifetimes + // This creates pairs of HIR lifetimes and def_ids. In the given example `type + // TestReturn<'a, T, 'x> = impl Debug + 'x`, it creates a collection containing the + // new lifetime of the RPIT 'x and the def_id of the lifetime 'x corresponding to + // `TestReturn`. + let lifetime_mapping: Vec<_> = collected_lifetimes .iter() - .map(|(_, lifetime, res)| { + .map(|(node_id, lifetime, res)| { let id = self.next_node_id(); let res = res.unwrap_or( self.resolver.get_lifetime_res(lifetime.id).unwrap_or(LifetimeRes::Error), ); - self.new_named_lifetime_with_res(id, lifetime.ident, res) + let lifetime = self.new_named_lifetime_with_res(id, lifetime.ident, res); + let def_id = self.local_def_id(*node_id); + (lifetime, def_id) }) .collect(); - debug!(?lifetimes); + debug!(?lifetime_mapping); self.with_hir_id_owner(opaque_ty_node_id, |this| { // Install the remapping from old to new (if any): @@ -2086,6 +2089,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { }), bounds: arena_vec![this; future_bound], origin: hir::OpaqueTyOrigin::AsyncFn(fn_def_id), + lifetime_mapping: self.arena.alloc_from_iter( + lifetime_mapping.iter().map(|(lifetime, def_id)| (**lifetime, *def_id)), + ), in_trait, }; @@ -2109,9 +2115,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // // For the "output" lifetime parameters, we just want to // generate `'_`. - let generic_args = self - .arena - .alloc_from_iter(lifetimes.iter().map(|lifetime| hir::GenericArg::Lifetime(*lifetime))); + let generic_args = self.arena.alloc_from_iter( + lifetime_mapping.iter().map(|(lifetime, _)| hir::GenericArg::Lifetime(*lifetime)), + ); // Create the `Foo<...>` reference itself. Note that the `type // Foo = impl Trait` is, internally, created as a child of the diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index c6c1e94edb2..6c419471de1 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -2664,6 +2664,10 @@ pub struct OpaqueTy<'hir> { pub generics: &'hir Generics<'hir>, pub bounds: GenericBounds<'hir>, pub origin: OpaqueTyOrigin, + // Opaques have duplicated lifetimes, this mapping connects the original lifetime with the copy + // so we can later generate bidirectional outlives predicates to enforce that these lifetimes + // stay in sync. + pub lifetime_mapping: &'hir [(Lifetime, LocalDefId)], pub in_trait: bool, } diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs index a84f957d7bc..b9e71aaa004 100644 --- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs @@ -10,7 +10,7 @@ use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::{self, Visitor}; use rustc_middle::ty::subst::InternalSubsts; use rustc_middle::ty::{self, Ty, TyCtxt}; -use rustc_middle::ty::{GenericPredicates, Generics, ToPredicate}; +use rustc_middle::ty::{GenericPredicates, Generics, ImplTraitInTraitData, ToPredicate}; use rustc_span::symbol::{sym, Ident}; use rustc_span::{Span, Symbol, DUMMY_SP}; @@ -62,6 +62,54 @@ pub(super) fn predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredic fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::GenericPredicates<'_> { use rustc_hir::*; + match tcx.opt_rpitit_info(def_id.to_def_id()) { + Some(ImplTraitInTraitData::Trait { opaque_def_id, .. }) => { + let opaque_ty_id = tcx.hir().local_def_id_to_hir_id(opaque_def_id.expect_local()); + let opaque_ty_node = tcx.hir().get(opaque_ty_id); + let Node::Item(&Item { kind: ItemKind::OpaqueTy(OpaqueTy { lifetime_mapping, .. }), .. }) = opaque_ty_node else { + bug!("unexpected {opaque_ty_node:?}") + }; + + let mut predicates = Vec::new(); + compute_bidirectional_outlives_predicates( + tcx, + def_id, + lifetime_mapping.iter().map(|(lifetime, def_id)| { + (*lifetime, (*def_id, lifetime.ident.name, lifetime.ident.span)) + }), + tcx.generics_of(def_id.to_def_id()), + &mut predicates, + ); + + return ty::GenericPredicates { + parent: Some(tcx.parent(def_id.to_def_id())), + predicates: tcx.arena.alloc_from_iter(predicates), + }; + } + + Some(ImplTraitInTraitData::Impl { fn_def_id }) => { + let assoc_item = tcx.associated_item(def_id); + let trait_assoc_predicates = tcx.predicates_of(assoc_item.trait_item_def_id.unwrap()); + + let impl_assoc_identity_substs = InternalSubsts::identity_for_item(tcx, def_id); + let impl_def_id = tcx.parent(fn_def_id); + let impl_trait_ref_substs = + tcx.impl_trait_ref(impl_def_id).unwrap().skip_binder().substs; + + let impl_assoc_substs = + impl_assoc_identity_substs.rebase_onto(tcx, impl_def_id, impl_trait_ref_substs); + + let impl_predicates = trait_assoc_predicates.instantiate_own(tcx, impl_assoc_substs); + + return ty::GenericPredicates { + parent: Some(impl_def_id), + predicates: tcx.arena.alloc_from_iter(impl_predicates), + }; + } + + None => {} + } + let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); let node = tcx.hir().get(hir_id); @@ -298,7 +346,13 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen .filter(|(_, dup)| matches!(dup.kind, hir::GenericParamKind::Lifetime { .. })) .map(|(lifetime, dup)| (lifetime, (dup.def_id, dup.name.ident().name, dup.span))); - bidirectional_lifetime_predicates(tcx, def_id, lifetime_mapping, generics, &mut predicates); + compute_bidirectional_outlives_predicates( + tcx, + def_id, + lifetime_mapping, + generics, + &mut predicates, + ); debug!(?predicates); } diff --git a/compiler/rustc_ty_utils/src/assoc.rs b/compiler/rustc_ty_utils/src/assoc.rs index 5b731641e9d..b59458bbf35 100644 --- a/compiler/rustc_ty_utils/src/assoc.rs +++ b/compiler/rustc_ty_utils/src/assoc.rs @@ -340,12 +340,6 @@ fn associated_type_for_impl_trait_in_trait( } }); - // There are no predicates for the synthesized associated type. - trait_assoc_ty.explicit_predicates_of(ty::GenericPredicates { - parent: Some(trait_def_id.to_def_id()), - predicates: &[], - }); - // There are no inferred outlives for the synthesized associated type. trait_assoc_ty.inferred_outlives_of(&[]); @@ -424,12 +418,6 @@ fn associated_type_for_impl_trait_in_impl( } }); - // There are no predicates for the synthesized associated type. - impl_assoc_ty.explicit_predicates_of(ty::GenericPredicates { - parent: Some(impl_local_def_id.to_def_id()), - predicates: &[], - }); - // There are no inferred outlives for the synthesized associated type. impl_assoc_ty.inferred_outlives_of(&[]); diff --git a/tests/ui/async-await/in-trait/async-lifetimes-and-bounds.rs b/tests/ui/async-await/in-trait/async-lifetimes-and-bounds.rs index d5481d277e4..9869a8d71c2 100644 --- a/tests/ui/async-await/in-trait/async-lifetimes-and-bounds.rs +++ b/tests/ui/async-await/in-trait/async-lifetimes-and-bounds.rs @@ -1,5 +1,7 @@ // check-pass // edition: 2021 +// [next] compile-flags: -Zlower-impl-trait-in-trait-to-assoc-ty +// revisions: current next #![feature(async_fn_in_trait)] #![allow(incomplete_features)] diff --git a/tests/ui/async-await/in-trait/async-lifetimes.rs b/tests/ui/async-await/in-trait/async-lifetimes.rs index f298e45d239..ecbd1910ac4 100644 --- a/tests/ui/async-await/in-trait/async-lifetimes.rs +++ b/tests/ui/async-await/in-trait/async-lifetimes.rs @@ -1,5 +1,7 @@ // check-pass // edition: 2021 +// [next] compile-flags: -Zlower-impl-trait-in-trait-to-assoc-ty +// revisions: current next #![feature(async_fn_in_trait)] #![allow(incomplete_features)]