]>
Commit | Line | Data |
---|---|---|
ba9703b0 XL |
1 | use std::convert::TryFrom; |
2 | ||
136023e0 XL |
3 | use rustc_middle::mir::interpret::{InterpResult, Pointer, PointerArithmetic}; |
4 | use rustc_middle::ty::{ | |
5 | self, Ty, COMMON_VTABLE_ENTRIES, COMMON_VTABLE_ENTRIES_ALIGN, | |
6 | COMMON_VTABLE_ENTRIES_DROPINPLACE, COMMON_VTABLE_ENTRIES_SIZE, | |
7 | }; | |
8 | use rustc_target::abi::{Align, Size}; | |
60c5eb7d | 9 | |
3dfed10e | 10 | use super::util::ensure_monomorphic_enough; |
136023e0 | 11 | use super::{FnVal, InterpCx, Machine}; |
8faf50e0 | 12 | |
ba9703b0 | 13 | impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { |
ff7c6d11 XL |
14 | /// Creates a dynamic vtable for the given type and vtable origin. This is used only for |
15 | /// objects. | |
16 | /// | |
60c5eb7d | 17 | /// The `trait_ref` encodes the erased self type. Hence, if we are |
ff7c6d11 | 18 | /// making an object `Foo<Trait>` from a value of type `Foo<T>`, then |
60c5eb7d | 19 | /// `trait_ref` would map `T: Trait`. |
ff7c6d11 XL |
20 | pub fn get_vtable( |
21 | &mut self, | |
22 | ty: Ty<'tcx>, | |
0731742a | 23 | poly_trait_ref: Option<ty::PolyExistentialTraitRef<'tcx>>, |
94222f64 | 24 | ) -> InterpResult<'tcx, Pointer<Option<M::PointerTag>>> { |
0bf4aa26 XL |
25 | trace!("get_vtable(trait_ref={:?})", poly_trait_ref); |
26 | ||
fc512014 | 27 | let (ty, poly_trait_ref) = self.tcx.erase_regions((ty, poly_trait_ref)); |
0bf4aa26 | 28 | |
e1599b0c | 29 | // All vtables must be monomorphic, bail out otherwise. |
3dfed10e XL |
30 | ensure_monomorphic_enough(*self.tcx, ty)?; |
31 | ensure_monomorphic_enough(*self.tcx, poly_trait_ref)?; | |
e1599b0c | 32 | |
dc3f5686 | 33 | let vtable_allocation = self.tcx.vtable_allocation((ty, poly_trait_ref)); |
dc9dc135 | 34 | |
136023e0 | 35 | let vtable_ptr = self.memory.global_base_pointer(Pointer::from(vtable_allocation))?; |
17df50a5 | 36 | |
94222f64 | 37 | Ok(vtable_ptr.into()) |
ff7c6d11 XL |
38 | } |
39 | ||
60c5eb7d | 40 | /// Resolves the function at the specified slot in the provided |
136023e0 XL |
41 | /// vtable. Currently an index of '3' (`COMMON_VTABLE_ENTRIES.len()`) |
42 | /// corresponds to the first method declared in the trait of the provided vtable. | |
60c5eb7d XL |
43 | pub fn get_vtable_slot( |
44 | &self, | |
136023e0 | 45 | vtable: Pointer<Option<M::PointerTag>>, |
ba9703b0 | 46 | idx: u64, |
60c5eb7d XL |
47 | ) -> InterpResult<'tcx, FnVal<'tcx, M::ExtraFnVal>> { |
48 | let ptr_size = self.pointer_size(); | |
136023e0 | 49 | let vtable_slot = vtable.offset(ptr_size * idx, self)?; |
dfeec247 XL |
50 | let vtable_slot = self |
51 | .memory | |
17df50a5 | 52 | .get(vtable_slot, ptr_size, self.tcx.data_layout.pointer_align.abi)? |
dfeec247 | 53 | .expect("cannot be a ZST"); |
136023e0 | 54 | let fn_ptr = self.scalar_to_ptr(vtable_slot.read_ptr_sized(Size::ZERO)?.check_init()?); |
6a06907d | 55 | self.memory.get_fn(fn_ptr) |
60c5eb7d XL |
56 | } |
57 | ||
58 | /// Returns the drop fn instance as well as the actual dynamic type. | |
ff7c6d11 XL |
59 | pub fn read_drop_type_from_vtable( |
60 | &self, | |
136023e0 | 61 | vtable: Pointer<Option<M::PointerTag>>, |
dc9dc135 | 62 | ) -> InterpResult<'tcx, (ty::Instance<'tcx>, Ty<'tcx>)> { |
136023e0 | 63 | let pointer_size = self.pointer_size(); |
60c5eb7d | 64 | // We don't care about the pointee type; we just want a pointer. |
dfeec247 XL |
65 | let vtable = self |
66 | .memory | |
136023e0 XL |
67 | .get( |
68 | vtable, | |
69 | pointer_size * u64::try_from(COMMON_VTABLE_ENTRIES.len()).unwrap(), | |
70 | self.tcx.data_layout.pointer_align.abi, | |
71 | )? | |
dfeec247 | 72 | .expect("cannot be a ZST"); |
136023e0 XL |
73 | let drop_fn = vtable |
74 | .read_ptr_sized( | |
75 | pointer_size * u64::try_from(COMMON_VTABLE_ENTRIES_DROPINPLACE).unwrap(), | |
76 | )? | |
77 | .check_init()?; | |
416331ca XL |
78 | // We *need* an instance here, no other kind of function value, to be able |
79 | // to determine the type. | |
136023e0 | 80 | let drop_instance = self.memory.get_fn(self.scalar_to_ptr(drop_fn))?.as_instance()?; |
b7449926 | 81 | trace!("Found drop fn: {:?}", drop_instance); |
3dfed10e | 82 | let fn_sig = drop_instance.ty(*self.tcx, self.param_env).fn_sig(*self.tcx); |
fc512014 | 83 | let fn_sig = self.tcx.normalize_erasing_late_bound_regions(self.param_env, fn_sig); |
dc9dc135 | 84 | // The drop function takes `*mut T` where `T` is the type being dropped, so get that. |
60c5eb7d XL |
85 | let args = fn_sig.inputs(); |
86 | if args.len() != 1 { | |
136023e0 | 87 | throw_ub!(InvalidVtableDropFn(fn_sig)); |
60c5eb7d | 88 | } |
136023e0 XL |
89 | let ty = |
90 | args[0].builtin_deref(true).ok_or_else(|| err_ub!(InvalidVtableDropFn(fn_sig)))?.ty; | |
b7449926 | 91 | Ok((drop_instance, ty)) |
ff7c6d11 XL |
92 | } |
93 | ||
94 | pub fn read_size_and_align_from_vtable( | |
95 | &self, | |
136023e0 | 96 | vtable: Pointer<Option<M::PointerTag>>, |
dc9dc135 | 97 | ) -> InterpResult<'tcx, (Size, Align)> { |
b7449926 | 98 | let pointer_size = self.pointer_size(); |
60c5eb7d | 99 | // We check for `size = 3 * ptr_size`, which covers the drop fn (unused here), |
dc9dc135 | 100 | // the size, and the align (which we read below). |
dfeec247 XL |
101 | let vtable = self |
102 | .memory | |
136023e0 XL |
103 | .get( |
104 | vtable, | |
105 | pointer_size * u64::try_from(COMMON_VTABLE_ENTRIES.len()).unwrap(), | |
106 | self.tcx.data_layout.pointer_align.abi, | |
107 | )? | |
dfeec247 | 108 | .expect("cannot be a ZST"); |
136023e0 XL |
109 | let size = vtable |
110 | .read_ptr_sized(pointer_size * u64::try_from(COMMON_VTABLE_ENTRIES_SIZE).unwrap())? | |
111 | .check_init()?; | |
112 | let size = size.to_machine_usize(self)?; | |
113 | let align = vtable | |
114 | .read_ptr_sized(pointer_size * u64::try_from(COMMON_VTABLE_ENTRIES_ALIGN).unwrap())? | |
115 | .check_init()?; | |
116 | let align = align.to_machine_usize(self)?; | |
117 | let align = Align::from_bytes(align).map_err(|e| err_ub!(InvalidVtableAlignment(e)))?; | |
e1599b0c | 118 | |
f035d41b | 119 | if size >= self.tcx.data_layout.obj_size_bound() { |
136023e0 | 120 | throw_ub!(InvalidVtableSize); |
e1599b0c | 121 | } |
17df50a5 | 122 | Ok((Size::from_bytes(size), align)) |
ff7c6d11 | 123 | } |
94222f64 XL |
124 | |
125 | pub fn read_new_vtable_after_trait_upcasting_from_vtable( | |
126 | &self, | |
127 | vtable: Pointer<Option<M::PointerTag>>, | |
128 | idx: u64, | |
129 | ) -> InterpResult<'tcx, Pointer<Option<M::PointerTag>>> { | |
130 | let pointer_size = self.pointer_size(); | |
131 | ||
132 | let vtable_slot = vtable.offset(pointer_size * idx, self)?; | |
133 | let new_vtable = self | |
134 | .memory | |
135 | .get(vtable_slot, pointer_size, self.tcx.data_layout.pointer_align.abi)? | |
136 | .expect("cannot be a ZST"); | |
137 | ||
138 | let new_vtable = self.scalar_to_ptr(new_vtable.read_ptr_sized(Size::ZERO)?.check_init()?); | |
139 | ||
140 | Ok(new_vtable) | |
141 | } | |
ff7c6d11 | 142 | } |