Auto merge of #114487 - compiler-errors:opaques-refactoring-idk, r=cjgillot
Consolidate opaque ty and async fn lowering code The codepaths for lowering "regular" opaques and async fn were almost identical, modulo some bookkeeping that seemed pretty easy to consolidate. r? `@cjgillot`
This commit is contained in:
commit
bc720ad36b
5 changed files with 219 additions and 459 deletions
|
@ -512,11 +512,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
self.resolver.node_id_to_def_id.get(&node).map(|local_def_id| *local_def_id)
|
||||
}
|
||||
|
||||
fn orig_local_def_id(&self, node: NodeId) -> LocalDefId {
|
||||
self.orig_opt_local_def_id(node)
|
||||
.unwrap_or_else(|| panic!("no entry for node id: `{node:?}`"))
|
||||
}
|
||||
|
||||
/// 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`.
|
||||
///
|
||||
|
@ -1521,6 +1516,45 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
// frequently opened issues show.
|
||||
let opaque_ty_span = self.mark_span_with_reason(DesugaringKind::OpaqueTy, span, None);
|
||||
|
||||
let captured_lifetimes_to_duplicate = match origin {
|
||||
hir::OpaqueTyOrigin::TyAlias { .. } => {
|
||||
// in a TAIT like `type Foo<'a> = impl Foo<'a>`, we don't duplicate any
|
||||
// lifetimes, since we don't have the issue that any are late-bound.
|
||||
Vec::new()
|
||||
}
|
||||
hir::OpaqueTyOrigin::FnReturn(..) => {
|
||||
// in fn return position, like the `fn test<'a>() -> impl Debug + 'a`
|
||||
// example, we only need to duplicate lifetimes that appear in the
|
||||
// bounds, since those are the only ones that are captured by the opaque.
|
||||
lifetime_collector::lifetimes_in_bounds(&self.resolver, bounds)
|
||||
}
|
||||
hir::OpaqueTyOrigin::AsyncFn(..) => {
|
||||
unreachable!("should be using `lower_async_fn_ret_ty`")
|
||||
}
|
||||
};
|
||||
debug!(?captured_lifetimes_to_duplicate);
|
||||
|
||||
self.lower_opaque_inner(
|
||||
opaque_ty_node_id,
|
||||
origin,
|
||||
in_trait,
|
||||
captured_lifetimes_to_duplicate,
|
||||
span,
|
||||
opaque_ty_span,
|
||||
|this| this.lower_param_bounds(bounds, itctx),
|
||||
)
|
||||
}
|
||||
|
||||
fn lower_opaque_inner(
|
||||
&mut self,
|
||||
opaque_ty_node_id: NodeId,
|
||||
origin: hir::OpaqueTyOrigin,
|
||||
in_trait: bool,
|
||||
captured_lifetimes_to_duplicate: Vec<Lifetime>,
|
||||
span: Span,
|
||||
opaque_ty_span: Span,
|
||||
lower_item_bounds: impl FnOnce(&mut Self) -> &'hir [hir::GenericBound<'hir>],
|
||||
) -> hir::TyKind<'hir> {
|
||||
let opaque_ty_def_id = self.create_def(
|
||||
self.current_hir_id_owner.def_id,
|
||||
opaque_ty_node_id,
|
||||
|
@ -1529,201 +1563,39 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
);
|
||||
debug!(?opaque_ty_def_id);
|
||||
|
||||
// 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 = match origin {
|
||||
hir::OpaqueTyOrigin::TyAlias { .. } => {
|
||||
// in a TAIT like `type Foo<'a> = impl Foo<'a>`, we don't keep all the lifetime parameters
|
||||
Vec::new()
|
||||
}
|
||||
hir::OpaqueTyOrigin::AsyncFn(..) | hir::OpaqueTyOrigin::FnReturn(..) => {
|
||||
// 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);
|
||||
// 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 = FxHashMap::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![];
|
||||
|
||||
let mut new_remapping = FxHashMap::default();
|
||||
|
||||
// Contains the new lifetime definitions created for the TAIT (if any).
|
||||
// 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).
|
||||
let collected_lifetimes =
|
||||
self.create_lifetime_defs(opaque_ty_def_id, &lifetimes_to_remap, &mut new_remapping);
|
||||
debug!(?collected_lifetimes);
|
||||
debug!(?new_remapping);
|
||||
|
||||
// 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 collected_lifetime_mapping: Vec<_> = collected_lifetimes
|
||||
.iter()
|
||||
.map(|(node_id, lifetime)| {
|
||||
let id = self.next_node_id();
|
||||
let lifetime = self.new_named_lifetime(lifetime.id, id, lifetime.ident);
|
||||
let def_id = self.local_def_id(*node_id);
|
||||
(lifetime, def_id)
|
||||
})
|
||||
.collect();
|
||||
debug!(?collected_lifetime_mapping);
|
||||
|
||||
self.with_hir_id_owner(opaque_ty_node_id, |lctx| {
|
||||
// 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 (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,
|
||||
def_id: lctx.local_def_id(new_node_id),
|
||||
name,
|
||||
span: lifetime.ident.span,
|
||||
pure_wrt_drop: false,
|
||||
kind: hir::GenericParamKind::Lifetime { kind },
|
||||
colon_span: None,
|
||||
source: hir::GenericParamSource::Generics,
|
||||
}
|
||||
},
|
||||
));
|
||||
debug!(?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 lifetime_mapping = if in_trait {
|
||||
Some(
|
||||
&*self.arena.alloc_from_iter(
|
||||
collected_lifetime_mapping
|
||||
.iter()
|
||||
.map(|(lifetime, def_id)| (**lifetime, *def_id)),
|
||||
),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
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,
|
||||
lifetime_mapping,
|
||||
in_trait,
|
||||
};
|
||||
debug!(?opaque_ty_item);
|
||||
|
||||
lctx.generate_opaque_type(opaque_ty_def_id, opaque_ty_item, span, opaque_ty_span)
|
||||
})
|
||||
});
|
||||
|
||||
// `impl Trait` now just becomes `Foo<'a, 'b, ..>`.
|
||||
hir::TyKind::OpaqueDef(
|
||||
hir::ItemId { owner_id: hir::OwnerId { def_id: opaque_ty_def_id } },
|
||||
self.arena.alloc_from_iter(
|
||||
collected_lifetime_mapping
|
||||
.iter()
|
||||
.map(|(lifetime, _)| hir::GenericArg::Lifetime(*lifetime)),
|
||||
),
|
||||
in_trait,
|
||||
)
|
||||
}
|
||||
|
||||
/// Registers a new opaque type with the proper `NodeId`s and
|
||||
/// returns the lowered node-ID for the opaque type.
|
||||
fn generate_opaque_type(
|
||||
&mut self,
|
||||
opaque_ty_id: LocalDefId,
|
||||
opaque_ty_item: hir::OpaqueTy<'hir>,
|
||||
span: Span,
|
||||
opaque_ty_span: Span,
|
||||
) -> hir::OwnerNode<'hir> {
|
||||
let opaque_ty_item_kind = hir::ItemKind::OpaqueTy(self.arena.alloc(opaque_ty_item));
|
||||
// Generate an `type Foo = impl Trait;` declaration.
|
||||
trace!("registering opaque type with id {:#?}", opaque_ty_id);
|
||||
let opaque_ty_item = hir::Item {
|
||||
owner_id: hir::OwnerId { def_id: opaque_ty_id },
|
||||
ident: Ident::empty(),
|
||||
kind: opaque_ty_item_kind,
|
||||
vis_span: self.lower_span(span.shrink_to_lo()),
|
||||
span: self.lower_span(opaque_ty_span),
|
||||
};
|
||||
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 {
|
||||
for lifetime in captured_lifetimes_to_duplicate {
|
||||
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),
|
||||
lifetime.ident.span,
|
||||
);
|
||||
remapping.insert(old_def_id, new_def_id);
|
||||
|
||||
result.push((node_id, *lifetime));
|
||||
}
|
||||
}
|
||||
let old_def_id = match res {
|
||||
LifetimeRes::Param { param: old_def_id, binder: _ } => old_def_id,
|
||||
|
||||
LifetimeRes::Fresh { param, binder: _ } => {
|
||||
debug_assert_eq!(lifetime.ident.name, kw::UnderscoreLifetime);
|
||||
if let Some(old_def_id) = self.orig_opt_local_def_id(param) && 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),
|
||||
lifetime.ident.span,
|
||||
);
|
||||
remapping.insert(old_def_id, new_def_id);
|
||||
|
||||
result.push((node_id, *lifetime));
|
||||
if let Some(old_def_id) = self.orig_opt_local_def_id(param) {
|
||||
old_def_id
|
||||
} else {
|
||||
self.tcx
|
||||
.sess
|
||||
.delay_span_bug(lifetime.ident.span, "no def-id for fresh lifetime");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
LifetimeRes::Static | LifetimeRes::Error => {}
|
||||
// Opaques do not capture `'static`
|
||||
LifetimeRes::Static | LifetimeRes::Error => {
|
||||
continue;
|
||||
}
|
||||
|
||||
res => {
|
||||
let bug_msg = format!(
|
||||
|
@ -1732,10 +1604,113 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
);
|
||||
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,
|
||||
DefPathData::LifetimeNs(lifetime.ident.name),
|
||||
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,
|
||||
lifetime.ident,
|
||||
));
|
||||
|
||||
// Now make an arg that we can use for the substs 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))
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
self.with_hir_id_owner(opaque_ty_node_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)| {
|
||||
let hir_id = this.lower_node_id(new_node_id);
|
||||
let (name, kind) = if ident.name == kw::UnderscoreLifetime {
|
||||
(hir::ParamName::Fresh, hir::LifetimeParamKind::Elided)
|
||||
} 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 = if in_trait {
|
||||
Some(&*self.arena.alloc_slice(&synthesized_lifetime_args))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
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,
|
||||
origin,
|
||||
lifetime_mapping,
|
||||
in_trait,
|
||||
};
|
||||
|
||||
// Generate an `type Foo = impl Trait;` declaration.
|
||||
trace!("registering opaque type with id {:#?}", opaque_ty_def_id);
|
||||
let opaque_ty_item = hir::Item {
|
||||
owner_id: hir::OwnerId { def_id: opaque_ty_def_id },
|
||||
ident: Ident::empty(),
|
||||
kind: hir::ItemKind::OpaqueTy(this.arena.alloc(opaque_ty_item)),
|
||||
vis_span: this.lower_span(span.shrink_to_lo()),
|
||||
span: this.lower_span(opaque_ty_span),
|
||||
};
|
||||
|
||||
hir::OwnerNode::Item(this.arena.alloc(opaque_ty_item))
|
||||
});
|
||||
|
||||
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(
|
||||
hir::ItemId { owner_id: hir::OwnerId { def_id: opaque_ty_def_id } },
|
||||
generic_args,
|
||||
in_trait,
|
||||
)
|
||||
}
|
||||
|
||||
fn lower_fn_params_to_names(&mut self, decl: &FnDecl) -> &'hir [Ident] {
|
||||
|
@ -1813,9 +1788,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
}
|
||||
}
|
||||
|
||||
let fn_def_id = self.local_def_id(fn_node_id);
|
||||
self.lower_async_fn_ret_ty(
|
||||
&decl.output,
|
||||
fn_node_id,
|
||||
fn_def_id,
|
||||
ret_id,
|
||||
matches!(kind, FnDeclKind::Trait),
|
||||
)
|
||||
|
@ -1892,151 +1868,28 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
fn lower_async_fn_ret_ty(
|
||||
&mut self,
|
||||
output: &FnRetTy,
|
||||
fn_node_id: NodeId,
|
||||
fn_def_id: LocalDefId,
|
||||
opaque_ty_node_id: NodeId,
|
||||
in_trait: bool,
|
||||
) -> hir::FnRetTy<'hir> {
|
||||
let span = output.span();
|
||||
|
||||
let span = self.lower_span(output.span());
|
||||
let opaque_ty_span = self.mark_span_with_reason(DesugaringKind::Async, span, None);
|
||||
|
||||
let fn_def_id = self.local_def_id(fn_node_id);
|
||||
|
||||
let opaque_ty_def_id =
|
||||
self.create_def(fn_def_id, opaque_ty_node_id, DefPathData::ImplTrait, opaque_ty_span);
|
||||
|
||||
// When we create the opaque type for this async fn, it is going to have
|
||||
// to capture all the lifetimes involved in the signature (including in the
|
||||
// return type). This is done by introducing lifetime parameters for:
|
||||
//
|
||||
// - all the explicitly declared lifetimes from the impl and function itself;
|
||||
// - all the elided lifetimes in the fn arguments;
|
||||
// - all the elided lifetimes in the return type.
|
||||
//
|
||||
// So for example in this snippet:
|
||||
//
|
||||
// ```rust
|
||||
// impl<'a> Foo<'a> {
|
||||
// async fn bar<'b>(&self, x: &'b Vec<f64>, y: &str) -> &u32 {
|
||||
// // ^ '0 ^ '1 ^ '2
|
||||
// // elided lifetimes used below
|
||||
// }
|
||||
// }
|
||||
// ```
|
||||
//
|
||||
// we would create an opaque type like:
|
||||
//
|
||||
// ```
|
||||
// type Bar<'a, 'b, '0, '1, '2> = impl Future<Output = &'2 u32>;
|
||||
// ```
|
||||
//
|
||||
// and we would then desugar `bar` to the equivalent of:
|
||||
//
|
||||
// ```rust
|
||||
// impl<'a> Foo<'a> {
|
||||
// fn bar<'b, '0, '1>(&'0 self, x: &'b Vec<f64>, y: &'1 str) -> Bar<'a, 'b, '0, '1, '_>
|
||||
// }
|
||||
// ```
|
||||
//
|
||||
// Note that the final parameter to `Bar` is `'_`, not `'2` --
|
||||
// this is because the elided lifetimes from the return type
|
||||
// should be figured out using the ordinary elision rules, and
|
||||
// this desugaring achieves that.
|
||||
|
||||
// Calculate all the lifetimes that should be captured
|
||||
// by the opaque type. This should include all in-scope
|
||||
// lifetime parameters, including those defined in-band.
|
||||
|
||||
// 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 outer_def_id = self.orig_local_def_id(outer_node_id);
|
||||
let inner_node_id = self.next_node_id();
|
||||
|
||||
// Add a definition for the in scope lifetime def.
|
||||
let inner_def_id = self.create_def(
|
||||
opaque_ty_def_id,
|
||||
inner_node_id,
|
||||
DefPathData::LifetimeNs(ident.name),
|
||||
ident.span,
|
||||
);
|
||||
new_remapping.insert(outer_def_id, inner_def_id);
|
||||
|
||||
let inner_res = match outer_res {
|
||||
// Input lifetime like `'a`:
|
||||
LifetimeRes::Param { param, .. } => {
|
||||
LifetimeRes::Param { param, binder: fn_node_id }
|
||||
}
|
||||
// Input lifetime like `'1`:
|
||||
LifetimeRes::Fresh { param, .. } => {
|
||||
LifetimeRes::Fresh { param, binder: fn_node_id }
|
||||
}
|
||||
LifetimeRes::Static | LifetimeRes::Error => continue,
|
||||
res => {
|
||||
panic!(
|
||||
"Unexpected lifetime resolution {:?} for {:?} at {:?}",
|
||||
res, ident, ident.span
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
let lifetime = Lifetime { id: outer_node_id, ident };
|
||||
collected_lifetimes.push((inner_node_id, lifetime, Some(inner_res)));
|
||||
}
|
||||
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);
|
||||
|
||||
// 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(
|
||||
self.create_lifetime_defs(opaque_ty_def_id, &lifetimes_to_remap, &mut new_remapping)
|
||||
let captured_lifetimes: Vec<_> = self
|
||||
.resolver
|
||||
.take_extra_lifetime_params(opaque_ty_node_id)
|
||||
.into_iter()
|
||||
.map(|(new_node_id, lifetime)| (new_node_id, lifetime, None)),
|
||||
);
|
||||
debug!(?collected_lifetimes);
|
||||
debug!(?new_remapping);
|
||||
|
||||
// This creates pairs of HIR lifetimes and def_ids. In the given example `type
|
||||
// TestReturn<'a, T, 'x> = impl Debug + 'x`, it creates a collection containing the
|
||||
// new lifetime of the RPIT 'x and the def_id of the lifetime 'x corresponding to
|
||||
// `TestReturn`.
|
||||
let collected_lifetime_mapping: Vec<_> = collected_lifetimes
|
||||
.iter()
|
||||
.map(|(node_id, lifetime, res)| {
|
||||
let id = self.next_node_id();
|
||||
let res = res.unwrap_or(
|
||||
self.resolver.get_lifetime_res(lifetime.id).unwrap_or(LifetimeRes::Error),
|
||||
);
|
||||
let lifetime = self.new_named_lifetime_with_res(id, lifetime.ident, res);
|
||||
let def_id = self.local_def_id(*node_id);
|
||||
(lifetime, def_id)
|
||||
})
|
||||
.map(|(ident, id, _)| Lifetime { id, ident })
|
||||
.collect();
|
||||
debug!(?collected_lifetime_mapping);
|
||||
|
||||
self.with_hir_id_owner(opaque_ty_node_id, |this| {
|
||||
// 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 opaque_ty_ref = self.lower_opaque_inner(
|
||||
opaque_ty_node_id,
|
||||
hir::OpaqueTyOrigin::AsyncFn(fn_def_id),
|
||||
in_trait,
|
||||
captured_lifetimes,
|
||||
span,
|
||||
opaque_ty_span,
|
||||
|this| {
|
||||
let future_bound = this.lower_async_fn_output_type_to_future_bound(
|
||||
output,
|
||||
span,
|
||||
|
@ -2052,96 +1905,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
}
|
||||
},
|
||||
);
|
||||
|
||||
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);
|
||||
|
||||
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,
|
||||
def_id: this.local_def_id(new_node_id),
|
||||
name,
|
||||
span: lifetime.ident.span,
|
||||
pure_wrt_drop: false,
|
||||
kind: hir::GenericParamKind::Lifetime { kind },
|
||||
colon_span: None,
|
||||
source: hir::GenericParamSource::Generics,
|
||||
}
|
||||
arena_vec![this; future_bound]
|
||||
},
|
||||
));
|
||||
debug!("lower_async_fn_ret_ty: generic_params={:#?}", generic_params);
|
||||
|
||||
let lifetime_mapping = if in_trait {
|
||||
Some(
|
||||
&*self.arena.alloc_from_iter(
|
||||
collected_lifetime_mapping
|
||||
.iter()
|
||||
.map(|(lifetime, def_id)| (**lifetime, *def_id)),
|
||||
),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
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),
|
||||
lifetime_mapping,
|
||||
in_trait,
|
||||
};
|
||||
|
||||
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
|
||||
// arguments to our opaque type. Continuing with our example,
|
||||
// we're creating the type arguments for the return type:
|
||||
//
|
||||
// ```
|
||||
// Bar<'a, 'b, '0, '1, '_>
|
||||
// ```
|
||||
//
|
||||
// For the "input" lifetime parameters, we wish to create
|
||||
// references to the parameters themselves, including the
|
||||
// "implicit" ones created from parameter types (`'a`, `'b`,
|
||||
// '`0`, `'1`).
|
||||
//
|
||||
// For the "output" lifetime parameters, we just want to
|
||||
// generate `'_`.
|
||||
let generic_args = self.arena.alloc_from_iter(
|
||||
collected_lifetime_mapping
|
||||
.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.
|
||||
let opaque_ty_ref = hir::TyKind::OpaqueDef(
|
||||
hir::ItemId { owner_id: hir::OwnerId { def_id: opaque_ty_def_id } },
|
||||
generic_args,
|
||||
in_trait,
|
||||
);
|
||||
let opaque_ty = self.ty(opaque_ty_span, opaque_ty_ref);
|
||||
hir::FnRetTy::Return(self.arena.alloc(opaque_ty))
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use super::ResolverAstLoweringExt;
|
||||
use rustc_ast::visit::{self, BoundKind, LifetimeCtxt, Visitor};
|
||||
use rustc_ast::{FnRetTy, GenericBounds, Lifetime, NodeId, PathSegment, PolyTraitRef, Ty, TyKind};
|
||||
use rustc_ast::{GenericBounds, Lifetime, NodeId, PathSegment, PolyTraitRef, Ty, TyKind};
|
||||
use rustc_hir::def::LifetimeRes;
|
||||
use rustc_middle::span_bug;
|
||||
use rustc_middle::ty::ResolverAstLowering;
|
||||
|
@ -94,12 +94,6 @@ impl<'ast> Visitor<'ast> for LifetimeCollectVisitor<'ast> {
|
|||
}
|
||||
}
|
||||
|
||||
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,
|
||||
|
|
|
@ -2675,7 +2675,7 @@ pub struct OpaqueTy<'hir> {
|
|||
///
|
||||
/// This mapping associated a captured lifetime (first parameter) with the new
|
||||
/// early-bound lifetime that was generated for the opaque.
|
||||
pub lifetime_mapping: Option<&'hir [(Lifetime, LocalDefId)]>,
|
||||
pub lifetime_mapping: Option<&'hir [(&'hir Lifetime, LocalDefId)]>,
|
||||
/// Whether the opaque is a return-position impl trait (or async future)
|
||||
/// originating from a trait method. This makes it so that the opaque is
|
||||
/// lowered as an associated type.
|
||||
|
|
|
@ -82,7 +82,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
|
|||
tcx,
|
||||
def_id,
|
||||
lifetime_mapping.iter().map(|(lifetime, def_id)| {
|
||||
(*lifetime, (*def_id, lifetime.ident.name, lifetime.ident.span))
|
||||
(**lifetime, (*def_id, lifetime.ident.name, lifetime.ident.span))
|
||||
}),
|
||||
tcx.generics_of(def_id.to_def_id()),
|
||||
&mut predicates,
|
||||
|
|
|
@ -904,9 +904,12 @@ impl<'a: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast,
|
|||
sig.decl.inputs.iter().map(|Param { ty, .. }| (None, &**ty)),
|
||||
&sig.decl.output,
|
||||
);
|
||||
|
||||
if let Some((async_node_id, span)) = sig.header.asyncness.opt_return_id() {
|
||||
this.record_lifetime_params_for_impl_trait(async_node_id, span);
|
||||
}
|
||||
},
|
||||
);
|
||||
self.record_lifetime_params_for_async(fn_id, sig.header.asyncness.opt_return_id());
|
||||
return;
|
||||
}
|
||||
FnKind::Fn(..) => {
|
||||
|
@ -942,11 +945,13 @@ impl<'a: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast,
|
|||
.iter()
|
||||
.map(|Param { pat, ty, .. }| (Some(&**pat), &**ty)),
|
||||
&declaration.output,
|
||||
)
|
||||
},
|
||||
);
|
||||
|
||||
this.record_lifetime_params_for_async(fn_id, async_node_id);
|
||||
if let Some((async_node_id, span)) = async_node_id {
|
||||
this.record_lifetime_params_for_impl_trait(async_node_id, span);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
if let Some(body) = body {
|
||||
// Ignore errors in function bodies if this is rustdoc
|
||||
|
@ -1694,6 +1699,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
|
|||
// Leave the responsibility to create the `LocalDefId` to lowering.
|
||||
let param = self.r.next_node_id();
|
||||
let res = LifetimeRes::Fresh { param, binder };
|
||||
self.record_lifetime_param(param, res);
|
||||
|
||||
// Record the created lifetime parameter so lowering can pick it up and add it to HIR.
|
||||
self.r
|
||||
|
@ -4326,22 +4332,16 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
|
|||
)
|
||||
}
|
||||
|
||||
/// Construct the list of in-scope lifetime parameters for async lowering.
|
||||
/// Construct the list of in-scope lifetime parameters for impl trait lowering.
|
||||
/// We include all lifetime parameters, either named or "Fresh".
|
||||
/// The order of those parameters does not matter, as long as it is
|
||||
/// deterministic.
|
||||
fn record_lifetime_params_for_async(
|
||||
&mut self,
|
||||
fn_id: NodeId,
|
||||
async_node_id: Option<(NodeId, Span)>,
|
||||
) {
|
||||
if let Some((async_node_id, span)) = async_node_id {
|
||||
let mut extra_lifetime_params =
|
||||
self.r.extra_lifetime_params_map.get(&fn_id).cloned().unwrap_or_default();
|
||||
fn record_lifetime_params_for_impl_trait(&mut self, impl_trait_node_id: NodeId, span: Span) {
|
||||
let mut extra_lifetime_params = vec![];
|
||||
|
||||
for rib in self.lifetime_ribs.iter().rev() {
|
||||
extra_lifetime_params.extend(
|
||||
rib.bindings.iter().map(|(&ident, &(node_id, res))| (ident, node_id, res)),
|
||||
);
|
||||
extra_lifetime_params
|
||||
.extend(rib.bindings.iter().map(|(&ident, &(node_id, res))| (ident, node_id, res)));
|
||||
match rib.kind {
|
||||
LifetimeRibKind::Item => break,
|
||||
LifetimeRibKind::AnonymousCreateParameter { binder, .. } => {
|
||||
|
@ -4357,8 +4357,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
|
|||
}
|
||||
}
|
||||
}
|
||||
self.r.extra_lifetime_params_map.insert(async_node_id, extra_lifetime_params);
|
||||
}
|
||||
self.r.extra_lifetime_params_map.insert(impl_trait_node_id, extra_lifetime_params);
|
||||
}
|
||||
|
||||
fn resolve_and_cache_rustdoc_path(&mut self, path_str: &str, ns: Namespace) -> Option<Res> {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue