diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 9ec5632a74982..15dff10d8310c 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -376,24 +376,44 @@ pub trait GenericArgsLowerer<'a, 'tcx> { ) -> ty::GenericArg<'tcx>; } -struct ForbidMCGParamUsesFolder<'tcx> { +/// Context in which `ForbidParamUsesFolder` is being used, to emit appropriate diagnostics. +enum ForbidParamContext { + /// Anon const in a const argument position. + ConstArgument, + /// Enum discriminant expression. + EnumDiscriminant, +} + +struct ForbidParamUsesFolder<'tcx> { tcx: TyCtxt<'tcx>, anon_const_def_id: LocalDefId, span: Span, is_self_alias: bool, + context: ForbidParamContext, } -impl<'tcx> ForbidMCGParamUsesFolder<'tcx> { +impl<'tcx> ForbidParamUsesFolder<'tcx> { fn error(&self) -> ErrorGuaranteed { - let msg = if self.is_self_alias { - "generic `Self` types are currently not permitted in anonymous constants" - } else if self.tcx.features().generic_const_args() { - "generic parameters in const blocks are only allowed as the direct value of a `type const`" - } else { - "generic parameters may not be used in const operations" + let msg = match self.context { + ForbidParamContext::EnumDiscriminant if self.is_self_alias => { + "generic `Self` types are not permitted in enum discriminant values" + } + ForbidParamContext::EnumDiscriminant => { + "generic parameters may not be used in enum discriminant values" + } + ForbidParamContext::ConstArgument if self.is_self_alias => { + "generic `Self` types are currently not permitted in anonymous constants" + } + ForbidParamContext::ConstArgument => { + if self.tcx.features().generic_const_args() { + "generic parameters in const blocks are only allowed as the direct value of a `type const`" + } else { + "generic parameters may not be used in const operations" + } + } }; let mut diag = self.tcx.dcx().struct_span_err(self.span, msg); - if self.is_self_alias { + if self.is_self_alias && matches!(self.context, ForbidParamContext::ConstArgument) { let anon_const_hir_id: HirId = HirId::make_owner(self.anon_const_def_id); let parent_impl = self.tcx.hir_parent_owner_iter(anon_const_hir_id).find_map( |(_, node)| match node { @@ -407,18 +427,20 @@ impl<'tcx> ForbidMCGParamUsesFolder<'tcx> { diag.span_note(impl_.self_ty.span, "not a concrete type"); } } - if self.tcx.features().min_generic_const_args() { + if matches!(self.context, ForbidParamContext::ConstArgument) + && self.tcx.features().min_generic_const_args() + { if !self.tcx.features().generic_const_args() { diag.help("add `#![feature(generic_const_args)]` to allow generic expressions as the RHS of const items"); } else { diag.help("consider factoring the expression into a `type const` item and use it as the const argument instead"); } - }; + } diag.emit() } } -impl<'tcx> ty::TypeFolder> for ForbidMCGParamUsesFolder<'tcx> { +impl<'tcx> ty::TypeFolder> for ForbidParamUsesFolder<'tcx> { fn cx(&self) -> TyCtxt<'tcx> { self.tcx } @@ -460,37 +482,80 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { && tcx.def_kind(parent_def_id) == DefKind::AnonConst && let ty::AnonConstKind::MCG = tcx.anon_const_kind(parent_def_id) { - let folder = ForbidMCGParamUsesFolder { + let folder = ForbidParamUsesFolder { tcx, anon_const_def_id: parent_def_id, span, is_self_alias: false, + context: ForbidParamContext::ConstArgument, }; return Err(folder.error()); } Ok(()) } + /// Returns the `ForbidParamContext` for the current anon const if it is a context that + /// forbids uses of generic parameters. `None` if the current item is not such a context. + /// + /// Name resolution handles most invalid generic parameter uses in these contexts, but it + /// cannot reject `Self` that aliases a generic type, nor generic parameters introduced by + /// type-dependent name resolution (e.g. `::Assoc` resolving to a type that + /// contains params). Those cases are handled by `check_param_uses_if_mcg`. + fn anon_const_forbids_generic_params(&self) -> Option { + let tcx = self.tcx(); + let parent_def_id = self.item_def_id(); + + // Inline consts and closures can be nested inside anon consts that forbid generic + // params (e.g. an enum discriminant). Walk up the def parent chain to find the + // nearest enclosing AnonConst and use that to determine the context. + let anon_const_def_id = match tcx.def_kind(parent_def_id) { + DefKind::AnonConst => parent_def_id, + DefKind::InlineConst | DefKind::Closure => { + let root = tcx.typeck_root_def_id(parent_def_id.into()); + match tcx.def_kind(root) { + DefKind::AnonConst => root.expect_local(), + _ => return None, + } + } + _ => return None, + }; + + match tcx.anon_const_kind(anon_const_def_id) { + ty::AnonConstKind::MCG => Some(ForbidParamContext::ConstArgument), + ty::AnonConstKind::NonTypeSystem => { + // NonTypeSystem anon consts only have accessible generic parameters in specific + // positions (ty patterns and field defaults — see `generics_of`). In all other + // positions (e.g. enum discriminants) generic parameters are not in scope. + if tcx.generics_of(anon_const_def_id).count() == 0 { + Some(ForbidParamContext::EnumDiscriminant) + } else { + None + } + } + ty::AnonConstKind::GCE + | ty::AnonConstKind::GCA + | ty::AnonConstKind::RepeatExprCount => None, + } + } + /// Check for uses of generic parameters that are not in scope due to this being - /// in a non-generic anon const context. + /// in a non-generic anon const context (e.g. MCG or an enum discriminant). + /// + /// Name resolution rejects most invalid uses, but cannot handle `Self` aliasing a + /// generic type or generic parameters introduced by type-dependent name resolution. #[must_use = "need to use transformed output"] fn check_param_uses_if_mcg(&self, term: T, span: Span, is_self_alias: bool) -> T where T: ty::TypeFoldable>, { let tcx = self.tcx(); - let parent_def_id = self.item_def_id(); - if tcx.def_kind(parent_def_id) == DefKind::AnonConst - && let ty::AnonConstKind::MCG = tcx.anon_const_kind(parent_def_id) + if let Some(context) = self.anon_const_forbids_generic_params() // Fast path if contains no params/escaping bound vars. && (term.has_param() || term.has_escaping_bound_vars()) { - let mut folder = ForbidMCGParamUsesFolder { - tcx, - anon_const_def_id: parent_def_id, - span, - is_self_alias, - }; + let anon_const_def_id = self.item_def_id(); + let mut folder = + ForbidParamUsesFolder { tcx, anon_const_def_id, span, is_self_alias, context }; term.fold_with(&mut folder) } else { term diff --git a/tests/ui/enum-discriminant/generic-self-in-discr-ice.rs b/tests/ui/enum-discriminant/generic-self-in-discr-ice.rs new file mode 100644 index 0000000000000..2d60380efffa0 --- /dev/null +++ b/tests/ui/enum-discriminant/generic-self-in-discr-ice.rs @@ -0,0 +1,12 @@ +#![feature(sized_hierarchy)] + +use std::marker::PointeeSized; + +#[repr(usize)] +enum What { + X = size_of::<*mut Self>(), + //~^ ERROR generic `Self` types are not permitted in enum discriminant values + Y(*mut T), +} + +fn main() {} diff --git a/tests/ui/enum-discriminant/generic-self-in-discr-ice.stderr b/tests/ui/enum-discriminant/generic-self-in-discr-ice.stderr new file mode 100644 index 0000000000000..d827897c8e3e7 --- /dev/null +++ b/tests/ui/enum-discriminant/generic-self-in-discr-ice.stderr @@ -0,0 +1,8 @@ +error: generic `Self` types are not permitted in enum discriminant values + --> $DIR/generic-self-in-discr-ice.rs:7:24 + | +LL | X = size_of::<*mut Self>(), + | ^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/enum-discriminant/generic-self-in-discr-inline-const.rs b/tests/ui/enum-discriminant/generic-self-in-discr-inline-const.rs new file mode 100644 index 0000000000000..de02bb95e5a0d --- /dev/null +++ b/tests/ui/enum-discriminant/generic-self-in-discr-inline-const.rs @@ -0,0 +1,17 @@ +//@ check-fail +// Test that `Self` is rejected even when nested inside an inline const +// or closure within an enum discriminant. Regression test for issue #154281. +#![feature(sized_hierarchy)] + +use std::marker::PointeeSized; + +#[repr(usize)] +enum What { + X = const { { let _: *mut Self; 1_usize } }, + //~^ ERROR generic `Self` types are not permitted in enum discriminant values + Y = { let _f = || { let _: *mut Self; }; 1_usize }, + //~^ ERROR generic `Self` types are not permitted in enum discriminant values + Z(*mut T), +} + +fn main() {} diff --git a/tests/ui/enum-discriminant/generic-self-in-discr-inline-const.stderr b/tests/ui/enum-discriminant/generic-self-in-discr-inline-const.stderr new file mode 100644 index 0000000000000..cc5db2eb266d1 --- /dev/null +++ b/tests/ui/enum-discriminant/generic-self-in-discr-inline-const.stderr @@ -0,0 +1,14 @@ +error: generic `Self` types are not permitted in enum discriminant values + --> $DIR/generic-self-in-discr-inline-const.rs:10:31 + | +LL | X = const { { let _: *mut Self; 1_usize } }, + | ^^^^ + +error: generic `Self` types are not permitted in enum discriminant values + --> $DIR/generic-self-in-discr-inline-const.rs:12:37 + | +LL | Y = { let _f = || { let _: *mut Self; }; 1_usize }, + | ^^^^ + +error: aborting due to 2 previous errors +