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