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};
8 use rustc::hir::def_id::DefId;
10 use rustc::ty::{self, query::TyCtxtAt};
13 Allocation, AllocId, EvalResult, Scalar, AllocationExtra,
14 InterpretCx, PlaceTy, OpTy, ImmTy, MemoryKind,
17 /// Whether this kind of memory is allowed to leak
18 pub trait MayLeak: Copy {
19 fn may_leak(self) -> bool;
22 /// The functionality needed by memory to manage its allocations
23 pub trait AllocMap<K: Hash + Eq, V> {
24 /// Tests if the map contains the given key.
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
30 /// Inserts a new entry into the map.
31 fn insert(&mut self, k: K, v: V) -> Option<V>;
33 /// Removes an entry from the map.
34 fn remove<Q: ?Sized + Hash + Eq>(&mut self, k: &Q) -> Option<V>
37 /// Returns data based the keys and values in the map.
38 fn filter_map_collect<T>(&self, f: impl FnMut(&K, &V) -> Option<T>) -> Vec<T>;
40 /// Returns a reference to entry `k`. If no such entry exists, call
41 /// `vacant` and either forward its error, or add its result to the map
42 /// and return a reference to *that*.
46 vacant: impl FnOnce() -> Result<V, E>
49 /// Returns a mutable reference to entry `k`. If no such entry exists, call
50 /// `vacant` and either forward its error, or add its result to the map
51 /// and return a reference to *that*.
55 vacant: impl FnOnce() -> Result<V, E>
56 ) -> Result<&mut V, E>;
59 /// Methods of this trait signifies a point where CTFE evaluation would fail
60 /// and some use case dependent behaviour can instead be applied.
61 pub trait Machine<'a, 'mir, 'tcx>: Sized {
62 /// Additional memory kinds a machine wishes to distinguish from the builtin ones
63 type MemoryKinds: ::std::fmt::Debug + MayLeak + Eq + 'static;
65 /// Tag tracked alongside every pointer. This is used to implement "Stacked Borrows"
66 /// <https://www.ralfj.de/blog/2018/08/07/stacked-borrows.html>.
67 /// The `default()` is used for pointers to consts, statics, vtables and functions.
68 type PointerTag: ::std::fmt::Debug + Copy + Eq + Hash + 'static;
70 /// Extra data stored in every call frame.
73 /// Extra data stored in memory. A reference to this is available when `AllocExtra`
74 /// gets initialized, so you can e.g., have an `Rc` here if there is global state you
75 /// need access to in the `AllocExtra` hooks.
76 type MemoryExtra: Default;
78 /// Extra data stored in every allocation.
79 type AllocExtra: AllocationExtra<Self::PointerTag> + 'static;
81 /// Memory's allocation map
85 (MemoryKind<Self::MemoryKinds>, Allocation<Self::PointerTag, Self::AllocExtra>)
90 /// The memory kind to use for copied statics -- or None if statics should not be mutated
91 /// and thus any such attempt will cause a `ModifiedStatic` error to be raised.
92 /// Statics are copied under two circumstances: When they are mutated, and when
93 /// `tag_allocation` or `find_foreign_static` (see below) returns an owned allocation
94 /// that is added to the memory so that the work is not done twice.
95 const STATIC_KIND: Option<Self::MemoryKinds>;
97 /// Whether to enforce the validity invariant
98 fn enforce_validity(ecx: &InterpretCx<'a, 'mir, 'tcx, Self>) -> bool;
100 /// Called before a basic block terminator is executed.
101 /// You can use this to detect endlessly running programs.
102 fn before_terminator(ecx: &mut InterpretCx<'a, 'mir, 'tcx, Self>) -> EvalResult<'tcx>;
104 /// Entry point to all function calls.
106 /// Returns either the mir to use for the call, or `None` if execution should
107 /// just proceed (which usually means this hook did all the work that the
108 /// called function should usually have done). In the latter case, it is
109 /// this hook's responsibility to call `goto_block(ret)` to advance the instruction pointer!
110 /// (This is to support functions like `__rust_maybe_catch_panic` that neither find a MIR
111 /// nor just jump to `ret`, but instead push their own stack frame.)
112 /// Passing `dest`and `ret` in the same `Option` proved very annoying when only one of them
115 ecx: &mut InterpretCx<'a, 'mir, 'tcx, Self>,
116 instance: ty::Instance<'tcx>,
117 args: &[OpTy<'tcx, Self::PointerTag>],
118 dest: Option<PlaceTy<'tcx, Self::PointerTag>>,
119 ret: Option<mir::BasicBlock>,
120 ) -> EvalResult<'tcx, Option<&'mir mir::Body<'tcx>>>;
122 /// Directly process an intrinsic without pushing a stack frame.
123 /// If this returns successfully, the engine will take care of jumping to the next block.
125 ecx: &mut InterpretCx<'a, 'mir, 'tcx, Self>,
126 instance: ty::Instance<'tcx>,
127 args: &[OpTy<'tcx, Self::PointerTag>],
128 dest: PlaceTy<'tcx, Self::PointerTag>,
129 ) -> EvalResult<'tcx>;
131 /// Called for read access to a foreign static item.
133 /// This will only be called once per static and machine; the result is cached in
134 /// the machine memory. (This relies on `AllocMap::get_or` being able to add the
135 /// owned allocation to the map even when the map is shared.)
137 /// This allocation will then be fed to `tag_allocation` to initialize the "extra" state.
138 fn find_foreign_static(
140 tcx: TyCtxtAt<'a, 'tcx, 'tcx>,
141 ) -> EvalResult<'tcx, Cow<'tcx, Allocation>>;
143 /// Called for all binary operations on integer(-like) types when one operand is a pointer
144 /// value, and for the `Offset` operation that is inherently about pointers.
146 /// Returns a (value, overflowed) pair if the operation succeeded
148 ecx: &InterpretCx<'a, 'mir, 'tcx, Self>,
150 left: ImmTy<'tcx, Self::PointerTag>,
151 right: ImmTy<'tcx, Self::PointerTag>,
152 ) -> EvalResult<'tcx, (Scalar<Self::PointerTag>, bool)>;
154 /// Heap allocations via the `box` keyword.
156 ecx: &mut InterpretCx<'a, 'mir, 'tcx, Self>,
157 dest: PlaceTy<'tcx, Self::PointerTag>,
158 ) -> EvalResult<'tcx>;
160 /// Called to initialize the "extra" state of an allocation and make the pointers
161 /// it contains (in relocations) tagged. The way we construct allocations is
162 /// to always first construct it without extra and then add the extra.
163 /// This keeps uniform code paths for handling both allocations created by CTFE
164 /// for statics, and allocations ceated by Miri during evaluation.
166 /// `kind` is the kind of the allocation being tagged; it can be `None` when
167 /// it's a static and `STATIC_KIND` is `None`.
169 /// This should avoid copying if no work has to be done! If this returns an owned
170 /// allocation (because a copy had to be done to add tags or metadata), machine memory will
171 /// cache the result. (This relies on `AllocMap::get_or` being able to add the
172 /// owned allocation to the map even when the map is shared.)
174 /// For static allocations, the tag returned must be the same as the one returned by
175 /// `tag_static_base_pointer`.
176 fn tag_allocation<'b>(
178 alloc: Cow<'b, Allocation>,
179 kind: Option<MemoryKind<Self::MemoryKinds>>,
180 memory_extra: &Self::MemoryExtra,
181 ) -> (Cow<'b, Allocation<Self::PointerTag, Self::AllocExtra>>, Self::PointerTag);
183 /// Return the "base" tag for the given static allocation: the one that is used for direct
184 /// accesses to this static/const/fn allocation.
186 /// Be aware that requesting the `Allocation` for that `id` will lead to cycles
187 /// for cyclic statics!
188 fn tag_static_base_pointer(
190 memory_extra: &Self::MemoryExtra,
191 ) -> Self::PointerTag;
193 /// Executes a retagging operation
196 _ecx: &mut InterpretCx<'a, 'mir, 'tcx, Self>,
197 _kind: mir::RetagKind,
198 _place: PlaceTy<'tcx, Self::PointerTag>,
199 ) -> EvalResult<'tcx> {
203 /// Called immediately before a new stack frame got pushed
205 ecx: &mut InterpretCx<'a, 'mir, 'tcx, Self>,
206 ) -> EvalResult<'tcx, Self::FrameExtra>;
208 /// Called immediately after a stack frame gets popped
210 ecx: &mut InterpretCx<'a, 'mir, 'tcx, Self>,
211 extra: Self::FrameExtra,
212 ) -> EvalResult<'tcx>;