Skip to content

Implement partial capturing of types in use<...> #135765

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions compiler/rustc_hir_analysis/messages.ftl
Original file line number Diff line number Diff line change
@@ -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
28 changes: 28 additions & 0 deletions compiler/rustc_hir_analysis/src/check/check.rs
Original file line number Diff line number Diff line change
@@ -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(&param.def_id) {
continue;
}

match param.kind {
ty::GenericParamDefKind::Lifetime => {
let use_span = tcx.def_span(param.def_id);
2 changes: 0 additions & 2 deletions compiler/rustc_hir_analysis/src/errors/precise_captures.rs
Original file line number Diff line number Diff line change
@@ -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,
56 changes: 56 additions & 0 deletions tests/ui/impl-trait/precise-capturing/partial-capture.rs
Original file line number Diff line number Diff line change
@@ -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<T>(slice: &[T]) -> impl Iterator<Item = usize> + use<> {
0..slice.len()
}

// `'_` and `T` are captured
fn enumerated_opaque<T>(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<T>(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<T>(slice: &[T]) -> impl Iterator<Item = (usize, &T)> + use<> {
//~^ ERROR `impl Trait` must mention all used type parameters in scope in `use<...>`
slice.iter().enumerate()
}

// `'_` and `T` are captured
fn enumerated_lt<T>(slice: &[T]) -> impl Iterator<Item = (usize, &T)> + 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<T, const N: usize>(arr: [T; N]) -> impl Iterator<Item = (usize, T)> + 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()
}
58 changes: 58 additions & 0 deletions tests/ui/impl-trait/precise-capturing/partial-capture.stderr
Original file line number Diff line number Diff line change
@@ -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<T>(slice: &[T]) -> impl Iterator + use<> {
| ---- --------------------- opaque type defined here
| |
| hidden type `Enumerate<std::slice::Iter<'_, T>>` captures the anonymous lifetime defined here
LL | slice.iter().enumerate()
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
help: add `'_` to the `use<...>` bound to explicitly capture it
|
LL | fn enumerated_opaque<T>(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<T>(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<T>(slice: &[T]) -> impl Iterator<Item = (usize, &T)> + 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<T>(slice: &[T]) -> impl Iterator<Item = (usize, &T)> + 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<T, const N: usize>(arr: [T; N]) -> impl Iterator<Item = (usize, T)> + 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<T, const N: usize>(arr: [T; N]) -> impl Iterator<Item = (usize, T)> + 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`.

Unchanged files with check annotations Beta

if in_trait_or_impl == Some(hir::RpitContext::Trait)
&& !tcx.defaultness(owner).has_value()
{
span_bug!(

Check failure on line 392 in compiler/rustc_hir_analysis/src/collect/type_of.rs

GitHub Actions / PR - x86_64-gnu-llvm-18

tried to get type of this RPITIT with no definition

Check failure on line 392 in compiler/rustc_hir_analysis/src/collect/type_of.rs

GitHub Actions / PR - x86_64-gnu-llvm-18

tried to get type of this RPITIT with no definition

Check failure on line 392 in compiler/rustc_hir_analysis/src/collect/type_of.rs

GitHub Actions / PR - x86_64-gnu-llvm-18

tried to get type of this RPITIT with no definition

Check failure on line 392 in compiler/rustc_hir_analysis/src/collect/type_of.rs

GitHub Actions / PR - x86_64-gnu-llvm-18

tried to get type of this RPITIT with no definition
tcx.def_span(def_id),
"tried to get type of this RPITIT with no definition"
);