]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/interpret/machine.rs
Some cleanup
[rust.git] / src / librustc_mir / interpret / machine.rs
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
5 use std::borrow::{Borrow, Cow};
6 use std::hash::Hash;
7
8 use rustc::hir::def_id::DefId;
9 use rustc::mir;
10 use rustc::ty::{self, Ty, TyCtxt};
11 use syntax_pos::Span;
12
13 use super::{
14     Allocation, AllocId, InterpResult, Scalar, AllocationExtra,
15     InterpCx, PlaceTy, OpTy, ImmTy, MemoryKind, Pointer, Memory,
16     Frame, Operand,
17 };
18
19 /// Data returned by Machine::stack_pop,
20 /// to provide further control over the popping of the stack frame
21 #[derive(Eq, PartialEq, Debug, Copy, Clone)]
22 pub enum StackPopInfo {
23     /// Indicates that we have just started unwinding
24     /// as the result of panic
25     StartUnwinding,
26
27     /// Indicates that we performed a normal return
28     Normal,
29
30     /// Indicates that we should stop unwinding,
31     /// as we've reached a catch frame
32     StopUnwinding
33 }
34
35 /// Whether this kind of memory is allowed to leak
36 pub trait MayLeak: Copy {
37     fn may_leak(self) -> bool;
38 }
39
40 /// The functionality needed by memory to manage its allocations
41 pub trait AllocMap<K: Hash + Eq, V> {
42     /// Tests if the map contains the given key.
43     /// Deliberately takes `&mut` because that is sufficient, and some implementations
44     /// can be more efficient then (using `RefCell::get_mut`).
45     fn contains_key<Q: ?Sized + Hash + Eq>(&mut self, k: &Q) -> bool
46         where K: Borrow<Q>;
47
48     /// Inserts a new entry into the map.
49     fn insert(&mut self, k: K, v: V) -> Option<V>;
50
51     /// Removes an entry from the map.
52     fn remove<Q: ?Sized + Hash + Eq>(&mut self, k: &Q) -> Option<V>
53         where K: Borrow<Q>;
54
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>;
57
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>(
62         &self,
63         k: K,
64         vacant: impl FnOnce() -> Result<V, E>
65     ) -> Result<&V, E>;
66
67     /// Returns a mutable reference to entry `k`. If no such entry exists, call
68     /// `vacant` and either forward its error, or add its result to the map
69     /// and return a reference to *that*.
70     fn get_mut_or<E>(
71         &mut self,
72         k: K,
73         vacant: impl FnOnce() -> Result<V, E>
74     ) -> Result<&mut V, E>;
75
76     /// Read-only lookup.
77     fn get(&self, k: K) -> Option<&V> {
78         self.get_or(k, || Err(())).ok()
79     }
80
81     /// Mutable lookup.
82     fn get_mut(&mut self, k: K) -> Option<&mut V> {
83         self.get_mut_or(k, || Err(())).ok()
84     }
85 }
86
87 /// Methods of this trait signifies a point where CTFE evaluation would fail
88 /// and some use case dependent behaviour can instead be applied.
89 pub trait Machine<'mir, 'tcx>: Sized {
90     /// Additional memory kinds a machine wishes to distinguish from the builtin ones
91     type MemoryKinds: ::std::fmt::Debug + MayLeak + Eq + 'static;
92
93     /// Tag tracked alongside every pointer. This is used to implement "Stacked Borrows"
94     /// <https://www.ralfj.de/blog/2018/08/07/stacked-borrows.html>.
95     /// The `default()` is used for pointers to consts, statics, vtables and functions.
96     type PointerTag: ::std::fmt::Debug + Copy + Eq + Hash + 'static;
97
98     /// Machines can define extra (non-instance) things that represent values of function pointers.
99     /// For example, Miri uses this to return a fucntion pointer from `dlsym`
100     /// that can later be called to execute the right thing.
101     type ExtraFnVal: ::std::fmt::Debug + Copy;
102
103     /// Extra data stored in every call frame.
104     type FrameExtra;
105
106     /// Extra data stored in memory. A reference to this is available when `AllocExtra`
107     /// gets initialized, so you can e.g., have an `Rc` here if there is global state you
108     /// need access to in the `AllocExtra` hooks.
109     type MemoryExtra;
110
111     /// Extra data stored in every allocation.
112     type AllocExtra: AllocationExtra<Self::PointerTag> + 'static;
113
114     /// Memory's allocation map
115     type MemoryMap:
116         AllocMap<
117             AllocId,
118             (MemoryKind<Self::MemoryKinds>, Allocation<Self::PointerTag, Self::AllocExtra>)
119         > +
120         Default +
121         Clone;
122
123     /// The memory kind to use for copied statics -- or None if statics should not be mutated
124     /// and thus any such attempt will cause a `ModifiedStatic` error to be raised.
125     /// Statics are copied under two circumstances: When they are mutated, and when
126     /// `tag_allocation` or `find_foreign_static` (see below) returns an owned allocation
127     /// that is added to the memory so that the work is not done twice.
128     const STATIC_KIND: Option<Self::MemoryKinds>;
129
130     /// Whether memory accesses should be alignment-checked.
131     const CHECK_ALIGN: bool;
132
133     /// Whether to enforce the validity invariant
134     fn enforce_validity(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool;
135
136     /// Called before a basic block terminator is executed.
137     /// You can use this to detect endlessly running programs.
138     fn before_terminator(ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx>;
139
140     /// Entry point to all function calls.
141     ///
142     /// Returns either the mir to use for the call, or `None` if execution should
143     /// just proceed (which usually means this hook did all the work that the
144     /// called function should usually have done). In the latter case, it is
145     /// this hook's responsibility to call `goto_block(ret)` to advance the instruction pointer!
146     /// (This is to support functions like `__rust_maybe_catch_panic` that neither find a MIR
147     /// nor just jump to `ret`, but instead push their own stack frame.)
148     /// Passing `dest`and `ret` in the same `Option` proved very annoying when only one of them
149     /// was used.
150     fn find_fn(
151         ecx: &mut InterpCx<'mir, 'tcx, Self>,
152         instance: ty::Instance<'tcx>,
153         args: &[OpTy<'tcx, Self::PointerTag>],
154         dest: Option<PlaceTy<'tcx, Self::PointerTag>>,
155         ret: Option<mir::BasicBlock>,
156         unwind: Option<mir::BasicBlock>
157     ) -> InterpResult<'tcx, Option<&'mir mir::Body<'tcx>>>;
158
159     /// Execute `fn_val`.  it is the hook's responsibility to advance the instruction
160     /// pointer as appropriate.
161     fn call_extra_fn(
162         ecx: &mut InterpCx<'mir, 'tcx, Self>,
163         fn_val: Self::ExtraFnVal,
164         args: &[OpTy<'tcx, Self::PointerTag>],
165         dest: Option<PlaceTy<'tcx, Self::PointerTag>>,
166         ret: Option<mir::BasicBlock>,
167     ) -> InterpResult<'tcx>;
168
169     /// Directly process an intrinsic without pushing a stack frame.
170     /// If this returns successfully, the engine will take care of jumping to the next block.
171     fn call_intrinsic(
172         ecx: &mut InterpCx<'mir, 'tcx, Self>,
173         span: Span,
174         instance: ty::Instance<'tcx>,
175         args: &[OpTy<'tcx, Self::PointerTag>],
176         dest: Option<PlaceTy<'tcx, Self::PointerTag>>,
177     ) -> InterpResult<'tcx>;
178
179     /// Called for read access to a foreign static item.
180     ///
181     /// This will only be called once per static and machine; the result is cached in
182     /// the machine memory. (This relies on `AllocMap::get_or` being able to add the
183     /// owned allocation to the map even when the map is shared.)
184     ///
185     /// This allocation will then be fed to `tag_allocation` to initialize the "extra" state.
186     fn find_foreign_static(
187         tcx: TyCtxt<'tcx>,
188         def_id: DefId,
189     ) -> InterpResult<'tcx, Cow<'tcx, Allocation>>;
190
191     /// Called for all binary operations where the LHS has pointer type.
192     ///
193     /// Returns a (value, overflowed) pair if the operation succeeded
194     fn binary_ptr_op(
195         ecx: &InterpCx<'mir, 'tcx, Self>,
196         bin_op: mir::BinOp,
197         left: ImmTy<'tcx, Self::PointerTag>,
198         right: ImmTy<'tcx, Self::PointerTag>,
199     ) -> InterpResult<'tcx, (Scalar<Self::PointerTag>, bool, Ty<'tcx>)>;
200
201     /// Heap allocations via the `box` keyword.
202     fn box_alloc(
203         ecx: &mut InterpCx<'mir, 'tcx, Self>,
204         dest: PlaceTy<'tcx, Self::PointerTag>,
205     ) -> InterpResult<'tcx>;
206
207     /// Called to read the specified `local` from the `frame`.
208     fn access_local(
209         _ecx: &InterpCx<'mir, 'tcx, Self>,
210         frame: &Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>,
211         local: mir::Local,
212     ) -> InterpResult<'tcx, Operand<Self::PointerTag>> {
213         frame.locals[local].access()
214     }
215
216     /// Called before a `StaticKind::Static` value is accessed.
217     fn before_access_static(
218         _allocation: &Allocation,
219     ) -> InterpResult<'tcx> {
220         Ok(())
221     }
222
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.
228     ///
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`.
231     ///
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.)
236     ///
237     /// For static allocations, the tag returned must be the same as the one returned by
238     /// `tag_static_base_pointer`.
239     fn tag_allocation<'b>(
240         memory_extra: &Self::MemoryExtra,
241         id: AllocId,
242         alloc: Cow<'b, Allocation>,
243         kind: Option<MemoryKind<Self::MemoryKinds>>,
244     ) -> (Cow<'b, Allocation<Self::PointerTag, Self::AllocExtra>>, Self::PointerTag);
245
246     /// Return the "base" tag for the given static allocation: the one that is used for direct
247     /// accesses to this static/const/fn allocation.
248     ///
249     /// Be aware that requesting the `Allocation` for that `id` will lead to cycles
250     /// for cyclic statics!
251     fn tag_static_base_pointer(
252         memory_extra: &Self::MemoryExtra,
253         id: AllocId,
254     ) -> Self::PointerTag;
255
256     /// Executes a retagging operation
257     #[inline]
258     fn retag(
259         _ecx: &mut InterpCx<'mir, 'tcx, Self>,
260         _kind: mir::RetagKind,
261         _place: PlaceTy<'tcx, Self::PointerTag>,
262     ) -> InterpResult<'tcx> {
263         Ok(())
264     }
265
266     /// Called immediately before a new stack frame got pushed
267     fn stack_push(ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx, Self::FrameExtra>;
268
269     /// Called immediately after a stack frame gets popped
270     fn stack_pop(
271         _ecx: &mut InterpCx<'mir, 'tcx, Self>,
272         _extra: Self::FrameExtra,
273     ) -> InterpResult<'tcx, StackPopInfo> {
274         // By default, we do not support unwinding from panics
275         Ok(StackPopInfo::Normal)
276     }
277
278     fn int_to_ptr(
279         _mem: &Memory<'mir, 'tcx, Self>,
280         int: u64,
281     ) -> InterpResult<'tcx, Pointer<Self::PointerTag>> {
282         Err((if int == 0 {
283             err_unsup!(InvalidNullPointerUsage)
284         } else {
285             err_unsup!(ReadBytesAsPointer)
286         }).into())
287     }
288
289     fn ptr_to_int(
290         _mem: &Memory<'mir, 'tcx, Self>,
291         _ptr: Pointer<Self::PointerTag>,
292     ) -> InterpResult<'tcx, u64>;
293 }