]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/interpret/machine.rs
f43cfb90fc4a1cf69bb6e24c1b2035419321db74
[rust.git] / src / librustc_mir / interpret / machine.rs
1 // Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 //! This module contains everything needed to instantiate an interpreter.
12 //! This separation exists to ensure that no fancy miri features like
13 //! interpreting common C functions leak into CTFE.
14
15 use std::borrow::{Borrow, Cow};
16 use std::hash::Hash;
17
18 use rustc::hir::{self, def_id::DefId};
19 use rustc::mir;
20 use rustc::ty::{self, layout::TyLayout, query::TyCtxtAt};
21
22 use super::{
23     Allocation, AllocId, EvalResult, Scalar, AllocationExtra,
24     EvalContext, PlaceTy, MPlaceTy, OpTy, Pointer, MemoryKind,
25 };
26
27 /// Whether this kind of memory is allowed to leak
28 pub trait MayLeak: Copy {
29     fn may_leak(self) -> bool;
30 }
31
32 /// The functionality needed by memory to manage its allocations
33 pub trait AllocMap<K: Hash + Eq, V> {
34     /// Test if the map contains the given key.
35     /// Deliberately takes `&mut` because that is sufficient, and some implementations
36     /// can be more efficient then (using `RefCell::get_mut`).
37     fn contains_key<Q: ?Sized + Hash + Eq>(&mut self, k: &Q) -> bool
38         where K: Borrow<Q>;
39
40     /// Insert new entry into the map.
41     fn insert(&mut self, k: K, v: V) -> Option<V>;
42
43     /// Remove entry from the map.
44     fn remove<Q: ?Sized + Hash + Eq>(&mut self, k: &Q) -> Option<V>
45         where K: Borrow<Q>;
46
47     /// Return data based the keys and values in the map.
48     fn filter_map_collect<T>(&self, f: impl FnMut(&K, &V) -> Option<T>) -> Vec<T>;
49
50     /// Return a reference to entry `k`.  If no such entry exists, call
51     /// `vacant` and either forward its error, or add its result to the map
52     /// and return a reference to *that*.
53     fn get_or<E>(
54         &self,
55         k: K,
56         vacant: impl FnOnce() -> Result<V, E>
57     ) -> Result<&V, E>;
58
59     /// Return a mutable reference to entry `k`.  If no such entry exists, call
60     /// `vacant` and either forward its error, or add its result to the map
61     /// and return a reference to *that*.
62     fn get_mut_or<E>(
63         &mut self,
64         k: K,
65         vacant: impl FnOnce() -> Result<V, E>
66     ) -> Result<&mut V, E>;
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<'a, '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 + Default + Copy + Eq + Hash + 'static;
79
80     /// Extra data stored in every call frame.
81     type FrameExtra;
82
83     /// Extra data stored in memory.  A reference to this is available when `AllocExtra`
84     /// gets initialized, so you can e.g. have an `Rc` here if there is global state you
85     /// need access to in the `AllocExtra` hooks.
86     type MemoryExtra: Default;
87
88     /// Extra data stored in every allocation.
89     type AllocExtra: AllocationExtra<Self::PointerTag, Self::MemoryExtra>;
90
91     /// Memory's allocation map
92     type MemoryMap:
93         AllocMap<
94             AllocId,
95             (MemoryKind<Self::MemoryKinds>, Allocation<Self::PointerTag, Self::AllocExtra>)
96         > +
97         Default +
98         Clone;
99
100     /// The memory kind to use for copied statics -- or None if statics should not be mutated
101     /// and thus any such attempt will cause a `ModifiedStatic` error to be raised.
102     /// Statics are copied under two circumstances: When they are mutated, and when
103     /// `static_with_default_tag` or `find_foreign_static` (see below) returns an owned allocation
104     /// that is added to the memory so that the work is not done twice.
105     const STATIC_KIND: Option<Self::MemoryKinds>;
106
107     /// Whether to enforce the validity invariant
108     fn enforce_validity(ecx: &EvalContext<'a, 'mir, 'tcx, Self>) -> bool;
109
110     /// Called before a basic block terminator is executed.
111     /// You can use this to detect endlessly running programs.
112     fn before_terminator(ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>) -> EvalResult<'tcx>;
113
114     /// Entry point to all function calls.
115     ///
116     /// Returns either the mir to use for the call, or `None` if execution should
117     /// just proceed (which usually means this hook did all the work that the
118     /// called function should usually have done).  In the latter case, it is
119     /// this hook's responsibility to call `goto_block(ret)` to advance the instruction pointer!
120     /// (This is to support functions like `__rust_maybe_catch_panic` that neither find a MIR
121     /// nor just jump to `ret`, but instead push their own stack frame.)
122     /// Passing `dest`and `ret` in the same `Option` proved very annoying when only one of them
123     /// was used.
124     fn find_fn(
125         ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
126         instance: ty::Instance<'tcx>,
127         args: &[OpTy<'tcx, Self::PointerTag>],
128         dest: Option<PlaceTy<'tcx, Self::PointerTag>>,
129         ret: Option<mir::BasicBlock>,
130     ) -> EvalResult<'tcx, Option<&'mir mir::Mir<'tcx>>>;
131
132     /// Directly process an intrinsic without pushing a stack frame.
133     /// If this returns successfully, the engine will take care of jumping to the next block.
134     fn call_intrinsic(
135         ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
136         instance: ty::Instance<'tcx>,
137         args: &[OpTy<'tcx, Self::PointerTag>],
138         dest: PlaceTy<'tcx, Self::PointerTag>,
139     ) -> EvalResult<'tcx>;
140
141     /// Called for read access to a foreign static item.
142     ///
143     /// This will only be called once per static and machine; the result is cached in
144     /// the machine memory. (This relies on `AllocMap::get_or` being able to add the
145     /// owned allocation to the map even when the map is shared.)
146     fn find_foreign_static(
147         def_id: DefId,
148         tcx: TyCtxtAt<'a, 'tcx, 'tcx>,
149         memory_extra: &Self::MemoryExtra,
150     ) -> EvalResult<'tcx, Cow<'tcx, Allocation<Self::PointerTag, Self::AllocExtra>>>;
151
152     /// Called to turn an allocation obtained from the `tcx` into one that has
153     /// the right type for this machine.
154     ///
155     /// This should avoid copying if no work has to be done! If this returns an owned
156     /// allocation (because a copy had to be done to add tags or metadata), machine memory will
157     /// cache the result. (This relies on `AllocMap::get_or` being able to add the
158     /// owned allocation to the map even when the map is shared.)
159     fn adjust_static_allocation<'b>(
160         alloc: &'b Allocation,
161         memory_extra: &Self::MemoryExtra,
162     ) -> Cow<'b, Allocation<Self::PointerTag, Self::AllocExtra>>;
163
164     /// Called for all binary operations on integer(-like) types when one operand is a pointer
165     /// value, and for the `Offset` operation that is inherently about pointers.
166     ///
167     /// Returns a (value, overflowed) pair if the operation succeeded
168     fn ptr_op(
169         ecx: &EvalContext<'a, 'mir, 'tcx, Self>,
170         bin_op: mir::BinOp,
171         left: Scalar<Self::PointerTag>,
172         left_layout: TyLayout<'tcx>,
173         right: Scalar<Self::PointerTag>,
174         right_layout: TyLayout<'tcx>,
175     ) -> EvalResult<'tcx, (Scalar<Self::PointerTag>, bool)>;
176
177     /// Heap allocations via the `box` keyword.
178     fn box_alloc(
179         ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
180         dest: PlaceTy<'tcx, Self::PointerTag>,
181     ) -> EvalResult<'tcx>;
182
183     /// Add the tag for a newly allocated pointer.
184     fn tag_new_allocation(
185         ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
186         ptr: Pointer,
187         kind: MemoryKind<Self::MemoryKinds>,
188     ) -> EvalResult<'tcx, Pointer<Self::PointerTag>>;
189
190     /// Executed when evaluating the `*` operator: Following a reference.
191     /// This has the chance to adjust the tag.  It should not change anything else!
192     /// `mutability` can be `None` in case a raw ptr is being dereferenced.
193     #[inline]
194     fn tag_dereference(
195         _ecx: &EvalContext<'a, 'mir, 'tcx, Self>,
196         place: MPlaceTy<'tcx, Self::PointerTag>,
197         _mutability: Option<hir::Mutability>,
198     ) -> EvalResult<'tcx, Scalar<Self::PointerTag>> {
199         Ok(place.ptr)
200     }
201
202     /// Execute a retagging operation
203     #[inline]
204     fn retag(
205         _ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
206         _fn_entry: bool,
207         _two_phase: bool,
208         _place: PlaceTy<'tcx, Self::PointerTag>,
209     ) -> EvalResult<'tcx> {
210         Ok(())
211     }
212
213     /// Execute an escape-to-raw operation
214     #[inline]
215     fn escape_to_raw(
216         _ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
217         _ptr: OpTy<'tcx, Self::PointerTag>,
218     ) -> EvalResult<'tcx> {
219         Ok(())
220     }
221
222     /// Called immediately before a new stack frame got pushed
223     fn stack_push(
224         ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
225     ) -> EvalResult<'tcx, Self::FrameExtra>;
226
227     /// Called immediately after a stack frame gets popped
228     fn stack_pop(
229         ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
230         extra: Self::FrameExtra,
231     ) -> EvalResult<'tcx>;
232 }