]>
Commit | Line | Data |
---|---|---|
ea8adc8c XL |
1 | //! This module contains everything needed to instantiate an interpreter. |
2 | //! This separation exists to ensure that no fancy miri features like | |
3 | //! interpreting common C functions leak into CTFE. | |
4 | ||
0bf4aa26 XL |
5 | use std::borrow::{Borrow, Cow}; |
6 | use std::hash::Hash; | |
7 | ||
dc9dc135 | 8 | use rustc::hir::def_id::DefId; |
ff7c6d11 | 9 | use rustc::mir; |
416331ca | 10 | use rustc::ty::{self, TyCtxt}; |
b7449926 | 11 | |
0bf4aa26 | 12 | use super::{ |
dc9dc135 | 13 | Allocation, AllocId, InterpResult, Scalar, AllocationExtra, |
416331ca | 14 | InterpCx, PlaceTy, OpTy, ImmTy, MemoryKind, Pointer, Memory, |
0bf4aa26 | 15 | }; |
ea8adc8c | 16 | |
0bf4aa26 XL |
17 | /// Whether this kind of memory is allowed to leak |
18 | pub trait MayLeak: Copy { | |
19 | fn may_leak(self) -> bool; | |
20 | } | |
21 | ||
22 | /// The functionality needed by memory to manage its allocations | |
23 | pub trait AllocMap<K: Hash + Eq, V> { | |
9fa01778 | 24 | /// Tests if the map contains the given key. |
0bf4aa26 XL |
25 | /// Deliberately takes `&mut` because that is sufficient, and some implementations |
26 | /// can be more efficient then (using `RefCell::get_mut`). | |
27 | fn contains_key<Q: ?Sized + Hash + Eq>(&mut self, k: &Q) -> bool | |
28 | where K: Borrow<Q>; | |
29 | ||
9fa01778 | 30 | /// Inserts a new entry into the map. |
0bf4aa26 XL |
31 | fn insert(&mut self, k: K, v: V) -> Option<V>; |
32 | ||
9fa01778 | 33 | /// Removes an entry from the map. |
0bf4aa26 XL |
34 | fn remove<Q: ?Sized + Hash + Eq>(&mut self, k: &Q) -> Option<V> |
35 | where K: Borrow<Q>; | |
36 | ||
9fa01778 | 37 | /// Returns data based the keys and values in the map. |
0bf4aa26 | 38 | fn filter_map_collect<T>(&self, f: impl FnMut(&K, &V) -> Option<T>) -> Vec<T>; |
ea8adc8c | 39 | |
9fa01778 | 40 | /// Returns a reference to entry `k`. If no such entry exists, call |
0bf4aa26 XL |
41 | /// `vacant` and either forward its error, or add its result to the map |
42 | /// and return a reference to *that*. | |
43 | fn get_or<E>( | |
44 | &self, | |
45 | k: K, | |
46 | vacant: impl FnOnce() -> Result<V, E> | |
47 | ) -> Result<&V, E>; | |
48 | ||
9fa01778 | 49 | /// Returns a mutable reference to entry `k`. If no such entry exists, call |
0bf4aa26 XL |
50 | /// `vacant` and either forward its error, or add its result to the map |
51 | /// and return a reference to *that*. | |
52 | fn get_mut_or<E>( | |
53 | &mut self, | |
54 | k: K, | |
55 | vacant: impl FnOnce() -> Result<V, E> | |
56 | ) -> Result<&mut V, E>; | |
dc9dc135 XL |
57 | |
58 | /// Read-only lookup. | |
59 | fn get(&self, k: K) -> Option<&V> { | |
60 | self.get_or(k, || Err(())).ok() | |
61 | } | |
416331ca XL |
62 | |
63 | /// Mutable lookup. | |
64 | fn get_mut(&mut self, k: K) -> Option<&mut V> { | |
65 | self.get_mut_or(k, || Err(())).ok() | |
66 | } | |
0bf4aa26 XL |
67 | } |
68 | ||
69 | /// Methods of this trait signifies a point where CTFE evaluation would fail | |
70 | /// and some use case dependent behaviour can instead be applied. | |
dc9dc135 | 71 | pub trait Machine<'mir, 'tcx>: Sized { |
ea8adc8c | 72 | /// Additional memory kinds a machine wishes to distinguish from the builtin ones |
0bf4aa26 XL |
73 | type MemoryKinds: ::std::fmt::Debug + MayLeak + Eq + 'static; |
74 | ||
9fa01778 | 75 | /// Tag tracked alongside every pointer. This is used to implement "Stacked Borrows" |
0bf4aa26 | 76 | /// <https://www.ralfj.de/blog/2018/08/07/stacked-borrows.html>. |
a1dfa0c6 | 77 | /// The `default()` is used for pointers to consts, statics, vtables and functions. |
dc9dc135 | 78 | type PointerTag: ::std::fmt::Debug + Copy + Eq + Hash + 'static; |
0bf4aa26 | 79 | |
416331ca XL |
80 | /// Machines can define extra (non-instance) things that represent values of function pointers. |
81 | /// For example, Miri uses this to return a fucntion pointer from `dlsym` | |
82 | /// that can later be called to execute the right thing. | |
83 | type ExtraFnVal: ::std::fmt::Debug + Copy; | |
84 | ||
a1dfa0c6 XL |
85 | /// Extra data stored in every call frame. |
86 | type FrameExtra; | |
87 | ||
9fa01778 | 88 | /// Extra data stored in memory. A reference to this is available when `AllocExtra` |
0731742a | 89 | /// gets initialized, so you can e.g., have an `Rc` here if there is global state you |
a1dfa0c6 | 90 | /// need access to in the `AllocExtra` hooks. |
416331ca | 91 | type MemoryExtra; |
a1dfa0c6 | 92 | |
0bf4aa26 | 93 | /// Extra data stored in every allocation. |
48663c56 | 94 | type AllocExtra: AllocationExtra<Self::PointerTag> + 'static; |
0bf4aa26 XL |
95 | |
96 | /// Memory's allocation map | |
97 | type MemoryMap: | |
98 | AllocMap< | |
99 | AllocId, | |
100 | (MemoryKind<Self::MemoryKinds>, Allocation<Self::PointerTag, Self::AllocExtra>) | |
101 | > + | |
102 | Default + | |
103 | Clone; | |
104 | ||
a1dfa0c6 XL |
105 | /// The memory kind to use for copied statics -- or None if statics should not be mutated |
106 | /// and thus any such attempt will cause a `ModifiedStatic` error to be raised. | |
0bf4aa26 | 107 | /// Statics are copied under two circumstances: When they are mutated, and when |
dc9dc135 | 108 | /// `tag_allocation` or `find_foreign_static` (see below) returns an owned allocation |
0bf4aa26 XL |
109 | /// that is added to the memory so that the work is not done twice. |
110 | const STATIC_KIND: Option<Self::MemoryKinds>; | |
111 | ||
416331ca XL |
112 | /// Whether memory accesses should be alignment-checked. |
113 | const CHECK_ALIGN: bool; | |
114 | ||
0bf4aa26 | 115 | /// Whether to enforce the validity invariant |
416331ca | 116 | fn enforce_validity(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool; |
b7449926 | 117 | |
0bf4aa26 XL |
118 | /// Called before a basic block terminator is executed. |
119 | /// You can use this to detect endlessly running programs. | |
416331ca | 120 | fn before_terminator(ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx>; |
ea8adc8c XL |
121 | |
122 | /// Entry point to all function calls. | |
123 | /// | |
b7449926 XL |
124 | /// Returns either the mir to use for the call, or `None` if execution should |
125 | /// just proceed (which usually means this hook did all the work that the | |
9fa01778 | 126 | /// called function should usually have done). In the latter case, it is |
b7449926 XL |
127 | /// this hook's responsibility to call `goto_block(ret)` to advance the instruction pointer! |
128 | /// (This is to support functions like `__rust_maybe_catch_panic` that neither find a MIR | |
129 | /// nor just jump to `ret`, but instead push their own stack frame.) | |
130 | /// Passing `dest`and `ret` in the same `Option` proved very annoying when only one of them | |
131 | /// was used. | |
0bf4aa26 | 132 | fn find_fn( |
416331ca | 133 | ecx: &mut InterpCx<'mir, 'tcx, Self>, |
ea8adc8c | 134 | instance: ty::Instance<'tcx>, |
0bf4aa26 XL |
135 | args: &[OpTy<'tcx, Self::PointerTag>], |
136 | dest: Option<PlaceTy<'tcx, Self::PointerTag>>, | |
b7449926 | 137 | ret: Option<mir::BasicBlock>, |
dc9dc135 | 138 | ) -> InterpResult<'tcx, Option<&'mir mir::Body<'tcx>>>; |
ea8adc8c | 139 | |
416331ca XL |
140 | /// Execute `fn_val`. it is the hook's responsibility to advance the instruction |
141 | /// pointer as appropriate. | |
142 | fn call_extra_fn( | |
143 | ecx: &mut InterpCx<'mir, 'tcx, Self>, | |
144 | fn_val: Self::ExtraFnVal, | |
145 | args: &[OpTy<'tcx, Self::PointerTag>], | |
146 | dest: Option<PlaceTy<'tcx, Self::PointerTag>>, | |
147 | ret: Option<mir::BasicBlock>, | |
148 | ) -> InterpResult<'tcx>; | |
149 | ||
b7449926 XL |
150 | /// Directly process an intrinsic without pushing a stack frame. |
151 | /// If this returns successfully, the engine will take care of jumping to the next block. | |
0bf4aa26 | 152 | fn call_intrinsic( |
416331ca | 153 | ecx: &mut InterpCx<'mir, 'tcx, Self>, |
ea8adc8c | 154 | instance: ty::Instance<'tcx>, |
0bf4aa26 XL |
155 | args: &[OpTy<'tcx, Self::PointerTag>], |
156 | dest: PlaceTy<'tcx, Self::PointerTag>, | |
dc9dc135 | 157 | ) -> InterpResult<'tcx>; |
ea8adc8c | 158 | |
b7449926 | 159 | /// Called for read access to a foreign static item. |
0bf4aa26 XL |
160 | /// |
161 | /// This will only be called once per static and machine; the result is cached in | |
162 | /// the machine memory. (This relies on `AllocMap::get_or` being able to add the | |
163 | /// owned allocation to the map even when the map is shared.) | |
dc9dc135 XL |
164 | /// |
165 | /// This allocation will then be fed to `tag_allocation` to initialize the "extra" state. | |
0bf4aa26 | 166 | fn find_foreign_static( |
416331ca | 167 | tcx: TyCtxt<'tcx>, |
b7449926 | 168 | def_id: DefId, |
dc9dc135 | 169 | ) -> InterpResult<'tcx, Cow<'tcx, Allocation>>; |
0bf4aa26 | 170 | |
416331ca | 171 | /// Called for all binary operations where the LHS has pointer type. |
ea8adc8c XL |
172 | /// |
173 | /// Returns a (value, overflowed) pair if the operation succeeded | |
416331ca XL |
174 | fn binary_ptr_op( |
175 | ecx: &InterpCx<'mir, 'tcx, Self>, | |
ea8adc8c | 176 | bin_op: mir::BinOp, |
9fa01778 XL |
177 | left: ImmTy<'tcx, Self::PointerTag>, |
178 | right: ImmTy<'tcx, Self::PointerTag>, | |
dc9dc135 | 179 | ) -> InterpResult<'tcx, (Scalar<Self::PointerTag>, bool)>; |
ea8adc8c | 180 | |
0bf4aa26 XL |
181 | /// Heap allocations via the `box` keyword. |
182 | fn box_alloc( | |
416331ca | 183 | ecx: &mut InterpCx<'mir, 'tcx, Self>, |
0bf4aa26 | 184 | dest: PlaceTy<'tcx, Self::PointerTag>, |
dc9dc135 | 185 | ) -> InterpResult<'tcx>; |
ff7c6d11 | 186 | |
dc9dc135 XL |
187 | /// Called to initialize the "extra" state of an allocation and make the pointers |
188 | /// it contains (in relocations) tagged. The way we construct allocations is | |
189 | /// to always first construct it without extra and then add the extra. | |
190 | /// This keeps uniform code paths for handling both allocations created by CTFE | |
191 | /// for statics, and allocations ceated by Miri during evaluation. | |
192 | /// | |
193 | /// `kind` is the kind of the allocation being tagged; it can be `None` when | |
194 | /// it's a static and `STATIC_KIND` is `None`. | |
48663c56 XL |
195 | /// |
196 | /// This should avoid copying if no work has to be done! If this returns an owned | |
197 | /// allocation (because a copy had to be done to add tags or metadata), machine memory will | |
198 | /// cache the result. (This relies on `AllocMap::get_or` being able to add the | |
199 | /// owned allocation to the map even when the map is shared.) | |
dc9dc135 XL |
200 | /// |
201 | /// For static allocations, the tag returned must be the same as the one returned by | |
202 | /// `tag_static_base_pointer`. | |
203 | fn tag_allocation<'b>( | |
416331ca | 204 | memory_extra: &Self::MemoryExtra, |
dc9dc135 XL |
205 | id: AllocId, |
206 | alloc: Cow<'b, Allocation>, | |
207 | kind: Option<MemoryKind<Self::MemoryKinds>>, | |
dc9dc135 XL |
208 | ) -> (Cow<'b, Allocation<Self::PointerTag, Self::AllocExtra>>, Self::PointerTag); |
209 | ||
210 | /// Return the "base" tag for the given static allocation: the one that is used for direct | |
211 | /// accesses to this static/const/fn allocation. | |
212 | /// | |
213 | /// Be aware that requesting the `Allocation` for that `id` will lead to cycles | |
214 | /// for cyclic statics! | |
215 | fn tag_static_base_pointer( | |
416331ca | 216 | memory_extra: &Self::MemoryExtra, |
dc9dc135 | 217 | id: AllocId, |
dc9dc135 | 218 | ) -> Self::PointerTag; |
0bf4aa26 | 219 | |
9fa01778 | 220 | /// Executes a retagging operation |
0bf4aa26 | 221 | #[inline] |
a1dfa0c6 | 222 | fn retag( |
416331ca | 223 | _ecx: &mut InterpCx<'mir, 'tcx, Self>, |
0731742a | 224 | _kind: mir::RetagKind, |
a1dfa0c6 | 225 | _place: PlaceTy<'tcx, Self::PointerTag>, |
dc9dc135 | 226 | ) -> InterpResult<'tcx> { |
0bf4aa26 XL |
227 | Ok(()) |
228 | } | |
229 | ||
a1dfa0c6 | 230 | /// Called immediately before a new stack frame got pushed |
416331ca | 231 | fn stack_push(ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx, Self::FrameExtra>; |
a1dfa0c6 XL |
232 | |
233 | /// Called immediately after a stack frame gets popped | |
234 | fn stack_pop( | |
416331ca | 235 | ecx: &mut InterpCx<'mir, 'tcx, Self>, |
a1dfa0c6 | 236 | extra: Self::FrameExtra, |
dc9dc135 XL |
237 | ) -> InterpResult<'tcx>; |
238 | ||
239 | fn int_to_ptr( | |
dc9dc135 | 240 | _mem: &Memory<'mir, 'tcx, Self>, |
416331ca | 241 | int: u64, |
dc9dc135 | 242 | ) -> InterpResult<'tcx, Pointer<Self::PointerTag>> { |
416331ca XL |
243 | Err((if int == 0 { |
244 | err_unsup!(InvalidNullPointerUsage) | |
dc9dc135 | 245 | } else { |
416331ca XL |
246 | err_unsup!(ReadBytesAsPointer) |
247 | }).into()) | |
dc9dc135 XL |
248 | } |
249 | ||
250 | fn ptr_to_int( | |
dc9dc135 | 251 | _mem: &Memory<'mir, 'tcx, Self>, |
416331ca XL |
252 | _ptr: Pointer<Self::PointerTag>, |
253 | ) -> InterpResult<'tcx, u64>; | |
ea8adc8c | 254 | } |