From 4b7db5a8141ee25fb89185423783fbf78e661ec7 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] Reject escaping generic params in the type of assoc const bindings --- compiler/rustc_hir_analysis/messages.ftl | 18 +++ .../rustc_hir_analysis/src/collect/type_of.rs | 105 +++++++++++++++++- compiler/rustc_hir_analysis/src/errors.rs | 23 ++++ .../assoc-const-eq-param-in-ty.rs | 55 +++++++++ .../assoc-const-eq-param-in-ty.stderr | 76 +++++++++++++ 5 files changed, 273 insertions(+), 4 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 f32b14aca23fd..2586886fd47c4 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -314,6 +314,22 @@ 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_binding = + the type of the associated constant `{$assoc_const}` must not depend on {$param_category -> + [self] `Self` + [synthetic] `impl Trait` + *[normal] generic parameters + } + .label = its type must not depend on {$param_category -> + [self] `Self` + [synthetic] `impl Trait` + *[normal] the {$param_def_kind} `{$param_name}` + } + .param_defined_here_label = {$param_category -> + [synthetic] the `impl Trait` is specified here + *[normal] the {$param_def_kind} `{$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 @@ -424,6 +440,8 @@ hir_analysis_transparent_non_zero_sized_enum = the variant of a transparent {$de .label = needs at most one field with non-trivial size or alignment, but has {$field_count} .labels = this field has non-zero size or requires alignment +hir_analysis_ty_of_assoc_const_binding_note = `{$assoc_const}` has type `{$ty}` + hir_analysis_ty_param_first_local = type parameter `{$param_ty}` must be covered by another type when it appears before the first local type (`{$local_type}`) .label = type parameter `{$param_ty}` must be covered by another type when it appears before the first local type (`{$local_type}`) .note = implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index 57bcf7602eaa4..b7ebffd820672 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -1,3 +1,6 @@ +use std::ops::ControlFlow; + +use rustc_data_structures::fx::FxIndexSet; use rustc_errors::{Applicability, StashKey}; use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; @@ -7,7 +10,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 crate::errors::TypeofReservedKeywordUsed; @@ -78,10 +82,23 @@ 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, .. }) => { - // FIXME(fmease): Reject “escaping” early-bound generic parameters. + Node::TypeBinding(&TypeBinding { hir_id, ident, .. }) => { + let ty = tcx.type_of_assoc_const_binding(hir_id); + + // We can't possibly catch this in the resolver, therefore we need to handle it here. + // FIXME(const_generics): Support generic const generics. + let Some(ty) = ty.no_bound_vars() else { + let reported = report_overly_generic_assoc_const_binding_type( + tcx, + ident, + ty.skip_binder().skip_binder(), + hir_id, + ); + return Ty::new_error(tcx, reported); + }; + // FIXME(fmease): Reject escaping late-bound vars. - return tcx.type_of_assoc_const_binding(hir_id).skip_binder().skip_binder(); + return ty.skip_binder(); } // This match arm is for when the def_id appears in a GAT whose @@ -302,6 +319,86 @@ pub(super) fn type_of_assoc_const_binding<'tcx>( ty::EarlyBinder::bind(ty::Binder::dummy(Ty::new_error(tcx, reported))) } +fn report_overly_generic_assoc_const_binding_type<'tcx>( + tcx: TyCtxt<'tcx>, + assoc_const: Ident, + ty: Ty<'tcx>, + hir_id: HirId, +) -> ErrorGuaranteed { + let mut collector = GenericParamCollector { params: Default::default() }; + ty.visit_with(&mut collector); + + let mut reported = None; + + let ty_note = ty + .make_suggestable(tcx, false) + .map(|ty| crate::errors::TyOfAssocConstBindingNote { assoc_const, ty }); + + // FIXME(fmease): Consider delaying a bug instead of unwrapping since maybe this could trigger + // if we encountered some malformed code, dunno? + let enclosing_item_owner_id = tcx + .hir() + .parent_owner_iter(hir_id) + .find_map(|(owner_id, parent)| parent.generics().map(|_| owner_id)) + .unwrap(); + let generics = tcx.generics_of(enclosing_item_owner_id); + for (param_index, param_name) in collector.params { + let param_def = generics.param_at(param_index as _, tcx); + let is_self_param = param_def.name == rustc_span::symbol::kw::SelfUpper; + reported.get_or_insert(tcx.dcx().emit_err(crate::errors::ParamInTyOfAssocConstBinding { + span: assoc_const.span, + assoc_const, + param_name, + param_def_kind: tcx.def_descr(param_def.def_id), + // FIXME(fmease): Yeah, I don't like this stringly-typed stuff either. + param_category: if is_self_param { + "self" + } else if param_def.kind.is_synthetic() { + "synthetic" + } else { + "normal" + }, + param_defined_here_label: + (!is_self_param).then(|| tcx.def_ident_span(param_def.def_id).unwrap()), + ty_note, + })); + } + + struct GenericParamCollector { + params: FxIndexSet<(u32, Symbol)>, + } + + impl<'tcx> TypeVisitor> for GenericParamCollector { + type BreakTy = !; + + fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow { + if let ty::Param(param) = ty.kind() { + self.params.insert((param.index, param.name)); + return ControlFlow::Continue(()); + } + ty.super_visit_with(self) + } + + fn visit_region(&mut self, re: ty::Region<'tcx>) -> ControlFlow { + if let ty::ReEarlyParam(param) = re.kind() { + self.params.insert((param.index, param.name)); + return ControlFlow::Continue(()); + } + ControlFlow::Continue(()) + } + + fn visit_const(&mut self, ct: ty::Const<'tcx>) -> ControlFlow { + if let ty::ConstKind::Param(param) = ct.kind() { + self.params.insert((param.index, param.name)); + return ControlFlow::Continue(()); + } + ct.super_visit_with(self) + } + } + + reported.unwrap_or_else(|| bug!("failed to find gen params in ty")) +} + 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 3bd7687a54425..f730fb69fb9c4 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -295,6 +295,29 @@ pub struct AssocTypeBindingNotAllowed { pub fn_trait_expansion: Option, } +#[derive(Diagnostic)] +#[diag(hir_analysis_param_in_ty_of_assoc_const_binding)] +pub(crate) struct ParamInTyOfAssocConstBinding<'tcx> { + #[primary_span] + #[label] + pub span: Span, + pub assoc_const: Ident, + pub param_name: Symbol, + pub param_def_kind: &'static str, + pub param_category: &'static str, + #[label(hir_analysis_param_defined_here_label)] + pub param_defined_here_label: Option, + #[subdiagnostic] + pub ty_note: Option>, +} + +#[derive(Subdiagnostic, Clone, Copy)] +#[note(hir_analysis_ty_of_assoc_const_binding_note)] +pub(crate) struct TyOfAssocConstBindingNote<'tcx> { + pub assoc_const: Ident, + pub ty: Ty<'tcx>, +} + #[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..aaf16181030b8 --- /dev/null +++ b/tests/ui/associated-consts/assoc-const-eq-param-in-ty.rs @@ -0,0 +1,55 @@ +// 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: 'a, const N: usize> { + const K: &'a [T; N]; +} + +fn take0<'r, A: 'r, const Q: usize>(_: impl Trait<'r, A, Q, K = { loop {} }>) {} +//~^ 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 +//~| NOTE `K` has type `&'r [A; Q]` +//~| 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 +//~| NOTE `K` has type `&'r [A; Q]` +//~| 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 +//~| NOTE `K` has type `&'r [A; Q]` + +trait Project { + const SELF: Self; +} + +fn take1(_: impl Project) {} +//~^ ERROR the type of the associated constant `SELF` must not depend on `impl Trait` +//~| NOTE its type must not depend on `impl Trait` +//~| NOTE the `impl Trait` is specified here + +fn take2>(_: P) {} +//~^ ERROR the type of the associated constant `SELF` 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 +//~| NOTE `SELF` has type `P` + +trait Iface<'r> { + //~^ NOTE the lifetime parameter `'r` is defined here + type Assoc: Trait<'r, Self, Q, K = { loop {} }> + //~^ 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 `K` has type `&'r [Self; Q]` + //~| ERROR the type of the associated constant `K` must not depend on `Self` + //~| NOTE its type must not depend on `Self` + //~| NOTE `K` has type `&'r [Self; Q]` + //~| 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 + //~| NOTE `K` has type `&'r [Self; Q]` + where + Self: Sized + 'r; +} + +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..077ac6e7f9358 --- /dev/null +++ b/tests/ui/associated-consts/assoc-const-eq-param-in-ty.stderr @@ -0,0 +1,76 @@ +error: the type of the associated constant `K` must not depend on generic parameters + --> $DIR/assoc-const-eq-param-in-ty.rs:9:61 + | +LL | fn take0<'r, A: 'r, const Q: usize>(_: impl Trait<'r, A, Q, K = { loop {} }>) {} + | -- the lifetime parameter `'r` is defined here ^ its type must not depend on the lifetime parameter `'r` + | + = note: `K` has type `&'r [A; Q]` + +error: the type of the associated constant `K` must not depend on generic parameters + --> $DIR/assoc-const-eq-param-in-ty.rs:9:61 + | +LL | fn take0<'r, A: 'r, const Q: usize>(_: impl Trait<'r, A, Q, K = { loop {} }>) {} + | - the type parameter `A` is defined here ^ its type must not depend on the type parameter `A` + | + = note: `K` has type `&'r [A; Q]` + +error: the type of the associated constant `K` must not depend on generic parameters + --> $DIR/assoc-const-eq-param-in-ty.rs:9:61 + | +LL | fn take0<'r, A: 'r, const Q: usize>(_: impl Trait<'r, A, Q, K = { loop {} }>) {} + | - ^ its type must not depend on the const parameter `Q` + | | + | the const parameter `Q` is defined here + | + = note: `K` has type `&'r [A; Q]` + +error: the type of the associated constant `SELF` must not depend on `impl Trait` + --> $DIR/assoc-const-eq-param-in-ty.rs:27:26 + | +LL | fn take1(_: impl Project) {} + | -------------^^^^------ + | | | + | | its type must not depend on `impl Trait` + | the `impl Trait` is specified here + +error: the type of the associated constant `SELF` must not depend on generic parameters + --> $DIR/assoc-const-eq-param-in-ty.rs:32:21 + | +LL | fn take2>(_: P) {} + | - ^^^^ its type must not depend on the type parameter `P` + | | + | the type parameter `P` is defined here + | + = note: `SELF` has type `P` + +error: the type of the associated constant `K` must not depend on generic parameters + --> $DIR/assoc-const-eq-param-in-ty.rs:40:52 + | +LL | trait Iface<'r> { + | -- the lifetime parameter `'r` is defined here +LL | +LL | type Assoc: Trait<'r, Self, Q, K = { loop {} }> + | ^ its type must not depend on the lifetime parameter `'r` + | + = note: `K` has type `&'r [Self; Q]` + +error: the type of the associated constant `K` must not depend on `Self` + --> $DIR/assoc-const-eq-param-in-ty.rs:40:52 + | +LL | type Assoc: Trait<'r, Self, Q, K = { loop {} }> + | ^ its type must not depend on `Self` + | + = note: `K` has type `&'r [Self; Q]` + +error: the type of the associated constant `K` must not depend on generic parameters + --> $DIR/assoc-const-eq-param-in-ty.rs:40:52 + | +LL | type Assoc: Trait<'r, Self, Q, K = { loop {} }> + | - ^ its type must not depend on the const parameter `Q` + | | + | the const parameter `Q` is defined here + | + = note: `K` has type `&'r [Self; Q]` + +error: aborting due to 8 previous errors +