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