Skip to content

Commit b7b56a9

Browse files
committed
Support structs in type info reflection
1 parent 0a3cd3b commit b7b56a9

7 files changed

Lines changed: 309 additions & 32 deletions

File tree

compiler/rustc_const_eval/src/const_eval/type_info.rs

Lines changed: 139 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,40 @@
1-
use rustc_abi::FieldIdx;
1+
use std::borrow::Cow;
2+
3+
use rustc_abi::{FieldIdx, VariantIdx};
24
use rustc_ast::Mutability;
35
use rustc_hir::LangItem;
46
use rustc_middle::mir::interpret::{CtfeProvenance, Scalar};
57
use rustc_middle::span_bug;
68
use rustc_middle::ty::layout::TyAndLayout;
7-
use rustc_middle::ty::{self, Const, ScalarInt, Ty};
9+
use rustc_middle::ty::{self, AdtDef, AdtKind, Const, GenericArgs, ScalarInt, Ty, VariantDef};
810
use rustc_span::{Symbol, sym};
911

1012
use crate::const_eval::CompileTimeMachine;
1113
use crate::interpret::{
12-
Immediate, InterpCx, InterpResult, MPlaceTy, MemoryKind, Writeable, interp_ok,
14+
Immediate, InterpCx, InterpResult, MPlaceTy, MemoryKind, Projectable, Writeable, interp_ok,
1315
};
1416

1517
impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
18+
fn downcast(
19+
&self,
20+
place: &(impl Writeable<'tcx, CtfeProvenance> + 'tcx),
21+
name: Symbol,
22+
) -> InterpResult<'tcx, (VariantIdx, impl Writeable<'tcx, CtfeProvenance> + 'tcx)> {
23+
let variants = place.layout().ty.ty_adt_def().unwrap().variants();
24+
let variant_id = variants
25+
.iter_enumerated()
26+
.find(|(_idx, var)| var.name == name)
27+
.unwrap_or_else(|| panic!("got {name} but expected one of {variants:#?}"))
28+
.0;
29+
30+
interp_ok((variant_id, self.project_downcast(place, variant_id)?))
31+
}
32+
1633
/// Writes a `core::mem::type_info::TypeInfo` for a given type, `ty` to the given place.
1734
pub(crate) fn write_type_info(
1835
&mut self,
1936
ty: Ty<'tcx>,
20-
dest: &impl Writeable<'tcx, CtfeProvenance>,
37+
dest: &(impl Writeable<'tcx, CtfeProvenance> + 'tcx),
2138
) -> InterpResult<'tcx> {
2239
let ty_struct = self.tcx.require_lang_item(LangItem::Type, self.tcx.span);
2340
let ty_struct = self.tcx.type_of(ty_struct).no_bound_vars().unwrap();
@@ -26,22 +43,13 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
2643
// Fill all fields of the `TypeInfo` struct.
2744
for (idx, field) in ty_struct.fields.iter_enumerated() {
2845
let field_dest = self.project_field(dest, idx)?;
29-
let downcast = |name: Symbol| {
30-
let variants = field_dest.layout().ty.ty_adt_def().unwrap().variants();
31-
let variant_id = variants
32-
.iter_enumerated()
33-
.find(|(_idx, var)| var.name == name)
34-
.unwrap_or_else(|| panic!("got {name} but expected one of {variants:#?}"))
35-
.0;
36-
37-
interp_ok((variant_id, self.project_downcast(&field_dest, variant_id)?))
38-
};
3946
let ptr_bit_width = || self.tcx.data_layout.pointer_size().bits();
4047
match field.name {
4148
sym::kind => {
4249
let variant_index = match ty.kind() {
4350
ty::Tuple(fields) => {
44-
let (variant, variant_place) = downcast(sym::Tuple)?;
51+
let (variant, variant_place) =
52+
self.downcast(&field_dest, sym::Tuple)?;
4553
// project to the single tuple variant field of `type_info::Tuple` struct type
4654
let tuple_place = self.project_field(&variant_place, FieldIdx::ZERO)?;
4755
assert_eq!(
@@ -59,23 +67,38 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
5967
variant
6068
}
6169
ty::Array(ty, len) => {
62-
let (variant, variant_place) = downcast(sym::Array)?;
70+
let (variant, variant_place) =
71+
self.downcast(&field_dest, sym::Array)?;
6372
let array_place = self.project_field(&variant_place, FieldIdx::ZERO)?;
6473

6574
self.write_array_type_info(array_place, *ty, *len)?;
6675

6776
variant
6877
}
78+
ty::Adt(adt_def, generics) => {
79+
// TODO(type_info): Handle enum and union
80+
if !adt_def.is_struct() {
81+
self.downcast(&field_dest, sym::Other)?.0
82+
} else {
83+
let (variant, variant_place) =
84+
self.downcast(&field_dest, sym::Struct)?;
85+
let place = self.project_field(&variant_place, FieldIdx::ZERO)?;
86+
self.write_adt_type_info(place, (ty, *adt_def), generics)?;
87+
variant
88+
}
89+
}
6990
ty::Bool => {
70-
let (variant, _variant_place) = downcast(sym::Bool)?;
91+
let (variant, _variant_place) =
92+
self.downcast(&field_dest, sym::Bool)?;
7193
variant
7294
}
7395
ty::Char => {
74-
let (variant, _variant_place) = downcast(sym::Char)?;
96+
let (variant, _variant_place) =
97+
self.downcast(&field_dest, sym::Char)?;
7598
variant
7699
}
77100
ty::Int(int_ty) => {
78-
let (variant, variant_place) = downcast(sym::Int)?;
101+
let (variant, variant_place) = self.downcast(&field_dest, sym::Int)?;
79102
let place = self.project_field(&variant_place, FieldIdx::ZERO)?;
80103
self.write_int_type_info(
81104
place,
@@ -85,7 +108,7 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
85108
variant
86109
}
87110
ty::Uint(uint_ty) => {
88-
let (variant, variant_place) = downcast(sym::Int)?;
111+
let (variant, variant_place) = self.downcast(&field_dest, sym::Int)?;
89112
let place = self.project_field(&variant_place, FieldIdx::ZERO)?;
90113
self.write_int_type_info(
91114
place,
@@ -95,25 +118,26 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
95118
variant
96119
}
97120
ty::Float(float_ty) => {
98-
let (variant, variant_place) = downcast(sym::Float)?;
121+
let (variant, variant_place) =
122+
self.downcast(&field_dest, sym::Float)?;
99123
let place = self.project_field(&variant_place, FieldIdx::ZERO)?;
100124
self.write_float_type_info(place, float_ty.bit_width())?;
101125
variant
102126
}
103127
ty::Str => {
104-
let (variant, _variant_place) = downcast(sym::Str)?;
128+
let (variant, _variant_place) = self.downcast(&field_dest, sym::Str)?;
105129
variant
106130
}
107131
ty::Ref(_, ty, mutability) => {
108-
let (variant, variant_place) = downcast(sym::Reference)?;
132+
let (variant, variant_place) =
133+
self.downcast(&field_dest, sym::Reference)?;
109134
let reference_place =
110135
self.project_field(&variant_place, FieldIdx::ZERO)?;
111136
self.write_reference_type_info(reference_place, *ty, *mutability)?;
112137

113138
variant
114139
}
115-
ty::Adt(_, _)
116-
| ty::Foreign(_)
140+
ty::Foreign(_)
117141
| ty::Pat(_, _)
118142
| ty::Slice(_)
119143
| ty::RawPtr(..)
@@ -131,14 +155,14 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
131155
| ty::Bound(..)
132156
| ty::Placeholder(_)
133157
| ty::Infer(..)
134-
| ty::Error(_) => downcast(sym::Other)?.0,
158+
| ty::Error(_) => self.downcast(&field_dest, sym::Other)?.0,
135159
};
136160
self.write_discriminant(variant_index, &field_dest)?
137161
}
138162
sym::size => {
139163
let layout = self.layout_of(ty)?;
140164
let variant_index = if layout.is_sized() {
141-
let (variant, variant_place) = downcast(sym::Some)?;
165+
let (variant, variant_place) = self.downcast(&field_dest, sym::Some)?;
142166
let size_field_place =
143167
self.project_field(&variant_place, FieldIdx::ZERO)?;
144168
self.write_scalar(
@@ -148,7 +172,7 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
148172
)?;
149173
variant
150174
} else {
151-
downcast(sym::None)?.0
175+
self.downcast(&field_dest, sym::None)?.0
152176
};
153177
self.write_discriminant(variant_index, &field_dest)?;
154178
}
@@ -184,7 +208,7 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
184208

185209
while let Some((i, place)) = fields_places.next(self)? {
186210
let field_ty = fields[i as usize];
187-
self.write_field(field_ty, place, tuple_layout, i)?;
211+
self.write_field(field_ty, place, tuple_layout, None, i)?;
188212
}
189213

190214
let fields_place = fields_place.map_provenance(CtfeProvenance::as_immutable);
@@ -199,13 +223,23 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
199223
field_ty: Ty<'tcx>,
200224
place: MPlaceTy<'tcx>,
201225
layout: TyAndLayout<'tcx>,
226+
name: Option<Symbol>,
202227
idx: u64,
203228
) -> InterpResult<'tcx> {
204229
for (field_idx, field_ty_field) in
205230
place.layout.ty.ty_adt_def().unwrap().non_enum_variant().fields.iter_enumerated()
206231
{
207232
let field_place = self.project_field(&place, field_idx)?;
208233
match field_ty_field.name {
234+
sym::name => {
235+
let name = match name.as_ref() {
236+
Some(name) => Cow::Borrowed(name.as_str()),
237+
None => Cow::Owned(idx.to_string()), // For tuples
238+
};
239+
let name_place = self.allocate_str_dedup(&name)?;
240+
let ptr = self.mplace_to_ref(&name_place)?;
241+
self.write_immediate(*ptr, &field_place)?
242+
}
209243
sym::ty => self.write_type_id(field_ty, &field_place)?,
210244
sym::offset => {
211245
let offset = layout.fields.offset(idx as usize);
@@ -246,6 +280,82 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
246280
interp_ok(())
247281
}
248282

283+
// FIXME(type_info): No semver considerations for now
284+
pub(crate) fn write_adt_type_info(
285+
&mut self,
286+
place: impl Writeable<'tcx, CtfeProvenance>,
287+
adt: (Ty<'tcx>, AdtDef<'tcx>),
288+
generics: &'tcx GenericArgs<'tcx>,
289+
) -> InterpResult<'tcx> {
290+
let (adt_ty, adt_def) = adt;
291+
match adt_def.adt_kind() {
292+
AdtKind::Struct => self.write_struct_type_info(
293+
place,
294+
(adt_ty, adt_def.variant(VariantIdx::ZERO)),
295+
generics,
296+
),
297+
AdtKind::Union => todo!(),
298+
AdtKind::Enum => todo!(),
299+
}
300+
}
301+
302+
pub(crate) fn write_struct_type_info(
303+
&mut self,
304+
place: impl Writeable<'tcx, CtfeProvenance>,
305+
struct_: (Ty<'tcx>, &'tcx VariantDef),
306+
generics: &'tcx GenericArgs<'tcx>,
307+
) -> InterpResult<'tcx> {
308+
let (struct_ty, struct_def) = struct_;
309+
let struct_layout = self.layout_of(struct_ty)?;
310+
311+
for (field_idx, field) in
312+
place.layout().ty.ty_adt_def().unwrap().non_enum_variant().fields.iter_enumerated()
313+
{
314+
let field_place = self.project_field(&place, field_idx)?;
315+
316+
match field.name {
317+
sym::fields => {
318+
let fields_slice_place = field_place;
319+
let field_type = fields_slice_place
320+
.layout()
321+
.ty
322+
.builtin_deref(false)
323+
.unwrap()
324+
.sequence_element_type(self.tcx.tcx);
325+
let fields_layout = self.layout_of(Ty::new_array(
326+
self.tcx.tcx,
327+
field_type,
328+
struct_def.fields.len() as u64,
329+
))?;
330+
let fields_place = self.allocate(fields_layout, MemoryKind::Stack)?;
331+
let mut fields_places = self.project_array_fields(&fields_place)?;
332+
333+
for field_def in &struct_def.fields {
334+
let (i, place) = fields_places.next(self)?.unwrap();
335+
let field_ty = field_def.ty(*self.tcx, generics);
336+
self.write_field(field_ty, place, struct_layout, Some(field_def.name), i)?;
337+
}
338+
339+
let fields_place = fields_place.map_provenance(CtfeProvenance::as_immutable);
340+
let ptr = Immediate::new_slice(
341+
fields_place.ptr(),
342+
struct_def.fields.len() as u64,
343+
self,
344+
);
345+
self.write_immediate(ptr, &fields_slice_place)?
346+
}
347+
sym::non_exhaustive => {
348+
let is_non_exhaustive = struct_def.is_field_list_non_exhaustive();
349+
self.write_scalar(Scalar::from_bool(is_non_exhaustive), &field_place)?
350+
}
351+
// TODO(type_info): Write more info
352+
other => span_bug!(self.tcx.def_span(field.did), "unimplemented field {other}"),
353+
}
354+
}
355+
356+
interp_ok(())
357+
}
358+
249359
fn write_int_type_info(
250360
&mut self,
251361
place: impl Writeable<'tcx, CtfeProvenance>,

compiler/rustc_span/src/symbol.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,7 @@ symbols! {
369369
Stdin,
370370
Str,
371371
String,
372+
Struct,
372373
StructuralPartialEq,
373374
SubdiagMessage,
374375
Subdiagnostic,
@@ -1079,6 +1080,7 @@ symbols! {
10791080
ffi_pure,
10801081
ffi_returns_twice,
10811082
field,
1083+
fields,
10821084
field_init_shorthand,
10831085
file,
10841086
file_options,

library/core/src/mem/type_info.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ pub enum TypeKind {
4545
Tuple(Tuple),
4646
/// Arrays.
4747
Array(Array),
48+
/// Structs.
49+
Struct(Struct),
4850
/// Primitive boolean type.
4951
Bool(Bool),
5052
/// Primitive character type.
@@ -75,6 +77,8 @@ pub struct Tuple {
7577
#[non_exhaustive]
7678
#[unstable(feature = "type_info", issue = "146922")]
7779
pub struct Field {
80+
/// The name of the field.
81+
pub name: &'static str,
7882
/// The field's type.
7983
pub ty: TypeId,
8084
/// Offset in bytes from the parent type
@@ -92,6 +96,17 @@ pub struct Array {
9296
pub len: usize,
9397
}
9498

99+
/// Compile-time type information about arrays.
100+
#[derive(Debug)]
101+
#[non_exhaustive]
102+
#[unstable(feature = "type_info", issue = "146922")]
103+
pub struct Struct {
104+
/// All fields of the struct.
105+
pub fields: &'static [Field],
106+
/// Whether the struct is non-exhaustive.
107+
pub non_exhaustive: bool,
108+
}
109+
95110
/// Compile-time type information about `bool`.
96111
#[derive(Debug)]
97112
#[non_exhaustive]

library/coretests/tests/mem/type_info.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,43 @@ fn test_tuples() {
5858
}
5959
}
6060

61+
#[test]
62+
fn test_structs() {
63+
use TypeKind::*;
64+
65+
struct TestStruct {
66+
first: u8,
67+
second: u16,
68+
}
69+
#[non_exhaustive]
70+
struct NonExhaustive {
71+
a: u8,
72+
}
73+
struct TupleStruct(u8, u16);
74+
75+
let Type { kind: Struct(ty), size, .. } = (const { Type::of::<TestStruct>() }) else {
76+
panic!()
77+
};
78+
assert_eq!(size, Some(size_of::<TestStruct>()));
79+
assert!(!ty.non_exhaustive);
80+
assert_eq!(ty.fields.len(), 2);
81+
assert_eq!(ty.fields[0].name, "first");
82+
assert_eq!(ty.fields[1].name, "second");
83+
84+
let Type { kind: Struct(ty), size, .. } = (const { Type::of::<NonExhaustive>() }) else {
85+
panic!()
86+
};
87+
assert_eq!(size, Some(1));
88+
assert!(ty.non_exhaustive);
89+
90+
let Type { kind: Struct(ty), size, .. } = (const { Type::of::<TupleStruct>() }) else {
91+
panic!()
92+
};
93+
assert_eq!(ty.fields.len(), 2);
94+
assert_eq!(ty.fields[0].name, "0");
95+
assert_eq!(ty.fields[1].name, "1");
96+
}
97+
6198
#[test]
6299
fn test_primitives() {
63100
use TypeKind::*;

0 commit comments

Comments
 (0)