]>
Commit | Line | Data |
---|---|---|
94222f64 | 1 | use std::fmt; |
136023e0 | 2 | |
f2b60f7d | 3 | use crate::mir::interpret::{alloc_range, AllocId, Allocation, Pointer, Scalar}; |
94222f64 | 4 | use crate::ty::{self, Instance, PolyTraitRef, Ty, TyCtxt}; |
136023e0 XL |
5 | use rustc_ast::Mutability; |
6 | ||
94222f64 | 7 | #[derive(Clone, Copy, PartialEq, HashStable)] |
136023e0 | 8 | pub enum VtblEntry<'tcx> { |
94222f64 | 9 | /// destructor of this type (used in vtable header) |
136023e0 | 10 | MetadataDropInPlace, |
94222f64 | 11 | /// layout size of this type (used in vtable header) |
136023e0 | 12 | MetadataSize, |
94222f64 | 13 | /// layout align of this type (used in vtable header) |
136023e0 | 14 | MetadataAlign, |
94222f64 | 15 | /// non-dispatchable associated function that is excluded from trait object |
136023e0 | 16 | Vacant, |
94222f64 XL |
17 | /// dispatchable associated function |
18 | Method(Instance<'tcx>), | |
19 | /// pointer to a separate supertrait vtable, can be used by trait upcasting coercion | |
20 | TraitVPtr(PolyTraitRef<'tcx>), | |
21 | } | |
22 | ||
23 | impl<'tcx> fmt::Debug for VtblEntry<'tcx> { | |
24 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
25 | // We want to call `Display` on `Instance` and `PolyTraitRef`, | |
26 | // so we implement this manually. | |
27 | match self { | |
28 | VtblEntry::MetadataDropInPlace => write!(f, "MetadataDropInPlace"), | |
29 | VtblEntry::MetadataSize => write!(f, "MetadataSize"), | |
30 | VtblEntry::MetadataAlign => write!(f, "MetadataAlign"), | |
31 | VtblEntry::Vacant => write!(f, "Vacant"), | |
32 | VtblEntry::Method(instance) => write!(f, "Method({})", instance), | |
33 | VtblEntry::TraitVPtr(trait_ref) => write!(f, "TraitVPtr({})", trait_ref), | |
34 | } | |
35 | } | |
136023e0 XL |
36 | } |
37 | ||
923072b8 FG |
38 | // Needs to be associated with the `'tcx` lifetime |
39 | impl<'tcx> TyCtxt<'tcx> { | |
40 | pub const COMMON_VTABLE_ENTRIES: &'tcx [VtblEntry<'tcx>] = | |
41 | &[VtblEntry::MetadataDropInPlace, VtblEntry::MetadataSize, VtblEntry::MetadataAlign]; | |
42 | } | |
136023e0 XL |
43 | |
44 | pub const COMMON_VTABLE_ENTRIES_DROPINPLACE: usize = 0; | |
45 | pub const COMMON_VTABLE_ENTRIES_SIZE: usize = 1; | |
46 | pub const COMMON_VTABLE_ENTRIES_ALIGN: usize = 2; | |
47 | ||
dc3f5686 XL |
48 | /// Retrieves an allocation that represents the contents of a vtable. |
49 | /// Since this is a query, allocations are cached and not duplicated. | |
50 | pub(super) fn vtable_allocation_provider<'tcx>( | |
51 | tcx: TyCtxt<'tcx>, | |
52 | key: (Ty<'tcx>, Option<ty::PolyExistentialTraitRef<'tcx>>), | |
53 | ) -> AllocId { | |
54 | let (ty, poly_trait_ref) = key; | |
136023e0 | 55 | |
dc3f5686 XL |
56 | let vtable_entries = if let Some(poly_trait_ref) = poly_trait_ref { |
57 | let trait_ref = poly_trait_ref.with_self_ty(tcx, ty); | |
58 | let trait_ref = tcx.erase_regions(trait_ref); | |
136023e0 | 59 | |
dc3f5686 XL |
60 | tcx.vtable_entries(trait_ref) |
61 | } else { | |
923072b8 | 62 | TyCtxt::COMMON_VTABLE_ENTRIES |
dc3f5686 | 63 | }; |
136023e0 | 64 | |
dc3f5686 XL |
65 | let layout = tcx |
66 | .layout_of(ty::ParamEnv::reveal_all().and(ty)) | |
67 | .expect("failed to build vtable representation"); | |
487cf647 | 68 | assert!(layout.is_sized(), "can't create a vtable for an unsized type"); |
dc3f5686 XL |
69 | let size = layout.size.bytes(); |
70 | let align = layout.align.abi.bytes(); | |
136023e0 | 71 | |
dc3f5686 XL |
72 | let ptr_size = tcx.data_layout.pointer_size; |
73 | let ptr_align = tcx.data_layout.pointer_align.abi; | |
136023e0 | 74 | |
dc3f5686 XL |
75 | let vtable_size = ptr_size * u64::try_from(vtable_entries.len()).unwrap(); |
76 | let mut vtable = Allocation::uninit(vtable_size, ptr_align, /* panic_on_fail */ true).unwrap(); | |
136023e0 | 77 | |
dc3f5686 XL |
78 | // No need to do any alignment checks on the memory accesses below, because we know the |
79 | // allocation is correctly aligned as we created it above. Also we're only offsetting by | |
80 | // multiples of `ptr_align`, which means that it will stay aligned to `ptr_align`. | |
136023e0 | 81 | |
dc3f5686 XL |
82 | for (idx, entry) in vtable_entries.iter().enumerate() { |
83 | let idx: u64 = u64::try_from(idx).unwrap(); | |
84 | let scalar = match entry { | |
85 | VtblEntry::MetadataDropInPlace => { | |
86 | let instance = ty::Instance::resolve_drop_in_place(tcx, ty); | |
87 | let fn_alloc_id = tcx.create_fn_alloc(instance); | |
88 | let fn_ptr = Pointer::from(fn_alloc_id); | |
f2b60f7d | 89 | Scalar::from_pointer(fn_ptr, &tcx) |
dc3f5686 | 90 | } |
9c376795 FG |
91 | VtblEntry::MetadataSize => Scalar::from_uint(size, ptr_size), |
92 | VtblEntry::MetadataAlign => Scalar::from_uint(align, ptr_size), | |
dc3f5686 XL |
93 | VtblEntry::Vacant => continue, |
94 | VtblEntry::Method(instance) => { | |
95 | // Prepare the fn ptr we write into the vtable. | |
96 | let instance = instance.polymorphize(tcx); | |
97 | let fn_alloc_id = tcx.create_fn_alloc(instance); | |
98 | let fn_ptr = Pointer::from(fn_alloc_id); | |
f2b60f7d | 99 | Scalar::from_pointer(fn_ptr, &tcx) |
dc3f5686 XL |
100 | } |
101 | VtblEntry::TraitVPtr(trait_ref) => { | |
102 | let super_trait_ref = trait_ref | |
103 | .map_bound(|trait_ref| ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref)); | |
104 | let supertrait_alloc_id = tcx.vtable_allocation((ty, Some(super_trait_ref))); | |
105 | let vptr = Pointer::from(supertrait_alloc_id); | |
f2b60f7d | 106 | Scalar::from_pointer(vptr, &tcx) |
dc3f5686 XL |
107 | } |
108 | }; | |
109 | vtable | |
110 | .write_scalar(&tcx, alloc_range(ptr_size * idx, ptr_size), scalar) | |
111 | .expect("failed to build vtable representation"); | |
136023e0 | 112 | } |
dc3f5686 XL |
113 | |
114 | vtable.mutability = Mutability::Not; | |
115 | tcx.create_memory_alloc(tcx.intern_const_alloc(vtable)) | |
136023e0 | 116 | } |