From 8dcb8e0759ea48df925968557bd81e37c084e2d0 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 7 Aug 2023 22:09:12 +0000 Subject: [PATCH 1/5] Unconditionally record lifetime mapping --- compiler/rustc_ast_lowering/src/lib.rs | 6 +----- compiler/rustc_hir/src/hir.rs | 2 +- compiler/rustc_hir_analysis/src/collect/predicates_of.rs | 2 +- compiler/rustc_ty_utils/src/implied_bounds.rs | 2 +- 4 files changed, 4 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index d29e9f9b3f6..a8151ccfff3 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -1663,11 +1663,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ); debug!("lower_async_fn_ret_ty: generic_params={:#?}", generic_params); - let lifetime_mapping = if in_trait { - Some(&*self.arena.alloc_slice(&synthesized_lifetime_args)) - } else { - None - }; + let lifetime_mapping = self.arena.alloc_slice(&synthesized_lifetime_args); let opaque_ty_item = hir::OpaqueTy { generics: this.arena.alloc(hir::Generics { diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index c6f8d1e211d..61353c7b1ec 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -2675,7 +2675,7 @@ pub struct OpaqueTy<'hir> { /// /// This mapping associated a captured lifetime (first parameter) with the new /// early-bound lifetime that was generated for the opaque. - pub lifetime_mapping: Option<&'hir [(&'hir Lifetime, LocalDefId)]>, + pub lifetime_mapping: &'hir [(&'hir Lifetime, LocalDefId)], /// Whether the opaque is a return-position impl trait (or async future) /// originating from a trait method. This makes it so that the opaque is /// lowered as an associated type. diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs index 83220be6883..f0a426722c3 100644 --- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs @@ -59,7 +59,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen 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: Some(lifetime_mapping), .. }), + kind: ItemKind::OpaqueTy(OpaqueTy { lifetime_mapping, .. }), .. }) = opaque_ty_node else { diff --git a/compiler/rustc_ty_utils/src/implied_bounds.rs b/compiler/rustc_ty_utils/src/implied_bounds.rs index 7eb1042d2f8..01fe3a408e8 100644 --- a/compiler/rustc_ty_utils/src/implied_bounds.rs +++ b/compiler/rustc_ty_utils/src/implied_bounds.rs @@ -67,7 +67,7 @@ fn assumed_wf_types<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &'tcx [(Ty<' let mut mapping = FxHashMap::default(); let generics = tcx.generics_of(def_id); for &(lifetime, new_early_bound_def_id) in - lifetime_mapping.expect("expected lifetime mapping for RPITIT") + lifetime_mapping { if let Some(rbv::ResolvedArg::LateBound(_, _, def_id)) = tcx.named_bound_var(lifetime.hir_id) From 420ee167a8fbda2a078cb3a294fa1108c0180c98 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 7 Aug 2023 23:37:57 +0000 Subject: [PATCH 2/5] Install bidirectional outlives predicates for RPITITs (and RPITs) correctly --- .../src/collect/predicates_of.rs | 94 ++++++------------- compiler/rustc_middle/src/ty/context.rs | 69 ++++++++++++++ compiler/rustc_ty_utils/src/implied_bounds.rs | 35 +++---- .../in-trait/outlives-in-nested-rpit.rs | 11 +++ 4 files changed, 121 insertions(+), 88 deletions(-) create mode 100644 tests/ui/impl-trait/in-trait/outlives-in-nested-rpit.rs diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs index f0a426722c3..495e663666c 100644 --- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs @@ -2,16 +2,16 @@ use crate::astconv::{AstConv, OnlySelfBounds, PredicateFilter}; use crate::bounds::Bounds; use crate::collect::ItemCtxt; use crate::constrained_generic_params as cgp; -use hir::{HirId, Lifetime, Node}; +use hir::{HirId, Node}; use rustc_data_structures::fx::FxIndexSet; use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::{self, Visitor}; use rustc_middle::ty::{self, Ty, TyCtxt}; -use rustc_middle::ty::{GenericPredicates, Generics, ImplTraitInTraitData, ToPredicate}; +use rustc_middle::ty::{GenericPredicates, ImplTraitInTraitData, ToPredicate}; use rustc_span::symbol::Ident; -use rustc_span::{Span, Symbol, DUMMY_SP}; +use rustc_span::{Span, DUMMY_SP}; /// Returns a list of all type predicates (explicit and implicit) for the definition with /// ID `def_id`. This includes all predicates returned by `predicates_defined_on`, plus @@ -55,17 +55,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen use rustc_hir::*; match tcx.opt_rpitit_info(def_id.to_def_id()) { - Some(ImplTraitInTraitData::Trait { opaque_def_id, fn_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:?}") - }; - + Some(ImplTraitInTraitData::Trait { fn_def_id, .. }) => { let mut predicates = Vec::new(); // RPITITs should inherit the predicates of their parent. This is @@ -78,13 +68,12 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen // We also install bidirectional outlives predicates for the RPITIT // to keep the duplicates lifetimes from opaque lowering in sync. + // We only need to compute bidirectional outlives for the duplicated + // opaque lifetimes, which explains the slicing below. 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()), + &tcx.generics_of(def_id.to_def_id()).params + [tcx.generics_of(fn_def_id).params.len()..], &mut predicates, ); @@ -351,21 +340,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen }; debug!(?lifetimes); - let lifetime_mapping = std::iter::zip(lifetimes, ast_generics.params) - .map(|(arg, dup)| { - let hir::GenericArg::Lifetime(arg) = arg else { bug!() }; - (**arg, dup) - }) - .filter(|(_, dup)| matches!(dup.kind, hir::GenericParamKind::Lifetime { .. })) - .map(|(lifetime, dup)| (lifetime, (dup.def_id, dup.name.ident().name, dup.span))); - - compute_bidirectional_outlives_predicates( - tcx, - def_id, - lifetime_mapping, - generics, - &mut predicates, - ); + compute_bidirectional_outlives_predicates(tcx, &generics.params, &mut predicates); debug!(?predicates); } @@ -379,41 +354,28 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen /// enforce that these lifetimes stay in sync. fn compute_bidirectional_outlives_predicates<'tcx>( tcx: TyCtxt<'tcx>, - item_def_id: LocalDefId, - lifetime_mapping: impl Iterator, - generics: &Generics, + opaque_own_params: &[ty::GenericParamDef], predicates: &mut Vec<(ty::Clause<'tcx>, Span)>, ) { - let icx = ItemCtxt::new(tcx, item_def_id); - - for (arg, (dup_def, name, span)) in lifetime_mapping { - let orig_region = icx.astconv().ast_region_to_region(&arg, None); - if !matches!(orig_region.kind(), ty::ReEarlyBound(..)) { - // There is no late-bound lifetime to actually match up here, since the lifetime doesn't - // show up in the opaque's parent's args. - continue; + for param in opaque_own_params { + let orig_lifetime = tcx.map_rpit_lifetime_to_fn_lifetime(param.def_id.expect_local()); + if let ty::ReEarlyBound(..) = *orig_lifetime { + let dup_lifetime = ty::Region::new_early_bound( + tcx, + ty::EarlyBoundRegion { def_id: param.def_id, index: param.index, name: param.name }, + ); + let span = tcx.def_span(param.def_id); + predicates.push(( + ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate(orig_lifetime, dup_lifetime)) + .to_predicate(tcx), + span, + )); + predicates.push(( + ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate(dup_lifetime, orig_lifetime)) + .to_predicate(tcx), + span, + )); } - - let Some(dup_index) = generics.param_def_id_to_index(icx.tcx, dup_def.to_def_id()) else { - bug!() - }; - - let dup_region = ty::Region::new_early_bound( - tcx, - ty::EarlyBoundRegion { def_id: dup_def.to_def_id(), index: dup_index, name }, - ); - - predicates.push(( - ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate(orig_region, dup_region)) - .to_predicate(tcx), - span, - )); - - predicates.push(( - ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate(dup_region, orig_region)) - .to_predicate(tcx), - span, - )); } } diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 5eef3e45f32..75a2c6cbd20 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -1931,6 +1931,75 @@ impl<'tcx> TyCtxt<'tcx> { ) } + /// Given the def-id of an early-bound lifetime on an RPIT corresponding to + /// a duplicated captured lifetime, map it back to the early- or late-bound + /// lifetime of the function from which it originally as captured. If it is + /// a late-bound lifetime, this will represent the liberated (`ReFree`) lifetime + /// of the signature. + // FIXME(RPITIT): if we ever synthesize new lifetimes for RPITITs and not just + // re-use the generics of the opaque, this function will need to be tweaked slightly. + pub fn map_rpit_lifetime_to_fn_lifetime( + self, + mut rpit_lifetime_param_def_id: LocalDefId, + ) -> ty::Region<'tcx> { + debug_assert!( + matches!(self.def_kind(rpit_lifetime_param_def_id), DefKind::LifetimeParam), + "{rpit_lifetime_param_def_id:?} is a {}", + self.def_descr(rpit_lifetime_param_def_id.to_def_id()) + ); + + loop { + let parent = self.local_parent(rpit_lifetime_param_def_id); + let hir::OpaqueTy { lifetime_mapping, .. } = + self.hir().get_by_def_id(parent).expect_item().expect_opaque_ty(); + + let Some((lifetime, _)) = lifetime_mapping + .iter() + .find(|(_, duplicated_param)| *duplicated_param == rpit_lifetime_param_def_id) + else { + bug!("duplicated lifetime param should be present"); + }; + + match self.named_bound_var(lifetime.hir_id) { + Some(resolve_bound_vars::ResolvedArg::EarlyBound(ebv)) => { + let new_parent = self.parent(ebv); + + // If we map to another opaque, then it should be a parent + // of the opaque we mapped from. Continue mapping. + if matches!(self.def_kind(new_parent), DefKind::OpaqueTy) { + debug_assert_eq!(self.parent(parent.to_def_id()), new_parent); + rpit_lifetime_param_def_id = ebv.expect_local(); + continue; + } + + let generics = self.generics_of(new_parent); + return ty::Region::new_early_bound( + self, + ty::EarlyBoundRegion { + def_id: ebv, + index: generics + .param_def_id_to_index(self, ebv) + .expect("early-bound var should be present in fn generics"), + name: self.hir().name(self.local_def_id_to_hir_id(ebv.expect_local())), + }, + ); + } + Some(resolve_bound_vars::ResolvedArg::LateBound(_, _, lbv)) => { + let new_parent = self.parent(lbv); + return ty::Region::new_free( + self, + new_parent, + ty::BoundRegionKind::BrNamed( + lbv, + self.hir().name(self.local_def_id_to_hir_id(lbv.expect_local())), + ), + ); + } + _ => bug!(), + } + } + } + /// Whether the `def_id` counts as const fn in the current crate, considering all active /// feature gates pub fn is_const_fn(self, def_id: DefId) -> bool { diff --git a/compiler/rustc_ty_utils/src/implied_bounds.rs b/compiler/rustc_ty_utils/src/implied_bounds.rs index 01fe3a408e8..ec2e0daaf88 100644 --- a/compiler/rustc_ty_utils/src/implied_bounds.rs +++ b/compiler/rustc_ty_utils/src/implied_bounds.rs @@ -2,7 +2,6 @@ use rustc_data_structures::fx::FxHashMap; use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; -use rustc_middle::middle::resolve_bound_vars as rbv; use rustc_middle::query::Providers; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::Span; @@ -52,9 +51,7 @@ fn assumed_wf_types<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &'tcx [(Ty<' tcx.arena.alloc_from_iter(tys.into_iter().map(|ty| (ty, impl_spans.next().unwrap()))) } DefKind::AssocTy if let Some(data) = tcx.opt_rpitit_info(def_id.to_def_id()) => match data { - ty::ImplTraitInTraitData::Trait { fn_def_id, opaque_def_id } => { - let hir::OpaqueTy { lifetime_mapping, .. } = - *tcx.hir().expect_item(opaque_def_id.expect_local()).expect_opaque_ty(); + ty::ImplTraitInTraitData::Trait { fn_def_id, .. } => { // We need to remap all of the late-bound lifetimes in theassumed wf types // of the fn (which are represented as ReFree) to the early-bound lifetimes // of the RPITIT (which are represented by ReEarlyBound owned by the opaque). @@ -66,28 +63,22 @@ fn assumed_wf_types<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &'tcx [(Ty<' // predicates we insert in the `explicit_predicates_of` query for RPITITs. let mut mapping = FxHashMap::default(); let generics = tcx.generics_of(def_id); - for &(lifetime, new_early_bound_def_id) in - lifetime_mapping - { - if let Some(rbv::ResolvedArg::LateBound(_, _, def_id)) = - tcx.named_bound_var(lifetime.hir_id) - { - let name = tcx.hir().name(lifetime.hir_id); - let index = generics - .param_def_id_to_index(tcx, new_early_bound_def_id.to_def_id()) - .unwrap(); + + // For each captured opaque lifetime, if it's late-bound (`ReFree` in this case, + // since it has been liberated), map it back to the early-bound lifetime of + // the GAT. Since RPITITs also have all of the fn's generics, we slice only + // the end of the list corresponding to the opaque's generics. + for param in &generics.params[tcx.generics_of(fn_def_id).params.len()..] { + let orig_lt = tcx.map_rpit_lifetime_to_fn_lifetime(param.def_id.expect_local()); + if matches!(*orig_lt, ty::ReFree(..)) { mapping.insert( - ty::Region::new_free( - tcx, - fn_def_id, - ty::BoundRegionKind::BrNamed(def_id, name), - ), + orig_lt, ty::Region::new_early_bound( tcx, ty::EarlyBoundRegion { - def_id: new_early_bound_def_id.to_def_id(), - index, - name, + def_id: param.def_id, + index: param.index, + name: param.name, }, ), ); diff --git a/tests/ui/impl-trait/in-trait/outlives-in-nested-rpit.rs b/tests/ui/impl-trait/in-trait/outlives-in-nested-rpit.rs new file mode 100644 index 00000000000..6330242ceeb --- /dev/null +++ b/tests/ui/impl-trait/in-trait/outlives-in-nested-rpit.rs @@ -0,0 +1,11 @@ +// check-pass + +#![feature(return_position_impl_trait_in_trait)] + +trait Foo { + fn early<'a, T: 'a>(x: &'a T) -> impl Iterator>; + + fn late<'a, T>(x: &'a T) -> impl Iterator>; +} + +fn main() {} From 67703b9161e4869c5ac155e9a12317c1a2fee339 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 7 Aug 2023 08:33:25 +0000 Subject: [PATCH 3/5] Stop using identity args for opaque type wf checks and instead load the args from the single use of a RPIT in its parent function's return type --- .../rustc_hir_analysis/src/check/check.rs | 193 +++++++++++------- .../wf-check-rpit-lifetimes.rs | 19 ++ 2 files changed, 134 insertions(+), 78 deletions(-) create mode 100644 tests/ui/type-alias-impl-trait/wf-check-rpit-lifetimes.rs diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 73627a818e5..f0a8ae9b7a8 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -13,7 +13,7 @@ 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::{RegionVariableOrigin, TyCtxtInferExt}; +use rustc_infer::infer::{LateBoundRegionConversionTime, RegionVariableOrigin, TyCtxtInferExt}; use rustc_infer::traits::{Obligation, TraitEngineExt as _}; use rustc_lint_defs::builtin::REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS; use rustc_middle::hir::nested_filter; @@ -407,7 +407,38 @@ fn check_opaque_meets_bounds<'tcx>( .build(); let ocx = ObligationCtxt::new(&infcx); - let args = GenericArgs::identity_for_item(tcx, def_id.to_def_id()); + let mut args = GenericArgs::identity_for_item(tcx, def_id.to_def_id()); + assert!(!args.has_escaping_bound_vars(), "{args:#?}"); + if let hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..) = origin { + // Find use of the RPIT in the function signature and thus find the right args to + // convert it into the parameter space of the function signature. This is needed, + // because that's what `type_of` returns, against which we compare later. + let ret = tcx.fn_sig(defining_use_anchor).instantiate_identity().output(); + + let a = ret + .skip_binder() + .visit_with(&mut FindOpaqueTypeArgs { + tcx, + opaque: def_id.to_def_id(), + fn_def_id: defining_use_anchor.to_def_id(), + seen: Default::default(), + depth: ty::INNERMOST, + }) + .break_value() + .ok_or_else(|| { + tcx.sess.delay_span_bug( + tcx.def_span(defining_use_anchor), + format!("return type of {defining_use_anchor:?} does not contain {def_id:?}"), + ) + })?; + let a = infcx.instantiate_binder_with_fresh_vars( + span, + LateBoundRegionConversionTime::HigherRankedType, + ret.rebind(a), + ); + assert!(!a.has_escaping_bound_vars(), "{a:#?}"); + args = ty::EarlyBinder::bind(args).instantiate(tcx, a); + } let opaque_ty = Ty::new_opaque(tcx, def_id.to_def_id(), args); // `ReErased` regions appear in the "parent_args" of closures/generators. @@ -468,9 +499,10 @@ fn check_opaque_meets_bounds<'tcx>( } } // Check that any hidden types found during wf checking match the hidden types that `type_of` sees. - for (key, mut ty) in infcx.take_opaque_types() { + for (mut key, mut ty) in infcx.take_opaque_types() { ty.hidden_type.ty = infcx.resolve_vars_if_possible(ty.hidden_type.ty); - sanity_check_found_hidden_type(tcx, key, ty.hidden_type, defining_use_anchor, origin)?; + key = infcx.resolve_vars_if_possible(key); + sanity_check_found_hidden_type(tcx, key, ty.hidden_type)?; } Ok(()) } @@ -479,8 +511,6 @@ fn sanity_check_found_hidden_type<'tcx>( tcx: TyCtxt<'tcx>, key: ty::OpaqueTypeKey<'tcx>, mut ty: ty::OpaqueHiddenType<'tcx>, - defining_use_anchor: LocalDefId, - origin: &hir::OpaqueTyOrigin, ) -> Result<(), ErrorGuaranteed> { if ty.ty.is_ty_var() { // Nothing was actually constrained. @@ -493,29 +523,23 @@ fn sanity_check_found_hidden_type<'tcx>( return Ok(()); } } + let strip_vars = |ty: Ty<'tcx>| { + ty.fold_with(&mut BottomUpFolder { + tcx, + ty_op: |t| t, + ct_op: |c| c, + lt_op: |l| match l.kind() { + RegionKind::ReVar(_) => tcx.lifetimes.re_erased, + _ => l, + }, + }) + }; // Closures frequently end up containing erased lifetimes in their final representation. // These correspond to lifetime variables that never got resolved, so we patch this up here. - ty.ty = ty.ty.fold_with(&mut BottomUpFolder { - tcx, - ty_op: |t| t, - ct_op: |c| c, - lt_op: |l| match l.kind() { - RegionKind::ReVar(_) => tcx.lifetimes.re_erased, - _ => l, - }, - }); + ty.ty = strip_vars(ty.ty); // Get the hidden type. - let mut hidden_ty = tcx.type_of(key.def_id).instantiate(tcx, key.args); - if let hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..) = origin { - if hidden_ty != ty.ty { - hidden_ty = find_and_apply_rpit_args( - tcx, - hidden_ty, - defining_use_anchor.to_def_id(), - key.def_id.to_def_id(), - )?; - } - } + let hidden_ty = tcx.type_of(key.def_id).instantiate(tcx, key.args); + let hidden_ty = strip_vars(hidden_ty); // If the hidden types differ, emit a type mismatch diagnostic. if hidden_ty == ty.ty { @@ -563,67 +587,80 @@ fn sanity_check_found_hidden_type<'tcx>( /// x /// } /// ``` -fn find_and_apply_rpit_args<'tcx>( +struct FindOpaqueTypeArgs<'tcx> { tcx: TyCtxt<'tcx>, - mut hidden_ty: Ty<'tcx>, - function: DefId, opaque: DefId, -) -> Result, ErrorGuaranteed> { - // Find use of the RPIT in the function signature and thus find the right args to - // convert it into the parameter space of the function signature. This is needed, - // because that's what `type_of` returns, against which we compare later. - let ret = tcx.fn_sig(function).instantiate_identity().output(); - struct Visitor<'tcx> { - tcx: TyCtxt<'tcx>, - opaque: DefId, - seen: FxHashSet, - } - impl<'tcx> ty::TypeVisitor> for Visitor<'tcx> { - type BreakTy = GenericArgsRef<'tcx>; + seen: FxHashSet, + fn_def_id: DefId, + depth: ty::DebruijnIndex, +} +impl<'tcx> ty::TypeVisitor> for FindOpaqueTypeArgs<'tcx> { + type BreakTy = GenericArgsRef<'tcx>; - #[instrument(level = "trace", skip(self), ret)] - fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow { - trace!("{:#?}", t.kind()); - match t.kind() { - ty::Alias(ty::Opaque, alias) => { - trace!(?alias.def_id); - if alias.def_id == self.opaque { - return ControlFlow::Break(alias.args); - } else if self.seen.insert(alias.def_id) { - for clause in self - .tcx - .explicit_item_bounds(alias.def_id) - .iter_instantiated_copied(self.tcx, alias.args) - { - trace!(?clause); - clause.visit_with(self)?; + #[instrument(level = "trace", skip(self), ret)] + fn visit_binder>>( + &mut self, + t: &ty::Binder<'tcx, T>, + ) -> ControlFlow { + self.depth.shift_in(1); + let binder = t.super_visit_with(self); + self.depth.shift_out(1); + binder + } + + #[instrument(level = "trace", skip(self), ret)] + fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow { + trace!("{:#?}", t.kind()); + match t.kind() { + ty::Alias(ty::Opaque, alias) => { + trace!(?alias.def_id); + if alias.def_id == self.opaque { + let args = self.tcx.fold_regions(alias.args, |re, depth| { + if let ty::ReLateBound(index, bv) = re.kind() { + if depth != ty::INNERMOST { + return ty::Region::new_error_with_message( + self.tcx, + self.tcx.def_span(self.opaque), + "opaque type behind meaningful binders are not supported yet", + ); + } + ty::Region::new_late_bound( + self.tcx, + index.shifted_out_to_binder(self.depth), + bv, + ) + } else { + re } + }); + return ControlFlow::Break(args); + } else if self.seen.insert(alias.def_id) { + for clause in self + .tcx + .explicit_item_bounds(alias.def_id) + .iter_instantiated_copied(self.tcx, alias.args) + { + trace!(?clause); + clause.visit_with(self)?; } } - ty::Alias(ty::Weak, alias) => { - self.tcx - .type_of(alias.def_id) - .instantiate(self.tcx, alias.args) - .visit_with(self)?; - } - _ => (), } - - t.super_visit_with(self) + ty::Alias(ty::Projection, alias) => { + if let Some(ty::ImplTraitInTraitData::Trait { fn_def_id, .. }) = self.tcx.opt_rpitit_info(alias.def_id) && fn_def_id == self.fn_def_id { + self.tcx.type_of(alias.def_id).instantiate(self.tcx, alias.args).visit_with(self)?; + } + } + ty::Alias(ty::Weak, alias) => { + self.tcx + .type_of(alias.def_id) + .instantiate(self.tcx, alias.args) + .visit_with(self)?; + } + _ => (), } + + t.super_visit_with(self) } - if let ControlFlow::Break(args) = - ret.visit_with(&mut Visitor { tcx, opaque, seen: Default::default() }) - { - trace!(?args); - trace!("expected: {hidden_ty:#?}"); - hidden_ty = ty::EarlyBinder::bind(hidden_ty).instantiate(tcx, args); - trace!("expected: {hidden_ty:#?}"); - } else { - tcx.sess - .delay_span_bug(tcx.def_span(function), format!("{ret:?} does not contain {opaque:?}")); - } - Ok(hidden_ty) } fn is_enum_of_nonnullable_ptr<'tcx>( diff --git a/tests/ui/type-alias-impl-trait/wf-check-rpit-lifetimes.rs b/tests/ui/type-alias-impl-trait/wf-check-rpit-lifetimes.rs new file mode 100644 index 00000000000..b92e15aad56 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/wf-check-rpit-lifetimes.rs @@ -0,0 +1,19 @@ +//check-pass + +pub struct Key; +#[derive(Clone)] +pub struct Value; + +use std::collections::HashMap; + +pub struct DiagnosticBuilder<'db> { + inner: HashMap<&'db Key, Vec<&'db Value>>, +} + +impl<'db> DiagnosticBuilder<'db> { + pub fn iter<'a>(&'a self) -> impl Iterator)> { + self.inner.iter().map(|(key, values)| (*key, values.iter().map(|v| *v))) + } +} + +fn main() {} From ef2a611803964e50853333e34d41f4347434513c Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 7 Aug 2023 23:54:04 +0000 Subject: [PATCH 4/5] Simplify via map_rpit_lifetime_to_fn_lifetime --- .../rustc_hir_analysis/src/check/check.rs | 155 ++---------------- compiler/rustc_middle/src/ty/context.rs | 11 +- 2 files changed, 21 insertions(+), 145 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index f0a8ae9b7a8..1c4ee9a2bc3 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -13,7 +13,7 @@ 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::{LateBoundRegionConversionTime, RegionVariableOrigin, TyCtxtInferExt}; +use rustc_infer::infer::{RegionVariableOrigin, TyCtxtInferExt}; use rustc_infer::traits::{Obligation, TraitEngineExt as _}; use rustc_lint_defs::builtin::REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS; use rustc_middle::hir::nested_filter; @@ -407,38 +407,17 @@ fn check_opaque_meets_bounds<'tcx>( .build(); let ocx = ObligationCtxt::new(&infcx); - let mut args = GenericArgs::identity_for_item(tcx, def_id.to_def_id()); - assert!(!args.has_escaping_bound_vars(), "{args:#?}"); - if let hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..) = origin { - // Find use of the RPIT in the function signature and thus find the right args to - // convert it into the parameter space of the function signature. This is needed, - // because that's what `type_of` returns, against which we compare later. - let ret = tcx.fn_sig(defining_use_anchor).instantiate_identity().output(); - - let a = ret - .skip_binder() - .visit_with(&mut FindOpaqueTypeArgs { + let args = match *origin { + hir::OpaqueTyOrigin::FnReturn(parent) | hir::OpaqueTyOrigin::AsyncFn(parent) => { + GenericArgs::identity_for_item(tcx, parent).extend_to( tcx, - opaque: def_id.to_def_id(), - fn_def_id: defining_use_anchor.to_def_id(), - seen: Default::default(), - depth: ty::INNERMOST, - }) - .break_value() - .ok_or_else(|| { - tcx.sess.delay_span_bug( - tcx.def_span(defining_use_anchor), - format!("return type of {defining_use_anchor:?} does not contain {def_id:?}"), - ) - })?; - let a = infcx.instantiate_binder_with_fresh_vars( - span, - LateBoundRegionConversionTime::HigherRankedType, - ret.rebind(a), - ); - assert!(!a.has_escaping_bound_vars(), "{a:#?}"); - args = ty::EarlyBinder::bind(args).instantiate(tcx, a); - } + def_id.to_def_id(), + |param, _| tcx.map_rpit_lifetime_to_fn_lifetime(param.def_id.expect_local()).into(), + ) + } + hir::OpaqueTyOrigin::TyAlias { .. } => GenericArgs::identity_for_item(tcx, def_id), + }; + let opaque_ty = Ty::new_opaque(tcx, def_id.to_def_id(), args); // `ReErased` regions appear in the "parent_args" of closures/generators. @@ -551,118 +530,6 @@ fn sanity_check_found_hidden_type<'tcx>( } } -/// In case it is in a nested opaque type, find that opaque type's -/// usage in the function signature and use the generic arguments from the usage site. -/// We need to do because RPITs ignore the lifetimes of the function, -/// as they have their own copies of all the lifetimes they capture. -/// So the only way to get the lifetimes represented in terms of the function, -/// is to look how they are used in the function signature (or do some other fancy -/// recording of this mapping at ast -> hir lowering time). -/// -/// As an example: -/// ```text -/// trait Id { -/// type Assoc; -/// } -/// impl<'a> Id for &'a () { -/// type Assoc = &'a (); -/// } -/// fn func<'a>(x: &'a ()) -> impl Id { x } -/// // desugared to -/// fn func<'a>(x: &'a () -> Outer<'a> where as Id>::Assoc = Inner<'a> { -/// // Note that in contrast to other nested items, RPIT type aliases can -/// // access their parents' generics. -/// -/// // hidden type is `&'aDupOuter ()` -/// // During wfcheck the hidden type of `Inner<'aDupOuter>` is `&'a ()`, but -/// // `typeof(Inner<'aDupOuter>) = &'aDupOuter ()`. -/// // So we walk the signature of `func` to find the use of `Inner<'a>` -/// // and then use that to replace the lifetimes in the hidden type, obtaining -/// // `&'a ()`. -/// type Outer<'aDupOuter> = impl Id>; -/// -/// // hidden type is `&'aDupInner ()` -/// type Inner<'aDupInner> = impl Sized + 'aDupInner; -/// -/// x -/// } -/// ``` -struct FindOpaqueTypeArgs<'tcx> { - tcx: TyCtxt<'tcx>, - opaque: DefId, - seen: FxHashSet, - fn_def_id: DefId, - depth: ty::DebruijnIndex, -} -impl<'tcx> ty::TypeVisitor> for FindOpaqueTypeArgs<'tcx> { - type BreakTy = GenericArgsRef<'tcx>; - - #[instrument(level = "trace", skip(self), ret)] - fn visit_binder>>( - &mut self, - t: &ty::Binder<'tcx, T>, - ) -> ControlFlow { - self.depth.shift_in(1); - let binder = t.super_visit_with(self); - self.depth.shift_out(1); - binder - } - - #[instrument(level = "trace", skip(self), ret)] - fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow { - trace!("{:#?}", t.kind()); - match t.kind() { - ty::Alias(ty::Opaque, alias) => { - trace!(?alias.def_id); - if alias.def_id == self.opaque { - let args = self.tcx.fold_regions(alias.args, |re, depth| { - if let ty::ReLateBound(index, bv) = re.kind() { - if depth != ty::INNERMOST { - return ty::Region::new_error_with_message( - self.tcx, - self.tcx.def_span(self.opaque), - "opaque type behind meaningful binders are not supported yet", - ); - } - ty::Region::new_late_bound( - self.tcx, - index.shifted_out_to_binder(self.depth), - bv, - ) - } else { - re - } - }); - return ControlFlow::Break(args); - } else if self.seen.insert(alias.def_id) { - for clause in self - .tcx - .explicit_item_bounds(alias.def_id) - .iter_instantiated_copied(self.tcx, alias.args) - { - trace!(?clause); - clause.visit_with(self)?; - } - } - } - ty::Alias(ty::Projection, alias) => { - if let Some(ty::ImplTraitInTraitData::Trait { fn_def_id, .. }) = self.tcx.opt_rpitit_info(alias.def_id) && fn_def_id == self.fn_def_id { - self.tcx.type_of(alias.def_id).instantiate(self.tcx, alias.args).visit_with(self)?; - } - } - ty::Alias(ty::Weak, alias) => { - self.tcx - .type_of(alias.def_id) - .instantiate(self.tcx, alias.args) - .visit_with(self)?; - } - _ => (), - } - - t.super_visit_with(self) - } -} - fn is_enum_of_nonnullable_ptr<'tcx>( tcx: TyCtxt<'tcx>, adt_def: AdtDef<'tcx>, diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 75a2c6cbd20..e25ca75bba8 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -1995,7 +1995,16 @@ impl<'tcx> TyCtxt<'tcx> { ), ); } - _ => bug!(), + Some(resolve_bound_vars::ResolvedArg::Error(guar)) => { + return ty::Region::new_error(self, guar); + } + _ => { + return ty::Region::new_error_with_message( + self, + lifetime.ident.span, + "cannot resolve lifetime", + ); + } } } } From 0adf7048d23649d561bb121d7e1ab00b4c0269b0 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 8 Aug 2023 00:28:17 +0000 Subject: [PATCH 5/5] add'l test --- .../mapping-duplicated-lifetimes-issue-114597.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 tests/ui/impl-trait/mapping-duplicated-lifetimes-issue-114597.rs diff --git a/tests/ui/impl-trait/mapping-duplicated-lifetimes-issue-114597.rs b/tests/ui/impl-trait/mapping-duplicated-lifetimes-issue-114597.rs new file mode 100644 index 00000000000..a2dd0a9308d --- /dev/null +++ b/tests/ui/impl-trait/mapping-duplicated-lifetimes-issue-114597.rs @@ -0,0 +1,15 @@ +// check-pass +// issue: 114597 +// edition: 2021 + +struct A<'a> { + dat: &'a (), +} + +impl<'a> A<'a> { + async fn a(&self) -> impl Iterator> { + std::iter::repeat(()).map(|()| std::iter::repeat(())) + } +} + +fn main() {}