Skip to content

Commit 4f2af12

Browse files
authored
Rollup merge of #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: * #130044
2 parents edb6693 + 322c4bd commit 4f2af12

30 files changed

+309
-164
lines changed

compiler/rustc_ast_lowering/src/lib.rs

+14-5
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

+2
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

+3
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

+16-9
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

+4
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

+9
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

+44-5
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

+4-1
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

+4
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

+1
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,

compiler/rustc_type_ir/src/interner.rs

+2
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,8 @@ pub trait Interner:
261261

262262
fn trait_may_be_implemented_via_object(self, trait_def_id: Self::DefId) -> bool;
263263

264+
fn is_impl_trait_in_trait(self, def_id: Self::DefId) -> bool;
265+
264266
fn delay_bug(self, msg: impl ToString) -> Self::ErrorGuaranteed;
265267

266268
fn is_general_coroutine(self, coroutine_def_id: Self::DefId) -> bool;

compiler/rustc_type_ir/src/relate.rs

+10
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,16 @@ impl<I: Interner> Relate<I> for ty::AliasTy<I> {
254254
b.args,
255255
false, // do not fetch `type_of(a_def_id)`, as it will cause a cycle
256256
)?,
257+
ty::Projection if relation.cx().is_impl_trait_in_trait(a.def_id) => {
258+
relate_args_with_variances(
259+
relation,
260+
a.def_id,
261+
relation.cx().variances_of(a.def_id),
262+
a.args,
263+
b.args,
264+
false, // do not fetch `type_of(a_def_id)`, as it will cause a cycle
265+
)?
266+
}
257267
ty::Projection | ty::Weak | ty::Inherent => {
258268
relate_args_invariantly(relation, a.args, b.args)?
259269
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
trait Foo {
2+
fn test() -> impl Sized + use<Self>;
3+
//~^ ERROR `use<...>` precise capturing syntax is currently not allowed in return-position
4+
}
5+
6+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
error: `use<...>` precise capturing syntax is currently not allowed in return-position `impl Trait` in traits
2+
--> $DIR/feature-gate-precise_capturing_in_traits.rs:2:31
3+
|
4+
LL | fn test() -> impl Sized + use<Self>;
5+
| ^^^^^^^^^
6+
|
7+
= note: currently, return-position `impl Trait` in traits and trait implementations capture all lifetimes in scope
8+
= note: see issue #130044 <https://github.com/rust-lang/rust/issues/130044> for more information
9+
= help: add `#![feature(precise_capturing_in_traits)]` to the crate attributes to enable
10+
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
11+
12+
error: aborting due to 1 previous error
13+
+13-10
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,25 @@
1-
#![feature(rustc_attrs)]
1+
#![feature(rustc_attrs, precise_capturing_in_traits)]
22
#![allow(internal_features)]
33
#![rustc_variance_of_opaques]
44

5-
trait Captures<'a> {}
6-
impl<T> Captures<'_> for T {}
7-
85
trait Foo<'i> {
96
fn implicit_capture_early<'a: 'a>() -> impl Sized {}
10-
//~^ [Self: o, 'i: *, 'a: *, 'a: o, 'i: o]
7+
//~^ [Self: o, 'i: o, 'a: *, 'a: o, 'i: o]
8+
9+
fn explicit_capture_early<'a: 'a>() -> impl Sized + use<'i, 'a, Self> {}
10+
//~^ [Self: o, 'i: o, 'a: *, 'i: o, 'a: o]
1111

12-
fn explicit_capture_early<'a: 'a>() -> impl Sized + Captures<'a> {}
13-
//~^ [Self: o, 'i: *, 'a: *, 'a: o, 'i: o]
12+
fn not_captured_early<'a: 'a>() -> impl Sized + use<'i, Self> {}
13+
//~^ [Self: o, 'i: o, 'a: *, 'i: o]
1414

1515
fn implicit_capture_late<'a>(_: &'a ()) -> impl Sized {}
16-
//~^ [Self: o, 'i: *, 'a: o, 'i: o]
16+
//~^ [Self: o, 'i: o, 'a: o, 'i: o]
17+
18+
fn explicit_capture_late<'a>(_: &'a ()) -> impl Sized + use<'i, 'a, Self> {}
19+
//~^ [Self: o, 'i: o, 'i: o, 'a: o]
1720

18-
fn explicit_capture_late<'a>(_: &'a ()) -> impl Sized + Captures<'a> {}
19-
//~^ [Self: o, 'i: *, 'a: o, 'i: o]
21+
fn not_cpatured_late<'a>(_: &'a ()) -> impl Sized + use<'i, Self> {}
22+
//~^ [Self: o, 'i: o, 'i: o]
2023
}
2124

2225
fn main() {}
+23-11
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,38 @@
1-
error: [Self: o, 'i: *, 'a: *, 'a: o, 'i: o]
2-
--> $DIR/variance.rs:9:44
1+
error: [Self: o, 'i: o, 'a: *, 'a: o, 'i: o]
2+
--> $DIR/variance.rs:6:44
33
|
44
LL | fn implicit_capture_early<'a: 'a>() -> impl Sized {}
55
| ^^^^^^^^^^
66

7-
error: [Self: o, 'i: *, 'a: *, 'a: o, 'i: o]
8-
--> $DIR/variance.rs:12:44
7+
error: [Self: o, 'i: o, 'a: *, 'i: o, 'a: o]
8+
--> $DIR/variance.rs:9:44
9+
|
10+
LL | fn explicit_capture_early<'a: 'a>() -> impl Sized + use<'i, 'a, Self> {}
11+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
12+
13+
error: [Self: o, 'i: o, 'a: *, 'i: o]
14+
--> $DIR/variance.rs:12:40
915
|
10-
LL | fn explicit_capture_early<'a: 'a>() -> impl Sized + Captures<'a> {}
11-
| ^^^^^^^^^^^^^^^^^^^^^^^^^
16+
LL | fn not_captured_early<'a: 'a>() -> impl Sized + use<'i, Self> {}
17+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
1218

13-
error: [Self: o, 'i: *, 'a: o, 'i: o]
19+
error: [Self: o, 'i: o, 'a: o, 'i: o]
1420
--> $DIR/variance.rs:15:48
1521
|
1622
LL | fn implicit_capture_late<'a>(_: &'a ()) -> impl Sized {}
1723
| ^^^^^^^^^^
1824

19-
error: [Self: o, 'i: *, 'a: o, 'i: o]
25+
error: [Self: o, 'i: o, 'i: o, 'a: o]
2026
--> $DIR/variance.rs:18:48
2127
|
22-
LL | fn explicit_capture_late<'a>(_: &'a ()) -> impl Sized + Captures<'a> {}
23-
| ^^^^^^^^^^^^^^^^^^^^^^^^^
28+
LL | fn explicit_capture_late<'a>(_: &'a ()) -> impl Sized + use<'i, 'a, Self> {}
29+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
30+
31+
error: [Self: o, 'i: o, 'i: o]
32+
--> $DIR/variance.rs:21:44
33+
|
34+
LL | fn not_cpatured_late<'a>(_: &'a ()) -> impl Sized + use<'i, Self> {}
35+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
2436

25-
error: aborting due to 4 previous errors
37+
error: aborting due to 6 previous errors
2638

Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1+
#![feature(precise_capturing_in_traits)]
2+
13
fn type_param<T>() -> impl Sized + use<> {}
24
//~^ ERROR `impl Trait` must mention all type parameters in scope
35

46
trait Foo {
57
fn bar() -> impl Sized + use<>;
68
//~^ ERROR `impl Trait` must mention the `Self` type of the trait
7-
//~| ERROR `use<...>` precise capturing syntax is currently not allowed in return-position `impl Trait` in traits
89
}
910

1011
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,5 @@
1-
error: `use<...>` precise capturing syntax is currently not allowed in return-position `impl Trait` in traits
2-
--> $DIR/forgot-to-capture-type.rs:5:30
3-
|
4-
LL | fn bar() -> impl Sized + use<>;
5-
| ^^^^^
6-
|
7-
= note: currently, return-position `impl Trait` in traits and trait implementations capture all lifetimes in scope
8-
91
error: `impl Trait` must mention all type parameters in scope in `use<...>`
10-
--> $DIR/forgot-to-capture-type.rs:1:23
2+
--> $DIR/forgot-to-capture-type.rs:3:23
113
|
124
LL | fn type_param<T>() -> impl Sized + use<> {}
135
| - ^^^^^^^^^^^^^^^^^^
@@ -17,7 +9,7 @@ LL | fn type_param<T>() -> impl Sized + use<> {}
179
= note: currently, all type parameters are required to be mentioned in the precise captures list
1810

1911
error: `impl Trait` must mention the `Self` type of the trait in `use<...>`
20-
--> $DIR/forgot-to-capture-type.rs:5:17
12+
--> $DIR/forgot-to-capture-type.rs:7:17
2113
|
2214
LL | trait Foo {
2315
| --------- `Self` type parameter is implicitly captured by this `impl Trait`
@@ -26,5 +18,5 @@ LL | fn bar() -> impl Sized + use<>;
2618
|
2719
= note: currently, all type parameters are required to be mentioned in the precise captures list
2820

29-
error: aborting due to 3 previous errors
21+
error: aborting due to 2 previous errors
3022

0 commit comments

Comments
 (0)