1 use std
::convert
::TryFrom
;
4 use crate::mir
::interpret
::{alloc_range, AllocId, Allocation, Pointer, Scalar, ScalarMaybeUninit}
;
5 use crate::ty
::{self, Instance, PolyTraitRef, Ty, TyCtxt}
;
6 use rustc_ast
::Mutability
;
8 #[derive(Clone, Copy, PartialEq, HashStable)]
9 pub enum VtblEntry
<'tcx
> {
10 /// destructor of this type (used in vtable header)
12 /// layout size of this type (used in vtable header)
14 /// layout align of this type (used in vtable header)
16 /// non-dispatchable associated function that is excluded from trait object
18 /// dispatchable associated function
19 Method(Instance
<'tcx
>),
20 /// pointer to a separate supertrait vtable, can be used by trait upcasting coercion
21 TraitVPtr(PolyTraitRef
<'tcx
>),
24 impl<'tcx
> fmt
::Debug
for VtblEntry
<'tcx
> {
25 fn fmt(&self, f
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
26 // We want to call `Display` on `Instance` and `PolyTraitRef`,
27 // so we implement this manually.
29 VtblEntry
::MetadataDropInPlace
=> write
!(f
, "MetadataDropInPlace"),
30 VtblEntry
::MetadataSize
=> write
!(f
, "MetadataSize"),
31 VtblEntry
::MetadataAlign
=> write
!(f
, "MetadataAlign"),
32 VtblEntry
::Vacant
=> write
!(f
, "Vacant"),
33 VtblEntry
::Method(instance
) => write
!(f
, "Method({})", instance
),
34 VtblEntry
::TraitVPtr(trait_ref
) => write
!(f
, "TraitVPtr({})", trait_ref
),
39 pub const COMMON_VTABLE_ENTRIES
: &[VtblEntry
<'_
>] =
40 &[VtblEntry
::MetadataDropInPlace
, VtblEntry
::MetadataSize
, VtblEntry
::MetadataAlign
];
42 pub const COMMON_VTABLE_ENTRIES_DROPINPLACE
: usize = 0;
43 pub const COMMON_VTABLE_ENTRIES_SIZE
: usize = 1;
44 pub const COMMON_VTABLE_ENTRIES_ALIGN
: usize = 2;
46 impl<'tcx
> TyCtxt
<'tcx
> {
47 /// Retrieves an allocation that represents the contents of a vtable.
48 /// There's a cache within `TyCtxt` so it will be deduplicated.
49 pub fn vtable_allocation(
52 poly_trait_ref
: Option
<ty
::PolyExistentialTraitRef
<'tcx
>>,
55 let vtables_cache
= tcx
.vtables_cache
.lock();
56 if let Some(alloc_id
) = vtables_cache
.get(&(ty
, poly_trait_ref
)).cloned() {
61 let vtable_entries
= if let Some(poly_trait_ref
) = poly_trait_ref
{
62 let trait_ref
= poly_trait_ref
.with_self_ty(tcx
, ty
);
63 let trait_ref
= tcx
.erase_regions(trait_ref
);
65 tcx
.vtable_entries(trait_ref
)
71 .layout_of(ty
::ParamEnv
::reveal_all().and(ty
))
72 .expect("failed to build vtable representation");
73 assert
!(!layout
.is_unsized(), "can't create a vtable for an unsized type");
74 let size
= layout
.size
.bytes();
75 let align
= layout
.align
.abi
.bytes();
77 let ptr_size
= tcx
.data_layout
.pointer_size
;
78 let ptr_align
= tcx
.data_layout
.pointer_align
.abi
;
80 let vtable_size
= ptr_size
* u64::try_from(vtable_entries
.len()).unwrap();
82 Allocation
::uninit(vtable_size
, ptr_align
, /* panic_on_fail */ true).unwrap();
84 // No need to do any alignment checks on the memory accesses below, because we know the
85 // allocation is correctly aligned as we created it above. Also we're only offsetting by
86 // multiples of `ptr_align`, which means that it will stay aligned to `ptr_align`.
88 for (idx
, entry
) in vtable_entries
.iter().enumerate() {
89 let idx
: u64 = u64::try_from(idx
).unwrap();
90 let scalar
= match entry
{
91 VtblEntry
::MetadataDropInPlace
=> {
92 let instance
= ty
::Instance
::resolve_drop_in_place(tcx
, ty
);
93 let fn_alloc_id
= tcx
.create_fn_alloc(instance
);
94 let fn_ptr
= Pointer
::from(fn_alloc_id
);
95 ScalarMaybeUninit
::from_pointer(fn_ptr
, &tcx
)
97 VtblEntry
::MetadataSize
=> Scalar
::from_uint(size
, ptr_size
).into(),
98 VtblEntry
::MetadataAlign
=> Scalar
::from_uint(align
, ptr_size
).into(),
99 VtblEntry
::Vacant
=> continue,
100 VtblEntry
::Method(instance
) => {
101 // Prepare the fn ptr we write into the vtable.
102 let instance
= instance
.polymorphize(tcx
);
103 let fn_alloc_id
= tcx
.create_fn_alloc(instance
);
104 let fn_ptr
= Pointer
::from(fn_alloc_id
);
105 ScalarMaybeUninit
::from_pointer(fn_ptr
, &tcx
)
107 VtblEntry
::TraitVPtr(trait_ref
) => {
108 let super_trait_ref
= trait_ref
.map_bound(|trait_ref
| {
109 ty
::ExistentialTraitRef
::erase_self_ty(tcx
, trait_ref
)
111 let supertrait_alloc_id
= self.vtable_allocation(ty
, Some(super_trait_ref
));
112 let vptr
= Pointer
::from(supertrait_alloc_id
);
113 ScalarMaybeUninit
::from_pointer(vptr
, &tcx
)
117 .write_scalar(&tcx
, alloc_range(ptr_size
* idx
, ptr_size
), scalar
)
118 .expect("failed to build vtable representation");
121 vtable
.mutability
= Mutability
::Not
;
122 let alloc_id
= tcx
.create_memory_alloc(tcx
.intern_const_alloc(vtable
));
123 let mut vtables_cache
= self.vtables_cache
.lock();
124 vtables_cache
.insert((ty
, poly_trait_ref
), alloc_id
);