Auto merge of #99867 - spastorino:refactor-remap-lifetimes, r=nikomatsakis
Split create_def and lowering of lifetimes for opaque types and bare async fns r? `@cjgillot` This work is kind of half-way, but I think it could be merged anyway. I think we should be able to remove all the vacant arms in `new_named_lifetime_with_res`, if I'm not wrong that requires visiting more nodes. We can do that as a follow up. In follow-up PRs, besides the thing mentioned previously, I'll be trying to remove `LifetimeCaptureContext`, `captured_lifetimes` as a global data structure, global `binders_to_ignore` and all their friends :). Also try to remap in a more general way based on def-ids.
This commit is contained in:
commit
cdfd675a63
8 changed files with 579 additions and 287 deletions
|
@ -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,
|
||||
|
|
|
@ -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 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: this.lower_span(fn_decl_span),
|
||||
movability: generator_option,
|
||||
});
|
||||
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 bound_generic_params = self.lower_lifetime_binder(closure_id, generic_params);
|
||||
|
||||
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)
|
||||
})
|
||||
// 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.
|
||||
|
|
|
@ -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<hir::WherePredicate<'hir>> {
|
||||
// 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,
|
||||
|
|
|
@ -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<LifetimeCaptureContext>,
|
||||
|
||||
current_hir_id_owner: LocalDefId,
|
||||
item_local_id_counter: hir::ItemLocalId,
|
||||
local_id_to_def_id: SortedMap<ItemLocalId, LocalDefId>,
|
||||
|
@ -129,28 +128,6 @@ struct LoweringContext<'a, 'hir> {
|
|||
allow_into_future: Option<Lrc<[Symbol]>>,
|
||||
}
|
||||
|
||||
/// 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<NodeId>,
|
||||
}
|
||||
|
||||
trait ResolverAstLoweringExt {
|
||||
fn legacy_const_generic_args(&self, expr: &Expr) -> Option<Vec<usize>>;
|
||||
fn get_partial_res(&self, id: NodeId) -> Option<PartialRes>;
|
||||
|
@ -159,6 +136,12 @@ trait ResolverAstLoweringExt {
|
|||
fn get_lifetime_res(&self, id: NodeId) -> Option<LifetimeRes>;
|
||||
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<LocalDefId> {
|
||||
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<R>(
|
||||
&mut self,
|
||||
remap: FxHashMap<LocalDefId, LocalDefId>,
|
||||
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<T>(
|
||||
&mut self,
|
||||
parent_def_id: LocalDefId,
|
||||
captures: &mut FxHashMap<LocalDefId, (Span, NodeId, ParamName, LifetimeRes)>,
|
||||
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<T>(
|
||||
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<T>(&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);
|
||||
|
||||
// Contains the new lifetime definitions created for the TAIT (if any).
|
||||
let mut collected_lifetimes = Vec::new();
|
||||
|
||||
// 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 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,
|
||||
)
|
||||
};
|
||||
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);
|
||||
|
||||
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);
|
||||
// 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 kind = if p_name.ident().name == kw::UnderscoreLifetime {
|
||||
hir::LifetimeParamKind::Elided
|
||||
} else {
|
||||
hir::LifetimeParamKind::Explicit
|
||||
};
|
||||
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,
|
||||
}
|
||||
},
|
||||
));
|
||||
hir::GenericParam {
|
||||
hir_id,
|
||||
name,
|
||||
span: lifetime.ident.span,
|
||||
pure_wrt_drop: false,
|
||||
kind: hir::GenericParamKind::Lifetime { kind },
|
||||
colon_span: None,
|
||||
}
|
||||
},
|
||||
));
|
||||
debug!(?lifetime_defs);
|
||||
|
||||
debug!("lower_opaque_impl_trait: lifetime_defs={:#?}", 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,
|
||||
};
|
||||
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<LocalDefId, LocalDefId>,
|
||||
) -> 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<generics_from_parent_fn> = impl Future<Output = T>;
|
||||
//
|
||||
// `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);
|
||||
debug!(?collected_lifetimes);
|
||||
|
||||
// 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);
|
||||
|
||||
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<Output = &'1 [ &'2 u32 ]>`.
|
||||
//
|
||||
// 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);
|
||||
// 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);
|
||||
|
||||
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);
|
||||
// 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<Output = &'1 [ &'2 u32 ]>`.
|
||||
//
|
||||
// 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 kind = if p_name.ident().name == kw::UnderscoreLifetime {
|
||||
hir::LifetimeParamKind::Elided
|
||||
} else {
|
||||
hir::LifetimeParamKind::Explicit
|
||||
};
|
||||
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);
|
||||
|
||||
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 (name, kind) = if lifetime.ident.name == kw::UnderscoreLifetime {
|
||||
(hir::ParamName::Fresh, hir::LifetimeParamKind::Elided)
|
||||
} else {
|
||||
(
|
||||
hir::ParamName::Plain(lifetime.ident),
|
||||
hir::LifetimeParamKind::Explicit,
|
||||
)
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
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)
|
||||
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)
|
||||
})
|
||||
});
|
||||
|
||||
// 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),
|
||||
);
|
||||
let param = self.resolver.get_remapped_def_id(param);
|
||||
|
||||
v.insert((span, p_id, p_name, res));
|
||||
param = p_def_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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),
|
||||
);
|
||||
let param = self.local_def_id(param);
|
||||
|
||||
v.insert((span, p_id, ParamName::Fresh, res));
|
||||
param = p_def_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
);
|
||||
|
||||
|
|
115
compiler/rustc_ast_lowering/src/lifetime_collector.rs
Normal file
115
compiler/rustc_ast_lowering/src/lifetime_collector.rs
Normal file
|
@ -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<NodeId>,
|
||||
collected_lifetimes: Vec<Lifetime>,
|
||||
}
|
||||
|
||||
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<T>` 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<Lifetime> {
|
||||
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<Lifetime> {
|
||||
let mut visitor = LifetimeCollectVisitor::new(resolver);
|
||||
for bound in bounds {
|
||||
visitor.visit_param_bound(bound, BoundKind::Bound);
|
||||
}
|
||||
visitor.collected_lifetimes
|
||||
}
|
|
@ -177,6 +177,11 @@ pub struct ResolverAstLowering {
|
|||
pub label_res_map: NodeMap<ast::NodeId>,
|
||||
/// Resolutions for lifetimes.
|
||||
pub lifetimes_res_map: NodeMap<LifetimeRes>,
|
||||
/// 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<FxHashMap<LocalDefId, LocalDefId>>,
|
||||
/// Lifetime parameters that lowering will have to introduce.
|
||||
pub extra_lifetime_params_map: NodeMap<Vec<(Ident, ast::NodeId, LifetimeRes)>>,
|
||||
|
||||
|
|
|
@ -913,6 +913,11 @@ pub struct Resolver<'a> {
|
|||
label_res_map: NodeMap<NodeId>,
|
||||
/// Resolutions for lifetimes.
|
||||
lifetimes_res_map: NodeMap<LifetimeRes>,
|
||||
/// 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<FxHashMap<LocalDefId, LocalDefId>>,
|
||||
/// Lifetime parameters that lowering will have to introduce.
|
||||
extra_lifetime_params_map: NodeMap<Vec<(Ident, NodeId, LifetimeRes)>>,
|
||||
|
||||
|
@ -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(),
|
||||
|
|
7
src/test/ui/impl-trait/rpit-assoc-pair-with-lifetime.rs
Normal file
7
src/test/ui/impl-trait/rpit-assoc-pair-with-lifetime.rs
Normal file
|
@ -0,0 +1,7 @@
|
|||
// check-pass
|
||||
|
||||
pub fn iter<'a>(v: Vec<(u32, &'a u32)>) -> impl DoubleEndedIterator<Item = (u32, &u32)> {
|
||||
v.into_iter()
|
||||
}
|
||||
|
||||
fn main() {}
|
Loading…
Add table
Reference in a new issue