Consolidate opaque ty and async fn lowering code
This commit is contained in:
parent
fbc11e9690
commit
57a96893f6
5 changed files with 191 additions and 429 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)
|
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
|
/// 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`.
|
/// 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.
|
// frequently opened issues show.
|
||||||
let opaque_ty_span = self.mark_span_with_reason(DesugaringKind::OpaqueTy, span, None);
|
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(
|
let opaque_ty_def_id = self.create_def(
|
||||||
self.current_hir_id_owner.def_id,
|
self.current_hir_id_owner.def_id,
|
||||||
opaque_ty_node_id,
|
opaque_ty_node_id,
|
||||||
|
@ -1529,201 +1563,39 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||||
);
|
);
|
||||||
debug!(?opaque_ty_def_id);
|
debug!(?opaque_ty_def_id);
|
||||||
|
|
||||||
// If this came from a TAIT (as opposed to a function that returns an RPIT), we only want
|
// Map from captured (old) lifetime to synthetic (new) lifetime.
|
||||||
// to capture the lifetimes that appear in the bounds. So visit the bounds to find out
|
// Used to resolve lifetimes in the bounds of the opaque.
|
||||||
// exactly which ones those are.
|
let mut captured_to_synthesized_mapping = FxHashMap::default();
|
||||||
let lifetimes_to_remap = match origin {
|
// List of (early-bound) synthetic lifetimes that are owned by the opaque.
|
||||||
hir::OpaqueTyOrigin::TyAlias { .. } => {
|
// This is used to create the `hir::Generics` owned by the opaque.
|
||||||
// in a TAIT like `type Foo<'a> = impl Foo<'a>`, we don't keep all the lifetime parameters
|
let mut synthesized_lifetime_definitions = vec![];
|
||||||
Vec::new()
|
// Pairs of lifetime arg (that resolves to the captured lifetime)
|
||||||
}
|
// and the def-id of the (early-bound) synthetic lifetime definition.
|
||||||
hir::OpaqueTyOrigin::AsyncFn(..) | hir::OpaqueTyOrigin::FnReturn(..) => {
|
// This is used both to create generics for the `TyKind::OpaqueDef` that
|
||||||
// in fn return position, like the `fn test<'a>() -> impl Debug + 'a` example,
|
// we return, and also as a captured lifetime mapping for RPITITs.
|
||||||
// we only keep the lifetimes that appear in the `impl Debug` itself:
|
let mut synthesized_lifetime_args = vec![];
|
||||||
lifetime_collector::lifetimes_in_bounds(&self.resolver, bounds)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
debug!(?lifetimes_to_remap);
|
|
||||||
|
|
||||||
let mut new_remapping = FxHashMap::default();
|
for lifetime in captured_lifetimes_to_duplicate {
|
||||||
|
|
||||||
// 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 {
|
|
||||||
let res = self.resolver.get_lifetime_res(lifetime.id).unwrap_or(LifetimeRes::Error);
|
let res = self.resolver.get_lifetime_res(lifetime.id).unwrap_or(LifetimeRes::Error);
|
||||||
debug!(?res);
|
let old_def_id = match res {
|
||||||
|
LifetimeRes::Param { param: old_def_id, binder: _ } => old_def_id,
|
||||||
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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LifetimeRes::Fresh { param, binder: _ } => {
|
LifetimeRes::Fresh { param, binder: _ } => {
|
||||||
debug_assert_eq!(lifetime.ident.name, kw::UnderscoreLifetime);
|
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() {
|
if let Some(old_def_id) = self.orig_opt_local_def_id(param) {
|
||||||
let node_id = self.next_node_id();
|
old_def_id
|
||||||
|
} else {
|
||||||
let new_def_id = self.create_def(
|
self.tcx
|
||||||
parent_def_id,
|
.sess
|
||||||
node_id,
|
.delay_span_bug(lifetime.ident.span, "no def-id for fresh lifetime");
|
||||||
DefPathData::LifetimeNs(kw::UnderscoreLifetime),
|
continue;
|
||||||
lifetime.ident.span,
|
|
||||||
);
|
|
||||||
remapping.insert(old_def_id, new_def_id);
|
|
||||||
|
|
||||||
result.push((node_id, *lifetime));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LifetimeRes::Static | LifetimeRes::Error => {}
|
// Opaques do not capture `'static`
|
||||||
|
LifetimeRes::Static | LifetimeRes::Error => {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
res => {
|
res => {
|
||||||
let bug_msg = format!(
|
let bug_msg = format!(
|
||||||
|
@ -1732,10 +1604,113 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||||
);
|
);
|
||||||
span_bug!(lifetime.ident.span, "{}", bug_msg);
|
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] {
|
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(
|
self.lower_async_fn_ret_ty(
|
||||||
&decl.output,
|
&decl.output,
|
||||||
fn_node_id,
|
fn_def_id,
|
||||||
ret_id,
|
ret_id,
|
||||||
matches!(kind, FnDeclKind::Trait),
|
matches!(kind, FnDeclKind::Trait),
|
||||||
)
|
)
|
||||||
|
@ -1892,151 +1868,28 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||||
fn lower_async_fn_ret_ty(
|
fn lower_async_fn_ret_ty(
|
||||||
&mut self,
|
&mut self,
|
||||||
output: &FnRetTy,
|
output: &FnRetTy,
|
||||||
fn_node_id: NodeId,
|
fn_def_id: LocalDefId,
|
||||||
opaque_ty_node_id: NodeId,
|
opaque_ty_node_id: NodeId,
|
||||||
in_trait: bool,
|
in_trait: bool,
|
||||||
) -> hir::FnRetTy<'hir> {
|
) -> 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 opaque_ty_span = self.mark_span_with_reason(DesugaringKind::Async, span, None);
|
||||||
|
|
||||||
let fn_def_id = self.local_def_id(fn_node_id);
|
let captured_lifetimes: Vec<_> = self
|
||||||
|
.resolver
|
||||||
let opaque_ty_def_id =
|
.take_extra_lifetime_params(opaque_ty_node_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)
|
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(new_node_id, lifetime)| (new_node_id, lifetime, None)),
|
.map(|(ident, id, _)| Lifetime { id, ident })
|
||||||
);
|
|
||||||
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)
|
|
||||||
})
|
|
||||||
.collect();
|
.collect();
|
||||||
debug!(?collected_lifetime_mapping);
|
|
||||||
|
|
||||||
self.with_hir_id_owner(opaque_ty_node_id, |this| {
|
let opaque_ty_ref = self.lower_opaque_inner(
|
||||||
// Install the remapping from old to new (if any):
|
opaque_ty_node_id,
|
||||||
this.with_remapping(new_remapping, |this| {
|
hir::OpaqueTyOrigin::AsyncFn(fn_def_id),
|
||||||
// We have to be careful to get elision right here. The
|
in_trait,
|
||||||
// idea is that we create a lifetime parameter for each
|
captured_lifetimes,
|
||||||
// lifetime in the return type. So, given a return type
|
span,
|
||||||
// like `async fn foo(..) -> &[&u32]`, we lower to `impl
|
opaque_ty_span,
|
||||||
// Future<Output = &'1 [ &'2 u32 ]>`.
|
|this| {
|
||||||
//
|
|
||||||
// Then, we will create `fn foo(..) -> Foo<'_, '_>`, and
|
|
||||||
// hence the elision takes place at the fn site.
|
|
||||||
let future_bound = this.lower_async_fn_output_type_to_future_bound(
|
let future_bound = this.lower_async_fn_output_type_to_future_bound(
|
||||||
output,
|
output,
|
||||||
span,
|
span,
|
||||||
|
@ -2052,96 +1905,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
arena_vec![this; future_bound]
|
||||||
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,
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
));
|
|
||||||
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);
|
let opaque_ty = self.ty(opaque_ty_span, opaque_ty_ref);
|
||||||
hir::FnRetTy::Return(self.arena.alloc(opaque_ty))
|
hir::FnRetTy::Return(self.arena.alloc(opaque_ty))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use super::ResolverAstLoweringExt;
|
use super::ResolverAstLoweringExt;
|
||||||
use rustc_ast::visit::{self, BoundKind, LifetimeCtxt, Visitor};
|
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_hir::def::LifetimeRes;
|
||||||
use rustc_middle::span_bug;
|
use rustc_middle::span_bug;
|
||||||
use rustc_middle::ty::ResolverAstLowering;
|
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(
|
pub fn lifetimes_in_bounds(
|
||||||
resolver: &ResolverAstLowering,
|
resolver: &ResolverAstLowering,
|
||||||
bounds: &GenericBounds,
|
bounds: &GenericBounds,
|
||||||
|
|
|
@ -2675,7 +2675,7 @@ pub struct OpaqueTy<'hir> {
|
||||||
///
|
///
|
||||||
/// This mapping associated a captured lifetime (first parameter) with the new
|
/// This mapping associated a captured lifetime (first parameter) with the new
|
||||||
/// early-bound lifetime that was generated for the opaque.
|
/// 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)
|
/// 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
|
/// originating from a trait method. This makes it so that the opaque is
|
||||||
/// lowered as an associated type.
|
/// lowered as an associated type.
|
||||||
|
|
|
@ -82,7 +82,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
|
||||||
tcx,
|
tcx,
|
||||||
def_id,
|
def_id,
|
||||||
lifetime_mapping.iter().map(|(lifetime, 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()),
|
tcx.generics_of(def_id.to_def_id()),
|
||||||
&mut predicates,
|
&mut predicates,
|
||||||
|
|
|
@ -1694,6 +1694,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
|
||||||
// Leave the responsibility to create the `LocalDefId` to lowering.
|
// Leave the responsibility to create the `LocalDefId` to lowering.
|
||||||
let param = self.r.next_node_id();
|
let param = self.r.next_node_id();
|
||||||
let res = LifetimeRes::Fresh { param, binder };
|
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.
|
// Record the created lifetime parameter so lowering can pick it up and add it to HIR.
|
||||||
self.r
|
self.r
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue