Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Identify anonymous lifetimes by their DefId in HIR. #94773

Merged
merged 2 commits into from
Mar 14, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 16 additions & 8 deletions compiler/rustc_ast_lowering/src/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ impl<'a> Visitor<'a> for ItemLowerer<'a, '_, '_> {
}

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)),
Expand All @@ -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);
oli-obk marked this conversation as resolved.
Show resolved Hide resolved
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
Expand Down
159 changes: 91 additions & 68 deletions compiler/rustc_ast_lowering/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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()),
Expand Down Expand Up @@ -720,9 +716,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
/// 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);
Expand All @@ -733,49 +731,41 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
}

/// 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
}

Expand Down Expand Up @@ -817,7 +807,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
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();
Expand All @@ -844,9 +834,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
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),
);

Expand Down Expand Up @@ -1763,15 +1751,53 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
.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
Expand All @@ -1782,25 +1808,22 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
// 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 {
Expand Down Expand Up @@ -1833,7 +1856,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
// 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),
Expand Down Expand Up @@ -1969,7 +1992,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
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| {
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_hir/src/hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand Down