From 82dae355eb87099bf16ecf0e1865ab3552e5eddf Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 13 Mar 2025 23:44:10 +0000 Subject: [PATCH 1/3] Implement precise capturing of types --- compiler/rustc_feature/src/unstable.rs | 4 +- compiler/rustc_hir_analysis/messages.ftl | 16 +-- .../rustc_hir_analysis/src/check/check.rs | 125 ++++++++++++------ .../src/errors/precise_captures.rs | 14 +- .../rustc_hir_analysis/src/variance/mod.rs | 12 +- compiler/rustc_middle/src/ty/context.rs | 4 + compiler/rustc_span/src/symbol.rs | 1 + compiler/rustc_type_ir/src/inherent.rs | 2 + compiler/rustc_type_ir/src/opaque_ty.rs | 4 +- ...feature-gate-precise-capturing-of-types.rs | 4 + ...ure-gate-precise-capturing-of-types.stderr | 12 ++ .../impl-trait/precise-capturing/of-types.rs | 18 +++ .../precise-capturing/of-types.stderr | 11 ++ .../precise-capturing/rpitit.stderr | 2 +- 14 files changed, 164 insertions(+), 65 deletions(-) create mode 100644 tests/ui/feature-gates/feature-gate-precise-capturing-of-types.rs create mode 100644 tests/ui/feature-gates/feature-gate-precise-capturing-of-types.stderr create mode 100644 tests/ui/impl-trait/precise-capturing/of-types.rs create mode 100644 tests/ui/impl-trait/precise-capturing/of-types.stderr diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 3b75c69132c20..26ee353a24baa 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -598,8 +598,10 @@ declare_features! ( (incomplete, pin_ergonomics, "1.83.0", Some(130494)), /// Allows postfix match `expr.match { ... }` (unstable, postfix_match, "1.79.0", Some(121618)), - /// Allows `use<..>` precise capturign on impl Trait in traits. + /// Allows `use<..>` precise capturing on impl Trait in traits. (unstable, precise_capturing_in_traits, "1.83.0", Some(130044)), + /// Allows `use<..>` precise capturing to omit type and const parameters. + (incomplete, precise_capturing_of_types, "1.83.0", Some(130043)), /// Allows macro attributes on expressions, statements and non-inline modules. (unstable, proc_macro_hygiene, "1.30.0", Some(54727)), /// Allows the use of raw-dylibs on ELF platforms diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index 194f2cd04e468..fc6789eb70378 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -284,16 +284,9 @@ hir_analysis_late_bound_lifetime_in_apit = `impl Trait` can only mention lifetim hir_analysis_late_bound_type_in_apit = `impl Trait` can only mention type parameters from an fn or impl .label = type parameter declared here -hir_analysis_lifetime_implicitly_captured = `impl Trait` captures lifetime parameter, but it is not mentioned in `use<...>` precise captures list - .param_label = all lifetime parameters originating from a trait are captured implicitly - hir_analysis_lifetime_must_be_first = lifetime parameter `{$name}` must be listed before non-lifetime parameters .label = move the lifetime before this parameter -hir_analysis_lifetime_not_captured = `impl Trait` captures lifetime parameter, but it is not mentioned in `use<...>` precise captures list - .label = lifetime captured due to being mentioned in the bounds of the `impl Trait` - .param_label = this lifetime parameter is captured - hir_analysis_lifetimes_or_bounds_mismatch_on_trait = lifetime parameters or bounds on {$item_kind} `{$ident}` do not match the trait declaration .label = lifetimes do not match {$item_kind} in trait @@ -412,6 +405,9 @@ hir_analysis_opaque_captures_higher_ranked_lifetime = `impl Trait` cannot captur .label = `impl Trait` implicitly captures all lifetimes in scope .note = lifetime declared here +hir_analysis_param_implicitly_captured = `impl Trait` captures {$kind} parameter, but it is not mentioned in `use<...>` precise captures list + .param_label = all parameters originating from a trait are captured implicitly + hir_analysis_param_in_ty_of_assoc_const_binding = the type of the associated constant `{$assoc_const}` must not depend on {$param_category -> [self] `Self` @@ -428,7 +424,11 @@ 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` captures {$kind} parameter, but it is not mentioned in `use<...>` precise captures list + .label = {$kind} captured due to being mentioned in the bounds of the `impl Trait` + .param_label = this {$kind} parameter is captured + +hir_analysis_param_not_captured_forced = `impl Trait` must mention all {$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 diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 07b5837bd871d..2589c282d04c1 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -609,44 +609,26 @@ fn check_opaque_precise_captures<'tcx>(tcx: TyCtxt<'tcx>, opaque_def_id: LocalDe } match param.kind { - ty::GenericParamDefKind::Lifetime => { - let use_span = tcx.def_span(param.def_id); - let opaque_span = tcx.def_span(opaque_def_id); - // Check if the lifetime param was captured but isn't named in the precise captures list. - if variances[param.index as usize] == ty::Invariant { - if let DefKind::OpaqueTy = tcx.def_kind(tcx.parent(param.def_id)) - && let Some(def_id) = tcx - .map_opaque_lifetime_to_parent_lifetime(param.def_id.expect_local()) - .opt_param_def_id(tcx, tcx.parent(opaque_def_id.to_def_id())) - { - tcx.dcx().emit_err(errors::LifetimeNotCaptured { - opaque_span, - use_span, - param_span: tcx.def_span(def_id), - }); - } else { - if tcx.def_kind(tcx.parent(param.def_id)) == DefKind::Trait { - tcx.dcx().emit_err(errors::LifetimeImplicitlyCaptured { - opaque_span, - param_span: tcx.def_span(param.def_id), - }); - } else { - // If the `use_span` is actually just the param itself, then we must - // have not duplicated the lifetime but captured the original. - // The "effective" `use_span` will be the span of the opaque itself, - // and the param span will be the def span of the param. - tcx.dcx().emit_err(errors::LifetimeNotCaptured { - opaque_span, - use_span: opaque_span, - param_span: use_span, - }); - } - } - continue; - } - } + ty::GenericParamDefKind::Lifetime => check_captured_arg_is_mentioned( + tcx, + opaque_def_id, + variances, + param, + "lifetime", + ), ty::GenericParamDefKind::Type { .. } => { - if matches!(tcx.def_kind(param.def_id), DefKind::Trait | DefKind::TraitAlias) { + if tcx.features().precise_capturing_of_types() { + check_captured_arg_is_mentioned( + tcx, + opaque_def_id, + variances, + param, + "type", + ) + } else if matches!( + tcx.def_kind(param.def_id), + DefKind::Trait | DefKind::TraitAlias + ) { // FIXME(precise_capturing): Structured suggestion for this would be useful tcx.dcx().emit_err(errors::SelfTyNotCaptured { trait_span: tcx.def_span(param.def_id), @@ -654,7 +636,7 @@ fn check_opaque_precise_captures<'tcx>(tcx: TyCtxt<'tcx>, opaque_def_id: LocalDe }); } else { // FIXME(precise_capturing): Structured suggestion for this would be useful - tcx.dcx().emit_err(errors::ParamNotCaptured { + tcx.dcx().emit_err(errors::ParamNotCapturedForced { param_span: tcx.def_span(param.def_id), opaque_span: tcx.def_span(opaque_def_id), kind: "type", @@ -662,18 +644,73 @@ fn check_opaque_precise_captures<'tcx>(tcx: TyCtxt<'tcx>, opaque_def_id: LocalDe } } ty::GenericParamDefKind::Const { .. } => { - // FIXME(precise_capturing): Structured suggestion for this would be useful - tcx.dcx().emit_err(errors::ParamNotCaptured { - param_span: tcx.def_span(param.def_id), - opaque_span: tcx.def_span(opaque_def_id), - kind: "const", - }); + if tcx.features().precise_capturing_of_types() { + check_captured_arg_is_mentioned( + tcx, + opaque_def_id, + variances, + param, + "const", + ) + } else { + // FIXME(precise_capturing): Structured suggestion for this would be useful + tcx.dcx().emit_err(errors::ParamNotCapturedForced { + param_span: tcx.def_span(param.def_id), + opaque_span: tcx.def_span(opaque_def_id), + kind: "const", + }); + } } } } } } +fn check_captured_arg_is_mentioned<'tcx>( + tcx: TyCtxt<'tcx>, + opaque_def_id: LocalDefId, + variances: &[ty::Variance], + param: &ty::GenericParamDef, + kind: &'static str, +) { + let use_span = tcx.def_span(param.def_id); + let opaque_span = tcx.def_span(opaque_def_id); + // Check if the lifetime param was captured but isn't named in the precise captures list. + if variances[param.index as usize] == ty::Invariant { + if let DefKind::OpaqueTy = tcx.def_kind(tcx.parent(param.def_id)) + && let Some(def_id) = tcx + .map_opaque_lifetime_to_parent_lifetime(param.def_id.expect_local()) + .opt_param_def_id(tcx, tcx.parent(opaque_def_id.to_def_id())) + { + tcx.dcx().emit_err(errors::ParamNotCaptured { + opaque_span, + use_span, + param_span: tcx.def_span(def_id), + kind, + }); + } else { + if tcx.def_kind(tcx.parent(param.def_id)) == DefKind::Trait { + tcx.dcx().emit_err(errors::ParamImplicitlyCaptured { + opaque_span, + param_span: tcx.def_span(param.def_id), + kind, + }); + } else { + // If the `use_span` is actually just the param itself, then we must + // have not duplicated the lifetime but captured the original. + // The "effective" `use_span` will be the span of the opaque itself, + // and the param span will be the def span of the param. + tcx.dcx().emit_err(errors::ParamNotCaptured { + opaque_span, + use_span: opaque_span, + param_span: use_span, + kind, + }); + } + } + } +} + fn is_enum_of_nonnullable_ptr<'tcx>( tcx: TyCtxt<'tcx>, adt_def: AdtDef<'tcx>, diff --git a/compiler/rustc_hir_analysis/src/errors/precise_captures.rs b/compiler/rustc_hir_analysis/src/errors/precise_captures.rs index 8a83866b7fa46..12eb0246860f1 100644 --- a/compiler/rustc_hir_analysis/src/errors/precise_captures.rs +++ b/compiler/rustc_hir_analysis/src/errors/precise_captures.rs @@ -3,9 +3,9 @@ use rustc_macros::Diagnostic; use rustc_span::{Span, Symbol}; #[derive(Diagnostic)] -#[diag(hir_analysis_param_not_captured)] +#[diag(hir_analysis_param_not_captured_forced)] #[note] -pub(crate) struct ParamNotCaptured { +pub(crate) struct ParamNotCapturedForced { #[primary_span] pub opaque_span: Span, #[label] @@ -24,23 +24,25 @@ pub(crate) struct SelfTyNotCaptured { } #[derive(Diagnostic)] -#[diag(hir_analysis_lifetime_not_captured)] -pub(crate) struct LifetimeNotCaptured { +#[diag(hir_analysis_param_not_captured)] +pub(crate) struct ParamNotCaptured { #[primary_span] pub use_span: Span, #[label(hir_analysis_param_label)] pub param_span: Span, #[label] pub opaque_span: Span, + pub kind: &'static str, } #[derive(Diagnostic)] -#[diag(hir_analysis_lifetime_implicitly_captured)] -pub(crate) struct LifetimeImplicitlyCaptured { +#[diag(hir_analysis_param_implicitly_captured)] +pub(crate) struct ParamImplicitlyCaptured { #[primary_span] pub opaque_span: Span, #[label(hir_analysis_param_label)] pub param_span: Span, + pub kind: &'static str, } #[derive(Diagnostic)] diff --git a/compiler/rustc_hir_analysis/src/variance/mod.rs b/compiler/rustc_hir_analysis/src/variance/mod.rs index 0800d99e9452e..6921ae02e49cb 100644 --- a/compiler/rustc_hir_analysis/src/variance/mod.rs +++ b/compiler/rustc_hir_analysis/src/variance/mod.rs @@ -63,7 +63,7 @@ fn variances_of(tcx: TyCtxt<'_>, item_def_id: LocalDefId) -> &[ty::Variance] { } DefKind::AssocTy => match tcx.opt_rpitit_info(item_def_id.to_def_id()) { Some(ty::ImplTraitInTraitData::Trait { opaque_def_id, .. }) => { - return variance_of_opaque( + return variances_of_opaque( tcx, opaque_def_id.expect_local(), ForceCaptureTraitArgs::Yes, @@ -83,7 +83,7 @@ fn variances_of(tcx: TyCtxt<'_>, item_def_id: LocalDefId) -> &[ty::Variance] { ForceCaptureTraitArgs::No }; - return variance_of_opaque(tcx, item_def_id, force_capture_trait_args); + return variances_of_opaque(tcx, item_def_id, force_capture_trait_args); } _ => {} } @@ -99,7 +99,7 @@ enum ForceCaptureTraitArgs { } #[instrument(level = "trace", skip(tcx), ret)] -fn variance_of_opaque( +fn variances_of_opaque( tcx: TyCtxt<'_>, item_def_id: LocalDefId, force_capture_trait_args: ForceCaptureTraitArgs, @@ -177,7 +177,11 @@ fn variance_of_opaque( variances[param.index as usize] = ty::Bivariant; } ty::GenericParamDefKind::Type { .. } - | ty::GenericParamDefKind::Const { .. } => {} + | ty::GenericParamDefKind::Const { .. } => { + if tcx.features().precise_capturing_of_types() { + variances[param.index as usize] = ty::Bivariant; + } + } } } } diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index f54dd2b0040ae..58a384fd7e578 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -771,6 +771,10 @@ impl<'tcx> rustc_type_ir::inherent::Features> for &'tcx rustc_featu fn associated_const_equality(self) -> bool { self.associated_const_equality() } + + fn precise_capturing_of_types(self) -> bool { + self.precise_capturing_of_types() + } } impl<'tcx> rustc_type_ir::inherent::Span> for Span { diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 8a8bec35d8194..cb68469091569 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1570,6 +1570,7 @@ symbols! { pre_dash_lto: "pre-lto", precise_capturing, precise_capturing_in_traits, + precise_capturing_of_types, precise_pointer_size_matching, pref_align_of, prefetch_read_data, diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs index d4134bdf3a782..e0ea4397edd21 100644 --- a/compiler/rustc_type_ir/src/inherent.rs +++ b/compiler/rustc_type_ir/src/inherent.rs @@ -575,6 +575,8 @@ pub trait Features: Copy { fn coroutine_clone(self) -> bool; fn associated_const_equality(self) -> bool; + + fn precise_capturing_of_types(self) -> bool; } pub trait DefId: Copy + Debug + Hash + Eq + TypeFoldable { diff --git a/compiler/rustc_type_ir/src/opaque_ty.rs b/compiler/rustc_type_ir/src/opaque_ty.rs index 416d355fd03bd..645159e6c6144 100644 --- a/compiler/rustc_type_ir/src/opaque_ty.rs +++ b/compiler/rustc_type_ir/src/opaque_ty.rs @@ -20,9 +20,11 @@ pub struct OpaqueTypeKey { impl OpaqueTypeKey { pub fn iter_captured_args(self, cx: I) -> impl Iterator { let variances = cx.variances_of(self.def_id.into()); + let precise_capturing_of_types = cx.features().precise_capturing_of_types(); std::iter::zip(self.args.iter(), variances.iter()).enumerate().filter_map( - |(i, (arg, v))| match (arg.kind(), v) { + move |(i, (arg, v))| match (arg.kind(), v) { (_, ty::Invariant) => Some((i, arg)), + (_, ty::Bivariant) if precise_capturing_of_types => None, (ty::GenericArgKind::Lifetime(_), ty::Bivariant) => None, _ => panic!("unexpected opaque type arg variance"), }, diff --git a/tests/ui/feature-gates/feature-gate-precise-capturing-of-types.rs b/tests/ui/feature-gates/feature-gate-precise-capturing-of-types.rs new file mode 100644 index 0000000000000..5c0a1e3c8292b --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-precise-capturing-of-types.rs @@ -0,0 +1,4 @@ +fn foo() -> impl Sized + use<> {} +//~^ ERROR `impl Trait` must mention all type parameters in scope in `use<...>` + +fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-precise-capturing-of-types.stderr b/tests/ui/feature-gates/feature-gate-precise-capturing-of-types.stderr new file mode 100644 index 0000000000000..7a04a777f3d59 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-precise-capturing-of-types.stderr @@ -0,0 +1,12 @@ +error: `impl Trait` must mention all type parameters in scope in `use<...>` + --> $DIR/feature-gate-precise-capturing-of-types.rs:1:16 + | +LL | fn foo() -> impl Sized + use<> {} + | - ^^^^^^^^^^^^^^^^^^ + | | + | type parameter is implicitly captured by this `impl Trait` + | + = note: currently, all type parameters are required to be mentioned in the precise captures list + +error: aborting due to 1 previous error + diff --git a/tests/ui/impl-trait/precise-capturing/of-types.rs b/tests/ui/impl-trait/precise-capturing/of-types.rs new file mode 100644 index 0000000000000..44707c2e3a97b --- /dev/null +++ b/tests/ui/impl-trait/precise-capturing/of-types.rs @@ -0,0 +1,18 @@ +//@ check-pass + +#![feature(precise_capturing_of_types)] +//~^ WARN the feature `precise_capturing_of_types` is incomplete + +use std::fmt::Display; +use std::ops::Deref; + +fn len>>(x: T) -> impl Display + use<> { + x.len() +} + +fn main() { + let x = vec![1, 2, 3]; + let len = len(&x); + drop(x); + println!("len = {len}"); +} diff --git a/tests/ui/impl-trait/precise-capturing/of-types.stderr b/tests/ui/impl-trait/precise-capturing/of-types.stderr new file mode 100644 index 0000000000000..045054a0f6f3c --- /dev/null +++ b/tests/ui/impl-trait/precise-capturing/of-types.stderr @@ -0,0 +1,11 @@ +warning: the feature `precise_capturing_of_types` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/of-types.rs:3:12 + | +LL | #![feature(precise_capturing_of_types)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #130043 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/impl-trait/precise-capturing/rpitit.stderr b/tests/ui/impl-trait/precise-capturing/rpitit.stderr index 498eae54a1c68..f204ab83c1fda 100644 --- a/tests/ui/impl-trait/precise-capturing/rpitit.stderr +++ b/tests/ui/impl-trait/precise-capturing/rpitit.stderr @@ -2,7 +2,7 @@ error: `impl Trait` captures lifetime parameter, but it is not mentioned in `use --> $DIR/rpitit.rs:11:19 | LL | trait TraitLt<'a: 'a> { - | -- all lifetime parameters originating from a trait are captured implicitly + | -- all parameters originating from a trait are captured implicitly LL | fn hello() -> impl Sized + use; | ^^^^^^^^^^^^^^^^^^^^^^ From ceac916b5d2ec5e006a89359099fba98dc634598 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 14 Mar 2025 00:32:26 +0000 Subject: [PATCH 2/3] Validate that uncaptured params are not in hidden type --- compiler/rustc_middle/messages.ftl | 3 - compiler/rustc_middle/src/error.rs | 8 -- compiler/rustc_middle/src/ty/mod.rs | 28 ++++- compiler/rustc_middle/src/ty/opaque_types.rs | 103 +++++++++++------- tests/ui/impl-trait/issue-55872-2.rs | 4 +- tests/ui/impl-trait/issue-55872-2.stderr | 4 +- tests/ui/impl-trait/issue-55872.rs | 2 +- tests/ui/impl-trait/issue-55872.stderr | 2 +- .../of-types-hidden-capture.rs | 9 ++ .../of-types-hidden-capture.stderr | 17 +++ .../type-alias-impl-trait/generic_not_used.rs | 2 +- .../generic_not_used.stderr | 2 +- tests/ui/type-alias-impl-trait/issue-53598.rs | 2 +- .../type-alias-impl-trait/issue-53598.stderr | 2 +- 14 files changed, 122 insertions(+), 66 deletions(-) create mode 100644 tests/ui/impl-trait/precise-capturing/of-types-hidden-capture.rs create mode 100644 tests/ui/impl-trait/precise-capturing/of-types-hidden-capture.stderr diff --git a/compiler/rustc_middle/messages.ftl b/compiler/rustc_middle/messages.ftl index 0b3c0be1a4e1a..c7a54fa439d32 100644 --- a/compiler/rustc_middle/messages.ftl +++ b/compiler/rustc_middle/messages.ftl @@ -46,9 +46,6 @@ middle_consider_type_length_limit = middle_const_eval_non_int = constant evaluation of enum discriminant resulted in non-integer -middle_const_not_used_in_type_alias = - const parameter `{$ct}` is part of concrete type but not used in parameter list for the `impl Trait` type alias - middle_deprecated = use of deprecated {$kind} `{$path}`{$has_note -> [true] : {$note} *[other] {""} diff --git a/compiler/rustc_middle/src/error.rs b/compiler/rustc_middle/src/error.rs index bd315577efb5e..f0352480845f2 100644 --- a/compiler/rustc_middle/src/error.rs +++ b/compiler/rustc_middle/src/error.rs @@ -99,14 +99,6 @@ pub(crate) struct RequiresLangItem { pub name: Symbol, } -#[derive(Diagnostic)] -#[diag(middle_const_not_used_in_type_alias)] -pub(super) struct ConstNotUsedTraitAlias { - pub ct: String, - #[primary_span] - pub span: Span, -} - pub struct CustomSubdiagnostic<'a> { pub msg: fn() -> DiagMessage, pub add_args: Box, diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index a508487c796bf..0c93aa852f015 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -823,16 +823,32 @@ impl<'tcx> OpaqueHiddenType<'tcx> { let id_args = GenericArgs::identity_for_item(tcx, def_id); debug!(?id_args); - // This zip may have several times the same lifetime in `args` paired with a different - // lifetime from `id_args`. Simply `collect`ing the iterator is the correct behaviour: - // it will pick the last one, which is the one we introduced in the impl-trait desugaring. - let map = args.iter().zip(id_args).collect(); - debug!("map = {:#?}", map); + let mut mapping = FxHashMap::default(); + let mut uncaptured_args = FxHashSet::default(); + for ((arg, identity_arg), &variance) in + args.iter().zip(id_args).zip(tcx.variances_of(def_id)) + { + match variance { + ty::Invariant => { + mapping.insert(arg, identity_arg); + } + ty::Bivariant => { + uncaptured_args.insert(arg); + } + _ => unreachable!(), + } + } // Convert the type from the function into a type valid outside // the function, by replacing invalid regions with 'static, // after producing an error for each of them. - self.fold_with(&mut opaque_types::ReverseMapper::new(tcx, map, self.span, ignore_errors)) + self.fold_with(&mut opaque_types::ReverseMapper::new( + tcx, + mapping, + uncaptured_args, + self.span, + ignore_errors, + )) } } diff --git a/compiler/rustc_middle/src/ty/opaque_types.rs b/compiler/rustc_middle/src/ty/opaque_types.rs index 56c44c8a84c04..adf8237f8c5c9 100644 --- a/compiler/rustc_middle/src/ty/opaque_types.rs +++ b/compiler/rustc_middle/src/ty/opaque_types.rs @@ -1,9 +1,8 @@ -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_span::Span; use rustc_span::def_id::DefId; use tracing::{debug, instrument, trace}; -use crate::error::ConstNotUsedTraitAlias; use crate::ty::{ self, GenericArg, GenericArgKind, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, }; @@ -15,7 +14,12 @@ pub type OpaqueTypeKey<'tcx> = rustc_type_ir::OpaqueTypeKey>; /// list to the opaque type's own generics. pub(super) struct ReverseMapper<'tcx> { tcx: TyCtxt<'tcx>, - map: FxHashMap, GenericArg<'tcx>>, + + mapping: FxHashMap, GenericArg<'tcx>>, + + /// List of uncaptured args (which are bivariant) + uncaptured_args: FxHashSet>, + /// see call sites to fold_kind_no_missing_regions_error /// for an explanation of this field. do_not_error: bool, @@ -33,11 +37,12 @@ pub(super) struct ReverseMapper<'tcx> { impl<'tcx> ReverseMapper<'tcx> { pub(super) fn new( tcx: TyCtxt<'tcx>, - map: FxHashMap, GenericArg<'tcx>>, + mapping: FxHashMap, GenericArg<'tcx>>, + uncaptured_args: FxHashSet>, span: Span, ignore_errors: bool, ) -> Self { - Self { tcx, map, do_not_error: false, ignore_errors, span } + Self { tcx, mapping, uncaptured_args, do_not_error: false, ignore_errors, span } } fn fold_kind_no_missing_regions_error(&mut self, kind: GenericArg<'tcx>) -> GenericArg<'tcx> { @@ -127,25 +132,29 @@ impl<'tcx> TypeFolder> for ReverseMapper<'tcx> { } } - match self.map.get(&r.into()).map(|k| k.unpack()) { + match self.mapping.get(&r.into()).map(|k| k.unpack()) { Some(GenericArgKind::Lifetime(r1)) => r1, Some(u) => panic!("region mapped to unexpected kind: {u:?}"), - None if self.do_not_error => self.tcx.lifetimes.re_static, None => { - let e = self - .tcx - .dcx() - .struct_span_err(self.span, "non-defining opaque type use in defining scope") - .with_span_label( + let guar = if self.uncaptured_args.contains(&r.into()) { + // FIXME(precise_capturing_of_types): Mention `use<>` list + // and add an structured suggestion. + self.tcx.dcx().struct_span_err( + self.span, + format!("hidden type mentions uncaptured lifetime parameter `{r}`"), + ) + } else { + self.tcx.dcx().struct_span_err( self.span, format!( - "lifetime `{r}` is part of concrete type but not used in \ - parameter list of the `impl Trait` type alias" + "lifetime `{r}` is mentioned in hidden type of type alias impl \ + trait, but is not declared in its generic args" ), ) - .emit(); + } + .emit_unless(self.do_not_error); - ty::Region::new_error(self.cx(), e) + ty::Region::new_error(self.cx(), guar) } } } @@ -169,27 +178,33 @@ impl<'tcx> TypeFolder> for ReverseMapper<'tcx> { ty::Param(param) => { // Look it up in the generic parameters list. - match self.map.get(&ty.into()).map(|k| k.unpack()) { + match self.mapping.get(&ty.into()).map(|k| k.unpack()) { // Found it in the generic parameters list; replace with the parameter from the // opaque type. Some(GenericArgKind::Type(t1)) => t1, Some(u) => panic!("type mapped to unexpected kind: {u:?}"), None => { - debug!(?param, ?self.map); - if !self.ignore_errors { - self.tcx - .dcx() - .struct_span_err( - self.span, - format!( - "type parameter `{ty}` is part of concrete type but not \ - used in parameter list for the `impl Trait` type alias" - ), - ) - .emit(); + debug!(?param, ?self.mapping); + let guar = if self.uncaptured_args.contains(&ty.into()) { + // FIXME(precise_capturing_of_types): Mention `use<>` list + // and add an structured suggestion. + self.tcx.dcx().struct_span_err( + self.span, + format!("hidden type mentions uncaptured type parameter `{ty}`"), + ) + } else { + self.tcx.dcx().struct_span_err( + self.span, + format!( + "type parameter `{ty}` is mentioned in hidden type of \ + type alias impl trait, but is not declared in its generic \ + args" + ), + ) } + .emit_unless(self.ignore_errors); - Ty::new_misc_error(self.tcx) + Ty::new_error(self.tcx, guar) } } } @@ -204,20 +219,30 @@ impl<'tcx> TypeFolder> for ReverseMapper<'tcx> { match ct.kind() { ty::ConstKind::Param(..) => { // Look it up in the generic parameters list. - match self.map.get(&ct.into()).map(|k| k.unpack()) { + match self.mapping.get(&ct.into()).map(|k| k.unpack()) { // Found it in the generic parameters list, replace with the parameter from the // opaque type. Some(GenericArgKind::Const(c1)) => c1, Some(u) => panic!("const mapped to unexpected kind: {u:?}"), None => { - let guar = self - .tcx - .dcx() - .create_err(ConstNotUsedTraitAlias { - ct: ct.to_string(), - span: self.span, - }) - .emit_unless(self.ignore_errors); + let guar = if self.uncaptured_args.contains(&ct.into()) { + // FIXME(precise_capturing_of_types): Mention `use<>` list + // and add an structured suggestion. + self.tcx.dcx().struct_span_err( + self.span, + format!("hidden type mentions uncaptured const parameter `{ct}`"), + ) + } else { + self.tcx.dcx().struct_span_err( + self.span, + format!( + "const parameter `{ct}` is mentioned in hidden type of \ + type alias impl trait, but is not declared in its generic \ + args" + ), + ) + } + .emit_unless(self.ignore_errors); ty::Const::new_error(self.tcx, guar) } diff --git a/tests/ui/impl-trait/issue-55872-2.rs b/tests/ui/impl-trait/issue-55872-2.rs index caca5c69a4aea..0fb00133163c8 100644 --- a/tests/ui/impl-trait/issue-55872-2.rs +++ b/tests/ui/impl-trait/issue-55872-2.rs @@ -12,8 +12,8 @@ impl Bar for S { type E = impl std::marker::Send; fn foo() -> Self::E { async {} - //~^ ERROR type parameter `T` is part of concrete type but not used in parameter list for the `impl Trait` type alias - //~| ERROR type parameter `T` is part of concrete type but not used in parameter list for the `impl Trait` type alias + //~^ ERROR type parameter `T` is mentioned + //~| ERROR type parameter `T` is mentioned } } diff --git a/tests/ui/impl-trait/issue-55872-2.stderr b/tests/ui/impl-trait/issue-55872-2.stderr index b5b7f293a40b2..12d0a4493e32f 100644 --- a/tests/ui/impl-trait/issue-55872-2.stderr +++ b/tests/ui/impl-trait/issue-55872-2.stderr @@ -1,10 +1,10 @@ -error: type parameter `T` is part of concrete type but not used in parameter list for the `impl Trait` type alias +error: type parameter `T` is mentioned in hidden type of type alias impl trait, but is not declared in its generic args --> $DIR/issue-55872-2.rs:14:9 | LL | async {} | ^^^^^^^^ -error: type parameter `T` is part of concrete type but not used in parameter list for the `impl Trait` type alias +error: type parameter `T` is mentioned in hidden type of type alias impl trait, but is not declared in its generic args --> $DIR/issue-55872-2.rs:14:9 | LL | async {} diff --git a/tests/ui/impl-trait/issue-55872.rs b/tests/ui/impl-trait/issue-55872.rs index 10850f0a9335e..d8311ff58a036 100644 --- a/tests/ui/impl-trait/issue-55872.rs +++ b/tests/ui/impl-trait/issue-55872.rs @@ -11,7 +11,7 @@ impl Bar for S { fn foo() -> Self::E { || () - //~^ ERROR type parameter `T` is part of concrete type but not used in parameter list for the `impl Trait` type alias + //~^ ERROR type parameter `T` is mentioned } } diff --git a/tests/ui/impl-trait/issue-55872.stderr b/tests/ui/impl-trait/issue-55872.stderr index 4ff8527bbe9d1..12e8e356b3875 100644 --- a/tests/ui/impl-trait/issue-55872.stderr +++ b/tests/ui/impl-trait/issue-55872.stderr @@ -1,4 +1,4 @@ -error: type parameter `T` is part of concrete type but not used in parameter list for the `impl Trait` type alias +error: type parameter `T` is mentioned in hidden type of type alias impl trait, but is not declared in its generic args --> $DIR/issue-55872.rs:13:9 | LL | || () diff --git a/tests/ui/impl-trait/precise-capturing/of-types-hidden-capture.rs b/tests/ui/impl-trait/precise-capturing/of-types-hidden-capture.rs new file mode 100644 index 0000000000000..bc739c6ec0cd5 --- /dev/null +++ b/tests/ui/impl-trait/precise-capturing/of-types-hidden-capture.rs @@ -0,0 +1,9 @@ +#![feature(precise_capturing_of_types)] +//~^ WARN the feature `precise_capturing_of_types` is incomplete + +fn foo(x: T) -> impl Sized + use<> { + x + //~^ ERROR hidden type mentions uncaptured type parameter `T` +} + +fn main() {} diff --git a/tests/ui/impl-trait/precise-capturing/of-types-hidden-capture.stderr b/tests/ui/impl-trait/precise-capturing/of-types-hidden-capture.stderr new file mode 100644 index 0000000000000..42972423b66d8 --- /dev/null +++ b/tests/ui/impl-trait/precise-capturing/of-types-hidden-capture.stderr @@ -0,0 +1,17 @@ +warning: the feature `precise_capturing_of_types` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/of-types-hidden-capture.rs:1:12 + | +LL | #![feature(precise_capturing_of_types)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #130043 for more information + = note: `#[warn(incomplete_features)]` on by default + +error: hidden type mentions uncaptured type parameter `T` + --> $DIR/of-types-hidden-capture.rs:5:5 + | +LL | x + | ^ + +error: aborting due to 1 previous error; 1 warning emitted + diff --git a/tests/ui/type-alias-impl-trait/generic_not_used.rs b/tests/ui/type-alias-impl-trait/generic_not_used.rs index 6042cdd30a940..ba715af8f5ed3 100644 --- a/tests/ui/type-alias-impl-trait/generic_not_used.rs +++ b/tests/ui/type-alias-impl-trait/generic_not_used.rs @@ -8,5 +8,5 @@ type WrongGeneric = impl 'static; #[define_opaque(WrongGeneric)] fn wrong_generic(_: U, v: V) -> WrongGeneric { v - //~^ ERROR type parameter `V` is part of concrete type but not used in parameter list + //~^ ERROR type parameter `V` is mentioned } diff --git a/tests/ui/type-alias-impl-trait/generic_not_used.stderr b/tests/ui/type-alias-impl-trait/generic_not_used.stderr index 5fe2fefcecfd0..526b5b09a6421 100644 --- a/tests/ui/type-alias-impl-trait/generic_not_used.stderr +++ b/tests/ui/type-alias-impl-trait/generic_not_used.stderr @@ -4,7 +4,7 @@ error: at least one trait must be specified LL | type WrongGeneric = impl 'static; | ^^^^^^^^^^^^ -error: type parameter `V` is part of concrete type but not used in parameter list for the `impl Trait` type alias +error: type parameter `V` is mentioned in hidden type of type alias impl trait, but is not declared in its generic args --> $DIR/generic_not_used.rs:10:5 | LL | v diff --git a/tests/ui/type-alias-impl-trait/issue-53598.rs b/tests/ui/type-alias-impl-trait/issue-53598.rs index e3e2787b66bb4..2d9605e7e8bc9 100644 --- a/tests/ui/type-alias-impl-trait/issue-53598.rs +++ b/tests/ui/type-alias-impl-trait/issue-53598.rs @@ -18,7 +18,7 @@ impl Foo for S2 { fn foo(_: T) -> Self::Item { S::(Default::default()) - //~^ Error type parameter `T` is part of concrete type but not used in parameter list for the `impl Trait` type alias + //~^ Error type parameter `T` is mentioned } } diff --git a/tests/ui/type-alias-impl-trait/issue-53598.stderr b/tests/ui/type-alias-impl-trait/issue-53598.stderr index a31aabedba542..0ddae22ef4cdb 100644 --- a/tests/ui/type-alias-impl-trait/issue-53598.stderr +++ b/tests/ui/type-alias-impl-trait/issue-53598.stderr @@ -1,4 +1,4 @@ -error: type parameter `T` is part of concrete type but not used in parameter list for the `impl Trait` type alias +error: type parameter `T` is mentioned in hidden type of type alias impl trait, but is not declared in its generic args --> $DIR/issue-53598.rs:20:9 | LL | S::(Default::default()) From 233dcff4239b4c3d65220028ece2c7b75fe8d457 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 14 Mar 2025 00:46:37 +0000 Subject: [PATCH 3/3] More tests --- .../precise-capturing/of-types-eq-uncaptured.rs | 11 +++++++++++ .../of-types-eq-uncaptured.stderr | 11 +++++++++++ .../precise-capturing/of-types-outlives.rs | 14 ++++++++++++++ .../precise-capturing/of-types-outlives.stderr | 11 +++++++++++ 4 files changed, 47 insertions(+) create mode 100644 tests/ui/impl-trait/precise-capturing/of-types-eq-uncaptured.rs create mode 100644 tests/ui/impl-trait/precise-capturing/of-types-eq-uncaptured.stderr create mode 100644 tests/ui/impl-trait/precise-capturing/of-types-outlives.rs create mode 100644 tests/ui/impl-trait/precise-capturing/of-types-outlives.stderr diff --git a/tests/ui/impl-trait/precise-capturing/of-types-eq-uncaptured.rs b/tests/ui/impl-trait/precise-capturing/of-types-eq-uncaptured.rs new file mode 100644 index 0000000000000..36d4f952ce901 --- /dev/null +++ b/tests/ui/impl-trait/precise-capturing/of-types-eq-uncaptured.rs @@ -0,0 +1,11 @@ +//@ check-pass + +#![feature(precise_capturing_of_types)] +//~^ WARN the feature `precise_capturing_of_types` is incomplete + +fn uncaptured() -> impl Sized + use<> {} + +fn main() { + let mut x = uncaptured::(); + x = uncaptured::(); +} diff --git a/tests/ui/impl-trait/precise-capturing/of-types-eq-uncaptured.stderr b/tests/ui/impl-trait/precise-capturing/of-types-eq-uncaptured.stderr new file mode 100644 index 0000000000000..10a2e5c2122e2 --- /dev/null +++ b/tests/ui/impl-trait/precise-capturing/of-types-eq-uncaptured.stderr @@ -0,0 +1,11 @@ +warning: the feature `precise_capturing_of_types` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/of-types-eq-uncaptured.rs:3:12 + | +LL | #![feature(precise_capturing_of_types)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #130043 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/impl-trait/precise-capturing/of-types-outlives.rs b/tests/ui/impl-trait/precise-capturing/of-types-outlives.rs new file mode 100644 index 0000000000000..44e0ac0a41c38 --- /dev/null +++ b/tests/ui/impl-trait/precise-capturing/of-types-outlives.rs @@ -0,0 +1,14 @@ +//@ check-pass + +#![feature(precise_capturing_of_types)] +//~^ WARN the feature `precise_capturing_of_types` is incomplete + +fn uncaptured() -> impl Sized + use<> {} + +fn outlives_static(_: T) {} + +fn foo<'a>() { + outlives_static(uncaptured::<&'a ()>()); +} + +fn main() {} diff --git a/tests/ui/impl-trait/precise-capturing/of-types-outlives.stderr b/tests/ui/impl-trait/precise-capturing/of-types-outlives.stderr new file mode 100644 index 0000000000000..e3ee0d93bb8a8 --- /dev/null +++ b/tests/ui/impl-trait/precise-capturing/of-types-outlives.stderr @@ -0,0 +1,11 @@ +warning: the feature `precise_capturing_of_types` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/of-types-outlives.rs:3:12 + | +LL | #![feature(precise_capturing_of_types)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #130043 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted +