diff --git a/compiler/rustc_const_eval/src/const_eval/valtrees.rs b/compiler/rustc_const_eval/src/const_eval/valtrees.rs index d6dc1a62f4d0d..5bba2610abe98 100644 --- a/compiler/rustc_const_eval/src/const_eval/valtrees.rs +++ b/compiler/rustc_const_eval/src/const_eval/valtrees.rs @@ -82,6 +82,10 @@ pub(crate) fn const_to_valtree_inner<'tcx>( return Err(ValTreeCreationError::NodesOverflow); } + if ty::maybe_not_supported_generic_const_param(ty).is_some_and(|v| v) { + return Err(ValTreeCreationError::NonSupportedType); + } + match ty.kind() { ty::FnDef(..) => { *num_nodes += 1; @@ -97,14 +101,8 @@ pub(crate) fn const_to_valtree_inner<'tcx>( Ok(ty::ValTree::Leaf(val.assert_int())) } - // Raw pointers are not allowed in type level constants, as we cannot properly test them for - // equality at compile-time (see `ptr_guaranteed_cmp`). - // Technically we could allow function pointers (represented as `ty::Instance`), but this is not guaranteed to - // agree with runtime equality tests. - ty::FnPtr(_) | ty::RawPtr(_) => Err(ValTreeCreationError::NonSupportedType), - - ty::Ref(_, _, _) => { - let Ok(derefd_place)= ecx.deref_pointer(place) else { + ty::Ref(_, _, _) => { + let Ok(derefd_place) = ecx.deref_pointer(place) else { return Err(ValTreeCreationError::Other); }; debug!(?derefd_place); @@ -112,22 +110,13 @@ pub(crate) fn const_to_valtree_inner<'tcx>( const_to_valtree_inner(ecx, &derefd_place, num_nodes) } - ty::Str | ty::Slice(_) | ty::Array(_, _) => { - slice_branches(ecx, place, num_nodes) - } - // Trait objects are not allowed in type level constants, as we have no concept for - // resolving their backing type, even if we can do that at const eval time. We may - // hypothetically be able to allow `dyn StructuralEq` trait objects in the future, - // but it is unclear if this is useful. - ty::Dynamic(..) => Err(ValTreeCreationError::NonSupportedType), - - ty::Tuple(elem_tys) => { - branches(ecx, place, elem_tys.len(), None, num_nodes) - } + ty::Str | ty::Slice(_) | ty::Array(_, _) => slice_branches(ecx, place, num_nodes), + + ty::Tuple(elem_tys) => branches(ecx, place, elem_tys.len(), None, num_nodes), ty::Adt(def, _) => { if def.is_union() { - return Err(ValTreeCreationError::NonSupportedType); + unreachable!(); } else if def.variants().is_empty() { bug!("uninhabited types should have errored and never gotten converted to valtree") } @@ -135,24 +124,15 @@ pub(crate) fn const_to_valtree_inner<'tcx>( let Ok(variant) = ecx.read_discriminant(place) else { return Err(ValTreeCreationError::Other); }; - branches(ecx, place, def.variant(variant).fields.len(), def.is_enum().then_some(variant), num_nodes) + branches( + ecx, + place, + def.variant(variant).fields.len(), + def.is_enum().then_some(variant), + num_nodes, + ) } - - ty::Never - | ty::Error(_) - | ty::Foreign(..) - | ty::Infer(ty::FreshIntTy(_)) - | ty::Infer(ty::FreshFloatTy(_)) - // FIXME(oli-obk): we could look behind opaque types - | ty::Alias(..) - | ty::Param(_) - | ty::Bound(..) - | ty::Placeholder(..) - | ty::Infer(_) - // FIXME(oli-obk): we can probably encode closures just like structs - | ty::Closure(..) - | ty::Coroutine(..) - | ty::CoroutineWitness(..) => Err(ValTreeCreationError::NonSupportedType), + _ => unreachable!(), } } diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index d7bd2a7b17fc3..416a60890b085 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -54,10 +54,20 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> { kind: GenericParamKind::Const { default: Some(ct), .. }, .. }) if ct.hir_id == hir_id => { - return tcx + let ty = tcx .type_of(param_def_id) .no_bound_vars() .expect("const parameter types cannot be generic"); + + if ty::maybe_not_supported_generic_const_param(ty).is_some_and(|v| v) { + return Ty::new_error_with_message( + tcx, + tcx.def_span(def_id), + "meet unsupported const generics type", + ) + } else { + return ty; + } } Node::TypeBinding(binding @ &TypeBinding { hir_id: binding_id, .. }) diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index cc0db39ac5719..f5b608529fff9 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -462,6 +462,42 @@ pub struct CReaderCacheKey { #[rustc_pass_by_value] pub struct Ty<'tcx>(Interned<'tcx, WithCachedTypeInfo>>); +pub fn maybe_not_supported_generic_const_param(ty: Ty<'_>) -> Option { + match ty.kind() { + // Raw pointers are not allowed in type level constants, as we cannot properly test them for + // equality at compile-time (see `ptr_guaranteed_cmp`). + // Technically we could allow function pointers (represented as `ty::Instance`), but this is not guaranteed to + // agree with runtime equality tests. + ty::FnPtr(_) | ty::RawPtr(_) => { + Some(true) + }, + // Trait objects are not allowed in type level constants, as we have no concept for + // resolving their backing type, even if we can do that at const eval time. We may + // hypothetically be able to allow `dyn StructuralEq` trait objects in the future, + // but it is unclear if this is useful. + ty::Dynamic(..) => Some(true), + ty::Adt(def, _) => { + def.is_union().then_some(true) + }, + ty::Never + | ty::Error(_) + | ty::Foreign(..) + | ty::Infer(ty::FreshIntTy(_)) + | ty::Infer(ty::FreshFloatTy(_)) + // FIXME(oli-obk): we could look behind opaque types + | ty::Alias(..) + | ty::Param(_) + | ty::Bound(..) + | ty::Placeholder(..) + | ty::Infer(_) + // FIXME(oli-obk): we can probably encode closures just like structs + | ty::Closure(..) + | ty::Coroutine(..) + | ty::CoroutineWitness(..) => Some(true), + _ => None, + } +} + impl ty::EarlyBoundRegion { /// Does this early bound region have a name? Early bound regions normally /// always have names except when using anonymous lifetimes (`'_`). diff --git a/tests/ui/consts/issue-116796.rs b/tests/ui/consts/issue-116796.rs new file mode 100644 index 0000000000000..32dc127ac2a50 --- /dev/null +++ b/tests/ui/consts/issue-116796.rs @@ -0,0 +1,3 @@ +struct X; +//~^ ERROR using function pointers as const generic parameters is forbidden +fn main() {} diff --git a/tests/ui/consts/issue-116796.stderr b/tests/ui/consts/issue-116796.stderr new file mode 100644 index 0000000000000..dceadf53e3ac6 --- /dev/null +++ b/tests/ui/consts/issue-116796.stderr @@ -0,0 +1,10 @@ +error: using function pointers as const generic parameters is forbidden + --> $DIR/issue-116796.rs:1:20 + | +LL | struct X; + | ^^^^ + | + = note: the only supported types are integers, `bool` and `char` + +error: aborting due to previous error +