Support structs in type info reflection

This commit is contained in:
Asuna 2026-01-14 22:19:12 +01:00
parent 18d13b5332
commit b23d308853
8 changed files with 333 additions and 39 deletions

View file

@ -1,18 +1,36 @@
use rustc_abi::FieldIdx;
use std::borrow::Cow;
use rustc_abi::{FieldIdx, VariantIdx};
use rustc_ast::Mutability;
use rustc_hir::LangItem;
use rustc_middle::span_bug;
use rustc_middle::ty::layout::TyAndLayout;
use rustc_middle::ty::{self, Const, ScalarInt, Ty};
use rustc_middle::ty::{self, AdtDef, AdtKind, Const, GenericArgs, ScalarInt, Ty, VariantDef};
use rustc_span::{Symbol, sym};
use crate::const_eval::CompileTimeMachine;
use crate::interpret::{
CtfeProvenance, Immediate, InterpCx, InterpResult, MPlaceTy, MemoryKind, Scalar, Writeable,
interp_ok,
CtfeProvenance, Immediate, InterpCx, InterpResult, MPlaceTy, MemoryKind, Projectable, Scalar,
Writeable, interp_ok,
};
impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
/// Equivalent to `project_downcast`, but identifies the variant by name instead of index.
fn downcast<'a>(
&self,
place: &(impl Writeable<'tcx, CtfeProvenance> + 'a),
name: Symbol,
) -> InterpResult<'tcx, (VariantIdx, impl Writeable<'tcx, CtfeProvenance> + 'a)> {
let variants = place.layout().ty.ty_adt_def().unwrap().variants();
let variant_idx = variants
.iter_enumerated()
.find(|(_idx, var)| var.name == name)
.unwrap_or_else(|| panic!("got {name} but expected one of {variants:#?}"))
.0;
interp_ok((variant_idx, self.project_downcast(place, variant_idx)?))
}
/// Writes a `core::mem::type_info::TypeInfo` for a given type, `ty` to the given place.
pub(crate) fn write_type_info(
&mut self,
@ -26,22 +44,13 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
// Fill all fields of the `TypeInfo` struct.
for (idx, field) in ty_struct.fields.iter_enumerated() {
let field_dest = self.project_field(dest, idx)?;
let downcast = |name: Symbol| {
let variants = field_dest.layout().ty.ty_adt_def().unwrap().variants();
let variant_id = variants
.iter_enumerated()
.find(|(_idx, var)| var.name == name)
.unwrap_or_else(|| panic!("got {name} but expected one of {variants:#?}"))
.0;
interp_ok((variant_id, self.project_downcast(&field_dest, variant_id)?))
};
let ptr_bit_width = || self.tcx.data_layout.pointer_size().bits();
match field.name {
sym::kind => {
let variant_index = match ty.kind() {
ty::Tuple(fields) => {
let (variant, variant_place) = downcast(sym::Tuple)?;
let (variant, variant_place) =
self.downcast(&field_dest, sym::Tuple)?;
// project to the single tuple variant field of `type_info::Tuple` struct type
let tuple_place = self.project_field(&variant_place, FieldIdx::ZERO)?;
assert_eq!(
@ -59,7 +68,8 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
variant
}
ty::Array(ty, len) => {
let (variant, variant_place) = downcast(sym::Array)?;
let (variant, variant_place) =
self.downcast(&field_dest, sym::Array)?;
let array_place = self.project_field(&variant_place, FieldIdx::ZERO)?;
self.write_array_type_info(array_place, *ty, *len)?;
@ -67,23 +77,38 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
variant
}
ty::Slice(ty) => {
let (variant, variant_place) = downcast(sym::Slice)?;
let (variant, variant_place) =
self.downcast(&field_dest, sym::Slice)?;
let slice_place = self.project_field(&variant_place, FieldIdx::ZERO)?;
self.write_slice_type_info(slice_place, *ty)?;
variant
}
ty::Adt(adt_def, generics) => {
// TODO(type_info): Handle enum and union
if !adt_def.is_struct() {
self.downcast(&field_dest, sym::Other)?.0
} else {
let (variant, variant_place) =
self.downcast(&field_dest, sym::Struct)?;
let place = self.project_field(&variant_place, FieldIdx::ZERO)?;
self.write_adt_type_info(place, (ty, *adt_def), generics)?;
variant
}
}
ty::Bool => {
let (variant, _variant_place) = downcast(sym::Bool)?;
let (variant, _variant_place) =
self.downcast(&field_dest, sym::Bool)?;
variant
}
ty::Char => {
let (variant, _variant_place) = downcast(sym::Char)?;
let (variant, _variant_place) =
self.downcast(&field_dest, sym::Char)?;
variant
}
ty::Int(int_ty) => {
let (variant, variant_place) = downcast(sym::Int)?;
let (variant, variant_place) = self.downcast(&field_dest, sym::Int)?;
let place = self.project_field(&variant_place, FieldIdx::ZERO)?;
self.write_int_type_info(
place,
@ -93,7 +118,7 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
variant
}
ty::Uint(uint_ty) => {
let (variant, variant_place) = downcast(sym::Int)?;
let (variant, variant_place) = self.downcast(&field_dest, sym::Int)?;
let place = self.project_field(&variant_place, FieldIdx::ZERO)?;
self.write_int_type_info(
place,
@ -103,17 +128,19 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
variant
}
ty::Float(float_ty) => {
let (variant, variant_place) = downcast(sym::Float)?;
let (variant, variant_place) =
self.downcast(&field_dest, sym::Float)?;
let place = self.project_field(&variant_place, FieldIdx::ZERO)?;
self.write_float_type_info(place, float_ty.bit_width())?;
variant
}
ty::Str => {
let (variant, _variant_place) = downcast(sym::Str)?;
let (variant, _variant_place) = self.downcast(&field_dest, sym::Str)?;
variant
}
ty::Ref(_, ty, mutability) => {
let (variant, variant_place) = downcast(sym::Reference)?;
let (variant, variant_place) =
self.downcast(&field_dest, sym::Reference)?;
let reference_place =
self.project_field(&variant_place, FieldIdx::ZERO)?;
self.write_reference_type_info(reference_place, *ty, *mutability)?;
@ -121,7 +148,8 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
variant
}
ty::RawPtr(ty, mutability) => {
let (variant, variant_place) = downcast(sym::Pointer)?;
let (variant, variant_place) =
self.downcast(&field_dest, sym::Pointer)?;
let pointer_place =
self.project_field(&variant_place, FieldIdx::ZERO)?;
@ -130,13 +158,13 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
variant
}
ty::Dynamic(predicates, region) => {
let (variant, variant_place) = downcast(sym::DynTrait)?;
let (variant, variant_place) =
self.downcast(&field_dest, sym::DynTrait)?;
let dyn_place = self.project_field(&variant_place, FieldIdx::ZERO)?;
self.write_dyn_trait_type_info(dyn_place, *predicates, *region)?;
variant
}
ty::Adt(_, _)
| ty::Foreign(_)
ty::Foreign(_)
| ty::Pat(_, _)
| ty::FnDef(..)
| ty::FnPtr(..)
@ -151,14 +179,14 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
| ty::Bound(..)
| ty::Placeholder(_)
| ty::Infer(..)
| ty::Error(_) => downcast(sym::Other)?.0,
| ty::Error(_) => self.downcast(&field_dest, sym::Other)?.0,
};
self.write_discriminant(variant_index, &field_dest)?
}
sym::size => {
let layout = self.layout_of(ty)?;
let variant_index = if layout.is_sized() {
let (variant, variant_place) = downcast(sym::Some)?;
let (variant, variant_place) = self.downcast(&field_dest, sym::Some)?;
let size_field_place =
self.project_field(&variant_place, FieldIdx::ZERO)?;
self.write_scalar(
@ -168,7 +196,7 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
)?;
variant
} else {
downcast(sym::None)?.0
self.downcast(&field_dest, sym::None)?.0
};
self.write_discriminant(variant_index, &field_dest)?;
}
@ -204,7 +232,7 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
while let Some((i, place)) = fields_places.next(self)? {
let field_ty = fields[i as usize];
self.write_field(field_ty, place, tuple_layout, i)?;
self.write_field(field_ty, place, tuple_layout, None, i)?;
}
let fields_place = fields_place.map_provenance(CtfeProvenance::as_immutable);
@ -219,6 +247,7 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
field_ty: Ty<'tcx>,
place: MPlaceTy<'tcx>,
layout: TyAndLayout<'tcx>,
name: Option<Symbol>,
idx: u64,
) -> InterpResult<'tcx> {
for (field_idx, field_ty_field) in
@ -226,6 +255,15 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
{
let field_place = self.project_field(&place, field_idx)?;
match field_ty_field.name {
sym::name => {
let name = match name.as_ref() {
Some(name) => Cow::Borrowed(name.as_str()),
None => Cow::Owned(idx.to_string()), // For tuples
};
let name_place = self.allocate_str_dedup(&name)?;
let ptr = self.mplace_to_ref(&name_place)?;
self.write_immediate(*ptr, &field_place)?
}
sym::ty => self.write_type_id(field_ty, &field_place)?,
sym::offset => {
let offset = layout.fields.offset(idx as usize);
@ -287,6 +325,81 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
interp_ok(())
}
// FIXME(type_info): No semver considerations for now
pub(crate) fn write_adt_type_info(
&mut self,
place: impl Writeable<'tcx, CtfeProvenance>,
adt: (Ty<'tcx>, AdtDef<'tcx>),
generics: &'tcx GenericArgs<'tcx>,
) -> InterpResult<'tcx> {
let (adt_ty, adt_def) = adt;
match adt_def.adt_kind() {
AdtKind::Struct => self.write_struct_type_info(
place,
(adt_ty, adt_def.variant(VariantIdx::ZERO)),
generics,
),
AdtKind::Union => todo!(),
AdtKind::Enum => todo!(),
}
}
pub(crate) fn write_struct_type_info(
&mut self,
place: impl Writeable<'tcx, CtfeProvenance>,
struct_: (Ty<'tcx>, &'tcx VariantDef),
generics: &'tcx GenericArgs<'tcx>,
) -> InterpResult<'tcx> {
let (struct_ty, struct_def) = struct_;
let struct_layout = self.layout_of(struct_ty)?;
for (field_idx, field) in
place.layout().ty.ty_adt_def().unwrap().non_enum_variant().fields.iter_enumerated()
{
let field_place = self.project_field(&place, field_idx)?;
match field.name {
sym::fields => {
let fields_slice_place = field_place;
let field_type = fields_slice_place
.layout()
.ty
.builtin_deref(false)
.unwrap()
.sequence_element_type(self.tcx.tcx);
let fields_layout = self.layout_of(Ty::new_array(
self.tcx.tcx,
field_type,
struct_def.fields.len() as u64,
))?;
let fields_place = self.allocate(fields_layout, MemoryKind::Stack)?;
let mut fields_places = self.project_array_fields(&fields_place)?;
for field_def in &struct_def.fields {
let (i, place) = fields_places.next(self)?.unwrap();
let field_ty = field_def.ty(*self.tcx, generics);
self.write_field(field_ty, place, struct_layout, Some(field_def.name), i)?;
}
let fields_place = fields_place.map_provenance(CtfeProvenance::as_immutable);
let ptr = Immediate::new_slice(
fields_place.ptr(),
struct_def.fields.len() as u64,
self,
);
self.write_immediate(ptr, &fields_slice_place)?
}
sym::non_exhaustive => {
let is_non_exhaustive = struct_def.is_field_list_non_exhaustive();
self.write_scalar(Scalar::from_bool(is_non_exhaustive), &field_place)?
}
other => span_bug!(self.tcx.def_span(field.did), "unimplemented field {other}"),
}
}
interp_ok(())
}
fn write_int_type_info(
&mut self,
place: impl Writeable<'tcx, CtfeProvenance>,

View file

@ -372,6 +372,7 @@ symbols! {
Stdin,
Str,
String,
Struct,
StructuralPartialEq,
SubdiagMessage,
Subdiagnostic,
@ -1086,6 +1087,7 @@ symbols! {
ffi_returns_twice,
field,
field_init_shorthand,
fields,
file,
file_options,
flags,

View file

@ -49,6 +49,8 @@ pub enum TypeKind {
Slice(Slice),
/// Dynamic Traits.
DynTrait(DynTrait),
/// Structs.
Struct(Struct),
/// Primitive boolean type.
Bool(Bool),
/// Primitive character type.
@ -81,6 +83,8 @@ pub struct Tuple {
#[non_exhaustive]
#[unstable(feature = "type_info", issue = "146922")]
pub struct Field {
/// The name of the field.
pub name: &'static str,
/// The field's type.
pub ty: TypeId,
/// Offset in bytes from the parent type
@ -137,6 +141,17 @@ pub struct Trait {
pub is_auto: bool,
}
/// Compile-time type information about arrays.
#[derive(Debug)]
#[non_exhaustive]
#[unstable(feature = "type_info", issue = "146922")]
pub struct Struct {
/// All fields of the struct.
pub fields: &'static [Field],
/// Whether the struct field list is non-exhaustive.
pub non_exhaustive: bool,
}
/// Compile-time type information about `bool`.
#[derive(Debug)]
#[non_exhaustive]

View file

@ -1,4 +1,7 @@
#![allow(dead_code)]
use std::any::{Any, TypeId};
use std::mem::offset_of;
use std::mem::type_info::{Type, TypeKind};
#[test]
@ -66,6 +69,54 @@ fn test_tuples() {
}
}
#[test]
fn test_structs() {
use TypeKind::*;
const {
struct TestStruct {
first: u8,
second: u16,
reference: &'static u16,
}
let Type { kind: Struct(ty), size, .. } = Type::of::<TestStruct>() else { panic!() };
assert!(size == Some(size_of::<TestStruct>()));
assert!(!ty.non_exhaustive);
assert!(ty.fields.len() == 3);
assert!(ty.fields[0].name == "first");
assert!(ty.fields[0].ty == TypeId::of::<u8>());
assert!(ty.fields[0].offset == offset_of!(TestStruct, first));
assert!(ty.fields[1].name == "second");
assert!(ty.fields[1].ty == TypeId::of::<u16>());
assert!(ty.fields[1].offset == offset_of!(TestStruct, second));
assert!(ty.fields[2].name == "reference");
assert!(ty.fields[2].ty != TypeId::of::<&'static u16>()); // FIXME(type_info): should be ==
assert!(ty.fields[2].offset == offset_of!(TestStruct, reference));
}
const {
#[non_exhaustive]
struct NonExhaustive {
a: u8,
}
let Type { kind: Struct(ty), .. } = Type::of::<NonExhaustive>() else { panic!() };
assert!(ty.non_exhaustive);
}
const {
struct TupleStruct(u8, u16);
let Type { kind: Struct(ty), .. } = Type::of::<TupleStruct>() else { panic!() };
assert!(ty.fields.len() == 2);
assert!(ty.fields[0].name == "0");
assert!(ty.fields[0].ty == TypeId::of::<u8>());
assert!(ty.fields[1].name == "1");
assert!(ty.fields[1].ty == TypeId::of::<u16>());
}
}
#[test]
fn test_primitives() {
use TypeKind::*;

View file

@ -1,17 +1,17 @@
error[E0599]: the method `clone` exists for struct `Struct<A>`, but its trait bounds were not satisfied
error[E0599]: the method `clone` exists for struct `issue_69725::Struct<A>`, but its trait bounds were not satisfied
--> $DIR/issue-69725.rs:9:32
|
LL | let _ = Struct::<A>::new().clone();
| ^^^^^ method cannot be called on `Struct<A>` due to unsatisfied trait bounds
| ^^^^^ method cannot be called on `issue_69725::Struct<A>` due to unsatisfied trait bounds
|
::: $DIR/auxiliary/issue-69725.rs:2:1
|
LL | pub struct Struct<A>(A);
| -------------------- doesn't satisfy `Struct<A>: Clone`
| -------------------- doesn't satisfy `issue_69725::Struct<A>: Clone`
|
= note: the following trait bounds were not satisfied:
`A: Clone`
which is required by `Struct<A>: Clone`
which is required by `issue_69725::Struct<A>: Clone`
help: consider restricting the type parameter to satisfy the trait bound
|
LL | fn crash<A>() where A: Clone {

View file

@ -3,14 +3,17 @@ Type {
Tuple {
fields: [
Field {
name: "0",
ty: TypeId(0x0596b48cc04376e64d5c788c2aa46bdb),
offset: 0,
},
Field {
name: "1",
ty: TypeId(0x0596b48cc04376e64d5c788c2aa46bdb),
offset: 1,
},
Field {
name: "2",
ty: TypeId(0x41223169ff28813ba79b7268a2a968d9),
offset: 2,
},
@ -143,7 +146,18 @@ Type {
),
}
Type {
kind: Other,
kind: Struct(
Struct {
fields: [
Field {
name: "a",
ty: TypeId(0x1378bb1c0a0202683eb65e7c11f2e4d7),
offset: 0,
},
],
non_exhaustive: false,
},
),
size: Some(
4,
),
@ -154,6 +168,45 @@ Type {
12,
),
}
Type {
kind: Struct(
Struct {
fields: [
Field {
name: "a",
ty: TypeId(0x1378bb1c0a0202683eb65e7c11f2e4d7),
offset: 0,
},
],
non_exhaustive: true,
},
),
size: Some(
4,
),
}
Type {
kind: Struct(
Struct {
fields: [
Field {
name: "0",
ty: TypeId(0x1378bb1c0a0202683eb65e7c11f2e4d7),
offset: 8,
},
Field {
name: "1",
ty: TypeId(0x9ed91be891e304132cb86891e578f4a5),
offset: 0,
},
],
non_exhaustive: false,
},
),
size: Some(
12,
),
}
Type {
kind: Reference(
Reference {

View file

@ -3,14 +3,17 @@ Type {
Tuple {
fields: [
Field {
name: "0",
ty: TypeId(0x0596b48cc04376e64d5c788c2aa46bdb),
offset: 0,
},
Field {
name: "1",
ty: TypeId(0x0596b48cc04376e64d5c788c2aa46bdb),
offset: 1,
},
Field {
name: "2",
ty: TypeId(0x41223169ff28813ba79b7268a2a968d9),
offset: 2,
},
@ -143,7 +146,18 @@ Type {
),
}
Type {
kind: Other,
kind: Struct(
Struct {
fields: [
Field {
name: "a",
ty: TypeId(0x1378bb1c0a0202683eb65e7c11f2e4d7),
offset: 0,
},
],
non_exhaustive: false,
},
),
size: Some(
4,
),
@ -154,6 +168,45 @@ Type {
24,
),
}
Type {
kind: Struct(
Struct {
fields: [
Field {
name: "a",
ty: TypeId(0x1378bb1c0a0202683eb65e7c11f2e4d7),
offset: 0,
},
],
non_exhaustive: true,
},
),
size: Some(
4,
),
}
Type {
kind: Struct(
Struct {
fields: [
Field {
name: "0",
ty: TypeId(0x1378bb1c0a0202683eb65e7c11f2e4d7),
offset: 8,
},
Field {
name: "1",
ty: TypeId(0x9ed91be891e304132cb86891e578f4a5),
offset: 0,
},
],
non_exhaustive: false,
},
),
size: Some(
16,
),
}
Type {
kind: Reference(
Reference {

View file

@ -14,6 +14,13 @@ struct Foo {
a: u32,
}
#[non_exhaustive]
struct NonExhaustiveStruct {
a: u32,
}
struct TupleStruct(u32, u64);
enum Bar {
Some(u32),
None,
@ -37,7 +44,7 @@ fn main() {
[u8; 2],
i8, i32, i64, i128, isize,
u8, u32, u64, u128, usize,
Foo, Bar,
Foo, Bar, NonExhaustiveStruct, TupleStruct,
&Unsized, &str, &[u8],
str, [u8],
&u8, &mut u8,