From 8e7007995a3f06133f95f4316affc342788e5dd1 Mon Sep 17 00:00:00 2001 From: Frank King Date: Thu, 26 Dec 2024 11:43:21 +0800 Subject: [PATCH] Implement partial capturing of types in `use<...>` --- compiler/rustc_hir_analysis/messages.ftl | 5 +- .../rustc_hir_analysis/src/check/check.rs | 28 +++++++++ .../src/errors/precise_captures.rs | 2 - .../precise-capturing/partial-capture.rs | 56 ++++++++++++++++++ .../precise-capturing/partial-capture.stderr | 58 +++++++++++++++++++ 5 files changed, 144 insertions(+), 5 deletions(-) create mode 100644 tests/ui/impl-trait/precise-capturing/partial-capture.rs create mode 100644 tests/ui/impl-trait/precise-capturing/partial-capture.stderr diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index d7ab6eca84b3d..80f41feb29b60 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -417,9 +417,9 @@ hir_analysis_param_in_ty_of_assoc_const_binding = *[normal] the {$param_def_kind} `{$param_name}` is defined here } -hir_analysis_param_not_captured = `impl Trait` must mention all {$kind} parameters in scope in `use<...>` +hir_analysis_param_not_captured = `impl Trait` must mention all used {$kind} parameters in scope in `use<...>` .label = {$kind} parameter is implicitly captured by this `impl Trait` - .note = currently, all {$kind} parameters are required to be mentioned in the precise captures list + .suggestion = add `{$name}` in the capture list hir_analysis_paren_sugar_attribute = the `#[rustc_paren_sugar]` attribute is a temporary means of controlling which traits can use parenthetical notation .help = add `#![feature(unboxed_closures)]` to the crate attributes to use it @@ -481,7 +481,6 @@ hir_analysis_self_in_impl_self = hir_analysis_self_ty_not_captured = `impl Trait` must mention the `Self` type of the trait in `use<...>` .label = `Self` type parameter is implicitly captured by this `impl Trait` - .note = currently, all type parameters are required to be mentioned in the precise captures list hir_analysis_simd_ffi_highly_experimental = use of SIMD type{$snip} in FFI is highly experimental and may result in invalid code .help = add `#![feature(simd_ffi)]` to the crate attributes to enable diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index ec82644ea5ba4..7850f1831ab10 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -660,6 +660,29 @@ fn check_opaque_precise_captures<'tcx>(tcx: TyCtxt<'tcx>, opaque_def_id: LocalDe } } + // Collect all the param used by the opaque type into `expected_captures`, so that we + // can check they must be in the capture list. + let mut used_params = UnordSet::default(); + let generics = tcx.generics_of(opaque_def_id); + for generic_arg in tcx.type_of(opaque_def_id).skip_binder().walk() { + match generic_arg.unpack() { + ty::GenericArgKind::Type(ty_arg) + if let &ty::Param(ty::ParamTy { index, .. }) = ty_arg.kind() => + { + used_params.insert(generics.param_at(index as usize, tcx).def_id); + } + ty::GenericArgKind::Const(const_arg) + if let ty::ConstKind::Param(ty::ParamConst { index, .. }) = const_arg.kind() => + { + used_params.insert(generics.param_at(index as usize, tcx).def_id); + } + // lifetime params are checked separately + ty::GenericArgKind::Lifetime(_) + | ty::GenericArgKind::Type(_) + | ty::GenericArgKind::Const(_) => {} + } + } + let variances = tcx.variances_of(opaque_def_id); let mut def_id = Some(opaque_def_id.to_def_id()); while let Some(generics) = def_id { @@ -682,6 +705,11 @@ fn check_opaque_precise_captures<'tcx>(tcx: TyCtxt<'tcx>, opaque_def_id: LocalDe continue; } + // If a param is not used by the opaque type, then it need not be captured. + if !used_params.contains(¶m.def_id) { + continue; + } + match param.kind { ty::GenericParamDefKind::Lifetime => { let use_span = tcx.def_span(param.def_id); diff --git a/compiler/rustc_hir_analysis/src/errors/precise_captures.rs b/compiler/rustc_hir_analysis/src/errors/precise_captures.rs index 8a83866b7fa46..249f1c01e61fa 100644 --- a/compiler/rustc_hir_analysis/src/errors/precise_captures.rs +++ b/compiler/rustc_hir_analysis/src/errors/precise_captures.rs @@ -4,7 +4,6 @@ use rustc_span::{Span, Symbol}; #[derive(Diagnostic)] #[diag(hir_analysis_param_not_captured)] -#[note] pub(crate) struct ParamNotCaptured { #[primary_span] pub opaque_span: Span, @@ -15,7 +14,6 @@ pub(crate) struct ParamNotCaptured { #[derive(Diagnostic)] #[diag(hir_analysis_self_ty_not_captured)] -#[note] pub(crate) struct SelfTyNotCaptured { #[primary_span] pub opaque_span: Span, diff --git a/tests/ui/impl-trait/precise-capturing/partial-capture.rs b/tests/ui/impl-trait/precise-capturing/partial-capture.rs new file mode 100644 index 0000000000000..8aa2f067f2e44 --- /dev/null +++ b/tests/ui/impl-trait/precise-capturing/partial-capture.rs @@ -0,0 +1,56 @@ +// See #130043 and #130031 + +fn main() { + let mut data = [1, 2, 3]; + let mut i = indices(&data); + data = [4, 5, 6]; + i.next(); + + let mut i = enumerated_opaque(&data); + i.next(); + + let mut i = enumerated(&data); + i.next(); + + let mut i = enumerated_lt(&data); + i.next(); + + let mut i = enumerated_arr(data); + i.next(); +} + +// No lifetime or type params captured +fn indices(slice: &[T]) -> impl Iterator + use<> { + 0..slice.len() +} + +// `'_` and `T` are captured +fn enumerated_opaque(slice: &[T]) -> impl Iterator + use<> { + slice.iter().enumerate() + //~^ ERROR hidden type for `impl Iterator` captures lifetime that does not appear in bounds +} + +// `'_` and `T` are captured +fn enumerated_opaque_lt(slice: &[T]) -> impl Iterator + use<'_> { + //~^ ERROR `impl Trait` must mention all used type parameters in scope in `use<...>` + slice.iter().enumerate() +} + +// `'_` and `T` are captured +fn enumerated(slice: &[T]) -> impl Iterator + use<> { + //~^ ERROR `impl Trait` must mention all used type parameters in scope in `use<...>` + slice.iter().enumerate() +} + +// `'_` and `T` are captured +fn enumerated_lt(slice: &[T]) -> impl Iterator + use<'_> { + //~^ ERROR `impl Trait` must mention all used type parameters in scope in `use<...>` + slice.iter().enumerate() +} + +// `T` and `N` are captured +fn enumerated_arr(arr: [T; N]) -> impl Iterator + use<> { + //~^ ERROR `impl Trait` must mention all used type parameters in scope in `use<...>` + //~| ERROR `impl Trait` must mention all used const parameters in scope in `use<...>` + <[T; N]>::into_iter(arr).enumerate() +} diff --git a/tests/ui/impl-trait/precise-capturing/partial-capture.stderr b/tests/ui/impl-trait/precise-capturing/partial-capture.stderr new file mode 100644 index 0000000000000..47fe4665fbe89 --- /dev/null +++ b/tests/ui/impl-trait/precise-capturing/partial-capture.stderr @@ -0,0 +1,58 @@ +error[E0700]: hidden type for `impl Iterator` captures lifetime that does not appear in bounds + --> $DIR/partial-capture.rs:29:5 + | +LL | fn enumerated_opaque(slice: &[T]) -> impl Iterator + use<> { + | ---- --------------------- opaque type defined here + | | + | hidden type `Enumerate>` captures the anonymous lifetime defined here +LL | slice.iter().enumerate() + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: add `'_` to the `use<...>` bound to explicitly capture it + | +LL | fn enumerated_opaque(slice: &[T]) -> impl Iterator + use<'_> { + | ++ + +error: `impl Trait` must mention all used type parameters in scope in `use<...>` + --> $DIR/partial-capture.rs:34:44 + | +LL | fn enumerated_opaque_lt(slice: &[T]) -> impl Iterator + use<'_> { + | - ^^^^^^^^^^^^^^^^^^^^^^^ + | | + | type parameter is implicitly captured by this `impl Trait` + +error: `impl Trait` must mention all used type parameters in scope in `use<...>` + --> $DIR/partial-capture.rs:40:34 + | +LL | fn enumerated(slice: &[T]) -> impl Iterator + use<> { + | - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | type parameter is implicitly captured by this `impl Trait` + +error: `impl Trait` must mention all used type parameters in scope in `use<...>` + --> $DIR/partial-capture.rs:46:37 + | +LL | fn enumerated_lt(slice: &[T]) -> impl Iterator + use<'_> { + | - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | type parameter is implicitly captured by this `impl Trait` + +error: `impl Trait` must mention all used type parameters in scope in `use<...>` + --> $DIR/partial-capture.rs:52:54 + | +LL | fn enumerated_arr(arr: [T; N]) -> impl Iterator + use<> { + | - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | type parameter is implicitly captured by this `impl Trait` + +error: `impl Trait` must mention all used const parameters in scope in `use<...>` + --> $DIR/partial-capture.rs:52:54 + | +LL | fn enumerated_arr(arr: [T; N]) -> impl Iterator + use<> { + | -------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | const parameter is implicitly captured by this `impl Trait` + +error: aborting due to 6 previous errors + +For more information about this error, try `rustc --explain E0700`.