Remap impl-trait lifetimes on HIR instead of AST lowering.
This commit is contained in:
@@ -45,16 +45,14 @@ use rustc_ast::ptr::P;
|
||||
use rustc_ast::{self as ast, *};
|
||||
use rustc_data_structures::captures::Captures;
|
||||
use rustc_data_structures::fingerprint::Fingerprint;
|
||||
use rustc_data_structures::fx::FxIndexSet;
|
||||
use rustc_data_structures::sorted_map::SortedMap;
|
||||
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
use rustc_errors::{DiagArgFromDisplay, DiagCtxtHandle, StashKey};
|
||||
use rustc_hir::def::{DefKind, LifetimeRes, Namespace, PartialRes, PerNS, Res};
|
||||
use rustc_hir::def_id::{CRATE_DEF_ID, LOCAL_CRATE, LocalDefId, LocalDefIdMap};
|
||||
use rustc_hir::def_id::{CRATE_DEF_ID, LOCAL_CRATE, LocalDefId};
|
||||
use rustc_hir::{
|
||||
self as hir, ConstArg, GenericArg, HirId, ItemLocalMap, LangItem, MissingLifetimeKind,
|
||||
ParamName, TraitCandidate,
|
||||
self as hir, ConstArg, GenericArg, HirId, ItemLocalMap, LangItem, ParamName, TraitCandidate,
|
||||
};
|
||||
use rustc_index::{Idx, IndexSlice, IndexVec};
|
||||
use rustc_macros::extension;
|
||||
@@ -83,7 +81,6 @@ mod expr;
|
||||
mod format;
|
||||
mod index;
|
||||
mod item;
|
||||
mod lifetime_collector;
|
||||
mod pat;
|
||||
mod path;
|
||||
|
||||
@@ -149,12 +146,6 @@ struct LoweringContext<'a, 'hir> {
|
||||
allow_async_iterator: Lrc<[Symbol]>,
|
||||
allow_for_await: Lrc<[Symbol]>,
|
||||
allow_async_fn_traits: Lrc<[Symbol]>,
|
||||
|
||||
/// 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<LocalDefIdMap<LocalDefId>>,
|
||||
}
|
||||
|
||||
impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
@@ -199,7 +190,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
// FIXME(gen_blocks): how does `closure_track_caller`/`async_fn_track_caller`
|
||||
// interact with `gen`/`async gen` blocks
|
||||
allow_async_iterator: [sym::gen_future, sym::async_iterator].into(),
|
||||
generics_def_id_map: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -528,54 +518,14 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
|
||||
/// Given the id of some node in the AST, finds the `LocalDefId` associated with it by the name
|
||||
/// resolver (if any).
|
||||
fn orig_opt_local_def_id(&self, node: NodeId) -> Option<LocalDefId> {
|
||||
self.resolver.node_id_to_def_id.get(&node).copied()
|
||||
}
|
||||
|
||||
/// 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.orig_opt_local_def_id(node).map(|local_def_id| self.get_remapped_def_id(local_def_id))
|
||||
self.resolver.node_id_to_def_id.get(&node).copied()
|
||||
}
|
||||
|
||||
fn local_def_id(&self, node: NodeId) -> LocalDefId {
|
||||
self.opt_local_def_id(node).unwrap_or_else(|| panic!("no entry for node id: `{node:?}`"))
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
// `generics_def_id_map` is a stack of mappings. As we go deeper in impl traits nesting we
|
||||
// push new mappings, so we first need to get the latest (innermost) mappings, hence `iter().rev()`.
|
||||
//
|
||||
// Consider:
|
||||
//
|
||||
// `fn test<'a, 'b>() -> impl Trait<&'a u8, Ty = impl Sized + 'b> {}`
|
||||
//
|
||||
// We would end with a generics_def_id_map like:
|
||||
//
|
||||
// `[[fn#'b -> impl_trait#'b], [fn#'b -> impl_sized#'b]]`
|
||||
//
|
||||
// for the opaque type generated on `impl Sized + 'b`, we want the result to be: impl_sized#'b.
|
||||
// So, if we were trying to find first from the start (outermost) would give the wrong result, impl_trait#'b.
|
||||
self.generics_def_id_map
|
||||
.iter()
|
||||
.rev()
|
||||
.find_map(|map| map.get(&local_def_id).copied())
|
||||
.unwrap_or(local_def_id)
|
||||
}
|
||||
|
||||
/// Freshen the `LoweringContext` and ready it to lower a nested item.
|
||||
/// The lowered item is registered into `self.children`.
|
||||
///
|
||||
@@ -647,27 +597,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
result
|
||||
}
|
||||
|
||||
/// 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: LocalDefIdMap<LocalDefId>,
|
||||
f: impl FnOnce(&mut Self) -> R,
|
||||
) -> R {
|
||||
self.generics_def_id_map.push(remap);
|
||||
let res = f(self);
|
||||
self.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);
|
||||
@@ -1499,27 +1428,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
// frequently opened issues show.
|
||||
let opaque_ty_span = self.mark_span_with_reason(DesugaringKind::OpaqueTy, span, None);
|
||||
|
||||
// Whether this opaque always captures lifetimes in scope.
|
||||
// Right now, this is all RPITIT and TAITs, and when `lifetime_capture_rules_2024`
|
||||
// is enabled. We don't check the span of the edition, since this is done
|
||||
// on a per-opaque basis to account for nested opaques.
|
||||
let always_capture_in_scope = match origin {
|
||||
_ if self.tcx.features().lifetime_capture_rules_2024() => true,
|
||||
hir::OpaqueTyOrigin::TyAlias { .. } => true,
|
||||
hir::OpaqueTyOrigin::FnReturn { in_trait_or_impl, .. } => in_trait_or_impl.is_some(),
|
||||
hir::OpaqueTyOrigin::AsyncFn { .. } => {
|
||||
unreachable!("should be using `lower_coroutine_fn_ret_ty`")
|
||||
}
|
||||
};
|
||||
let captured_lifetimes_to_duplicate = lifetime_collector::lifetimes_for_opaque(
|
||||
self.resolver,
|
||||
always_capture_in_scope,
|
||||
opaque_ty_node_id,
|
||||
bounds,
|
||||
span,
|
||||
);
|
||||
debug!(?captured_lifetimes_to_duplicate);
|
||||
|
||||
// Feature gate for RPITIT + use<..>
|
||||
match origin {
|
||||
rustc_hir::OpaqueTyOrigin::FnReturn { in_trait_or_impl: Some(_), .. } => {
|
||||
@@ -1542,22 +1450,15 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
_ => {}
|
||||
}
|
||||
|
||||
self.lower_opaque_inner(
|
||||
opaque_ty_node_id,
|
||||
origin,
|
||||
captured_lifetimes_to_duplicate,
|
||||
span,
|
||||
opaque_ty_span,
|
||||
|this| this.lower_param_bounds(bounds, itctx),
|
||||
)
|
||||
self.lower_opaque_inner(opaque_ty_node_id, origin, opaque_ty_span, |this| {
|
||||
this.lower_param_bounds(bounds, itctx)
|
||||
})
|
||||
}
|
||||
|
||||
fn lower_opaque_inner(
|
||||
&mut self,
|
||||
opaque_ty_node_id: NodeId,
|
||||
origin: hir::OpaqueTyOrigin,
|
||||
captured_lifetimes_to_duplicate: FxIndexSet<Lifetime>,
|
||||
span: Span,
|
||||
opaque_ty_span: Span,
|
||||
lower_item_bounds: impl FnOnce(&mut Self) -> &'hir [hir::GenericBound<'hir>],
|
||||
) -> hir::TyKind<'hir> {
|
||||
@@ -1565,145 +1466,19 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
let opaque_ty_hir_id = self.lower_node_id(opaque_ty_node_id);
|
||||
debug!(?opaque_ty_def_id, ?opaque_ty_hir_id);
|
||||
|
||||
// Map from captured (old) lifetime to synthetic (new) lifetime.
|
||||
// Used to resolve lifetimes in the bounds of the opaque.
|
||||
let mut captured_to_synthesized_mapping = LocalDefIdMap::default();
|
||||
// List of (early-bound) synthetic lifetimes that are owned by the opaque.
|
||||
// This is used to create the `hir::Generics` owned by the opaque.
|
||||
let mut synthesized_lifetime_definitions = vec![];
|
||||
// Pairs of lifetime arg (that resolves to the captured lifetime)
|
||||
// and the def-id of the (early-bound) synthetic lifetime definition.
|
||||
// This is used both to create generics for the `TyKind::OpaqueDef` that
|
||||
// we return, and also as a captured lifetime mapping for RPITITs.
|
||||
let mut synthesized_lifetime_args = vec![];
|
||||
|
||||
for lifetime in captured_lifetimes_to_duplicate {
|
||||
let res = self.resolver.get_lifetime_res(lifetime.id).unwrap_or(LifetimeRes::Error);
|
||||
let (old_def_id, missing_kind) = match res {
|
||||
LifetimeRes::Param { param: old_def_id, binder: _ } => (old_def_id, None),
|
||||
|
||||
LifetimeRes::Fresh { param, kind, .. } => {
|
||||
debug_assert_eq!(lifetime.ident.name, kw::UnderscoreLifetime);
|
||||
if let Some(old_def_id) = self.orig_opt_local_def_id(param) {
|
||||
(old_def_id, Some(kind))
|
||||
} else {
|
||||
self.dcx()
|
||||
.span_delayed_bug(lifetime.ident.span, "no def-id for fresh lifetime");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Opaques do not capture `'static`
|
||||
LifetimeRes::Static { .. } | LifetimeRes::Error => {
|
||||
continue;
|
||||
}
|
||||
|
||||
res => {
|
||||
let bug_msg = format!(
|
||||
"Unexpected lifetime resolution {:?} for {:?} at {:?}",
|
||||
res, lifetime.ident, lifetime.ident.span
|
||||
);
|
||||
span_bug!(lifetime.ident.span, "{}", bug_msg);
|
||||
}
|
||||
};
|
||||
|
||||
if captured_to_synthesized_mapping.get(&old_def_id).is_none() {
|
||||
// Create a new lifetime parameter local to the opaque.
|
||||
let duplicated_lifetime_node_id = self.next_node_id();
|
||||
let duplicated_lifetime_def_id = self.create_def(
|
||||
opaque_ty_def_id,
|
||||
duplicated_lifetime_node_id,
|
||||
lifetime.ident.name,
|
||||
DefKind::LifetimeParam,
|
||||
self.lower_span(lifetime.ident.span),
|
||||
);
|
||||
captured_to_synthesized_mapping.insert(old_def_id, duplicated_lifetime_def_id);
|
||||
// FIXME: Instead of doing this, we could move this whole loop
|
||||
// into the `with_hir_id_owner`, then just directly construct
|
||||
// the `hir::GenericParam` here.
|
||||
synthesized_lifetime_definitions.push((
|
||||
duplicated_lifetime_node_id,
|
||||
duplicated_lifetime_def_id,
|
||||
self.lower_ident(lifetime.ident),
|
||||
missing_kind,
|
||||
));
|
||||
|
||||
// Now make an arg that we can use for the generic params of the opaque tykind.
|
||||
let id = self.next_node_id();
|
||||
let lifetime_arg = self.new_named_lifetime_with_res(id, lifetime.ident, res);
|
||||
let duplicated_lifetime_def_id = self.local_def_id(duplicated_lifetime_node_id);
|
||||
synthesized_lifetime_args.push((lifetime_arg, duplicated_lifetime_def_id))
|
||||
}
|
||||
}
|
||||
|
||||
let opaque_ty_def = self.with_def_id_parent(opaque_ty_def_id, |this| {
|
||||
// Install the remapping from old to new (if any). This makes sure that
|
||||
// any lifetimes that would have resolved to the def-id of captured
|
||||
// lifetimes are remapped to the new *synthetic* lifetimes of the opaque.
|
||||
let bounds = this
|
||||
.with_remapping(captured_to_synthesized_mapping, |this| lower_item_bounds(this));
|
||||
|
||||
let generic_params =
|
||||
this.arena.alloc_from_iter(synthesized_lifetime_definitions.iter().map(
|
||||
|&(new_node_id, new_def_id, ident, missing_kind)| {
|
||||
let hir_id = this.lower_node_id(new_node_id);
|
||||
let (name, kind) = if ident.name == kw::UnderscoreLifetime {
|
||||
(
|
||||
hir::ParamName::Fresh,
|
||||
hir::LifetimeParamKind::Elided(
|
||||
missing_kind.unwrap_or(MissingLifetimeKind::Underscore),
|
||||
),
|
||||
)
|
||||
} else {
|
||||
(hir::ParamName::Plain(ident), hir::LifetimeParamKind::Explicit)
|
||||
};
|
||||
|
||||
hir::GenericParam {
|
||||
hir_id,
|
||||
def_id: new_def_id,
|
||||
name,
|
||||
span: ident.span,
|
||||
pure_wrt_drop: false,
|
||||
kind: hir::GenericParamKind::Lifetime { kind },
|
||||
colon_span: None,
|
||||
source: hir::GenericParamSource::Generics,
|
||||
}
|
||||
},
|
||||
));
|
||||
debug!("lower_async_fn_ret_ty: generic_params={:#?}", generic_params);
|
||||
|
||||
let lifetime_mapping = self.arena.alloc_slice(&synthesized_lifetime_args);
|
||||
|
||||
trace!("registering opaque type with id {:#?}", opaque_ty_def_id);
|
||||
let bounds = lower_item_bounds(this);
|
||||
let opaque_ty_def = hir::OpaqueTy {
|
||||
hir_id: opaque_ty_hir_id,
|
||||
def_id: opaque_ty_def_id,
|
||||
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,
|
||||
origin,
|
||||
lifetime_mapping,
|
||||
span: this.lower_span(opaque_ty_span),
|
||||
};
|
||||
this.arena.alloc(opaque_ty_def)
|
||||
});
|
||||
|
||||
let generic_args = self.arena.alloc_from_iter(
|
||||
synthesized_lifetime_args
|
||||
.iter()
|
||||
.map(|(lifetime, _)| hir::GenericArg::Lifetime(*lifetime)),
|
||||
);
|
||||
|
||||
// Create the `Foo<...>` reference itself. Note that the `type
|
||||
// Foo = impl Trait` is, internally, created as a child of the
|
||||
// async fn, so the *type parameters* are inherited. It's
|
||||
// only the lifetime parameters that we must supply.
|
||||
hir::TyKind::OpaqueDef(opaque_ty_def, generic_args)
|
||||
hir::TyKind::OpaqueDef(opaque_ty_def)
|
||||
}
|
||||
|
||||
fn lower_precise_capturing_args(
|
||||
@@ -1885,13 +1660,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
let opaque_ty_span =
|
||||
self.mark_span_with_reason(DesugaringKind::Async, span, allowed_features);
|
||||
|
||||
let captured_lifetimes = self
|
||||
.resolver
|
||||
.extra_lifetime_params(opaque_ty_node_id)
|
||||
.into_iter()
|
||||
.map(|(ident, id, _)| Lifetime { id, ident })
|
||||
.collect();
|
||||
|
||||
let in_trait_or_impl = match fn_kind {
|
||||
FnDeclKind::Trait => Some(hir::RpitContext::Trait),
|
||||
FnDeclKind::Impl => Some(hir::RpitContext::TraitImpl),
|
||||
@@ -1902,8 +1670,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
let opaque_ty_ref = self.lower_opaque_inner(
|
||||
opaque_ty_node_id,
|
||||
hir::OpaqueTyOrigin::AsyncFn { parent: fn_def_id, in_trait_or_impl },
|
||||
captured_lifetimes,
|
||||
span,
|
||||
opaque_ty_span,
|
||||
|this| {
|
||||
let bound = this.lower_coroutine_fn_output_type_to_bound(
|
||||
@@ -2000,10 +1766,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
res: LifetimeRes,
|
||||
) -> &'hir hir::Lifetime {
|
||||
let res = match res {
|
||||
LifetimeRes::Param { param, .. } => {
|
||||
let param = self.get_remapped_def_id(param);
|
||||
hir::LifetimeName::Param(param)
|
||||
}
|
||||
LifetimeRes::Param { param, .. } => hir::LifetimeName::Param(param),
|
||||
LifetimeRes::Fresh { param, .. } => {
|
||||
let param = self.local_def_id(param);
|
||||
hir::LifetimeName::Param(param)
|
||||
|
||||
Reference in New Issue
Block a user