Skip to main content

rustc_const_eval/const_eval/type_info/
adt.rs

1use 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    // FIXME(type_info): No semver considerations for now
16    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    // Write fields for struct, enum variants
185    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}