Skip to content

Commit 1bd8449

Browse files
committed
Support structs in type info reflection
1 parent 79ec275 commit 1bd8449

6 files changed

Lines changed: 220 additions & 30 deletions

File tree

compiler/rustc_const_eval/src/const_eval/type_info.rs

Lines changed: 135 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,38 @@
1-
use rustc_abi::FieldIdx;
1+
use rustc_abi::{FieldIdx, VariantIdx};
22
use rustc_hir::LangItem;
33
use rustc_middle::mir::interpret::CtfeProvenance;
44
use rustc_middle::span_bug;
55
use rustc_middle::ty::layout::TyAndLayout;
6-
use rustc_middle::ty::{self, Const, ScalarInt, Ty};
6+
use rustc_middle::ty::{self, AdtDef, AdtKind, Const, GenericArgs, ScalarInt, Ty, VariantDef};
77
use rustc_span::{Symbol, sym};
88

99
use crate::const_eval::CompileTimeMachine;
1010
use crate::interpret::{
11-
Immediate, InterpCx, InterpResult, MPlaceTy, MemoryKind, Writeable, interp_ok,
11+
Immediate, InterpCx, InterpResult, MPlaceTy, MemoryKind, Projectable, Scalar, Writeable,
12+
interp_ok,
1213
};
1314

1415
impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
16+
fn downcast(
17+
&self,
18+
place: &(impl Writeable<'tcx, CtfeProvenance> + 'tcx),
19+
name: Symbol,
20+
) -> InterpResult<'tcx, (VariantIdx, impl Writeable<'tcx, CtfeProvenance> + 'tcx)> {
21+
let variants = place.layout().ty.ty_adt_def().unwrap().variants();
22+
let variant_id = variants
23+
.iter_enumerated()
24+
.find(|(_idx, var)| var.name == name)
25+
.unwrap_or_else(|| panic!("got {name} but expected one of {variants:#?}"))
26+
.0;
27+
28+
interp_ok((variant_id, self.project_downcast(place, variant_id)?))
29+
}
30+
1531
/// Writes a `core::mem::type_info::TypeInfo` for a given type, `ty` to the given place.
1632
pub(crate) fn write_type_info(
1733
&mut self,
1834
ty: Ty<'tcx>,
19-
dest: &impl Writeable<'tcx, CtfeProvenance>,
35+
dest: &(impl Writeable<'tcx, CtfeProvenance> + 'tcx),
2036
) -> InterpResult<'tcx> {
2137
let ty_struct = self.tcx.require_lang_item(LangItem::Type, self.tcx.span);
2238
let ty_struct = self.tcx.type_of(ty_struct).no_bound_vars().unwrap();
@@ -25,22 +41,13 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
2541
// Fill all fields of the `TypeInfo` struct.
2642
for (idx, field) in ty_struct.fields.iter_enumerated() {
2743
let field_dest = self.project_field(dest, idx)?;
28-
let downcast = |name: Symbol| {
29-
let variants = field_dest.layout().ty.ty_adt_def().unwrap().variants();
30-
let variant_id = variants
31-
.iter_enumerated()
32-
.find(|(_idx, var)| var.name == name)
33-
.unwrap_or_else(|| panic!("got {name} but expected one of {variants:#?}"))
34-
.0;
35-
36-
interp_ok((variant_id, self.project_downcast(&field_dest, variant_id)?))
37-
};
3844
let ptr_bit_width = || self.tcx.data_layout.pointer_size().bits();
3945
match field.name {
4046
sym::kind => {
4147
let variant_index = match ty.kind() {
4248
ty::Tuple(fields) => {
43-
let (variant, variant_place) = downcast(sym::Tuple)?;
49+
let (variant, variant_place) =
50+
self.downcast(&field_dest, sym::Tuple)?;
4451
// project to the single tuple variant field of `type_info::Tuple` struct type
4552
let tuple_place = self.project_field(&variant_place, FieldIdx::ZERO)?;
4653
assert_eq!(
@@ -58,27 +65,40 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
5865
variant
5966
}
6067
ty::Array(ty, len) => {
61-
let (variant, variant_place) = downcast(sym::Array)?;
68+
let (variant, variant_place) =
69+
self.downcast(&field_dest, sym::Array)?;
6270
let array_place = self.project_field(&variant_place, FieldIdx::ZERO)?;
6371

6472
self.write_array_type_info(array_place, *ty, *len)?;
6573

6674
variant
6775
}
76+
ty::Adt(adt_def, generics) => {
77+
// TODO(type_info): Handle enum and union
78+
if !adt_def.is_struct() {
79+
self.downcast(&field_dest, sym::Other)?.0
80+
} else {
81+
let (variant, variant_place) =
82+
self.downcast(&field_dest, sym::Struct)?;
83+
let place = self.project_field(&variant_place, FieldIdx::ZERO)?;
84+
self.write_adt_type_info(place, (ty, *adt_def), generics)?;
85+
variant
86+
}
87+
}
6888
ty::Bool => {
69-
let (variant, variant_place) = downcast(sym::Bool)?;
89+
let (variant, variant_place) = self.downcast(&field_dest, sym::Bool)?;
7090
let place = self.project_field(&variant_place, FieldIdx::ZERO)?;
7191
self.write_primitive_type_info(place, ty, None)?;
7292
variant
7393
}
7494
ty::Char => {
75-
let (variant, variant_place) = downcast(sym::Char)?;
95+
let (variant, variant_place) = self.downcast(&field_dest, sym::Char)?;
7696
let place = self.project_field(&variant_place, FieldIdx::ZERO)?;
7797
self.write_primitive_type_info(place, ty, None)?;
7898
variant
7999
}
80100
ty::Int(int_ty) => {
81-
let (variant, variant_place) = downcast(sym::Int)?;
101+
let (variant, variant_place) = self.downcast(&field_dest, sym::Int)?;
82102
let place = self.project_field(&variant_place, FieldIdx::ZERO)?;
83103
self.write_primitive_type_info(
84104
place,
@@ -92,7 +112,7 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
92112
variant
93113
}
94114
ty::Uint(uint_ty) => {
95-
let (variant, variant_place) = downcast(sym::Uint)?;
115+
let (variant, variant_place) = self.downcast(&field_dest, sym::Uint)?;
96116
let place = self.project_field(&variant_place, FieldIdx::ZERO)?;
97117
self.write_primitive_type_info(
98118
place,
@@ -106,19 +126,19 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
106126
variant
107127
}
108128
ty::Float(float_ty) => {
109-
let (variant, variant_place) = downcast(sym::Float)?;
129+
let (variant, variant_place) =
130+
self.downcast(&field_dest, sym::Float)?;
110131
let place = self.project_field(&variant_place, FieldIdx::ZERO)?;
111132
self.write_primitive_type_info(place, ty, Some(float_ty.bit_width()))?;
112133
variant
113134
}
114135
ty::Str => {
115-
let (variant, variant_place) = downcast(sym::Str)?;
136+
let (variant, variant_place) = self.downcast(&field_dest, sym::Str)?;
116137
let place = self.project_field(&variant_place, FieldIdx::ZERO)?;
117138
self.write_primitive_type_info(place, ty, None)?;
118139
variant
119140
}
120-
ty::Adt(_, _)
121-
| ty::Foreign(_)
141+
ty::Foreign(_)
122142
| ty::Pat(_, _)
123143
| ty::Slice(_)
124144
| ty::RawPtr(..)
@@ -137,14 +157,14 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
137157
| ty::Bound(..)
138158
| ty::Placeholder(_)
139159
| ty::Infer(..)
140-
| ty::Error(_) => downcast(sym::Other)?.0,
160+
| ty::Error(_) => self.downcast(&field_dest, sym::Other)?.0,
141161
};
142162
self.write_discriminant(variant_index, &field_dest)?
143163
}
144164
sym::size => {
145165
let layout = self.layout_of(ty)?;
146166
let variant_index = if layout.is_sized() {
147-
let (variant, variant_place) = downcast(sym::Some)?;
167+
let (variant, variant_place) = self.downcast(&field_dest, sym::Some)?;
148168
let size_field_place =
149169
self.project_field(&variant_place, FieldIdx::ZERO)?;
150170
self.write_scalar(
@@ -154,7 +174,7 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
154174
)?;
155175
variant
156176
} else {
157-
downcast(sym::None)?.0
177+
self.downcast(&field_dest, sym::None)?.0
158178
};
159179
self.write_discriminant(variant_index, &field_dest)?;
160180
}
@@ -190,7 +210,7 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
190210

191211
while let Some((i, place)) = fields_places.next(self)? {
192212
let field_ty = fields[i as usize];
193-
self.write_field(field_ty, place, tuple_layout, i)?;
213+
self.write_field(field_ty, place, tuple_layout, None, i)?;
194214
}
195215

196216
let fields_place = fields_place.map_provenance(CtfeProvenance::as_immutable);
@@ -205,13 +225,24 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
205225
field_ty: Ty<'tcx>,
206226
place: MPlaceTy<'tcx>,
207227
layout: TyAndLayout<'tcx>,
228+
name: Option<Symbol>,
208229
idx: u64,
209230
) -> InterpResult<'tcx> {
210231
for (field_idx, field_ty_field) in
211232
place.layout.ty.ty_adt_def().unwrap().non_enum_variant().fields.iter_enumerated()
212233
{
213234
let field_place = self.project_field(&place, field_idx)?;
214235
match field_ty_field.name {
236+
sym::name => {
237+
if let Some(name) = name {
238+
let name_place = self.allocate_str_dedup(name.as_str())?;
239+
let ptr = self.mplace_to_ref(&name_place)?;
240+
self.write_immediate(*ptr, &field_place)?
241+
} else {
242+
let (variant, _) = self.downcast(&field_place, sym::None)?;
243+
self.write_discriminant(variant, &field_place)?;
244+
}
245+
}
215246
sym::ty => self.write_type_id(field_ty, &field_place)?,
216247
sym::offset => {
217248
let offset = layout.fields.offset(idx as usize);
@@ -252,6 +283,82 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
252283
interp_ok(())
253284
}
254285

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

compiler/rustc_span/src/symbol.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,7 @@ symbols! {
368368
Stdin,
369369
Str,
370370
String,
371+
Struct,
371372
StructuralPartialEq,
372373
SubdiagMessage,
373374
Subdiagnostic,
@@ -1078,6 +1079,7 @@ symbols! {
10781079
ffi_pure,
10791080
ffi_returns_twice,
10801081
field,
1082+
fields,
10811083
field_init_shorthand,
10821084
file,
10831085
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 struct.
81+
pub name: Option<&'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: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,32 @@ fn test_tuples() {
5858
}
5959
}
6060

61+
#[test]
62+
fn test_structs() {
63+
struct TestStruct {
64+
first: u8,
65+
second: u16,
66+
}
67+
struct NonExhaustive {
68+
a: u8,
69+
}
70+
71+
let Type { kind: Struct(ty), size, .. } = (const { Type::of::<TestStruct>() }) else {
72+
panic!()
73+
};
74+
assert_eq!(size, Some(4));
75+
assert!(!ty.non_exhaustive);
76+
assert_eq!(ty.fields.len(), 2);
77+
assert_eq!(ty.fields[0].name, "first");
78+
assert_eq!(ty.fields[1].name, "second");
79+
80+
let Type { kind: Struct(ty), size, .. } = (const { Type::of::<NonExhaustive>() }) else {
81+
panic!()
82+
};
83+
assert_eq!(size, Some(1));
84+
assert!(ty.non_exhaustive);
85+
}
86+
6187
#[test]
6288
fn test_primitives() {
6389
use TypeKind::*;

0 commit comments

Comments
 (0)