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