From 299d62f138246d16ce303323c9573ec1c6907bde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Thu, 28 Dec 2023 18:51:53 +0100 Subject: [PATCH] [detect escaping gen params] --- compiler/rustc_hir_analysis/messages.ftl | 4 + .../rustc_hir_analysis/src/collect/type_of.rs | 75 ++++++++++++++++++- compiler/rustc_hir_analysis/src/errors.rs | 13 ++++ .../assoc-const-eq-param-in-ty.rs | 38 ++++++++++ .../assoc-const-eq-param-in-ty.stderr | 43 +++++++++++ 5 files changed, 170 insertions(+), 3 deletions(-) create mode 100644 tests/ui/associated-consts/assoc-const-eq-param-in-ty.rs create mode 100644 tests/ui/associated-consts/assoc-const-eq-param-in-ty.stderr diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index 139e1c0ac5fdc..5136ff1e2a663 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -285,6 +285,10 @@ hir_analysis_only_current_traits_primitive = only traits defined in the current hir_analysis_only_current_traits_ty = `{$ty}` is not defined in the current crate +hir_analysis_param_in_ty_of_assoc_const = the type of the associated constant `{$assoc_const}` must not depend on generic parameters + .label = its type must not depend on the {$param_kind} parameter `{$param_name}` + .param_defined_here_label = the {$param_kind} parameter `{$param_name}` is defined here + 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 diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index fc8d58aad99c0..163abef5f232a 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -1,3 +1,5 @@ +use std::ops::ControlFlow; + use rustc_errors::{Applicability, StashKey}; use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; @@ -7,7 +9,8 @@ use rustc_middle::ty::print::with_forced_trimmed_paths; use rustc_middle::ty::util::IntTypeExt; use rustc_middle::ty::{self, ImplTraitInTraitData, IsSuggestable, Ty, TyCtxt, TypeVisitableExt}; use rustc_span::symbol::Ident; -use rustc_span::{Span, DUMMY_SP}; +use rustc_span::{ErrorGuaranteed, Span, Symbol, DUMMY_SP}; +use rustc_type_ir::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor}; use super::ItemCtxt; use super::{bad_placeholder, is_suggestable_infer_ty}; @@ -66,8 +69,17 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> { .expect("const parameter types cannot be generic"); } - Node::TypeBinding(&TypeBinding { hir_id, .. }) => { - return tcx.type_of_assoc_const_binding(hir_id); + Node::TypeBinding(&TypeBinding { hir_id, ident, .. }) => { + let ty = tcx.type_of_assoc_const_binding(hir_id); + + // FIXME(const_generics): Support generic const generics. + if ty.has_param() { + // We can't possibly catch this in the resolver, therefore we need to handle it here. + let reported = report_unsupported_generic_associated_const(tcx, ident, ty, hir_id); + return Ty::new_error(tcx, reported); + } + + return ty; } // This match arm is for when the def_id appears in a GAT whose @@ -276,6 +288,63 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> { } } +fn report_unsupported_generic_associated_const<'tcx>( + tcx: TyCtxt<'tcx>, + assoc_const: Ident, + ty: Ty<'tcx>, + hir_id: HirId, +) -> ErrorGuaranteed { + // Just find the first generic parameter. This should be sufficient in practice. + let ControlFlow::Break((param_index, param_name)) = ty.visit_with(&mut GenericParamFinder) + else { + bug!() + }; + + let body_owner = tcx.hir().enclosing_body_owner(hir_id); + let param_def = tcx.generics_of(body_owner).param_at(param_index as _, tcx); + struct GenericParamFinder; + + impl<'tcx> TypeVisitor> for GenericParamFinder { + type BreakTy = (u32, Symbol); + + fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow { + if let ty::Param(param) = ty.kind() { + return ControlFlow::Break((param.index, param.name)); + } + + ty.super_visit_with(self) + } + + fn visit_region(&mut self, re: ty::Region<'tcx>) -> ControlFlow { + if let ty::ReEarlyParam(param) = re.kind() { + return ControlFlow::Break((param.index, param.name)); + } + + ControlFlow::Continue(()) + } + + fn visit_const(&mut self, ct: ty::Const<'tcx>) -> ControlFlow { + if let ty::ConstKind::Param(param) = ct.kind() { + return ControlFlow::Break((param.index, param.name)); + } + + ct.super_visit_with(self) + } + } + + tcx.dcx().emit_err(crate::errors::ParamInTyOfAssocConst { + span: assoc_const.span, + assoc_const, + param_name, + param_kind: match ¶m_def.kind { + ty::GenericParamDefKind::Lifetime => "lifetime", + ty::GenericParamDefKind::Type { .. } => "type", + ty::GenericParamDefKind::Const { .. } => "const", + }, + param_defined_here_label: tcx.def_ident_span(param_def.def_id).unwrap(), + }) +} + fn get_path_containing_arg_in_pat<'hir>( pat: &'hir hir::Pat<'hir>, arg_id: HirId, diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 41f30057902b6..1adabf0854376 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -254,6 +254,19 @@ pub struct AssocTypeBindingNotAllowed { pub fn_trait_expansion: Option, } +#[derive(Diagnostic)] +#[diag(hir_analysis_param_in_ty_of_assoc_const)] +pub(crate) struct ParamInTyOfAssocConst { + #[primary_span] + #[label] + pub span: Span, + pub assoc_const: Ident, + pub param_name: Symbol, + pub param_kind: &'static str, + #[label(hir_analysis_param_defined_here_label)] + pub param_defined_here_label: Span, +} + #[derive(Subdiagnostic)] #[help(hir_analysis_parenthesized_fn_trait_expansion)] pub struct ParenthesizedFnTraitExpansion { diff --git a/tests/ui/associated-consts/assoc-const-eq-param-in-ty.rs b/tests/ui/associated-consts/assoc-const-eq-param-in-ty.rs new file mode 100644 index 0000000000000..9f44e3423df13 --- /dev/null +++ b/tests/ui/associated-consts/assoc-const-eq-param-in-ty.rs @@ -0,0 +1,38 @@ +// Regression test for issue #108271. +// Detect and reject generic params in the type of assoc consts used in an equality bound. +#![feature(associated_const_equality)] + +trait Trait<'a, T, const N: usize> { + const K: &'a [T; N]; +} + +fn take0<'r>(_: impl Trait<'r, (), 0, K = { &[] }>) {} +//~^ ERROR the type of the associated constant `K` must not depend on generic parameters +//~| NOTE its type must not depend on the lifetime parameter `'r` +//~| NOTE the lifetime parameter `'r` is defined here +fn take1(_: impl Trait<'static, A, 0, K = { &[] }>) {} +//~^ ERROR the type of the associated constant `K` must not depend on generic parameters +//~| NOTE its type must not depend on the type parameter `A` +//~| NOTE the type parameter `A` is defined here +fn take2(_: impl Trait<'static, (), Q, K = { [] }>) {} +//~^ ERROR the type of the associated constant `K` must not depend on generic parameters +//~| NOTE its type must not depend on the const parameter `Q` +//~| NOTE the const parameter `Q` is defined here + +trait Project { + const S: Self; +} + +// FIXME(fmease): Clean this up, treat synthetic type parameters differently in diags. + +fn take3(_: impl Project) {} +//~^ ERROR the type of the associated constant `S` must not depend on generic parameters +//~| NOTE its type must not depend on the type parameter `impl Project` +//~| NOTE the type parameter `impl Project` is defined here + +fn take4>(_: P) {} +//~^ ERROR the type of the associated constant `S` must not depend on generic parameters +//~| NOTE its type must not depend on the type parameter `P` +//~| NOTE the type parameter `P` is defined here + +fn main() {} diff --git a/tests/ui/associated-consts/assoc-const-eq-param-in-ty.stderr b/tests/ui/associated-consts/assoc-const-eq-param-in-ty.stderr new file mode 100644 index 0000000000000..86c4a10b9530e --- /dev/null +++ b/tests/ui/associated-consts/assoc-const-eq-param-in-ty.stderr @@ -0,0 +1,43 @@ +error: the type of the associated constant `K` must not depend on generic parameters + --> $DIR/assoc-const-eq-param-in-ty.rs:9:39 + | +LL | fn take0<'r>(_: impl Trait<'r, (), 0, K = { &[] }>) {} + | -- ^ its type must not depend on the lifetime parameter `'r` + | | + | the lifetime parameter `'r` is defined here + +error: the type of the associated constant `K` must not depend on generic parameters + --> $DIR/assoc-const-eq-param-in-ty.rs:13:42 + | +LL | fn take1(_: impl Trait<'static, A, 0, K = { &[] }>) {} + | - ^ its type must not depend on the type parameter `A` + | | + | the type parameter `A` is defined here + +error: the type of the associated constant `K` must not depend on generic parameters + --> $DIR/assoc-const-eq-param-in-ty.rs:17:56 + | +LL | fn take2(_: impl Trait<'static, (), Q, K = { [] }>) {} + | - ^ its type must not depend on the const parameter `Q` + | | + | the const parameter `Q` is defined here + +error: the type of the associated constant `S` must not depend on generic parameters + --> $DIR/assoc-const-eq-param-in-ty.rs:28:26 + | +LL | fn take3(_: impl Project) {} + | -------------^------ + | | | + | | its type must not depend on the type parameter `impl Project` + | the type parameter `impl Project` is defined here + +error: the type of the associated constant `S` must not depend on generic parameters + --> $DIR/assoc-const-eq-param-in-ty.rs:33:21 + | +LL | fn take4>(_: P) {} + | - ^ its type must not depend on the type parameter `P` + | | + | the type parameter `P` is defined here + +error: aborting due to 5 previous errors +