Skip to content

Commit 82dae35

Browse files
Implement precise capturing of types
1 parent 75530e9 commit 82dae35

File tree

14 files changed

+164
-65
lines changed

14 files changed

+164
-65
lines changed

compiler/rustc_feature/src/unstable.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -598,8 +598,10 @@ declare_features! (
598598
(incomplete, pin_ergonomics, "1.83.0", Some(130494)),
599599
/// Allows postfix match `expr.match { ... }`
600600
(unstable, postfix_match, "1.79.0", Some(121618)),
601-
/// Allows `use<..>` precise capturign on impl Trait in traits.
601+
/// Allows `use<..>` precise capturing on impl Trait in traits.
602602
(unstable, precise_capturing_in_traits, "1.83.0", Some(130044)),
603+
/// Allows `use<..>` precise capturing to omit type and const parameters.
604+
(incomplete, precise_capturing_of_types, "1.83.0", Some(130043)),
603605
/// Allows macro attributes on expressions, statements and non-inline modules.
604606
(unstable, proc_macro_hygiene, "1.30.0", Some(54727)),
605607
/// Allows the use of raw-dylibs on ELF platforms

compiler/rustc_hir_analysis/messages.ftl

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

+81-44
Original file line numberDiff line numberDiff line change
@@ -609,71 +609,108 @@ fn check_opaque_precise_captures<'tcx>(tcx: TyCtxt<'tcx>, opaque_def_id: LocalDe
609609
}
610610

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

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

compiler/rustc_hir_analysis/src/errors/precise_captures.rs

+8-6
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

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

+4
Original file line numberDiff line numberDiff line change
@@ -771,6 +771,10 @@ impl<'tcx> rustc_type_ir::inherent::Features<TyCtxt<'tcx>> for &'tcx rustc_featu
771771
fn associated_const_equality(self) -> bool {
772772
self.associated_const_equality()
773773
}
774+
775+
fn precise_capturing_of_types(self) -> bool {
776+
self.precise_capturing_of_types()
777+
}
774778
}
775779

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

compiler/rustc_span/src/symbol.rs

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

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

+3-1
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,11 @@ pub struct OpaqueTypeKey<I: Interner> {
2020
impl<I: Interner> OpaqueTypeKey<I> {
2121
pub fn iter_captured_args(self, cx: I) -> impl Iterator<Item = (usize, I::GenericArg)> {
2222
let variances = cx.variances_of(self.def_id.into());
23+
let precise_capturing_of_types = cx.features().precise_capturing_of_types();
2324
std::iter::zip(self.args.iter(), variances.iter()).enumerate().filter_map(
24-
|(i, (arg, v))| match (arg.kind(), v) {
25+
move |(i, (arg, v))| match (arg.kind(), v) {
2526
(_, ty::Invariant) => Some((i, arg)),
27+
(_, ty::Bivariant) if precise_capturing_of_types => None,
2628
(ty::GenericArgKind::Lifetime(_), ty::Bivariant) => None,
2729
_ => panic!("unexpected opaque type arg variance"),
2830
},
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() {}
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+
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+
}
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

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