rustc_const_eval/const_eval/type_info/
adt.rs1use rustc_abi::{FieldIdx, VariantIdx};
2use rustc_middle::ty::layout::TyAndLayout;
3use rustc_middle::ty::{
4 AdtDef, AdtKind, Const, ConstKind, GenericArgKind, GenericArgs, Region, Ty, VariantDef,
5};
6use rustc_middle::{bug, span_bug};
7use rustc_span::sym;
8
9use crate::const_eval::CompileTimeMachine;
10use crate::interpret::{
11 CtfeProvenance, InterpCx, InterpResult, MPlaceTy, Projectable, Scalar, Writeable, interp_ok,
12};
13
14impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
15 pub(crate) fn write_adt_type_info(
17 &mut self,
18 place: &impl Writeable<'tcx, CtfeProvenance>,
19 adt: (Ty<'tcx>, AdtDef<'tcx>),
20 generics: &'tcx GenericArgs<'tcx>,
21 ) -> InterpResult<'tcx, VariantIdx> {
22 let (adt_ty, adt_def) = adt;
23 let variant_idx = match adt_def.adt_kind() {
24 AdtKind::Struct => {
25 let (variant, variant_place) = self.downcast(place, sym::Struct)?;
26 let place = self.project_field(&variant_place, FieldIdx::ZERO)?;
27 self.write_struct_type_info(
28 place,
29 (adt_ty, adt_def.variant(VariantIdx::ZERO)),
30 generics,
31 )?;
32 variant
33 }
34 AdtKind::Union => {
35 let (variant, variant_place) = self.downcast(place, sym::Union)?;
36 let place = self.project_field(&variant_place, FieldIdx::ZERO)?;
37 self.write_union_type_info(
38 place,
39 (adt_ty, adt_def.variant(VariantIdx::ZERO)),
40 generics,
41 )?;
42 variant
43 }
44 AdtKind::Enum => {
45 let (variant, variant_place) = self.downcast(place, sym::Enum)?;
46 let place = self.project_field(&variant_place, FieldIdx::ZERO)?;
47 self.write_enum_type_info(place, adt, generics)?;
48 variant
49 }
50 };
51 interp_ok(variant_idx)
52 }
53
54 pub(crate) fn write_struct_type_info(
55 &mut self,
56 place: impl Writeable<'tcx, CtfeProvenance>,
57 struct_: (Ty<'tcx>, &'tcx VariantDef),
58 generics: &'tcx GenericArgs<'tcx>,
59 ) -> InterpResult<'tcx> {
60 let (struct_ty, struct_def) = struct_;
61 let struct_layout = self.layout_of(struct_ty)?;
62
63 for (field_idx, field) in
64 place.layout().ty.ty_adt_def().unwrap().non_enum_variant().fields.iter_enumerated()
65 {
66 let field_place = self.project_field(&place, field_idx)?;
67
68 match field.name {
69 sym::generics => self.write_generics(field_place, generics)?,
70 sym::fields => {
71 self.write_variant_fields(field_place, struct_def, struct_layout, generics)?
72 }
73 sym::non_exhaustive => {
74 let is_non_exhaustive = struct_def.is_field_list_non_exhaustive();
75 self.write_scalar(Scalar::from_bool(is_non_exhaustive), &field_place)?
76 }
77 other => ::rustc_middle::util::bug::span_bug_fmt(self.tcx.def_span(field.did),
format_args!("unimplemented field {0}", other))span_bug!(self.tcx.def_span(field.did), "unimplemented field {other}"),
78 }
79 }
80
81 interp_ok(())
82 }
83
84 pub(crate) fn write_union_type_info(
85 &mut self,
86 place: impl Writeable<'tcx, CtfeProvenance>,
87 union_: (Ty<'tcx>, &'tcx VariantDef),
88 generics: &'tcx GenericArgs<'tcx>,
89 ) -> InterpResult<'tcx> {
90 let (union_ty, union_def) = union_;
91 let union_layout = self.layout_of(union_ty)?;
92
93 for (field_idx, field) in
94 place.layout().ty.ty_adt_def().unwrap().non_enum_variant().fields.iter_enumerated()
95 {
96 let field_place = self.project_field(&place, field_idx)?;
97
98 match field.name {
99 sym::generics => self.write_generics(field_place, generics)?,
100 sym::fields => {
101 self.write_variant_fields(field_place, union_def, union_layout, generics)?
102 }
103 sym::non_exhaustive => {
104 let is_non_exhaustive = union_def.is_field_list_non_exhaustive();
105 self.write_scalar(Scalar::from_bool(is_non_exhaustive), &field_place)?
106 }
107 other => ::rustc_middle::util::bug::span_bug_fmt(self.tcx.def_span(field.did),
format_args!("unimplemented field {0}", other))span_bug!(self.tcx.def_span(field.did), "unimplemented field {other}"),
108 }
109 }
110
111 interp_ok(())
112 }
113
114 pub(crate) fn write_enum_type_info(
115 &mut self,
116 place: impl Writeable<'tcx, CtfeProvenance>,
117 enum_: (Ty<'tcx>, AdtDef<'tcx>),
118 generics: &'tcx GenericArgs<'tcx>,
119 ) -> InterpResult<'tcx> {
120 let (enum_ty, enum_def) = enum_;
121 let enum_layout = self.layout_of(enum_ty)?;
122
123 for (field_idx, field) in
124 place.layout().ty.ty_adt_def().unwrap().non_enum_variant().fields.iter_enumerated()
125 {
126 let field_place = self.project_field(&place, field_idx)?;
127
128 match field.name {
129 sym::generics => self.write_generics(field_place, generics)?,
130 sym::variants => {
131 self.allocate_fill_and_write_slice_ptr(
132 field_place,
133 enum_def.variants().len() as u64,
134 |this, i, place| {
135 let variant_idx = VariantIdx::from_usize(i as usize);
136 let variant_def = &enum_def.variants()[variant_idx];
137 let variant_layout = enum_layout.for_variant(this, variant_idx);
138 this.write_enum_variant(place, (variant_layout, &variant_def), generics)
139 },
140 )?;
141 }
142 sym::non_exhaustive => {
143 let is_non_exhaustive = enum_def.is_variant_list_non_exhaustive();
144 self.write_scalar(Scalar::from_bool(is_non_exhaustive), &field_place)?
145 }
146 other => ::rustc_middle::util::bug::span_bug_fmt(self.tcx.def_span(field.did),
format_args!("unimplemented field {0}", other))span_bug!(self.tcx.def_span(field.did), "unimplemented field {other}"),
147 }
148 }
149
150 interp_ok(())
151 }
152
153 fn write_enum_variant(
154 &mut self,
155 place: impl Writeable<'tcx, CtfeProvenance>,
156 variant: (TyAndLayout<'tcx>, &'tcx VariantDef),
157 generics: &'tcx GenericArgs<'tcx>,
158 ) -> InterpResult<'tcx> {
159 let (variant_layout, variant_def) = variant;
160
161 for (field_idx, field_def) in
162 place.layout().ty.ty_adt_def().unwrap().non_enum_variant().fields.iter_enumerated()
163 {
164 let field_place = self.project_field(&place, field_idx)?;
165 match field_def.name {
166 sym::name => {
167 let name_place = self.allocate_str_dedup(variant_def.name.as_str())?;
168 let ptr = self.mplace_to_imm_ptr(&name_place, None)?;
169 self.write_immediate(*ptr, &field_place)?
170 }
171 sym::fields => {
172 self.write_variant_fields(field_place, &variant_def, variant_layout, generics)?
173 }
174 sym::non_exhaustive => {
175 let is_non_exhaustive = variant_def.is_field_list_non_exhaustive();
176 self.write_scalar(Scalar::from_bool(is_non_exhaustive), &field_place)?
177 }
178 other => ::rustc_middle::util::bug::span_bug_fmt(self.tcx.def_span(field_def.did),
format_args!("unimplemented field {0}", other))span_bug!(self.tcx.def_span(field_def.did), "unimplemented field {other}"),
179 }
180 }
181 interp_ok(())
182 }
183
184 fn write_variant_fields(
186 &mut self,
187 place: impl Writeable<'tcx, CtfeProvenance>,
188 variant_def: &'tcx VariantDef,
189 variant_layout: TyAndLayout<'tcx>,
190 generics: &'tcx GenericArgs<'tcx>,
191 ) -> InterpResult<'tcx> {
192 self.allocate_fill_and_write_slice_ptr(
193 place,
194 variant_def.fields.len() as u64,
195 |this, i, place| {
196 let field_def = &variant_def.fields[FieldIdx::from_usize(i as usize)];
197 let field_ty = field_def.ty(*this.tcx, generics);
198 this.write_field(field_ty, place, variant_layout, Some(field_def.name), i)
199 },
200 )
201 }
202
203 fn write_generics(
204 &mut self,
205 place: impl Writeable<'tcx, CtfeProvenance>,
206 generics: &'tcx GenericArgs<'tcx>,
207 ) -> InterpResult<'tcx> {
208 self.allocate_fill_and_write_slice_ptr(place, generics.len() as u64, |this, i, place| {
209 match generics[i as usize].kind() {
210 GenericArgKind::Lifetime(region) => this.write_generic_lifetime(region, place),
211 GenericArgKind::Type(ty) => this.write_generic_type(ty, place),
212 GenericArgKind::Const(c) => this.write_generic_const(c, place),
213 }
214 })
215 }
216
217 fn write_generic_lifetime(
218 &mut self,
219 _region: Region<'tcx>,
220 place: MPlaceTy<'tcx>,
221 ) -> InterpResult<'tcx> {
222 let (variant_idx, _) = self.downcast(&place, sym::Lifetime)?;
223 self.write_discriminant(variant_idx, &place)?;
224 interp_ok(())
225 }
226
227 fn write_generic_type(&mut self, ty: Ty<'tcx>, place: MPlaceTy<'tcx>) -> InterpResult<'tcx> {
228 let (variant_idx, variant_place) = self.downcast(&place, sym::Type)?;
229 let generic_type_place = self.project_field(&variant_place, FieldIdx::ZERO)?;
230
231 for (field_idx, field_def) in generic_type_place
232 .layout()
233 .ty
234 .ty_adt_def()
235 .unwrap()
236 .non_enum_variant()
237 .fields
238 .iter_enumerated()
239 {
240 let field_place = self.project_field(&generic_type_place, field_idx)?;
241 match field_def.name {
242 sym::ty => self.write_type_id(ty, &field_place)?,
243 other => ::rustc_middle::util::bug::span_bug_fmt(self.tcx.def_span(field_def.did),
format_args!("unimplemented field {0}", other))span_bug!(self.tcx.def_span(field_def.did), "unimplemented field {other}"),
244 }
245 }
246
247 self.write_discriminant(variant_idx, &place)?;
248 interp_ok(())
249 }
250
251 fn write_generic_const(&mut self, c: Const<'tcx>, place: MPlaceTy<'tcx>) -> InterpResult<'tcx> {
252 let ConstKind::Value(c) = c.kind() else { ::rustc_middle::util::bug::bug_fmt(format_args!("expected a computed const, got {0:?}",
c))bug!("expected a computed const, got {c:?}") };
253
254 let (variant_idx, variant_place) = self.downcast(&place, sym::Const)?;
255 let const_place = self.project_field(&variant_place, FieldIdx::ZERO)?;
256
257 for (field_idx, field_def) in const_place
258 .layout()
259 .ty
260 .ty_adt_def()
261 .unwrap()
262 .non_enum_variant()
263 .fields
264 .iter_enumerated()
265 {
266 let field_place = self.project_field(&const_place, field_idx)?;
267 match field_def.name {
268 sym::ty => self.write_type_id(c.ty, &field_place)?,
269 other => ::rustc_middle::util::bug::span_bug_fmt(self.tcx.def_span(field_def.did),
format_args!("unimplemented field {0}", other))span_bug!(self.tcx.def_span(field_def.did), "unimplemented field {other}"),
270 }
271 }
272
273 self.write_discriminant(variant_idx, &place)?;
274 interp_ok(())
275 }
276}