diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 116497109f1dd..870a7c0be33dc 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -64,7 +64,7 @@ impl fmt::Debug for Label { /// A "Lifetime" is an annotation of the scope in which variable /// can be used, e.g. `'a` in `&'a i32`. -#[derive(Clone, Encodable, Decodable, Copy)] +#[derive(Clone, Encodable, Decodable, Copy, PartialEq, Eq)] pub struct Lifetime { pub id: NodeId, pub ident: Ident, diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 983efa48a4579..fb6715ff17ee9 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -864,22 +864,21 @@ impl<'hir> LoweringContext<'_, 'hir> { (body_id, generator_option) }); - self.with_lifetime_binder(closure_id, generic_params, |this, bound_generic_params| { - // Lower outside new scope to preserve `is_in_loop_condition`. - let fn_decl = this.lower_fn_decl(decl, None, FnDeclKind::Closure, None); - - let c = self.arena.alloc(hir::Closure { - binder: binder_clause, - capture_clause, - bound_generic_params, - fn_decl, - body: body_id, - fn_decl_span: this.lower_span(fn_decl_span), - movability: generator_option, - }); + let bound_generic_params = self.lower_lifetime_binder(closure_id, generic_params); + // Lower outside new scope to preserve `is_in_loop_condition`. + let fn_decl = self.lower_fn_decl(decl, None, FnDeclKind::Closure, None); + + let c = self.arena.alloc(hir::Closure { + binder: binder_clause, + capture_clause, + bound_generic_params, + fn_decl, + body: body_id, + fn_decl_span: self.lower_span(fn_decl_span), + movability: generator_option, + }); - hir::ExprKind::Closure(c) - }) + hir::ExprKind::Closure(c) } fn generator_movability_for_fn( @@ -991,23 +990,23 @@ impl<'hir> LoweringContext<'_, 'hir> { body_id }); - self.with_lifetime_binder(closure_id, generic_params, |this, bound_generic_params| { - // We need to lower the declaration outside the new scope, because we - // have to conserve the state of being inside a loop condition for the - // closure argument types. - let fn_decl = this.lower_fn_decl(&outer_decl, None, FnDeclKind::Closure, None); - - let c = self.arena.alloc(hir::Closure { - binder: binder_clause, - capture_clause, - bound_generic_params, - fn_decl, - body, - fn_decl_span: this.lower_span(fn_decl_span), - movability: None, - }); - hir::ExprKind::Closure(c) - }) + let bound_generic_params = self.lower_lifetime_binder(closure_id, generic_params); + + // We need to lower the declaration outside the new scope, because we + // have to conserve the state of being inside a loop condition for the + // closure argument types. + let fn_decl = self.lower_fn_decl(&outer_decl, None, FnDeclKind::Closure, None); + + let c = self.arena.alloc(hir::Closure { + binder: binder_clause, + capture_clause, + bound_generic_params, + fn_decl, + body, + fn_decl_span: self.lower_span(fn_decl_span), + movability: None, + }); + hir::ExprKind::Closure(c) } /// Destructure the LHS of complex assignments. diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 99f81afc1e25d..ee4c0036f7698 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -80,7 +80,6 @@ impl<'a, 'hir> ItemLowerer<'a, 'hir> { generator_kind: None, task_context: None, current_item: None, - captured_lifetimes: None, impl_trait_defs: Vec::new(), impl_trait_bounds: Vec::new(), allow_try_trait: Some([sym::try_trait_v2, sym::yeet_desugar_details][..].into()), @@ -1350,12 +1349,12 @@ impl<'hir> LoweringContext<'_, 'hir> { let mut predicates: SmallVec<[hir::WherePredicate<'hir>; 4]> = SmallVec::new(); predicates.extend(generics.params.iter().filter_map(|param| { - let bounds = self.lower_param_bounds(¶m.bounds, itctx); self.lower_generic_bound_predicate( param.ident, param.id, ¶m.kind, - bounds, + ¶m.bounds, + itctx, PredicateOrigin::GenericParam, ) })); @@ -1403,13 +1402,17 @@ impl<'hir> LoweringContext<'_, 'hir> { ident: Ident, id: NodeId, kind: &GenericParamKind, - bounds: &'hir [hir::GenericBound<'hir>], + bounds: &[GenericBound], + itctx: ImplTraitContext, origin: PredicateOrigin, ) -> Option> { // Do not create a clause if we do not have anything inside it. if bounds.is_empty() { return None; } + + let bounds = self.lower_param_bounds(bounds, itctx); + let ident = self.lower_ident(ident); let param_span = ident.span; let span = bounds @@ -1450,11 +1453,8 @@ impl<'hir> LoweringContext<'_, 'hir> { GenericParamKind::Lifetime => { let ident_span = self.lower_span(ident.span); let ident = self.lower_ident(ident); - let res = self.resolver.get_lifetime_res(id).unwrap_or_else(|| { - panic!("Missing resolution for lifetime {:?} at {:?}", id, ident.span) - }); let lt_id = self.next_node_id(); - let lifetime = self.new_named_lifetime_with_res(lt_id, ident_span, ident, res); + let lifetime = self.new_named_lifetime(id, lt_id, ident_span, ident); Some(hir::WherePredicate::RegionPredicate(hir::WhereRegionPredicate { lifetime, span, diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index a1bf0f94964bb..a5b089b032d8b 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -45,7 +45,7 @@ use rustc_ast::{self as ast, *}; use rustc_ast_pretty::pprust; use rustc_data_structures::captures::Captures; use rustc_data_structures::fingerprint::Fingerprint; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sorted_map::SortedMap; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::sync::Lrc; @@ -56,6 +56,7 @@ use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID}; use rustc_hir::definitions::DefPathData; use rustc_hir::{ConstArg, GenericArg, ItemLocalId, ParamName, TraitCandidate}; use rustc_index::vec::{Idx, IndexVec}; +use rustc_middle::span_bug; use rustc_middle::ty::{ResolverAstLowering, TyCtxt}; use rustc_session::parse::feature_err; use rustc_span::hygiene::MacroKind; @@ -77,6 +78,7 @@ mod block; mod expr; mod index; mod item; +mod lifetime_collector; mod pat; mod path; @@ -110,9 +112,6 @@ struct LoweringContext<'a, 'hir> { is_in_trait_impl: bool, is_in_dyn_type: bool, - /// Used to handle lifetimes appearing in impl-traits. - captured_lifetimes: Option, - current_hir_id_owner: LocalDefId, item_local_id_counter: hir::ItemLocalId, local_id_to_def_id: SortedMap, @@ -129,28 +128,6 @@ struct LoweringContext<'a, 'hir> { allow_into_future: Option>, } -/// When we lower a lifetime, it is inserted in `captures`, and the resolution is modified so -/// to point to the lifetime parameter impl-trait will generate. -/// When traversing `for<...>` binders, they are inserted in `binders_to_ignore` so we know *not* -/// to rebind the introduced lifetimes. -#[derive(Debug)] -struct LifetimeCaptureContext { - /// parent def_id for new definitions - parent_def_id: LocalDefId, - /// Set of lifetimes to rebind. - captures: FxHashMap< - LocalDefId, // original parameter id - ( - Span, // Span - NodeId, // synthetized parameter id - ParamName, // parameter name - LifetimeRes, // original resolution - ), - >, - /// Traversed binders. The ids in this set should *not* be rebound. - binders_to_ignore: FxHashSet, -} - trait ResolverAstLoweringExt { fn legacy_const_generic_args(&self, expr: &Expr) -> Option>; fn get_partial_res(&self, id: NodeId) -> Option; @@ -159,6 +136,12 @@ trait ResolverAstLoweringExt { fn get_lifetime_res(&self, id: NodeId) -> Option; fn take_extra_lifetime_params(&mut self, id: NodeId) -> Vec<(Ident, NodeId, LifetimeRes)>; fn decl_macro_kind(&self, def_id: LocalDefId) -> MacroKind; + /// Record the map from `from` local def id to `to` local def id, on `generics_def_id_map` + /// field. + fn record_def_id_remap(&mut self, from: LocalDefId, to: LocalDefId); + /// Get the previously recorded `to` local def id given the `from` local def id, obtained using + /// `generics_def_id_map` field. + fn get_remapped_def_id(&self, local_def_id: LocalDefId) -> LocalDefId; } impl ResolverAstLoweringExt for ResolverAstLowering { @@ -226,6 +209,28 @@ impl ResolverAstLoweringExt for ResolverAstLowering { fn decl_macro_kind(&self, def_id: LocalDefId) -> MacroKind { self.builtin_macro_kinds.get(&def_id).copied().unwrap_or(MacroKind::Bang) } + + /// Push a remapping into the top-most map. + /// Panics if no map has been pushed. + /// Remapping is used when creating lowering `-> impl Trait` return + /// types to create the resulting opaque type. + #[tracing::instrument(level = "debug", skip(self))] + fn record_def_id_remap(&mut self, from: LocalDefId, to: LocalDefId) { + self.generics_def_id_map.last_mut().expect("no map pushed").insert(from, to); + } + + fn get_remapped_def_id(&self, mut local_def_id: LocalDefId) -> LocalDefId { + for map in &self.generics_def_id_map { + if let Some(r) = map.get(&local_def_id) { + debug!("def_id_remapper: remapping from `{local_def_id:?}` to `{r:?}`"); + local_def_id = *r; + } else { + debug!("def_id_remapper: no remapping for `{local_def_id:?}` found in map"); + } + } + + local_def_id + } } /// Context of `impl Trait` in code, which determines whether it is allowed in an HIR subtree, @@ -481,8 +486,24 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { start } + /// Given the id of some node in the AST, finds the `LocalDefId` associated with it by the name + /// resolver (if any), after applying any remapping from `get_remapped_def_id`. + /// + /// For example, in a function like `fn foo<'a>(x: &'a u32)`, + /// invoking with the id from the `ast::Lifetime` node found inside + /// the `&'a u32` type would return the `LocalDefId` of the + /// `'a` parameter declared on `foo`. + /// + /// This function also applies remapping from `get_remapped_def_id`. + /// These are used when synthesizing opaque types from `-> impl Trait` return types and so forth. + /// For example, in a function like `fn foo<'a>() -> impl Debug + 'a`, + /// we would create an opaque type `type FooReturn<'a1> = impl Debug + 'a1`. + /// When lowering the `Debug + 'a` bounds, we add a remapping to map `'a` to `'a1`. fn opt_local_def_id(&self, node: NodeId) -> Option { - self.resolver.node_id_to_def_id.get(&node).copied() + self.resolver + .node_id_to_def_id + .get(&node) + .map(|local_def_id| self.resolver.get_remapped_def_id(*local_def_id)) } fn local_def_id(&self, node: NodeId) -> LocalDefId { @@ -542,6 +563,27 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { debug_assert!(_old.is_none()) } + /// Installs the remapping `remap` in scope while `f` is being executed. + /// This causes references to the `LocalDefId` keys to be changed to + /// refer to the values instead. + /// + /// The remapping is used when one piece of AST expands to multiple + /// pieces of HIR. For example, the function `fn foo<'a>(...) -> impl Debug + 'a`, + /// expands to both a function definition (`foo`) and a TAIT for the return value, + /// both of which have a lifetime parameter `'a`. The remapping allows us to + /// rewrite the `'a` in the return value to refer to the + /// `'a` declared on the TAIT, instead of the function. + fn with_remapping( + &mut self, + remap: FxHashMap, + f: impl FnOnce(&mut Self) -> R, + ) -> R { + self.resolver.generics_def_id_map.push(remap); + let res = f(self); + self.resolver.generics_def_id_map.pop(); + res + } + fn make_owner_info(&mut self, node: hir::OwnerNode<'hir>) -> &'hir hir::OwnerInfo<'hir> { let attrs = std::mem::take(&mut self.attrs); let mut bodies = std::mem::take(&mut self.bodies); @@ -751,40 +793,18 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { }) } - /// Setup lifetime capture for and impl-trait. - /// The captures will be added to `captures`. - fn while_capturing_lifetimes( - &mut self, - parent_def_id: LocalDefId, - captures: &mut FxHashMap, - f: impl FnOnce(&mut Self) -> T, - ) -> T { - let lifetime_stash = std::mem::replace( - &mut self.captured_lifetimes, - Some(LifetimeCaptureContext { - parent_def_id, - captures: std::mem::take(captures), - binders_to_ignore: Default::default(), - }), - ); - - let ret = f(self); - - let ctxt = std::mem::replace(&mut self.captured_lifetimes, lifetime_stash).unwrap(); - *captures = ctxt.captures; - - ret - } - - /// Register a binder to be ignored for lifetime capture. - #[tracing::instrument(level = "debug", skip(self, f))] + /// Lowers a lifetime binder that defines `generic_params`, returning the corresponding HIR + /// nodes. The returned list includes any "extra" lifetime parameters that were added by the + /// name resolver owing to lifetime elision; this also populates the resolver's node-id->def-id + /// map, so that later calls to `opt_node_id_to_def_id` that refer to these extra lifetime + /// parameters will be successful. + #[tracing::instrument(level = "debug", skip(self))] #[inline] - fn with_lifetime_binder( + fn lower_lifetime_binder( &mut self, binder: NodeId, generic_params: &[GenericParam], - f: impl FnOnce(&mut Self, &'hir [hir::GenericParam<'hir>]) -> T, - ) -> T { + ) -> &'hir [hir::GenericParam<'hir>] { let mut generic_params: Vec<_> = self.lower_generic_params_mut(generic_params).collect(); let extra_lifetimes = self.resolver.take_extra_lifetime_params(binder); debug!(?extra_lifetimes); @@ -794,14 +814,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let generic_params = self.arena.alloc_from_iter(generic_params); debug!(?generic_params); - if let Some(ctxt) = &mut self.captured_lifetimes { - ctxt.binders_to_ignore.insert(binder); - } - let ret = f(self, generic_params); - if let Some(ctxt) = &mut self.captured_lifetimes { - ctxt.binders_to_ignore.remove(&binder); - } - ret + generic_params } fn with_dyn_type_scope(&mut self, in_scope: bool, f: impl FnOnce(&mut Self) -> T) -> T { @@ -1222,15 +1235,14 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { hir::TyKind::Rptr(lifetime, self.lower_mt(mt, itctx)) } TyKind::BareFn(ref f) => { - self.with_lifetime_binder(t.id, &f.generic_params, |this, generic_params| { - hir::TyKind::BareFn(this.arena.alloc(hir::BareFnTy { - generic_params, - unsafety: this.lower_unsafety(f.unsafety), - abi: this.lower_extern(f.ext), - decl: this.lower_fn_decl(&f.decl, None, FnDeclKind::Pointer, None), - param_names: this.lower_fn_params_to_names(&f.decl), - })) - }) + let generic_params = self.lower_lifetime_binder(t.id, &f.generic_params); + hir::TyKind::BareFn(self.arena.alloc(hir::BareFnTy { + generic_params, + unsafety: self.lower_unsafety(f.unsafety), + abi: self.lower_extern(f.ext), + decl: self.lower_fn_decl(&f.decl, None, FnDeclKind::Pointer, None), + param_names: self.lower_fn_params_to_names(&f.decl), + })) } TyKind::Never => hir::TyKind::Never, TyKind::Tup(ref tys) => hir::TyKind::Tup( @@ -1293,17 +1305,17 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { TyKind::ImplTrait(def_node_id, ref bounds) => { let span = t.span; match itctx { - ImplTraitContext::ReturnPositionOpaqueTy { origin } => self - .lower_opaque_impl_trait(span, origin, def_node_id, |this| { - this.lower_param_bounds(bounds, itctx) - }), + ImplTraitContext::ReturnPositionOpaqueTy { origin } => { + self.lower_opaque_impl_trait(span, origin, def_node_id, bounds, itctx) + } ImplTraitContext::TypeAliasesOpaqueTy => { let nested_itctx = ImplTraitContext::TypeAliasesOpaqueTy; self.lower_opaque_impl_trait( span, hir::OpaqueTyOrigin::TyAlias, def_node_id, - |this| this.lower_param_bounds(bounds, nested_itctx), + bounds, + nested_itctx, ) } ImplTraitContext::Universal => { @@ -1343,13 +1355,43 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { hir::Ty { kind, span: self.lower_span(t.span), hir_id: self.lower_node_id(t.id) } } - #[tracing::instrument(level = "debug", skip(self, lower_bounds))] + /// Lowers a `ReturnPositionOpaqueTy` (`-> impl Trait`) or a `TypeAliasesOpaqueTy` (`type F = + /// impl Trait`): this creates the associated Opaque Type (TAIT) definition and then returns a + /// HIR type that references the TAIT. + /// + /// Given a function definition like: + /// + /// ```rust + /// fn test<'a, T: Debug>(x: &'a T) -> impl Debug + 'a { + /// x + /// } + /// ``` + /// + /// we will create a TAIT definition in the HIR like + /// + /// ``` + /// type TestReturn<'a, T, 'x> = impl Debug + 'x + /// ``` + /// + /// and return a type like `TestReturn<'static, T, 'a>`, so that the function looks like: + /// + /// ```rust + /// fn test<'a, T: Debug>(x: &'a T) -> TestReturn<'static, T, 'a> + /// ``` + /// + /// Note the subtlety around type parameters! The new TAIT, `TestReturn`, inherits all the + /// type parameters from the function `test` (this is implemented in the query layer, they aren't + /// added explicitly in the HIR). But this includes all the lifetimes, and we only want to + /// capture the lifetimes that are referenced in the bounds. Therefore, we add *extra* lifetime parameters + /// for the lifetimes that get captured (`'x`, in our example above) and reference those. + #[tracing::instrument(level = "debug", skip(self))] fn lower_opaque_impl_trait( &mut self, span: Span, origin: hir::OpaqueTyOrigin, opaque_ty_node_id: NodeId, - lower_bounds: impl FnOnce(&mut Self) -> hir::GenericBounds<'hir>, + bounds: &GenericBounds, + itctx: ImplTraitContext, ) -> hir::TyKind<'hir> { // Make sure we know that some funky desugaring has been going on here. // This is a first: there is code in other places like for loop @@ -1359,70 +1401,108 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let opaque_ty_span = self.mark_span_with_reason(DesugaringKind::OpaqueTy, span, None); let opaque_ty_def_id = self.local_def_id(opaque_ty_node_id); + debug!(?opaque_ty_def_id); - let mut collected_lifetimes = FxHashMap::default(); - self.with_hir_id_owner(opaque_ty_node_id, |lctx| { - let hir_bounds = if origin == hir::OpaqueTyOrigin::TyAlias { - lower_bounds(lctx) - } else { - lctx.while_capturing_lifetimes( - opaque_ty_def_id, - &mut collected_lifetimes, - lower_bounds, - ) - }; - debug!(?collected_lifetimes); + // Contains the new lifetime definitions created for the TAIT (if any). + let mut collected_lifetimes = Vec::new(); - let lifetime_defs = lctx.arena.alloc_from_iter(collected_lifetimes.iter().map( - |(_, &(span, p_id, p_name, _))| { - let hir_id = lctx.lower_node_id(p_id); - debug_assert_ne!(lctx.opt_local_def_id(p_id), None); + // If this came from a TAIT (as opposed to a function that returns an RPIT), we only want + // to capture the lifetimes that appear in the bounds. So visit the bounds to find out + // exactly which ones those are. + let lifetimes_to_remap = if origin == hir::OpaqueTyOrigin::TyAlias { + // in a TAIT like `type Foo<'a> = impl Foo<'a>`, we don't keep all the lifetime parameters + Vec::new() + } else { + // in fn return position, like the `fn test<'a>() -> impl Debug + 'a` example, + // we only keep the lifetimes that appear in the `impl Debug` itself: + lifetime_collector::lifetimes_in_bounds(&self.resolver, bounds) + }; + debug!(?lifetimes_to_remap); - let kind = if p_name.ident().name == kw::UnderscoreLifetime { - hir::LifetimeParamKind::Elided - } else { - hir::LifetimeParamKind::Explicit - }; + self.with_hir_id_owner(opaque_ty_node_id, |lctx| { + let mut new_remapping = FxHashMap::default(); + + // If this opaque type is only capturing a subset of the lifetimes (those that appear + // in bounds), then create the new lifetime parameters required and create a mapping + // from the old `'a` (on the function) to the new `'a` (on the opaque type). + collected_lifetimes = lctx.create_lifetime_defs( + opaque_ty_def_id, + &lifetimes_to_remap, + &mut new_remapping, + ); + debug!(?collected_lifetimes); + debug!(?new_remapping); + + // Install the remapping from old to new (if any): + lctx.with_remapping(new_remapping, |lctx| { + // This creates HIR lifetime definitions as `hir::GenericParam`, in the given + // example `type TestReturn<'a, T, 'x> = impl Debug + 'x`, it creates a collection + // containing `&['x]`. + let lifetime_defs = lctx.arena.alloc_from_iter(collected_lifetimes.iter().map( + |&(new_node_id, lifetime)| { + let hir_id = lctx.lower_node_id(new_node_id); + debug_assert_ne!(lctx.opt_local_def_id(new_node_id), None); + + let (name, kind) = if lifetime.ident.name == kw::UnderscoreLifetime { + (hir::ParamName::Fresh, hir::LifetimeParamKind::Elided) + } else { + ( + hir::ParamName::Plain(lifetime.ident), + hir::LifetimeParamKind::Explicit, + ) + }; - hir::GenericParam { - hir_id, - name: p_name, - span, - pure_wrt_drop: false, - kind: hir::GenericParamKind::Lifetime { kind }, - colon_span: None, - } - }, - )); - - debug!("lower_opaque_impl_trait: lifetime_defs={:#?}", lifetime_defs); - - let opaque_ty_item = hir::OpaqueTy { - generics: self.arena.alloc(hir::Generics { - params: lifetime_defs, - predicates: &[], - has_where_clause_predicates: false, - where_clause_span: lctx.lower_span(span), - span: lctx.lower_span(span), - }), - bounds: hir_bounds, - origin, - }; + hir::GenericParam { + hir_id, + name, + span: lifetime.ident.span, + pure_wrt_drop: false, + kind: hir::GenericParamKind::Lifetime { kind }, + colon_span: None, + } + }, + )); + debug!(?lifetime_defs); + + // Then when we lower the param bounds, references to 'a are remapped to 'a1, so we + // get back Debug + 'a1, which is suitable for use on the TAIT. + let hir_bounds = lctx.lower_param_bounds(bounds, itctx); + debug!(?hir_bounds); + + let opaque_ty_item = hir::OpaqueTy { + generics: self.arena.alloc(hir::Generics { + params: lifetime_defs, + predicates: &[], + has_where_clause_predicates: false, + where_clause_span: lctx.lower_span(span), + span: lctx.lower_span(span), + }), + bounds: hir_bounds, + origin, + }; + debug!(?opaque_ty_item); - trace!("lower_opaque_impl_trait: {:#?}", opaque_ty_def_id); - lctx.generate_opaque_type(opaque_ty_def_id, opaque_ty_item, span, opaque_ty_span) + lctx.generate_opaque_type(opaque_ty_def_id, opaque_ty_item, span, opaque_ty_span) + }) }); - let lifetimes = self.arena.alloc_from_iter(collected_lifetimes.into_iter().map( - |(_, (span, _, p_name, res))| { + // 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(collected_lifetimes.into_iter().map(|(_, lifetime)| { let id = self.next_node_id(); - let ident = Ident::new(p_name.ident().name, span); - let l = self.new_named_lifetime_with_res(id, span, ident, res); - hir::GenericArg::Lifetime(l) - }, - )); + let span = lifetime.ident.span; - debug!("lower_opaque_impl_trait: lifetimes={:#?}", lifetimes); + let ident = if lifetime.ident.name == kw::UnderscoreLifetime { + Ident::with_dummy_span(kw::UnderscoreLifetime) + } else { + lifetime.ident + }; + + let l = self.new_named_lifetime(lifetime.id, id, span, ident); + hir::GenericArg::Lifetime(l) + })); + debug!(?lifetimes); // `impl Trait` now just becomes `Foo<'a, 'b, ..>`. hir::TyKind::OpaqueDef(hir::ItemId { def_id: opaque_ty_def_id }, lifetimes) @@ -1450,6 +1530,70 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { hir::OwnerNode::Item(self.arena.alloc(opaque_ty_item)) } + /// Given a `parent_def_id`, a list of `lifetimes_in_bounds and a `remapping` hash to be + /// filled, this function creates new definitions for `Param` and `Fresh` lifetimes, inserts the + /// new definition, adds it to the remapping with the definition of the given lifetime and + /// returns a list of lifetimes to be lowered afterwards. + fn create_lifetime_defs( + &mut self, + parent_def_id: LocalDefId, + lifetimes_in_bounds: &[Lifetime], + remapping: &mut FxHashMap, + ) -> Vec<(NodeId, Lifetime)> { + let mut result = Vec::new(); + + for lifetime in lifetimes_in_bounds { + let res = self.resolver.get_lifetime_res(lifetime.id).unwrap_or(LifetimeRes::Error); + debug!(?res); + + match res { + LifetimeRes::Param { param: old_def_id, binder: _ } => { + if remapping.get(&old_def_id).is_none() { + let node_id = self.next_node_id(); + + let new_def_id = self.create_def( + parent_def_id, + node_id, + DefPathData::LifetimeNs(lifetime.ident.name), + ); + remapping.insert(old_def_id, new_def_id); + + result.push((node_id, *lifetime)); + } + } + + LifetimeRes::Fresh { param, binder: _ } => { + debug_assert_eq!(lifetime.ident.name, kw::UnderscoreLifetime); + let old_def_id = self.local_def_id(param); + if remapping.get(&old_def_id).is_none() { + let node_id = self.next_node_id(); + + let new_def_id = self.create_def( + parent_def_id, + node_id, + DefPathData::LifetimeNs(kw::UnderscoreLifetime), + ); + remapping.insert(old_def_id, new_def_id); + + result.push((node_id, *lifetime)); + } + } + + LifetimeRes::Static | LifetimeRes::Error => {} + + res => { + let bug_msg = format!( + "Unexpected lifetime resolution {:?} for {:?} at {:?}", + res, lifetime.ident, lifetime.ident.span + ); + span_bug!(lifetime.ident.span, "{}", bug_msg); + } + } + } + + result + } + fn lower_fn_params_to_names(&mut self, decl: &FnDecl) -> &'hir [Ident] { // Skip the `...` (`CVarArgs`) trailing arguments from the AST, // as they are not explicit in HIR/Ty function signatures. @@ -1582,11 +1726,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // // type OpaqueTy = impl Future; // - // `inputs`: lowered types of parameters to the function (used to collect lifetimes) // `output`: unlowered output type (`T` in `-> T`) // `fn_def_id`: `DefId` of the parent function (used to create child impl trait definition) // `opaque_ty_node_id`: `NodeId` of the opaque `impl Trait` type that should be created - // `elided_lt_replacement`: replacement for elided lifetimes in the return type #[tracing::instrument(level = "debug", skip(self))] fn lower_async_fn_ret_ty( &mut self, @@ -1643,90 +1785,126 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // by the opaque type. This should include all in-scope // lifetime parameters, including those defined in-band. - let mut captures = FxHashMap::default(); + // Contains the new lifetime definitions created for the TAIT (if any) generated for the + // return type. + let mut collected_lifetimes = Vec::new(); + let mut new_remapping = FxHashMap::default(); let extra_lifetime_params = self.resolver.take_extra_lifetime_params(opaque_ty_node_id); debug!(?extra_lifetime_params); for (ident, outer_node_id, outer_res) in extra_lifetime_params { - let Ident { name, span } = ident; let outer_def_id = self.local_def_id(outer_node_id); let inner_node_id = self.next_node_id(); // Add a definition for the in scope lifetime def. - self.create_def(opaque_ty_def_id, inner_node_id, DefPathData::LifetimeNs(name)); + let inner_def_id = self.create_def( + opaque_ty_def_id, + inner_node_id, + DefPathData::LifetimeNs(ident.name), + ); + new_remapping.insert(outer_def_id, inner_def_id); - let (p_name, inner_res) = match outer_res { + let inner_res = match outer_res { // Input lifetime like `'a`: LifetimeRes::Param { param, .. } => { - (hir::ParamName::Plain(ident), LifetimeRes::Param { param, binder: fn_node_id }) + LifetimeRes::Param { param, binder: fn_node_id } } // Input lifetime like `'1`: LifetimeRes::Fresh { param, .. } => { - (hir::ParamName::Fresh, LifetimeRes::Fresh { param, binder: fn_node_id }) + LifetimeRes::Fresh { param, binder: fn_node_id } } LifetimeRes::Static | LifetimeRes::Error => continue, res => { - panic!("Unexpected lifetime resolution {:?} for {:?} at {:?}", res, ident, span) + panic!( + "Unexpected lifetime resolution {:?} for {:?} at {:?}", + res, ident, ident.span + ) } }; - captures.insert(outer_def_id, (span, inner_node_id, p_name, inner_res)); + let lifetime = Lifetime { id: outer_node_id, ident }; + collected_lifetimes.push((inner_node_id, lifetime, Some(inner_res))); } - debug!(?captures); - - self.with_hir_id_owner(opaque_ty_node_id, |this| { - let future_bound = - this.while_capturing_lifetimes(opaque_ty_def_id, &mut captures, |this| { - // We have to be careful to get elision right here. The - // idea is that we create a lifetime parameter for each - // lifetime in the return type. So, given a return type - // like `async fn foo(..) -> &[&u32]`, we lower to `impl - // Future`. - // - // Then, we will create `fn foo(..) -> Foo<'_, '_>`, and - // hence the elision takes place at the fn site. - this.lower_async_fn_output_type_to_future_bound(output, fn_def_id, span) - }); - debug!("lower_async_fn_ret_ty: future_bound={:#?}", future_bound); - debug!("lower_async_fn_ret_ty: captures={:#?}", captures); + debug!(?collected_lifetimes); - let generic_params = - this.arena.alloc_from_iter(captures.iter().map(|(_, &(span, p_id, p_name, _))| { - let hir_id = this.lower_node_id(p_id); - debug_assert_ne!(this.opt_local_def_id(p_id), None); + // We only want to capture the lifetimes that appear in the bounds. So visit the bounds to + // find out exactly which ones those are. + // in fn return position, like the `fn test<'a>() -> impl Debug + 'a` example, + // we only keep the lifetimes that appear in the `impl Debug` itself: + let lifetimes_to_remap = lifetime_collector::lifetimes_in_ret_ty(&self.resolver, output); + debug!(?lifetimes_to_remap); - let kind = if p_name.ident().name == kw::UnderscoreLifetime { - hir::LifetimeParamKind::Elided - } else { - hir::LifetimeParamKind::Explicit - }; + self.with_hir_id_owner(opaque_ty_node_id, |this| { + // If this opaque type is only capturing a subset of the lifetimes (those that appear + // in bounds), then create the new lifetime parameters required and create a mapping + // from the old `'a` (on the function) to the new `'a` (on the opaque type). + collected_lifetimes.extend( + this.create_lifetime_defs( + opaque_ty_def_id, + &lifetimes_to_remap, + &mut new_remapping, + ) + .into_iter() + .map(|(new_node_id, lifetime)| (new_node_id, lifetime, None)), + ); + debug!(?collected_lifetimes); + debug!(?new_remapping); + + // Install the remapping from old to new (if any): + this.with_remapping(new_remapping, |this| { + // We have to be careful to get elision right here. The + // idea is that we create a lifetime parameter for each + // lifetime in the return type. So, given a return type + // like `async fn foo(..) -> &[&u32]`, we lower to `impl + // Future`. + // + // Then, we will create `fn foo(..) -> Foo<'_, '_>`, and + // hence the elision takes place at the fn site. + let future_bound = + this.lower_async_fn_output_type_to_future_bound(output, fn_def_id, span); + + let generic_params = this.arena.alloc_from_iter(collected_lifetimes.iter().map( + |&(new_node_id, lifetime, _)| { + let hir_id = this.lower_node_id(new_node_id); + debug_assert_ne!(this.opt_local_def_id(new_node_id), None); + + let (name, kind) = if lifetime.ident.name == kw::UnderscoreLifetime { + (hir::ParamName::Fresh, hir::LifetimeParamKind::Elided) + } else { + ( + hir::ParamName::Plain(lifetime.ident), + hir::LifetimeParamKind::Explicit, + ) + }; - hir::GenericParam { - hir_id, - name: p_name, - span, - pure_wrt_drop: false, - kind: hir::GenericParamKind::Lifetime { kind }, - colon_span: None, - } - })); - debug!("lower_async_fn_ret_ty: generic_params={:#?}", generic_params); - - let opaque_ty_item = hir::OpaqueTy { - generics: this.arena.alloc(hir::Generics { - params: generic_params, - predicates: &[], - has_where_clause_predicates: false, - where_clause_span: this.lower_span(span), - span: this.lower_span(span), - }), - bounds: arena_vec![this; future_bound], - origin: hir::OpaqueTyOrigin::AsyncFn(fn_def_id), - }; + hir::GenericParam { + hir_id, + name, + span: lifetime.ident.span, + pure_wrt_drop: false, + kind: hir::GenericParamKind::Lifetime { kind }, + colon_span: None, + } + }, + )); + debug!("lower_async_fn_ret_ty: generic_params={:#?}", generic_params); + + let opaque_ty_item = hir::OpaqueTy { + generics: this.arena.alloc(hir::Generics { + params: generic_params, + predicates: &[], + has_where_clause_predicates: false, + where_clause_span: this.lower_span(span), + span: this.lower_span(span), + }), + bounds: arena_vec![this; future_bound], + origin: hir::OpaqueTyOrigin::AsyncFn(fn_def_id), + }; - trace!("exist ty from async fn def id: {:#?}", opaque_ty_def_id); - this.generate_opaque_type(opaque_ty_def_id, opaque_ty_item, span, opaque_ty_span) + trace!("exist ty from async fn def id: {:#?}", opaque_ty_def_id); + this.generate_opaque_type(opaque_ty_def_id, opaque_ty_item, span, opaque_ty_span) + }) }); // As documented above, we need to create the lifetime @@ -1744,13 +1922,24 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // // For the "output" lifetime parameters, we just want to // generate `'_`. - let generic_args = - self.arena.alloc_from_iter(captures.into_iter().map(|(_, (span, _, p_name, res))| { + let generic_args = self.arena.alloc_from_iter(collected_lifetimes.into_iter().map( + |(_, lifetime, res)| { let id = self.next_node_id(); - let ident = Ident::new(p_name.ident().name, span); + let span = lifetime.ident.span; + + let ident = if lifetime.ident.name == kw::UnderscoreLifetime { + Ident::with_dummy_span(kw::UnderscoreLifetime) + } else { + lifetime.ident + }; + + let res = res.unwrap_or( + self.resolver.get_lifetime_res(lifetime.id).unwrap_or(LifetimeRes::Error), + ); let l = self.new_named_lifetime_with_res(id, span, ident, res); hir::GenericArg::Lifetime(l) - })); + }, + )); // Create the `Foo<...>` reference itself. Note that the `type // Foo = impl Trait` is, internally, created as a child of the @@ -1820,8 +2009,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { fn lower_lifetime(&mut self, l: &Lifetime) -> hir::Lifetime { let span = self.lower_span(l.ident.span); let ident = self.lower_ident(l.ident); - let res = self.resolver.get_lifetime_res(l.id).unwrap_or(LifetimeRes::Error); - self.new_named_lifetime_with_res(l.id, span, ident, res) + self.new_named_lifetime(l.id, l.id, span, ident) } #[tracing::instrument(level = "debug", skip(self))] @@ -1832,55 +2020,17 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ident: Ident, res: LifetimeRes, ) -> hir::Lifetime { - debug!(?self.captured_lifetimes); let name = match res { - LifetimeRes::Param { mut param, binder } => { + LifetimeRes::Param { param, .. } => { let p_name = ParamName::Plain(ident); - if let Some(mut captured_lifetimes) = self.captured_lifetimes.take() { - if !captured_lifetimes.binders_to_ignore.contains(&binder) { - match captured_lifetimes.captures.entry(param) { - Entry::Occupied(o) => param = self.local_def_id(o.get().1), - Entry::Vacant(v) => { - let p_id = self.next_node_id(); - let p_def_id = self.create_def( - captured_lifetimes.parent_def_id, - p_id, - DefPathData::LifetimeNs(p_name.ident().name), - ); - - v.insert((span, p_id, p_name, res)); - param = p_def_id; - } - } - } + let param = self.resolver.get_remapped_def_id(param); - self.captured_lifetimes = Some(captured_lifetimes); - } hir::LifetimeName::Param(param, p_name) } - LifetimeRes::Fresh { param, binder } => { + LifetimeRes::Fresh { param, .. } => { debug_assert_eq!(ident.name, kw::UnderscoreLifetime); - let mut param = self.local_def_id(param); - if let Some(mut captured_lifetimes) = self.captured_lifetimes.take() { - if !captured_lifetimes.binders_to_ignore.contains(&binder) { - match captured_lifetimes.captures.entry(param) { - Entry::Occupied(o) => param = self.local_def_id(o.get().1), - Entry::Vacant(v) => { - let p_id = self.next_node_id(); - let p_def_id = self.create_def( - captured_lifetimes.parent_def_id, - p_id, - DefPathData::LifetimeNs(kw::UnderscoreLifetime), - ); - - v.insert((span, p_id, ParamName::Fresh, res)); - param = p_def_id; - } - } - } + let param = self.local_def_id(param); - self.captured_lifetimes = Some(captured_lifetimes); - } hir::LifetimeName::Param(param, ParamName::Fresh) } LifetimeRes::Infer => hir::LifetimeName::Infer, @@ -1888,11 +2038,23 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { LifetimeRes::Error => hir::LifetimeName::Error, res => panic!("Unexpected lifetime resolution {:?} for {:?} at {:?}", res, ident, span), }; - debug!(?self.captured_lifetimes); + debug!(?name); hir::Lifetime { hir_id: self.lower_node_id(id), span: self.lower_span(span), name } } + #[tracing::instrument(level = "debug", skip(self))] + fn new_named_lifetime( + &mut self, + id: NodeId, + new_id: NodeId, + span: Span, + ident: Ident, + ) -> hir::Lifetime { + let res = self.resolver.get_lifetime_res(id).unwrap_or(LifetimeRes::Error); + self.new_named_lifetime_with_res(new_id, span, ident, res) + } + fn lower_generic_params_mut<'s>( &'s mut self, params: &'s [GenericParam], @@ -1975,14 +2137,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { p: &PolyTraitRef, itctx: ImplTraitContext, ) -> hir::PolyTraitRef<'hir> { - self.with_lifetime_binder( - p.trait_ref.ref_id, - &p.bound_generic_params, - |this, bound_generic_params| { - let trait_ref = this.lower_trait_ref(&p.trait_ref, itctx); - hir::PolyTraitRef { bound_generic_params, trait_ref, span: this.lower_span(p.span) } - }, - ) + let bound_generic_params = + self.lower_lifetime_binder(p.trait_ref.ref_id, &p.bound_generic_params); + let trait_ref = self.lower_trait_ref(&p.trait_ref, itctx); + hir::PolyTraitRef { bound_generic_params, trait_ref, span: self.lower_span(p.span) } } fn lower_mt(&mut self, mt: &MutTy, itctx: ImplTraitContext) -> hir::MutTy<'hir> { @@ -2015,7 +2173,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // Add a definition for the in-band `Param`. let def_id = self.local_def_id(node_id); - let hir_bounds = self.lower_param_bounds(bounds, ImplTraitContext::Universal); // Set the name to `impl Bound1 + Bound2`. let param = hir::GenericParam { hir_id: self.lower_node_id(node_id), @@ -2030,7 +2187,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ident, node_id, &GenericParamKind::Type { default: None }, - hir_bounds, + bounds, + ImplTraitContext::Universal, hir::PredicateOrigin::ImplTrait, ); diff --git a/compiler/rustc_ast_lowering/src/lifetime_collector.rs b/compiler/rustc_ast_lowering/src/lifetime_collector.rs new file mode 100644 index 0000000000000..81006e00fd4e5 --- /dev/null +++ b/compiler/rustc_ast_lowering/src/lifetime_collector.rs @@ -0,0 +1,115 @@ +use super::ResolverAstLoweringExt; +use rustc_ast::visit::{self, BoundKind, LifetimeCtxt, Visitor}; +use rustc_ast::{ + FnRetTy, GenericBounds, Lifetime, NodeId, PathSegment, PolyTraitRef, TraitBoundModifier, Ty, + TyKind, +}; +use rustc_hir::def::LifetimeRes; +use rustc_middle::span_bug; +use rustc_middle::ty::ResolverAstLowering; +use rustc_span::symbol::{kw, Ident}; +use rustc_span::Span; + +struct LifetimeCollectVisitor<'ast> { + resolver: &'ast ResolverAstLowering, + current_binders: Vec, + collected_lifetimes: Vec, +} + +impl<'ast> LifetimeCollectVisitor<'ast> { + fn new(resolver: &'ast ResolverAstLowering) -> Self { + Self { resolver, current_binders: Vec::new(), collected_lifetimes: Vec::new() } + } + + fn record_lifetime_use(&mut self, lifetime: Lifetime) { + match self.resolver.get_lifetime_res(lifetime.id).unwrap_or(LifetimeRes::Error) { + LifetimeRes::Param { binder, .. } | LifetimeRes::Fresh { binder, .. } => { + if !self.current_binders.contains(&binder) { + if !self.collected_lifetimes.contains(&lifetime) { + self.collected_lifetimes.push(lifetime); + } + } + } + LifetimeRes::Static | LifetimeRes::Error => { + if !self.collected_lifetimes.contains(&lifetime) { + self.collected_lifetimes.push(lifetime); + } + } + LifetimeRes::Infer => {} + res => { + let bug_msg = format!( + "Unexpected lifetime resolution {:?} for {:?} at {:?}", + res, lifetime.ident, lifetime.ident.span + ); + span_bug!(lifetime.ident.span, "{}", bug_msg); + } + } + } + + /// This collect lifetimes that are elided, for nodes like `Foo` where there are no explicit + /// lifetime nodes. Is equivalent to having "pseudo" nodes introduced for each of the node ids + /// in the list start..end. + fn record_elided_anchor(&mut self, node_id: NodeId, span: Span) { + if let Some(LifetimeRes::ElidedAnchor { start, end }) = + self.resolver.get_lifetime_res(node_id) + { + for i in start..end { + let lifetime = Lifetime { id: i, ident: Ident::new(kw::UnderscoreLifetime, span) }; + self.record_lifetime_use(lifetime); + } + } + } +} + +impl<'ast> Visitor<'ast> for LifetimeCollectVisitor<'ast> { + fn visit_lifetime(&mut self, lifetime: &'ast Lifetime, _: LifetimeCtxt) { + self.record_lifetime_use(*lifetime); + } + + fn visit_path_segment(&mut self, path_span: Span, path_segment: &'ast PathSegment) { + self.record_elided_anchor(path_segment.id, path_span); + visit::walk_path_segment(self, path_span, path_segment); + } + + fn visit_poly_trait_ref(&mut self, t: &'ast PolyTraitRef, m: &'ast TraitBoundModifier) { + self.current_binders.push(t.trait_ref.ref_id); + + visit::walk_poly_trait_ref(self, t, m); + + self.current_binders.pop(); + } + + fn visit_ty(&mut self, t: &'ast Ty) { + match t.kind { + TyKind::BareFn(_) => { + self.current_binders.push(t.id); + visit::walk_ty(self, t); + self.current_binders.pop(); + } + TyKind::Rptr(None, _) => { + self.record_elided_anchor(t.id, t.span); + visit::walk_ty(self, t); + } + _ => { + visit::walk_ty(self, t); + } + } + } +} + +pub fn lifetimes_in_ret_ty(resolver: &ResolverAstLowering, ret_ty: &FnRetTy) -> Vec { + let mut visitor = LifetimeCollectVisitor::new(resolver); + visitor.visit_fn_ret_ty(ret_ty); + visitor.collected_lifetimes +} + +pub fn lifetimes_in_bounds( + resolver: &ResolverAstLowering, + bounds: &GenericBounds, +) -> Vec { + let mut visitor = LifetimeCollectVisitor::new(resolver); + for bound in bounds { + visitor.visit_param_bound(bound, BoundKind::Bound); + } + visitor.collected_lifetimes +} diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 4b081316e2308..02da02568d7e5 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -177,6 +177,11 @@ pub struct ResolverAstLowering { pub label_res_map: NodeMap, /// Resolutions for lifetimes. pub lifetimes_res_map: NodeMap, + /// Mapping from generics `def_id`s to TAIT generics `def_id`s. + /// For each captured lifetime (e.g., 'a), we create a new lifetime parameter that is a generic + /// defined on the TAIT, so we have type Foo<'a1> = ... and we establish a mapping in this + /// field from the original parameter 'a to the new parameter 'a1. + pub generics_def_id_map: Vec>, /// Lifetime parameters that lowering will have to introduce. pub extra_lifetime_params_map: NodeMap>, diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 31d10008efbfb..ef3c3da89c572 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -913,6 +913,11 @@ pub struct Resolver<'a> { label_res_map: NodeMap, /// Resolutions for lifetimes. lifetimes_res_map: NodeMap, + /// Mapping from generics `def_id`s to TAIT generics `def_id`s. + /// For each captured lifetime (e.g., 'a), we create a new lifetime parameter that is a generic + /// defined on the TAIT, so we have type Foo<'a1> = ... and we establish a mapping in this + /// field from the original parameter 'a to the new parameter 'a1. + generics_def_id_map: Vec>, /// Lifetime parameters that lowering will have to introduce. extra_lifetime_params_map: NodeMap>, @@ -1277,6 +1282,7 @@ impl<'a> Resolver<'a> { import_res_map: Default::default(), label_res_map: Default::default(), lifetimes_res_map: Default::default(), + generics_def_id_map: Vec::new(), extra_lifetime_params_map: Default::default(), extern_crate_map: Default::default(), reexport_map: FxHashMap::default(), @@ -1444,6 +1450,7 @@ impl<'a> Resolver<'a> { import_res_map: self.import_res_map, label_res_map: self.label_res_map, lifetimes_res_map: self.lifetimes_res_map, + generics_def_id_map: self.generics_def_id_map, extra_lifetime_params_map: self.extra_lifetime_params_map, next_node_id: self.next_node_id, node_id_to_def_id: self.node_id_to_def_id, @@ -1488,6 +1495,7 @@ impl<'a> Resolver<'a> { import_res_map: self.import_res_map.clone(), label_res_map: self.label_res_map.clone(), lifetimes_res_map: self.lifetimes_res_map.clone(), + generics_def_id_map: self.generics_def_id_map.clone(), extra_lifetime_params_map: self.extra_lifetime_params_map.clone(), next_node_id: self.next_node_id.clone(), node_id_to_def_id: self.node_id_to_def_id.clone(), diff --git a/src/test/ui/impl-trait/rpit-assoc-pair-with-lifetime.rs b/src/test/ui/impl-trait/rpit-assoc-pair-with-lifetime.rs new file mode 100644 index 0000000000000..a4e603de1ac24 --- /dev/null +++ b/src/test/ui/impl-trait/rpit-assoc-pair-with-lifetime.rs @@ -0,0 +1,7 @@ +// check-pass + +pub fn iter<'a>(v: Vec<(u32, &'a u32)>) -> impl DoubleEndedIterator { + v.into_iter() +} + +fn main() {}