Skip to content

Commit c3c9ccb

Browse files
Implement precise capturing of types
1 parent cbfdf0b commit c3c9ccb

File tree

14 files changed

+164
-65
lines changed

14 files changed

+164
-65
lines changed

compiler/rustc_feature/src/unstable.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -600,8 +600,10 @@ declare_features! (
600600
(incomplete, pin_ergonomics, "1.83.0", Some(130494)),
601601
/// Allows postfix match `expr.match { ... }`
602602
(unstable, postfix_match, "1.79.0", Some(121618)),
603-
/// Allows `use<..>` precise capturign on impl Trait in traits.
603+
/// Allows `use<..>` precise capturing on impl Trait in traits.
604604
(unstable, precise_capturing_in_traits, "1.83.0", Some(130044)),
605+
/// Allows `use<..>` precise capturing to omit type and const parameters.
606+
(incomplete, precise_capturing_of_types, "1.83.0", Some(130043)),
605607
/// Allows macro attributes on expressions, statements and non-inline modules.
606608
(unstable, proc_macro_hygiene, "1.30.0", Some(54727)),
607609
/// Allows the use of raw-dylibs on ELF platforms

compiler/rustc_hir_analysis/messages.ftl

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -284,16 +284,9 @@ hir_analysis_late_bound_lifetime_in_apit = `impl Trait` can only mention lifetim
284284
hir_analysis_late_bound_type_in_apit = `impl Trait` can only mention type parameters from an fn or impl
285285
.label = type parameter declared here
286286
287-
hir_analysis_lifetime_implicitly_captured = `impl Trait` captures lifetime parameter, but it is not mentioned in `use<...>` precise captures list
288-
.param_label = all lifetime parameters originating from a trait are captured implicitly
289-
290287
hir_analysis_lifetime_must_be_first = lifetime parameter `{$name}` must be listed before non-lifetime parameters
291288
.label = move the lifetime before this parameter
292289
293-
hir_analysis_lifetime_not_captured = `impl Trait` captures lifetime parameter, but it is not mentioned in `use<...>` precise captures list
294-
.label = lifetime captured due to being mentioned in the bounds of the `impl Trait`
295-
.param_label = this lifetime parameter is captured
296-
297290
hir_analysis_lifetimes_or_bounds_mismatch_on_trait =
298291
lifetime parameters or bounds on {$item_kind} `{$ident}` do not match the trait declaration
299292
.label = lifetimes do not match {$item_kind} in trait
@@ -412,6 +405,9 @@ hir_analysis_opaque_captures_higher_ranked_lifetime = `impl Trait` cannot captur
412405
.label = `impl Trait` implicitly captures all lifetimes in scope
413406
.note = lifetime declared here
414407
408+
hir_analysis_param_implicitly_captured = `impl Trait` captures {$kind} parameter, but it is not mentioned in `use<...>` precise captures list
409+
.param_label = all parameters originating from a trait are captured implicitly
410+
415411
hir_analysis_param_in_ty_of_assoc_const_binding =
416412
the type of the associated constant `{$assoc_const}` must not depend on {$param_category ->
417413
[self] `Self`
@@ -428,7 +424,11 @@ hir_analysis_param_in_ty_of_assoc_const_binding =
428424
*[normal] the {$param_def_kind} `{$param_name}` is defined here
429425
}
430426
431-
hir_analysis_param_not_captured = `impl Trait` must mention all {$kind} parameters in scope in `use<...>`
427+
hir_analysis_param_not_captured = `impl Trait` captures {$kind} parameter, but it is not mentioned in `use<...>` precise captures list
428+
.label = {$kind} captured due to being mentioned in the bounds of the `impl Trait`
429+
.param_label = this {$kind} parameter is captured
430+
431+
hir_analysis_param_not_captured_forced = `impl Trait` must mention all {$kind} parameters in scope in `use<...>`
432432
.label = {$kind} parameter is implicitly captured by this `impl Trait`
433433
.note = currently, all {$kind} parameters are required to be mentioned in the precise captures list
434434

compiler/rustc_hir_analysis/src/check/check.rs

Lines changed: 81 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -610,71 +610,108 @@ fn check_opaque_precise_captures<'tcx>(tcx: TyCtxt<'tcx>, opaque_def_id: LocalDe
610610
}
611611

612612
match param.kind {
613-
ty::GenericParamDefKind::Lifetime => {
614-
let use_span = tcx.def_span(param.def_id);
615-
let opaque_span = tcx.def_span(opaque_def_id);
616-
// Check if the lifetime param was captured but isn't named in the precise captures list.
617-
if variances[param.index as usize] == ty::Invariant {
618-
if let DefKind::OpaqueTy = tcx.def_kind(tcx.parent(param.def_id))
619-
&& let Some(def_id) = tcx
620-
.map_opaque_lifetime_to_parent_lifetime(param.def_id.expect_local())
621-
.opt_param_def_id(tcx, tcx.parent(opaque_def_id.to_def_id()))
622-
{
623-
tcx.dcx().emit_err(errors::LifetimeNotCaptured {
624-
opaque_span,
625-
use_span,
626-
param_span: tcx.def_span(def_id),
627-
});
628-
} else {
629-
if tcx.def_kind(tcx.parent(param.def_id)) == DefKind::Trait {
630-
tcx.dcx().emit_err(errors::LifetimeImplicitlyCaptured {
631-
opaque_span,
632-
param_span: tcx.def_span(param.def_id),
633-
});
634-
} else {
635-
// If the `use_span` is actually just the param itself, then we must
636-
// have not duplicated the lifetime but captured the original.
637-
// The "effective" `use_span` will be the span of the opaque itself,
638-
// and the param span will be the def span of the param.
639-
tcx.dcx().emit_err(errors::LifetimeNotCaptured {
640-
opaque_span,
641-
use_span: opaque_span,
642-
param_span: use_span,
643-
});
644-
}
645-
}
646-
continue;
647-
}
648-
}
613+
ty::GenericParamDefKind::Lifetime => check_captured_arg_is_mentioned(
614+
tcx,
615+
opaque_def_id,
616+
variances,
617+
param,
618+
"lifetime",
619+
),
649620
ty::GenericParamDefKind::Type { .. } => {
650-
if matches!(tcx.def_kind(param.def_id), DefKind::Trait | DefKind::TraitAlias) {
621+
if tcx.features().precise_capturing_of_types() {
622+
check_captured_arg_is_mentioned(
623+
tcx,
624+
opaque_def_id,
625+
variances,
626+
param,
627+
"type",
628+
)
629+
} else if matches!(
630+
tcx.def_kind(param.def_id),
631+
DefKind::Trait | DefKind::TraitAlias
632+
) {
651633
// FIXME(precise_capturing): Structured suggestion for this would be useful
652634
tcx.dcx().emit_err(errors::SelfTyNotCaptured {
653635
trait_span: tcx.def_span(param.def_id),
654636
opaque_span: tcx.def_span(opaque_def_id),
655637
});
656638
} else {
657639
// FIXME(precise_capturing): Structured suggestion for this would be useful
658-
tcx.dcx().emit_err(errors::ParamNotCaptured {
640+
tcx.dcx().emit_err(errors::ParamNotCapturedForced {
659641
param_span: tcx.def_span(param.def_id),
660642
opaque_span: tcx.def_span(opaque_def_id),
661643
kind: "type",
662644
});
663645
}
664646
}
665647
ty::GenericParamDefKind::Const { .. } => {
666-
// FIXME(precise_capturing): Structured suggestion for this would be useful
667-
tcx.dcx().emit_err(errors::ParamNotCaptured {
668-
param_span: tcx.def_span(param.def_id),
669-
opaque_span: tcx.def_span(opaque_def_id),
670-
kind: "const",
671-
});
648+
if tcx.features().precise_capturing_of_types() {
649+
check_captured_arg_is_mentioned(
650+
tcx,
651+
opaque_def_id,
652+
variances,
653+
param,
654+
"const",
655+
)
656+
} else {
657+
// FIXME(precise_capturing): Structured suggestion for this would be useful
658+
tcx.dcx().emit_err(errors::ParamNotCapturedForced {
659+
param_span: tcx.def_span(param.def_id),
660+
opaque_span: tcx.def_span(opaque_def_id),
661+
kind: "const",
662+
});
663+
}
672664
}
673665
}
674666
}
675667
}
676668
}
677669

670+
fn check_captured_arg_is_mentioned<'tcx>(
671+
tcx: TyCtxt<'tcx>,
672+
opaque_def_id: LocalDefId,
673+
variances: &[ty::Variance],
674+
param: &ty::GenericParamDef,
675+
kind: &'static str,
676+
) {
677+
let use_span = tcx.def_span(param.def_id);
678+
let opaque_span = tcx.def_span(opaque_def_id);
679+
// Check if the lifetime param was captured but isn't named in the precise captures list.
680+
if variances[param.index as usize] == ty::Invariant {
681+
if let DefKind::OpaqueTy = tcx.def_kind(tcx.parent(param.def_id))
682+
&& let Some(def_id) = tcx
683+
.map_opaque_lifetime_to_parent_lifetime(param.def_id.expect_local())
684+
.opt_param_def_id(tcx, tcx.parent(opaque_def_id.to_def_id()))
685+
{
686+
tcx.dcx().emit_err(errors::ParamNotCaptured {
687+
opaque_span,
688+
use_span,
689+
param_span: tcx.def_span(def_id),
690+
kind,
691+
});
692+
} else {
693+
if tcx.def_kind(tcx.parent(param.def_id)) == DefKind::Trait {
694+
tcx.dcx().emit_err(errors::ParamImplicitlyCaptured {
695+
opaque_span,
696+
param_span: tcx.def_span(param.def_id),
697+
kind,
698+
});
699+
} else {
700+
// If the `use_span` is actually just the param itself, then we must
701+
// have not duplicated the lifetime but captured the original.
702+
// The "effective" `use_span` will be the span of the opaque itself,
703+
// and the param span will be the def span of the param.
704+
tcx.dcx().emit_err(errors::ParamNotCaptured {
705+
opaque_span,
706+
use_span: opaque_span,
707+
param_span: use_span,
708+
kind,
709+
});
710+
}
711+
}
712+
}
713+
}
714+
678715
fn is_enum_of_nonnullable_ptr<'tcx>(
679716
tcx: TyCtxt<'tcx>,
680717
adt_def: AdtDef<'tcx>,

compiler/rustc_hir_analysis/src/errors/precise_captures.rs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ use rustc_macros::Diagnostic;
33
use rustc_span::{Span, Symbol};
44

55
#[derive(Diagnostic)]
6-
#[diag(hir_analysis_param_not_captured)]
6+
#[diag(hir_analysis_param_not_captured_forced)]
77
#[note]
8-
pub(crate) struct ParamNotCaptured {
8+
pub(crate) struct ParamNotCapturedForced {
99
#[primary_span]
1010
pub opaque_span: Span,
1111
#[label]
@@ -24,23 +24,25 @@ pub(crate) struct SelfTyNotCaptured {
2424
}
2525

2626
#[derive(Diagnostic)]
27-
#[diag(hir_analysis_lifetime_not_captured)]
28-
pub(crate) struct LifetimeNotCaptured {
27+
#[diag(hir_analysis_param_not_captured)]
28+
pub(crate) struct ParamNotCaptured {
2929
#[primary_span]
3030
pub use_span: Span,
3131
#[label(hir_analysis_param_label)]
3232
pub param_span: Span,
3333
#[label]
3434
pub opaque_span: Span,
35+
pub kind: &'static str,
3536
}
3637

3738
#[derive(Diagnostic)]
38-
#[diag(hir_analysis_lifetime_implicitly_captured)]
39-
pub(crate) struct LifetimeImplicitlyCaptured {
39+
#[diag(hir_analysis_param_implicitly_captured)]
40+
pub(crate) struct ParamImplicitlyCaptured {
4041
#[primary_span]
4142
pub opaque_span: Span,
4243
#[label(hir_analysis_param_label)]
4344
pub param_span: Span,
45+
pub kind: &'static str,
4446
}
4547

4648
#[derive(Diagnostic)]

compiler/rustc_hir_analysis/src/variance/mod.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ fn variances_of(tcx: TyCtxt<'_>, item_def_id: LocalDefId) -> &[ty::Variance] {
6363
}
6464
DefKind::AssocTy => match tcx.opt_rpitit_info(item_def_id.to_def_id()) {
6565
Some(ty::ImplTraitInTraitData::Trait { opaque_def_id, .. }) => {
66-
return variance_of_opaque(
66+
return variances_of_opaque(
6767
tcx,
6868
opaque_def_id.expect_local(),
6969
ForceCaptureTraitArgs::Yes,
@@ -83,7 +83,7 @@ fn variances_of(tcx: TyCtxt<'_>, item_def_id: LocalDefId) -> &[ty::Variance] {
8383
ForceCaptureTraitArgs::No
8484
};
8585

86-
return variance_of_opaque(tcx, item_def_id, force_capture_trait_args);
86+
return variances_of_opaque(tcx, item_def_id, force_capture_trait_args);
8787
}
8888
_ => {}
8989
}
@@ -99,7 +99,7 @@ enum ForceCaptureTraitArgs {
9999
}
100100

101101
#[instrument(level = "trace", skip(tcx), ret)]
102-
fn variance_of_opaque(
102+
fn variances_of_opaque(
103103
tcx: TyCtxt<'_>,
104104
item_def_id: LocalDefId,
105105
force_capture_trait_args: ForceCaptureTraitArgs,
@@ -177,7 +177,11 @@ fn variance_of_opaque(
177177
variances[param.index as usize] = ty::Bivariant;
178178
}
179179
ty::GenericParamDefKind::Type { .. }
180-
| ty::GenericParamDefKind::Const { .. } => {}
180+
| ty::GenericParamDefKind::Const { .. } => {
181+
if tcx.features().precise_capturing_of_types() {
182+
variances[param.index as usize] = ty::Bivariant;
183+
}
184+
}
181185
}
182186
}
183187
}

compiler/rustc_middle/src/ty/context.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -772,6 +772,10 @@ impl<'tcx> rustc_type_ir::inherent::Features<TyCtxt<'tcx>> for &'tcx rustc_featu
772772
fn associated_const_equality(self) -> bool {
773773
self.associated_const_equality()
774774
}
775+
776+
fn precise_capturing_of_types(self) -> bool {
777+
self.precise_capturing_of_types()
778+
}
775779
}
776780

777781
impl<'tcx> rustc_type_ir::inherent::Span<TyCtxt<'tcx>> for Span {

compiler/rustc_span/src/symbol.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1570,6 +1570,7 @@ symbols! {
15701570
pre_dash_lto: "pre-lto",
15711571
precise_capturing,
15721572
precise_capturing_in_traits,
1573+
precise_capturing_of_types,
15731574
precise_pointer_size_matching,
15741575
pref_align_of,
15751576
prefetch_read_data,

compiler/rustc_type_ir/src/inherent.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -575,6 +575,8 @@ pub trait Features<I: Interner>: Copy {
575575
fn coroutine_clone(self) -> bool;
576576

577577
fn associated_const_equality(self) -> bool;
578+
579+
fn precise_capturing_of_types(self) -> bool;
578580
}
579581

580582
pub trait DefId<I: Interner>: Copy + Debug + Hash + Eq + TypeFoldable<I> {

compiler/rustc_type_ir/src/opaque_ty.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,11 @@ pub struct OpaqueTypeKey<I: Interner> {
1717
impl<I: Interner> OpaqueTypeKey<I> {
1818
pub fn iter_captured_args(self, cx: I) -> impl Iterator<Item = (usize, I::GenericArg)> {
1919
let variances = cx.variances_of(self.def_id.into());
20+
let precise_capturing_of_types = cx.features().precise_capturing_of_types();
2021
std::iter::zip(self.args.iter(), variances.iter()).enumerate().filter_map(
21-
|(i, (arg, v))| match (arg.kind(), v) {
22+
move |(i, (arg, v))| match (arg.kind(), v) {
2223
(_, ty::Invariant) => Some((i, arg)),
24+
(_, ty::Bivariant) if precise_capturing_of_types => None,
2325
(ty::GenericArgKind::Lifetime(_), ty::Bivariant) => None,
2426
_ => panic!("unexpected opaque type arg variance"),
2527
},
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
fn foo<T>() -> impl Sized + use<> {}
2+
//~^ ERROR `impl Trait` must mention all type parameters in scope in `use<...>`
3+
4+
fn main() {}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
error: `impl Trait` must mention all type parameters in scope in `use<...>`
2+
--> $DIR/feature-gate-precise-capturing-of-types.rs:1:16
3+
|
4+
LL | fn foo<T>() -> impl Sized + use<> {}
5+
| - ^^^^^^^^^^^^^^^^^^
6+
| |
7+
| type parameter is implicitly captured by this `impl Trait`
8+
|
9+
= note: currently, all type parameters are required to be mentioned in the precise captures list
10+
11+
error: aborting due to 1 previous error
12+
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
//@ check-pass
2+
3+
#![feature(precise_capturing_of_types)]
4+
//~^ WARN the feature `precise_capturing_of_types` is incomplete
5+
6+
use std::fmt::Display;
7+
use std::ops::Deref;
8+
9+
fn len<T: Deref<Target: Deref<Target = [u8]>>>(x: T) -> impl Display + use<> {
10+
x.len()
11+
}
12+
13+
fn main() {
14+
let x = vec![1, 2, 3];
15+
let len = len(&x);
16+
drop(x);
17+
println!("len = {len}");
18+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
warning: the feature `precise_capturing_of_types` is incomplete and may not be safe to use and/or cause compiler crashes
2+
--> $DIR/of-types.rs:3:12
3+
|
4+
LL | #![feature(precise_capturing_of_types)]
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: see issue #130043 <https://github.com/rust-lang/rust/issues/130043> for more information
8+
= note: `#[warn(incomplete_features)]` on by default
9+
10+
warning: 1 warning emitted
11+

tests/ui/impl-trait/precise-capturing/rpitit.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ error: `impl Trait` captures lifetime parameter, but it is not mentioned in `use
22
--> $DIR/rpitit.rs:11:19
33
|
44
LL | trait TraitLt<'a: 'a> {
5-
| -- all lifetime parameters originating from a trait are captured implicitly
5+
| -- all parameters originating from a trait are captured implicitly
66
LL | fn hello() -> impl Sized + use<Self>;
77
| ^^^^^^^^^^^^^^^^^^^^^^
88

0 commit comments

Comments
 (0)