Skip to content

Commit 8ce12cd

Browse files
committed
[detect escaping gen params]
1 parent dc18547 commit 8ce12cd

File tree

5 files changed

+177
-4
lines changed

5 files changed

+177
-4
lines changed

compiler/rustc_hir_analysis/messages.ftl

+4
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,10 @@ hir_analysis_only_current_traits_primitive = only traits defined in the current
285285
286286
hir_analysis_only_current_traits_ty = `{$ty}` is not defined in the current crate
287287
288+
hir_analysis_param_in_ty_of_assoc_const = the type of the associated constant `{$assoc_const}` must not depend on generic parameters
289+
.label = its type must not depend on the {$param_kind} parameter `{$param_name}`
290+
.param_defined_here_label = the {$param_kind} parameter `{$param_name}` is defined here
291+
288292
hir_analysis_paren_sugar_attribute = the `#[rustc_paren_sugar]` attribute is a temporary means of controlling which traits can use parenthetical notation
289293
.help = add `#![feature(unboxed_closures)]` to the crate attributes to use it
290294

compiler/rustc_hir_analysis/src/collect/type_of.rs

+79-4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use std::ops::ControlFlow;
2+
13
use rustc_errors::{Applicability, StashKey};
24
use rustc_hir as hir;
35
use rustc_hir::def_id::{DefId, LocalDefId};
@@ -7,7 +9,8 @@ use rustc_middle::ty::print::with_forced_trimmed_paths;
79
use rustc_middle::ty::util::IntTypeExt;
810
use rustc_middle::ty::{self, ImplTraitInTraitData, IsSuggestable, Ty, TyCtxt, TypeVisitableExt};
911
use rustc_span::symbol::Ident;
10-
use rustc_span::{Span, DUMMY_SP};
12+
use rustc_span::{ErrorGuaranteed, Span, Symbol, DUMMY_SP};
13+
use rustc_type_ir::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor};
1114

1215
use super::ItemCtxt;
1316
use super::{bad_placeholder, is_suggestable_infer_ty};
@@ -66,10 +69,24 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> {
6669
.expect("const parameter types cannot be generic");
6770
}
6871

69-
Node::TypeBinding(&TypeBinding { hir_id, .. }) => {
70-
// FIXME(fmease): Reject “escaping” early-bound generic parameters.
72+
Node::TypeBinding(&TypeBinding { hir_id, ident, .. }) => {
73+
let ty = tcx.type_of_assoc_const_binding(hir_id);
74+
75+
// FIXME(const_generics): Support generic const generics.
76+
let Some(ty) = ty.no_bound_vars() else {
77+
// We can't possibly catch this in the resolver, therefore we need to handle it here.
78+
let guar = report_assoc_const_depends_on_generic_params(
79+
tcx,
80+
ident,
81+
// FIXME(fmease): Improve this situation:
82+
ty.skip_binder().skip_binder(),
83+
hir_id,
84+
);
85+
return Ty::new_error(tcx, guar);
86+
};
87+
7188
// FIXME(fmease): Reject escaping late-bound vars.
72-
return tcx.type_of_assoc_const_binding(hir_id).skip_binder().skip_binder();
89+
return ty.skip_binder();
7390
}
7491

7592
// This match arm is for when the def_id appears in a GAT whose
@@ -293,6 +310,64 @@ pub(super) fn type_of_assoc_const_binding<'tcx>(
293310
ty::EarlyBinder::bind(ty::Binder::dummy(Ty::new_error(tcx, guar)))
294311
}
295312

313+
fn report_assoc_const_depends_on_generic_params<'tcx>(
314+
tcx: TyCtxt<'tcx>,
315+
assoc_const: Ident,
316+
ty: Ty<'tcx>,
317+
hir_id: HirId,
318+
) -> ErrorGuaranteed {
319+
// FIXME(fmease): Find all generic params instead of just the first, it's cleaner.
320+
let ControlFlow::Break((param_index, param_name)) = ty.visit_with(&mut GenericParamFinder)
321+
else {
322+
// FIXME(fmease): Proper ICE message
323+
bug!()
324+
};
325+
326+
let body_owner = tcx.hir().enclosing_body_owner(hir_id);
327+
let param_def = tcx.generics_of(body_owner).param_at(param_index as _, tcx);
328+
struct GenericParamFinder;
329+
330+
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for GenericParamFinder {
331+
type BreakTy = (u32, Symbol);
332+
333+
fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
334+
if let ty::Param(param) = ty.kind() {
335+
return ControlFlow::Break((param.index, param.name));
336+
}
337+
338+
ty.super_visit_with(self)
339+
}
340+
341+
fn visit_region(&mut self, re: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
342+
if let ty::ReEarlyParam(param) = re.kind() {
343+
return ControlFlow::Break((param.index, param.name));
344+
}
345+
346+
ControlFlow::Continue(())
347+
}
348+
349+
fn visit_const(&mut self, ct: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
350+
if let ty::ConstKind::Param(param) = ct.kind() {
351+
return ControlFlow::Break((param.index, param.name));
352+
}
353+
354+
ct.super_visit_with(self)
355+
}
356+
}
357+
358+
tcx.dcx().emit_err(crate::errors::ParamInTyOfAssocConst {
359+
span: assoc_const.span,
360+
assoc_const,
361+
param_name,
362+
param_kind: match &param_def.kind {
363+
ty::GenericParamDefKind::Lifetime => "lifetime",
364+
ty::GenericParamDefKind::Type { .. } => "type",
365+
ty::GenericParamDefKind::Const { .. } => "const",
366+
},
367+
param_defined_here_label: tcx.def_ident_span(param_def.def_id).unwrap(),
368+
})
369+
}
370+
296371
fn get_path_containing_arg_in_pat<'hir>(
297372
pat: &'hir hir::Pat<'hir>,
298373
arg_id: HirId,

compiler/rustc_hir_analysis/src/errors.rs

+13
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,19 @@ pub struct AssocTypeBindingNotAllowed {
254254
pub fn_trait_expansion: Option<ParenthesizedFnTraitExpansion>,
255255
}
256256

257+
#[derive(Diagnostic)]
258+
#[diag(hir_analysis_param_in_ty_of_assoc_const)]
259+
pub(crate) struct ParamInTyOfAssocConst {
260+
#[primary_span]
261+
#[label]
262+
pub span: Span,
263+
pub assoc_const: Ident,
264+
pub param_name: Symbol,
265+
pub param_kind: &'static str,
266+
#[label(hir_analysis_param_defined_here_label)]
267+
pub param_defined_here_label: Span,
268+
}
269+
257270
#[derive(Subdiagnostic)]
258271
#[help(hir_analysis_parenthesized_fn_trait_expansion)]
259272
pub struct ParenthesizedFnTraitExpansion {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Regression test for issue #108271.
2+
// Detect and reject generic params in the type of assoc consts used in an equality bound.
3+
#![feature(associated_const_equality)]
4+
5+
trait Trait<'a, T, const N: usize> {
6+
const K: &'a [T; N];
7+
}
8+
9+
fn take0<'r>(_: impl Trait<'r, (), 0, K = { &[] }>) {}
10+
//~^ ERROR the type of the associated constant `K` must not depend on generic parameters
11+
//~| NOTE its type must not depend on the lifetime parameter `'r`
12+
//~| NOTE the lifetime parameter `'r` is defined here
13+
fn take1<A>(_: impl Trait<'static, A, 0, K = { &[] }>) {}
14+
//~^ ERROR the type of the associated constant `K` must not depend on generic parameters
15+
//~| NOTE its type must not depend on the type parameter `A`
16+
//~| NOTE the type parameter `A` is defined here
17+
fn take2<const Q: usize>(_: impl Trait<'static, (), Q, K = { [] }>) {}
18+
//~^ ERROR the type of the associated constant `K` must not depend on generic parameters
19+
//~| NOTE its type must not depend on the const parameter `Q`
20+
//~| NOTE the const parameter `Q` is defined here
21+
22+
trait Project {
23+
const S: Self;
24+
}
25+
26+
// FIXME(fmease): Clean this up, treat synthetic type parameters differently in diags.
27+
28+
fn take3(_: impl Project<S = {}>) {}
29+
//~^ ERROR the type of the associated constant `S` must not depend on generic parameters
30+
//~| NOTE its type must not depend on the type parameter `impl Project<S = const {}>`
31+
//~| NOTE the type parameter `impl Project<S = const {}>` is defined here
32+
33+
fn take4<P: Project<S = {}>>(_: P) {}
34+
//~^ ERROR the type of the associated constant `S` must not depend on generic parameters
35+
//~| NOTE its type must not depend on the type parameter `P`
36+
//~| NOTE the type parameter `P` is defined here
37+
38+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
error: the type of the associated constant `K` must not depend on generic parameters
2+
--> $DIR/assoc-const-eq-param-in-ty.rs:9:39
3+
|
4+
LL | fn take0<'r>(_: impl Trait<'r, (), 0, K = { &[] }>) {}
5+
| -- ^ its type must not depend on the lifetime parameter `'r`
6+
| |
7+
| the lifetime parameter `'r` is defined here
8+
9+
error: the type of the associated constant `K` must not depend on generic parameters
10+
--> $DIR/assoc-const-eq-param-in-ty.rs:13:42
11+
|
12+
LL | fn take1<A>(_: impl Trait<'static, A, 0, K = { &[] }>) {}
13+
| - ^ its type must not depend on the type parameter `A`
14+
| |
15+
| the type parameter `A` is defined here
16+
17+
error: the type of the associated constant `K` must not depend on generic parameters
18+
--> $DIR/assoc-const-eq-param-in-ty.rs:17:56
19+
|
20+
LL | fn take2<const Q: usize>(_: impl Trait<'static, (), Q, K = { [] }>) {}
21+
| - ^ its type must not depend on the const parameter `Q`
22+
| |
23+
| the const parameter `Q` is defined here
24+
25+
error: the type of the associated constant `S` must not depend on generic parameters
26+
--> $DIR/assoc-const-eq-param-in-ty.rs:28:26
27+
|
28+
LL | fn take3(_: impl Project<S = {}>) {}
29+
| -------------^------
30+
| | |
31+
| | its type must not depend on the type parameter `impl Project<S = const {}>`
32+
| the type parameter `impl Project<S = const {}>` is defined here
33+
34+
error: the type of the associated constant `S` must not depend on generic parameters
35+
--> $DIR/assoc-const-eq-param-in-ty.rs:33:21
36+
|
37+
LL | fn take4<P: Project<S = {}>>(_: P) {}
38+
| - ^ its type must not depend on the type parameter `P`
39+
| |
40+
| the type parameter `P` is defined here
41+
42+
error: aborting due to 5 previous errors
43+

0 commit comments

Comments
 (0)