Skip to content

Commit 4f2af12

Browse files
authored
Rollup merge of rust-lang#131033 - compiler-errors:precise-capturing-in-traits, r=spastorino
Precise capturing in traits This PR begins to implement `feature(precise_capturing_in_traits)`, which enables using the `impl Trait + use<..>` syntax for RPITITs. It implements this by giving the desugared GATs variance, and representing the uncaptured lifetimes as bivariant, like how opaque captures work. Right now, I've left out implementing a necessary extension to the `refining_impl_trait` lint, and also I've made it so that all RPITITs always capture the parameters that come from the trait, because I'm not totally yet convinced that it's sound to not capture these args. It's certainly required to capture the type and const parameters from the trait (e.g. Self), or else users could bivariantly relate two RPITIT args that come from different impls, but region parameters don't affect trait selection in the same way, so it *may* be possible to relax this in the future. Let's stay conservative for now, though. I'm not totally sure what tests could be added on top of the ones I already added, since we really don't need to exercise the `precise_capturing` feature but simply what makes it special for RPITITs. r? types Tracking issue: * rust-lang#130044
2 parents edb6693 + 322c4bd commit 4f2af12

30 files changed

+309
-164
lines changed

compiler/rustc_ast_lowering/src/lib.rs

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1573,11 +1573,20 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
15731573
// Feature gate for RPITIT + use<..>
15741574
match origin {
15751575
rustc_hir::OpaqueTyOrigin::FnReturn { in_trait_or_impl: Some(_), .. } => {
1576-
if let Some(span) = bounds.iter().find_map(|bound| match *bound {
1577-
ast::GenericBound::Use(_, span) => Some(span),
1578-
_ => None,
1579-
}) {
1580-
self.tcx.dcx().emit_err(errors::NoPreciseCapturesOnRpitit { span });
1576+
if !self.tcx.features().precise_capturing_in_traits
1577+
&& let Some(span) = bounds.iter().find_map(|bound| match *bound {
1578+
ast::GenericBound::Use(_, span) => Some(span),
1579+
_ => None,
1580+
})
1581+
{
1582+
let mut diag =
1583+
self.tcx.dcx().create_err(errors::NoPreciseCapturesOnRpitit { span });
1584+
add_feature_diagnostics(
1585+
&mut diag,
1586+
self.tcx.sess,
1587+
sym::precise_capturing_in_traits,
1588+
);
1589+
diag.emit();
15811590
}
15821591
}
15831592
_ => {}

compiler/rustc_feature/src/unstable.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -565,6 +565,8 @@ declare_features! (
565565
(incomplete, pin_ergonomics, "CURRENT_RUSTC_VERSION", Some(130494)),
566566
/// Allows postfix match `expr.match { ... }`
567567
(unstable, postfix_match, "1.79.0", Some(121618)),
568+
/// Allows `use<..>` precise capturign on impl Trait in traits.
569+
(unstable, precise_capturing_in_traits, "CURRENT_RUSTC_VERSION", Some(130044)),
568570
/// Allows macro attributes on expressions, statements and non-inline modules.
569571
(unstable, proc_macro_hygiene, "1.30.0", Some(54727)),
570572
/// Makes `&` and `&mut` patterns eat only one layer of references in Rust 2024.

compiler/rustc_hir_analysis/messages.ftl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,9 @@ hir_analysis_late_bound_lifetime_in_apit = `impl Trait` can only mention lifetim
259259
hir_analysis_late_bound_type_in_apit = `impl Trait` can only mention type parameters from an fn or impl
260260
.label = type parameter declared here
261261
262+
hir_analysis_lifetime_implicitly_captured = `impl Trait` captures lifetime parameter, but it is not mentioned in `use<...>` precise captures list
263+
.param_label = all lifetime parameters originating from a trait are captured implicitly
264+
262265
hir_analysis_lifetime_must_be_first = lifetime parameter `{$name}` must be listed before non-lifetime parameters
263266
.label = move the lifetime before this parameter
264267

compiler/rustc_hir_analysis/src/check/check.rs

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -589,15 +589,22 @@ fn check_opaque_precise_captures<'tcx>(tcx: TyCtxt<'tcx>, opaque_def_id: LocalDe
589589
param_span: tcx.def_span(def_id),
590590
});
591591
} else {
592-
// If the `use_span` is actually just the param itself, then we must
593-
// have not duplicated the lifetime but captured the original.
594-
// The "effective" `use_span` will be the span of the opaque itself,
595-
// and the param span will be the def span of the param.
596-
tcx.dcx().emit_err(errors::LifetimeNotCaptured {
597-
opaque_span,
598-
use_span: opaque_span,
599-
param_span: use_span,
600-
});
592+
if tcx.def_kind(tcx.parent(param.def_id)) == DefKind::Trait {
593+
tcx.dcx().emit_err(errors::LifetimeImplicitlyCaptured {
594+
opaque_span,
595+
param_span: tcx.def_span(param.def_id),
596+
});
597+
} else {
598+
// If the `use_span` is actually just the param itself, then we must
599+
// have not duplicated the lifetime but captured the original.
600+
// The "effective" `use_span` will be the span of the opaque itself,
601+
// and the param span will be the def span of the param.
602+
tcx.dcx().emit_err(errors::LifetimeNotCaptured {
603+
opaque_span,
604+
use_span: opaque_span,
605+
param_span: use_span,
606+
});
607+
}
601608
}
602609
continue;
603610
}

compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,10 @@ pub(super) fn check_refining_return_position_impl_trait_in_trait<'tcx>(
6464
return;
6565
};
6666

67+
if hidden_tys.items().any(|(_, &ty)| ty.skip_binder().references_error()) {
68+
return;
69+
}
70+
6771
let mut collector = ImplTraitInTraitCollector { tcx, types: FxIndexSet::default() };
6872
trait_m_sig.visit_with(&mut collector);
6973

compiler/rustc_hir_analysis/src/errors/precise_captures.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,15 @@ pub(crate) struct LifetimeNotCaptured {
3434
pub opaque_span: Span,
3535
}
3636

37+
#[derive(Diagnostic)]
38+
#[diag(hir_analysis_lifetime_implicitly_captured)]
39+
pub(crate) struct LifetimeImplicitlyCaptured {
40+
#[primary_span]
41+
pub opaque_span: Span,
42+
#[label(hir_analysis_param_label)]
43+
pub param_span: Span,
44+
}
45+
3746
#[derive(Diagnostic)]
3847
#[diag(hir_analysis_bad_precise_capture)]
3948
pub(crate) struct BadPreciseCapture {

compiler/rustc_hir_analysis/src/variance/mod.rs

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
66
use itertools::Itertools;
77
use rustc_arena::DroplessArena;
8+
use rustc_hir as hir;
89
use rustc_hir::def::DefKind;
910
use rustc_hir::def_id::{DefId, LocalDefId};
1011
use rustc_middle::query::Providers;
@@ -63,8 +64,29 @@ fn variances_of(tcx: TyCtxt<'_>, item_def_id: LocalDefId) -> &[ty::Variance] {
6364
let crate_map = tcx.crate_variances(());
6465
return crate_map.variances.get(&item_def_id.to_def_id()).copied().unwrap_or(&[]);
6566
}
67+
DefKind::AssocTy => match tcx.opt_rpitit_info(item_def_id.to_def_id()) {
68+
Some(ty::ImplTraitInTraitData::Trait { opaque_def_id, .. }) => {
69+
return variance_of_opaque(
70+
tcx,
71+
opaque_def_id.expect_local(),
72+
ForceCaptureTraitArgs::Yes,
73+
);
74+
}
75+
None | Some(ty::ImplTraitInTraitData::Impl { .. }) => {}
76+
},
6677
DefKind::OpaqueTy => {
67-
return variance_of_opaque(tcx, item_def_id);
78+
let force_capture_trait_args = if let hir::OpaqueTyOrigin::FnReturn {
79+
parent: _,
80+
in_trait_or_impl: Some(hir::RpitContext::Trait),
81+
} =
82+
tcx.hir_node_by_def_id(item_def_id).expect_opaque_ty().origin
83+
{
84+
ForceCaptureTraitArgs::Yes
85+
} else {
86+
ForceCaptureTraitArgs::No
87+
};
88+
89+
return variance_of_opaque(tcx, item_def_id, force_capture_trait_args);
6890
}
6991
_ => {}
7092
}
@@ -73,8 +95,18 @@ fn variances_of(tcx: TyCtxt<'_>, item_def_id: LocalDefId) -> &[ty::Variance] {
7395
span_bug!(tcx.def_span(item_def_id), "asked to compute variance for wrong kind of item");
7496
}
7597

98+
#[derive(Debug, Copy, Clone)]
99+
enum ForceCaptureTraitArgs {
100+
Yes,
101+
No,
102+
}
103+
76104
#[instrument(level = "trace", skip(tcx), ret)]
77-
fn variance_of_opaque(tcx: TyCtxt<'_>, item_def_id: LocalDefId) -> &[ty::Variance] {
105+
fn variance_of_opaque(
106+
tcx: TyCtxt<'_>,
107+
item_def_id: LocalDefId,
108+
force_capture_trait_args: ForceCaptureTraitArgs,
109+
) -> &[ty::Variance] {
78110
let generics = tcx.generics_of(item_def_id);
79111

80112
// Opaque types may only use regions that are bound. So for
@@ -115,9 +147,7 @@ fn variance_of_opaque(tcx: TyCtxt<'_>, item_def_id: LocalDefId) -> &[ty::Varianc
115147
#[instrument(level = "trace", skip(self), ret)]
116148
fn visit_ty(&mut self, t: Ty<'tcx>) {
117149
match t.kind() {
118-
ty::Alias(_, ty::AliasTy { def_id, args, .. })
119-
if matches!(self.tcx.def_kind(*def_id), DefKind::OpaqueTy) =>
120-
{
150+
ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => {
121151
self.visit_opaque(*def_id, args);
122152
}
123153
_ => t.super_visit_with(self),
@@ -135,6 +165,15 @@ fn variance_of_opaque(tcx: TyCtxt<'_>, item_def_id: LocalDefId) -> &[ty::Varianc
135165
let mut generics = generics;
136166
while let Some(def_id) = generics.parent {
137167
generics = tcx.generics_of(def_id);
168+
169+
// Don't mark trait params generic if we're in an RPITIT.
170+
if matches!(force_capture_trait_args, ForceCaptureTraitArgs::Yes)
171+
&& generics.parent.is_none()
172+
{
173+
debug_assert_eq!(tcx.def_kind(def_id), DefKind::Trait);
174+
break;
175+
}
176+
138177
for param in &generics.own_params {
139178
match param.kind {
140179
ty::GenericParamDefKind::Lifetime => {

compiler/rustc_metadata/src/rmeta/encoder.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1100,9 +1100,12 @@ fn should_encode_variances<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, def_kind: Def
11001100
| DefKind::Fn
11011101
| DefKind::Ctor(..)
11021102
| DefKind::AssocFn => true,
1103+
DefKind::AssocTy => {
1104+
// Only encode variances for RPITITs (for traits)
1105+
matches!(tcx.opt_rpitit_info(def_id), Some(ty::ImplTraitInTraitData::Trait { .. }))
1106+
}
11031107
DefKind::Mod
11041108
| DefKind::Field
1105-
| DefKind::AssocTy
11061109
| DefKind::AssocConst
11071110
| DefKind::TyParam
11081111
| DefKind::ConstParam

compiler/rustc_middle/src/ty/context.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -539,6 +539,10 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
539539
self.trait_def(trait_def_id).implement_via_object
540540
}
541541

542+
fn is_impl_trait_in_trait(self, def_id: DefId) -> bool {
543+
self.is_impl_trait_in_trait(def_id)
544+
}
545+
542546
fn delay_bug(self, msg: impl ToString) -> ErrorGuaranteed {
543547
self.dcx().span_delayed_bug(DUMMY_SP, msg.to_string())
544548
}

compiler/rustc_span/src/symbol.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1479,6 +1479,7 @@ symbols! {
14791479
powif64,
14801480
pre_dash_lto: "pre-lto",
14811481
precise_capturing,
1482+
precise_capturing_in_traits,
14821483
precise_pointer_size_matching,
14831484
pref_align_of,
14841485
prefetch_read_data,

0 commit comments

Comments
 (0)