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.
5 use std::borrow::{Borrow, Cow};
9 use rustc::ty::{self, Ty, TyCtxt};
10 use rustc_hir::def_id::DefId;
14 AllocId, Allocation, AllocationExtra, AssertMessage, Frame, ImmTy, InterpCx, InterpResult,
15 Memory, MemoryKind, OpTy, Operand, PlaceTy, Pointer, Scalar,
18 /// Data returned by Machine::stack_pop,
19 /// to provide further control over the popping of the stack frame
20 #[derive(Eq, PartialEq, Debug, Copy, Clone)]
21 pub enum StackPopInfo {
22 /// Indicates that no special handling should be
23 /// done - we'll either return normally or unwind
24 /// based on the terminator for the function
28 /// Indicates that we should stop unwinding,
29 /// as we've reached a catch frame
33 /// Whether this kind of memory is allowed to leak
34 pub trait MayLeak: Copy {
35 fn may_leak(self) -> bool;
38 /// The functionality needed by memory to manage its allocations
39 pub trait AllocMap<K: Hash + Eq, V> {
40 /// Tests if the map contains the given key.
41 /// Deliberately takes `&mut` because that is sufficient, and some implementations
42 /// can be more efficient then (using `RefCell::get_mut`).
43 fn contains_key<Q: ?Sized + Hash + Eq>(&mut self, k: &Q) -> bool
47 /// Inserts a new entry into the map.
48 fn insert(&mut self, k: K, v: V) -> Option<V>;
50 /// Removes an entry from the map.
51 fn remove<Q: ?Sized + Hash + Eq>(&mut self, k: &Q) -> Option<V>
55 /// Returns data based the keys and values in the map.
56 fn filter_map_collect<T>(&self, f: impl FnMut(&K, &V) -> Option<T>) -> Vec<T>;
58 /// Returns a reference to entry `k`. If no such entry exists, call
59 /// `vacant` and either forward its error, or add its result to the map
60 /// and return a reference to *that*.
61 fn get_or<E>(&self, k: K, vacant: impl FnOnce() -> Result<V, E>) -> Result<&V, E>;
63 /// Returns a mutable reference to entry `k`. If no such entry exists, call
64 /// `vacant` and either forward its error, or add its result to the map
65 /// and return a reference to *that*.
66 fn get_mut_or<E>(&mut self, k: K, vacant: impl FnOnce() -> Result<V, E>) -> Result<&mut V, E>;
69 fn get(&self, k: K) -> Option<&V> {
70 self.get_or(k, || Err(())).ok()
74 fn get_mut(&mut self, k: K) -> Option<&mut V> {
75 self.get_mut_or(k, || Err(())).ok()
79 /// Methods of this trait signifies a point where CTFE evaluation would fail
80 /// and some use case dependent behaviour can instead be applied.
81 pub trait Machine<'mir, 'tcx>: Sized {
82 /// Additional memory kinds a machine wishes to distinguish from the builtin ones
83 type MemoryKinds: ::std::fmt::Debug + MayLeak + Eq + 'static;
85 /// Tag tracked alongside every pointer. This is used to implement "Stacked Borrows"
86 /// <https://www.ralfj.de/blog/2018/08/07/stacked-borrows.html>.
87 /// The `default()` is used for pointers to consts, statics, vtables and functions.
88 type PointerTag: ::std::fmt::Debug + Copy + Eq + Hash + 'static;
90 /// Machines can define extra (non-instance) things that represent values of function pointers.
91 /// For example, Miri uses this to return a function pointer from `dlsym`
92 /// that can later be called to execute the right thing.
93 type ExtraFnVal: ::std::fmt::Debug + Copy;
95 /// Extra data stored in every call frame.
98 /// Extra data stored in memory. A reference to this is available when `AllocExtra`
99 /// gets initialized, so you can e.g., have an `Rc` here if there is global state you
100 /// need access to in the `AllocExtra` hooks.
103 /// Extra data stored in every allocation.
104 type AllocExtra: AllocationExtra<Self::PointerTag> + 'static;
106 /// Memory's allocation map
107 type MemoryMap: AllocMap<
109 (MemoryKind<Self::MemoryKinds>, Allocation<Self::PointerTag, Self::AllocExtra>),
113 /// The memory kind to use for copied statics -- or None if statics should not be mutated
114 /// and thus any such attempt will cause a `ModifiedStatic` error to be raised.
115 /// Statics are copied under two circumstances: When they are mutated, and when
116 /// `tag_allocation` or `find_foreign_static` (see below) returns an owned allocation
117 /// that is added to the memory so that the work is not done twice.
118 const STATIC_KIND: Option<Self::MemoryKinds>;
120 /// Whether memory accesses should be alignment-checked.
121 const CHECK_ALIGN: bool;
123 /// Whether to enforce the validity invariant
124 fn enforce_validity(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool;
126 /// Called before a basic block terminator is executed.
127 /// You can use this to detect endlessly running programs.
128 fn before_terminator(ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx>;
130 /// Entry point to all function calls.
132 /// Returns either the mir to use for the call, or `None` if execution should
133 /// just proceed (which usually means this hook did all the work that the
134 /// called function should usually have done). In the latter case, it is
135 /// this hook's responsibility to advance the instruction pointer!
136 /// (This is to support functions like `__rust_maybe_catch_panic` that neither find a MIR
137 /// nor just jump to `ret`, but instead push their own stack frame.)
138 /// Passing `dest`and `ret` in the same `Option` proved very annoying when only one of them
140 fn find_mir_or_eval_fn(
141 ecx: &mut InterpCx<'mir, 'tcx, Self>,
143 instance: ty::Instance<'tcx>,
144 args: &[OpTy<'tcx, Self::PointerTag>],
145 ret: Option<(PlaceTy<'tcx, Self::PointerTag>, mir::BasicBlock)>,
146 unwind: Option<mir::BasicBlock>,
147 ) -> InterpResult<'tcx, Option<&'mir mir::Body<'tcx>>>;
149 /// Execute `fn_val`. It is the hook's responsibility to advance the instruction
150 /// pointer as appropriate.
152 ecx: &mut InterpCx<'mir, 'tcx, Self>,
153 fn_val: Self::ExtraFnVal,
154 args: &[OpTy<'tcx, Self::PointerTag>],
155 ret: Option<(PlaceTy<'tcx, Self::PointerTag>, mir::BasicBlock)>,
156 unwind: Option<mir::BasicBlock>,
157 ) -> InterpResult<'tcx>;
159 /// Directly process an intrinsic without pushing a stack frame. It is the hook's
160 /// responsibility to advance the instruction pointer as appropriate.
162 ecx: &mut InterpCx<'mir, 'tcx, Self>,
164 instance: ty::Instance<'tcx>,
165 args: &[OpTy<'tcx, Self::PointerTag>],
166 ret: Option<(PlaceTy<'tcx, Self::PointerTag>, mir::BasicBlock)>,
167 unwind: Option<mir::BasicBlock>,
168 ) -> InterpResult<'tcx>;
170 /// Called to evaluate `Assert` MIR terminators that trigger a panic.
172 ecx: &mut InterpCx<'mir, 'tcx, Self>,
174 msg: &AssertMessage<'tcx>,
175 unwind: Option<mir::BasicBlock>,
176 ) -> InterpResult<'tcx>;
178 /// Called for read access to a foreign static item.
180 /// This will only be called once per static and machine; the result is cached in
181 /// the machine memory. (This relies on `AllocMap::get_or` being able to add the
182 /// owned allocation to the map even when the map is shared.)
184 /// This allocation will then be fed to `tag_allocation` to initialize the "extra" state.
185 fn find_foreign_static(
188 ) -> InterpResult<'tcx, Cow<'tcx, Allocation>>;
190 /// Called for all binary operations where the LHS has pointer type.
192 /// Returns a (value, overflowed) pair if the operation succeeded
194 ecx: &InterpCx<'mir, 'tcx, Self>,
196 left: ImmTy<'tcx, Self::PointerTag>,
197 right: ImmTy<'tcx, Self::PointerTag>,
198 ) -> InterpResult<'tcx, (Scalar<Self::PointerTag>, bool, Ty<'tcx>)>;
200 /// Heap allocations via the `box` keyword.
202 ecx: &mut InterpCx<'mir, 'tcx, Self>,
203 dest: PlaceTy<'tcx, Self::PointerTag>,
204 ) -> InterpResult<'tcx>;
206 /// Called to read the specified `local` from the `frame`.
208 _ecx: &InterpCx<'mir, 'tcx, Self>,
209 frame: &Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>,
211 ) -> InterpResult<'tcx, Operand<Self::PointerTag>> {
212 frame.locals[local].access()
215 /// Called before a `StaticKind::Static` value is accessed.
216 fn before_access_static(
217 _memory_extra: &Self::MemoryExtra,
218 _allocation: &Allocation,
219 ) -> InterpResult<'tcx> {
223 /// Called to initialize the "extra" state of an allocation and make the pointers
224 /// it contains (in relocations) tagged. The way we construct allocations is
225 /// to always first construct it without extra and then add the extra.
226 /// This keeps uniform code paths for handling both allocations created by CTFE
227 /// for statics, and allocations ceated by Miri during evaluation.
229 /// `kind` is the kind of the allocation being tagged; it can be `None` when
230 /// it's a static and `STATIC_KIND` is `None`.
232 /// This should avoid copying if no work has to be done! If this returns an owned
233 /// allocation (because a copy had to be done to add tags or metadata), machine memory will
234 /// cache the result. (This relies on `AllocMap::get_or` being able to add the
235 /// owned allocation to the map even when the map is shared.)
237 /// Also return the "base" tag to use for this allocation: the one that is used for direct
238 /// accesses to this allocation. If `kind == STATIC_KIND`, this tag must be consistent
239 /// with `tag_static_base_pointer`.
240 fn init_allocation_extra<'b>(
241 memory_extra: &Self::MemoryExtra,
243 alloc: Cow<'b, Allocation>,
244 kind: Option<MemoryKind<Self::MemoryKinds>>,
245 ) -> (Cow<'b, Allocation<Self::PointerTag, Self::AllocExtra>>, Self::PointerTag);
247 /// Return the "base" tag for the given *static* allocation: the one that is used for direct
248 /// accesses to this static/const/fn allocation. If `id` is not a static allocation,
249 /// this will return an unusable tag (i.e., accesses will be UB)!
250 fn tag_static_base_pointer(memory_extra: &Self::MemoryExtra, id: AllocId) -> Self::PointerTag;
252 /// Executes a retagging operation
255 _ecx: &mut InterpCx<'mir, 'tcx, Self>,
256 _kind: mir::RetagKind,
257 _place: PlaceTy<'tcx, Self::PointerTag>,
258 ) -> InterpResult<'tcx> {
262 /// Called immediately before a new stack frame got pushed
263 fn stack_push(ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx, Self::FrameExtra>;
265 /// Called immediately after a stack frame gets popped
267 _ecx: &mut InterpCx<'mir, 'tcx, Self>,
268 _extra: Self::FrameExtra,
270 ) -> InterpResult<'tcx, StackPopInfo> {
271 // By default, we do not support unwinding from panics
272 Ok(StackPopInfo::Normal)
276 _mem: &Memory<'mir, 'tcx, Self>,
278 ) -> InterpResult<'tcx, Pointer<Self::PointerTag>> {
280 err_unsup!(InvalidNullPointerUsage)
282 err_unsup!(ReadBytesAsPointer)
288 _mem: &Memory<'mir, 'tcx, Self>,
289 _ptr: Pointer<Self::PointerTag>,
290 ) -> InterpResult<'tcx, u64>;