Skip to content

Commit c503c83

Browse files
committed
Add TypeId::field method to get field TypeId for type_info
1 parent 61f5419 commit c503c83

8 files changed

Lines changed: 156 additions & 1 deletion

File tree

compiler/rustc_const_eval/src/const_eval/machine.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -644,6 +644,38 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> {
644644
ecx.write_scalar(Scalar::from_target_usize(fields_num as u64, ecx), dest)?;
645645
}
646646

647+
sym::type_id_field => {
648+
let ty = ecx.read_type_id(&args[0])?;
649+
let variant_idx = ecx.read_target_usize(&args[1])? as usize;
650+
let field_idx = ecx.read_target_usize(&args[2])? as usize;
651+
652+
let ty::Adt(adt_def, generics) = ty.kind() else {
653+
throw_ub!(BoundsCheckFailed { len: 0, index: variant_idx as u64 });
654+
};
655+
656+
let varients_num = adt_def.variants().len();
657+
if variant_idx >= varients_num {
658+
throw_ub!(BoundsCheckFailed {
659+
len: varients_num as u64,
660+
index: variant_idx as u64
661+
});
662+
}
663+
664+
let variant_def = &adt_def.variants()[VariantIdx::from_usize(variant_idx)];
665+
let fields_num = variant_def.fields.len();
666+
if field_idx >= fields_num {
667+
throw_ub!(BoundsCheckFailed {
668+
len: fields_num as u64,
669+
index: field_idx as u64
670+
});
671+
}
672+
673+
let field_def = &variant_def.fields[FieldIdx::from_usize(field_idx)];
674+
let field_ty = field_def.ty(ecx.tcx.tcx, generics);
675+
let field_ty = ecx.tcx.erase_and_anonymize_regions(field_ty);
676+
ecx.write_type_id(field_ty, dest)?;
677+
}
678+
647679
sym::type_id_variants => {
648680
let ty = ecx.read_type_id(&args[0])?;
649681
let variants_num = ty.ty_adt_def().map(|def| def.variants().len()).unwrap_or(1);

compiler/rustc_hir_analysis/src/check/intrinsic.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi
212212
| sym::truncf128
213213
| sym::type_id
214214
| sym::type_id_eq
215+
| sym::type_id_field
215216
| sym::type_id_fields
216217
| sym::type_id_variants
217218
| sym::type_id_vtable
@@ -320,6 +321,9 @@ pub(crate) fn check_intrinsic_type(
320321
sym::type_name => (1, 0, vec![], Ty::new_static_str(tcx)),
321322
sym::type_id => (1, 0, vec![], type_id_ty()),
322323
sym::type_id_eq => (0, 0, vec![type_id_ty(), type_id_ty()], tcx.types.bool),
324+
sym::type_id_field => {
325+
(0, 0, vec![type_id_ty(), tcx.types.usize, tcx.types.usize], type_id_ty())
326+
}
323327
sym::type_id_fields => (0, 0, vec![type_id_ty(), tcx.types.usize], tcx.types.usize),
324328
sym::type_id_variants => (0, 0, vec![type_id_ty()], tcx.types.usize),
325329
sym::type_id_vtable => {

compiler/rustc_span/src/symbol.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2093,6 +2093,7 @@ symbols! {
20932093
type_changing_struct_update,
20942094
type_id,
20952095
type_id_eq,
2096+
type_id_field,
20962097
type_id_fields,
20972098
type_id_variants,
20982099
type_id_vtable,

library/core/src/intrinsics/mod.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2964,6 +2964,19 @@ pub const fn type_id_fields(_id: crate::any::TypeId, _variant_index: usize) -> u
29642964
panic!("`TypeId::fields` can only be called at compile-time")
29652965
}
29662966

2967+
/// Gets the `TypeId` of the field at the given index of the type represented by this `TypeId`.
2968+
///
2969+
/// The more user-friendly version of this intrinsic is [`core::any::TypeId::field`].
2970+
#[rustc_intrinsic]
2971+
#[unstable(feature = "core_intrinsics", issue = "none")]
2972+
pub const fn type_id_field(
2973+
_id: crate::any::TypeId,
2974+
_variant_index: usize,
2975+
_field_index: usize,
2976+
) -> crate::any::TypeId {
2977+
panic!("`TypeId::field` can only be called at compile-time")
2978+
}
2979+
29672980
/// Lowers in MIR to `Rvalue::Aggregate` with `AggregateKind::RawPtr`.
29682981
///
29692982
/// This is used to implement functions like `slice::from_raw_parts_mut` and

library/core/src/mem/type_info.rs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -451,4 +451,52 @@ impl TypeId {
451451
pub const fn fields(self, variant_index: usize) -> usize {
452452
intrinsics::type_id_fields(self, variant_index)
453453
}
454+
455+
/// Returns the field `TypeId` at the given index of the type represented by this `TypeId`.
456+
///
457+
/// ```
458+
/// #![feature(type_info)]
459+
/// use std::any::TypeId;
460+
///
461+
/// struct Point {
462+
/// x: u32,
463+
/// y: u32,
464+
/// }
465+
/// assert_eq!(const { TypeId::of::<Point>().field(0, 0) }, TypeId::of::<u32>());
466+
/// assert_eq!(const { TypeId::of::<Point>().field(0, 1) }, TypeId::of::<u32>());
467+
///
468+
/// enum Enum {
469+
/// Unit,
470+
/// Tuple(u32, u64),
471+
/// Struct { x: u32, y: u32, z: String },
472+
/// }
473+
/// assert_eq!(const { TypeId::of::<Enum>().field(1, 0) }, TypeId::of::<u32>());
474+
/// assert_eq!(const { TypeId::of::<Enum>().field(2, 2) }, TypeId::of::<String>());
475+
/// ```
476+
///
477+
/// Out-of-bounds indexing will be treated as a compile-time error.
478+
///
479+
/// ```compile_fail,E0080
480+
/// # #![feature(type_info)]
481+
/// # use std::any::TypeId;
482+
/// #
483+
/// # struct Point {
484+
/// # x: u32,
485+
/// # y: u32,
486+
/// # }
487+
/// # enum Enum {
488+
/// # Unit,
489+
/// # Tuple(u32, u64),
490+
/// # Struct { x: u32, y: u32, z: String },
491+
/// # }
492+
/// const {
493+
/// _ = TypeId::of::<Point>().field(0, 2); // error: indexing out of bounds: the len is 2 but the index is 2
494+
/// _ = TypeId::of::<Enum>().field(2, 3); // error: indexing out of bounds: the len is 3 but the index is 3
495+
/// }
496+
/// ```
497+
#[unstable(feature = "type_info", issue = "146922")]
498+
#[rustc_const_unstable(feature = "type_info", issue = "146922")]
499+
pub const fn field(self, variant_index: usize, field_index: usize) -> TypeId {
500+
intrinsics::type_id_field(self, variant_index, field_index)
501+
}
454502
}

library/coretests/tests/mem/type_info.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,9 @@ fn test_structs() {
9797
assert!(ty_id.size() == Some(size_of::<TestStruct>()));
9898
assert!(ty_id.variants() == 1);
9999
assert!(ty_id.fields(0) == 3);
100+
assert!(ty_id.field(0, 0) == TypeId::of::<u8>());
101+
assert!(ty_id.field(0, 1) == TypeId::of::<u16>());
102+
assert!(ty_id.field(0, 2) == TypeId::of::<&u16>());
100103
}
101104

102105
const {
@@ -160,6 +163,8 @@ fn test_unions() {
160163
assert!(ty_id.size() == Some(size_of::<TestUnion>()));
161164
assert!(ty_id.variants() == 1);
162165
assert!(ty_id.fields(0) == 2);
166+
assert!(ty_id.field(0, 0) == TypeId::of::<i16>());
167+
assert!(ty_id.field(0, 1) == TypeId::of::<u16>());
163168
}
164169

165170
const {
@@ -220,6 +225,9 @@ fn test_enums() {
220225
assert!(ty_id.fields(0) == 1);
221226
assert!(ty_id.fields(1) == 0);
222227
assert!(ty_id.fields(2) == 2);
228+
assert!(ty_id.field(0, 0) == TypeId::of::<u32>());
229+
assert!(ty_id.field(2, 0) == TypeId::of::<()>());
230+
assert!(ty_id.field(2, 1) == TypeId::of::<&str>());
223231
}
224232

225233
const {
@@ -234,6 +242,7 @@ fn test_enums() {
234242
assert!(ty_id.variants() == 2);
235243
assert!(ty_id.fields(0) == 0);
236244
assert!(ty_id.fields(1) == 1);
245+
assert!(ty_id.field(1, 0) == TypeId::of::<i32>());
237246
}
238247
}
239248

tests/ui/reflection/variant_index_out_of_bounds.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,13 @@ const _: () = const {
88
TypeId::of::<Option::<()>>().fields(2);
99
//~^ ERROR indexing out of bounds: the len is 2 but the index is 2
1010
};
11+
12+
const _: () = const {
13+
TypeId::of::<u32>().field(0, 1);
14+
//~^ ERROR indexing out of bounds: the len is 0 but the index is 0
15+
};
16+
17+
const _: () = const {
18+
TypeId::of::<Option::<()>>().field(0, 1);
19+
//~^ ERROR indexing out of bounds: the len is 0 but the index is 1
20+
};

tests/ui/reflection/variant_index_out_of_bounds.stderr

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,44 @@ LL | |
1717
LL | | };
1818
| |_^
1919

20-
error: aborting due to 1 previous error
20+
error[E0080]: indexing out of bounds: the len is 0 but the index is 0
21+
--> $DIR/variant_index_out_of_bounds.rs:18:5
22+
|
23+
LL | TypeId::of::<u32>().field(0, 1);
24+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `_::{constant#0}` failed inside this call
25+
|
26+
note: inside `type_info::<impl TypeId>::field`
27+
--> $SRC_DIR/core/src/mem/type_info.rs:LL:COL
28+
29+
note: erroneous constant encountered
30+
--> $DIR/variant_index_out_of_bounds.rs:17:15
31+
|
32+
LL | const _: () = const {
33+
| _______________^
34+
LL | | TypeId::of::<u32>().field(0, 1);
35+
LL | |
36+
LL | | };
37+
| |_^
38+
39+
error[E0080]: indexing out of bounds: the len is 0 but the index is 1
40+
--> $DIR/variant_index_out_of_bounds.rs:23:5
41+
|
42+
LL | TypeId::of::<Option::<()>>().field(0, 1);
43+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `_::{constant#0}` failed inside this call
44+
|
45+
note: inside `type_info::<impl TypeId>::field`
46+
--> $SRC_DIR/core/src/mem/type_info.rs:LL:COL
47+
48+
note: erroneous constant encountered
49+
--> $DIR/variant_index_out_of_bounds.rs:22:15
50+
|
51+
LL | const _: () = const {
52+
| _______________^
53+
LL | | TypeId::of::<Option::<()>>().field(0, 1);
54+
LL | |
55+
LL | | };
56+
| |_^
57+
58+
error: aborting due to 4 previous errors
2159

2260
For more information about this error, try `rustc --explain E0080`.

0 commit comments

Comments
 (0)