]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/interpret/machine.rs
Rollup merge of #70950 - nikomatsakis:leak-check-nll-2, r=matthewjasper
[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_middle::mir;
9 use rustc_middle::ty::{self, Ty};
10 use rustc_span::def_id::DefId;
11
12 use super::{
13     AllocId, Allocation, AllocationExtra, Frame, ImmTy, InterpCx, InterpResult, Memory, MemoryKind,
14     OpTy, Operand, PlaceTy, Pointer, Scalar,
15 };
16
17 /// Data returned by Machine::stack_pop,
18 /// to provide further control over the popping of the stack frame
19 #[derive(Eq, PartialEq, Debug, Copy, Clone)]
20 pub enum StackPopJump {
21     /// Indicates that no special handling should be
22     /// done - we'll either return normally or unwind
23     /// based on the terminator for the function
24     /// we're leaving.
25     Normal,
26
27     /// Indicates that we should *not* jump to the return/unwind address, as the callback already
28     /// took care of everything.
29     NoJump,
30 }
31
32 /// Whether this kind of memory is allowed to leak
33 pub trait MayLeak: Copy {
34     fn may_leak(self) -> bool;
35 }
36
37 /// The functionality needed by memory to manage its allocations
38 pub trait AllocMap<K: Hash + Eq, V> {
39     /// Tests if the map contains the given key.
40     /// Deliberately takes `&mut` because that is sufficient, and some implementations
41     /// can be more efficient then (using `RefCell::get_mut`).
42     fn contains_key<Q: ?Sized + Hash + Eq>(&mut self, k: &Q) -> bool
43     where
44         K: Borrow<Q>;
45
46     /// Inserts a new entry into the map.
47     fn insert(&mut self, k: K, v: V) -> Option<V>;
48
49     /// Removes an entry from the map.
50     fn remove<Q: ?Sized + Hash + Eq>(&mut self, k: &Q) -> Option<V>
51     where
52         K: Borrow<Q>;
53
54     /// Returns data based on the keys and values in the map.
55     fn filter_map_collect<T>(&self, f: impl FnMut(&K, &V) -> Option<T>) -> Vec<T>;
56
57     /// Returns a reference to entry `k`. If no such entry exists, call
58     /// `vacant` and either forward its error, or add its result to the map
59     /// and return a reference to *that*.
60     fn get_or<E>(&self, k: K, vacant: impl FnOnce() -> Result<V, E>) -> Result<&V, E>;
61
62     /// Returns a mutable reference to entry `k`. If no such entry exists, call
63     /// `vacant` and either forward its error, or add its result to the map
64     /// and return a reference to *that*.
65     fn get_mut_or<E>(&mut self, k: K, vacant: impl FnOnce() -> Result<V, E>) -> Result<&mut V, E>;
66
67     /// Read-only lookup.
68     fn get(&self, k: K) -> Option<&V> {
69         self.get_or(k, || Err(())).ok()
70     }
71
72     /// Mutable lookup.
73     fn get_mut(&mut self, k: K) -> Option<&mut V> {
74         self.get_mut_or(k, || Err(())).ok()
75     }
76 }
77
78 /// Methods of this trait signifies a point where CTFE evaluation would fail
79 /// and some use case dependent behaviour can instead be applied.
80 pub trait Machine<'mir, 'tcx>: Sized {
81     /// Additional memory kinds a machine wishes to distinguish from the builtin ones
82     type MemoryKind: ::std::fmt::Debug + ::std::fmt::Display + MayLeak + Eq + 'static;
83
84     /// Tag tracked alongside every pointer. This is used to implement "Stacked Borrows"
85     /// <https://www.ralfj.de/blog/2018/08/07/stacked-borrows.html>.
86     /// The `default()` is used for pointers to consts, statics, vtables and functions.
87     type PointerTag: ::std::fmt::Debug + Copy + Eq + Hash + 'static;
88
89     /// Machines can define extra (non-instance) things that represent values of function pointers.
90     /// For example, Miri uses this to return a function pointer from `dlsym`
91     /// that can later be called to execute the right thing.
92     type ExtraFnVal: ::std::fmt::Debug + Copy;
93
94     /// Extra data stored in every call frame.
95     type FrameExtra;
96
97     /// Extra data stored in memory. A reference to this is available when `AllocExtra`
98     /// gets initialized, so you can e.g., have an `Rc` here if there is global state you
99     /// need access to in the `AllocExtra` hooks.
100     type MemoryExtra;
101
102     /// Extra data stored in every allocation.
103     type AllocExtra: AllocationExtra<Self::PointerTag> + 'static;
104
105     /// Memory's allocation map
106     type MemoryMap: AllocMap<
107             AllocId,
108             (MemoryKind<Self::MemoryKind>, Allocation<Self::PointerTag, Self::AllocExtra>),
109         > + Default
110         + Clone;
111
112     /// The memory kind to use for copied global memory (held in `tcx`) --
113     /// or None if such memory should not be mutated and thus any such attempt will cause
114     /// a `ModifiedStatic` error to be raised.
115     /// Statics are copied under two circumstances: When they are mutated, and when
116     /// `tag_allocation` (see below) returns an owned allocation
117     /// that is added to the memory so that the work is not done twice.
118     const GLOBAL_KIND: Option<Self::MemoryKind>;
119
120     /// Whether memory accesses should be alignment-checked.
121     fn enforce_alignment(memory_extra: &Self::MemoryExtra) -> bool;
122
123     /// Whether to enforce the validity invariant
124     fn enforce_validity(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool;
125
126     /// Entry point to all function calls.
127     ///
128     /// Returns either the mir to use for the call, or `None` if execution should
129     /// just proceed (which usually means this hook did all the work that the
130     /// called function should usually have done). In the latter case, it is
131     /// this hook's responsibility to advance the instruction pointer!
132     /// (This is to support functions like `__rust_maybe_catch_panic` that neither find a MIR
133     /// nor just jump to `ret`, but instead push their own stack frame.)
134     /// Passing `dest`and `ret` in the same `Option` proved very annoying when only one of them
135     /// was used.
136     fn find_mir_or_eval_fn(
137         ecx: &mut InterpCx<'mir, 'tcx, Self>,
138         instance: ty::Instance<'tcx>,
139         args: &[OpTy<'tcx, Self::PointerTag>],
140         ret: Option<(PlaceTy<'tcx, Self::PointerTag>, mir::BasicBlock)>,
141         unwind: Option<mir::BasicBlock>,
142     ) -> InterpResult<'tcx, Option<&'mir mir::Body<'tcx>>>;
143
144     /// Execute `fn_val`.  It is the hook's responsibility to advance the instruction
145     /// pointer as appropriate.
146     fn call_extra_fn(
147         ecx: &mut InterpCx<'mir, 'tcx, Self>,
148         fn_val: Self::ExtraFnVal,
149         args: &[OpTy<'tcx, Self::PointerTag>],
150         ret: Option<(PlaceTy<'tcx, Self::PointerTag>, mir::BasicBlock)>,
151         unwind: Option<mir::BasicBlock>,
152     ) -> InterpResult<'tcx>;
153
154     /// Directly process an intrinsic without pushing a stack frame. It is the hook's
155     /// responsibility to advance the instruction pointer as appropriate.
156     fn call_intrinsic(
157         ecx: &mut InterpCx<'mir, 'tcx, Self>,
158         instance: ty::Instance<'tcx>,
159         args: &[OpTy<'tcx, Self::PointerTag>],
160         ret: Option<(PlaceTy<'tcx, Self::PointerTag>, mir::BasicBlock)>,
161         unwind: Option<mir::BasicBlock>,
162     ) -> InterpResult<'tcx>;
163
164     /// Called to evaluate `Assert` MIR terminators that trigger a panic.
165     fn assert_panic(
166         ecx: &mut InterpCx<'mir, 'tcx, Self>,
167         msg: &mir::AssertMessage<'tcx>,
168         unwind: Option<mir::BasicBlock>,
169     ) -> InterpResult<'tcx>;
170
171     /// Called to evaluate `Abort` MIR terminator.
172     fn abort(_ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx, !> {
173         throw_unsup_format!("aborting execution is not supported")
174     }
175
176     /// Called for all binary operations where the LHS has pointer type.
177     ///
178     /// Returns a (value, overflowed) pair if the operation succeeded
179     fn binary_ptr_op(
180         ecx: &InterpCx<'mir, 'tcx, Self>,
181         bin_op: mir::BinOp,
182         left: ImmTy<'tcx, Self::PointerTag>,
183         right: ImmTy<'tcx, Self::PointerTag>,
184     ) -> InterpResult<'tcx, (Scalar<Self::PointerTag>, bool, Ty<'tcx>)>;
185
186     /// Heap allocations via the `box` keyword.
187     fn box_alloc(
188         ecx: &mut InterpCx<'mir, 'tcx, Self>,
189         dest: PlaceTy<'tcx, Self::PointerTag>,
190     ) -> InterpResult<'tcx>;
191
192     /// Called to read the specified `local` from the `frame`.
193     #[inline]
194     fn access_local(
195         _ecx: &InterpCx<'mir, 'tcx, Self>,
196         frame: &Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>,
197         local: mir::Local,
198     ) -> InterpResult<'tcx, Operand<Self::PointerTag>> {
199         frame.locals[local].access()
200     }
201
202     /// Called before a basic block terminator is executed.
203     /// You can use this to detect endlessly running programs.
204     #[inline]
205     fn before_terminator(_ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> {
206         Ok(())
207     }
208
209     /// Called before a global allocation is accessed.
210     /// `def_id` is `Some` if this is the "lazy" allocation of a static.
211     #[inline]
212     fn before_access_global(
213         _memory_extra: &Self::MemoryExtra,
214         _alloc_id: AllocId,
215         _allocation: &Allocation,
216         _static_def_id: Option<DefId>,
217         _is_write: bool,
218     ) -> InterpResult<'tcx> {
219         Ok(())
220     }
221
222     /// Called for *every* memory access to determine the real ID of the given allocation.
223     /// This provides a way for the machine to "redirect" certain allocations as it sees fit.
224     ///
225     /// This is used by Miri to redirect extern statics to real allocations.
226     ///
227     /// This function must be idempotent.
228     #[inline]
229     fn canonical_alloc_id(_mem: &Memory<'mir, 'tcx, Self>, id: AllocId) -> AllocId {
230         id
231     }
232
233     /// Called when converting a `ty::Const` to an operand (in
234     /// `eval_const_to_op`).
235     ///
236     /// Miri uses this callback for creating per thread allocations for thread
237     /// locals. In Rust, one way of creating a thread local is by marking a
238     /// static with `#[thread_local]`. On supported platforms this gets
239     /// translated to a LLVM thread local for which LLVM automatically ensures
240     /// that each thread gets its own copy. Since LLVM automatically handles
241     /// thread locals, the Rust compiler just treats thread local statics as
242     /// regular statics even though accessing a thread local static should be an
243     /// effectful computation that depends on the current thread. The long term
244     /// plan is to change MIR to make accesses to thread locals explicit
245     /// (https://github.com/rust-lang/rust/issues/70685). While the issue 70685
246     /// is not fixed, our current workaround in Miri is to use this function to
247     /// make per-thread copies of thread locals. Please note that we cannot make
248     /// these copies in `canonical_alloc_id` because that is too late: for
249     /// example, if one created a pointer in thread `t1` to a thread local and
250     /// sent it to another thread `t2`, resolving the access in
251     /// `canonical_alloc_id` would result in pointer pointing to `t2`'s thread
252     /// local and not `t1` as it should.
253     #[inline]
254     fn adjust_global_const(
255         _ecx: &InterpCx<'mir, 'tcx, Self>,
256         val: mir::interpret::ConstValue<'tcx>,
257     ) -> InterpResult<'tcx, mir::interpret::ConstValue<'tcx>> {
258         Ok(val)
259     }
260
261     /// Called to initialize the "extra" state of an allocation and make the pointers
262     /// it contains (in relocations) tagged.  The way we construct allocations is
263     /// to always first construct it without extra and then add the extra.
264     /// This keeps uniform code paths for handling both allocations created by CTFE
265     /// for globals, and allocations created by Miri during evaluation.
266     ///
267     /// `kind` is the kind of the allocation being tagged; it can be `None` when
268     /// it's a global and `GLOBAL_KIND` is `None`.
269     ///
270     /// This should avoid copying if no work has to be done! If this returns an owned
271     /// allocation (because a copy had to be done to add tags or metadata), machine memory will
272     /// cache the result. (This relies on `AllocMap::get_or` being able to add the
273     /// owned allocation to the map even when the map is shared.)
274     ///
275     /// Also return the "base" tag to use for this allocation: the one that is used for direct
276     /// accesses to this allocation. If `kind == STATIC_KIND`, this tag must be consistent
277     /// with `tag_global_base_pointer`.
278     fn init_allocation_extra<'b>(
279         memory_extra: &Self::MemoryExtra,
280         id: AllocId,
281         alloc: Cow<'b, Allocation>,
282         kind: Option<MemoryKind<Self::MemoryKind>>,
283     ) -> (Cow<'b, Allocation<Self::PointerTag, Self::AllocExtra>>, Self::PointerTag);
284
285     /// Called to notify the machine before a deallocation occurs.
286     fn before_deallocation(
287         _memory_extra: &mut Self::MemoryExtra,
288         _id: AllocId,
289     ) -> InterpResult<'tcx> {
290         Ok(())
291     }
292
293     /// Return the "base" tag for the given *global* allocation: the one that is used for direct
294     /// accesses to this static/const/fn allocation. If `id` is not a global allocation,
295     /// this will return an unusable tag (i.e., accesses will be UB)!
296     ///
297     /// Expects `id` to be already canonical, if needed.
298     fn tag_global_base_pointer(memory_extra: &Self::MemoryExtra, id: AllocId) -> Self::PointerTag;
299
300     /// Executes a retagging operation
301     #[inline]
302     fn retag(
303         _ecx: &mut InterpCx<'mir, 'tcx, Self>,
304         _kind: mir::RetagKind,
305         _place: PlaceTy<'tcx, Self::PointerTag>,
306     ) -> InterpResult<'tcx> {
307         Ok(())
308     }
309
310     /// Called immediately before a new stack frame gets pushed.
311     fn init_frame_extra(
312         ecx: &mut InterpCx<'mir, 'tcx, Self>,
313         frame: Frame<'mir, 'tcx, Self::PointerTag>,
314     ) -> InterpResult<'tcx, Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>>;
315
316     /// Borrow the current thread's stack.
317     fn stack(
318         ecx: &'a InterpCx<'mir, 'tcx, Self>,
319     ) -> &'a [Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>];
320
321     /// Mutably borrow the current thread's stack.
322     fn stack_mut(
323         ecx: &'a mut InterpCx<'mir, 'tcx, Self>,
324     ) -> &'a mut Vec<Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>>;
325
326     /// Called immediately after a stack frame got pushed and its locals got initialized.
327     fn after_stack_push(_ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> {
328         Ok(())
329     }
330
331     /// Called immediately after a stack frame got popped, but before jumping back to the caller.
332     fn after_stack_pop(
333         _ecx: &mut InterpCx<'mir, 'tcx, Self>,
334         _frame: Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>,
335         _unwinding: bool,
336     ) -> InterpResult<'tcx, StackPopJump> {
337         // By default, we do not support unwinding from panics
338         Ok(StackPopJump::Normal)
339     }
340
341     fn int_to_ptr(
342         _mem: &Memory<'mir, 'tcx, Self>,
343         int: u64,
344     ) -> InterpResult<'tcx, Pointer<Self::PointerTag>> {
345         Err((if int == 0 {
346             // This is UB, seriously.
347             err_ub!(InvalidIntPointerUsage(0))
348         } else {
349             // This is just something we cannot support during const-eval.
350             err_unsup!(ReadBytesAsPointer)
351         })
352         .into())
353     }
354
355     fn ptr_to_int(
356         _mem: &Memory<'mir, 'tcx, Self>,
357         _ptr: Pointer<Self::PointerTag>,
358     ) -> InterpResult<'tcx, u64>;
359 }
360
361 // A lot of the flexibility above is just needed for `Miri`, but all "compile-time" machines
362 // (CTFE and ConstProp) use the same instance.  Here, we share that code.
363 pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) {
364     type PointerTag = ();
365     type ExtraFnVal = !;
366
367     type MemoryKind = !;
368     type MemoryMap = rustc_data_structures::fx::FxHashMap<AllocId, (MemoryKind<!>, Allocation)>;
369     const GLOBAL_KIND: Option<!> = None; // no copying of globals from `tcx` to machine memory
370
371     type AllocExtra = ();
372     type FrameExtra = ();
373
374     #[inline(always)]
375     fn enforce_alignment(_memory_extra: &Self::MemoryExtra) -> bool {
376         // We do not check for alignment to avoid having to carry an `Align`
377         // in `ConstValue::ByRef`.
378         false
379     }
380
381     #[inline(always)]
382     fn enforce_validity(_ecx: &InterpCx<$mir, $tcx, Self>) -> bool {
383         false // for now, we don't enforce validity
384     }
385
386     #[inline(always)]
387     fn call_extra_fn(
388         _ecx: &mut InterpCx<$mir, $tcx, Self>,
389         fn_val: !,
390         _args: &[OpTy<$tcx>],
391         _ret: Option<(PlaceTy<$tcx>, mir::BasicBlock)>,
392         _unwind: Option<mir::BasicBlock>,
393     ) -> InterpResult<$tcx> {
394         match fn_val {}
395     }
396
397     #[inline(always)]
398     fn init_allocation_extra<'b>(
399         _memory_extra: &Self::MemoryExtra,
400         _id: AllocId,
401         alloc: Cow<'b, Allocation>,
402         _kind: Option<MemoryKind<!>>,
403     ) -> (Cow<'b, Allocation<Self::PointerTag>>, Self::PointerTag) {
404         // We do not use a tag so we can just cheaply forward the allocation
405         (alloc, ())
406     }
407
408     #[inline(always)]
409     fn tag_global_base_pointer(
410         _memory_extra: &Self::MemoryExtra,
411         _id: AllocId,
412     ) -> Self::PointerTag {
413         ()
414     }
415
416     #[inline(always)]
417     fn init_frame_extra(
418         _ecx: &mut InterpCx<$mir, $tcx, Self>,
419         frame: Frame<$mir, $tcx>,
420     ) -> InterpResult<$tcx, Frame<$mir, $tcx>> {
421         Ok(frame)
422     }
423 }