1
Fork 0

Create a specific struct for lifetime capture.

This commit is contained in:
Camille GILLOT 2022-04-20 20:13:42 +02:00
parent a621b8499f
commit 6857a8d14e
2 changed files with 170 additions and 137 deletions

View file

@ -32,6 +32,7 @@
#![feature(crate_visibility_modifier)] #![feature(crate_visibility_modifier)]
#![feature(box_patterns)] #![feature(box_patterns)]
#![feature(let_chains)]
#![feature(let_else)] #![feature(let_else)]
#![feature(never_type)] #![feature(never_type)]
#![recursion_limit = "256"] #![recursion_limit = "256"]
@ -135,24 +136,8 @@ struct LoweringContext<'a, 'hir: 'a> {
/// Currently in-scope lifetimes defined in impl headers, fn headers, or HRTB. /// Currently in-scope lifetimes defined in impl headers, fn headers, or HRTB.
in_scope_lifetimes: Vec<(ParamName, LocalDefId)>, in_scope_lifetimes: Vec<(ParamName, LocalDefId)>,
/// Used to handle lifetimes appearing in impl-traits. When we lower a lifetime, /// Used to handle lifetimes appearing in impl-traits.
/// it is inserted in the `FxHashMap`, and the resolution is modified so to point captured_lifetimes: Option<LifetimeCaptureContext>,
/// to the lifetime parameter impl-trait will generate.
/// When traversing `for<...>` binders, they are inserted in the `FxHashSet` so
/// we know *not* to rebind the introduced lifetimes.
captured_lifetimes: Option<(
LocalDefId, // parent def_id for new definitions
FxHashMap<
LocalDefId, // original parameter id
(
Span, // Span
NodeId, // synthetized parameter id
ParamName, // parameter name
LifetimeRes, // original resolution
),
>,
FxHashSet<NodeId>, // traversed binders, to ignore
)>,
current_hir_id_owner: LocalDefId, current_hir_id_owner: LocalDefId,
item_local_id_counter: hir::ItemLocalId, item_local_id_counter: hir::ItemLocalId,
@ -179,6 +164,9 @@ pub enum LifetimeRes {
/// - a TraitRef's ref_id, identifying the `for<...>` binder; /// - a TraitRef's ref_id, identifying the `for<...>` binder;
/// - a BareFn type's id; /// - a BareFn type's id;
/// - a Path's id when this path has parenthesized generic args. /// - a Path's id when this path has parenthesized generic args.
///
/// This information is used for impl-trait lifetime captures, to know when to or not to
/// capture any given lifetime.
binder: NodeId, binder: NodeId,
}, },
/// Created a generic parameter for an anonymous lifetime. /// Created a generic parameter for an anonymous lifetime.
@ -206,6 +194,28 @@ pub enum LifetimeRes {
ElidedAnchor { start: NodeId, end: NodeId }, ElidedAnchor { start: NodeId, end: NodeId },
} }
/// When we lower a lifetime, it is inserted in `captures`, and the resolution is modified so
/// to point to the lifetime parameter impl-trait will generate.
/// When traversing `for<...>` binders, they are inserted in `binders_to_ignore` so we know *not*
/// to rebind the introduced lifetimes.
#[derive(Debug)]
struct LifetimeCaptureContext {
/// parent def_id for new definitions
parent_def_id: LocalDefId,
/// Set of lifetimes to rebind.
captures: FxHashMap<
LocalDefId, // original parameter id
(
Span, // Span
NodeId, // synthetized parameter id
ParamName, // parameter name
LifetimeRes, // original resolution
),
>,
/// Traversed binders. The ids in this set should *not* be rebound.
binders_to_ignore: FxHashSet<NodeId>,
}
pub trait ResolverAstLowering { pub trait ResolverAstLowering {
fn def_key(&self, id: DefId) -> DefKey; fn def_key(&self, id: DefId) -> DefKey;
@ -790,6 +800,45 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
(lowered_generics, res) (lowered_generics, res)
} }
/// Setup lifetime capture for and impl-trait.
/// The captures will be added to `captures`.
fn while_capturing_lifetimes<T>(
&mut self,
parent_def_id: LocalDefId,
captures: &mut FxHashMap<LocalDefId, (Span, NodeId, ParamName, LifetimeRes)>,
f: impl FnOnce(&mut Self) -> T,
) -> T {
let lifetime_stash = std::mem::replace(
&mut self.captured_lifetimes,
Some(LifetimeCaptureContext {
parent_def_id,
captures: std::mem::take(captures),
binders_to_ignore: Default::default(),
}),
);
let ret = f(self);
let ctxt = std::mem::replace(&mut self.captured_lifetimes, lifetime_stash).unwrap();
*captures = ctxt.captures;
ret
}
/// Register a binder to be ignored for lifetime capture.
#[tracing::instrument(level = "debug", skip(self, f))]
#[inline]
fn with_lifetime_binder<T>(&mut self, binder: NodeId, f: impl FnOnce(&mut Self) -> T) -> T {
if let Some(ctxt) = &mut self.captured_lifetimes {
ctxt.binders_to_ignore.insert(binder);
}
let ret = f(self);
if let Some(ctxt) = &mut self.captured_lifetimes {
ctxt.binders_to_ignore.remove(&binder);
}
ret
}
fn with_dyn_type_scope<T>(&mut self, in_scope: bool, f: impl FnOnce(&mut Self) -> T) -> T { fn with_dyn_type_scope<T>(&mut self, in_scope: bool, f: impl FnOnce(&mut Self) -> T) -> T {
let was_in_dyn_type = self.is_in_dyn_type; let was_in_dyn_type = self.is_in_dyn_type;
self.is_in_dyn_type = in_scope; self.is_in_dyn_type = in_scope;
@ -1197,11 +1246,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
hir::TyKind::Rptr(lifetime, self.lower_mt(mt, itctx)) hir::TyKind::Rptr(lifetime, self.lower_mt(mt, itctx))
} }
TyKind::BareFn(ref f) => self.with_in_scope_lifetime_defs(&f.generic_params, |this| { TyKind::BareFn(ref f) => self.with_in_scope_lifetime_defs(&f.generic_params, |this| {
if let Some((_, _, binders)) = &mut this.captured_lifetimes { this.with_lifetime_binder(t.id, |this| {
binders.insert(t.id); hir::TyKind::BareFn(this.arena.alloc(hir::BareFnTy {
}
let ret = hir::TyKind::BareFn(this.arena.alloc(hir::BareFnTy {
generic_params: this.lower_generic_params( generic_params: this.lower_generic_params(
&f.generic_params, &f.generic_params,
ImplTraitContext::Disallowed(ImplTraitPosition::Generic), ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
@ -1210,12 +1256,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
abi: this.lower_extern(f.ext), abi: this.lower_extern(f.ext),
decl: this.lower_fn_decl(&f.decl, None, FnDeclKind::Pointer, None), decl: this.lower_fn_decl(&f.decl, None, FnDeclKind::Pointer, None),
param_names: this.lower_fn_params_to_names(&f.decl), param_names: this.lower_fn_params_to_names(&f.decl),
})); }))
})
if let Some((_, _, binders)) = &mut this.captured_lifetimes {
binders.remove(&t.id);
}
ret
}), }),
TyKind::Never => hir::TyKind::Never, TyKind::Never => hir::TyKind::Never,
TyKind::Tup(ref tys) => { TyKind::Tup(ref tys) => {
@ -1366,15 +1408,15 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
let mut collected_lifetimes = FxHashMap::default(); let mut collected_lifetimes = FxHashMap::default();
self.with_hir_id_owner(opaque_ty_node_id, |lctx| { self.with_hir_id_owner(opaque_ty_node_id, |lctx| {
let capture_framework = if origin == hir::OpaqueTyOrigin::TyAlias { let hir_bounds = if origin == hir::OpaqueTyOrigin::TyAlias {
None lower_bounds(lctx)
} else { } else {
Some((opaque_ty_def_id, FxHashMap::default(), FxHashSet::default())) lctx.while_capturing_lifetimes(
opaque_ty_def_id,
&mut collected_lifetimes,
lower_bounds,
)
}; };
let lifetime_stash = std::mem::replace(&mut lctx.captured_lifetimes, capture_framework);
let hir_bounds = lower_bounds(lctx);
collected_lifetimes = std::mem::replace(&mut lctx.captured_lifetimes, lifetime_stash)
.map_or_else(FxHashMap::default, |c| c.1);
debug!(?collected_lifetimes); debug!(?collected_lifetimes);
let lifetime_defs = lctx.arena.alloc_from_iter(collected_lifetimes.iter().map( let lifetime_defs = lctx.arena.alloc_from_iter(collected_lifetimes.iter().map(
@ -1716,12 +1758,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
debug!(?captures); debug!(?captures);
self.with_hir_id_owner(opaque_ty_node_id, |this| { self.with_hir_id_owner(opaque_ty_node_id, |this| {
let lifetime_stash = std::mem::replace(
&mut this.captured_lifetimes,
Some((opaque_ty_def_id, std::mem::take(&mut captures), FxHashSet::default())),
);
debug!("lower_async_fn_ret_ty: lifetimes_to_define={:#?}", this.lifetimes_to_define); debug!("lower_async_fn_ret_ty: lifetimes_to_define={:#?}", this.lifetimes_to_define);
let future_bound =
this.while_capturing_lifetimes(opaque_ty_def_id, &mut captures, |this| {
// We have to be careful to get elision right here. The // We have to be careful to get elision right here. The
// idea is that we create a lifetime parameter for each // idea is that we create a lifetime parameter for each
// lifetime in the return type. So, given a return type // lifetime in the return type. So, given a return type
@ -1730,10 +1769,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
// //
// Then, we will create `fn foo(..) -> Foo<'_, '_>`, and // Then, we will create `fn foo(..) -> Foo<'_, '_>`, and
// hence the elision takes place at the fn site. // hence the elision takes place at the fn site.
let future_bound = this.lower_async_fn_output_type_to_future_bound(output, fn_def_id, span)
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: future_bound={:#?}", future_bound);
captures = std::mem::replace(&mut this.captured_lifetimes, lifetime_stash).unwrap().1;
debug!("lower_async_fn_ret_ty: captures={:#?}", captures); debug!("lower_async_fn_ret_ty: captures={:#?}", captures);
let generic_params = let generic_params =
@ -1882,11 +1920,13 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
LifetimeRes::Param { param, binder } => { LifetimeRes::Param { param, binder } => {
debug_assert_ne!(ident.name, kw::UnderscoreLifetime); debug_assert_ne!(ident.name, kw::UnderscoreLifetime);
let p_name = ParamName::Plain(ident); let p_name = ParamName::Plain(ident);
if let Some((parent_def_id, captures, binders)) = &mut self.captured_lifetimes { if let Some(LifetimeCaptureContext { parent_def_id, captures, binders_to_ignore }) =
&mut self.captured_lifetimes
&& !binders_to_ignore.contains(&binder)
{
match captures.entry(param) { match captures.entry(param) {
Entry::Occupied(_) => {} Entry::Occupied(_) => {}
Entry::Vacant(v) => { Entry::Vacant(v) => {
if !binders.contains(&binder) {
let p_id = self.resolver.next_node_id(); let p_id = self.resolver.next_node_id();
self.resolver.create_def( self.resolver.create_def(
*parent_def_id, *parent_def_id,
@ -1900,7 +1940,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
} }
} }
} }
}
hir::LifetimeName::Param(p_name) hir::LifetimeName::Param(p_name)
} }
LifetimeRes::Fresh { mut param, introducer, binder } => { LifetimeRes::Fresh { mut param, introducer, binder } => {
@ -1908,11 +1947,13 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
// Only items are allowed to introduce fresh lifetimes, // Only items are allowed to introduce fresh lifetimes,
// so we know `binder` has a `LocalDefId`. // so we know `binder` has a `LocalDefId`.
let binder_def_id = self.resolver.local_def_id(binder); let binder_def_id = self.resolver.local_def_id(binder);
if let Some((parent_def_id, captures, binders)) = &mut self.captured_lifetimes { if let Some(LifetimeCaptureContext { parent_def_id, captures, binders_to_ignore }) =
&mut self.captured_lifetimes
&& !binders_to_ignore.contains(&binder)
{
match captures.entry(param) { match captures.entry(param) {
Entry::Occupied(o) => param = self.resolver.local_def_id(o.get().1), Entry::Occupied(o) => param = self.resolver.local_def_id(o.get().1),
Entry::Vacant(v) => { Entry::Vacant(v) => {
if !binders.contains(&binder) {
let p_id = self.resolver.next_node_id(); let p_id = self.resolver.next_node_id();
let p_def_id = self.resolver.create_def( let p_def_id = self.resolver.create_def(
*parent_def_id, *parent_def_id,
@ -1927,7 +1968,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
param = p_def_id; param = p_def_id;
} }
} }
}
} else if let Some(introducer) = introducer { } else if let Some(introducer) = introducer {
if self.is_collecting_anonymous_lifetimes == Some(binder_def_id) if self.is_collecting_anonymous_lifetimes == Some(binder_def_id)
&& self.resolver.opt_local_def_id(introducer) == Some(param) && self.resolver.opt_local_def_id(introducer) == Some(param)
@ -1948,8 +1988,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
} else { } else {
hir::LifetimeName::Underscore hir::LifetimeName::Underscore
}; };
match &mut self.captured_lifetimes { if let Some(LifetimeCaptureContext { parent_def_id, captures, binders_to_ignore }) =
Some((parent_def_id, captures, binders)) if !binders.contains(&binder) => { &mut self.captured_lifetimes
&& !binders_to_ignore.contains(&binder)
{
let p_id = self.resolver.next_node_id(); let p_id = self.resolver.next_node_id();
let p_def_id = self.resolver.create_def( let p_def_id = self.resolver.create_def(
*parent_def_id, *parent_def_id,
@ -1961,8 +2003,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
let p_name = ParamName::Fresh(p_def_id); let p_name = ParamName::Fresh(p_def_id);
captures.insert(p_def_id, (span, p_id, p_name, res)); captures.insert(p_def_id, (span, p_id, p_name, res));
hir::LifetimeName::Param(p_name) hir::LifetimeName::Param(p_name)
} } else {
_ => l_name, l_name
} }
} }
LifetimeRes::Static => hir::LifetimeName::Static, LifetimeRes::Static => hir::LifetimeName::Static,
@ -2069,16 +2111,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
self.lower_generic_params(&p.bound_generic_params, itctx.reborrow()); self.lower_generic_params(&p.bound_generic_params, itctx.reborrow());
let trait_ref = self.with_in_scope_lifetime_defs(&p.bound_generic_params, |this| { let trait_ref = self.with_in_scope_lifetime_defs(&p.bound_generic_params, |this| {
if let Some((_, _, binders)) = &mut this.captured_lifetimes { this.with_lifetime_binder(p.trait_ref.ref_id, |this| {
binders.insert(p.trait_ref.ref_id); this.lower_trait_ref(&p.trait_ref, itctx.reborrow())
} })
let trait_ref = this.lower_trait_ref(&p.trait_ref, itctx.reborrow());
if let Some((_, _, binders)) = &mut this.captured_lifetimes {
binders.remove(&p.trait_ref.ref_id);
}
trait_ref
}); });
hir::PolyTraitRef { bound_generic_params, trait_ref, span: self.lower_span(p.span) } hir::PolyTraitRef { bound_generic_params, trait_ref, span: self.lower_span(p.span) }

View file

@ -353,33 +353,31 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
// a hidden lifetime parameter. This is needed for backwards // a hidden lifetime parameter. This is needed for backwards
// compatibility, even in contexts like an impl header where // compatibility, even in contexts like an impl header where
// we generally don't permit such things (see #51008). // we generally don't permit such things (see #51008).
if let Some((_, _, binders)) = &mut self.captured_lifetimes { self.with_lifetime_binder(id, |this| {
binders.insert(id);
}
let ParenthesizedArgs { span, inputs, inputs_span, output } = data; let ParenthesizedArgs { span, inputs, inputs_span, output } = data;
let inputs = self.arena.alloc_from_iter(inputs.iter().map(|ty| { let inputs = this.arena.alloc_from_iter(inputs.iter().map(|ty| {
self.lower_ty_direct(ty, ImplTraitContext::Disallowed(ImplTraitPosition::FnTraitParam)) this.lower_ty_direct(
ty,
ImplTraitContext::Disallowed(ImplTraitPosition::FnTraitParam),
)
})); }));
let output_ty = match output { let output_ty = match output {
FnRetTy::Ty(ty) => { FnRetTy::Ty(ty) => this
self.lower_ty(&ty, ImplTraitContext::Disallowed(ImplTraitPosition::FnTraitReturn)) .lower_ty(&ty, ImplTraitContext::Disallowed(ImplTraitPosition::FnTraitReturn)),
} FnRetTy::Default(_) => this.arena.alloc(this.ty_tup(*span, &[])),
FnRetTy::Default(_) => self.arena.alloc(self.ty_tup(*span, &[])),
}; };
let args = smallvec![GenericArg::Type(self.ty_tup(*inputs_span, inputs))]; let args = smallvec![GenericArg::Type(this.ty_tup(*inputs_span, inputs))];
let binding = self.output_ty_binding(output_ty.span, output_ty); let binding = this.output_ty_binding(output_ty.span, output_ty);
if let Some((_, _, binders)) = &mut self.captured_lifetimes {
binders.remove(&id);
}
( (
GenericArgsCtor { GenericArgsCtor {
args, args,
bindings: arena_vec![self; binding], bindings: arena_vec![this; binding],
parenthesized: true, parenthesized: true,
span: data.inputs_span, span: data.inputs_span,
}, },
false, false,
) )
})
} }
/// An associated type binding `Output = $ty`. /// An associated type binding `Output = $ty`.