]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/interpret/machine.rs
Rollup merge of #67572 - aidanhs:aphs-choco-direct-cdn, r=Mark-Simulacrum
[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     AllocId, Allocation, AllocationExtra, AssertMessage, Frame, ImmTy, InterpCx, InterpResult,
15     Memory, MemoryKind, OpTy, Operand, PlaceTy, Pointer, Scalar,
16 };
17
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
25     /// we're leaving.
26     Normal,
27
28     /// Indicates that we should stop unwinding,
29     /// as we've reached a catch frame
30     StopUnwinding,
31 }
32
33 /// Whether this kind of memory is allowed to leak
34 pub trait MayLeak: Copy {
35     fn may_leak(self) -> bool;
36 }
37
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
44     where
45         K: Borrow<Q>;
46
47     /// Inserts a new entry into the map.
48     fn insert(&mut self, k: K, v: V) -> Option<V>;
49
50     /// Removes an entry from the map.
51     fn remove<Q: ?Sized + Hash + Eq>(&mut self, k: &Q) -> Option<V>
52     where
53         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>(&self, k: K, vacant: impl FnOnce() -> Result<V, E>) -> Result<&V, E>;
62
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>;
67
68     /// Read-only lookup.
69     fn get(&self, k: K) -> Option<&V> {
70         self.get_or(k, || Err(())).ok()
71     }
72
73     /// Mutable lookup.
74     fn get_mut(&mut self, k: K) -> Option<&mut V> {
75         self.get_mut_or(k, || Err(())).ok()
76     }
77 }
78
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;
84
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;
89
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;
94
95     /// Extra data stored in every call frame.
96     type FrameExtra;
97
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.
101     type MemoryExtra;
102
103     /// Extra data stored in every allocation.
104     type AllocExtra: AllocationExtra<Self::PointerTag> + 'static;
105
106     /// Memory's allocation map
107     type MemoryMap: AllocMap<
108             AllocId,
109             (MemoryKind<Self::MemoryKinds>, Allocation<Self::PointerTag, Self::AllocExtra>),
110         > + Default
111         + Clone;
112
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>;
119
120     /// Whether memory accesses should be alignment-checked.
121     const CHECK_ALIGN: bool;
122
123     /// Whether to enforce the validity invariant
124     fn enforce_validity(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool;
125
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>;
129
130     /// Entry point to all function calls.
131     ///
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
139     /// was used.
140     fn find_mir_or_eval_fn(
141         ecx: &mut InterpCx<'mir, 'tcx, Self>,
142         instance: ty::Instance<'tcx>,
143         args: &[OpTy<'tcx, Self::PointerTag>],
144         ret: Option<(PlaceTy<'tcx, Self::PointerTag>, mir::BasicBlock)>,
145         unwind: Option<mir::BasicBlock>,
146     ) -> InterpResult<'tcx, Option<&'mir mir::Body<'tcx>>>;
147
148     /// Execute `fn_val`.  It is the hook's responsibility to advance the instruction
149     /// pointer as appropriate.
150     fn call_extra_fn(
151         ecx: &mut InterpCx<'mir, 'tcx, Self>,
152         fn_val: Self::ExtraFnVal,
153         args: &[OpTy<'tcx, Self::PointerTag>],
154         ret: Option<(PlaceTy<'tcx, Self::PointerTag>, mir::BasicBlock)>,
155         unwind: Option<mir::BasicBlock>,
156     ) -> InterpResult<'tcx>;
157
158     /// Directly process an intrinsic without pushing a stack frame. It is the hook's
159     /// responsibility to advance the instruction pointer as appropriate.
160     fn call_intrinsic(
161         ecx: &mut InterpCx<'mir, 'tcx, Self>,
162         span: Span,
163         instance: ty::Instance<'tcx>,
164         args: &[OpTy<'tcx, Self::PointerTag>],
165         ret: Option<(PlaceTy<'tcx, Self::PointerTag>, mir::BasicBlock)>,
166         unwind: Option<mir::BasicBlock>,
167     ) -> InterpResult<'tcx>;
168
169     /// Called to evaluate `Assert` MIR terminators that trigger a panic.
170     fn assert_panic(
171         ecx: &mut InterpCx<'mir, 'tcx, Self>,
172         span: Span,
173         msg: &AssertMessage<'tcx>,
174         unwind: Option<mir::BasicBlock>,
175     ) -> InterpResult<'tcx>;
176
177     /// Called for read access to a foreign static item.
178     ///
179     /// This will only be called once per static and machine; the result is cached in
180     /// the machine memory. (This relies on `AllocMap::get_or` being able to add the
181     /// owned allocation to the map even when the map is shared.)
182     ///
183     /// This allocation will then be fed to `tag_allocation` to initialize the "extra" state.
184     fn find_foreign_static(
185         tcx: TyCtxt<'tcx>,
186         def_id: DefId,
187     ) -> InterpResult<'tcx, Cow<'tcx, Allocation>>;
188
189     /// Called for all binary operations where the LHS has pointer type.
190     ///
191     /// Returns a (value, overflowed) pair if the operation succeeded
192     fn binary_ptr_op(
193         ecx: &InterpCx<'mir, 'tcx, Self>,
194         bin_op: mir::BinOp,
195         left: ImmTy<'tcx, Self::PointerTag>,
196         right: ImmTy<'tcx, Self::PointerTag>,
197     ) -> InterpResult<'tcx, (Scalar<Self::PointerTag>, bool, Ty<'tcx>)>;
198
199     /// Heap allocations via the `box` keyword.
200     fn box_alloc(
201         ecx: &mut InterpCx<'mir, 'tcx, Self>,
202         dest: PlaceTy<'tcx, Self::PointerTag>,
203     ) -> InterpResult<'tcx>;
204
205     /// Called to read the specified `local` from the `frame`.
206     fn access_local(
207         _ecx: &InterpCx<'mir, 'tcx, Self>,
208         frame: &Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>,
209         local: mir::Local,
210     ) -> InterpResult<'tcx, Operand<Self::PointerTag>> {
211         frame.locals[local].access()
212     }
213
214     /// Called before a `StaticKind::Static` value is accessed.
215     fn before_access_static(
216         _memory_extra: &Self::MemoryExtra,
217         _allocation: &Allocation,
218     ) -> InterpResult<'tcx> {
219         Ok(())
220     }
221
222     /// Called to initialize the "extra" state of an allocation and make the pointers
223     /// it contains (in relocations) tagged.  The way we construct allocations is
224     /// to always first construct it without extra and then add the extra.
225     /// This keeps uniform code paths for handling both allocations created by CTFE
226     /// for statics, and allocations ceated by Miri during evaluation.
227     ///
228     /// `kind` is the kind of the allocation being tagged; it can be `None` when
229     /// it's a static and `STATIC_KIND` is `None`.
230     ///
231     /// This should avoid copying if no work has to be done! If this returns an owned
232     /// allocation (because a copy had to be done to add tags or metadata), machine memory will
233     /// cache the result. (This relies on `AllocMap::get_or` being able to add the
234     /// owned allocation to the map even when the map is shared.)
235     ///
236     /// Also return the "base" tag to use for this allocation: the one that is used for direct
237     /// accesses to this allocation. If `kind == STATIC_KIND`, this tag must be consistent
238     /// with `tag_static_base_pointer`.
239     fn init_allocation_extra<'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. If `id` is not a static allocation,
248     /// this will return an unusable tag (i.e., accesses will be UB)!
249     fn tag_static_base_pointer(memory_extra: &Self::MemoryExtra, id: AllocId) -> Self::PointerTag;
250
251     /// Executes a retagging operation
252     #[inline]
253     fn retag(
254         _ecx: &mut InterpCx<'mir, 'tcx, Self>,
255         _kind: mir::RetagKind,
256         _place: PlaceTy<'tcx, Self::PointerTag>,
257     ) -> InterpResult<'tcx> {
258         Ok(())
259     }
260
261     /// Called immediately before a new stack frame got pushed
262     fn stack_push(ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx, Self::FrameExtra>;
263
264     /// Called immediately after a stack frame gets popped
265     fn stack_pop(
266         _ecx: &mut InterpCx<'mir, 'tcx, Self>,
267         _extra: Self::FrameExtra,
268         _unwinding: bool,
269     ) -> InterpResult<'tcx, StackPopInfo> {
270         // By default, we do not support unwinding from panics
271         Ok(StackPopInfo::Normal)
272     }
273
274     fn int_to_ptr(
275         _mem: &Memory<'mir, 'tcx, Self>,
276         int: u64,
277     ) -> InterpResult<'tcx, Pointer<Self::PointerTag>> {
278         Err((if int == 0 {
279             err_unsup!(InvalidNullPointerUsage)
280         } else {
281             err_unsup!(ReadBytesAsPointer)
282         })
283         .into())
284     }
285
286     fn ptr_to_int(
287         _mem: &Memory<'mir, 'tcx, Self>,
288         _ptr: Pointer<Self::PointerTag>,
289     ) -> InterpResult<'tcx, u64>;
290 }