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.
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.
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.
15 use std::borrow::{Borrow, Cow};
18 use rustc::hir::{self, def_id::DefId};
20 use rustc::ty::{self, layout::TyLayout, query::TyCtxtAt};
23 Allocation, AllocId, EvalResult, Scalar, AllocationExtra,
24 EvalContext, PlaceTy, MPlaceTy, OpTy, Pointer, MemoryKind,
27 /// Whether this kind of memory is allowed to leak
28 pub trait MayLeak: Copy {
29 fn may_leak(self) -> bool;
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
40 /// Insert new entry into the map.
41 fn insert(&mut self, k: K, v: V) -> Option<V>;
43 /// Remove entry from the map.
44 fn remove<Q: ?Sized + Hash + Eq>(&mut self, k: &Q) -> Option<V>
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>;
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*.
56 vacant: impl FnOnce() -> Result<V, E>
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*.
65 vacant: impl FnOnce() -> Result<V, E>
66 ) -> Result<&mut V, E>;
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;
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;
80 /// Extra data stored in memory. A reference to this is available when `AllocExtra`
81 /// gets initialized, so you can e.g. have an `Rc` here if there is global state you
82 /// need access to in the `AllocExtra` hooks.
83 type MemoryExtra: Default;
85 /// Extra data stored in every allocation.
86 type AllocExtra: AllocationExtra<Self::PointerTag, Self::MemoryExtra>;
88 /// Memory's allocation map
92 (MemoryKind<Self::MemoryKinds>, Allocation<Self::PointerTag, Self::AllocExtra>)
97 /// The memory kind to use for copied statics -- or None if those are not supported.
98 /// Statics are copied under two circumstances: When they are mutated, and when
99 /// `static_with_default_tag` or `find_foreign_static` (see below) returns an owned allocation
100 /// that is added to the memory so that the work is not done twice.
101 const STATIC_KIND: Option<Self::MemoryKinds>;
103 /// Whether to enforce the validity invariant
104 fn enforce_validity(ecx: &EvalContext<'a, 'mir, 'tcx, Self>) -> bool;
106 /// Called before a basic block terminator is executed.
107 /// You can use this to detect endlessly running programs.
108 fn before_terminator(ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>) -> EvalResult<'tcx>;
110 /// Entry point to all function calls.
112 /// Returns either the mir to use for the call, or `None` if execution should
113 /// just proceed (which usually means this hook did all the work that the
114 /// called function should usually have done). In the latter case, it is
115 /// this hook's responsibility to call `goto_block(ret)` to advance the instruction pointer!
116 /// (This is to support functions like `__rust_maybe_catch_panic` that neither find a MIR
117 /// nor just jump to `ret`, but instead push their own stack frame.)
118 /// Passing `dest`and `ret` in the same `Option` proved very annoying when only one of them
121 ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
122 instance: ty::Instance<'tcx>,
123 args: &[OpTy<'tcx, Self::PointerTag>],
124 dest: Option<PlaceTy<'tcx, Self::PointerTag>>,
125 ret: Option<mir::BasicBlock>,
126 ) -> EvalResult<'tcx, Option<&'mir mir::Mir<'tcx>>>;
128 /// Directly process an intrinsic without pushing a stack frame.
129 /// If this returns successfully, the engine will take care of jumping to the next block.
131 ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
132 instance: ty::Instance<'tcx>,
133 args: &[OpTy<'tcx, Self::PointerTag>],
134 dest: PlaceTy<'tcx, Self::PointerTag>,
135 ) -> EvalResult<'tcx>;
137 /// Called for read access to a foreign static item.
139 /// This will only be called once per static and machine; the result is cached in
140 /// the machine memory. (This relies on `AllocMap::get_or` being able to add the
141 /// owned allocation to the map even when the map is shared.)
142 fn find_foreign_static(
143 tcx: TyCtxtAt<'a, 'tcx, 'tcx>,
145 ) -> EvalResult<'tcx, Cow<'tcx, Allocation<Self::PointerTag, Self::AllocExtra>>>;
147 /// Called to turn an allocation obtained from the `tcx` into one that has
148 /// the right type for this machine.
150 /// This should avoid copying if no work has to be done! If this returns an owned
151 /// allocation (because a copy had to be done to add tags or metadata), machine memory will
152 /// cache the result. (This relies on `AllocMap::get_or` being able to add the
153 /// owned allocation to the map even when the map is shared.)
154 fn adjust_static_allocation(
155 alloc: &'_ Allocation
156 ) -> Cow<'_, Allocation<Self::PointerTag, Self::AllocExtra>>;
158 /// Called for all binary operations on integer(-like) types when one operand is a pointer
159 /// value, and for the `Offset` operation that is inherently about pointers.
161 /// Returns a (value, overflowed) pair if the operation succeeded
163 ecx: &EvalContext<'a, 'mir, 'tcx, Self>,
165 left: Scalar<Self::PointerTag>,
166 left_layout: TyLayout<'tcx>,
167 right: Scalar<Self::PointerTag>,
168 right_layout: TyLayout<'tcx>,
169 ) -> EvalResult<'tcx, (Scalar<Self::PointerTag>, bool)>;
171 /// Heap allocations via the `box` keyword.
173 ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
174 dest: PlaceTy<'tcx, Self::PointerTag>,
175 ) -> EvalResult<'tcx>;
177 /// Add the tag for a newly allocated pointer.
178 fn tag_new_allocation(
179 ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
181 kind: MemoryKind<Self::MemoryKinds>,
182 ) -> EvalResult<'tcx, Pointer<Self::PointerTag>>;
184 /// Executed when evaluating the `*` operator: Following a reference.
185 /// This has the chance to adjust the tag. It should not change anything else!
186 /// `mutability` can be `None` in case a raw ptr is being dereferenced.
189 _ecx: &EvalContext<'a, 'mir, 'tcx, Self>,
190 place: MPlaceTy<'tcx, Self::PointerTag>,
191 _mutability: Option<hir::Mutability>,
192 ) -> EvalResult<'tcx, Scalar<Self::PointerTag>> {
196 /// Execute a retagging operation
199 _ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
201 _place: PlaceTy<'tcx, Self::PointerTag>,
202 ) -> EvalResult<'tcx> {
206 /// Execute an escape-to-raw operation
209 _ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
210 _ptr: OpTy<'tcx, Self::PointerTag>,
211 ) -> EvalResult<'tcx> {