Skip to content

Commit 77cc69c

Browse files
Make sure that outer opaques capture inner opaques's lifetimes even with precise capturing syntax
1 parent a0c2aba commit 77cc69c

File tree

5 files changed

+104
-70
lines changed

5 files changed

+104
-70
lines changed

compiler/rustc_ast_lowering/src/item.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1574,7 +1574,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
15741574
.collect();
15751575

15761576
// Introduce extra lifetimes if late resolution tells us to.
1577-
let extra_lifetimes = self.resolver.take_extra_lifetime_params(parent_node_id);
1577+
let extra_lifetimes = self.resolver.extra_lifetime_params(parent_node_id);
15781578
params.extend(extra_lifetimes.into_iter().filter_map(|(ident, node_id, res)| {
15791579
self.lifetime_res_to_generic_param(
15801580
ident,

compiler/rustc_ast_lowering/src/lib.rs

+20-59
Original file line numberDiff line numberDiff line change
@@ -268,8 +268,8 @@ impl ResolverAstLowering {
268268
///
269269
/// The extra lifetimes that appear from the parenthesized `Fn`-trait desugaring
270270
/// should appear at the enclosing `PolyTraitRef`.
271-
fn take_extra_lifetime_params(&mut self, id: NodeId) -> Vec<(Ident, NodeId, LifetimeRes)> {
272-
self.extra_lifetime_params_map.remove(&id).unwrap_or_default()
271+
fn extra_lifetime_params(&mut self, id: NodeId) -> Vec<(Ident, NodeId, LifetimeRes)> {
272+
self.extra_lifetime_params_map.get(&id).cloned().unwrap_or_default()
273273
}
274274
}
275275

@@ -885,7 +885,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
885885
let mut generic_params: Vec<_> = self
886886
.lower_generic_params_mut(generic_params, hir::GenericParamSource::Binder)
887887
.collect();
888-
let extra_lifetimes = self.resolver.take_extra_lifetime_params(binder);
888+
let extra_lifetimes = self.resolver.extra_lifetime_params(binder);
889889
debug!(?extra_lifetimes);
890890
generic_params.extend(extra_lifetimes.into_iter().filter_map(|(ident, node_id, res)| {
891891
self.lifetime_res_to_generic_param(ident, node_id, res, hir::GenericParamSource::Binder)
@@ -1495,62 +1495,23 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
14951495
// frequently opened issues show.
14961496
let opaque_ty_span = self.mark_span_with_reason(DesugaringKind::OpaqueTy, span, None);
14971497

1498-
let captured_lifetimes_to_duplicate = if let Some(args) =
1499-
// We only look for one `use<...>` syntax since we syntactially reject more than one.
1500-
bounds.iter().find_map(
1501-
|bound| match bound {
1502-
ast::GenericBound::Use(a, _) => Some(a),
1503-
_ => None,
1504-
},
1505-
) {
1506-
// We'll actually validate these later on; all we need is the list of
1507-
// lifetimes to duplicate during this portion of lowering.
1508-
args.iter()
1509-
.filter_map(|arg| match arg {
1510-
PreciseCapturingArg::Lifetime(lt) => Some(*lt),
1511-
PreciseCapturingArg::Arg(..) => None,
1512-
})
1513-
// Add in all the lifetimes mentioned in the bounds. We will error
1514-
// them out later, but capturing them here is important to make sure
1515-
// they actually get resolved in resolve_bound_vars.
1516-
.chain(lifetime_collector::lifetimes_in_bounds(self.resolver, bounds))
1517-
.collect()
1518-
} else {
1519-
match origin {
1520-
hir::OpaqueTyOrigin::TyAlias { .. } => {
1521-
// type alias impl trait and associated type position impl trait were
1522-
// decided to capture all in-scope lifetimes, which we collect for
1523-
// all opaques during resolution.
1524-
self.resolver
1525-
.take_extra_lifetime_params(opaque_ty_node_id)
1526-
.into_iter()
1527-
.map(|(ident, id, _)| Lifetime { id, ident })
1528-
.collect()
1529-
}
1530-
hir::OpaqueTyOrigin::FnReturn { in_trait_or_impl, .. } => {
1531-
if in_trait_or_impl.is_some()
1532-
|| self.tcx.features().lifetime_capture_rules_2024
1533-
|| span.at_least_rust_2024()
1534-
{
1535-
// return-position impl trait in trait was decided to capture all
1536-
// in-scope lifetimes, which we collect for all opaques during resolution.
1537-
self.resolver
1538-
.take_extra_lifetime_params(opaque_ty_node_id)
1539-
.into_iter()
1540-
.map(|(ident, id, _)| Lifetime { id, ident })
1541-
.collect()
1542-
} else {
1543-
// in fn return position, like the `fn test<'a>() -> impl Debug + 'a`
1544-
// example, we only need to duplicate lifetimes that appear in the
1545-
// bounds, since those are the only ones that are captured by the opaque.
1546-
lifetime_collector::lifetimes_in_bounds(self.resolver, bounds)
1547-
}
1548-
}
1549-
hir::OpaqueTyOrigin::AsyncFn { .. } => {
1550-
unreachable!("should be using `lower_async_fn_ret_ty`")
1551-
}
1552-
}
1498+
// Whether this opaque always captures lifetimes in scope.
1499+
// Right now, this is all RPITIT and TAITs, and when `lifetime_capture_rules_2024`
1500+
// is enabled. We don't check the span of the edition, since this is done
1501+
// on a per-opaque basis to account for nested opaques.
1502+
let always_capture_in_scope = match origin {
1503+
_ if self.tcx.features().lifetime_capture_rules_2024 => true,
1504+
hir::OpaqueTyOrigin::FnReturn { in_trait_or_impl, .. }
1505+
| hir::OpaqueTyOrigin::AsyncFn { in_trait_or_impl, .. } => in_trait_or_impl.is_some(),
1506+
hir::OpaqueTyOrigin::TyAlias { .. } => true,
15531507
};
1508+
let captured_lifetimes_to_duplicate = lifetime_collector::lifetimes_for_opaque(
1509+
self.resolver,
1510+
always_capture_in_scope,
1511+
opaque_ty_node_id,
1512+
bounds,
1513+
span,
1514+
);
15541515
debug!(?captured_lifetimes_to_duplicate);
15551516

15561517
// Feature gate for RPITIT + use<..>
@@ -1920,7 +1881,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
19201881

19211882
let captured_lifetimes = self
19221883
.resolver
1923-
.take_extra_lifetime_params(opaque_ty_node_id)
1884+
.extra_lifetime_params(opaque_ty_node_id)
19241885
.into_iter()
19251886
.map(|(ident, id, _)| Lifetime { id, ident })
19261887
.collect();

compiler/rustc_ast_lowering/src/lifetime_collector.rs

+46-10
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
use rustc_ast::visit::{self, BoundKind, LifetimeCtxt, Visitor};
2-
use rustc_ast::{GenericBounds, Lifetime, NodeId, PathSegment, PolyTraitRef, Ty, TyKind};
2+
use rustc_ast::{
3+
GenericBound, GenericBounds, Lifetime, NodeId, PathSegment, PolyTraitRef, Ty, TyKind,
4+
};
35
use rustc_data_structures::fx::FxIndexSet;
46
use rustc_hir::def::{DefKind, LifetimeRes, Res};
57
use rustc_middle::span_bug;
@@ -10,14 +12,44 @@ use rustc_span::symbol::{Ident, kw};
1012
use super::ResolverAstLoweringExt;
1113

1214
struct LifetimeCollectVisitor<'ast> {
13-
resolver: &'ast ResolverAstLowering,
15+
resolver: &'ast mut ResolverAstLowering,
16+
always_capture_in_scope: bool,
1417
current_binders: Vec<NodeId>,
1518
collected_lifetimes: FxIndexSet<Lifetime>,
1619
}
1720

1821
impl<'ast> LifetimeCollectVisitor<'ast> {
19-
fn new(resolver: &'ast ResolverAstLowering) -> Self {
20-
Self { resolver, current_binders: Vec::new(), collected_lifetimes: FxIndexSet::default() }
22+
fn new(resolver: &'ast mut ResolverAstLowering, always_capture_in_scope: bool) -> Self {
23+
Self {
24+
resolver,
25+
always_capture_in_scope,
26+
current_binders: Vec::new(),
27+
collected_lifetimes: FxIndexSet::default(),
28+
}
29+
}
30+
31+
fn visit_opaque(&mut self, opaque_ty_node_id: NodeId, bounds: &'ast GenericBounds, span: Span) {
32+
// If we're edition 2024 or within a TAIT or RPITIT, *and* there is no
33+
// `use<>` statement to override the default capture behavior, then
34+
// capture all of the in-scope lifetimes.
35+
if (self.always_capture_in_scope || span.at_least_rust_2024())
36+
&& !bounds.iter().any(|bound| match bound {
37+
GenericBound::Use(..) => true,
38+
_ => false,
39+
})
40+
{
41+
for (ident, id, _) in self.resolver.extra_lifetime_params(opaque_ty_node_id) {
42+
self.record_lifetime_use(Lifetime { id, ident });
43+
}
44+
}
45+
46+
// We also recurse on the bounds to make sure we capture all the lifetimes
47+
// mentioned in the bounds. These may disagree with the `use<>` list, in which
48+
// case we will error on these later. We will also recurse to visit any
49+
// nested opaques, which may *implicitly* capture lifetimes.
50+
for bound in bounds {
51+
self.visit_param_bound(bound, BoundKind::Bound);
52+
}
2153
}
2254

2355
fn record_lifetime_use(&mut self, lifetime: Lifetime) {
@@ -99,20 +131,24 @@ impl<'ast> Visitor<'ast> for LifetimeCollectVisitor<'ast> {
99131
self.record_elided_anchor(t.id, t.span);
100132
visit::walk_ty(self, t);
101133
}
134+
TyKind::ImplTrait(opaque_ty_node_id, bounds) => {
135+
self.visit_opaque(*opaque_ty_node_id, bounds, t.span)
136+
}
102137
_ => {
103138
visit::walk_ty(self, t);
104139
}
105140
}
106141
}
107142
}
108143

109-
pub(crate) fn lifetimes_in_bounds(
110-
resolver: &ResolverAstLowering,
144+
pub(crate) fn lifetimes_for_opaque(
145+
resolver: &mut ResolverAstLowering,
146+
always_capture_in_scope: bool,
147+
opaque_ty_node_id: NodeId,
111148
bounds: &GenericBounds,
149+
span: Span,
112150
) -> FxIndexSet<Lifetime> {
113-
let mut visitor = LifetimeCollectVisitor::new(resolver);
114-
for bound in bounds {
115-
visitor.visit_param_bound(bound, BoundKind::Bound);
116-
}
151+
let mut visitor = LifetimeCollectVisitor::new(resolver, always_capture_in_scope);
152+
visitor.visit_opaque(opaque_ty_node_id, bounds, span);
117153
visitor.collected_lifetimes
118154
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
//@ edition: 2024
2+
//@ compile-flags: -Zunstable-options
3+
4+
#![feature(rustc_attrs)]
5+
#![feature(type_alias_impl_trait)]
6+
#![rustc_variance_of_opaques]
7+
8+
fn foo(x: &()) -> impl IntoIterator<Item = impl Sized> + use<> {
9+
//~^ ERROR ['_: o]
10+
//~| ERROR ['_: o]
11+
//~| ERROR `impl Trait` captures lifetime parameter
12+
[*x]
13+
}
14+
15+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
error: `impl Trait` captures lifetime parameter, but it is not mentioned in `use<...>` precise captures list
2+
--> $DIR/capturing-implicit.rs:8:11
3+
|
4+
LL | fn foo(x: &()) -> impl IntoIterator<Item = impl Sized> + use<> {
5+
| ^ -------------------------------------------- lifetime captured due to being mentioned in the bounds of the `impl Trait`
6+
| |
7+
| this lifetime parameter is captured
8+
9+
error: ['_: o]
10+
--> $DIR/capturing-implicit.rs:8:19
11+
|
12+
LL | fn foo(x: &()) -> impl IntoIterator<Item = impl Sized> + use<> {
13+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
14+
15+
error: ['_: o]
16+
--> $DIR/capturing-implicit.rs:8:44
17+
|
18+
LL | fn foo(x: &()) -> impl IntoIterator<Item = impl Sized> + use<> {
19+
| ^^^^^^^^^^
20+
21+
error: aborting due to 3 previous errors
22+

0 commit comments

Comments
 (0)