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