Skip to content

Commit c002c0a

Browse files
committed
Auto merge of #123468 - compiler-errors:precise-capturing, r=oli-obk
Implement syntax for `impl Trait` to specify its captures explicitly (`feature(precise_capturing)`) Implements `impl use<'a, 'b, T, U> Sized` syntax that allows users to explicitly list the captured parameters for an opaque, rather than inferring it from the opaque's bounds (or capturing *all* lifetimes under 2024-edition capture rules). This allows us to exclude some implicit captures, so this syntax may be used as a migration strategy for changes due to #117587. We represent this list of captured params as `PreciseCapturingArg` in AST and HIR, resolving them between `rustc_resolve` and `resolve_bound_vars`. Later on, we validate that the opaques only capture the parameters in this list. We artificially limit the feature to *require* mentioning all type and const parameters, since we don't currently have support for non-lifetime bivariant generics. This can be relaxed in the future. We also may need to limit this to require naming *all* lifetime parameters for RPITIT, since GATs have no variance. I have to investigate this. This can also be relaxed in the future. r? `@oli-obk` Tracking issue: - #123432
2 parents 63f70b3 + 8ddd280 commit c002c0a

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+1033
-71
lines changed

compiler/rustc_ast/src/ast.rs

+10-2
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ impl fmt::Debug for Label {
6363

6464
/// A "Lifetime" is an annotation of the scope in which variable
6565
/// can be used, e.g. `'a` in `&'a i32`.
66-
#[derive(Clone, Encodable, Decodable, Copy, PartialEq, Eq)]
66+
#[derive(Clone, Encodable, Decodable, Copy, PartialEq, Eq, Hash)]
6767
pub struct Lifetime {
6868
pub id: NodeId,
6969
pub ident: Ident,
@@ -2132,7 +2132,7 @@ pub enum TyKind {
21322132
/// The `NodeId` exists to prevent lowering from having to
21332133
/// generate `NodeId`s on the fly, which would complicate
21342134
/// the generation of opaque `type Foo = impl Trait` items significantly.
2135-
ImplTrait(NodeId, GenericBounds),
2135+
ImplTrait(NodeId, GenericBounds, Option<P<(ThinVec<PreciseCapturingArg>, Span)>>),
21362136
/// No-op; kept solely so that we can pretty-print faithfully.
21372137
Paren(P<Ty>),
21382138
/// Unused for now.
@@ -2188,6 +2188,14 @@ pub enum TraitObjectSyntax {
21882188
None,
21892189
}
21902190

2191+
#[derive(Clone, Encodable, Decodable, Debug)]
2192+
pub enum PreciseCapturingArg {
2193+
/// Lifetime parameter
2194+
Lifetime(Lifetime),
2195+
/// Type or const parameter
2196+
Arg(Path, NodeId),
2197+
}
2198+
21912199
/// Inline assembly operand explicit register or register class.
21922200
///
21932201
/// E.g., `"eax"` as in `asm!("mov eax, 2", out("eax") result)`.

compiler/rustc_ast/src/mut_visit.rs

+22-1
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,10 @@ pub trait MutVisitor: Sized {
259259
noop_visit_param_bound(tpb, self);
260260
}
261261

262+
fn visit_precise_capturing_arg(&mut self, arg: &mut PreciseCapturingArg) {
263+
noop_visit_precise_capturing_arg(arg, self);
264+
}
265+
262266
fn visit_mt(&mut self, mt: &mut MutTy) {
263267
noop_visit_mt(mt, self);
264268
}
@@ -518,9 +522,14 @@ pub fn noop_visit_ty<T: MutVisitor>(ty: &mut P<Ty>, vis: &mut T) {
518522
TyKind::TraitObject(bounds, _syntax) => {
519523
visit_vec(bounds, |bound| vis.visit_param_bound(bound))
520524
}
521-
TyKind::ImplTrait(id, bounds) => {
525+
TyKind::ImplTrait(id, bounds, precise_capturing) => {
522526
vis.visit_id(id);
523527
visit_vec(bounds, |bound| vis.visit_param_bound(bound));
528+
if let Some((precise_capturing, _span)) = precise_capturing.as_deref_mut() {
529+
for arg in precise_capturing {
530+
vis.visit_precise_capturing_arg(arg);
531+
}
532+
}
524533
}
525534
TyKind::MacCall(mac) => vis.visit_mac_call(mac),
526535
TyKind::AnonStruct(id, fields) | TyKind::AnonUnion(id, fields) => {
@@ -914,6 +923,18 @@ pub fn noop_visit_param_bound<T: MutVisitor>(pb: &mut GenericBound, vis: &mut T)
914923
}
915924
}
916925

926+
pub fn noop_visit_precise_capturing_arg<T: MutVisitor>(arg: &mut PreciseCapturingArg, vis: &mut T) {
927+
match arg {
928+
PreciseCapturingArg::Lifetime(lt) => {
929+
vis.visit_lifetime(lt);
930+
}
931+
PreciseCapturingArg::Arg(path, id) => {
932+
vis.visit_path(path);
933+
vis.visit_id(id);
934+
}
935+
}
936+
}
937+
917938
pub fn noop_flat_map_generic_param<T: MutVisitor>(
918939
mut param: GenericParam,
919940
vis: &mut T,

compiler/rustc_ast/src/visit.rs

+23-1
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,9 @@ pub trait Visitor<'ast>: Sized {
184184
fn visit_param_bound(&mut self, bounds: &'ast GenericBound, _ctxt: BoundKind) -> Self::Result {
185185
walk_param_bound(self, bounds)
186186
}
187+
fn visit_precise_capturing_arg(&mut self, arg: &'ast PreciseCapturingArg) {
188+
walk_precise_capturing_arg(self, arg);
189+
}
187190
fn visit_poly_trait_ref(&mut self, t: &'ast PolyTraitRef) -> Self::Result {
188191
walk_poly_trait_ref(self, t)
189192
}
@@ -457,8 +460,13 @@ pub fn walk_ty<'a, V: Visitor<'a>>(visitor: &mut V, typ: &'a Ty) -> V::Result {
457460
TyKind::TraitObject(bounds, ..) => {
458461
walk_list!(visitor, visit_param_bound, bounds, BoundKind::TraitObject);
459462
}
460-
TyKind::ImplTrait(_, bounds) => {
463+
TyKind::ImplTrait(_, bounds, precise_capturing) => {
461464
walk_list!(visitor, visit_param_bound, bounds, BoundKind::Impl);
465+
if let Some((precise_capturing, _span)) = precise_capturing.as_deref() {
466+
for arg in precise_capturing {
467+
try_visit!(visitor.visit_precise_capturing_arg(arg));
468+
}
469+
}
462470
}
463471
TyKind::Typeof(expression) => try_visit!(visitor.visit_anon_const(expression)),
464472
TyKind::Infer | TyKind::ImplicitSelf | TyKind::Dummy | TyKind::Err(_) => {}
@@ -637,6 +645,20 @@ pub fn walk_param_bound<'a, V: Visitor<'a>>(visitor: &mut V, bound: &'a GenericB
637645
}
638646
}
639647

648+
pub fn walk_precise_capturing_arg<'a, V: Visitor<'a>>(
649+
visitor: &mut V,
650+
arg: &'a PreciseCapturingArg,
651+
) {
652+
match arg {
653+
PreciseCapturingArg::Lifetime(lt) => {
654+
visitor.visit_lifetime(lt, LifetimeCtxt::GenericArg);
655+
}
656+
PreciseCapturingArg::Arg(path, id) => {
657+
visitor.visit_path(path, *id);
658+
}
659+
}
660+
}
661+
640662
pub fn walk_generic_param<'a, V: Visitor<'a>>(
641663
visitor: &mut V,
642664
param: &'a GenericParam,

compiler/rustc_ast_lowering/messages.ftl

+2
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,8 @@ ast_lowering_never_pattern_with_guard =
127127
a guard on a never pattern will never be run
128128
.suggestion = remove this guard
129129
130+
ast_lowering_no_precise_captures_on_apit = `use<...>` precise capturing syntax not allowed on argument-position `impl Trait`
131+
130132
ast_lowering_previously_used_here = previously used here
131133
132134
ast_lowering_register1 = register `{$reg1_name}`

compiler/rustc_ast_lowering/src/errors.rs

+7
Original file line numberDiff line numberDiff line change
@@ -414,3 +414,10 @@ pub(crate) struct AsyncBoundOnlyForFnTraits {
414414
#[primary_span]
415415
pub span: Span,
416416
}
417+
418+
#[derive(Diagnostic)]
419+
#[diag(ast_lowering_no_precise_captures_on_apit)]
420+
pub(crate) struct NoPreciseCapturesOnApit {
421+
#[primary_span]
422+
pub span: Span,
423+
}

compiler/rustc_ast_lowering/src/index.rs

+17
Original file line numberDiff line numberDiff line change
@@ -385,4 +385,21 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> {
385385
fn visit_pattern_type_pattern(&mut self, p: &'hir hir::Pat<'hir>) {
386386
self.visit_pat(p)
387387
}
388+
389+
fn visit_precise_capturing_arg(
390+
&mut self,
391+
arg: &'hir PreciseCapturingArg<'hir>,
392+
) -> Self::Result {
393+
match arg {
394+
PreciseCapturingArg::Lifetime(_) => {
395+
// This is represented as a `Node::Lifetime`, intravisit will get to it below.
396+
}
397+
PreciseCapturingArg::Param(param) => self.insert(
398+
param.ident.span,
399+
param.hir_id,
400+
Node::PreciseCapturingNonLifetimeArg(param),
401+
),
402+
}
403+
intravisit::walk_precise_capturing_arg(self, arg);
404+
}
388405
}

compiler/rustc_ast_lowering/src/lib.rs

+97-39
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ use rustc_ast::{self as ast, *};
4848
use rustc_ast_pretty::pprust;
4949
use rustc_data_structures::captures::Captures;
5050
use rustc_data_structures::fingerprint::Fingerprint;
51+
use rustc_data_structures::fx::FxIndexSet;
5152
use rustc_data_structures::sorted_map::SortedMap;
5253
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
5354
use rustc_data_structures::sync::Lrc;
@@ -1398,7 +1399,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
13981399
});
13991400
hir::TyKind::TraitObject(bounds, lifetime_bound, *kind)
14001401
}
1401-
TyKind::ImplTrait(def_node_id, bounds) => {
1402+
TyKind::ImplTrait(def_node_id, bounds, precise_capturing) => {
14021403
let span = t.span;
14031404
match itctx {
14041405
ImplTraitContext::OpaqueTy { origin, fn_kind } => self.lower_opaque_impl_trait(
@@ -1408,8 +1409,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
14081409
bounds,
14091410
fn_kind,
14101411
itctx,
1412+
precise_capturing.as_deref().map(|(args, _)| args.as_slice()),
14111413
),
14121414
ImplTraitContext::Universal => {
1415+
if let Some(&(_, span)) = precise_capturing.as_deref() {
1416+
self.tcx.dcx().emit_err(errors::NoPreciseCapturesOnApit { span });
1417+
};
14131418
let span = t.span;
14141419

14151420
// HACK: pprust breaks strings with newlines when the type
@@ -1520,6 +1525,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
15201525
bounds: &GenericBounds,
15211526
fn_kind: Option<FnDeclKind>,
15221527
itctx: ImplTraitContext,
1528+
precise_capturing_args: Option<&[PreciseCapturingArg]>,
15231529
) -> hir::TyKind<'hir> {
15241530
// Make sure we know that some funky desugaring has been going on here.
15251531
// This is a first: there is code in other places like for loop
@@ -1528,42 +1534,59 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
15281534
// frequently opened issues show.
15291535
let opaque_ty_span = self.mark_span_with_reason(DesugaringKind::OpaqueTy, span, None);
15301536

1531-
let captured_lifetimes_to_duplicate = match origin {
1532-
hir::OpaqueTyOrigin::TyAlias { .. } => {
1533-
// type alias impl trait and associated type position impl trait were
1534-
// decided to capture all in-scope lifetimes, which we collect for
1535-
// all opaques during resolution.
1536-
self.resolver
1537-
.take_extra_lifetime_params(opaque_ty_node_id)
1538-
.into_iter()
1539-
.map(|(ident, id, _)| Lifetime { id, ident })
1537+
let captured_lifetimes_to_duplicate =
1538+
if let Some(precise_capturing) = precise_capturing_args {
1539+
// We'll actually validate these later on; all we need is the list of
1540+
// lifetimes to duplicate during this portion of lowering.
1541+
precise_capturing
1542+
.iter()
1543+
.filter_map(|arg| match arg {
1544+
PreciseCapturingArg::Lifetime(lt) => Some(*lt),
1545+
PreciseCapturingArg::Arg(..) => None,
1546+
})
1547+
// Add in all the lifetimes mentioned in the bounds. We will error
1548+
// them out later, but capturing them here is important to make sure
1549+
// they actually get resolved in resolve_bound_vars.
1550+
.chain(lifetime_collector::lifetimes_in_bounds(self.resolver, bounds))
15401551
.collect()
1541-
}
1542-
hir::OpaqueTyOrigin::FnReturn(..) => {
1543-
if matches!(
1544-
fn_kind.expect("expected RPITs to be lowered with a FnKind"),
1545-
FnDeclKind::Impl | FnDeclKind::Trait
1546-
) || self.tcx.features().lifetime_capture_rules_2024
1547-
|| span.at_least_rust_2024()
1548-
{
1549-
// return-position impl trait in trait was decided to capture all
1550-
// in-scope lifetimes, which we collect for all opaques during resolution.
1551-
self.resolver
1552-
.take_extra_lifetime_params(opaque_ty_node_id)
1553-
.into_iter()
1554-
.map(|(ident, id, _)| Lifetime { id, ident })
1555-
.collect()
1556-
} else {
1557-
// in fn return position, like the `fn test<'a>() -> impl Debug + 'a`
1558-
// example, we only need to duplicate lifetimes that appear in the
1559-
// bounds, since those are the only ones that are captured by the opaque.
1560-
lifetime_collector::lifetimes_in_bounds(self.resolver, bounds)
1552+
} else {
1553+
match origin {
1554+
hir::OpaqueTyOrigin::TyAlias { .. } => {
1555+
// type alias impl trait and associated type position impl trait were
1556+
// decided to capture all in-scope lifetimes, which we collect for
1557+
// all opaques during resolution.
1558+
self.resolver
1559+
.take_extra_lifetime_params(opaque_ty_node_id)
1560+
.into_iter()
1561+
.map(|(ident, id, _)| Lifetime { id, ident })
1562+
.collect()
1563+
}
1564+
hir::OpaqueTyOrigin::FnReturn(..) => {
1565+
if matches!(
1566+
fn_kind.expect("expected RPITs to be lowered with a FnKind"),
1567+
FnDeclKind::Impl | FnDeclKind::Trait
1568+
) || self.tcx.features().lifetime_capture_rules_2024
1569+
|| span.at_least_rust_2024()
1570+
{
1571+
// return-position impl trait in trait was decided to capture all
1572+
// in-scope lifetimes, which we collect for all opaques during resolution.
1573+
self.resolver
1574+
.take_extra_lifetime_params(opaque_ty_node_id)
1575+
.into_iter()
1576+
.map(|(ident, id, _)| Lifetime { id, ident })
1577+
.collect()
1578+
} else {
1579+
// in fn return position, like the `fn test<'a>() -> impl Debug + 'a`
1580+
// example, we only need to duplicate lifetimes that appear in the
1581+
// bounds, since those are the only ones that are captured by the opaque.
1582+
lifetime_collector::lifetimes_in_bounds(self.resolver, bounds)
1583+
}
1584+
}
1585+
hir::OpaqueTyOrigin::AsyncFn(..) => {
1586+
unreachable!("should be using `lower_async_fn_ret_ty`")
1587+
}
15611588
}
1562-
}
1563-
hir::OpaqueTyOrigin::AsyncFn(..) => {
1564-
unreachable!("should be using `lower_async_fn_ret_ty`")
1565-
}
1566-
};
1589+
};
15671590
debug!(?captured_lifetimes_to_duplicate);
15681591

15691592
self.lower_opaque_inner(
@@ -1573,6 +1596,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
15731596
captured_lifetimes_to_duplicate,
15741597
span,
15751598
opaque_ty_span,
1599+
precise_capturing_args,
15761600
|this| this.lower_param_bounds(bounds, itctx),
15771601
)
15781602
}
@@ -1582,9 +1606,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
15821606
opaque_ty_node_id: NodeId,
15831607
origin: hir::OpaqueTyOrigin,
15841608
in_trait: bool,
1585-
captured_lifetimes_to_duplicate: Vec<Lifetime>,
1609+
captured_lifetimes_to_duplicate: FxIndexSet<Lifetime>,
15861610
span: Span,
15871611
opaque_ty_span: Span,
1612+
precise_capturing_args: Option<&[PreciseCapturingArg]>,
15881613
lower_item_bounds: impl FnOnce(&mut Self) -> &'hir [hir::GenericBound<'hir>],
15891614
) -> hir::TyKind<'hir> {
15901615
let opaque_ty_def_id = self.create_def(
@@ -1671,8 +1696,15 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
16711696
// Install the remapping from old to new (if any). This makes sure that
16721697
// any lifetimes that would have resolved to the def-id of captured
16731698
// lifetimes are remapped to the new *synthetic* lifetimes of the opaque.
1674-
let bounds = this
1675-
.with_remapping(captured_to_synthesized_mapping, |this| lower_item_bounds(this));
1699+
let (bounds, precise_capturing_args) =
1700+
this.with_remapping(captured_to_synthesized_mapping, |this| {
1701+
(
1702+
lower_item_bounds(this),
1703+
precise_capturing_args.map(|precise_capturing| {
1704+
this.lower_precise_capturing_args(precise_capturing)
1705+
}),
1706+
)
1707+
});
16761708

16771709
let generic_params =
16781710
this.arena.alloc_from_iter(synthesized_lifetime_definitions.iter().map(
@@ -1717,6 +1749,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
17171749
origin,
17181750
lifetime_mapping,
17191751
in_trait,
1752+
precise_capturing_args,
17201753
};
17211754

17221755
// Generate an `type Foo = impl Trait;` declaration.
@@ -1749,6 +1782,30 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
17491782
)
17501783
}
17511784

1785+
fn lower_precise_capturing_args(
1786+
&mut self,
1787+
precise_capturing_args: &[PreciseCapturingArg],
1788+
) -> &'hir [hir::PreciseCapturingArg<'hir>] {
1789+
self.arena.alloc_from_iter(precise_capturing_args.iter().map(|arg| match arg {
1790+
PreciseCapturingArg::Lifetime(lt) => {
1791+
hir::PreciseCapturingArg::Lifetime(self.lower_lifetime(lt))
1792+
}
1793+
PreciseCapturingArg::Arg(path, id) => {
1794+
let [segment] = path.segments.as_slice() else {
1795+
panic!();
1796+
};
1797+
let res = self.resolver.get_partial_res(*id).map_or(Res::Err, |partial_res| {
1798+
partial_res.full_res().expect("no partial res expected for precise capture arg")
1799+
});
1800+
hir::PreciseCapturingArg::Param(hir::PreciseCapturingNonLifetimeArg {
1801+
hir_id: self.lower_node_id(*id),
1802+
ident: self.lower_ident(segment.ident),
1803+
res: self.lower_res(res),
1804+
})
1805+
}
1806+
}))
1807+
}
1808+
17521809
fn lower_fn_params_to_names(&mut self, decl: &FnDecl) -> &'hir [Ident] {
17531810
self.arena.alloc_from_iter(decl.inputs.iter().map(|param| match param.pat.kind {
17541811
PatKind::Ident(_, ident, _) => self.lower_ident(ident),
@@ -1889,7 +1946,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
18891946
let opaque_ty_span =
18901947
self.mark_span_with_reason(DesugaringKind::Async, span, allowed_features);
18911948

1892-
let captured_lifetimes: Vec<_> = self
1949+
let captured_lifetimes = self
18931950
.resolver
18941951
.take_extra_lifetime_params(opaque_ty_node_id)
18951952
.into_iter()
@@ -1903,6 +1960,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
19031960
captured_lifetimes,
19041961
span,
19051962
opaque_ty_span,
1963+
None,
19061964
|this| {
19071965
let bound = this.lower_coroutine_fn_output_type_to_bound(
19081966
output,

0 commit comments

Comments
 (0)