2018-08-22 16:52:01 -03:00
|
|
|
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
|
|
|
|
|
// file at the top-level directory of this distribution and at
|
|
|
|
|
// http://rust-lang.org/COPYRIGHT.
|
|
|
|
|
//
|
|
|
|
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
|
|
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
|
|
|
// option. This file may not be copied, modified, or distributed
|
|
|
|
|
// except according to those terms.
|
|
|
|
|
|
2017-07-21 17:25:30 +02:00
|
|
|
//! This module contains everything needed to instantiate an interpreter.
|
|
|
|
|
//! This separation exists to ensure that no fancy miri features like
|
|
|
|
|
//! interpreting common C functions leak into CTFE.
|
|
|
|
|
|
2018-10-05 15:13:59 +02:00
|
|
|
use std::borrow::{Borrow, Cow};
|
2018-09-21 23:32:59 +02:00
|
|
|
use std::hash::Hash;
|
|
|
|
|
|
2018-10-18 18:01:42 +02:00
|
|
|
use rustc::hir::{self, def_id::DefId};
|
2017-12-12 17:14:49 +01:00
|
|
|
use rustc::mir;
|
2018-11-12 09:11:29 +01:00
|
|
|
use rustc::ty::{self, layout::TyLayout, query::TyCtxtAt};
|
2018-08-23 21:22:27 +02:00
|
|
|
|
2018-10-16 14:50:07 +02:00
|
|
|
use super::{
|
2018-11-04 15:14:54 +01:00
|
|
|
Allocation, AllocId, EvalResult, Scalar, AllocationExtra,
|
2018-11-02 13:17:06 +01:00
|
|
|
EvalContext, PlaceTy, MPlaceTy, OpTy, Pointer, MemoryKind,
|
2018-10-16 14:50:07 +02:00
|
|
|
};
|
|
|
|
|
|
2018-10-16 12:45:44 +02:00
|
|
|
/// Whether this kind of memory is allowed to leak
|
|
|
|
|
pub trait MayLeak: Copy {
|
|
|
|
|
fn may_leak(self) -> bool;
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-05 15:13:59 +02:00
|
|
|
/// The functionality needed by memory to manage its allocations
|
|
|
|
|
pub trait AllocMap<K: Hash + Eq, V> {
|
|
|
|
|
/// Test if the map contains the given key.
|
|
|
|
|
/// Deliberately takes `&mut` because that is sufficient, and some implementations
|
|
|
|
|
/// can be more efficient then (using `RefCell::get_mut`).
|
|
|
|
|
fn contains_key<Q: ?Sized + Hash + Eq>(&mut self, k: &Q) -> bool
|
|
|
|
|
where K: Borrow<Q>;
|
|
|
|
|
|
|
|
|
|
/// Insert new entry into the map.
|
|
|
|
|
fn insert(&mut self, k: K, v: V) -> Option<V>;
|
|
|
|
|
|
|
|
|
|
/// Remove entry from the map.
|
|
|
|
|
fn remove<Q: ?Sized + Hash + Eq>(&mut self, k: &Q) -> Option<V>
|
|
|
|
|
where K: Borrow<Q>;
|
|
|
|
|
|
|
|
|
|
/// Return data based the keys and values in the map.
|
|
|
|
|
fn filter_map_collect<T>(&self, f: impl FnMut(&K, &V) -> Option<T>) -> Vec<T>;
|
|
|
|
|
|
|
|
|
|
/// Return a reference to entry `k`. If no such entry exists, call
|
|
|
|
|
/// `vacant` and either forward its error, or add its result to the map
|
|
|
|
|
/// and return a reference to *that*.
|
|
|
|
|
fn get_or<E>(
|
|
|
|
|
&self,
|
|
|
|
|
k: K,
|
|
|
|
|
vacant: impl FnOnce() -> Result<V, E>
|
|
|
|
|
) -> Result<&V, E>;
|
|
|
|
|
|
|
|
|
|
/// Return a mutable reference to entry `k`. If no such entry exists, call
|
|
|
|
|
/// `vacant` and either forward its error, or add its result to the map
|
|
|
|
|
/// and return a reference to *that*.
|
|
|
|
|
fn get_mut_or<E>(
|
|
|
|
|
&mut self,
|
|
|
|
|
k: K,
|
|
|
|
|
vacant: impl FnOnce() -> Result<V, E>
|
|
|
|
|
) -> Result<&mut V, E>;
|
|
|
|
|
}
|
2018-08-23 19:04:33 +02:00
|
|
|
|
2017-07-21 17:25:30 +02:00
|
|
|
/// Methods of this trait signifies a point where CTFE evaluation would fail
|
2018-09-20 10:12:21 +02:00
|
|
|
/// and some use case dependent behaviour can instead be applied.
|
|
|
|
|
pub trait Machine<'a, 'mir, 'tcx>: Sized {
|
2017-07-28 16:48:43 +02:00
|
|
|
/// Additional memory kinds a machine wishes to distinguish from the builtin ones
|
2018-10-16 12:45:44 +02:00
|
|
|
type MemoryKinds: ::std::fmt::Debug + MayLeak + Eq + 'static;
|
2018-10-16 09:15:13 +02:00
|
|
|
|
|
|
|
|
/// Tag tracked alongside every pointer. This is used to implement "Stacked Borrows"
|
|
|
|
|
/// <https://www.ralfj.de/blog/2018/08/07/stacked-borrows.html>.
|
2018-10-22 17:15:42 +02:00
|
|
|
/// The `default()` is used for pointers to consts, statics, vtables and functions.
|
2018-10-16 09:15:13 +02:00
|
|
|
type PointerTag: ::std::fmt::Debug + Default + Copy + Eq + Hash + 'static;
|
|
|
|
|
|
2018-11-14 16:00:52 +01:00
|
|
|
/// Extra data stored in memory. A reference to this is available when `AllocExtra`
|
|
|
|
|
/// gets initialized, so you can e.g. have an `Rc` here if there is global state you
|
|
|
|
|
/// need access to in the `AllocExtra` hooks.
|
|
|
|
|
type MemoryExtra: Default;
|
|
|
|
|
|
2018-10-16 09:15:13 +02:00
|
|
|
/// Extra data stored in every allocation.
|
2018-11-14 16:00:52 +01:00
|
|
|
type AllocExtra: AllocationExtra<Self::PointerTag, Self::MemoryExtra>;
|
2018-08-26 12:59:59 +02:00
|
|
|
|
2018-10-05 15:13:59 +02:00
|
|
|
/// Memory's allocation map
|
|
|
|
|
type MemoryMap:
|
2018-10-16 09:15:13 +02:00
|
|
|
AllocMap<
|
|
|
|
|
AllocId,
|
|
|
|
|
(MemoryKind<Self::MemoryKinds>, Allocation<Self::PointerTag, Self::AllocExtra>)
|
|
|
|
|
> +
|
2018-10-05 15:13:59 +02:00
|
|
|
Default +
|
|
|
|
|
Clone;
|
|
|
|
|
|
2018-09-21 23:32:59 +02:00
|
|
|
/// The memory kind to use for copied statics -- or None if those are not supported.
|
|
|
|
|
/// Statics are copied under two circumstances: When they are mutated, and when
|
|
|
|
|
/// `static_with_default_tag` or `find_foreign_static` (see below) returns an owned allocation
|
|
|
|
|
/// that is added to the memory so that the work is not done twice.
|
|
|
|
|
const STATIC_KIND: Option<Self::MemoryKinds>;
|
2017-07-28 16:48:43 +02:00
|
|
|
|
2018-10-02 20:20:14 +02:00
|
|
|
/// Whether to enforce the validity invariant
|
2018-10-11 08:48:15 +02:00
|
|
|
fn enforce_validity(ecx: &EvalContext<'a, 'mir, 'tcx, Self>) -> bool;
|
2018-10-02 20:20:14 +02:00
|
|
|
|
2018-09-20 10:12:21 +02:00
|
|
|
/// Called before a basic block terminator is executed.
|
|
|
|
|
/// You can use this to detect endlessly running programs.
|
|
|
|
|
fn before_terminator(ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>) -> EvalResult<'tcx>;
|
2018-09-09 10:06:39 +02:00
|
|
|
|
2017-07-28 09:52:19 +02:00
|
|
|
/// Entry point to all function calls.
|
|
|
|
|
///
|
2018-08-23 19:04:33 +02:00
|
|
|
/// Returns either the mir to use for the call, or `None` if execution should
|
|
|
|
|
/// just proceed (which usually means this hook did all the work that the
|
|
|
|
|
/// called function should usually have done). In the latter case, it is
|
|
|
|
|
/// this hook's responsibility to call `goto_block(ret)` to advance the instruction pointer!
|
|
|
|
|
/// (This is to support functions like `__rust_maybe_catch_panic` that neither find a MIR
|
|
|
|
|
/// nor just jump to `ret`, but instead push their own stack frame.)
|
|
|
|
|
/// Passing `dest`and `ret` in the same `Option` proved very annoying when only one of them
|
|
|
|
|
/// was used.
|
2018-09-20 10:12:21 +02:00
|
|
|
fn find_fn(
|
2018-01-16 09:31:48 +01:00
|
|
|
ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
|
2017-07-21 17:25:30 +02:00
|
|
|
instance: ty::Instance<'tcx>,
|
2018-09-21 23:32:59 +02:00
|
|
|
args: &[OpTy<'tcx, Self::PointerTag>],
|
|
|
|
|
dest: Option<PlaceTy<'tcx, Self::PointerTag>>,
|
2018-08-23 19:04:33 +02:00
|
|
|
ret: Option<mir::BasicBlock>,
|
|
|
|
|
) -> EvalResult<'tcx, Option<&'mir mir::Mir<'tcx>>>;
|
2017-07-25 11:32:48 +02:00
|
|
|
|
2018-08-23 19:04:33 +02:00
|
|
|
/// Directly process an intrinsic without pushing a stack frame.
|
|
|
|
|
/// If this returns successfully, the engine will take care of jumping to the next block.
|
2018-09-20 10:12:21 +02:00
|
|
|
fn call_intrinsic(
|
2018-01-16 09:31:48 +01:00
|
|
|
ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
|
2017-07-28 13:08:27 +02:00
|
|
|
instance: ty::Instance<'tcx>,
|
2018-09-21 23:32:59 +02:00
|
|
|
args: &[OpTy<'tcx, Self::PointerTag>],
|
|
|
|
|
dest: PlaceTy<'tcx, Self::PointerTag>,
|
2017-07-28 13:08:27 +02:00
|
|
|
) -> EvalResult<'tcx>;
|
|
|
|
|
|
2018-08-23 21:22:27 +02:00
|
|
|
/// Called for read access to a foreign static item.
|
2018-09-21 23:32:59 +02:00
|
|
|
///
|
|
|
|
|
/// This will only be called once per static and machine; the result is cached in
|
2018-10-05 16:49:51 +02:00
|
|
|
/// the machine memory. (This relies on `AllocMap::get_or` being able to add the
|
|
|
|
|
/// owned allocation to the map even when the map is shared.)
|
2018-09-20 10:12:21 +02:00
|
|
|
fn find_foreign_static(
|
2018-08-23 21:22:27 +02:00
|
|
|
def_id: DefId,
|
2018-11-15 12:03:38 +01:00
|
|
|
tcx: TyCtxtAt<'a, 'tcx, 'tcx>,
|
|
|
|
|
memory_extra: &Self::MemoryExtra,
|
2018-10-16 09:15:13 +02:00
|
|
|
) -> EvalResult<'tcx, Cow<'tcx, Allocation<Self::PointerTag, Self::AllocExtra>>>;
|
2018-09-21 23:32:59 +02:00
|
|
|
|
|
|
|
|
/// Called to turn an allocation obtained from the `tcx` into one that has
|
2018-10-22 17:15:42 +02:00
|
|
|
/// the right type for this machine.
|
2018-09-21 23:32:59 +02:00
|
|
|
///
|
|
|
|
|
/// This should avoid copying if no work has to be done! If this returns an owned
|
2018-10-22 17:15:42 +02:00
|
|
|
/// allocation (because a copy had to be done to add tags or metadata), machine memory will
|
2018-10-05 16:49:51 +02:00
|
|
|
/// cache the result. (This relies on `AllocMap::get_or` being able to add the
|
|
|
|
|
/// owned allocation to the map even when the map is shared.)
|
2018-11-15 12:03:38 +01:00
|
|
|
fn adjust_static_allocation<'b>(
|
|
|
|
|
alloc: &'b Allocation,
|
|
|
|
|
memory_extra: &Self::MemoryExtra,
|
|
|
|
|
) -> Cow<'b, Allocation<Self::PointerTag, Self::AllocExtra>>;
|
2018-08-23 21:22:27 +02:00
|
|
|
|
2018-08-28 01:14:29 +02:00
|
|
|
/// Called for all binary operations on integer(-like) types when one operand is a pointer
|
|
|
|
|
/// value, and for the `Offset` operation that is inherently about pointers.
|
2017-07-25 11:32:48 +02:00
|
|
|
///
|
2017-08-01 11:11:57 +02:00
|
|
|
/// Returns a (value, overflowed) pair if the operation succeeded
|
2018-09-20 10:12:21 +02:00
|
|
|
fn ptr_op(
|
2018-01-16 09:31:48 +01:00
|
|
|
ecx: &EvalContext<'a, 'mir, 'tcx, Self>,
|
2017-07-25 11:32:48 +02:00
|
|
|
bin_op: mir::BinOp,
|
2018-09-21 23:32:59 +02:00
|
|
|
left: Scalar<Self::PointerTag>,
|
2018-08-13 16:14:22 +02:00
|
|
|
left_layout: TyLayout<'tcx>,
|
2018-09-21 23:32:59 +02:00
|
|
|
right: Scalar<Self::PointerTag>,
|
2018-08-13 16:14:22 +02:00
|
|
|
right_layout: TyLayout<'tcx>,
|
2018-09-21 23:32:59 +02:00
|
|
|
) -> EvalResult<'tcx, (Scalar<Self::PointerTag>, bool)>;
|
2017-07-28 16:48:43 +02:00
|
|
|
|
2018-10-16 14:50:07 +02:00
|
|
|
/// Heap allocations via the `box` keyword.
|
2018-09-20 10:12:21 +02:00
|
|
|
fn box_alloc(
|
2018-01-16 09:31:48 +01:00
|
|
|
ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
|
2018-09-21 23:32:59 +02:00
|
|
|
dest: PlaceTy<'tcx, Self::PointerTag>,
|
2017-09-25 15:55:21 +02:00
|
|
|
) -> EvalResult<'tcx>;
|
2017-09-15 13:02:33 +02:00
|
|
|
|
2018-10-22 17:15:42 +02:00
|
|
|
/// Add the tag for a newly allocated pointer.
|
|
|
|
|
fn tag_new_allocation(
|
|
|
|
|
ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
|
|
|
|
|
ptr: Pointer,
|
|
|
|
|
kind: MemoryKind<Self::MemoryKinds>,
|
|
|
|
|
) -> EvalResult<'tcx, Pointer<Self::PointerTag>>;
|
|
|
|
|
|
2018-10-16 17:00:39 +02:00
|
|
|
/// Executed when evaluating the `*` operator: Following a reference.
|
2018-11-02 13:17:06 +01:00
|
|
|
/// This has the chance to adjust the tag. It should not change anything else!
|
2018-10-18 18:01:42 +02:00
|
|
|
/// `mutability` can be `None` in case a raw ptr is being dereferenced.
|
2018-10-19 17:11:23 +02:00
|
|
|
#[inline]
|
2018-10-16 17:00:39 +02:00
|
|
|
fn tag_dereference(
|
2018-10-19 17:11:23 +02:00
|
|
|
_ecx: &EvalContext<'a, 'mir, 'tcx, Self>,
|
2018-11-02 13:17:06 +01:00
|
|
|
place: MPlaceTy<'tcx, Self::PointerTag>,
|
2018-10-19 17:11:23 +02:00
|
|
|
_mutability: Option<hir::Mutability>,
|
2018-11-02 13:17:06 +01:00
|
|
|
) -> EvalResult<'tcx, Scalar<Self::PointerTag>> {
|
|
|
|
|
Ok(place.ptr)
|
2018-10-19 17:11:23 +02:00
|
|
|
}
|
2018-10-16 17:00:39 +02:00
|
|
|
|
2018-11-06 11:04:10 +01:00
|
|
|
/// Execute a retagging operation
|
2018-10-16 14:50:07 +02:00
|
|
|
#[inline]
|
2018-10-24 11:47:17 +02:00
|
|
|
fn retag(
|
2018-01-16 09:31:48 +01:00
|
|
|
_ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
|
2018-10-24 11:47:17 +02:00
|
|
|
_fn_entry: bool,
|
|
|
|
|
_place: PlaceTy<'tcx, Self::PointerTag>,
|
2017-12-14 11:36:28 +01:00
|
|
|
) -> EvalResult<'tcx> {
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
2018-11-06 11:04:10 +01:00
|
|
|
|
|
|
|
|
/// Execute an escape-to-raw operation
|
|
|
|
|
#[inline]
|
|
|
|
|
fn escape_to_raw(
|
|
|
|
|
_ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
|
|
|
|
|
_ptr: OpTy<'tcx, Self::PointerTag>,
|
|
|
|
|
) -> EvalResult<'tcx> {
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
2017-07-21 17:25:30 +02:00
|
|
|
}
|