]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/interpret/machine.rs
4f04803addd8b673c8b800d454cf0265f39ab73e
[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, TyCtxt};
11
12 use super::{
13     Allocation, AllocId, InterpResult, InterpError, Scalar, AllocationExtra,
14     InterpCx, PlaceTy, OpTy, ImmTy, MemoryKind, Pointer, Memory, UnsupportedInfo::*
15 };
16
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> {
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
28         where K: Borrow<Q>;
29
30     /// Inserts a new entry into the map.
31     fn insert(&mut self, k: K, v: V) -> Option<V>;
32
33     /// Removes an entry from the map.
34     fn remove<Q: ?Sized + Hash + Eq>(&mut self, k: &Q) -> Option<V>
35         where K: Borrow<Q>;
36
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>;
39
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*.
43     fn get_or<E>(
44         &self,
45         k: K,
46         vacant: impl FnOnce() -> Result<V, E>
47     ) -> Result<&V, E>;
48
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*.
52     fn get_mut_or<E>(
53         &mut self,
54         k: K,
55         vacant: impl FnOnce() -> Result<V, E>
56     ) -> Result<&mut V, E>;
57
58     /// Read-only lookup.
59     fn get(&self, k: K) -> Option<&V> {
60         self.get_or(k, || Err(())).ok()
61     }
62
63     /// Mutable lookup.
64     fn get_mut(&mut self, k: K) -> Option<&mut V> {
65         self.get_mut_or(k, || Err(())).ok()
66     }
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.
71 pub trait Machine<'mir, 'tcx>: Sized {
72     /// Additional memory kinds a machine wishes to distinguish from the builtin ones
73     type MemoryKinds: ::std::fmt::Debug + MayLeak + Eq + 'static;
74
75     /// Tag tracked alongside every pointer. This is used to implement "Stacked Borrows"
76     /// <https://www.ralfj.de/blog/2018/08/07/stacked-borrows.html>.
77     /// The `default()` is used for pointers to consts, statics, vtables and functions.
78     type PointerTag: ::std::fmt::Debug + Copy + Eq + Hash + 'static;
79
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
85     /// Extra data stored in every call frame.
86     type FrameExtra;
87
88     /// Extra data stored in memory. A reference to this is available when `AllocExtra`
89     /// gets initialized, so you can e.g., have an `Rc` here if there is global state you
90     /// need access to in the `AllocExtra` hooks.
91     type MemoryExtra;
92
93     /// Extra data stored in every allocation.
94     type AllocExtra: AllocationExtra<Self::PointerTag> + 'static;
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
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.
107     /// Statics are copied under two circumstances: When they are mutated, and when
108     /// `tag_allocation` or `find_foreign_static` (see below) returns an owned allocation
109     /// that is added to the memory so that the work is not done twice.
110     const STATIC_KIND: Option<Self::MemoryKinds>;
111
112     /// Whether to enforce the validity invariant
113     fn enforce_validity(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool;
114
115     /// Called before a basic block terminator is executed.
116     /// You can use this to detect endlessly running programs.
117     fn before_terminator(ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx>;
118
119     /// Entry point to all function calls.
120     ///
121     /// Returns either the mir to use for the call, or `None` if execution should
122     /// just proceed (which usually means this hook did all the work that the
123     /// called function should usually have done). In the latter case, it is
124     /// this hook's responsibility to call `goto_block(ret)` to advance the instruction pointer!
125     /// (This is to support functions like `__rust_maybe_catch_panic` that neither find a MIR
126     /// nor just jump to `ret`, but instead push their own stack frame.)
127     /// Passing `dest`and `ret` in the same `Option` proved very annoying when only one of them
128     /// was used.
129     fn find_fn(
130         ecx: &mut InterpCx<'mir, 'tcx, Self>,
131         instance: ty::Instance<'tcx>,
132         args: &[OpTy<'tcx, Self::PointerTag>],
133         dest: Option<PlaceTy<'tcx, Self::PointerTag>>,
134         ret: Option<mir::BasicBlock>,
135     ) -> InterpResult<'tcx, Option<&'mir mir::Body<'tcx>>>;
136
137     /// Execute `fn_val`.  it is the hook's responsibility to advance the instruction
138     /// pointer as appropriate.
139     fn call_extra_fn(
140         ecx: &mut InterpCx<'mir, 'tcx, Self>,
141         fn_val: Self::ExtraFnVal,
142         args: &[OpTy<'tcx, Self::PointerTag>],
143         dest: Option<PlaceTy<'tcx, Self::PointerTag>>,
144         ret: Option<mir::BasicBlock>,
145     ) -> InterpResult<'tcx>;
146
147     /// Directly process an intrinsic without pushing a stack frame.
148     /// If this returns successfully, the engine will take care of jumping to the next block.
149     fn call_intrinsic(
150         ecx: &mut InterpCx<'mir, 'tcx, Self>,
151         instance: ty::Instance<'tcx>,
152         args: &[OpTy<'tcx, Self::PointerTag>],
153         dest: PlaceTy<'tcx, Self::PointerTag>,
154     ) -> InterpResult<'tcx>;
155
156     /// Called for read access to a foreign static item.
157     ///
158     /// This will only be called once per static and machine; the result is cached in
159     /// the machine memory. (This relies on `AllocMap::get_or` being able to add the
160     /// owned allocation to the map even when the map is shared.)
161     ///
162     /// This allocation will then be fed to `tag_allocation` to initialize the "extra" state.
163     fn find_foreign_static(
164         tcx: TyCtxt<'tcx>,
165         def_id: DefId,
166     ) -> InterpResult<'tcx, Cow<'tcx, Allocation>>;
167
168     /// Called for all binary operations on integer(-like) types when one operand is a pointer
169     /// value, and for the `Offset` operation that is inherently about pointers.
170     ///
171     /// Returns a (value, overflowed) pair if the operation succeeded
172     fn ptr_op(
173         ecx: &InterpCx<'mir, 'tcx, Self>,
174         bin_op: mir::BinOp,
175         left: ImmTy<'tcx, Self::PointerTag>,
176         right: ImmTy<'tcx, Self::PointerTag>,
177     ) -> InterpResult<'tcx, (Scalar<Self::PointerTag>, bool)>;
178
179     /// Heap allocations via the `box` keyword.
180     fn box_alloc(
181         ecx: &mut InterpCx<'mir, 'tcx, Self>,
182         dest: PlaceTy<'tcx, Self::PointerTag>,
183     ) -> InterpResult<'tcx>;
184
185     /// Called to initialize the "extra" state of an allocation and make the pointers
186     /// it contains (in relocations) tagged.  The way we construct allocations is
187     /// to always first construct it without extra and then add the extra.
188     /// This keeps uniform code paths for handling both allocations created by CTFE
189     /// for statics, and allocations ceated by Miri during evaluation.
190     ///
191     /// `kind` is the kind of the allocation being tagged; it can be `None` when
192     /// it's a static and `STATIC_KIND` is `None`.
193     ///
194     /// This should avoid copying if no work has to be done! If this returns an owned
195     /// allocation (because a copy had to be done to add tags or metadata), machine memory will
196     /// cache the result. (This relies on `AllocMap::get_or` being able to add the
197     /// owned allocation to the map even when the map is shared.)
198     ///
199     /// For static allocations, the tag returned must be the same as the one returned by
200     /// `tag_static_base_pointer`.
201     fn tag_allocation<'b>(
202         memory_extra: &Self::MemoryExtra,
203         id: AllocId,
204         alloc: Cow<'b, Allocation>,
205         kind: Option<MemoryKind<Self::MemoryKinds>>,
206     ) -> (Cow<'b, Allocation<Self::PointerTag, Self::AllocExtra>>, Self::PointerTag);
207
208     /// Return the "base" tag for the given static allocation: the one that is used for direct
209     /// accesses to this static/const/fn allocation.
210     ///
211     /// Be aware that requesting the `Allocation` for that `id` will lead to cycles
212     /// for cyclic statics!
213     fn tag_static_base_pointer(
214         memory_extra: &Self::MemoryExtra,
215         id: AllocId,
216     ) -> Self::PointerTag;
217
218     /// Executes a retagging operation
219     #[inline]
220     fn retag(
221         _ecx: &mut InterpCx<'mir, 'tcx, Self>,
222         _kind: mir::RetagKind,
223         _place: PlaceTy<'tcx, Self::PointerTag>,
224     ) -> InterpResult<'tcx> {
225         Ok(())
226     }
227
228     /// Called immediately before a new stack frame got pushed
229     fn stack_push(ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx, Self::FrameExtra>;
230
231     /// Called immediately after a stack frame gets popped
232     fn stack_pop(
233         ecx: &mut InterpCx<'mir, 'tcx, Self>,
234         extra: Self::FrameExtra,
235     ) -> InterpResult<'tcx>;
236
237     #[inline(always)]
238     fn int_to_ptr(
239         _mem: &Memory<'mir, 'tcx, Self>,
240         int: u64,
241     ) -> InterpResult<'tcx, Pointer<Self::PointerTag>> {
242         Err((if int == 0 {
243             InterpError::Unsupported(InvalidNullPointerUsage)
244         } else {
245             InterpError::Unsupported(ReadBytesAsPointer)
246         }).into())
247     }
248
249     #[inline(always)]
250     fn ptr_to_int(
251         _mem: &Memory<'mir, 'tcx, Self>,
252         _ptr: Pointer<Self::PointerTag>,
253     ) -> InterpResult<'tcx, u64> {
254         err!(ReadPointerAsBytes)
255     }
256 }