提交 3f58828f 编写于 作者: B bors

Auto merge of #94773 - cjgillot:lifetime-fresh-did, r=oli-obk

Identify anonymous lifetimes by their DefId in HIR.

`ParamName::Fresh` currently identifies anonymous lifetimes by an `usize` index computed from the number of lifetimes in scope. This makes the behaviour of lowering dependent on the contents of the surrounding item in unpredictable ways.

This PR replaces this index by the `LocalDefId` of the synthetized generic lifetime parameter. This makes obvious which parameter the lifetime corresponds to.
......@@ -95,6 +95,7 @@ fn visit_fn(&mut self, fk: FnKind<'a>, sp: Span, _: NodeId) {
}
fn visit_assoc_item(&mut self, item: &'a AssocItem, ctxt: AssocCtxt) {
debug!(in_scope_lifetimes = ?self.lctx.in_scope_lifetimes);
self.lctx.with_hir_id_owner(item.id, |lctx| match ctxt {
AssocCtxt::Trait => hir::OwnerNode::TraitItem(lctx.lower_trait_item(item)),
AssocCtxt::Impl => hir::OwnerNode::ImplItem(lctx.lower_impl_item(item)),
......@@ -118,35 +119,42 @@ impl<'hir> LoweringContext<'_, 'hir> {
// This should only be used with generics that have already had their
// in-band lifetimes added. In practice, this means that this function is
// only used when lowering a child item of a trait or impl.
#[tracing::instrument(level = "debug", skip(self, f))]
fn with_parent_item_lifetime_defs<T>(
&mut self,
parent_hir_id: LocalDefId,
f: impl FnOnce(&mut Self) -> T,
) -> T {
let old_len = self.in_scope_lifetimes.len();
let parent_generics = match self.owners[parent_hir_id].unwrap().node().expect_item().kind {
hir::ItemKind::Impl(hir::Impl { ref generics, .. })
| hir::ItemKind::Trait(_, _, ref generics, ..) => generics.params,
_ => &[],
};
let lt_def_names = parent_generics.iter().filter_map(|param| match param.kind {
hir::GenericParamKind::Lifetime { .. } => Some(param.name.normalize_to_macros_2_0()),
_ => None,
});
self.in_scope_lifetimes.extend(lt_def_names);
let lt_def_names = parent_generics
.iter()
.filter_map(|param| match param.kind {
hir::GenericParamKind::Lifetime { .. } => {
Some(param.name.normalize_to_macros_2_0())
}
_ => None,
})
.collect();
let old_in_scope_lifetimes = mem::replace(&mut self.in_scope_lifetimes, lt_def_names);
debug!(in_scope_lifetimes = ?self.in_scope_lifetimes);
let res = f(self);
self.in_scope_lifetimes.truncate(old_len);
self.in_scope_lifetimes = old_in_scope_lifetimes;
res
}
// Clears (and restores) the `in_scope_lifetimes` field. Used when
// visiting nested items, which never inherit in-scope lifetimes
// from their surrounding environment.
#[tracing::instrument(level = "debug", skip(self, f))]
fn without_in_scope_lifetime_defs<T>(&mut self, f: impl FnOnce(&mut Self) -> T) -> T {
let old_in_scope_lifetimes = mem::replace(&mut self.in_scope_lifetimes, vec![]);
debug!(?old_in_scope_lifetimes);
// this vector is only used when walking over impl headers,
// input types, and the like, and should not be non-empty in
......
......@@ -129,20 +129,16 @@ struct LoweringContext<'a, 'hir: 'a> {
/// written at all (e.g., `&T` or `std::cell::Ref<T>`).
anonymous_lifetime_mode: AnonymousLifetimeMode,
/// Used to create lifetime definitions from in-band lifetime usages.
/// e.g., `fn foo(x: &'x u8) -> &'x u8` to `fn foo<'x>(x: &'x u8) -> &'x u8`
/// When a named lifetime is encountered in a function or impl header and
/// has not been defined
/// (i.e., it doesn't appear in the in_scope_lifetimes list), it is added
/// Used to create lifetime definitions for anonymous lifetimes.
/// When an anonymous lifetime is encountered in a function or impl header and
/// requires to create a fresh lifetime parameter, it is added
/// to this list. The results of this list are then added to the list of
/// lifetime definitions in the corresponding impl or function generics.
lifetimes_to_define: Vec<(Span, ParamName)>,
lifetimes_to_define: Vec<(Span, NodeId)>,
/// `true` if in-band lifetimes are being collected. This is used to
/// indicate whether or not we're in a place where new lifetimes will result
/// in in-band lifetime definitions, such a function or an impl header,
/// including implicit lifetimes from `impl_header_lifetime_elision`.
is_collecting_anonymous_lifetimes: bool,
/// If anonymous lifetimes are being collected, this field holds the parent
/// `LocalDefId` to create the fresh lifetime parameters' `LocalDefId`.
is_collecting_anonymous_lifetimes: Option<LocalDefId>,
/// Currently in-scope lifetimes defined in impl headers, fn headers, or HRTB.
/// We always store a `normalize_to_macros_2_0()` version of the param-name in this
......@@ -375,7 +371,7 @@ pub fn lower_crate<'a, 'hir>(
task_context: None,
current_item: None,
lifetimes_to_define: Vec::new(),
is_collecting_anonymous_lifetimes: false,
is_collecting_anonymous_lifetimes: None,
in_scope_lifetimes: Vec::new(),
allow_try_trait: Some([sym::try_trait_v2][..].into()),
allow_gen_future: Some([sym::gen_future][..].into()),
......@@ -720,9 +716,11 @@ fn lower_ident(&self, ident: Ident) -> Ident {
/// parameter while `f` is running (and restored afterwards).
fn collect_in_band_defs<T>(
&mut self,
parent_def_id: LocalDefId,
f: impl FnOnce(&mut Self) -> T,
) -> (Vec<(Span, ParamName)>, T) {
let was_collecting = std::mem::replace(&mut self.is_collecting_anonymous_lifetimes, true);
) -> (Vec<(Span, NodeId)>, T) {
let was_collecting =
std::mem::replace(&mut self.is_collecting_anonymous_lifetimes, Some(parent_def_id));
let len = self.lifetimes_to_define.len();
let res = f(self);
......@@ -733,49 +731,41 @@ fn collect_in_band_defs<T>(
}
/// Converts a lifetime into a new generic parameter.
fn lifetime_to_generic_param(
fn fresh_lifetime_to_generic_param(
&mut self,
span: Span,
hir_name: ParamName,
parent_def_id: LocalDefId,
node_id: NodeId,
) -> hir::GenericParam<'hir> {
let node_id = self.resolver.next_node_id();
// Get the name we'll use to make the def-path. Note
// that collisions are ok here and this shouldn't
// really show up for end-user.
let (str_name, kind) = match hir_name {
ParamName::Plain(ident) => (ident.name, hir::LifetimeParamKind::Explicit),
ParamName::Fresh(_) => (kw::UnderscoreLifetime, hir::LifetimeParamKind::Elided),
ParamName::Error => (kw::UnderscoreLifetime, hir::LifetimeParamKind::Error),
};
// Add a definition for the in-band lifetime def.
self.resolver.create_def(
parent_def_id,
node_id,
DefPathData::LifetimeNs(str_name),
ExpnId::root(),
span.with_parent(None),
);
let hir_id = self.lower_node_id(node_id);
let def_id = self.resolver.local_def_id(node_id);
hir::GenericParam {
hir_id: self.lower_node_id(node_id),
name: hir_name,
hir_id,
name: hir::ParamName::Fresh(def_id),
bounds: &[],
span: self.lower_span(span),
pure_wrt_drop: false,
kind: hir::GenericParamKind::Lifetime { kind },
kind: hir::GenericParamKind::Lifetime { kind: hir::LifetimeParamKind::Elided },
}
}
/// When we have either an elided or `'_` lifetime in an impl
/// header, we convert it to an in-band lifetime.
fn collect_fresh_anonymous_lifetime(&mut self, span: Span) -> ParamName {
assert!(self.is_collecting_anonymous_lifetimes);
let index = self.lifetimes_to_define.len() + self.in_scope_lifetimes.len();
let hir_name = ParamName::Fresh(index);
self.lifetimes_to_define.push((span, hir_name));
let Some(parent_def_id) = self.is_collecting_anonymous_lifetimes else { panic!() };
let node_id = self.resolver.next_node_id();
// Add a definition for the in-band lifetime def.
let param_def_id = self.resolver.create_def(
parent_def_id,
node_id,
DefPathData::LifetimeNs(kw::UnderscoreLifetime),
ExpnId::root(),
span.with_parent(None),
);
let hir_name = ParamName::Fresh(param_def_id);
self.lifetimes_to_define.push((span, node_id));
hir_name
}
......@@ -817,7 +807,7 @@ fn add_in_band_defs<T>(
f: impl FnOnce(&mut Self, &mut Vec<hir::GenericParam<'hir>>) -> T,
) -> (hir::Generics<'hir>, T) {
let (lifetimes_to_define, (mut lowered_generics, impl_trait_defs, res)) = self
.collect_in_band_defs(|this| {
.collect_in_band_defs(parent_def_id, |this| {
this.with_anonymous_lifetime_mode(anonymous_lifetime_mode, |this| {
this.with_in_scope_lifetime_defs(&generics.params, |this| {
let mut impl_trait_defs = Vec::new();
......@@ -844,9 +834,7 @@ fn add_in_band_defs<T>(
lowered_generics.params.extend(
lifetimes_to_define
.into_iter()
.map(|(span, hir_name)| {
self.lifetime_to_generic_param(span, hir_name, parent_def_id)
})
.map(|(span, node_id)| self.fresh_lifetime_to_generic_param(span, node_id))
.chain(impl_trait_defs),
);
......@@ -1763,15 +1751,53 @@ fn lower_async_fn_ret_ty(
.in_scope_lifetimes
.iter()
.cloned()
.map(|name| (name.ident().span, name, hir::LifetimeName::Param(name)))
.chain(
self.lifetimes_to_define
.iter()
.map(|&(span, name)| (span, name, hir::LifetimeName::Param(name))),
)
.map(|name| (name.ident().span, hir::LifetimeName::Param(name)))
.chain(self.lifetimes_to_define.iter().map(|&(span, node_id)| {
let def_id = self.resolver.local_def_id(node_id);
let name = hir::ParamName::Fresh(def_id);
(span, hir::LifetimeName::Param(name))
}))
.collect();
self.with_hir_id_owner(opaque_ty_node_id, |this| {
let mut generic_params: Vec<_> = lifetime_params
.iter()
.map(|&(span, name)| {
// We can only get lifetime names from the outside.
let hir::LifetimeName::Param(hir_name) = name else { panic!() };
let node_id = this.resolver.next_node_id();
// Add a definition for the in-band lifetime def.
let def_id = this.resolver.create_def(
opaque_ty_def_id,
node_id,
DefPathData::LifetimeNs(hir_name.ident().name),
ExpnId::root(),
span.with_parent(None),
);
let (kind, name) = match hir_name {
ParamName::Plain(ident) => {
(hir::LifetimeParamKind::Explicit, hir::ParamName::Plain(ident))
}
ParamName::Fresh(_) => {
(hir::LifetimeParamKind::Elided, hir::ParamName::Fresh(def_id))
}
ParamName::Error => (hir::LifetimeParamKind::Error, hir::ParamName::Error),
};
hir::GenericParam {
hir_id: this.lower_node_id(node_id),
name,
bounds: &[],
span: this.lower_span(span),
pure_wrt_drop: false,
kind: hir::GenericParamKind::Lifetime { kind },
}
})
.collect();
// 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
......@@ -1782,25 +1808,22 @@ fn lower_async_fn_ret_ty(
// hence the elision takes place at the fn site.
let (lifetimes_to_define, future_bound) =
this.with_anonymous_lifetime_mode(AnonymousLifetimeMode::CreateParameter, |this| {
this.collect_in_band_defs(|this| {
this.collect_in_band_defs(opaque_ty_def_id, |this| {
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: lifetimes_to_define={:#?}", lifetimes_to_define);
lifetime_params.extend(
// Output lifetime like `'_`:
lifetimes_to_define
.into_iter()
.map(|(span, name)| (span, name, hir::LifetimeName::Implicit(false))),
);
// Output lifetime like `'_`:
for (span, node_id) in lifetimes_to_define {
let param = this.fresh_lifetime_to_generic_param(span, node_id);
lifetime_params.push((span, hir::LifetimeName::Implicit(false)));
generic_params.push(param);
}
let generic_params = this.arena.alloc_from_iter(generic_params);
debug!("lower_async_fn_ret_ty: lifetime_params={:#?}", lifetime_params);
let generic_params =
this.arena.alloc_from_iter(lifetime_params.iter().map(|&(span, hir_name, _)| {
this.lifetime_to_generic_param(span, hir_name, opaque_ty_def_id)
}));
debug!("lower_async_fn_ret_ty: generic_params={:#?}", generic_params);
let opaque_ty_item = hir::OpaqueTy {
generics: hir::Generics {
......@@ -1833,7 +1856,7 @@ fn lower_async_fn_ret_ty(
// For the "output" lifetime parameters, we just want to
// generate `'_`.
let generic_args =
self.arena.alloc_from_iter(lifetime_params.into_iter().map(|(span, _, name)| {
self.arena.alloc_from_iter(lifetime_params.into_iter().map(|(span, name)| {
GenericArg::Lifetime(hir::Lifetime {
hir_id: self.next_id(),
span: self.lower_span(span),
......@@ -1969,7 +1992,7 @@ fn lower_generic_param(
let (name, kind) = match param.kind {
GenericParamKind::Lifetime => {
let was_collecting_in_band = self.is_collecting_anonymous_lifetimes;
self.is_collecting_anonymous_lifetimes = false;
self.is_collecting_anonymous_lifetimes = None;
let lt = self
.with_anonymous_lifetime_mode(AnonymousLifetimeMode::ReportError, |this| {
......
......@@ -59,7 +59,7 @@ pub enum ParamName {
///
/// where `'f` is something like `Fresh(0)`. The indices are
/// unique per impl, but not necessarily continuous.
Fresh(usize),
Fresh(LocalDefId),
/// Indicates an illegal name was given and an error has been
/// reported (so we should squelch other derived errors). Occurs
......@@ -3303,7 +3303,7 @@ mod size_asserts {
rustc_data_structures::static_assert_size!(super::Expr<'static>, 56);
rustc_data_structures::static_assert_size!(super::Pat<'static>, 88);
rustc_data_structures::static_assert_size!(super::QPath<'static>, 24);
rustc_data_structures::static_assert_size!(super::Ty<'static>, 80);
rustc_data_structures::static_assert_size!(super::Ty<'static>, 72);
rustc_data_structures::static_assert_size!(super::Item<'static>, 184);
rustc_data_structures::static_assert_size!(super::TraitItem<'static>, 128);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册