Skip to content

Commit 3f58828

Browse files
committed
Auto merge of rust-lang#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.
2 parents 737ef08 + c8c691f commit 3f58828

File tree

3 files changed

+109
-78
lines changed

3 files changed

+109
-78
lines changed

Diff for: compiler/rustc_ast_lowering/src/item.rs

+16-8
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ impl<'a> Visitor<'a> for ItemLowerer<'a, '_, '_> {
9595
}
9696

9797
fn visit_assoc_item(&mut self, item: &'a AssocItem, ctxt: AssocCtxt) {
98+
debug!(in_scope_lifetimes = ?self.lctx.in_scope_lifetimes);
9899
self.lctx.with_hir_id_owner(item.id, |lctx| match ctxt {
99100
AssocCtxt::Trait => hir::OwnerNode::TraitItem(lctx.lower_trait_item(item)),
100101
AssocCtxt::Impl => hir::OwnerNode::ImplItem(lctx.lower_impl_item(item)),
@@ -118,35 +119,42 @@ impl<'hir> LoweringContext<'_, 'hir> {
118119
// This should only be used with generics that have already had their
119120
// in-band lifetimes added. In practice, this means that this function is
120121
// only used when lowering a child item of a trait or impl.
122+
#[tracing::instrument(level = "debug", skip(self, f))]
121123
fn with_parent_item_lifetime_defs<T>(
122124
&mut self,
123125
parent_hir_id: LocalDefId,
124126
f: impl FnOnce(&mut Self) -> T,
125127
) -> T {
126-
let old_len = self.in_scope_lifetimes.len();
127-
128128
let parent_generics = match self.owners[parent_hir_id].unwrap().node().expect_item().kind {
129129
hir::ItemKind::Impl(hir::Impl { ref generics, .. })
130130
| hir::ItemKind::Trait(_, _, ref generics, ..) => generics.params,
131131
_ => &[],
132132
};
133-
let lt_def_names = parent_generics.iter().filter_map(|param| match param.kind {
134-
hir::GenericParamKind::Lifetime { .. } => Some(param.name.normalize_to_macros_2_0()),
135-
_ => None,
136-
});
137-
self.in_scope_lifetimes.extend(lt_def_names);
133+
let lt_def_names = parent_generics
134+
.iter()
135+
.filter_map(|param| match param.kind {
136+
hir::GenericParamKind::Lifetime { .. } => {
137+
Some(param.name.normalize_to_macros_2_0())
138+
}
139+
_ => None,
140+
})
141+
.collect();
142+
let old_in_scope_lifetimes = mem::replace(&mut self.in_scope_lifetimes, lt_def_names);
143+
debug!(in_scope_lifetimes = ?self.in_scope_lifetimes);
138144

139145
let res = f(self);
140146

141-
self.in_scope_lifetimes.truncate(old_len);
147+
self.in_scope_lifetimes = old_in_scope_lifetimes;
142148
res
143149
}
144150

145151
// Clears (and restores) the `in_scope_lifetimes` field. Used when
146152
// visiting nested items, which never inherit in-scope lifetimes
147153
// from their surrounding environment.
154+
#[tracing::instrument(level = "debug", skip(self, f))]
148155
fn without_in_scope_lifetime_defs<T>(&mut self, f: impl FnOnce(&mut Self) -> T) -> T {
149156
let old_in_scope_lifetimes = mem::replace(&mut self.in_scope_lifetimes, vec![]);
157+
debug!(?old_in_scope_lifetimes);
150158

151159
// this vector is only used when walking over impl headers,
152160
// input types, and the like, and should not be non-empty in

Diff for: compiler/rustc_ast_lowering/src/lib.rs

+91-68
Original file line numberDiff line numberDiff line change
@@ -129,20 +129,16 @@ struct LoweringContext<'a, 'hir: 'a> {
129129
/// written at all (e.g., `&T` or `std::cell::Ref<T>`).
130130
anonymous_lifetime_mode: AnonymousLifetimeMode,
131131

132-
/// Used to create lifetime definitions from in-band lifetime usages.
133-
/// e.g., `fn foo(x: &'x u8) -> &'x u8` to `fn foo<'x>(x: &'x u8) -> &'x u8`
134-
/// When a named lifetime is encountered in a function or impl header and
135-
/// has not been defined
136-
/// (i.e., it doesn't appear in the in_scope_lifetimes list), it is added
132+
/// Used to create lifetime definitions for anonymous lifetimes.
133+
/// When an anonymous lifetime is encountered in a function or impl header and
134+
/// requires to create a fresh lifetime parameter, it is added
137135
/// to this list. The results of this list are then added to the list of
138136
/// lifetime definitions in the corresponding impl or function generics.
139-
lifetimes_to_define: Vec<(Span, ParamName)>,
137+
lifetimes_to_define: Vec<(Span, NodeId)>,
140138

141-
/// `true` if in-band lifetimes are being collected. This is used to
142-
/// indicate whether or not we're in a place where new lifetimes will result
143-
/// in in-band lifetime definitions, such a function or an impl header,
144-
/// including implicit lifetimes from `impl_header_lifetime_elision`.
145-
is_collecting_anonymous_lifetimes: bool,
139+
/// If anonymous lifetimes are being collected, this field holds the parent
140+
/// `LocalDefId` to create the fresh lifetime parameters' `LocalDefId`.
141+
is_collecting_anonymous_lifetimes: Option<LocalDefId>,
146142

147143
/// Currently in-scope lifetimes defined in impl headers, fn headers, or HRTB.
148144
/// 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>(
375371
task_context: None,
376372
current_item: None,
377373
lifetimes_to_define: Vec::new(),
378-
is_collecting_anonymous_lifetimes: false,
374+
is_collecting_anonymous_lifetimes: None,
379375
in_scope_lifetimes: Vec::new(),
380376
allow_try_trait: Some([sym::try_trait_v2][..].into()),
381377
allow_gen_future: Some([sym::gen_future][..].into()),
@@ -720,9 +716,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
720716
/// parameter while `f` is running (and restored afterwards).
721717
fn collect_in_band_defs<T>(
722718
&mut self,
719+
parent_def_id: LocalDefId,
723720
f: impl FnOnce(&mut Self) -> T,
724-
) -> (Vec<(Span, ParamName)>, T) {
725-
let was_collecting = std::mem::replace(&mut self.is_collecting_anonymous_lifetimes, true);
721+
) -> (Vec<(Span, NodeId)>, T) {
722+
let was_collecting =
723+
std::mem::replace(&mut self.is_collecting_anonymous_lifetimes, Some(parent_def_id));
726724
let len = self.lifetimes_to_define.len();
727725

728726
let res = f(self);
@@ -733,49 +731,41 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
733731
}
734732

735733
/// Converts a lifetime into a new generic parameter.
736-
fn lifetime_to_generic_param(
734+
fn fresh_lifetime_to_generic_param(
737735
&mut self,
738736
span: Span,
739-
hir_name: ParamName,
740-
parent_def_id: LocalDefId,
737+
node_id: NodeId,
741738
) -> hir::GenericParam<'hir> {
742-
let node_id = self.resolver.next_node_id();
743-
744-
// Get the name we'll use to make the def-path. Note
745-
// that collisions are ok here and this shouldn't
746-
// really show up for end-user.
747-
let (str_name, kind) = match hir_name {
748-
ParamName::Plain(ident) => (ident.name, hir::LifetimeParamKind::Explicit),
749-
ParamName::Fresh(_) => (kw::UnderscoreLifetime, hir::LifetimeParamKind::Elided),
750-
ParamName::Error => (kw::UnderscoreLifetime, hir::LifetimeParamKind::Error),
751-
};
752-
753-
// Add a definition for the in-band lifetime def.
754-
self.resolver.create_def(
755-
parent_def_id,
756-
node_id,
757-
DefPathData::LifetimeNs(str_name),
758-
ExpnId::root(),
759-
span.with_parent(None),
760-
);
761-
739+
let hir_id = self.lower_node_id(node_id);
740+
let def_id = self.resolver.local_def_id(node_id);
762741
hir::GenericParam {
763-
hir_id: self.lower_node_id(node_id),
764-
name: hir_name,
742+
hir_id,
743+
name: hir::ParamName::Fresh(def_id),
765744
bounds: &[],
766745
span: self.lower_span(span),
767746
pure_wrt_drop: false,
768-
kind: hir::GenericParamKind::Lifetime { kind },
747+
kind: hir::GenericParamKind::Lifetime { kind: hir::LifetimeParamKind::Elided },
769748
}
770749
}
771750

772751
/// When we have either an elided or `'_` lifetime in an impl
773752
/// header, we convert it to an in-band lifetime.
774753
fn collect_fresh_anonymous_lifetime(&mut self, span: Span) -> ParamName {
775-
assert!(self.is_collecting_anonymous_lifetimes);
776-
let index = self.lifetimes_to_define.len() + self.in_scope_lifetimes.len();
777-
let hir_name = ParamName::Fresh(index);
778-
self.lifetimes_to_define.push((span, hir_name));
754+
let Some(parent_def_id) = self.is_collecting_anonymous_lifetimes else { panic!() };
755+
756+
let node_id = self.resolver.next_node_id();
757+
758+
// Add a definition for the in-band lifetime def.
759+
let param_def_id = self.resolver.create_def(
760+
parent_def_id,
761+
node_id,
762+
DefPathData::LifetimeNs(kw::UnderscoreLifetime),
763+
ExpnId::root(),
764+
span.with_parent(None),
765+
);
766+
767+
let hir_name = ParamName::Fresh(param_def_id);
768+
self.lifetimes_to_define.push((span, node_id));
779769
hir_name
780770
}
781771

@@ -817,7 +807,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
817807
f: impl FnOnce(&mut Self, &mut Vec<hir::GenericParam<'hir>>) -> T,
818808
) -> (hir::Generics<'hir>, T) {
819809
let (lifetimes_to_define, (mut lowered_generics, impl_trait_defs, res)) = self
820-
.collect_in_band_defs(|this| {
810+
.collect_in_band_defs(parent_def_id, |this| {
821811
this.with_anonymous_lifetime_mode(anonymous_lifetime_mode, |this| {
822812
this.with_in_scope_lifetime_defs(&generics.params, |this| {
823813
let mut impl_trait_defs = Vec::new();
@@ -844,9 +834,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
844834
lowered_generics.params.extend(
845835
lifetimes_to_define
846836
.into_iter()
847-
.map(|(span, hir_name)| {
848-
self.lifetime_to_generic_param(span, hir_name, parent_def_id)
849-
})
837+
.map(|(span, node_id)| self.fresh_lifetime_to_generic_param(span, node_id))
850838
.chain(impl_trait_defs),
851839
);
852840

@@ -1763,15 +1751,53 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
17631751
.in_scope_lifetimes
17641752
.iter()
17651753
.cloned()
1766-
.map(|name| (name.ident().span, name, hir::LifetimeName::Param(name)))
1767-
.chain(
1768-
self.lifetimes_to_define
1769-
.iter()
1770-
.map(|&(span, name)| (span, name, hir::LifetimeName::Param(name))),
1771-
)
1754+
.map(|name| (name.ident().span, hir::LifetimeName::Param(name)))
1755+
.chain(self.lifetimes_to_define.iter().map(|&(span, node_id)| {
1756+
let def_id = self.resolver.local_def_id(node_id);
1757+
let name = hir::ParamName::Fresh(def_id);
1758+
(span, hir::LifetimeName::Param(name))
1759+
}))
17721760
.collect();
17731761

17741762
self.with_hir_id_owner(opaque_ty_node_id, |this| {
1763+
let mut generic_params: Vec<_> = lifetime_params
1764+
.iter()
1765+
.map(|&(span, name)| {
1766+
// We can only get lifetime names from the outside.
1767+
let hir::LifetimeName::Param(hir_name) = name else { panic!() };
1768+
1769+
let node_id = this.resolver.next_node_id();
1770+
1771+
// Add a definition for the in-band lifetime def.
1772+
let def_id = this.resolver.create_def(
1773+
opaque_ty_def_id,
1774+
node_id,
1775+
DefPathData::LifetimeNs(hir_name.ident().name),
1776+
ExpnId::root(),
1777+
span.with_parent(None),
1778+
);
1779+
1780+
let (kind, name) = match hir_name {
1781+
ParamName::Plain(ident) => {
1782+
(hir::LifetimeParamKind::Explicit, hir::ParamName::Plain(ident))
1783+
}
1784+
ParamName::Fresh(_) => {
1785+
(hir::LifetimeParamKind::Elided, hir::ParamName::Fresh(def_id))
1786+
}
1787+
ParamName::Error => (hir::LifetimeParamKind::Error, hir::ParamName::Error),
1788+
};
1789+
1790+
hir::GenericParam {
1791+
hir_id: this.lower_node_id(node_id),
1792+
name,
1793+
bounds: &[],
1794+
span: this.lower_span(span),
1795+
pure_wrt_drop: false,
1796+
kind: hir::GenericParamKind::Lifetime { kind },
1797+
}
1798+
})
1799+
.collect();
1800+
17751801
// We have to be careful to get elision right here. The
17761802
// idea is that we create a lifetime parameter for each
17771803
// lifetime in the return type. So, given a return type
@@ -1782,25 +1808,22 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
17821808
// hence the elision takes place at the fn site.
17831809
let (lifetimes_to_define, future_bound) =
17841810
this.with_anonymous_lifetime_mode(AnonymousLifetimeMode::CreateParameter, |this| {
1785-
this.collect_in_band_defs(|this| {
1811+
this.collect_in_band_defs(opaque_ty_def_id, |this| {
17861812
this.lower_async_fn_output_type_to_future_bound(output, fn_def_id, span)
17871813
})
17881814
});
17891815
debug!("lower_async_fn_ret_ty: future_bound={:#?}", future_bound);
17901816
debug!("lower_async_fn_ret_ty: lifetimes_to_define={:#?}", lifetimes_to_define);
17911817

1792-
lifetime_params.extend(
1793-
// Output lifetime like `'_`:
1794-
lifetimes_to_define
1795-
.into_iter()
1796-
.map(|(span, name)| (span, name, hir::LifetimeName::Implicit(false))),
1797-
);
1818+
// Output lifetime like `'_`:
1819+
for (span, node_id) in lifetimes_to_define {
1820+
let param = this.fresh_lifetime_to_generic_param(span, node_id);
1821+
lifetime_params.push((span, hir::LifetimeName::Implicit(false)));
1822+
generic_params.push(param);
1823+
}
1824+
let generic_params = this.arena.alloc_from_iter(generic_params);
17981825
debug!("lower_async_fn_ret_ty: lifetime_params={:#?}", lifetime_params);
1799-
1800-
let generic_params =
1801-
this.arena.alloc_from_iter(lifetime_params.iter().map(|&(span, hir_name, _)| {
1802-
this.lifetime_to_generic_param(span, hir_name, opaque_ty_def_id)
1803-
}));
1826+
debug!("lower_async_fn_ret_ty: generic_params={:#?}", generic_params);
18041827

18051828
let opaque_ty_item = hir::OpaqueTy {
18061829
generics: hir::Generics {
@@ -1833,7 +1856,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
18331856
// For the "output" lifetime parameters, we just want to
18341857
// generate `'_`.
18351858
let generic_args =
1836-
self.arena.alloc_from_iter(lifetime_params.into_iter().map(|(span, _, name)| {
1859+
self.arena.alloc_from_iter(lifetime_params.into_iter().map(|(span, name)| {
18371860
GenericArg::Lifetime(hir::Lifetime {
18381861
hir_id: self.next_id(),
18391862
span: self.lower_span(span),
@@ -1969,7 +1992,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
19691992
let (name, kind) = match param.kind {
19701993
GenericParamKind::Lifetime => {
19711994
let was_collecting_in_band = self.is_collecting_anonymous_lifetimes;
1972-
self.is_collecting_anonymous_lifetimes = false;
1995+
self.is_collecting_anonymous_lifetimes = None;
19731996

19741997
let lt = self
19751998
.with_anonymous_lifetime_mode(AnonymousLifetimeMode::ReportError, |this| {

Diff for: compiler/rustc_hir/src/hir.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ pub enum ParamName {
5959
///
6060
/// where `'f` is something like `Fresh(0)`. The indices are
6161
/// unique per impl, but not necessarily continuous.
62-
Fresh(usize),
62+
Fresh(LocalDefId),
6363

6464
/// Indicates an illegal name was given and an error has been
6565
/// reported (so we should squelch other derived errors). Occurs
@@ -3303,7 +3303,7 @@ mod size_asserts {
33033303
rustc_data_structures::static_assert_size!(super::Expr<'static>, 56);
33043304
rustc_data_structures::static_assert_size!(super::Pat<'static>, 88);
33053305
rustc_data_structures::static_assert_size!(super::QPath<'static>, 24);
3306-
rustc_data_structures::static_assert_size!(super::Ty<'static>, 80);
3306+
rustc_data_structures::static_assert_size!(super::Ty<'static>, 72);
33073307

33083308
rustc_data_structures::static_assert_size!(super::Item<'static>, 184);
33093309
rustc_data_structures::static_assert_size!(super::TraitItem<'static>, 128);

0 commit comments

Comments
 (0)