Skip to content

Commit 0b7ff30

Browse files
committed
Support enums in type info reflection
1 parent 8317c4e commit 0b7ff30

9 files changed

Lines changed: 276 additions & 35 deletions

File tree

compiler/rustc_const_eval/src/const_eval/type_info.rs

Lines changed: 113 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -110,15 +110,11 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
110110
variant
111111
}
112112
ty::Adt(adt_def, generics) => {
113-
// TODO(type_info): Handle enum and union
114-
if !adt_def.is_struct() {
113+
// TODO(type_info): Handle union
114+
if !adt_def.is_struct() && !adt_def.is_enum() {
115115
self.downcast(&field_dest, sym::Other)?.0
116116
} else {
117-
let (variant, variant_place) =
118-
self.downcast(&field_dest, sym::Struct)?;
119-
let place = self.project_field(&variant_place, FieldIdx::ZERO)?;
120-
self.write_adt_type_info(place, (ty, *adt_def), generics)?;
121-
variant
117+
self.write_adt_type_info(&field_dest, (ty, *adt_def), generics)?
122118
}
123119
}
124120
ty::Bool => {
@@ -253,6 +249,21 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
253249
self.write_immediate(ptr, &fields_slice_place)
254250
}
255251

252+
// Write fields for struct, enum variants
253+
fn write_variant_fields(
254+
&mut self,
255+
place: impl Writeable<'tcx, CtfeProvenance>,
256+
variant_def: &'tcx VariantDef,
257+
variant_layout: TyAndLayout<'tcx>,
258+
generics: &'tcx GenericArgs<'tcx>,
259+
) -> InterpResult<'tcx> {
260+
self.project_write_array(place, variant_def.fields.len() as u64, |this, i, place| {
261+
let field_def = &variant_def.fields[FieldIdx::from_usize(i as usize)];
262+
let field_ty = field_def.ty(*this.tcx, generics);
263+
this.write_field(field_ty, place, variant_layout, Some(field_def.name), i)
264+
})
265+
}
266+
256267
fn write_field(
257268
&mut self,
258269
field_ty: Ty<'tcx>,
@@ -318,20 +329,31 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
318329
// FIXME(type_info): No semver considerations for now
319330
pub(crate) fn write_adt_type_info(
320331
&mut self,
321-
place: impl Writeable<'tcx, CtfeProvenance>,
332+
place: &(impl Writeable<'tcx, CtfeProvenance> + 'tcx),
322333
adt: (Ty<'tcx>, AdtDef<'tcx>),
323334
generics: &'tcx GenericArgs<'tcx>,
324-
) -> InterpResult<'tcx> {
335+
) -> InterpResult<'tcx, VariantIdx> {
325336
let (adt_ty, adt_def) = adt;
326-
match adt_def.adt_kind() {
327-
AdtKind::Struct => self.write_struct_type_info(
328-
place,
329-
(adt_ty, adt_def.variant(VariantIdx::ZERO)),
330-
generics,
331-
),
337+
let variant_idx = match adt_def.adt_kind() {
338+
AdtKind::Struct => {
339+
let (variant, variant_place) = self.downcast(place, sym::Struct)?;
340+
let place = self.project_field(&variant_place, FieldIdx::ZERO)?;
341+
self.write_struct_type_info(
342+
place,
343+
(adt_ty, adt_def.variant(VariantIdx::ZERO)),
344+
generics,
345+
)?;
346+
variant
347+
}
348+
AdtKind::Enum => {
349+
let (variant, variant_place) = self.downcast(place, sym::Enum)?;
350+
let place = self.project_field(&variant_place, FieldIdx::ZERO)?;
351+
self.write_enum_type_info(place, adt, generics)?;
352+
variant
353+
}
332354
AdtKind::Union => todo!(),
333-
AdtKind::Enum => todo!(),
334-
}
355+
};
356+
interp_ok(variant_idx)
335357
}
336358

337359
pub(crate) fn write_struct_type_info(
@@ -350,15 +372,9 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
350372

351373
match field.name {
352374
sym::generics => self.write_generics(field_place, generics)?,
353-
sym::fields => self.project_write_array(
354-
field_place,
355-
struct_def.fields.len() as u64,
356-
|this, i, place| {
357-
let field_def = &struct_def.fields[FieldIdx::from_usize(i as usize)];
358-
let field_ty = field_def.ty(*this.tcx, generics);
359-
this.write_field(field_ty, place, struct_layout, Some(field_def.name), i)
360-
},
361-
)?,
375+
sym::fields => {
376+
self.write_variant_fields(field_place, struct_def, struct_layout, generics)?
377+
}
362378
sym::non_exhaustive => {
363379
let is_non_exhaustive = struct_def.is_field_list_non_exhaustive();
364380
self.write_scalar(Scalar::from_bool(is_non_exhaustive), &field_place)?
@@ -371,6 +387,77 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
371387
interp_ok(())
372388
}
373389

390+
pub(crate) fn write_enum_type_info(
391+
&mut self,
392+
place: impl Writeable<'tcx, CtfeProvenance>,
393+
enum_: (Ty<'tcx>, AdtDef<'tcx>),
394+
generics: &'tcx GenericArgs<'tcx>,
395+
) -> InterpResult<'tcx> {
396+
let (enum_ty, enum_def) = enum_;
397+
let enum_layout = self.layout_of(enum_ty)?;
398+
399+
for (field_idx, field) in
400+
place.layout().ty.ty_adt_def().unwrap().non_enum_variant().fields.iter_enumerated()
401+
{
402+
let field_place = self.project_field(&place, field_idx)?;
403+
404+
match field.name {
405+
sym::generics => self.write_generics(field_place, generics)?,
406+
sym::variants => {
407+
self.project_write_array(
408+
field_place,
409+
enum_def.variants().len() as u64,
410+
|this, i, place| {
411+
let variant_idx = VariantIdx::from_usize(i as usize);
412+
let variant_def = &enum_def.variants()[variant_idx];
413+
let variant_layout = enum_layout.for_variant(this, variant_idx);
414+
// TODO(type_info): Is it correct to use enum_ty here? If yes, leave some explanation.
415+
this.write_enum_variant(place, (variant_layout, &variant_def), generics)
416+
},
417+
)?;
418+
}
419+
sym::non_exhaustive => {
420+
let is_non_exhaustive = enum_def.is_variant_list_non_exhaustive();
421+
self.write_scalar(Scalar::from_bool(is_non_exhaustive), &field_place)?
422+
}
423+
other => span_bug!(self.tcx.def_span(field.did), "unimplemented field {other}"),
424+
}
425+
}
426+
427+
interp_ok(())
428+
}
429+
430+
fn write_enum_variant(
431+
&mut self,
432+
place: impl Writeable<'tcx, CtfeProvenance>,
433+
variant: (TyAndLayout<'tcx>, &'tcx VariantDef),
434+
generics: &'tcx GenericArgs<'tcx>,
435+
) -> InterpResult<'tcx> {
436+
let (variant_layout, variant_def) = variant;
437+
438+
for (field_idx, field_def) in
439+
place.layout().ty.ty_adt_def().unwrap().non_enum_variant().fields.iter_enumerated()
440+
{
441+
let field_place = self.project_field(&place, field_idx)?;
442+
match field_def.name {
443+
sym::name => {
444+
let name_place = self.allocate_str_dedup(variant_def.name.as_str())?;
445+
let ptr = self.mplace_to_ref(&name_place)?;
446+
self.write_immediate(*ptr, &field_place)?
447+
}
448+
sym::fields => {
449+
self.write_variant_fields(field_place, &variant_def, variant_layout, generics)?
450+
}
451+
sym::non_exhaustive => {
452+
let is_non_exhaustive = variant_def.is_field_list_non_exhaustive();
453+
self.write_scalar(Scalar::from_bool(is_non_exhaustive), &field_place)?
454+
}
455+
other => span_bug!(self.tcx.def_span(field_def.did), "unimplemented field {other}"),
456+
}
457+
}
458+
interp_ok(())
459+
}
460+
374461
fn write_generics(
375462
&mut self,
376463
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
@@ -234,6 +234,7 @@ symbols! {
234234
Duration,
235235
Encodable,
236236
Encoder,
237+
Enum,
237238
Enumerate,
238239
Eq,
239240
Equal,
@@ -2469,6 +2470,7 @@ symbols! {
24692470
values,
24702471
var,
24712472
variant_count,
2473+
variants,
24722474
vec,
24732475
vec_as_mut_slice,
24742476
vec_as_slice,

library/core/src/mem/type_info.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ pub enum TypeKind {
4747
Array(Array),
4848
/// Structs.
4949
Struct(Struct),
50+
/// Enums.
51+
Enum(Enum),
5052
/// Primitive boolean type.
5153
Bool(Bool),
5254
/// Primitive character type.
@@ -109,6 +111,32 @@ pub struct Struct {
109111
pub non_exhaustive: bool,
110112
}
111113

114+
/// Compile-time type information about enums.
115+
#[derive(Debug)]
116+
#[non_exhaustive]
117+
#[unstable(feature = "type_info", issue = "146922")]
118+
pub struct Enum {
119+
/// Instantiated generics of the enum.
120+
pub generics: &'static [Generic],
121+
/// All variants of the enum.
122+
pub variants: &'static [Variant],
123+
/// Whether the enum variant list is non-exhaustive.
124+
pub non_exhaustive: bool,
125+
}
126+
127+
/// Compile-time type information about variants of enums.
128+
#[derive(Debug)]
129+
#[non_exhaustive]
130+
#[unstable(feature = "type_info", issue = "146922")]
131+
pub struct Variant {
132+
/// The name of the variant.
133+
pub name: &'static str,
134+
/// All fields of the variant.
135+
pub fields: &'static [Field],
136+
/// Whether the enum variant fields is non-exhaustive.
137+
pub non_exhaustive: bool,
138+
}
139+
112140
/// Compile-time type information about instantiated generics of structs, enum and union variants.
113141
#[derive(Debug)]
114142
#[non_exhaustive]

library/coretests/tests/mem/type_info.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,48 @@ fn test_structs() {
119119
}
120120
}
121121

122+
#[test]
123+
fn test_enums() {
124+
use TypeKind::*;
125+
126+
const {
127+
enum E {
128+
Some(u32),
129+
None,
130+
#[non_exhaustive]
131+
Foomp {
132+
a: (),
133+
b: &'static str,
134+
},
135+
}
136+
137+
let Type { kind: Enum(ty), size, .. } = Type::of::<E>() else { panic!() };
138+
assert!(size == Some(size_of::<E>()));
139+
assert!(ty.variants.len() == 3);
140+
141+
assert!(ty.variants[0].name == "Some");
142+
assert!(!ty.variants[0].non_exhaustive);
143+
assert!(ty.variants[0].fields.len() == 1);
144+
145+
assert!(ty.variants[1].name == "None");
146+
assert!(!ty.variants[1].non_exhaustive);
147+
assert!(ty.variants[1].fields.len() == 0);
148+
149+
assert!(ty.variants[2].name == "Foomp");
150+
assert!(ty.variants[2].non_exhaustive);
151+
assert!(ty.variants[2].fields.len() == 2);
152+
}
153+
154+
const {
155+
let Type { kind: Enum(ty), size, .. } = Type::of::<Option<i32>>() else { panic!() };
156+
assert!(size == Some(size_of::<Option<i32>>()));
157+
assert!(ty.variants.len() == 2);
158+
assert!(ty.generics.len() == 1);
159+
let Generic::Type(GenericType { ty: generic_ty, .. }) = ty.generics[0] else { panic!() };
160+
assert!(generic_ty == TypeId::of::<i32>());
161+
}
162+
}
163+
122164
#[test]
123165
fn test_primitives() {
124166
use TypeKind::*;

tests/ui/privacy/issue-79593.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ mod foo {
1010
Pub {};
1111
//~^ ERROR missing field `private` in initializer of `Pub`
1212
Enum::Variant { x: () };
13-
//~^ ERROR missing field `y` in initializer of `Enum`
13+
//~^ ERROR missing field `y` in initializer of `foo::Enum`
1414
}
1515
}
1616

@@ -21,9 +21,9 @@ fn correct() {
2121

2222
fn wrong() {
2323
foo::Enum::Variant { x: () };
24-
//~^ ERROR missing field `y` in initializer of `Enum`
24+
//~^ ERROR missing field `y` in initializer of `foo::Enum`
2525
foo::Enum::Variant { };
26-
//~^ ERROR missing fields `x` and `y` in initializer of `Enum`
26+
//~^ ERROR missing fields `x` and `y` in initializer of `foo::Enum`
2727
}
2828

2929
fn main() {}

tests/ui/privacy/issue-79593.stderr

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ error[E0063]: missing field `private` in initializer of `Pub`
44
LL | Pub {};
55
| ^^^ missing `private`
66

7-
error[E0063]: missing field `y` in initializer of `Enum`
7+
error[E0063]: missing field `y` in initializer of `foo::Enum`
88
--> $DIR/issue-79593.rs:12:9
99
|
1010
LL | Enum::Variant { x: () };
@@ -18,13 +18,13 @@ LL | foo::Pub {};
1818
|
1919
= note: private field `private` that was not provided
2020

21-
error[E0063]: missing field `y` in initializer of `Enum`
21+
error[E0063]: missing field `y` in initializer of `foo::Enum`
2222
--> $DIR/issue-79593.rs:23:5
2323
|
2424
LL | foo::Enum::Variant { x: () };
2525
| ^^^^^^^^^^^^^^^^^^ missing `y`
2626

27-
error[E0063]: missing fields `x` and `y` in initializer of `Enum`
27+
error[E0063]: missing fields `x` and `y` in initializer of `foo::Enum`
2828
--> $DIR/issue-79593.rs:25:5
2929
|
3030
LL | foo::Enum::Variant { };

tests/ui/reflection/dump.bit32.run.stdout

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,46 @@ Type {
164164
),
165165
}
166166
Type {
167-
kind: Other,
167+
kind: Enum(
168+
Enum {
169+
generics: [],
170+
variants: [
171+
Variant {
172+
name: "Some",
173+
fields: [
174+
Field {
175+
name: "0",
176+
ty: TypeId(0x1378bb1c0a0202683eb65e7c11f2e4d7),
177+
offset: 4,
178+
},
179+
],
180+
non_exhaustive: false,
181+
},
182+
Variant {
183+
name: "None",
184+
fields: [],
185+
non_exhaustive: false,
186+
},
187+
Variant {
188+
name: "Foomp",
189+
fields: [
190+
Field {
191+
name: "a",
192+
ty: TypeId(0x41223169ff28813ba79b7268a2a968d9),
193+
offset: 4,
194+
},
195+
Field {
196+
name: "b",
197+
ty: TypeId(0xb98b1b7157a6417863eb502cd6cb5d6d),
198+
offset: 4,
199+
},
200+
],
201+
non_exhaustive: true,
202+
},
203+
],
204+
non_exhaustive: false,
205+
},
206+
),
168207
size: Some(
169208
12,
170209
),

0 commit comments

Comments
 (0)