]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/interpret/machine.rs
Auto merge of #59987 - saleemjaffer:refactor_adjust_castkinds, r=oli-obk
[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::{self, def_id::DefId};
9 use rustc::mir;
10 use rustc::ty::{self, query::TyCtxtAt, layout::Size};
11
12 use super::{
13     Allocation, AllocId, EvalResult, Scalar, AllocationExtra,
14     InterpretCx, PlaceTy, MPlaceTy, OpTy, ImmTy, MemoryKind,
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
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;
64
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 + Default + Copy + Eq + Hash + 'static;
69
70     /// Extra data stored in every call frame.
71     type FrameExtra;
72
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;
77
78     /// Extra data stored in every allocation.
79     type AllocExtra: AllocationExtra<Self::PointerTag> + 'static;
80
81     /// Memory's allocation map
82     type MemoryMap:
83         AllocMap<
84             AllocId,
85             (MemoryKind<Self::MemoryKinds>, Allocation<Self::PointerTag, Self::AllocExtra>)
86         > +
87         Default +
88         Clone;
89
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     /// `static_with_default_tag` 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>;
96
97     /// Whether to enforce the validity invariant
98     fn enforce_validity(ecx: &InterpretCx<'a, 'mir, 'tcx, Self>) -> bool;
99
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>;
103
104     /// Entry point to all function calls.
105     ///
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
113     /// was used.
114     fn find_fn(
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::Mir<'tcx>>>;
121
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.
124     fn call_intrinsic(
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>;
130
131     /// Called for read access to a foreign static item.
132     ///
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.)
136     fn find_foreign_static(
137         def_id: DefId,
138         tcx: TyCtxtAt<'a, 'tcx, 'tcx>,
139         memory_extra: &Self::MemoryExtra,
140     ) -> EvalResult<'tcx, Cow<'tcx, Allocation<Self::PointerTag, Self::AllocExtra>>>;
141
142     /// Called for all binary operations on integer(-like) types when one operand is a pointer
143     /// value, and for the `Offset` operation that is inherently about pointers.
144     ///
145     /// Returns a (value, overflowed) pair if the operation succeeded
146     fn ptr_op(
147         ecx: &InterpretCx<'a, 'mir, 'tcx, Self>,
148         bin_op: mir::BinOp,
149         left: ImmTy<'tcx, Self::PointerTag>,
150         right: ImmTy<'tcx, Self::PointerTag>,
151     ) -> EvalResult<'tcx, (Scalar<Self::PointerTag>, bool)>;
152
153     /// Heap allocations via the `box` keyword.
154     fn box_alloc(
155         ecx: &mut InterpretCx<'a, 'mir, 'tcx, Self>,
156         dest: PlaceTy<'tcx, Self::PointerTag>,
157     ) -> EvalResult<'tcx>;
158
159     /// Called to turn an allocation obtained from the `tcx` into one that has
160     /// the right type for this machine.
161     ///
162     /// This should avoid copying if no work has to be done! If this returns an owned
163     /// allocation (because a copy had to be done to add tags or metadata), machine memory will
164     /// cache the result. (This relies on `AllocMap::get_or` being able to add the
165     /// owned allocation to the map even when the map is shared.)
166     fn adjust_static_allocation<'b>(
167         alloc: &'b Allocation,
168         memory_extra: &Self::MemoryExtra,
169     ) -> Cow<'b, Allocation<Self::PointerTag, Self::AllocExtra>>;
170
171     /// Computes the extra state and the tag for a new allocation.
172     fn new_allocation(
173         size: Size,
174         extra: &Self::MemoryExtra,
175         kind: MemoryKind<Self::MemoryKinds>,
176     ) -> (Self::AllocExtra, Self::PointerTag);
177
178     /// Executed when evaluating the `*` operator: Following a reference.
179     /// This has the chance to adjust the tag. It should not change anything else!
180     /// `mutability` can be `None` in case a raw ptr is being dereferenced.
181     #[inline]
182     fn tag_dereference(
183         _ecx: &InterpretCx<'a, 'mir, 'tcx, Self>,
184         place: MPlaceTy<'tcx, Self::PointerTag>,
185         _mutability: Option<hir::Mutability>,
186     ) -> EvalResult<'tcx, Scalar<Self::PointerTag>> {
187         Ok(place.ptr)
188     }
189
190     /// Executes a retagging operation
191     #[inline]
192     fn retag(
193         _ecx: &mut InterpretCx<'a, 'mir, 'tcx, Self>,
194         _kind: mir::RetagKind,
195         _place: PlaceTy<'tcx, Self::PointerTag>,
196     ) -> EvalResult<'tcx> {
197         Ok(())
198     }
199
200     /// Called immediately before a new stack frame got pushed
201     fn stack_push(
202         ecx: &mut InterpretCx<'a, 'mir, 'tcx, Self>,
203     ) -> EvalResult<'tcx, Self::FrameExtra>;
204
205     /// Called immediately after a stack frame gets popped
206     fn stack_pop(
207         ecx: &mut InterpretCx<'a, 'mir, 'tcx, Self>,
208         extra: Self::FrameExtra,
209     ) -> EvalResult<'tcx>;
210 }