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
@@ -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,75 @@ 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_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;
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
};
if field_idx >= fields_num {
throw_ub!(BoundsCheckFailed {
len: fields_num as u64,
index: field_idx as u64
});
}
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 => {
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)?;
@@ -643,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 {
@@ -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,6 +214,9 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi
| sym::truncf128
| sym::type_id
| sym::type_id_eq
| sym::type_id_field_representing_type
| sym::type_id_fields
| sym::type_id_variants
| sym::type_id_vtable
| sym::type_name
| sym::type_of
@@ -319,6 +323,11 @@ 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_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),
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);
@@ -339,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,
+4
View File
@@ -948,6 +948,7 @@
field_offset,
field_projections,
field_representing_type,
field_representing_type_actual_type_id,
field_representing_type_raw,
field_type,
fields,
@@ -2098,6 +2099,9 @@
type_changing_struct_update,
type_id,
type_id_eq,
type_id_field_representing_type,
type_id_fields,
type_id_variants,
type_id_vtable,
type_info,
type_ir,