From 7a96d86fb76789f5ad35e837d90ce1b7e31c3d55 Mon Sep 17 00:00:00 2001 From: Asuna Date: Thu, 7 May 2026 10:39:44 +0000 Subject: [PATCH 1/5] Fix typos in `intrinsics::size_of_type_id` panic message and doc comment --- library/core/src/intrinsics/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 1aeb1a0eb397..411483b65cda 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -2939,11 +2939,11 @@ 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 { - panic!("`Type::size` can only be called at compile-time") + panic!("`TypeId::size` can only be called at compile-time") } /// Lowers in MIR to `Rvalue::Aggregate` with `AggregateKind::RawPtr`. From d1ad6292d6f5a9cfb0c1b8552d2ae87468cacb9c Mon Sep 17 00:00:00 2001 From: Asuna Date: Thu, 7 May 2026 11:10:02 +0000 Subject: [PATCH 2/5] Add `TypeId::variants` method to get number of variants for `type_info` --- .../src/const_eval/machine.rs | 6 +++++ .../rustc_hir_analysis/src/check/intrinsic.rs | 2 ++ compiler/rustc_span/src/symbol.rs | 1 + library/core/src/intrinsics/mod.rs | 9 +++++++ library/core/src/mem/type_info.rs | 25 +++++++++++++++++++ library/coretests/tests/mem/type_info.rs | 12 +++++++++ 6 files changed, 55 insertions(+) diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index fb456d80e465..e406523122f0 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -622,6 +622,12 @@ fn call_intrinsic( ecx.write_discriminant(variant_index, dest)?; } + sym::type_id_variants => { + let ty = ecx.read_type_id(&args[0])?; + let variants_num = ty.ty_adt_def().map(|def| def.variants().len()).unwrap_or(1); + ecx.write_scalar(Scalar::from_target_usize(variants_num as u64, ecx), dest)?; + } + sym::field_offset => { let frt_ty = instance.args.type_at(0); ensure_monomorphic_enough(ecx.tcx.tcx, frt_ty)?; diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 9059070157fa..528ccad1e618 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -213,6 +213,7 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi | sym::truncf128 | sym::type_id | sym::type_id_eq + | sym::type_id_variants | sym::type_id_vtable | sym::type_name | sym::type_of @@ -319,6 +320,7 @@ pub(crate) fn check_intrinsic_type( sym::type_name => (1, 0, vec![], Ty::new_static_str(tcx)), sym::type_id => (1, 0, vec![], type_id_ty()), sym::type_id_eq => (0, 0, vec![type_id_ty(), type_id_ty()], tcx.types.bool), + sym::type_id_variants => (0, 0, vec![type_id_ty()], tcx.types.usize), sym::type_id_vtable => { let dyn_metadata = tcx.require_lang_item(LangItem::DynMetadata, span); let dyn_metadata_adt_ref = tcx.adt_def(dyn_metadata); diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 72339efd0a13..b71bc9e96561 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -2098,6 +2098,7 @@ type_changing_struct_update, type_id, type_id_eq, + type_id_variants, type_id_vtable, type_info, type_ir, diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 411483b65cda..c549fea53741 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -2946,6 +2946,15 @@ pub const fn size_of_type_id(_id: crate::any::TypeId) -> Option { 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") +} + /// Lowers in MIR to `Rvalue::Aggregate` with `AggregateKind::RawPtr`. /// /// This is used to implement functions like `slice::from_raw_parts_mut` and diff --git a/library/core/src/mem/type_info.rs b/library/core/src/mem/type_info.rs index 17c51ccaad85..6718cc4aed5b 100644 --- a/library/core/src/mem/type_info.rs +++ b/library/core/src/mem/type_info.rs @@ -376,4 +376,29 @@ impl TypeId { pub const fn size(self) -> Option { 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::>().variants() }, 2); + /// + /// struct Unit; + /// struct Point { + /// x: u32, + /// y: u32, + /// } + /// assert_eq!(const { TypeId::of::().variants() }, 1); + /// assert_eq!(const { TypeId::of::().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) + } } diff --git a/library/coretests/tests/mem/type_info.rs b/library/coretests/tests/mem/type_info.rs index e02077b96d35..986166c5a488 100644 --- a/library/coretests/tests/mem/type_info.rs +++ b/library/coretests/tests/mem/type_info.rs @@ -95,6 +95,7 @@ struct TestStruct { let ty_id = TypeId::of::(); assert!(ty_id.size() == Some(size_of::())); + assert!(ty_id.variants() == 1); } const { @@ -156,6 +157,7 @@ union TestUnion { let ty_id = TypeId::of::(); assert!(ty_id.size() == Some(size_of::())); + assert!(ty_id.variants() == 1); } const { @@ -212,6 +214,7 @@ enum E { let ty_id = TypeId::of::(); assert!(ty_id.size() == Some(size_of::())); + assert!(ty_id.variants() == 3); } const { @@ -223,6 +226,7 @@ enum E { let ty_id = TypeId::of::>(); assert!(ty_id.size() == Some(size_of::>())); + assert!(ty_id.variants() == 2); } } @@ -234,43 +238,51 @@ fn test_primitives() { let Type { kind: Bool(_ty), .. } = (const { Type::of::() }) else { panic!() }; let ty_id = TypeId::of::(); assert!(ty_id.size() == Some(size_of::())); + assert!(ty_id.variants() == 1); let Type { kind: Char(_ty), .. } = (const { Type::of::() }) else { panic!() }; let ty_id = TypeId::of::(); assert!(ty_id.size() == Some(size_of::())); + assert!(ty_id.variants() == 1); let Type { kind: Int(ty), .. } = (const { Type::of::() }) else { panic!() }; assert!(ty.bits == 32); assert!(ty.signed); let ty_id = TypeId::of::(); assert!(ty_id.size() == Some(size_of::())); + assert!(ty_id.variants() == 1); let Type { kind: Int(ty), .. } = (const { Type::of::() }) else { panic!() }; assert!(ty.bits as usize == size_of::() * 8); assert!(ty.signed); let ty_id = TypeId::of::(); assert!(ty_id.size() == Some(size_of::())); + assert!(ty_id.variants() == 1); let Type { kind: Int(ty), .. } = (const { Type::of::() }) else { panic!() }; assert!(ty.bits == 32); assert!(!ty.signed); let ty_id = TypeId::of::(); assert!(ty_id.size() == Some(size_of::())); + assert!(ty_id.variants() == 1); let Type { kind: Int(ty), .. } = (const { Type::of::() }) else { panic!() }; assert!(ty.bits as usize == size_of::() * 8); assert!(!ty.signed); let ty_id = TypeId::of::(); assert!(ty_id.size() == Some(size_of::())); + assert!(ty_id.variants() == 1); let Type { kind: Float(ty), .. } = (const { Type::of::() }) else { panic!() }; assert!(ty.bits == 32); let ty_id = TypeId::of::(); assert!(ty_id.size() == Some(size_of::())); + assert!(ty_id.variants() == 1); let Type { kind: Str(_ty), .. } = (const { Type::of::() }) else { panic!() }; let ty_id = TypeId::of::(); assert!(ty_id.size() == None); + assert!(ty_id.variants() == 1); } } From b50aae66b8a76e3b57cf8c1282a89e9b92fd6305 Mon Sep 17 00:00:00 2001 From: Asuna Date: Thu, 7 May 2026 12:26:47 +0000 Subject: [PATCH 3/5] Add `TypeId::fields` method to get number of fields for `type_info` --- .../src/const_eval/machine.rs | 27 +++++++- .../rustc_hir_analysis/src/check/intrinsic.rs | 2 + compiler/rustc_span/src/symbol.rs | 1 + library/core/src/intrinsics/mod.rs | 9 +++ library/core/src/mem/type_info.rs | 62 +++++++++++++++++++ library/coretests/tests/mem/type_info.rs | 24 +++++++ .../reflection/variant_index_out_of_bounds.rs | 10 +++ .../variant_index_out_of_bounds.stderr | 22 +++++++ 8 files changed, 156 insertions(+), 1 deletion(-) create mode 100644 tests/ui/reflection/variant_index_out_of_bounds.rs create mode 100644 tests/ui/reflection/variant_index_out_of_bounds.stderr diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index e406523122f0..da34f073795d 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -2,7 +2,7 @@ use std::hash::Hash; use std::{fmt, mem}; -use rustc_abi::{Align, FIRST_VARIANT, FieldIdx, Size}; +use rustc_abi::{Align, FIRST_VARIANT, FieldIdx, Size, VariantIdx}; use rustc_ast::Mutability; use rustc_data_structures::fx::{FxHashMap, FxIndexMap, IndexEntry}; use rustc_hir::def_id::{DefId, LocalDefId}; @@ -622,6 +622,31 @@ fn call_intrinsic( ecx.write_discriminant(variant_index, dest)?; } + sym::type_id_fields => { + let ty = ecx.read_type_id(&args[0])?; + let variant_idx = ecx.read_target_usize(&args[1])? as usize; + + let variants_num = + ty.ty_adt_def().map(|adt_def| adt_def.variants().len()).unwrap_or(1); + if variant_idx >= variants_num { + throw_ub!(BoundsCheckFailed { + len: variants_num as u64, + index: variant_idx as u64 + }); + } + + let fields_num = match ty.kind() { + ty::Adt(adt_def, _) => { + let variant_def = &adt_def.variants()[VariantIdx::from_usize(variant_idx)]; + variant_def.fields.len() + } + ty::Tuple(fields) => fields.len(), + _ => 0, // Other types have no fields + }; + + ecx.write_scalar(Scalar::from_target_usize(fields_num as u64, ecx), dest)?; + } + sym::type_id_variants => { let ty = ecx.read_type_id(&args[0])?; let variants_num = ty.ty_adt_def().map(|def| def.variants().len()).unwrap_or(1); diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 528ccad1e618..99f1750f9ccc 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -213,6 +213,7 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi | sym::truncf128 | sym::type_id | sym::type_id_eq + | sym::type_id_fields | sym::type_id_variants | sym::type_id_vtable | sym::type_name @@ -320,6 +321,7 @@ pub(crate) fn check_intrinsic_type( sym::type_name => (1, 0, vec![], Ty::new_static_str(tcx)), sym::type_id => (1, 0, vec![], type_id_ty()), sym::type_id_eq => (0, 0, vec![type_id_ty(), type_id_ty()], tcx.types.bool), + sym::type_id_fields => (0, 0, vec![type_id_ty(), tcx.types.usize], tcx.types.usize), sym::type_id_variants => (0, 0, vec![type_id_ty()], tcx.types.usize), sym::type_id_vtable => { let dyn_metadata = tcx.require_lang_item(LangItem::DynMetadata, span); diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index b71bc9e96561..540d30a215f5 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -2098,6 +2098,7 @@ type_changing_struct_update, type_id, type_id_eq, + type_id_fields, type_id_variants, type_id_vtable, type_info, diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index c549fea53741..1fe7b839597e 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -2955,6 +2955,15 @@ 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") +} + /// Lowers in MIR to `Rvalue::Aggregate` with `AggregateKind::RawPtr`. /// /// This is used to implement functions like `slice::from_raw_parts_mut` and diff --git a/library/core/src/mem/type_info.rs b/library/core/src/mem/type_info.rs index 6718cc4aed5b..a03d4ad1d6d0 100644 --- a/library/core/src/mem/type_info.rs +++ b/library/core/src/mem/type_info.rs @@ -401,4 +401,66 @@ pub const fn size(self) -> Option { 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::().fields(0) }, 0); + /// + /// struct Point { + /// x: u32, + /// y: u32, + /// } + /// assert_eq!(const { TypeId::of::().fields(0) }, 2); + /// + /// enum Enum { + /// Unit, + /// Tuple(u32, u64), + /// Struct { x: u32, y: u32, z: String }, + /// } + /// assert_eq!(const { TypeId::of::().fields(0) }, 0); + /// assert_eq!(const { TypeId::of::().fields(1) }, 2); + /// assert_eq!(const { TypeId::of::().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::().fields(10); // error: indexing out of bounds: the len is 2 but the index is 10 + /// _ = TypeId::of::().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) + } } diff --git a/library/coretests/tests/mem/type_info.rs b/library/coretests/tests/mem/type_info.rs index 986166c5a488..ab5b8d8ffe57 100644 --- a/library/coretests/tests/mem/type_info.rs +++ b/library/coretests/tests/mem/type_info.rs @@ -67,6 +67,23 @@ fn assert_tuple_arity() { _ => 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); + + let ty_id = TypeId::of::<(u8, u8)>(); + assert!(ty_id.size() == Some(size_of::<(u8, u8)>())); + assert!(ty_id.variants() == 1); + assert!(ty_id.fields(0) == 2); + } } #[test] @@ -96,6 +113,7 @@ struct TestStruct { let ty_id = TypeId::of::(); assert!(ty_id.size() == Some(size_of::())); assert!(ty_id.variants() == 1); + assert!(ty_id.fields(0) == 3); } const { @@ -158,6 +176,7 @@ union TestUnion { let ty_id = TypeId::of::(); assert!(ty_id.size() == Some(size_of::())); assert!(ty_id.variants() == 1); + assert!(ty_id.fields(0) == 2); } const { @@ -215,6 +234,9 @@ enum E { let ty_id = TypeId::of::(); assert!(ty_id.size() == Some(size_of::())); assert!(ty_id.variants() == 3); + assert!(ty_id.fields(0) == 1); + assert!(ty_id.fields(1) == 0); + assert!(ty_id.fields(2) == 2); } const { @@ -227,6 +249,8 @@ enum E { let ty_id = TypeId::of::>(); assert!(ty_id.size() == Some(size_of::>())); assert!(ty_id.variants() == 2); + assert!(ty_id.fields(0) == 0); + assert!(ty_id.fields(1) == 1); } } diff --git a/tests/ui/reflection/variant_index_out_of_bounds.rs b/tests/ui/reflection/variant_index_out_of_bounds.rs new file mode 100644 index 000000000000..3f25e5205084 --- /dev/null +++ b/tests/ui/reflection/variant_index_out_of_bounds.rs @@ -0,0 +1,10 @@ +#![feature(type_info)] + +use std::any::TypeId; + +fn main() {} + +const _: () = const { + TypeId::of::>().fields(2); + //~^ ERROR indexing out of bounds: the len is 2 but the index is 2 +}; diff --git a/tests/ui/reflection/variant_index_out_of_bounds.stderr b/tests/ui/reflection/variant_index_out_of_bounds.stderr new file mode 100644 index 000000000000..dec9a8d1393f --- /dev/null +++ b/tests/ui/reflection/variant_index_out_of_bounds.stderr @@ -0,0 +1,22 @@ +error[E0080]: indexing out of bounds: the len is 2 but the index is 2 + --> $DIR/variant_index_out_of_bounds.rs:8:5 + | +LL | TypeId::of::>().fields(2); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `_::{constant#0}` failed inside this call + | +note: inside `type_info::::fields` + --> $SRC_DIR/core/src/mem/type_info.rs:LL:COL + +note: erroneous constant encountered + --> $DIR/variant_index_out_of_bounds.rs:7:15 + | +LL | const _: () = const { + | _______________^ +LL | | TypeId::of::>().fields(2); +LL | | +LL | | }; + | |_^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. From 28733aa84bda079d2e342404548d913032f623b4 Mon Sep 17 00:00:00 2001 From: Asuna Date: Sun, 10 May 2026 14:23:30 +0000 Subject: [PATCH 4/5] Add `TypeId::field` method to get a field representing type for `type_info` --- .../src/const_eval/machine.rs | 47 ++++++++++ .../rustc_hir_analysis/src/check/intrinsic.rs | 4 + compiler/rustc_span/src/symbol.rs | 1 + library/core/src/any.rs | 2 +- library/core/src/intrinsics/mod.rs | 13 +++ library/core/src/mem/type_info.rs | 91 +++++++++++++++++++ library/coretests/tests/mem/type_info.rs | 23 ++++- .../reflection/variant_index_out_of_bounds.rs | 5 + .../variant_index_out_of_bounds.stderr | 21 ++++- 9 files changed, 203 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index da34f073795d..f37c57c73de7 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -647,6 +647,53 @@ fn call_intrinsic( ecx.write_scalar(Scalar::from_target_usize(fields_num as u64, ecx), dest)?; } + sym::type_id_field => { + let ty = ecx.read_type_id(&args[0])?; + let variant_idx = ecx.read_target_usize(&args[1])? as usize; + let field_idx = ecx.read_target_usize(&args[2])? as usize; + + let variants_num = + ty.ty_adt_def().map(|adt_def| adt_def.variants().len()).unwrap_or(1); + if variant_idx >= variants_num { + throw_ub!(BoundsCheckFailed { + len: variants_num as u64, + index: variant_idx as u64 + }); + } + + let field_ty = match ty.kind() { + ty::Adt(adt_def, generics) => { + let variant_def = &adt_def.variants()[VariantIdx::from_usize(variant_idx)]; + let fields_num = variant_def.fields.len(); + if field_idx >= fields_num { + throw_ub!(BoundsCheckFailed { + len: fields_num as u64, + index: field_idx as u64 + }); + } + let field_def = &variant_def.fields[FieldIdx::from_usize(field_idx)]; + field_def.ty(ecx.tcx.tcx, generics).skip_norm_wip() + } + ty::Tuple(fields) => { + let fields_num = fields.len(); + if field_idx >= fields_num { + throw_ub!(BoundsCheckFailed { + len: fields_num as u64, + index: field_idx as u64 + }); + } + fields[field_idx] + } + _ => { + // Other types have no fields, so any field index is out of bounds. + throw_ub!(BoundsCheckFailed { len: 0, index: field_idx as u64 }); + } + }; + + let field_ty = ecx.tcx.erase_and_anonymize_regions(field_ty); + ecx.write_type_id(field_ty, dest)?; + } + sym::type_id_variants => { let ty = ecx.read_type_id(&args[0])?; let variants_num = ty.ty_adt_def().map(|def| def.variants().len()).unwrap_or(1); diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 99f1750f9ccc..b37aa4fac4e9 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -213,6 +213,7 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi | sym::truncf128 | sym::type_id | sym::type_id_eq + | sym::type_id_field | sym::type_id_fields | sym::type_id_variants | sym::type_id_vtable @@ -321,6 +322,9 @@ pub(crate) fn check_intrinsic_type( sym::type_name => (1, 0, vec![], Ty::new_static_str(tcx)), sym::type_id => (1, 0, vec![], type_id_ty()), sym::type_id_eq => (0, 0, vec![type_id_ty(), type_id_ty()], tcx.types.bool), + sym::type_id_field => { + (0, 0, vec![type_id_ty(), tcx.types.usize, tcx.types.usize], type_id_ty()) + } sym::type_id_fields => (0, 0, vec![type_id_ty(), tcx.types.usize], tcx.types.usize), sym::type_id_variants => (0, 0, vec![type_id_ty()], tcx.types.usize), sym::type_id_vtable => { diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 540d30a215f5..5d3b9ad64f10 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -2098,6 +2098,7 @@ type_changing_struct_update, type_id, type_id_eq, + type_id_field, type_id_fields, type_id_variants, type_id_vtable, diff --git a/library/core/src/any.rs b/library/core/src/any.rs index 62300d8b70a9..92e45c46c869 100644 --- a/library/core/src/any.rs +++ b/library/core/src/any.rs @@ -852,7 +852,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. diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 1fe7b839597e..9b5aa9d6255a 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -2964,6 +2964,19 @@ pub const fn type_id_fields(_id: crate::any::TypeId, _variant_index: usize) -> u panic!("`TypeId::fields` can only be called at compile-time") } +/// Gets the `TypeId` of the field at the given index of the type represented by this `TypeId`. +/// +/// The more user-friendly version of this intrinsic is [`core::any::TypeId::field`]. +#[rustc_intrinsic] +#[unstable(feature = "core_intrinsics", issue = "none")] +pub const fn type_id_field( + _id: crate::any::TypeId, + _variant_index: usize, + _field_index: usize, +) -> crate::any::TypeId { + panic!("`TypeId::field` can only be called at compile-time") +} + /// Lowers in MIR to `Rvalue::Aggregate` with `AggregateKind::RawPtr`. /// /// This is used to implement functions like `slice::from_raw_parts_mut` and diff --git a/library/core/src/mem/type_info.rs b/library/core/src/mem/type_info.rs index a03d4ad1d6d0..373eb81a201b 100644 --- a/library/core/src/mem/type_info.rs +++ b/library/core/src/mem/type_info.rs @@ -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; @@ -463,4 +464,94 @@ pub const fn variants(self) -> usize { 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::().field(0, 0).type_id() }, TypeId::of::()); + /// assert_eq!(const { TypeId::of::().field(0, 1).type_id() }, TypeId::of::()); + /// + /// enum Enum { + /// Unit, + /// Tuple(u32, u64), + /// Struct { x: u32, y: u32, z: String }, + /// } + /// assert_eq!(const { TypeId::of::().field(1, 0).type_id() }, TypeId::of::()); + /// assert_eq!(const { TypeId::of::().field(2, 2).type_id() }, TypeId::of::()); + /// ``` + /// + /// 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::().field(0, 10); // error: indexing out of bounds: the len is 2 but the index is 10 + /// _ = TypeId::of::().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 { type_id: intrinsics::type_id_field(self, variant_index, field_index) } + } +} + +/// `FieldId` is a wrapper of `TypeId`, 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 { + 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.type_id.as_u128()) + } +} + +impl FieldId { + /// Returns the `TypeId` of this field. + #[unstable(feature = "type_info", issue = "146922")] + #[rustc_const_unstable(feature = "type_info", issue = "146922")] + pub const fn type_id(self) -> TypeId { + self.type_id + } } diff --git a/library/coretests/tests/mem/type_info.rs b/library/coretests/tests/mem/type_info.rs index ab5b8d8ffe57..9a37a2ba0db5 100644 --- a/library/coretests/tests/mem/type_info.rs +++ b/library/coretests/tests/mem/type_info.rs @@ -78,11 +78,14 @@ fn assert_tuple_arity() { 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::()); - let ty_id = TypeId::of::<(u8, u8)>(); - assert!(ty_id.size() == Some(size_of::<(u8, 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::()); + assert!(ty_id.field(0, 1).type_id() == TypeId::of::()); } } @@ -114,6 +117,9 @@ struct TestStruct { assert!(ty_id.size() == Some(size_of::())); assert!(ty_id.variants() == 1); assert!(ty_id.fields(0) == 3); + assert!(ty_id.field(0, 0).type_id() == TypeId::of::()); + assert!(ty_id.field(0, 1).type_id() == TypeId::of::()); + assert!(ty_id.field(0, 2).type_id() == TypeId::of::<&u16>()); } const { @@ -135,6 +141,13 @@ struct NonExhaustive { assert!(ty.fields[0].ty == TypeId::of::()); assert!(ty.fields[1].name == "1"); assert!(ty.fields[1].ty == TypeId::of::()); + + let ty_id = TypeId::of::(); + assert!(ty_id.size() == Some(size_of::())); + assert!(ty_id.variants() == 1); + assert!(ty_id.fields(0) == 2); + assert!(ty_id.field(0, 0).type_id() == TypeId::of::()); + assert!(ty_id.field(0, 1).type_id() == TypeId::of::()); } const { @@ -177,6 +190,8 @@ union TestUnion { assert!(ty_id.size() == Some(size_of::())); assert!(ty_id.variants() == 1); assert!(ty_id.fields(0) == 2); + assert!(ty_id.field(0, 0).type_id() == TypeId::of::()); + assert!(ty_id.field(0, 1).type_id() == TypeId::of::()); } const { @@ -237,6 +252,9 @@ enum E { 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::()); + assert!(ty_id.field(2, 0).type_id() == TypeId::of::<()>()); + assert!(ty_id.field(2, 1).type_id() == TypeId::of::<&str>()); } const { @@ -251,6 +269,7 @@ enum E { 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::()); } } diff --git a/tests/ui/reflection/variant_index_out_of_bounds.rs b/tests/ui/reflection/variant_index_out_of_bounds.rs index 3f25e5205084..2e6ac7f65927 100644 --- a/tests/ui/reflection/variant_index_out_of_bounds.rs +++ b/tests/ui/reflection/variant_index_out_of_bounds.rs @@ -8,3 +8,8 @@ fn main() {} TypeId::of::>().fields(2); //~^ ERROR indexing out of bounds: the len is 2 but the index is 2 }; + +const _: () = const { + TypeId::of::>().field(0, 1); + //~^ ERROR indexing out of bounds: the len is 0 but the index is 1 +}; diff --git a/tests/ui/reflection/variant_index_out_of_bounds.stderr b/tests/ui/reflection/variant_index_out_of_bounds.stderr index dec9a8d1393f..177b662d84b7 100644 --- a/tests/ui/reflection/variant_index_out_of_bounds.stderr +++ b/tests/ui/reflection/variant_index_out_of_bounds.stderr @@ -17,6 +17,25 @@ LL | | LL | | }; | |_^ -error: aborting due to 1 previous error +error[E0080]: indexing out of bounds: the len is 0 but the index is 1 + --> $DIR/variant_index_out_of_bounds.rs:13:5 + | +LL | TypeId::of::>().field(0, 1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `_::{constant#0}` failed inside this call + | +note: inside `type_info::::field` + --> $SRC_DIR/core/src/mem/type_info.rs:LL:COL + +note: erroneous constant encountered + --> $DIR/variant_index_out_of_bounds.rs:12:15 + | +LL | const _: () = const { + | _______________^ +LL | | TypeId::of::>().field(0, 1); +LL | | +LL | | }; + | |_^ + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0080`. From b513e539c3df674885b9f5ae8844b60e1ced22d1 Mon Sep 17 00:00:00 2001 From: Asuna Date: Wed, 27 May 2026 22:25:09 +0000 Subject: [PATCH 5/5] `FieldId` wraps FRT `TypeId` instead of the actual field `TypeId` --- .../src/const_eval/machine.rs | 61 ++++++++++--------- .../rustc_hir_analysis/src/check/intrinsic.rs | 6 +- compiler/rustc_span/src/symbol.rs | 3 +- library/core/src/intrinsics/mod.rs | 19 +++++- library/core/src/mem/type_info.rs | 32 ++++++++-- 5 files changed, 82 insertions(+), 39 deletions(-) diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index f37c57c73de7..1dee2f34371e 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -647,7 +647,7 @@ fn call_intrinsic( ecx.write_scalar(Scalar::from_target_usize(fields_num as u64, ecx), dest)?; } - sym::type_id_field => { + sym::type_id_field_representing_type => { let ty = ecx.read_type_id(&args[0])?; let variant_idx = ecx.read_target_usize(&args[1])? as usize; let field_idx = ecx.read_target_usize(&args[2])? as usize; @@ -661,37 +661,28 @@ fn call_intrinsic( }); } - let field_ty = match ty.kind() { - ty::Adt(adt_def, generics) => { + let fields_num = match ty.kind() { + ty::Adt(adt_def, _) => { let variant_def = &adt_def.variants()[VariantIdx::from_usize(variant_idx)]; - let fields_num = variant_def.fields.len(); - if field_idx >= fields_num { - throw_ub!(BoundsCheckFailed { - len: fields_num as u64, - index: field_idx as u64 - }); - } - let field_def = &variant_def.fields[FieldIdx::from_usize(field_idx)]; - field_def.ty(ecx.tcx.tcx, generics).skip_norm_wip() - } - ty::Tuple(fields) => { - let fields_num = fields.len(); - if field_idx >= fields_num { - throw_ub!(BoundsCheckFailed { - len: fields_num as u64, - index: field_idx as u64 - }); - } - fields[field_idx] - } - _ => { - // Other types have no fields, so any field index is out of bounds. - throw_ub!(BoundsCheckFailed { len: 0, index: field_idx as u64 }); + variant_def.fields.len() } + ty::Tuple(fields) => fields.len(), + _ => 0, // Other types have no fields }; + if field_idx >= fields_num { + throw_ub!(BoundsCheckFailed { + len: fields_num as u64, + index: field_idx as u64 + }); + } - let field_ty = ecx.tcx.erase_and_anonymize_regions(field_ty); - ecx.write_type_id(field_ty, dest)?; + let frt = Ty::new_field_representing_type( + *ecx.tcx, + ty, + VariantIdx::from_usize(variant_idx), + FieldIdx::from_usize(field_idx), + ); + ecx.write_type_id(frt, dest)?; } sym::type_id_variants => { @@ -721,6 +712,20 @@ fn call_intrinsic( ecx.write_scalar(Scalar::from_target_usize(offset, ecx), dest)?; } + sym::field_representing_type_actual_type_id => { + let frt_ty = ecx.read_type_id(&args[0])?; + + let field_ty = if let ty::Adt(def, args) = frt_ty.kind() + && let Some(FieldInfo { ty, .. }) = + def.field_representing_type_info(ecx.tcx.tcx, args) + { + ecx.tcx.erase_and_anonymize_regions(ty) + } else { + span_bug!(ecx.cur_span(), "expected field representing type, got {frt_ty}") + }; + ecx.write_type_id(field_ty, dest)?; + } + _ => { // We haven't handled the intrinsic, let's see if we can use a fallback body. if ecx.tcx.intrinsic(instance.def_id()).unwrap().must_be_overridden { diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index b37aa4fac4e9..c1edbb4fc520 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -114,6 +114,7 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi | sym::fadd_algebraic | sym::fdiv_algebraic | sym::field_offset + | sym::field_representing_type_actual_type_id | sym::floorf16 | sym::floorf32 | sym::floorf64 @@ -213,7 +214,7 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi | sym::truncf128 | sym::type_id | sym::type_id_eq - | sym::type_id_field + | sym::type_id_field_representing_type | sym::type_id_fields | sym::type_id_variants | sym::type_id_vtable @@ -322,7 +323,7 @@ pub(crate) fn check_intrinsic_type( sym::type_name => (1, 0, vec![], Ty::new_static_str(tcx)), sym::type_id => (1, 0, vec![], type_id_ty()), sym::type_id_eq => (0, 0, vec![type_id_ty(), type_id_ty()], tcx.types.bool), - sym::type_id_field => { + sym::type_id_field_representing_type => { (0, 0, vec![type_id_ty(), tcx.types.usize, tcx.types.usize], type_id_ty()) } sym::type_id_fields => (0, 0, vec![type_id_ty(), tcx.types.usize], tcx.types.usize), @@ -347,6 +348,7 @@ pub(crate) fn check_intrinsic_type( vec![type_id_ty()], tcx.type_of(tcx.lang_items().type_struct().unwrap()).no_bound_vars().unwrap(), ), + sym::field_representing_type_actual_type_id => (0, 0, vec![type_id_ty()], type_id_ty()), sym::offload => ( 3, 0, diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 5d3b9ad64f10..90afa3bad1aa 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -949,6 +949,7 @@ field_offset, field_projections, field_representing_type, + field_representing_type_actual_type_id, field_representing_type_raw, field_type, fields, @@ -2098,7 +2099,7 @@ type_changing_struct_update, type_id, type_id_eq, - type_id_field, + type_id_field_representing_type, type_id_fields, type_id_variants, type_id_vtable, diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 9b5aa9d6255a..d1216f0f7212 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -2964,12 +2964,14 @@ pub const fn type_id_fields(_id: crate::any::TypeId, _variant_index: usize) -> u panic!("`TypeId::fields` can only be called at compile-time") } -/// Gets the `TypeId` of the field at the given index of the type represented by this `TypeId`. +/// 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( +pub const fn type_id_field_representing_type( _id: crate::any::TypeId, _variant_index: usize, _field_index: usize, @@ -2977,6 +2979,19 @@ pub const fn type_id_field( 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`. /// /// This is used to implement functions like `slice::from_raw_parts_mut` and diff --git a/library/core/src/mem/type_info.rs b/library/core/src/mem/type_info.rs index 373eb81a201b..8e7e51caad1f 100644 --- a/library/core/src/mem/type_info.rs +++ b/library/core/src/mem/type_info.rs @@ -528,30 +528,50 @@ pub const fn fields(self, variant_index: usize) -> usize { #[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 { type_id: intrinsics::type_id_field(self, variant_index, field_index) } + FieldId { + frt_type_id: intrinsics::type_id_field_representing_type( + self, + variant_index, + field_index, + ), + } } } -/// `FieldId` is a wrapper of `TypeId`, representing a field of a struct, tuple or enum variant. +/// 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 { - type_id: TypeId, + 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.type_id.as_u128()) + write!(f, "FieldId({:#034x})", self.frt_type_id.as_u128()) } } impl FieldId { - /// Returns the `TypeId` of this field. + /// 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::().field(0, 0).type_id() }, + /// TypeId::of::() + /// ); + /// ``` #[unstable(feature = "type_info", issue = "146922")] #[rustc_const_unstable(feature = "type_info", issue = "146922")] pub const fn type_id(self) -> TypeId { - self.type_id + intrinsics::field_representing_type_actual_type_id(self.frt_type_id) } }