Skip to content

Commit 6857a8d

Browse files
committed
Create a specific struct for lifetime capture.
1 parent a621b84 commit 6857a8d

File tree

2 files changed

+172
-139
lines changed

2 files changed

+172
-139
lines changed

compiler/rustc_ast_lowering/src/lib.rs

+147-112
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
3333
#![feature(crate_visibility_modifier)]
3434
#![feature(box_patterns)]
35+
#![feature(let_chains)]
3536
#![feature(let_else)]
3637
#![feature(never_type)]
3738
#![recursion_limit = "256"]
@@ -135,24 +136,8 @@ struct LoweringContext<'a, 'hir: 'a> {
135136
/// Currently in-scope lifetimes defined in impl headers, fn headers, or HRTB.
136137
in_scope_lifetimes: Vec<(ParamName, LocalDefId)>,
137138

138-
/// Used to handle lifetimes appearing in impl-traits. When we lower a lifetime,
139-
/// it is inserted in the `FxHashMap`, and the resolution is modified so to point
140-
/// to the lifetime parameter impl-trait will generate.
141-
/// When traversing `for<...>` binders, they are inserted in the `FxHashSet` so
142-
/// we know *not* to rebind the introduced lifetimes.
143-
captured_lifetimes: Option<(
144-
LocalDefId, // parent def_id for new definitions
145-
FxHashMap<
146-
LocalDefId, // original parameter id
147-
(
148-
Span, // Span
149-
NodeId, // synthetized parameter id
150-
ParamName, // parameter name
151-
LifetimeRes, // original resolution
152-
),
153-
>,
154-
FxHashSet<NodeId>, // traversed binders, to ignore
155-
)>,
139+
/// Used to handle lifetimes appearing in impl-traits.
140+
captured_lifetimes: Option<LifetimeCaptureContext>,
156141

157142
current_hir_id_owner: LocalDefId,
158143
item_local_id_counter: hir::ItemLocalId,
@@ -179,6 +164,9 @@ pub enum LifetimeRes {
179164
/// - a TraitRef's ref_id, identifying the `for<...>` binder;
180165
/// - a BareFn type's id;
181166
/// - a Path's id when this path has parenthesized generic args.
167+
///
168+
/// This information is used for impl-trait lifetime captures, to know when to or not to
169+
/// capture any given lifetime.
182170
binder: NodeId,
183171
},
184172
/// Created a generic parameter for an anonymous lifetime.
@@ -206,6 +194,28 @@ pub enum LifetimeRes {
206194
ElidedAnchor { start: NodeId, end: NodeId },
207195
}
208196

197+
/// When we lower a lifetime, it is inserted in `captures`, and the resolution is modified so
198+
/// to point to the lifetime parameter impl-trait will generate.
199+
/// When traversing `for<...>` binders, they are inserted in `binders_to_ignore` so we know *not*
200+
/// to rebind the introduced lifetimes.
201+
#[derive(Debug)]
202+
struct LifetimeCaptureContext {
203+
/// parent def_id for new definitions
204+
parent_def_id: LocalDefId,
205+
/// Set of lifetimes to rebind.
206+
captures: FxHashMap<
207+
LocalDefId, // original parameter id
208+
(
209+
Span, // Span
210+
NodeId, // synthetized parameter id
211+
ParamName, // parameter name
212+
LifetimeRes, // original resolution
213+
),
214+
>,
215+
/// Traversed binders. The ids in this set should *not* be rebound.
216+
binders_to_ignore: FxHashSet<NodeId>,
217+
}
218+
209219
pub trait ResolverAstLowering {
210220
fn def_key(&self, id: DefId) -> DefKey;
211221

@@ -790,6 +800,45 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
790800
(lowered_generics, res)
791801
}
792802

803+
/// Setup lifetime capture for and impl-trait.
804+
/// The captures will be added to `captures`.
805+
fn while_capturing_lifetimes<T>(
806+
&mut self,
807+
parent_def_id: LocalDefId,
808+
captures: &mut FxHashMap<LocalDefId, (Span, NodeId, ParamName, LifetimeRes)>,
809+
f: impl FnOnce(&mut Self) -> T,
810+
) -> T {
811+
let lifetime_stash = std::mem::replace(
812+
&mut self.captured_lifetimes,
813+
Some(LifetimeCaptureContext {
814+
parent_def_id,
815+
captures: std::mem::take(captures),
816+
binders_to_ignore: Default::default(),
817+
}),
818+
);
819+
820+
let ret = f(self);
821+
822+
let ctxt = std::mem::replace(&mut self.captured_lifetimes, lifetime_stash).unwrap();
823+
*captures = ctxt.captures;
824+
825+
ret
826+
}
827+
828+
/// Register a binder to be ignored for lifetime capture.
829+
#[tracing::instrument(level = "debug", skip(self, f))]
830+
#[inline]
831+
fn with_lifetime_binder<T>(&mut self, binder: NodeId, f: impl FnOnce(&mut Self) -> T) -> T {
832+
if let Some(ctxt) = &mut self.captured_lifetimes {
833+
ctxt.binders_to_ignore.insert(binder);
834+
}
835+
let ret = f(self);
836+
if let Some(ctxt) = &mut self.captured_lifetimes {
837+
ctxt.binders_to_ignore.remove(&binder);
838+
}
839+
ret
840+
}
841+
793842
fn with_dyn_type_scope<T>(&mut self, in_scope: bool, f: impl FnOnce(&mut Self) -> T) -> T {
794843
let was_in_dyn_type = self.is_in_dyn_type;
795844
self.is_in_dyn_type = in_scope;
@@ -1197,25 +1246,18 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
11971246
hir::TyKind::Rptr(lifetime, self.lower_mt(mt, itctx))
11981247
}
11991248
TyKind::BareFn(ref f) => self.with_in_scope_lifetime_defs(&f.generic_params, |this| {
1200-
if let Some((_, _, binders)) = &mut this.captured_lifetimes {
1201-
binders.insert(t.id);
1202-
}
1203-
1204-
let ret = hir::TyKind::BareFn(this.arena.alloc(hir::BareFnTy {
1205-
generic_params: this.lower_generic_params(
1206-
&f.generic_params,
1207-
ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
1208-
),
1209-
unsafety: this.lower_unsafety(f.unsafety),
1210-
abi: this.lower_extern(f.ext),
1211-
decl: this.lower_fn_decl(&f.decl, None, FnDeclKind::Pointer, None),
1212-
param_names: this.lower_fn_params_to_names(&f.decl),
1213-
}));
1214-
1215-
if let Some((_, _, binders)) = &mut this.captured_lifetimes {
1216-
binders.remove(&t.id);
1217-
}
1218-
ret
1249+
this.with_lifetime_binder(t.id, |this| {
1250+
hir::TyKind::BareFn(this.arena.alloc(hir::BareFnTy {
1251+
generic_params: this.lower_generic_params(
1252+
&f.generic_params,
1253+
ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
1254+
),
1255+
unsafety: this.lower_unsafety(f.unsafety),
1256+
abi: this.lower_extern(f.ext),
1257+
decl: this.lower_fn_decl(&f.decl, None, FnDeclKind::Pointer, None),
1258+
param_names: this.lower_fn_params_to_names(&f.decl),
1259+
}))
1260+
})
12191261
}),
12201262
TyKind::Never => hir::TyKind::Never,
12211263
TyKind::Tup(ref tys) => {
@@ -1366,15 +1408,15 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
13661408

13671409
let mut collected_lifetimes = FxHashMap::default();
13681410
self.with_hir_id_owner(opaque_ty_node_id, |lctx| {
1369-
let capture_framework = if origin == hir::OpaqueTyOrigin::TyAlias {
1370-
None
1411+
let hir_bounds = if origin == hir::OpaqueTyOrigin::TyAlias {
1412+
lower_bounds(lctx)
13711413
} else {
1372-
Some((opaque_ty_def_id, FxHashMap::default(), FxHashSet::default()))
1414+
lctx.while_capturing_lifetimes(
1415+
opaque_ty_def_id,
1416+
&mut collected_lifetimes,
1417+
lower_bounds,
1418+
)
13731419
};
1374-
let lifetime_stash = std::mem::replace(&mut lctx.captured_lifetimes, capture_framework);
1375-
let hir_bounds = lower_bounds(lctx);
1376-
collected_lifetimes = std::mem::replace(&mut lctx.captured_lifetimes, lifetime_stash)
1377-
.map_or_else(FxHashMap::default, |c| c.1);
13781420
debug!(?collected_lifetimes);
13791421

13801422
let lifetime_defs = lctx.arena.alloc_from_iter(collected_lifetimes.iter().map(
@@ -1716,24 +1758,20 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
17161758
debug!(?captures);
17171759

17181760
self.with_hir_id_owner(opaque_ty_node_id, |this| {
1719-
let lifetime_stash = std::mem::replace(
1720-
&mut this.captured_lifetimes,
1721-
Some((opaque_ty_def_id, std::mem::take(&mut captures), FxHashSet::default())),
1722-
);
17231761
debug!("lower_async_fn_ret_ty: lifetimes_to_define={:#?}", this.lifetimes_to_define);
1724-
1725-
// We have to be careful to get elision right here. The
1726-
// idea is that we create a lifetime parameter for each
1727-
// lifetime in the return type. So, given a return type
1728-
// like `async fn foo(..) -> &[&u32]`, we lower to `impl
1729-
// Future<Output = &'1 [ &'2 u32 ]>`.
1730-
//
1731-
// Then, we will create `fn foo(..) -> Foo<'_, '_>`, and
1732-
// hence the elision takes place at the fn site.
17331762
let future_bound =
1734-
this.lower_async_fn_output_type_to_future_bound(output, fn_def_id, span);
1763+
this.while_capturing_lifetimes(opaque_ty_def_id, &mut captures, |this| {
1764+
// We have to be careful to get elision right here. The
1765+
// idea is that we create a lifetime parameter for each
1766+
// lifetime in the return type. So, given a return type
1767+
// like `async fn foo(..) -> &[&u32]`, we lower to `impl
1768+
// Future<Output = &'1 [ &'2 u32 ]>`.
1769+
//
1770+
// Then, we will create `fn foo(..) -> Foo<'_, '_>`, and
1771+
// hence the elision takes place at the fn site.
1772+
this.lower_async_fn_output_type_to_future_bound(output, fn_def_id, span)
1773+
});
17351774
debug!("lower_async_fn_ret_ty: future_bound={:#?}", future_bound);
1736-
captures = std::mem::replace(&mut this.captured_lifetimes, lifetime_stash).unwrap().1;
17371775
debug!("lower_async_fn_ret_ty: captures={:#?}", captures);
17381776

17391777
let generic_params =
@@ -1882,22 +1920,23 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
18821920
LifetimeRes::Param { param, binder } => {
18831921
debug_assert_ne!(ident.name, kw::UnderscoreLifetime);
18841922
let p_name = ParamName::Plain(ident);
1885-
if let Some((parent_def_id, captures, binders)) = &mut self.captured_lifetimes {
1923+
if let Some(LifetimeCaptureContext { parent_def_id, captures, binders_to_ignore }) =
1924+
&mut self.captured_lifetimes
1925+
&& !binders_to_ignore.contains(&binder)
1926+
{
18861927
match captures.entry(param) {
18871928
Entry::Occupied(_) => {}
18881929
Entry::Vacant(v) => {
1889-
if !binders.contains(&binder) {
1890-
let p_id = self.resolver.next_node_id();
1891-
self.resolver.create_def(
1892-
*parent_def_id,
1893-
p_id,
1894-
DefPathData::LifetimeNs(p_name.ident().name),
1895-
ExpnId::root(),
1896-
span.with_parent(None),
1897-
);
1898-
1899-
v.insert((span, p_id, p_name, res));
1900-
}
1930+
let p_id = self.resolver.next_node_id();
1931+
self.resolver.create_def(
1932+
*parent_def_id,
1933+
p_id,
1934+
DefPathData::LifetimeNs(p_name.ident().name),
1935+
ExpnId::root(),
1936+
span.with_parent(None),
1937+
);
1938+
1939+
v.insert((span, p_id, p_name, res));
19011940
}
19021941
}
19031942
}
@@ -1908,24 +1947,25 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
19081947
// Only items are allowed to introduce fresh lifetimes,
19091948
// so we know `binder` has a `LocalDefId`.
19101949
let binder_def_id = self.resolver.local_def_id(binder);
1911-
if let Some((parent_def_id, captures, binders)) = &mut self.captured_lifetimes {
1950+
if let Some(LifetimeCaptureContext { parent_def_id, captures, binders_to_ignore }) =
1951+
&mut self.captured_lifetimes
1952+
&& !binders_to_ignore.contains(&binder)
1953+
{
19121954
match captures.entry(param) {
19131955
Entry::Occupied(o) => param = self.resolver.local_def_id(o.get().1),
19141956
Entry::Vacant(v) => {
1915-
if !binders.contains(&binder) {
1916-
let p_id = self.resolver.next_node_id();
1917-
let p_def_id = self.resolver.create_def(
1918-
*parent_def_id,
1919-
p_id,
1920-
DefPathData::LifetimeNs(kw::UnderscoreLifetime),
1921-
ExpnId::root(),
1922-
span.with_parent(None),
1923-
);
1924-
1925-
let p_name = ParamName::Fresh(param);
1926-
v.insert((span, p_id, p_name, res));
1927-
param = p_def_id;
1928-
}
1957+
let p_id = self.resolver.next_node_id();
1958+
let p_def_id = self.resolver.create_def(
1959+
*parent_def_id,
1960+
p_id,
1961+
DefPathData::LifetimeNs(kw::UnderscoreLifetime),
1962+
ExpnId::root(),
1963+
span.with_parent(None),
1964+
);
1965+
1966+
let p_name = ParamName::Fresh(param);
1967+
v.insert((span, p_id, p_name, res));
1968+
param = p_def_id;
19291969
}
19301970
}
19311971
} else if let Some(introducer) = introducer {
@@ -1948,21 +1988,23 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
19481988
} else {
19491989
hir::LifetimeName::Underscore
19501990
};
1951-
match &mut self.captured_lifetimes {
1952-
Some((parent_def_id, captures, binders)) if !binders.contains(&binder) => {
1953-
let p_id = self.resolver.next_node_id();
1954-
let p_def_id = self.resolver.create_def(
1955-
*parent_def_id,
1956-
p_id,
1957-
DefPathData::LifetimeNs(kw::UnderscoreLifetime),
1958-
ExpnId::root(),
1959-
span.with_parent(None),
1960-
);
1961-
let p_name = ParamName::Fresh(p_def_id);
1962-
captures.insert(p_def_id, (span, p_id, p_name, res));
1963-
hir::LifetimeName::Param(p_name)
1964-
}
1965-
_ => l_name,
1991+
if let Some(LifetimeCaptureContext { parent_def_id, captures, binders_to_ignore }) =
1992+
&mut self.captured_lifetimes
1993+
&& !binders_to_ignore.contains(&binder)
1994+
{
1995+
let p_id = self.resolver.next_node_id();
1996+
let p_def_id = self.resolver.create_def(
1997+
*parent_def_id,
1998+
p_id,
1999+
DefPathData::LifetimeNs(kw::UnderscoreLifetime),
2000+
ExpnId::root(),
2001+
span.with_parent(None),
2002+
);
2003+
let p_name = ParamName::Fresh(p_def_id);
2004+
captures.insert(p_def_id, (span, p_id, p_name, res));
2005+
hir::LifetimeName::Param(p_name)
2006+
} else {
2007+
l_name
19662008
}
19672009
}
19682010
LifetimeRes::Static => hir::LifetimeName::Static,
@@ -2069,16 +2111,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
20692111
self.lower_generic_params(&p.bound_generic_params, itctx.reborrow());
20702112

20712113
let trait_ref = self.with_in_scope_lifetime_defs(&p.bound_generic_params, |this| {
2072-
if let Some((_, _, binders)) = &mut this.captured_lifetimes {
2073-
binders.insert(p.trait_ref.ref_id);
2074-
}
2075-
2076-
let trait_ref = this.lower_trait_ref(&p.trait_ref, itctx.reborrow());
2077-
2078-
if let Some((_, _, binders)) = &mut this.captured_lifetimes {
2079-
binders.remove(&p.trait_ref.ref_id);
2080-
}
2081-
trait_ref
2114+
this.with_lifetime_binder(p.trait_ref.ref_id, |this| {
2115+
this.lower_trait_ref(&p.trait_ref, itctx.reborrow())
2116+
})
20822117
});
20832118

20842119
hir::PolyTraitRef { bound_generic_params, trait_ref, span: self.lower_span(p.span) }

0 commit comments

Comments
 (0)