1 use std
::convert
::TryFrom
;
3 use rustc_middle
::mir
::interpret
::{InterpResult, Pointer, PointerArithmetic, Scalar}
;
4 use rustc_middle
::ty
::{self, Instance, Ty}
;
5 use rustc_target
::abi
::{Align, LayoutOf, Size}
;
7 use super::util
::ensure_monomorphic_enough
;
8 use super::{FnVal, InterpCx, Machine, MemoryKind}
;
10 impl<'mir
, 'tcx
: 'mir
, M
: Machine
<'mir
, 'tcx
>> InterpCx
<'mir
, 'tcx
, M
> {
11 /// Creates a dynamic vtable for the given type and vtable origin. This is used only for
14 /// The `trait_ref` encodes the erased self type. Hence, if we are
15 /// making an object `Foo<Trait>` from a value of type `Foo<T>`, then
16 /// `trait_ref` would map `T: Trait`.
20 poly_trait_ref
: Option
<ty
::PolyExistentialTraitRef
<'tcx
>>,
21 ) -> InterpResult
<'tcx
, Pointer
<M
::PointerTag
>> {
22 trace
!("get_vtable(trait_ref={:?})", poly_trait_ref
);
24 let (ty
, poly_trait_ref
) = self.tcx
.erase_regions(&(ty
, poly_trait_ref
));
26 // All vtables must be monomorphic, bail out otherwise.
27 ensure_monomorphic_enough(*self.tcx
, ty
)?
;
28 ensure_monomorphic_enough(*self.tcx
, poly_trait_ref
)?
;
30 if let Some(&vtable
) = self.vtables
.get(&(ty
, poly_trait_ref
)) {
31 // This means we guarantee that there are no duplicate vtables, we will
32 // always use the same vtable for the same (Type, Trait) combination.
33 // That's not what happens in rustc, but emulating per-crate deduplication
34 // does not sound like it actually makes anything any better.
38 let methods
= if let Some(poly_trait_ref
) = poly_trait_ref
{
39 let trait_ref
= poly_trait_ref
.with_self_ty(*self.tcx
, ty
);
40 let trait_ref
= self.tcx
.erase_regions(&trait_ref
);
42 self.tcx
.vtable_methods(trait_ref
)
47 let layout
= self.layout_of(ty
)?
;
48 assert
!(!layout
.is_unsized(), "can't create a vtable for an unsized type");
49 let size
= layout
.size
.bytes();
50 let align
= layout
.align
.abi
.bytes();
53 let ptr_size
= self.pointer_size();
54 let ptr_align
= tcx
.data_layout
.pointer_align
.abi
;
55 // /////////////////////////////////////////////////////////////////////////////////////////
56 // If you touch this code, be sure to also make the corresponding changes to
57 // `get_vtable` in `rust_codegen_llvm/meth.rs`.
58 // /////////////////////////////////////////////////////////////////////////////////////////
59 let vtable
= self.memory
.allocate(
60 ptr_size
* u64::try_from(methods
.len()).unwrap().checked_add(3).unwrap(),
65 let drop
= Instance
::resolve_drop_in_place(tcx
, ty
);
66 let drop
= self.memory
.create_fn_alloc(FnVal
::Instance(drop
));
68 // No need to do any alignment checks on the memory accesses below, because we know the
69 // allocation is correctly aligned as we created it above. Also we're only offsetting by
70 // multiples of `ptr_align`, which means that it will stay aligned to `ptr_align`.
71 let vtable_alloc
= self.memory
.get_raw_mut(vtable
.alloc_id
)?
;
72 vtable_alloc
.write_ptr_sized(&tcx
, vtable
, drop
.into())?
;
74 let size_ptr
= vtable
.offset(ptr_size
, &tcx
)?
;
75 vtable_alloc
.write_ptr_sized(&tcx
, size_ptr
, Scalar
::from_uint(size
, ptr_size
).into())?
;
76 let align_ptr
= vtable
.offset(ptr_size
* 2, &tcx
)?
;
77 vtable_alloc
.write_ptr_sized(&tcx
, align_ptr
, Scalar
::from_uint(align
, ptr_size
).into())?
;
79 for (i
, method
) in methods
.iter().enumerate() {
80 if let Some((def_id
, substs
)) = *method
{
81 // resolve for vtable: insert shims where needed
83 ty
::Instance
::resolve_for_vtable(tcx
, self.param_env
, def_id
, substs
)
84 .ok_or_else(|| err_inval
!(TooGeneric
))?
;
85 let fn_ptr
= self.memory
.create_fn_alloc(FnVal
::Instance(instance
));
86 // We cannot use `vtable_allic` as we are creating fn ptrs in this loop.
87 let method_ptr
= vtable
.offset(ptr_size
* (3 + i
as u64), &tcx
)?
;
88 self.memory
.get_raw_mut(vtable
.alloc_id
)?
.write_ptr_sized(
96 self.memory
.mark_immutable(vtable
.alloc_id
)?
;
97 assert
!(self.vtables
.insert((ty
, poly_trait_ref
), vtable
).is_none());
102 /// Resolves the function at the specified slot in the provided
103 /// vtable. An index of '0' corresponds to the first method
104 /// declared in the trait of the provided vtable.
105 pub fn get_vtable_slot(
107 vtable
: Scalar
<M
::PointerTag
>,
109 ) -> InterpResult
<'tcx
, FnVal
<'tcx
, M
::ExtraFnVal
>> {
110 let ptr_size
= self.pointer_size();
111 // Skip over the 'drop_ptr', 'size', and 'align' fields.
112 let vtable_slot
= vtable
.ptr_offset(ptr_size
* idx
.checked_add(3).unwrap(), self)?
;
113 let vtable_slot
= self
115 .check_ptr_access(vtable_slot
, ptr_size
, self.tcx
.data_layout
.pointer_align
.abi
)?
116 .expect("cannot be a ZST");
119 .get_raw(vtable_slot
.alloc_id
)?
120 .read_ptr_sized(self, vtable_slot
)?
122 Ok(self.memory
.get_fn(fn_ptr
)?
)
125 /// Returns the drop fn instance as well as the actual dynamic type.
126 pub fn read_drop_type_from_vtable(
128 vtable
: Scalar
<M
::PointerTag
>,
129 ) -> InterpResult
<'tcx
, (ty
::Instance
<'tcx
>, Ty
<'tcx
>)> {
130 // We don't care about the pointee type; we just want a pointer.
135 self.tcx
.data_layout
.pointer_size
,
136 self.tcx
.data_layout
.pointer_align
.abi
,
138 .expect("cannot be a ZST");
140 self.memory
.get_raw(vtable
.alloc_id
)?
.read_ptr_sized(self, vtable
)?
.check_init()?
;
141 // We *need* an instance here, no other kind of function value, to be able
142 // to determine the type.
143 let drop_instance
= self.memory
.get_fn(drop_fn
)?
.as_instance()?
;
144 trace
!("Found drop fn: {:?}", drop_instance
);
145 let fn_sig
= drop_instance
.ty(*self.tcx
, self.param_env
).fn_sig(*self.tcx
);
146 let fn_sig
= self.tcx
.normalize_erasing_late_bound_regions(self.param_env
, &fn_sig
);
147 // The drop function takes `*mut T` where `T` is the type being dropped, so get that.
148 let args
= fn_sig
.inputs();
150 throw_ub
!(InvalidDropFn(fn_sig
));
152 let ty
= args
[0].builtin_deref(true).ok_or_else(|| err_ub
!(InvalidDropFn(fn_sig
)))?
.ty
;
153 Ok((drop_instance
, ty
))
156 pub fn read_size_and_align_from_vtable(
158 vtable
: Scalar
<M
::PointerTag
>,
159 ) -> InterpResult
<'tcx
, (Size
, Align
)> {
160 let pointer_size
= self.pointer_size();
161 // We check for `size = 3 * ptr_size`, which covers the drop fn (unused here),
162 // the size, and the align (which we read below).
165 .check_ptr_access(vtable
, 3 * pointer_size
, self.tcx
.data_layout
.pointer_align
.abi
)?
166 .expect("cannot be a ZST");
167 let alloc
= self.memory
.get_raw(vtable
.alloc_id
)?
;
168 let size
= alloc
.read_ptr_sized(self, vtable
.offset(pointer_size
, self)?
)?
.check_init()?
;
169 let size
= u64::try_from(self.force_bits(size
, pointer_size
)?
).unwrap();
171 alloc
.read_ptr_sized(self, vtable
.offset(pointer_size
* 2, self)?
)?
.check_init()?
;
172 let align
= u64::try_from(self.force_bits(align
, pointer_size
)?
).unwrap();
174 if size
>= self.tcx
.data_layout
.obj_size_bound() {
177 size is bigger than largest supported object"
180 Ok((Size
::from_bytes(size
), Align
::from_bytes(align
).unwrap()))