Skip to content

Commit ccab170

Browse files
committed
Reject early-bound params in the type of assoc const bindings
1 parent 3b85d2c commit ccab170

File tree

6 files changed

+271
-10
lines changed

6 files changed

+271
-10
lines changed

compiler/rustc_hir_analysis/messages.ftl

+18
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,22 @@ hir_analysis_opaque_captures_higher_ranked_lifetime = `impl Trait` cannot captur
316316
.label = `impl Trait` implicitly captures all lifetimes in scope
317317
.note = lifetime declared here
318318
319+
hir_analysis_param_in_ty_of_assoc_const_binding =
320+
the type of the associated constant `{$assoc_const}` must not depend on {$param_category ->
321+
[self] `Self`
322+
[synthetic] `impl Trait`
323+
*[normal] generic parameters
324+
}
325+
.label = its type must not depend on {$param_category ->
326+
[self] `Self`
327+
[synthetic] `impl Trait`
328+
*[normal] the {$param_def_kind} `{$param_name}`
329+
}
330+
.param_defined_here_label = {$param_category ->
331+
[synthetic] the `impl Trait` is specified here
332+
*[normal] the {$param_def_kind} `{$param_name}` is defined here
333+
}
334+
319335
hir_analysis_paren_sugar_attribute = the `#[rustc_paren_sugar]` attribute is a temporary means of controlling which traits can use parenthetical notation
320336
.help = add `#![feature(unboxed_closures)]` to the crate attributes to use it
321337
@@ -431,6 +447,8 @@ hir_analysis_transparent_non_zero_sized_enum = the variant of a transparent {$de
431447
.label = needs at most one field with non-trivial size or alignment, but has {$field_count}
432448
.labels = this field has non-zero size or requires alignment
433449
450+
hir_analysis_ty_of_assoc_const_binding_note = `{$assoc_const}` has type `{$ty}`
451+
434452
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}`)
435453
.label = type parameter `{$param_ty}` must be covered by another type when it appears before the first local type (`{$local_type}`)
436454
.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

compiler/rustc_hir_analysis/src/astconv/bounds.rs

+98-10
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
1-
use rustc_data_structures::fx::FxIndexMap;
1+
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
22
use rustc_errors::{codes::*, struct_span_code_err};
33
use rustc_hir as hir;
44
use rustc_hir::def::{DefKind, Res};
55
use rustc_hir::def_id::{DefId, LocalDefId};
6-
use rustc_middle::ty::{self as ty, Ty};
6+
use rustc_middle::ty::{self as ty, IsSuggestable, Ty, TyCtxt};
77
use rustc_span::symbol::Ident;
88
use rustc_span::{ErrorGuaranteed, Span};
99
use rustc_trait_selection::traits;
10+
use rustc_type_ir::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor};
1011
use smallvec::SmallVec;
1112

1213
use crate::astconv::{AstConv, OnlySelfBounds, PredicateFilter};
@@ -438,14 +439,8 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
438439
binding.kind
439440
{
440441
let ty = alias_ty.map_bound(|ty| tcx.type_of(ty.def_id).instantiate(tcx, ty.args));
441-
// Since the arguments passed to the alias type above may contain early-bound
442-
// generic parameters, the instantiated type may contain some as well.
443-
// Therefore wrap it in `EarlyBinder`.
444-
// FIXME(fmease): Reject escaping late-bound vars.
445-
tcx.feed_anon_const_type(
446-
anon_const.def_id,
447-
ty::EarlyBinder::bind(ty.skip_binder()),
448-
);
442+
let ty = check_assoc_const_binding_type(tcx, assoc_ident, ty, binding.hir_id);
443+
tcx.feed_anon_const_type(anon_const.def_id, ty::EarlyBinder::bind(ty));
449444
}
450445

451446
alias_ty
@@ -537,3 +532,96 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
537532
Ok(())
538533
}
539534
}
535+
536+
/// Detect and reject early-bound generic params in the type of associated const bindings.
537+
///
538+
/// FIXME(const_generics): This is a temporary and semi-artifical restriction until the
539+
/// arrival of *generic const generics*[^1].
540+
///
541+
/// It might actually be possible that we can already support early-bound generic params
542+
/// in such types if we just lifted some more checks in other places, too, for example
543+
/// inside [`ty::Const::from_anon_const`]. However, even if that were the case, we should
544+
/// probably gate this behind another feature flag.
545+
///
546+
/// [^1]: <https://github.com/rust-lang/project-const-generics/issues/28>.
547+
fn check_assoc_const_binding_type<'tcx>(
548+
tcx: TyCtxt<'tcx>,
549+
assoc_const: Ident,
550+
ty: ty::Binder<'tcx, Ty<'tcx>>,
551+
hir_id: hir::HirId,
552+
) -> Ty<'tcx> {
553+
// We can't perform the checks for early-bound params during name resolution unlike E0770
554+
// because this information depends on *type* resolution.
555+
556+
// FIXME(fmease): Reject escaping late-bound vars.
557+
let ty = ty.skip_binder();
558+
if !ty.has_param() {
559+
return ty;
560+
}
561+
562+
let mut collector = GenericParamCollector { params: Default::default() };
563+
ty.visit_with(&mut collector);
564+
565+
let mut guar = None;
566+
let ty_note = ty
567+
.make_suggestable(tcx, false)
568+
.map(|ty| crate::errors::TyOfAssocConstBindingNote { assoc_const, ty });
569+
570+
let enclosing_item_owner_id = tcx
571+
.hir()
572+
.parent_owner_iter(hir_id)
573+
.find_map(|(owner_id, parent)| parent.generics().map(|_| owner_id))
574+
.unwrap();
575+
let generics = tcx.generics_of(enclosing_item_owner_id);
576+
for index in collector.params {
577+
let param = generics.param_at(index as _, tcx);
578+
let is_self_param = param.name == rustc_span::symbol::kw::SelfUpper;
579+
guar.get_or_insert(tcx.dcx().emit_err(crate::errors::ParamInTyOfAssocConstBinding {
580+
span: assoc_const.span,
581+
assoc_const,
582+
param_name: param.name,
583+
param_def_kind: tcx.def_descr(param.def_id),
584+
param_category: if is_self_param {
585+
"self"
586+
} else if param.kind.is_synthetic() {
587+
"synthetic"
588+
} else {
589+
"normal"
590+
},
591+
param_defined_here_label:
592+
(!is_self_param).then(|| tcx.def_ident_span(param.def_id).unwrap()),
593+
ty_note,
594+
}));
595+
}
596+
597+
let guar = guar.unwrap_or_else(|| bug!("failed to find gen params in ty"));
598+
Ty::new_error(tcx, guar)
599+
}
600+
601+
struct GenericParamCollector {
602+
params: FxIndexSet<u32>,
603+
}
604+
605+
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for GenericParamCollector {
606+
fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
607+
if let ty::Param(param) = ty.kind() {
608+
self.params.insert(param.index);
609+
} else if ty.has_param() {
610+
ty.super_visit_with(self)
611+
}
612+
}
613+
614+
fn visit_region(&mut self, re: ty::Region<'tcx>) -> Self::Result {
615+
if let ty::ReEarlyParam(param) = re.kind() {
616+
self.params.insert(param.index);
617+
}
618+
}
619+
620+
fn visit_const(&mut self, ct: ty::Const<'tcx>) -> Self::Result {
621+
if let ty::ConstKind::Param(param) = ct.kind() {
622+
self.params.insert(param.index);
623+
} else if ct.has_param() {
624+
ct.super_visit_with(self)
625+
}
626+
}
627+
}

compiler/rustc_hir_analysis/src/collect/type_of.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use core::ops::ControlFlow;
2+
23
use rustc_errors::{Applicability, StashKey};
34
use rustc_hir as hir;
45
use rustc_hir::def_id::{DefId, LocalDefId};

compiler/rustc_hir_analysis/src/errors.rs

+23
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,29 @@ pub struct AssocTypeBindingNotAllowed {
295295
pub fn_trait_expansion: Option<ParenthesizedFnTraitExpansion>,
296296
}
297297

298+
#[derive(Diagnostic)]
299+
#[diag(hir_analysis_param_in_ty_of_assoc_const_binding)]
300+
pub(crate) struct ParamInTyOfAssocConstBinding<'tcx> {
301+
#[primary_span]
302+
#[label]
303+
pub span: Span,
304+
pub assoc_const: Ident,
305+
pub param_name: Symbol,
306+
pub param_def_kind: &'static str,
307+
pub param_category: &'static str,
308+
#[label(hir_analysis_param_defined_here_label)]
309+
pub param_defined_here_label: Option<Span>,
310+
#[subdiagnostic]
311+
pub ty_note: Option<TyOfAssocConstBindingNote<'tcx>>,
312+
}
313+
314+
#[derive(Subdiagnostic, Clone, Copy)]
315+
#[note(hir_analysis_ty_of_assoc_const_binding_note)]
316+
pub(crate) struct TyOfAssocConstBindingNote<'tcx> {
317+
pub assoc_const: Ident,
318+
pub ty: Ty<'tcx>,
319+
}
320+
298321
#[derive(Subdiagnostic)]
299322
#[help(hir_analysis_parenthesized_fn_trait_expansion)]
300323
pub struct ParenthesizedFnTraitExpansion {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
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: 'a, const N: usize> {
6+
const K: &'a [T; N];
7+
}
8+
9+
fn take0<'r, A: 'r, const Q: usize>(_: impl Trait<'r, A, Q, K = { loop {} }>) {}
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+
//~| NOTE `K` has type `&'r [A; Q]`
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+
//~| NOTE `K` has type `&'r [A; Q]`
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+
//~| NOTE `K` has type `&'r [A; Q]`
22+
23+
trait Project {
24+
const SELF: Self;
25+
}
26+
27+
fn take1(_: impl Project<SELF = {}>) {}
28+
//~^ ERROR the type of the associated constant `SELF` must not depend on `impl Trait`
29+
//~| NOTE its type must not depend on `impl Trait`
30+
//~| NOTE the `impl Trait` is specified here
31+
32+
fn take2<P: Project<SELF = {}>>(_: P) {}
33+
//~^ ERROR the type of the associated constant `SELF` must not depend on generic parameters
34+
//~| NOTE its type must not depend on the type parameter `P`
35+
//~| NOTE the type parameter `P` is defined here
36+
//~| NOTE `SELF` has type `P`
37+
38+
trait Iface<'r> {
39+
//~^ NOTE the lifetime parameter `'r` is defined here
40+
type Assoc<const Q: usize>: Trait<'r, Self, Q, K = { loop {} }>
41+
//~^ ERROR the type of the associated constant `K` must not depend on generic parameters
42+
//~| NOTE its type must not depend on the lifetime parameter `'r`
43+
//~| NOTE `K` has type `&'r [Self; Q]`
44+
//~| ERROR the type of the associated constant `K` must not depend on `Self`
45+
//~| NOTE its type must not depend on `Self`
46+
//~| NOTE `K` has type `&'r [Self; Q]`
47+
//~| ERROR the type of the associated constant `K` must not depend on generic parameters
48+
//~| NOTE its type must not depend on the const parameter `Q`
49+
//~| NOTE the const parameter `Q` is defined here
50+
//~| NOTE `K` has type `&'r [Self; Q]`
51+
where
52+
Self: Sized + 'r;
53+
}
54+
55+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
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:61
3+
|
4+
LL | fn take0<'r, A: 'r, const Q: usize>(_: impl Trait<'r, A, Q, K = { loop {} }>) {}
5+
| -- the lifetime parameter `'r` is defined here ^ its type must not depend on the lifetime parameter `'r`
6+
|
7+
= note: `K` has type `&'r [A; Q]`
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:9:61
11+
|
12+
LL | fn take0<'r, A: 'r, const Q: usize>(_: impl Trait<'r, A, Q, K = { loop {} }>) {}
13+
| - the type parameter `A` is defined here ^ its type must not depend on the type parameter `A`
14+
|
15+
= note: `K` has type `&'r [A; Q]`
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:9:61
19+
|
20+
LL | fn take0<'r, A: 'r, const Q: usize>(_: impl Trait<'r, A, Q, K = { loop {} }>) {}
21+
| - ^ its type must not depend on the const parameter `Q`
22+
| |
23+
| the const parameter `Q` is defined here
24+
|
25+
= note: `K` has type `&'r [A; Q]`
26+
27+
error: the type of the associated constant `SELF` must not depend on `impl Trait`
28+
--> $DIR/assoc-const-eq-param-in-ty.rs:27:26
29+
|
30+
LL | fn take1(_: impl Project<SELF = {}>) {}
31+
| -------------^^^^------
32+
| | |
33+
| | its type must not depend on `impl Trait`
34+
| the `impl Trait` is specified here
35+
36+
error: the type of the associated constant `SELF` must not depend on generic parameters
37+
--> $DIR/assoc-const-eq-param-in-ty.rs:32:21
38+
|
39+
LL | fn take2<P: Project<SELF = {}>>(_: P) {}
40+
| - ^^^^ its type must not depend on the type parameter `P`
41+
| |
42+
| the type parameter `P` is defined here
43+
|
44+
= note: `SELF` has type `P`
45+
46+
error: the type of the associated constant `K` must not depend on generic parameters
47+
--> $DIR/assoc-const-eq-param-in-ty.rs:40:52
48+
|
49+
LL | trait Iface<'r> {
50+
| -- the lifetime parameter `'r` is defined here
51+
LL |
52+
LL | type Assoc<const Q: usize>: Trait<'r, Self, Q, K = { loop {} }>
53+
| ^ its type must not depend on the lifetime parameter `'r`
54+
|
55+
= note: `K` has type `&'r [Self; Q]`
56+
57+
error: the type of the associated constant `K` must not depend on `Self`
58+
--> $DIR/assoc-const-eq-param-in-ty.rs:40:52
59+
|
60+
LL | type Assoc<const Q: usize>: Trait<'r, Self, Q, K = { loop {} }>
61+
| ^ its type must not depend on `Self`
62+
|
63+
= note: `K` has type `&'r [Self; Q]`
64+
65+
error: the type of the associated constant `K` must not depend on generic parameters
66+
--> $DIR/assoc-const-eq-param-in-ty.rs:40:52
67+
|
68+
LL | type Assoc<const Q: usize>: Trait<'r, Self, Q, K = { loop {} }>
69+
| - ^ its type must not depend on the const parameter `Q`
70+
| |
71+
| the const parameter `Q` is defined here
72+
|
73+
= note: `K` has type `&'r [Self; Q]`
74+
75+
error: aborting due to 8 previous errors
76+

0 commit comments

Comments
 (0)