Skip to content

Commit eca8904

Browse files
committed
Fix ICE when Self is used in enum discriminant of a generic enum
Move the validation into the existing `check_param_uses_if_mcg` machinery in HIR ty lowering instead of adding a new check in wfcheck. After #150519 introduced `AnonConstKind`, `ForbidMCGParamUsesFolder` was only gated on `AnonConstKind::MCG`, causing discriminant anon consts (`NonTypeSystem`) to bypass it entirely. Add `anon_const_forbids_generic_params()` which returns the appropriate `ForbidParamContext` for both MCG and enum discriminant contexts. Wire it into `check_param_uses_if_mcg` so that `Self` aliasing a generic type is caught before reaching `const_eval_poly`. Convert the `TooGeneric` span_bug into a proper diagnostic as a fallback for anything slipping through type-dependent path resolution.
1 parent c6e30c7 commit eca8904

5 files changed

Lines changed: 79 additions & 73 deletions

File tree

compiler/rustc_hir_analysis/src/check/wfcheck.rs

Lines changed: 11 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1107,27 +1107,17 @@ fn check_type_defn<'tcx>(
11071107

11081108
// Explicit `enum` discriminant values must const-evaluate successfully.
11091109
if let ty::VariantDiscr::Explicit(discr_def_id) = variant.discr {
1110-
// Reject discriminants that use `Self` when the enum has
1111-
// type or const generic parameters, since `Self` resolves
1112-
// to the enum type including those parameters.
1113-
if let Some(local_id) = discr_def_id.as_local()
1114-
&& enum_has_type_or_const_generics(tcx, item.owner_id.def_id)
1115-
&& discr_body_references_self(tcx, local_id)
1116-
{
1117-
tcx.dcx().span_err(
1118-
tcx.def_span(discr_def_id),
1119-
"generic parameters may not be used in enum discriminant values",
1120-
);
1121-
} else {
1122-
match tcx.const_eval_poly(discr_def_id) {
1123-
Ok(_) => {}
1124-
Err(ErrorHandled::Reported(..)) => {}
1125-
Err(ErrorHandled::TooGeneric(_)) => {
1126-
tcx.dcx().span_err(
1127-
tcx.def_span(discr_def_id),
1128-
"enum discriminant value depends on generic parameters",
1129-
);
1130-
}
1110+
match tcx.const_eval_poly(discr_def_id) {
1111+
Ok(_) => {}
1112+
Err(ErrorHandled::Reported(..)) => {}
1113+
Err(ErrorHandled::TooGeneric(_)) => {
1114+
// This can happen if a discriminant slips past the checks in HIR ty
1115+
// lowering (e.g. via type-dependent path resolution). Emit a proper
1116+
// error rather than ICE-ing.
1117+
tcx.dcx().span_err(
1118+
tcx.def_span(discr_def_id),
1119+
"enum discriminant value depends on generic parameters",
1120+
);
11311121
}
11321122
}
11331123
}
@@ -1138,31 +1128,6 @@ fn check_type_defn<'tcx>(
11381128
})
11391129
}
11401130

1141-
fn enum_has_type_or_const_generics(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
1142-
tcx.generics_of(def_id).own_params.iter().any(|p| {
1143-
matches!(
1144-
p.kind,
1145-
ty::GenericParamDefKind::Type { .. } | ty::GenericParamDefKind::Const { .. }
1146-
)
1147-
})
1148-
}
1149-
1150-
fn discr_body_references_self(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
1151-
struct SelfRefVisitor(bool);
1152-
impl<'hir> intravisit::Visitor<'hir> for SelfRefVisitor {
1153-
fn visit_path(&mut self, path: &hir::Path<'hir>, _: hir::HirId) {
1154-
if matches!(path.res, Res::SelfTyAlias { .. }) {
1155-
self.0 = true;
1156-
}
1157-
intravisit::walk_path(self, path);
1158-
}
1159-
}
1160-
let body = tcx.hir_body_owned_by(def_id);
1161-
let mut visitor = SelfRefVisitor(false);
1162-
intravisit::walk_body(&mut visitor, body);
1163-
visitor.0
1164-
}
1165-
11661131
#[instrument(skip(tcx, item))]
11671132
fn check_trait(tcx: TyCtxt<'_>, item: &hir::Item<'_>) -> Result<(), ErrorGuaranteed> {
11681133
debug!(?item.owner_id);

compiler/rustc_hir_analysis/src/collect/generics_of.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -163,11 +163,9 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
163163
}
164164
ty::AnonConstKind::GCE => Some(parent_did),
165165

166+
// Field defaults are allowed to use generic parameters, e.g. `field: u32 = /*defid: N + 1*/`
166167
ty::AnonConstKind::NonTypeSystem
167-
if matches!(
168-
tcx.parent_hir_node(hir_id),
169-
Node::TyPat(_) | Node::Field(_) | Node::Variant(_)
170-
) =>
168+
if matches!(tcx.parent_hir_node(hir_id), Node::TyPat(_) | Node::Field(_)) =>
171169
{
172170
Some(parent_did)
173171
}

compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs

Lines changed: 62 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -376,24 +376,41 @@ pub trait GenericArgsLowerer<'a, 'tcx> {
376376
) -> ty::GenericArg<'tcx>;
377377
}
378378

379+
/// Context in which `ForbidMCGParamUsesFolder` is being used, to emit appropriate diagnostics.
380+
enum ForbidParamContext {
381+
/// Anon const in a min-const-generics (MCG) position, e.g. a const argument.
382+
MinConstGenerics,
383+
/// Enum discriminant expression.
384+
EnumDiscriminant,
385+
}
386+
379387
struct ForbidMCGParamUsesFolder<'tcx> {
380388
tcx: TyCtxt<'tcx>,
381389
anon_const_def_id: LocalDefId,
382390
span: Span,
383391
is_self_alias: bool,
392+
context: ForbidParamContext,
384393
}
385394

386395
impl<'tcx> ForbidMCGParamUsesFolder<'tcx> {
387396
fn error(&self) -> ErrorGuaranteed {
388-
let msg = if self.is_self_alias {
389-
"generic `Self` types are currently not permitted in anonymous constants"
390-
} else if self.tcx.features().opaque_generic_const_args() {
391-
"generic parameters in const blocks are only allowed as the direct value of a `type const`"
392-
} else {
393-
"generic parameters may not be used in const operations"
397+
let msg = match self.context {
398+
ForbidParamContext::EnumDiscriminant if self.is_self_alias =>
399+
"generic `Self` types are not permitted in enum discriminant values",
400+
ForbidParamContext::EnumDiscriminant =>
401+
"generic parameters may not be used in enum discriminant values",
402+
ForbidParamContext::MinConstGenerics if self.is_self_alias =>
403+
"generic `Self` types are currently not permitted in anonymous constants",
404+
ForbidParamContext::MinConstGenerics => {
405+
if self.tcx.features().opaque_generic_const_args() {
406+
"generic parameters in const blocks are only allowed as the direct value of a `type const`"
407+
} else {
408+
"generic parameters may not be used in const operations"
409+
}
410+
}
394411
};
395412
let mut diag = self.tcx.dcx().struct_span_err(self.span, msg);
396-
if self.is_self_alias {
413+
if self.is_self_alias && matches!(self.context, ForbidParamContext::MinConstGenerics) {
397414
let anon_const_hir_id: HirId = HirId::make_owner(self.anon_const_def_id);
398415
let parent_impl = self.tcx.hir_parent_owner_iter(anon_const_hir_id).find_map(
399416
|(_, node)| match node {
@@ -407,13 +424,15 @@ impl<'tcx> ForbidMCGParamUsesFolder<'tcx> {
407424
diag.span_note(impl_.self_ty.span, "not a concrete type");
408425
}
409426
}
410-
if self.tcx.features().min_generic_const_args() {
427+
if matches!(self.context, ForbidParamContext::MinConstGenerics)
428+
&& self.tcx.features().min_generic_const_args()
429+
{
411430
if !self.tcx.features().opaque_generic_const_args() {
412431
diag.help("add `#![feature(opaque_generic_const_args)]` to allow generic expressions as the RHS of const items");
413432
} else {
414433
diag.help("consider factoring the expression into a `type const` item and use it as the const argument instead");
415434
}
416-
};
435+
}
417436
diag.emit()
418437
}
419438
}
@@ -465,32 +484,56 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
465484
anon_const_def_id: parent_def_id,
466485
span,
467486
is_self_alias: false,
487+
context: ForbidParamContext::MinConstGenerics,
468488
};
469489
return Err(folder.error());
470490
}
471491
Ok(())
472492
}
473493

494+
/// Returns the `ForbidParamContext` for the current anon const if it is a context that
495+
/// forbids uses of generic parameters that escape through type-dependent paths (e.g. `Self`
496+
/// aliasing a generic type), or `None` if the current item is not such a context.
497+
///
498+
/// Name resolution handles most invalid generic parameter uses in these contexts, but it
499+
/// cannot reject `Self` that aliases a generic type, nor generic parameters introduced by
500+
/// type-dependent name resolution (e.g. `<Self as Trait>::Assoc` resolving to a type that
501+
/// contains params). Those cases are handled by `check_param_uses_if_mcg`.
502+
fn anon_const_forbids_generic_params(&self) -> Option<ForbidParamContext> {
503+
let tcx = self.tcx();
504+
let parent_def_id = self.item_def_id();
505+
if tcx.def_kind(parent_def_id) != DefKind::AnonConst {
506+
return None;
507+
}
508+
match tcx.anon_const_kind(parent_def_id) {
509+
ty::AnonConstKind::MCG => Some(ForbidParamContext::MinConstGenerics),
510+
ty::AnonConstKind::NonTypeSystem => {
511+
let hir_id = tcx.local_def_id_to_hir_id(parent_def_id);
512+
matches!(tcx.parent_hir_node(hir_id), hir::Node::Variant(_))
513+
.then_some(ForbidParamContext::EnumDiscriminant)
514+
}
515+
_ => None,
516+
}
517+
}
518+
474519
/// Check for uses of generic parameters that are not in scope due to this being
475-
/// in a non-generic anon const context.
520+
/// in a non-generic anon const context (e.g. MCG or an enum discriminant).
521+
///
522+
/// Name resolution rejects most invalid uses, but cannot handle `Self` aliasing a
523+
/// generic type or generic parameters introduced by type-dependent name resolution.
476524
#[must_use = "need to use transformed output"]
477525
fn check_param_uses_if_mcg<T>(&self, term: T, span: Span, is_self_alias: bool) -> T
478526
where
479527
T: ty::TypeFoldable<TyCtxt<'tcx>>,
480528
{
481529
let tcx = self.tcx();
482-
let parent_def_id = self.item_def_id();
483-
if tcx.def_kind(parent_def_id) == DefKind::AnonConst
484-
&& let ty::AnonConstKind::MCG = tcx.anon_const_kind(parent_def_id)
530+
let anon_const_def_id = self.item_def_id();
531+
if let Some(context) = self.anon_const_forbids_generic_params()
485532
// Fast path if contains no params/escaping bound vars.
486533
&& (term.has_param() || term.has_escaping_bound_vars())
487534
{
488-
let mut folder = ForbidMCGParamUsesFolder {
489-
tcx,
490-
anon_const_def_id: parent_def_id,
491-
span,
492-
is_self_alias,
493-
};
535+
let mut folder =
536+
ForbidMCGParamUsesFolder { tcx, anon_const_def_id, span, is_self_alias, context };
494537
term.fold_with(&mut folder)
495538
} else {
496539
term

tests/ui/enum-discriminant/generic-self-in-discr-ice.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use std::marker::PointeeSized;
55
#[repr(usize)]
66
enum What<T: PointeeSized> {
77
X = size_of::<*mut Self>(),
8-
//~^ ERROR generic parameters may not be used in enum discriminant values
8+
//~^ ERROR generic `Self` types are not permitted in enum discriminant values
99
Y(*mut T),
1010
}
1111

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
error: generic parameters may not be used in enum discriminant values
2-
--> $DIR/generic-self-in-discr-ice.rs:7:9
1+
error: generic `Self` types are not permitted in enum discriminant values
2+
--> $DIR/generic-self-in-discr-ice.rs:7:24
33
|
44
LL | X = size_of::<*mut Self>(),
5-
| ^^^^^^^^^^^^^^^^^^^^^^
5+
| ^^^^
66

77
error: aborting due to 1 previous error
88

0 commit comments

Comments
 (0)