Skip to main content

rustc_const_eval/const_eval/
type_info.rs

1mod adt;
2
3use std::borrow::Cow;
4
5use rustc_abi::{ExternAbi, FieldIdx};
6use rustc_ast::Mutability;
7use rustc_hir::LangItem;
8use rustc_middle::span_bug;
9use rustc_middle::ty::layout::TyAndLayout;
10use rustc_middle::ty::{self, Const, FnHeader, FnSigTys, ScalarInt, Ty, TyCtxt};
11use rustc_span::{Symbol, sym};
12
13use crate::const_eval::CompileTimeMachine;
14use crate::interpret::{
15    CtfeProvenance, Immediate, InterpCx, InterpResult, MPlaceTy, MemoryKind, Scalar, Writeable,
16    interp_ok,
17};
18
19impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
20    // A general method to write an array to a static slice place.
21    fn allocate_fill_and_write_slice_ptr(
22        &mut self,
23        slice_place: impl Writeable<'tcx, CtfeProvenance>,
24        len: u64,
25        writer: impl Fn(&mut Self, /* index */ u64, MPlaceTy<'tcx>) -> InterpResult<'tcx>,
26    ) -> InterpResult<'tcx> {
27        // Array element type
28        let field_ty = slice_place
29            .layout()
30            .ty
31            .builtin_deref(false)
32            .unwrap()
33            .sequence_element_type(self.tcx.tcx);
34
35        // Allocate an array
36        let array_layout = self.layout_of(Ty::new_array(self.tcx.tcx, field_ty, len))?;
37        let array_place = self.allocate(array_layout, MemoryKind::Stack)?;
38
39        // Fill the array fields
40        let mut field_places = self.project_array_fields(&array_place)?;
41        while let Some((i, place)) = field_places.next(self)? {
42            writer(self, i, place)?;
43        }
44
45        // Write the slice pointing to the array
46        let array_place = array_place.map_provenance(CtfeProvenance::as_immutable);
47        let ptr = Immediate::new_slice(array_place.ptr(), len, self);
48        self.write_immediate(ptr, &slice_place)
49    }
50
51    /// Writes a `core::mem::type_info::TypeInfo` for a given type, `ty` to the given place.
52    pub(crate) fn write_type_info(
53        &mut self,
54        ty: Ty<'tcx>,
55        dest: &impl Writeable<'tcx, CtfeProvenance>,
56    ) -> InterpResult<'tcx> {
57        let ty_struct = self.tcx.require_lang_item(LangItem::Type, self.tcx.span);
58        let ty_struct = self.tcx.type_of(ty_struct).no_bound_vars().unwrap();
59        match (&ty_struct, &dest.layout().ty) {
    (left_val, right_val) => {
        if !(*left_val == *right_val) {
            let kind = ::core::panicking::AssertKind::Eq;
            ::core::panicking::assert_failed(kind, &*left_val, &*right_val,
                ::core::option::Option::None);
        }
    }
};assert_eq!(ty_struct, dest.layout().ty);
60        let ty_struct = ty_struct.ty_adt_def().unwrap().non_enum_variant();
61        // Fill all fields of the `TypeInfo` struct.
62        for (idx, field) in ty_struct.fields.iter_enumerated() {
63            let field_dest = self.project_field(dest, idx)?;
64            let ptr_bit_width = || self.tcx.data_layout.pointer_size().bits();
65            match field.name {
66                sym::kind => {
67                    let variant_index = match ty.kind() {
68                        ty::Tuple(fields) => {
69                            let (variant, variant_place) =
70                                self.project_downcast_named(&field_dest, sym::Tuple)?;
71                            // project to the single tuple variant field of `type_info::Tuple` struct type
72                            let tuple_place = self.project_field(&variant_place, FieldIdx::ZERO)?;
73                            match (&1,
        &tuple_place.layout().ty.ty_adt_def().unwrap().non_enum_variant().fields.len())
    {
    (left_val, right_val) => {
        if !(*left_val == *right_val) {
            let kind = ::core::panicking::AssertKind::Eq;
            ::core::panicking::assert_failed(kind, &*left_val, &*right_val,
                ::core::option::Option::None);
        }
    }
};assert_eq!(
74                                1,
75                                tuple_place
76                                    .layout()
77                                    .ty
78                                    .ty_adt_def()
79                                    .unwrap()
80                                    .non_enum_variant()
81                                    .fields
82                                    .len()
83                            );
84                            self.write_tuple_type_info(tuple_place, fields, ty)?;
85                            variant
86                        }
87                        ty::Array(ty, len) => {
88                            let (variant, variant_place) =
89                                self.project_downcast_named(&field_dest, sym::Array)?;
90                            let array_place = self.project_field(&variant_place, FieldIdx::ZERO)?;
91
92                            self.write_array_type_info(array_place, *ty, *len)?;
93
94                            variant
95                        }
96                        ty::Slice(ty) => {
97                            let (variant, variant_place) =
98                                self.project_downcast_named(&field_dest, sym::Slice)?;
99                            let slice_place = self.project_field(&variant_place, FieldIdx::ZERO)?;
100
101                            self.write_slice_type_info(slice_place, *ty)?;
102
103                            variant
104                        }
105                        ty::Adt(adt_def, generics) => {
106                            self.write_adt_type_info(&field_dest, (ty, *adt_def), generics)?
107                        }
108                        ty::Bool => {
109                            let (variant, _variant_place) =
110                                self.project_downcast_named(&field_dest, sym::Bool)?;
111                            variant
112                        }
113                        ty::Char => {
114                            let (variant, _variant_place) =
115                                self.project_downcast_named(&field_dest, sym::Char)?;
116                            variant
117                        }
118                        ty::Int(int_ty) => {
119                            let (variant, variant_place) =
120                                self.project_downcast_named(&field_dest, sym::Int)?;
121                            let place = self.project_field(&variant_place, FieldIdx::ZERO)?;
122                            self.write_int_type_info(
123                                place,
124                                int_ty.bit_width().unwrap_or_else(/* isize */ ptr_bit_width),
125                                true,
126                            )?;
127                            variant
128                        }
129                        ty::Uint(uint_ty) => {
130                            let (variant, variant_place) =
131                                self.project_downcast_named(&field_dest, sym::Int)?;
132                            let place = self.project_field(&variant_place, FieldIdx::ZERO)?;
133                            self.write_int_type_info(
134                                place,
135                                uint_ty.bit_width().unwrap_or_else(/* usize */ ptr_bit_width),
136                                false,
137                            )?;
138                            variant
139                        }
140                        ty::Float(float_ty) => {
141                            let (variant, variant_place) =
142                                self.project_downcast_named(&field_dest, sym::Float)?;
143                            let place = self.project_field(&variant_place, FieldIdx::ZERO)?;
144                            self.write_float_type_info(place, float_ty.bit_width())?;
145                            variant
146                        }
147                        ty::Str => {
148                            let (variant, _variant_place) =
149                                self.project_downcast_named(&field_dest, sym::Str)?;
150                            variant
151                        }
152                        ty::Ref(_, ty, mutability) => {
153                            let (variant, variant_place) =
154                                self.project_downcast_named(&field_dest, sym::Reference)?;
155                            let reference_place =
156                                self.project_field(&variant_place, FieldIdx::ZERO)?;
157                            self.write_reference_type_info(reference_place, *ty, *mutability)?;
158
159                            variant
160                        }
161                        ty::RawPtr(ty, mutability) => {
162                            let (variant, variant_place) =
163                                self.project_downcast_named(&field_dest, sym::Pointer)?;
164                            let pointer_place =
165                                self.project_field(&variant_place, FieldIdx::ZERO)?;
166
167                            self.write_pointer_type_info(pointer_place, *ty, *mutability)?;
168
169                            variant
170                        }
171                        ty::Dynamic(predicates, region) => {
172                            let (variant, variant_place) =
173                                self.project_downcast_named(&field_dest, sym::DynTrait)?;
174                            let dyn_place = self.project_field(&variant_place, FieldIdx::ZERO)?;
175                            self.write_dyn_trait_type_info(dyn_place, *predicates, *region)?;
176                            variant
177                        }
178                        ty::FnPtr(sig, fn_header) => {
179                            let (variant, variant_place) =
180                                self.project_downcast_named(&field_dest, sym::FnPtr)?;
181                            let fn_ptr_place =
182                                self.project_field(&variant_place, FieldIdx::ZERO)?;
183
184                            // FIXME: handle lifetime bounds
185                            let sig = sig.skip_binder();
186
187                            self.write_fn_ptr_type_info(fn_ptr_place, &sig, fn_header)?;
188                            variant
189                        }
190                        ty::Foreign(_)
191                        | ty::Pat(_, _)
192                        | ty::FnDef(..)
193                        | ty::UnsafeBinder(..)
194                        | ty::Closure(..)
195                        | ty::CoroutineClosure(..)
196                        | ty::Coroutine(..)
197                        | ty::CoroutineWitness(..)
198                        | ty::Never
199                        | ty::Alias(..)
200                        | ty::Param(_)
201                        | ty::Bound(..)
202                        | ty::Placeholder(_)
203                        | ty::Infer(..)
204                        | ty::Error(_) => self.project_downcast_named(&field_dest, sym::Other)?.0,
205                    };
206                    self.write_discriminant(variant_index, &field_dest)?
207                }
208                other => ::rustc_middle::util::bug::span_bug_fmt(self.tcx.span,
    format_args!("unknown `Type` field {0}", other))span_bug!(self.tcx.span, "unknown `Type` field {other}"),
209            }
210        }
211
212        interp_ok(())
213    }
214
215    fn write_field(
216        &mut self,
217        field_ty: Ty<'tcx>,
218        place: MPlaceTy<'tcx>,
219        layout: TyAndLayout<'tcx>,
220        name: Option<Symbol>,
221        idx: u64,
222    ) -> InterpResult<'tcx> {
223        for (field_idx, field_ty_field) in
224            place.layout.ty.ty_adt_def().unwrap().non_enum_variant().fields.iter_enumerated()
225        {
226            let field_place = self.project_field(&place, field_idx)?;
227            match field_ty_field.name {
228                sym::name => {
229                    let name = match name.as_ref() {
230                        Some(name) => Cow::Borrowed(name.as_str()),
231                        None => Cow::Owned(idx.to_string()), // For tuples
232                    };
233                    let name_place = self.allocate_str_dedup(&name)?;
234                    let ptr = self.mplace_to_imm_ptr(&name_place, None)?;
235                    self.write_immediate(*ptr, &field_place)?
236                }
237                sym::ty => {
238                    let field_ty = self.tcx.erase_and_anonymize_regions(field_ty);
239                    self.write_type_id(field_ty, &field_place)?
240                }
241                sym::offset => {
242                    let offset = layout.fields.offset(idx as usize);
243                    self.write_scalar(
244                        ScalarInt::try_from_target_usize(offset.bytes(), self.tcx.tcx).unwrap(),
245                        &field_place,
246                    )?;
247                }
248                other => {
249                    ::rustc_middle::util::bug::span_bug_fmt(self.tcx.def_span(field_ty_field.did),
    format_args!("unimplemented field {0}", other))span_bug!(self.tcx.def_span(field_ty_field.did), "unimplemented field {other}")
250                }
251            }
252        }
253        interp_ok(())
254    }
255
256    pub(crate) fn write_tuple_type_info(
257        &mut self,
258        tuple_place: impl Writeable<'tcx, CtfeProvenance>,
259        fields: &[Ty<'tcx>],
260        tuple_ty: Ty<'tcx>,
261    ) -> InterpResult<'tcx> {
262        let tuple_layout = self.layout_of(tuple_ty)?;
263        let fields_slice_place = self.project_field(&tuple_place, FieldIdx::ZERO)?;
264        self.allocate_fill_and_write_slice_ptr(
265            fields_slice_place,
266            fields.len() as u64,
267            |this, i, place| {
268                let field_ty = fields[i as usize];
269                this.write_field(field_ty, place, tuple_layout, None, i)
270            },
271        )
272    }
273
274    pub(crate) fn write_array_type_info(
275        &mut self,
276        place: impl Writeable<'tcx, CtfeProvenance>,
277        ty: Ty<'tcx>,
278        len: Const<'tcx>,
279    ) -> InterpResult<'tcx> {
280        // Iterate over all fields of `type_info::Array`.
281        for (field_idx, field) in
282            place.layout().ty.ty_adt_def().unwrap().non_enum_variant().fields.iter_enumerated()
283        {
284            let field_place = self.project_field(&place, field_idx)?;
285
286            match field.name {
287                // Write the `TypeId` of the array's elements to the `element_ty` field.
288                sym::element_ty => self.write_type_id(ty, &field_place)?,
289                // Write the length of the array to the `len` field.
290                sym::len => self.write_scalar(len.to_leaf(), &field_place)?,
291                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}"),
292            }
293        }
294
295        interp_ok(())
296    }
297
298    pub(crate) fn write_slice_type_info(
299        &mut self,
300        place: impl Writeable<'tcx, CtfeProvenance>,
301        ty: Ty<'tcx>,
302    ) -> InterpResult<'tcx> {
303        // Iterate over all fields of `type_info::Slice`.
304        for (field_idx, field) in
305            place.layout().ty.ty_adt_def().unwrap().non_enum_variant().fields.iter_enumerated()
306        {
307            let field_place = self.project_field(&place, field_idx)?;
308
309            match field.name {
310                // Write the `TypeId` of the slice's elements to the `element_ty` field.
311                sym::element_ty => self.write_type_id(ty, &field_place)?,
312                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}"),
313            }
314        }
315
316        interp_ok(())
317    }
318
319    fn write_int_type_info(
320        &mut self,
321        place: impl Writeable<'tcx, CtfeProvenance>,
322        bit_width: u64,
323        signed: bool,
324    ) -> InterpResult<'tcx> {
325        for (field_idx, field) in
326            place.layout().ty.ty_adt_def().unwrap().non_enum_variant().fields.iter_enumerated()
327        {
328            let field_place = self.project_field(&place, field_idx)?;
329            match field.name {
330                sym::bits => self.write_scalar(
331                    Scalar::from_u32(bit_width.try_into().expect("bit_width overflowed")),
332                    &field_place,
333                )?,
334                sym::signed => self.write_scalar(Scalar::from_bool(signed), &field_place)?,
335                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}"),
336            }
337        }
338        interp_ok(())
339    }
340
341    fn write_float_type_info(
342        &mut self,
343        place: impl Writeable<'tcx, CtfeProvenance>,
344        bit_width: u64,
345    ) -> InterpResult<'tcx> {
346        for (field_idx, field) in
347            place.layout().ty.ty_adt_def().unwrap().non_enum_variant().fields.iter_enumerated()
348        {
349            let field_place = self.project_field(&place, field_idx)?;
350            match field.name {
351                sym::bits => self.write_scalar(
352                    Scalar::from_u32(bit_width.try_into().expect("bit_width overflowed")),
353                    &field_place,
354                )?,
355                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}"),
356            }
357        }
358        interp_ok(())
359    }
360
361    pub(crate) fn write_reference_type_info(
362        &mut self,
363        place: impl Writeable<'tcx, CtfeProvenance>,
364        ty: Ty<'tcx>,
365        mutability: Mutability,
366    ) -> InterpResult<'tcx> {
367        // Iterate over all fields of `type_info::Reference`.
368        for (field_idx, field) in
369            place.layout().ty.ty_adt_def().unwrap().non_enum_variant().fields.iter_enumerated()
370        {
371            let field_place = self.project_field(&place, field_idx)?;
372
373            match field.name {
374                // Write the `TypeId` of the reference's inner type to the `ty` field.
375                sym::pointee => self.write_type_id(ty, &field_place)?,
376                // Write the boolean representing the reference's mutability to the `mutable` field.
377                sym::mutable => {
378                    self.write_scalar(Scalar::from_bool(mutability.is_mut()), &field_place)?
379                }
380                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}"),
381            }
382        }
383        interp_ok(())
384    }
385
386    pub(crate) fn write_fn_ptr_type_info(
387        &mut self,
388        place: impl Writeable<'tcx, CtfeProvenance>,
389        sig: &FnSigTys<TyCtxt<'tcx>>,
390        fn_header: &FnHeader<TyCtxt<'tcx>>,
391    ) -> InterpResult<'tcx> {
392        let FnHeader { fn_sig_kind } = fn_header;
393
394        for (field_idx, field) in
395            place.layout().ty.ty_adt_def().unwrap().non_enum_variant().fields.iter_enumerated()
396        {
397            let field_place = self.project_field(&place, field_idx)?;
398
399            match field.name {
400                sym::unsafety => {
401                    self.write_scalar(Scalar::from_bool(!fn_sig_kind.is_safe()), &field_place)?;
402                }
403                sym::abi => match fn_sig_kind.abi() {
404                    ExternAbi::C { .. } => {
405                        let (rust_variant, _rust_place) =
406                            self.project_downcast_named(&field_place, sym::ExternC)?;
407                        self.write_discriminant(rust_variant, &field_place)?;
408                    }
409                    ExternAbi::Rust => {
410                        let (rust_variant, _rust_place) =
411                            self.project_downcast_named(&field_place, sym::ExternRust)?;
412                        self.write_discriminant(rust_variant, &field_place)?;
413                    }
414                    other_abi => {
415                        let (variant, variant_place) =
416                            self.project_downcast_named(&field_place, sym::Named)?;
417                        let str_place = self.allocate_str_dedup(other_abi.as_str())?;
418                        let str_ref = self.mplace_to_imm_ptr(&str_place, None)?;
419                        let payload = self.project_field(&variant_place, FieldIdx::ZERO)?;
420                        self.write_immediate(*str_ref, &payload)?;
421                        self.write_discriminant(variant, &field_place)?;
422                    }
423                },
424                sym::inputs => {
425                    let inputs = sig.inputs();
426                    self.allocate_fill_and_write_slice_ptr(
427                        field_place,
428                        inputs.len() as _,
429                        |this, i, place| this.write_type_id(inputs[i as usize], &place),
430                    )?;
431                }
432                sym::output => {
433                    let output = sig.output();
434                    self.write_type_id(output, &field_place)?;
435                }
436                sym::variadic => {
437                    self.write_scalar(Scalar::from_bool(fn_sig_kind.c_variadic()), &field_place)?;
438                }
439                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}"),
440            }
441        }
442
443        interp_ok(())
444    }
445
446    pub(crate) fn write_pointer_type_info(
447        &mut self,
448        place: impl Writeable<'tcx, CtfeProvenance>,
449        ty: Ty<'tcx>,
450        mutability: Mutability,
451    ) -> InterpResult<'tcx> {
452        // Iterate over all fields of `type_info::Pointer`.
453        for (field_idx, field) in
454            place.layout().ty.ty_adt_def().unwrap().non_enum_variant().fields.iter_enumerated()
455        {
456            let field_place = self.project_field(&place, field_idx)?;
457
458            match field.name {
459                // Write the `TypeId` of the pointer's inner type to the `ty` field.
460                sym::pointee => self.write_type_id(ty, &field_place)?,
461                // Write the boolean representing the pointer's mutability to the `mutable` field.
462                sym::mutable => {
463                    self.write_scalar(Scalar::from_bool(mutability.is_mut()), &field_place)?
464                }
465                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}"),
466            }
467        }
468
469        interp_ok(())
470    }
471}