Rollup merge of #156403 - SpriteOvO:type-info-refactor-variant, r=oli-obk

Add `TypeId` methods `variants` `fields` `field` for `type_info`

Tracking issue rust-lang/rust#146922

- Adds `fn TypeId::variants` returns the number of variants, for struct and union and primitive types, it's always 1.
- Adds `fn TypeId::fields` returns the number of fields.
- Adds `fn TypeId::field` returns a field representing type `FieldId`.
- Adds a new type `FieldId`, which is a wrapper of `FieldRepresentingType`'s `TypeId`.

For methods `{fields,field}`, if indexing out of bounds, a compile-time error will be raised.

Regarding the removal of `Type` items, this will be done in a later PR in one go.

r? @oli-obk
This commit is contained in:
Matthias Krüger
2026-05-28 07:53:35 +02:00
committed by GitHub
9 changed files with 456 additions and 4 deletions
+1 -1
View File
@@ -829,7 +829,7 @@ pub const fn trait_info_of_trait_type_id(
}
}
fn as_u128(self) -> u128 {
pub(crate) fn as_u128(self) -> u128 {
let mut bytes = [0; 16];
// This is a provenance-stripping memcpy.
+48 -2
View File
@@ -2941,11 +2941,57 @@ pub const fn type_id_eq(a: crate::any::TypeId, b: crate::any::TypeId) -> bool {
/// Gets the size of the type represented by this `TypeId`.
///
/// The stabilized version of this intrinsic is [`core::any::TypeId::size`].
/// The more user-friendly version of this intrinsic is [`core::any::TypeId::size`].
#[rustc_intrinsic]
#[unstable(feature = "core_intrinsics", issue = "none")]
pub const fn size_of_type_id(_id: crate::any::TypeId) -> Option<usize> {
panic!("`Type::size` can only be called at compile-time")
panic!("`TypeId::size` can only be called at compile-time")
}
/// Gets the number of variants of the type represented by this `TypeId`.
///
/// The more user-friendly version of this intrinsic is [`core::any::TypeId::variants`].
#[rustc_intrinsic]
#[unstable(feature = "core_intrinsics", issue = "none")]
pub const fn type_id_variants(_id: crate::any::TypeId) -> usize {
panic!("`TypeId::variants` can only be called at compile-time")
}
/// Gets the number of fields at the given `variant_index` represented by this `TypeId`.
///
/// The more user-friendly version of this intrinsic is [`core::any::TypeId::fields`].
#[rustc_intrinsic]
#[unstable(feature = "core_intrinsics", issue = "none")]
pub const fn type_id_fields(_id: crate::any::TypeId, _variant_index: usize) -> usize {
panic!("`TypeId::fields` can only be called at compile-time")
}
/// Gets the [`FieldRepresentingType`]'s `TypeId` at the given index of the type represented by this `TypeId`.
///
/// The more user-friendly version of this intrinsic is [`core::any::TypeId::field`].
///
/// [`FieldRepresentingType`]: crate::field::FieldRepresentingType
#[rustc_intrinsic]
#[unstable(feature = "core_intrinsics", issue = "none")]
pub const fn type_id_field_representing_type(
_id: crate::any::TypeId,
_variant_index: usize,
_field_index: usize,
) -> crate::any::TypeId {
panic!("`TypeId::field` can only be called at compile-time")
}
/// Gets the actual field `TypeId` of the [`FieldRepresentingType`]'s `TypeId`.
///
/// The more user-friendly version of this intrinsic is [`core::mem::type_info::FieldId::type_id`].
///
/// [`FieldRepresentingType`]: crate::field::FieldRepresentingType
#[rustc_intrinsic]
#[unstable(feature = "core_intrinsics", issue = "none")]
pub const fn field_representing_type_actual_type_id(
_frt_type_id: crate::any::TypeId,
) -> crate::any::TypeId {
panic!("`FieldId::type_id` can only be called at compile-time")
}
/// Lowers in MIR to `Rvalue::Aggregate` with `AggregateKind::RawPtr`.
+198
View File
@@ -2,6 +2,7 @@
//! runtime or const-eval processable way.
use crate::any::TypeId;
use crate::fmt;
use crate::intrinsics::{self, type_id, type_of};
use crate::marker::PointeeSized;
use crate::ptr::DynMetadata;
@@ -376,4 +377,201 @@ impl TypeId {
pub const fn size(self) -> Option<usize> {
intrinsics::size_of_type_id(self)
}
/// Returns the number of variants of the type represented by this `TypeId`.
///
/// For enums, this is the number of variants. For structs and unions, this is always 1.
///
/// ```
/// #![feature(type_info)]
/// use std::any::TypeId;
///
/// assert_eq!(const { TypeId::of::<Option<()>>().variants() }, 2);
///
/// struct Unit;
/// struct Point {
/// x: u32,
/// y: u32,
/// }
/// assert_eq!(const { TypeId::of::<Unit>().variants() }, 1);
/// assert_eq!(const { TypeId::of::<Point>().variants() }, 1);
/// assert_eq!(const { TypeId::of::<(f32, f32)>().variants() }, 1);
/// ```
#[unstable(feature = "type_info", issue = "146922")]
#[rustc_const_unstable(feature = "type_info", issue = "146922")]
pub const fn variants(self) -> usize {
intrinsics::type_id_variants(self)
}
/// Returns the number of fields at the given `variant_index` of the type represented by this `TypeId`.
///
/// ```
/// #![feature(type_info)]
/// use std::any::TypeId;
///
/// assert_eq!(const { TypeId::of::<u32>().fields(0) }, 0);
///
/// struct Point {
/// x: u32,
/// y: u32,
/// }
/// assert_eq!(const { TypeId::of::<Point>().fields(0) }, 2);
///
/// enum Enum {
/// Unit,
/// Tuple(u32, u64),
/// Struct { x: u32, y: u32, z: String },
/// }
/// assert_eq!(const { TypeId::of::<Enum>().fields(0) }, 0);
/// assert_eq!(const { TypeId::of::<Enum>().fields(1) }, 2);
/// assert_eq!(const { TypeId::of::<Enum>().fields(2) }, 3);
/// ```
///
/// The variant index refers to the source order index of a variant in a type.
///
/// For enums, these are always `0..variant_count`, regardless of any custom discriminants that may have been defined.
/// `struct`s, `tuples`, and `unions`s are considered to have a single variant with variant index zero.
///
/// ```
/// enum Number {
/// Seven = 7, // variant index == 0
/// Six = 6, // variant index == 1
/// }
/// ```
///
/// Out-of-bounds indexing will be treated as a compile-time error.
///
/// ```compile_fail,E0080
/// # #![feature(type_info)]
/// # use std::any::TypeId;
/// #
/// # struct Point {
/// # x: u32,
/// # y: u32,
/// # }
/// # enum Enum {
/// # Unit,
/// # Tuple(u32, u64),
/// # Struct { x: u32, y: u32, z: String },
/// # }
/// const {
/// _ = TypeId::of::<Point>().fields(10); // error: indexing out of bounds: the len is 2 but the index is 10
/// _ = TypeId::of::<Enum>().fields(10); // error: indexing out of bounds: the len is 3 but the index is 10
/// }
/// ```
#[unstable(feature = "type_info", issue = "146922")]
#[rustc_const_unstable(feature = "type_info", issue = "146922")]
pub const fn fields(self, variant_index: usize) -> usize {
intrinsics::type_id_fields(self, variant_index)
}
/// Returns the field representing type at the given index of the type represented by this `TypeId`.
///
/// ```
/// #![feature(type_info)]
/// use std::any::TypeId;
///
/// struct Point {
/// x: u32,
/// y: u32,
/// }
/// assert_eq!(const { TypeId::of::<Point>().field(0, 0).type_id() }, TypeId::of::<u32>());
/// assert_eq!(const { TypeId::of::<Point>().field(0, 1).type_id() }, TypeId::of::<u32>());
///
/// enum Enum {
/// Unit,
/// Tuple(u32, u64),
/// Struct { x: u32, y: u32, z: String },
/// }
/// assert_eq!(const { TypeId::of::<Enum>().field(1, 0).type_id() }, TypeId::of::<u32>());
/// assert_eq!(const { TypeId::of::<Enum>().field(2, 2).type_id() }, TypeId::of::<String>());
/// ```
///
/// The variant index and field index refer to the source order index of a variant in a type and
/// the source order index of a field in a variant, respectively.
///
/// For enums, variant indexes are always `0..variant_count`, regardless of any custom discriminants that may have been defined.
/// `struct`s, `tuples`, and `unions`s are considered to have a single variant with variant index zero.
///
/// As for field indexes, they may not be the same as the layout order for `repr(Rust)` types, but they are for `repr(C)` types.
///
/// ```
/// enum Enum {
/// Foo, // variant index == 0
/// Bar { // variant index == 1
/// a: (), // field index == 0 in `Bar`
/// b: (), // field index == 1 in `Bar`
/// }
/// }
/// ```
///
/// Out-of-bounds indexing will be treated as a compile-time error.
///
/// ```compile_fail,E0080
/// # #![feature(type_info)]
/// # use std::any::TypeId;
/// #
/// # struct Point {
/// # x: u32,
/// # y: u32,
/// # }
/// # enum Enum {
/// # Unit,
/// # Tuple(u32, u64),
/// # Struct { x: u32, y: u32, z: String },
/// # }
/// const {
/// _ = TypeId::of::<Point>().field(0, 10); // error: indexing out of bounds: the len is 2 but the index is 10
/// _ = TypeId::of::<Enum>().field(2, 10); // error: indexing out of bounds: the len is 3 but the index is 10
/// }
/// ```
#[unstable(feature = "type_info", issue = "146922")]
#[rustc_const_unstable(feature = "type_info", issue = "146922")]
pub const fn field(self, variant_index: usize, field_index: usize) -> FieldId {
FieldId {
frt_type_id: intrinsics::type_id_field_representing_type(
self,
variant_index,
field_index,
),
}
}
}
/// Field representing type ID. Representing a field of a struct, tuple or enum variant.
#[derive(Copy, PartialOrd, Ord, Hash)]
#[derive_const(Clone, PartialEq, Eq)]
#[unstable(feature = "type_info", issue = "146922")]
pub struct FieldId {
frt_type_id: TypeId,
}
#[unstable(feature = "type_info", issue = "146922")]
impl fmt::Debug for FieldId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "FieldId({:#034x})", self.frt_type_id.as_u128())
}
}
impl FieldId {
/// Returns the `TypeId` of the actual field type.
///
/// ```
/// #![feature(type_info)]
/// use std::any::TypeId;
///
/// struct Point {
/// x: u32,
/// y: u32,
/// }
/// assert_eq!(
/// const { TypeId::of::<Point>().field(0, 0).type_id() },
/// TypeId::of::<u32>()
/// );
/// ```
#[unstable(feature = "type_info", issue = "146922")]
#[rustc_const_unstable(feature = "type_info", issue = "146922")]
pub const fn type_id(self) -> TypeId {
intrinsics::field_representing_type_actual_type_id(self.frt_type_id)
}
}
+55
View File
@@ -67,6 +67,26 @@ fn assert_tuple_arity<T: 'static, const N: usize>() {
_ => unreachable!(),
}
}
const {
let ty_id = TypeId::of::<()>();
assert!(ty_id.size() == Some(size_of::<()>()));
assert!(ty_id.variants() == 1);
assert!(ty_id.fields(0) == 0);
let ty_id = TypeId::of::<(u8,)>();
assert!(ty_id.size() == Some(size_of::<(u8,)>()));
assert!(ty_id.variants() == 1);
assert!(ty_id.fields(0) == 1);
assert!(ty_id.field(0, 0).type_id() == TypeId::of::<u8>());
let ty_id = TypeId::of::<(u8, u16)>();
assert!(ty_id.size() == Some(size_of::<(u8, u16)>()));
assert!(ty_id.variants() == 1);
assert!(ty_id.fields(0) == 2);
assert!(ty_id.field(0, 0).type_id() == TypeId::of::<u8>());
assert!(ty_id.field(0, 1).type_id() == TypeId::of::<u16>());
}
}
#[test]
@@ -95,6 +115,11 @@ struct TestStruct {
let ty_id = TypeId::of::<TestStruct>();
assert!(ty_id.size() == Some(size_of::<TestStruct>()));
assert!(ty_id.variants() == 1);
assert!(ty_id.fields(0) == 3);
assert!(ty_id.field(0, 0).type_id() == TypeId::of::<u8>());
assert!(ty_id.field(0, 1).type_id() == TypeId::of::<u16>());
assert!(ty_id.field(0, 2).type_id() == TypeId::of::<&u16>());
}
const {
@@ -116,6 +141,13 @@ struct NonExhaustive {
assert!(ty.fields[0].ty == TypeId::of::<u8>());
assert!(ty.fields[1].name == "1");
assert!(ty.fields[1].ty == TypeId::of::<u16>());
let ty_id = TypeId::of::<TupleStruct>();
assert!(ty_id.size() == Some(size_of::<TupleStruct>()));
assert!(ty_id.variants() == 1);
assert!(ty_id.fields(0) == 2);
assert!(ty_id.field(0, 0).type_id() == TypeId::of::<u8>());
assert!(ty_id.field(0, 1).type_id() == TypeId::of::<u16>());
}
const {
@@ -156,6 +188,10 @@ union TestUnion {
let ty_id = TypeId::of::<TestUnion>();
assert!(ty_id.size() == Some(size_of::<TestUnion>()));
assert!(ty_id.variants() == 1);
assert!(ty_id.fields(0) == 2);
assert!(ty_id.field(0, 0).type_id() == TypeId::of::<i16>());
assert!(ty_id.field(0, 1).type_id() == TypeId::of::<u16>());
}
const {
@@ -212,6 +248,13 @@ enum E {
let ty_id = TypeId::of::<E>();
assert!(ty_id.size() == Some(size_of::<E>()));
assert!(ty_id.variants() == 3);
assert!(ty_id.fields(0) == 1);
assert!(ty_id.fields(1) == 0);
assert!(ty_id.fields(2) == 2);
assert!(ty_id.field(0, 0).type_id() == TypeId::of::<u32>());
assert!(ty_id.field(2, 0).type_id() == TypeId::of::<()>());
assert!(ty_id.field(2, 1).type_id() == TypeId::of::<&str>());
}
const {
@@ -223,6 +266,10 @@ enum E {
let ty_id = TypeId::of::<Option<i32>>();
assert!(ty_id.size() == Some(size_of::<Option<i32>>()));
assert!(ty_id.variants() == 2);
assert!(ty_id.fields(0) == 0);
assert!(ty_id.fields(1) == 1);
assert!(ty_id.field(1, 0).type_id() == TypeId::of::<i32>());
}
}
@@ -234,43 +281,51 @@ fn test_primitives() {
let Type { kind: Bool(_ty), .. } = (const { Type::of::<bool>() }) else { panic!() };
let ty_id = TypeId::of::<bool>();
assert!(ty_id.size() == Some(size_of::<bool>()));
assert!(ty_id.variants() == 1);
let Type { kind: Char(_ty), .. } = (const { Type::of::<char>() }) else { panic!() };
let ty_id = TypeId::of::<char>();
assert!(ty_id.size() == Some(size_of::<char>()));
assert!(ty_id.variants() == 1);
let Type { kind: Int(ty), .. } = (const { Type::of::<i32>() }) else { panic!() };
assert!(ty.bits == 32);
assert!(ty.signed);
let ty_id = TypeId::of::<i32>();
assert!(ty_id.size() == Some(size_of::<i32>()));
assert!(ty_id.variants() == 1);
let Type { kind: Int(ty), .. } = (const { Type::of::<isize>() }) else { panic!() };
assert!(ty.bits as usize == size_of::<isize>() * 8);
assert!(ty.signed);
let ty_id = TypeId::of::<isize>();
assert!(ty_id.size() == Some(size_of::<isize>()));
assert!(ty_id.variants() == 1);
let Type { kind: Int(ty), .. } = (const { Type::of::<u32>() }) else { panic!() };
assert!(ty.bits == 32);
assert!(!ty.signed);
let ty_id = TypeId::of::<u32>();
assert!(ty_id.size() == Some(size_of::<u32>()));
assert!(ty_id.variants() == 1);
let Type { kind: Int(ty), .. } = (const { Type::of::<usize>() }) else { panic!() };
assert!(ty.bits as usize == size_of::<usize>() * 8);
assert!(!ty.signed);
let ty_id = TypeId::of::<usize>();
assert!(ty_id.size() == Some(size_of::<usize>()));
assert!(ty_id.variants() == 1);
let Type { kind: Float(ty), .. } = (const { Type::of::<f32>() }) else { panic!() };
assert!(ty.bits == 32);
let ty_id = TypeId::of::<f32>();
assert!(ty_id.size() == Some(size_of::<f32>()));
assert!(ty_id.variants() == 1);
let Type { kind: Str(_ty), .. } = (const { Type::of::<str>() }) else { panic!() };
let ty_id = TypeId::of::<str>();
assert!(ty_id.size() == None);
assert!(ty_id.variants() == 1);
}
}