Skip to content

Commit

Permalink
Reject gen params in the ty of assoc consts in eq bounds
Browse files Browse the repository at this point in the history
  • Loading branch information
fmease committed Nov 27, 2023
1 parent 18437e3 commit 94d7033
Show file tree
Hide file tree
Showing 5 changed files with 180 additions and 6 deletions.
4 changes: 4 additions & 0 deletions compiler/rustc_hir_analysis/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,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
Expand Down
93 changes: 87 additions & 6 deletions compiler/rustc_hir_analysis/src/collect/type_of.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand All @@ -7,8 +9,9 @@ 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_trait_selection::traits;
use rustc_type_ir::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor};

use super::{bad_placeholder, is_suggestable_infer_ty};
use super::{AstConv, ItemCtxt};
Expand Down Expand Up @@ -72,10 +75,11 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> {
);
};

// FIXME(associated_const_equality): Doing all this work *here* isn't great.
// Ideally, we would've computed this already somewhere else in a query.
// FIXME(associated_const_equality): We're now performing a full but ad-hoc type-based
// resolution of the associated constant. Doing all this work *here* isn't great.
// Ideally, we would've computed this already somewhere else (in a query?).

let icx = ItemCtxt::new(tcx, trait_def_id.expect_local());
let icx = ItemCtxt::new(tcx, def_id);
let trait_segment = trait_ref.path.segments.last().unwrap();
let (trait_args, _) = icx.astconv().create_args_for_ast_path(
trait_ref.path.span,
Expand All @@ -87,6 +91,8 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> {
// FIXME(associated_const_equality): This isn't correct, it should be the concrete /
// instantiated self type. Theoretically, we could search for it in the HIR of the
// parent item but that's super fragile and hairy.
// If the projected type ends up containing this `Self` parameter, we will reject it
// below, so not much harm done.
Some(tcx.types.self_param),
ty::BoundConstness::NotConst,
);
Expand Down Expand Up @@ -124,14 +130,23 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> {
&hir::PathSegment {
ident,
hir_id,
res: def::Res::Err,
res: def::Res::Def(def::DefKind::AssocConst, assoc_item.def_id),
args: Some(gen_args),
infer_args: false,
},
parent_args,
);

return tcx.type_of(assoc_item.def_id).instantiate(tcx, args);
let ty = tcx.type_of(assoc_item.def_id).instantiate(tcx, args);

// FIXME(const_generics): Support generic const generics.
if ty.has_param() {
// We can't 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
Expand Down Expand Up @@ -339,6 +354,72 @@ 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!()
};

// FIXME(associated_const_equality): Since we use a `Self` type parameter as a hack in
// `anon_const_type_of`, we can't be sure where `Self` comes from: It may come from the
// def site or the usage site of the assoc const. Therefore, don't try to find its definition.
let (param_kind, param_def_span) = if param_name != rustc_span::symbol::kw::SelfUpper {
let body_owner = tcx.hir().enclosing_body_owner(hir_id);
let param_def = tcx.generics_of(body_owner).param_at(param_index as _, tcx);
(&param_def.kind, Some(tcx.def_ident_span(param_def.def_id).unwrap()))
} else {
(&ty::GenericParamDefKind::Type { has_default: false, synthetic: false }, None)
};

struct GenericParamFinder;

impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for GenericParamFinder {
type BreakTy = (u32, Symbol);

fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
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<Self::BreakTy> {
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<Self::BreakTy> {
if let ty::ConstKind::Param(param) = ct.kind() {
return ControlFlow::Break((param.index, param.name));
}

ct.super_visit_with(self)
}
}

tcx.sess.emit_err(crate::errors::ParamInTyOfAssocConst {
span: assoc_const.span,
assoc_const,
param_name,
param_kind: match param_kind {
ty::GenericParamDefKind::Lifetime => "lifetime",
ty::GenericParamDefKind::Type { .. } => "type",
ty::GenericParamDefKind::Const { .. } => "const",
},
param_defined_here_label: param_def_span,
})
}

fn get_path_containing_arg_in_pat<'hir>(
pat: &'hir hir::Pat<'hir>,
arg_id: HirId,
Expand Down
13 changes: 13 additions & 0 deletions compiler/rustc_hir_analysis/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,19 @@ pub struct AssocTypeBindingNotAllowed {
pub fn_trait_expansion: Option<ParenthesizedFnTraitExpansion>,
}

#[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: Option<Span>,
}

#[derive(Subdiagnostic)]
#[help(hir_analysis_parenthesized_fn_trait_expansion)]
pub struct ParenthesizedFnTraitExpansion {
Expand Down
38 changes: 38 additions & 0 deletions tests/ui/associated-consts/assoc-const-eq-param-in-ty.rs
Original file line number Diff line number Diff line change
@@ -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<A>(_: 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<const Q: usize>(_: 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(associated_const_equality): The error messages below aren't super great at the moment:
// Here, `Self` is a type parameter of the trait `Project`, not of the function `take3`
// unlike the cases above. We should mention the APIT / the parameter `P` instead.

fn take3(_: impl Project<S = {}>) {}
//~^ ERROR the type of the associated constant `S` must not depend on generic parameters
//~| NOTE its type must not depend on the type parameter `Self`

fn take4<P: Project<S = {}>>(_: 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 `Self`

fn main() {}
38 changes: 38 additions & 0 deletions tests/ui/associated-consts/assoc-const-eq-param-in-ty.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
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<A>(_: 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<const Q: usize>(_: 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:30:26
|
LL | fn take3(_: impl Project<S = {}>) {}
| ^ its type must not depend on the type parameter `Self`

error: the type of the associated constant `S` must not depend on generic parameters
--> $DIR/assoc-const-eq-param-in-ty.rs:34:21
|
LL | fn take4<P: Project<S = {}>>(_: P) {}
| ^ its type must not depend on the type parameter `Self`

error: aborting due to 5 previous errors

0 comments on commit 94d7033

Please sign in to comment.