Move large chunks of miri from rustc::mir::interpret to rustc_mir::interpret

This commit is contained in:
Oliver Schneider 2017-12-12 17:14:49 +01:00
parent 8c2ec689c1
commit acac58502b
No known key found for this signature in database
GPG Key ID: A69F8D225B3AD7D9
26 changed files with 1103 additions and 1053 deletions

View File

@ -293,6 +293,7 @@ before_deploy:
cp -r obj/build/dist/* deploy/$TRAVIS_COMMIT;
fi
- travis_retry gem update --system
- ls -la deploy/$TRAVIS_COMMIT
deploy:
- provider: s3

13
src/Cargo.lock generated
View File

@ -961,6 +961,11 @@ name = "lazy_static"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "lazy_static"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "lazycell"
version = "0.5.1"
@ -1618,9 +1623,7 @@ dependencies = [
"fmt_macros 0.0.0",
"graphviz 0.0.0",
"jobserver 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
"log_settings 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc_apfloat 0.0.0",
@ -1863,9 +1866,14 @@ name = "rustc_mir"
version = "0.0.0"
dependencies = [
"bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"graphviz 0.0.0",
"lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
"log_settings 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc 0.0.0",
"rustc_apfloat 0.0.0",
"rustc_const_eval 0.0.0",
"rustc_const_math 0.0.0",
"rustc_data_structures 0.0.0",
@ -2763,6 +2771,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum kuchiki 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e03098e8e719c92b7794515dfd5c1724e2b12f5ce1788e61cfa4663f82eba8d8"
"checksum languageserver-types 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "773e175c945800aeea4c21c04090bcb9db987b1a566ad9c6f569972299950e3e"
"checksum lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "76f033c7ad61445c5b347c7382dd1237847eb1bce590fe50365dcb33d546be73"
"checksum lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c8f31047daa365f19be14b47c29df4f7c3b581832407daabe6ae77397619237d"
"checksum lazycell 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3b585b7a6811fb03aa10e74b278a0f00f8dd9b45dc681f148bb29fa5cb61859b"
"checksum libc 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)" = "36fbc8a8929c632868295d0178dd8f63fc423fd7537ad0738372bd010b3ac9b0"
"checksum libgit2-sys 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)" = "6f74b4959cef96898f5123148724fc7dee043b9a6b99f219d948851bfbe53cb2"

View File

@ -24,8 +24,6 @@ rustc_errors = { path = "../librustc_errors" }
serialize = { path = "../libserialize" }
syntax = { path = "../libsyntax" }
syntax_pos = { path = "../libsyntax_pos" }
log_settings = "0.1.1"
lazy_static = "0.2.8"
regex = "0.2.2"
backtrace = "0.3.3"
byteorder = { version = "1.1", features = ["i128"]}

View File

@ -91,10 +91,7 @@ extern crate jobserver;
extern crate serialize as rustc_serialize; // used by deriving
extern crate rustc_apfloat;
extern crate log_settings;
extern crate byteorder;
#[macro_use]
extern crate lazy_static;
extern crate regex;
extern crate backtrace;

View File

@ -1,288 +0,0 @@
use ty::{self, TyCtxt, Ty, Instance};
use ty::layout::{self, LayoutOf};
use mir;
use syntax::ast::Mutability;
use syntax::codemap::Span;
use super::{EvalResult, EvalError, EvalErrorKind, GlobalId, Place, Value, PrimVal, EvalContext,
StackPopCleanup, PtrAndAlign, ValTy, HasMemory};
use rustc_const_math::ConstInt;
use std::fmt;
use std::error::Error;
pub fn eval_body<'a, 'tcx>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
instance: Instance<'tcx>,
param_env: ty::ParamEnv<'tcx>,
) -> (EvalResult<'tcx, (PtrAndAlign, Ty<'tcx>)>, EvalContext<'a, 'tcx, CompileTimeEvaluator>) {
debug!("eval_body: {:?}, {:?}", instance, param_env);
let limits = super::ResourceLimits::default();
let mut ecx = EvalContext::new(tcx, param_env, limits, CompileTimeEvaluator, ());
let cid = GlobalId {
instance,
promoted: None,
};
let try = (|| {
if ecx.tcx.has_attr(instance.def_id(), "linkage") {
return Err(ConstEvalError::NotConst("extern global".to_string()).into());
}
// FIXME(eddyb) use `Instance::ty` when it becomes available.
let instance_ty =
ecx.monomorphize(instance.def.def_ty(tcx), instance.substs);
if tcx.interpret_interner.borrow().get_cached(cid).is_none() {
let mir = ecx.load_mir(instance.def)?;
let layout = ecx.layout_of(instance_ty)?;
assert!(!layout.is_unsized());
let ptr = ecx.memory.allocate(
layout.size.bytes(),
layout.align.abi(),
None,
)?;
tcx.interpret_interner.borrow_mut().cache(
cid,
PtrAndAlign {
ptr: ptr.into(),
aligned: !layout.is_packed(),
},
);
let cleanup = StackPopCleanup::MarkStatic(Mutability::Immutable);
let name = ty::tls::with(|tcx| tcx.item_path_str(instance.def_id()));
trace!("const_eval: pushing stack frame for global: {}", name);
ecx.push_stack_frame(
instance,
mir.span,
mir,
Place::from_ptr(ptr),
cleanup.clone(),
)?;
while ecx.step()? {}
// reinsert the stack frame so any future queries have the correct substs
ecx.push_stack_frame(
instance,
mir.span,
mir,
Place::from_ptr(ptr),
cleanup,
)?;
}
let value = tcx.interpret_interner.borrow().get_cached(cid).expect("global not cached");
Ok((value, instance_ty))
})();
(try, ecx)
}
pub fn eval_body_as_integer<'a, 'tcx>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
instance: Instance<'tcx>,
) -> EvalResult<'tcx, ConstInt> {
let (ptr_ty, ecx) = eval_body(tcx, instance, param_env);
let (ptr, ty) = ptr_ty?;
let prim = match ecx.read_maybe_aligned(ptr.aligned, |ectx| ectx.try_read_value(ptr.ptr, ty))? {
Some(Value::ByVal(prim)) => prim.to_bytes()?,
_ => return err!(TypeNotPrimitive(ty)),
};
use syntax::ast::{IntTy, UintTy};
use ty::TypeVariants::*;
use rustc_const_math::{ConstIsize, ConstUsize};
Ok(match ty.sty {
TyInt(IntTy::I8) => ConstInt::I8(prim as i128 as i8),
TyInt(IntTy::I16) => ConstInt::I16(prim as i128 as i16),
TyInt(IntTy::I32) => ConstInt::I32(prim as i128 as i32),
TyInt(IntTy::I64) => ConstInt::I64(prim as i128 as i64),
TyInt(IntTy::I128) => ConstInt::I128(prim as i128),
TyInt(IntTy::Is) => ConstInt::Isize(
ConstIsize::new(prim as i128 as i64, tcx.sess.target.isize_ty)
.expect("miri should already have errored"),
),
TyUint(UintTy::U8) => ConstInt::U8(prim as u8),
TyUint(UintTy::U16) => ConstInt::U16(prim as u16),
TyUint(UintTy::U32) => ConstInt::U32(prim as u32),
TyUint(UintTy::U64) => ConstInt::U64(prim as u64),
TyUint(UintTy::U128) => ConstInt::U128(prim),
TyUint(UintTy::Us) => ConstInt::Usize(
ConstUsize::new(prim as u64, tcx.sess.target.usize_ty)
.expect("miri should already have errored"),
),
_ => {
return Err(
ConstEvalError::NeedsRfc(
"evaluating anything other than isize/usize during typeck".to_string(),
).into(),
)
}
})
}
pub struct CompileTimeEvaluator;
impl<'tcx> Into<EvalError<'tcx>> for ConstEvalError {
fn into(self) -> EvalError<'tcx> {
EvalErrorKind::MachineError(Box::new(self)).into()
}
}
#[derive(Clone, Debug)]
enum ConstEvalError {
NeedsRfc(String),
NotConst(String),
}
impl fmt::Display for ConstEvalError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::ConstEvalError::*;
match *self {
NeedsRfc(ref msg) => {
write!(
f,
"\"{}\" needs an rfc before being allowed inside constants",
msg
)
}
NotConst(ref msg) => write!(f, "Cannot evaluate within constants: \"{}\"", msg),
}
}
}
impl Error for ConstEvalError {
fn description(&self) -> &str {
use self::ConstEvalError::*;
match *self {
NeedsRfc(_) => "this feature needs an rfc before being allowed inside constants",
NotConst(_) => "this feature is not compatible with constant evaluation",
}
}
fn cause(&self) -> Option<&Error> {
None
}
}
impl<'tcx> super::Machine<'tcx> for CompileTimeEvaluator {
type MemoryData = ();
type MemoryKinds = !;
fn eval_fn_call<'a>(
ecx: &mut EvalContext<'a, 'tcx, Self>,
instance: ty::Instance<'tcx>,
destination: Option<(Place, mir::BasicBlock)>,
_args: &[ValTy<'tcx>],
span: Span,
_sig: ty::FnSig<'tcx>,
) -> EvalResult<'tcx, bool> {
debug!("eval_fn_call: {:?}", instance);
if !ecx.tcx.is_const_fn(instance.def_id()) {
return Err(
ConstEvalError::NotConst(format!("calling non-const fn `{}`", instance)).into(),
);
}
let mir = match ecx.load_mir(instance.def) {
Ok(mir) => mir,
Err(EvalError { kind: EvalErrorKind::NoMirFor(path), .. }) => {
// some simple things like `malloc` might get accepted in the future
return Err(
ConstEvalError::NeedsRfc(format!("calling extern function `{}`", path))
.into(),
);
}
Err(other) => return Err(other),
};
let (return_place, return_to_block) = match destination {
Some((place, block)) => (place, StackPopCleanup::Goto(block)),
None => (Place::undef(), StackPopCleanup::None),
};
ecx.push_stack_frame(
instance,
span,
mir,
return_place,
return_to_block,
)?;
Ok(false)
}
fn call_intrinsic<'a>(
ecx: &mut EvalContext<'a, 'tcx, Self>,
instance: ty::Instance<'tcx>,
_args: &[ValTy<'tcx>],
dest: Place,
dest_layout: layout::TyLayout<'tcx>,
target: mir::BasicBlock,
) -> EvalResult<'tcx> {
let substs = instance.substs;
let intrinsic_name = &ecx.tcx.item_name(instance.def_id())[..];
match intrinsic_name {
"min_align_of" => {
let elem_ty = substs.type_at(0);
let elem_align = ecx.layout_of(elem_ty)?.align.abi();
let align_val = PrimVal::from_u128(elem_align as u128);
ecx.write_primval(dest, align_val, dest_layout.ty)?;
}
"size_of" => {
let ty = substs.type_at(0);
let size = ecx.layout_of(ty)?.size.bytes() as u128;
ecx.write_primval(dest, PrimVal::from_u128(size), dest_layout.ty)?;
}
name => return Err(ConstEvalError::NeedsRfc(format!("calling intrinsic `{}`", name)).into()),
}
ecx.goto_block(target);
// Since we pushed no stack frame, the main loop will act
// as if the call just completed and it's returning to the
// current frame.
Ok(())
}
fn try_ptr_op<'a>(
_ecx: &EvalContext<'a, 'tcx, Self>,
_bin_op: mir::BinOp,
left: PrimVal,
_left_ty: Ty<'tcx>,
right: PrimVal,
_right_ty: Ty<'tcx>,
) -> EvalResult<'tcx, Option<(PrimVal, bool)>> {
if left.is_bytes() && right.is_bytes() {
Ok(None)
} else {
Err(
ConstEvalError::NeedsRfc("Pointer arithmetic or comparison".to_string()).into(),
)
}
}
fn mark_static_initialized(m: !) -> EvalResult<'tcx> {
m
}
fn box_alloc<'a>(
_ecx: &mut EvalContext<'a, 'tcx, Self>,
_ty: Ty<'tcx>,
_dest: Place,
) -> EvalResult<'tcx> {
Err(
ConstEvalError::NeedsRfc("Heap allocations via `box` keyword".to_string()).into(),
)
}
fn global_item_with_linkage<'a>(
_ecx: &mut EvalContext<'a, 'tcx, Self>,
_instance: ty::Instance<'tcx>,
_mutability: Mutability,
) -> EvalResult<'tcx> {
Err(
ConstEvalError::NotConst("statics with `linkage` attribute".to_string()).into(),
)
}
}

View File

@ -5,38 +5,266 @@ macro_rules! err {
($($tt:tt)*) => { Err($crate::mir::interpret::EvalErrorKind::$($tt)*.into()) };
}
mod cast;
mod const_eval;
mod error;
mod eval_context;
mod place;
mod validation;
mod machine;
mod memory;
mod operator;
mod range_map;
mod step;
mod terminator;
mod traits;
mod value;
pub use self::error::{EvalError, EvalResult, EvalErrorKind};
pub use self::eval_context::{EvalContext, Frame, ResourceLimits, StackPopCleanup, DynamicLifetime,
TyAndPacked, PtrAndAlign, ValTy};
pub use self::value::{PrimVal, PrimValKind, Value, Pointer, PtrAndAlign, bytes_to_f32, bytes_to_f64};
pub use self::place::{Place, PlaceExtra, GlobalId};
use std::collections::BTreeMap;
use ty::layout::HasDataLayout;
use std::fmt;
use ty::layout;
use mir;
use ty;
use middle::region;
use std::iter;
pub use self::memory::{AllocId, Memory, MemoryPointer, MemoryKind, HasMemory, AccessKind, Allocation};
#[derive(Clone, Debug, PartialEq)]
pub enum Lock {
NoLock,
WriteLock(DynamicLifetime),
/// This should never be empty -- that would be a read lock held and nobody there to release it...
ReadLock(Vec<DynamicLifetime>),
}
use self::memory::{PointerArithmetic, Lock};
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct DynamicLifetime {
pub frame: usize,
pub region: Option<region::Scope>, // "None" indicates "until the function ends"
}
use self::range_map::RangeMap;
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum AccessKind {
Read,
Write,
}
pub use self::value::{PrimVal, PrimValKind, Value, Pointer};
/// Uniquely identifies a specific constant or static.
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub struct GlobalId<'tcx> {
/// For a constant or static, the `Instance` of the item itself.
/// For a promoted global, the `Instance` of the function they belong to.
pub instance: ty::Instance<'tcx>,
pub use self::const_eval::{eval_body_as_integer, eval_body, CompileTimeEvaluator};
/// The index for promoted globals within their function's `Mir`.
pub promoted: Option<mir::Promoted>,
}
pub use self::machine::Machine;
////////////////////////////////////////////////////////////////////////////////
// Pointer arithmetic
////////////////////////////////////////////////////////////////////////////////
pub use self::validation::{ValidationQuery, AbsPlace};
pub trait PointerArithmetic: layout::HasDataLayout {
// These are not supposed to be overriden.
//// Trunace the given value to the pointer size; also return whether there was an overflow
fn truncate_to_ptr(self, val: u128) -> (u64, bool) {
let max_ptr_plus_1 = 1u128 << self.data_layout().pointer_size.bits();
((val % max_ptr_plus_1) as u64, val >= max_ptr_plus_1)
}
// Overflow checking only works properly on the range from -u64 to +u64.
fn overflowing_signed_offset(self, val: u64, i: i128) -> (u64, bool) {
// FIXME: is it possible to over/underflow here?
if i < 0 {
// trickery to ensure that i64::min_value() works fine
// this formula only works for true negative values, it panics for zero!
let n = u64::max_value() - (i as u64) + 1;
val.overflowing_sub(n)
} else {
self.overflowing_offset(val, i as u64)
}
}
fn overflowing_offset(self, val: u64, i: u64) -> (u64, bool) {
let (res, over1) = val.overflowing_add(i);
let (res, over2) = self.truncate_to_ptr(res as u128);
(res, over1 || over2)
}
fn signed_offset<'tcx>(self, val: u64, i: i64) -> EvalResult<'tcx, u64> {
let (res, over) = self.overflowing_signed_offset(val, i as i128);
if over { err!(OverflowingMath) } else { Ok(res) }
}
fn offset<'tcx>(self, val: u64, i: u64) -> EvalResult<'tcx, u64> {
let (res, over) = self.overflowing_offset(val, i);
if over { err!(OverflowingMath) } else { Ok(res) }
}
fn wrapping_signed_offset(self, val: u64, i: i64) -> u64 {
self.overflowing_signed_offset(val, i as i128).0
}
}
impl<T: layout::HasDataLayout> PointerArithmetic for T {}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct MemoryPointer {
pub alloc_id: AllocId,
pub offset: u64,
}
impl<'tcx> MemoryPointer {
pub fn new(alloc_id: AllocId, offset: u64) -> Self {
MemoryPointer { alloc_id, offset }
}
pub(crate) fn wrapping_signed_offset<C: HasDataLayout>(self, i: i64, cx: C) -> Self {
MemoryPointer::new(
self.alloc_id,
cx.data_layout().wrapping_signed_offset(self.offset, i),
)
}
pub fn overflowing_signed_offset<C: HasDataLayout>(self, i: i128, cx: C) -> (Self, bool) {
let (res, over) = cx.data_layout().overflowing_signed_offset(self.offset, i);
(MemoryPointer::new(self.alloc_id, res), over)
}
pub(crate) fn signed_offset<C: HasDataLayout>(self, i: i64, cx: C) -> EvalResult<'tcx, Self> {
Ok(MemoryPointer::new(
self.alloc_id,
cx.data_layout().signed_offset(self.offset, i)?,
))
}
pub fn overflowing_offset<C: HasDataLayout>(self, i: u64, cx: C) -> (Self, bool) {
let (res, over) = cx.data_layout().overflowing_offset(self.offset, i);
(MemoryPointer::new(self.alloc_id, res), over)
}
pub fn offset<C: HasDataLayout>(self, i: u64, cx: C) -> EvalResult<'tcx, Self> {
Ok(MemoryPointer::new(
self.alloc_id,
cx.data_layout().offset(self.offset, i)?,
))
}
}
#[derive(Copy, Clone, Eq, Hash, Ord, PartialEq, PartialOrd, Debug)]
pub struct AllocId(pub u64);
impl fmt::Display for AllocId {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(Debug, Eq, PartialEq, Hash)]
pub struct Allocation {
/// The actual bytes of the allocation.
/// Note that the bytes of a pointer represent the offset of the pointer
pub bytes: Vec<u8>,
/// Maps from byte addresses to allocations.
/// Only the first byte of a pointer is inserted into the map.
pub relocations: BTreeMap<u64, AllocId>,
/// Denotes undefined memory. Reading from undefined memory is forbidden in miri
pub undef_mask: UndefMask,
/// The alignment of the allocation to detect unaligned reads.
pub align: u64,
}
impl Allocation {
pub fn from_bytes(slice: &[u8]) -> Self {
let mut undef_mask = UndefMask::new(0);
undef_mask.grow(slice.len() as u64, true);
Self {
bytes: slice.to_owned(),
relocations: BTreeMap::new(),
undef_mask,
align: 1,
}
}
}
////////////////////////////////////////////////////////////////////////////////
// Undefined byte tracking
////////////////////////////////////////////////////////////////////////////////
type Block = u64;
const BLOCK_SIZE: u64 = 64;
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub struct UndefMask {
blocks: Vec<Block>,
len: u64,
}
impl UndefMask {
pub fn new(size: u64) -> Self {
let mut m = UndefMask {
blocks: vec![],
len: 0,
};
m.grow(size, false);
m
}
/// Check whether the range `start..end` (end-exclusive) is entirely defined.
pub fn is_range_defined(&self, start: u64, end: u64) -> bool {
if end > self.len {
return false;
}
for i in start..end {
if !self.get(i) {
return false;
}
}
true
}
pub fn set_range(&mut self, start: u64, end: u64, new_state: bool) {
let len = self.len;
if end > len {
self.grow(end - len, new_state);
}
self.set_range_inbounds(start, end, new_state);
}
pub fn set_range_inbounds(&mut self, start: u64, end: u64, new_state: bool) {
for i in start..end {
self.set(i, new_state);
}
}
pub fn get(&self, i: u64) -> bool {
let (block, bit) = bit_index(i);
(self.blocks[block] & 1 << bit) != 0
}
pub fn set(&mut self, i: u64, new_state: bool) {
let (block, bit) = bit_index(i);
if new_state {
self.blocks[block] |= 1 << bit;
} else {
self.blocks[block] &= !(1 << bit);
}
}
pub fn grow(&mut self, amount: u64, new_state: bool) {
let unused_trailing_bits = self.blocks.len() as u64 * BLOCK_SIZE - self.len;
if amount > unused_trailing_bits {
let additional_blocks = amount / BLOCK_SIZE + 1;
assert_eq!(additional_blocks as usize as u64, additional_blocks);
self.blocks.extend(
iter::repeat(0).take(additional_blocks as usize),
);
}
let start = self.len;
self.len += amount;
self.set_range_inbounds(start, start + amount, new_state);
}
}
fn bit_index(bits: u64) -> (usize, usize) {
let a = bits / BLOCK_SIZE;
let b = bits % BLOCK_SIZE;
assert_eq!(a as usize as u64, a);
assert_eq!(b as usize as u64, b);
(a as usize, b as usize)
}

View File

@ -2,18 +2,37 @@
use ty::layout::HasDataLayout;
use super::{EvalResult, Memory, MemoryPointer, HasMemory, PointerArithmetic, Machine, PtrAndAlign};
use super::{EvalResult, MemoryPointer, PointerArithmetic};
use syntax::ast::FloatTy;
use rustc_const_math::ConstFloat;
pub(super) fn bytes_to_f32(bits: u128) -> ConstFloat {
#[derive(Copy, Clone, Debug)]
pub struct PtrAndAlign {
pub ptr: Pointer,
/// Remember whether this place is *supposed* to be aligned.
pub aligned: bool,
}
impl PtrAndAlign {
pub fn to_ptr<'tcx>(self) -> EvalResult<'tcx, MemoryPointer> {
self.ptr.to_ptr()
}
pub fn offset<'tcx, C: HasDataLayout>(self, i: u64, cx: C) -> EvalResult<'tcx, Self> {
Ok(PtrAndAlign {
ptr: self.ptr.offset(i, cx)?,
aligned: self.aligned,
})
}
}
pub fn bytes_to_f32(bits: u128) -> ConstFloat {
ConstFloat {
bits,
ty: FloatTy::F32,
}
}
pub(super) fn bytes_to_f64(bits: u128) -> ConstFloat {
pub fn bytes_to_f64(bits: u128) -> ConstFloat {
ConstFloat {
bits,
ty: FloatTy::F64,
@ -168,76 +187,6 @@ impl<'a, 'tcx: 'a> Value {
pub fn by_ref(ptr: Pointer) -> Self {
Value::ByRef(PtrAndAlign { ptr, aligned: true })
}
/// Convert the value into a pointer (or a pointer-sized integer). If the value is a ByRef,
/// this may have to perform a load.
pub fn into_ptr<M: Machine<'tcx>>(
&self,
mem: &Memory<'a, 'tcx, M>,
) -> EvalResult<'tcx, Pointer> {
use self::Value::*;
Ok(match *self {
ByRef(PtrAndAlign { ptr, aligned }) => {
mem.read_maybe_aligned(aligned, |mem| mem.read_ptr_sized_unsigned(ptr.to_ptr()?))?
}
ByVal(ptr) |
ByValPair(ptr, _) => ptr,
}.into())
}
pub(super) fn into_ptr_vtable_pair<M: Machine<'tcx>>(
&self,
mem: &Memory<'a, 'tcx, M>,
) -> EvalResult<'tcx, (Pointer, MemoryPointer)> {
use self::Value::*;
match *self {
ByRef(PtrAndAlign {
ptr: ref_ptr,
aligned,
}) => {
mem.read_maybe_aligned(aligned, |mem| {
let ptr = mem.read_ptr_sized_unsigned(ref_ptr.to_ptr()?)?.into();
let vtable = mem.read_ptr_sized_unsigned(
ref_ptr.offset(mem.pointer_size(), &mem.tcx.data_layout)?.to_ptr()?,
)?.to_ptr()?;
Ok((ptr, vtable))
})
}
ByValPair(ptr, vtable) => Ok((ptr.into(), vtable.to_ptr()?)),
ByVal(PrimVal::Undef) => err!(ReadUndefBytes),
_ => bug!("expected ptr and vtable, got {:?}", self),
}
}
pub(super) fn into_slice<M: Machine<'tcx>>(
&self,
mem: &Memory<'a, 'tcx, M>,
) -> EvalResult<'tcx, (Pointer, u64)> {
use self::Value::*;
match *self {
ByRef(PtrAndAlign {
ptr: ref_ptr,
aligned,
}) => {
mem.read_maybe_aligned(aligned, |mem| {
let ptr = mem.read_ptr_sized_unsigned(ref_ptr.to_ptr()?)?.into();
let len = mem.read_ptr_sized_unsigned(
ref_ptr.offset(mem.pointer_size(), &mem.tcx.data_layout)?.to_ptr()?,
)?.to_bytes()? as u64;
Ok((ptr, len))
})
}
ByValPair(ptr, val) => {
let len = val.to_u128()?;
assert_eq!(len as u64 as u128, len);
Ok((ptr.into(), len as u64))
}
ByVal(PrimVal::Undef) => err!(ReadUndefBytes),
ByVal(_) => bug!("expected ptr and length, got {:?}", self),
}
}
}
impl<'tcx> PrimVal {

View File

@ -23,12 +23,6 @@ use rustc::ty::subst::{Substs, Subst};
use rustc::util::common::ErrorReported;
use rustc::util::nodemap::NodeMap;
use rustc::mir::interpret::{PrimVal, Value, PtrAndAlign, HasMemory, EvalError};
use rustc::mir::interpret::{CompileTimeEvaluator, EvalContext};
use rustc::mir::Field;
use rustc::mir::interpret::{Place, PlaceExtra};
use rustc_data_structures::indexed_vec::Idx;
use syntax::abi::Abi;
use syntax::ast;
use syntax::attr;
@ -688,292 +682,3 @@ impl<'a, 'tcx> ConstContext<'a, 'tcx> {
compare_const_vals(tcx, span, &a.val, &b.val)
}
}
pub(crate) fn const_eval<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
key: ty::ParamEnvAnd<'tcx, (DefId, &'tcx Substs<'tcx>)>)
-> EvalResult<'tcx> {
trace!("const eval: {:?}", key);
let (def_id, substs) = if let Some(resolved) = lookup_const_by_id(tcx, key) {
resolved
} else {
return Err(ConstEvalErr {
span: tcx.def_span(key.value.0),
kind: TypeckError
});
};
let tables = tcx.typeck_tables_of(def_id);
let body = if let Some(id) = tcx.hir.as_local_node_id(def_id) {
let body_id = tcx.hir.body_owned_by(id);
// Do match-check before building MIR
if tcx.check_match(def_id).is_err() {
return Err(ConstEvalErr {
span: tcx.def_span(key.value.0),
kind: CheckMatchError,
});
}
tcx.mir_const_qualif(def_id);
tcx.hir.body(body_id)
} else {
tcx.extern_const_body(def_id).body
};
// do not continue into miri if typeck errors occurred
// it will fail horribly
if tables.tainted_by_errors {
signal!(&body.value, TypeckError);
}
trace!("running old const eval");
let old_result = ConstContext::new(tcx, key.param_env.and(substs), tables).eval(&body.value);
trace!("old const eval produced {:?}", old_result);
if tcx.sess.opts.debugging_opts.miri {
let instance = ty::Instance::new(def_id, substs);
trace!("const eval instance: {:?}, {:?}", instance, key.param_env);
let miri_result = ::rustc::mir::interpret::eval_body(tcx, instance, key.param_env);
match (miri_result, old_result) {
((Err(err), ecx), Ok(ok)) => {
trace!("miri failed, ctfe returned {:?}", ok);
tcx.sess.span_warn(
tcx.def_span(key.value.0),
"miri failed to eval, while ctfe succeeded",
);
let () = unwrap_miri(&ecx, Err(err));
Ok(ok)
},
((Ok(_), _), Err(err)) => {
Err(err)
},
((Err(_), _), Err(err)) => Err(err),
((Ok((miri_val, miri_ty)), mut ecx), Ok(ctfe)) => {
check_ctfe_against_miri(&mut ecx, miri_val, miri_ty, ctfe.val);
Ok(ctfe)
}
}
} else {
old_result
}
}
fn check_ctfe_against_miri<'a, 'tcx>(
ecx: &mut EvalContext<'a, 'tcx, CompileTimeEvaluator>,
miri_val: PtrAndAlign,
miri_ty: Ty<'tcx>,
ctfe: ConstVal<'tcx>,
) {
use rustc::ty::TypeVariants::*;
match miri_ty.sty {
TyInt(int_ty) => {
let value = ecx.read_maybe_aligned(miri_val.aligned, |ectx| {
ectx.try_read_value(miri_val.ptr, miri_ty)
});
let prim = get_prim(ecx, value);
let c = ConstInt::new_signed_truncating(prim as i128,
int_ty,
ecx.tcx.sess.target.isize_ty);
let c = ConstVal::Integral(c);
assert_eq!(c, ctfe, "miri evaluated to {:?}, but ctfe yielded {:?}", c, ctfe);
},
TyUint(uint_ty) => {
let value = ecx.read_maybe_aligned(miri_val.aligned, |ectx| {
ectx.try_read_value(miri_val.ptr, miri_ty)
});
let prim = get_prim(ecx, value);
let c = ConstInt::new_unsigned_truncating(prim,
uint_ty,
ecx.tcx.sess.target.usize_ty);
let c = ConstVal::Integral(c);
assert_eq!(c, ctfe, "miri evaluated to {:?}, but ctfe yielded {:?}", c, ctfe);
},
TyFloat(ty) => {
let value = ecx.read_maybe_aligned(miri_val.aligned, |ectx| {
ectx.try_read_value(miri_val.ptr, miri_ty)
});
let prim = get_prim(ecx, value);
let f = ConstVal::Float(ConstFloat { bits: prim, ty });
assert_eq!(f, ctfe, "miri evaluated to {:?}, but ctfe yielded {:?}", f, ctfe);
},
TyBool => {
let value = ecx.read_maybe_aligned(miri_val.aligned, |ectx| {
ectx.try_read_value(miri_val.ptr, miri_ty)
});
let bits = get_prim(ecx, value);
if bits > 1 {
bug!("miri evaluated to {}, but expected a bool {:?}", bits, ctfe);
}
let b = ConstVal::Bool(bits == 1);
assert_eq!(b, ctfe, "miri evaluated to {:?}, but ctfe yielded {:?}", b, ctfe);
},
TyChar => {
let value = ecx.read_maybe_aligned(miri_val.aligned, |ectx| {
ectx.try_read_value(miri_val.ptr, miri_ty)
});
let bits = get_prim(ecx, value);
if let Some(cm) = ::std::char::from_u32(bits as u32) {
assert_eq!(
ConstVal::Char(cm), ctfe,
"miri evaluated to {:?}, but expected {:?}", cm, ctfe,
);
} else {
bug!("miri evaluated to {}, but expected a char {:?}", bits, ctfe);
}
},
TyStr => {
let value = ecx.read_maybe_aligned(miri_val.aligned, |ectx| {
ectx.try_read_value(miri_val.ptr, miri_ty)
});
if let Ok(Some(Value::ByValPair(PrimVal::Ptr(ptr), PrimVal::Bytes(len)))) = value {
let bytes = ecx
.memory
.read_bytes(ptr.into(), len as u64)
.expect("bad miri memory for str");
if let Ok(s) = ::std::str::from_utf8(bytes) {
if let ConstVal::Str(s2) = ctfe {
assert_eq!(s, s2, "miri produced {:?}, but expected {:?}", s, s2);
} else {
bug!("miri produced {:?}, but expected {:?}", s, ctfe);
}
} else {
bug!(
"miri failed to produce valid utf8 {:?}, while ctfe produced {:?}",
bytes,
ctfe,
);
}
} else {
bug!("miri evaluated to {:?}, but expected a str {:?}", value, ctfe);
}
},
TyArray(elem_ty, n) => {
let n = n.val.to_const_int().unwrap().to_u64().unwrap();
let size = ecx.layout_of(elem_ty).unwrap().size.bytes();
let vec: Vec<(ConstVal, Ty<'tcx>)> = match ctfe {
ConstVal::ByteStr(arr) => arr.data.iter().map(|&b| {
(ConstVal::Integral(ConstInt::U8(b)), ecx.tcx.types.u8)
}).collect(),
ConstVal::Aggregate(Array(v)) => {
v.iter().map(|c| (c.val, c.ty)).collect()
},
ConstVal::Aggregate(Repeat(v, n)) => {
vec![(v.val, v.ty); n as usize]
},
_ => bug!("miri produced {:?}, but ctfe yielded {:?}", miri_ty, ctfe),
};
for (i, elem) in vec.into_iter().enumerate() {
assert!((i as u64) < n);
let ptr = miri_val.offset(size * i as u64, &ecx).unwrap();
check_ctfe_against_miri(ecx, ptr, elem_ty, elem.0);
}
},
TyTuple(..) => {
let vec = match ctfe {
ConstVal::Aggregate(Tuple(v)) => v,
_ => bug!("miri produced {:?}, but ctfe yielded {:?}", miri_ty, ctfe),
};
let layout = ecx.layout_of(miri_ty).unwrap();
for (i, elem) in vec.into_iter().enumerate() {
let offset = layout.fields.offset(i);
let ptr = miri_val.offset(offset.bytes(), &ecx).unwrap();
check_ctfe_against_miri(ecx, ptr, elem.ty, elem.val);
}
},
TyAdt(def, _) => {
let (struct_variant, extra) = if def.is_enum() {
let discr = ecx.read_discriminant_value(
Place::Ptr { ptr: miri_val, extra: PlaceExtra::None },
miri_ty).unwrap();
let variant = def.discriminants(ecx.tcx).position(|variant_discr| {
variant_discr.to_u128_unchecked() == discr
}).expect("miri produced invalid enum discriminant");
(&def.variants[variant], PlaceExtra::DowncastVariant(variant))
} else {
(def.struct_variant(), PlaceExtra::None)
};
let vec = match ctfe {
ConstVal::Aggregate(Struct(v)) => v,
ConstVal::Variant(did) => {
assert_eq!(struct_variant.fields.len(), 0);
assert_eq!(did, struct_variant.did);
return;
},
ctfe => bug!("miri produced {:?}, but ctfe yielded {:?}", miri_ty, ctfe),
};
let layout = ecx.layout_of(miri_ty).unwrap();
for &(name, elem) in vec.into_iter() {
let field = struct_variant.fields.iter().position(|f| f.name == name).unwrap();
let (place, _) = ecx.place_field(
Place::Ptr { ptr: miri_val, extra },
Field::new(field),
layout,
).unwrap();
let ptr = place.to_ptr_extra_aligned().0;
check_ctfe_against_miri(ecx, ptr, elem.ty, elem.val);
}
},
TySlice(_) => bug!("miri produced a slice?"),
// not supported by ctfe
TyRawPtr(_) |
TyRef(..) => {}
TyDynamic(..) => bug!("miri produced a trait object"),
TyClosure(..) => bug!("miri produced a closure"),
TyGenerator(..) => bug!("miri produced a generator"),
TyNever => bug!("miri produced a value of the never type"),
TyProjection(_) => bug!("miri produced a projection"),
TyAnon(..) => bug!("miri produced an impl Trait type"),
TyParam(_) => bug!("miri produced an unmonomorphized type"),
TyInfer(_) => bug!("miri produced an uninferred type"),
TyError => bug!("miri produced a type error"),
TyForeign(_) => bug!("miri produced an extern type"),
// should be fine
TyFnDef(..) => {}
TyFnPtr(_) => {
let value = ecx.read_maybe_aligned(miri_val.aligned, |ectx| {
ectx.try_read_value(miri_val.ptr, miri_ty)
});
let ptr = match value {
Ok(Some(Value::ByVal(PrimVal::Ptr(ptr)))) => ptr,
value => bug!("expected fn ptr, got {:?}", value),
};
let inst = ecx.memory.get_fn(ptr).unwrap();
match ctfe {
ConstVal::Function(did, substs) => {
let ctfe = ty::Instance::resolve(
ecx.tcx,
ecx.param_env,
did,
substs,
).unwrap();
assert_eq!(inst, ctfe, "expected fn ptr {:?}, but got {:?}", ctfe, inst);
},
_ => bug!("ctfe produced {:?}, but miri produced function {:?}", ctfe, inst),
}
},
}
}
fn get_prim<'a, 'tcx>(
ecx: &mut EvalContext<'a, 'tcx, CompileTimeEvaluator>,
res: Result<Option<Value>, EvalError<'tcx>>,
) -> u128 {
match res {
Ok(Some(Value::ByVal(prim))) => unwrap_miri(ecx, prim.to_bytes()),
Err(err) => unwrap_miri(ecx, Err(err)),
val => bug!("got {:?}", val),
}
}
fn unwrap_miri<'a, 'tcx, T>(
ecx: &EvalContext<'a, 'tcx, CompileTimeEvaluator>,
res: Result<T, EvalError<'tcx>>,
) -> T {
match res {
Ok(val) => val,
Err(mut err) => {
ecx.report(&mut err);
ecx.tcx.sess.abort_if_errors();
bug!("{:#?}", err);
}
}
}

View File

@ -50,7 +50,6 @@ use rustc::ty::maps::Providers;
pub fn provide(providers: &mut Providers) {
*providers = Providers {
const_eval: eval::const_eval,
check_match: check_match::check_match,
..*providers
};

View File

@ -12,6 +12,8 @@ crate-type = ["dylib"]
bitflags = "1.0"
graphviz = { path = "../libgraphviz" }
log = "0.3"
log_settings = "0.1.1"
lazy_static = "1.0"
rustc = { path = "../librustc" }
rustc_const_eval = { path = "../librustc_const_eval" }
rustc_const_math = { path = "../librustc_const_math" }
@ -20,3 +22,6 @@ rustc_errors = { path = "../librustc_errors" }
serialize = { path = "../libserialize" }
syntax = { path = "../libsyntax" }
syntax_pos = { path = "../libsyntax_pos" }
byteorder = { version = "1.1", features = ["i128"] }
regex = "0.2"
rustc_apfloat = { path = "../librustc_apfloat" }

View File

@ -1,8 +1,9 @@
use ty::Ty;
use rustc::ty::Ty;
use syntax::ast::{FloatTy, IntTy, UintTy};
use rustc_const_math::ConstFloat;
use super::{PrimVal, EvalContext, EvalResult, MemoryPointer, PointerArithmetic, Machine};
use super::{EvalContext, Machine};
use rustc::mir::interpret::{PrimVal, EvalResult, MemoryPointer, PointerArithmetic};
use rustc_apfloat::ieee::{Single, Double};
use rustc_apfloat::Float;
@ -20,7 +21,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
PrimVal::Undef => Ok(PrimVal::Undef),
PrimVal::Ptr(ptr) => self.cast_from_ptr(ptr, dest_ty),
val @ PrimVal::Bytes(_) => {
use super::PrimValKind::*;
use rustc::mir::interpret::PrimValKind::*;
match src_kind {
F32 => self.cast_from_float(val.to_f32()?, dest_ty),
F64 => self.cast_from_float(val.to_f64()?, dest_ty),
@ -75,7 +76,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
negative: bool,
) -> EvalResult<'tcx, PrimVal> {
trace!("cast_from_int: {}, {}, {}", v, ty, negative);
use ty::TypeVariants::*;
use rustc::ty::TypeVariants::*;
match ty.sty {
// Casts to bool are not permitted by rustc, no need to handle them here.
TyInt(ty) => Ok(PrimVal::Bytes(self.int_to_int(v as i128, ty))),
@ -95,7 +96,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
}
fn cast_from_float(&self, val: ConstFloat, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> {
use ty::TypeVariants::*;
use rustc::ty::TypeVariants::*;
match ty.sty {
TyUint(t) => {
let width = t.bit_width().unwrap_or(self.memory.pointer_size() as usize * 8);
@ -119,7 +120,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
}
fn cast_from_ptr(&self, ptr: MemoryPointer, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> {
use ty::TypeVariants::*;
use rustc::ty::TypeVariants::*;
match ty.sty {
// Casting to a reference or fn pointer is not permitted by rustc, no need to support it here.
TyRawPtr(_) |

View File

@ -0,0 +1,587 @@
use rustc::ty::{self, TyCtxt, Ty, Instance};
use rustc::ty::layout::{self, LayoutOf};
use rustc::ty::subst::Substs;
use rustc::hir::def_id::DefId;
use rustc::mir;
use rustc::middle::const_val::ErrKind::{CheckMatchError, TypeckError};
use rustc::middle::const_val::{ConstEvalErr, ConstVal};
use rustc_const_eval::{lookup_const_by_id, ConstContext};
use rustc::mir::Field;
use rustc_data_structures::indexed_vec::Idx;
use syntax::ast::Mutability;
use syntax::codemap::Span;
use rustc::mir::interpret::{EvalResult, EvalError, EvalErrorKind, GlobalId, Value, PrimVal, PtrAndAlign};
use super::{Place, PlaceExtra, EvalContext, StackPopCleanup, ValTy, HasMemory};
use rustc_const_math::ConstInt;
use std::fmt;
use std::error::Error;
pub fn eval_body<'a, 'tcx>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
instance: Instance<'tcx>,
param_env: ty::ParamEnv<'tcx>,
) -> (EvalResult<'tcx, (PtrAndAlign, Ty<'tcx>)>, EvalContext<'a, 'tcx, CompileTimeEvaluator>) {
debug!("eval_body: {:?}, {:?}", instance, param_env);
let limits = super::ResourceLimits::default();
let mut ecx = EvalContext::new(tcx, param_env, limits, CompileTimeEvaluator, ());
let cid = GlobalId {
instance,
promoted: None,
};
let try = (|| {
if ecx.tcx.has_attr(instance.def_id(), "linkage") {
return Err(ConstEvalError::NotConst("extern global".to_string()).into());
}
// FIXME(eddyb) use `Instance::ty` when it becomes available.
let instance_ty =
ecx.monomorphize(instance.def.def_ty(tcx), instance.substs);
if tcx.interpret_interner.borrow().get_cached(cid).is_none() {
let mir = ecx.load_mir(instance.def)?;
let layout = ecx.layout_of(instance_ty)?;
assert!(!layout.is_unsized());
let ptr = ecx.memory.allocate(
layout.size.bytes(),
layout.align.abi(),
None,
)?;
tcx.interpret_interner.borrow_mut().cache(
cid,
PtrAndAlign {
ptr: ptr.into(),
aligned: !layout.is_packed(),
},
);
let cleanup = StackPopCleanup::MarkStatic(Mutability::Immutable);
let name = ty::tls::with(|tcx| tcx.item_path_str(instance.def_id()));
trace!("const_eval: pushing stack frame for global: {}", name);
ecx.push_stack_frame(
instance,
mir.span,
mir,
Place::from_ptr(ptr),
cleanup.clone(),
)?;
while ecx.step()? {}
// reinsert the stack frame so any future queries have the correct substs
ecx.push_stack_frame(
instance,
mir.span,
mir,
Place::from_ptr(ptr),
cleanup,
)?;
}
let value = tcx.interpret_interner.borrow().get_cached(cid).expect("global not cached");
Ok((value, instance_ty))
})();
(try, ecx)
}
pub fn eval_body_as_integer<'a, 'tcx>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
instance: Instance<'tcx>,
) -> EvalResult<'tcx, ConstInt> {
let (ptr_ty, ecx) = eval_body(tcx, instance, param_env);
let (ptr, ty) = ptr_ty?;
let prim = match ecx.read_maybe_aligned(ptr.aligned, |ectx| ectx.try_read_value(ptr.ptr, ty))? {
Some(Value::ByVal(prim)) => prim.to_bytes()?,
_ => return err!(TypeNotPrimitive(ty)),
};
use syntax::ast::{IntTy, UintTy};
use rustc::ty::TypeVariants::*;
use rustc_const_math::{ConstIsize, ConstUsize};
Ok(match ty.sty {
TyInt(IntTy::I8) => ConstInt::I8(prim as i128 as i8),
TyInt(IntTy::I16) => ConstInt::I16(prim as i128 as i16),
TyInt(IntTy::I32) => ConstInt::I32(prim as i128 as i32),
TyInt(IntTy::I64) => ConstInt::I64(prim as i128 as i64),
TyInt(IntTy::I128) => ConstInt::I128(prim as i128),
TyInt(IntTy::Is) => ConstInt::Isize(
ConstIsize::new(prim as i128 as i64, tcx.sess.target.isize_ty)
.expect("miri should already have errored"),
),
TyUint(UintTy::U8) => ConstInt::U8(prim as u8),
TyUint(UintTy::U16) => ConstInt::U16(prim as u16),
TyUint(UintTy::U32) => ConstInt::U32(prim as u32),
TyUint(UintTy::U64) => ConstInt::U64(prim as u64),
TyUint(UintTy::U128) => ConstInt::U128(prim),
TyUint(UintTy::Us) => ConstInt::Usize(
ConstUsize::new(prim as u64, tcx.sess.target.usize_ty)
.expect("miri should already have errored"),
),
_ => {
return Err(
ConstEvalError::NeedsRfc(
"evaluating anything other than isize/usize during typeck".to_string(),
).into(),
)
}
})
}
pub struct CompileTimeEvaluator;
impl<'tcx> Into<EvalError<'tcx>> for ConstEvalError {
fn into(self) -> EvalError<'tcx> {
EvalErrorKind::MachineError(Box::new(self)).into()
}
}
#[derive(Clone, Debug)]
enum ConstEvalError {
NeedsRfc(String),
NotConst(String),
}
impl fmt::Display for ConstEvalError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::ConstEvalError::*;
match *self {
NeedsRfc(ref msg) => {
write!(
f,
"\"{}\" needs an rfc before being allowed inside constants",
msg
)
}
NotConst(ref msg) => write!(f, "Cannot evaluate within constants: \"{}\"", msg),
}
}
}
impl Error for ConstEvalError {
fn description(&self) -> &str {
use self::ConstEvalError::*;
match *self {
NeedsRfc(_) => "this feature needs an rfc before being allowed inside constants",
NotConst(_) => "this feature is not compatible with constant evaluation",
}
}
fn cause(&self) -> Option<&Error> {
None
}
}
impl<'tcx> super::Machine<'tcx> for CompileTimeEvaluator {
type MemoryData = ();
type MemoryKinds = !;
fn eval_fn_call<'a>(
ecx: &mut EvalContext<'a, 'tcx, Self>,
instance: ty::Instance<'tcx>,
destination: Option<(Place, mir::BasicBlock)>,
_args: &[ValTy<'tcx>],
span: Span,
_sig: ty::FnSig<'tcx>,
) -> EvalResult<'tcx, bool> {
debug!("eval_fn_call: {:?}", instance);
if !ecx.tcx.is_const_fn(instance.def_id()) {
return Err(
ConstEvalError::NotConst(format!("calling non-const fn `{}`", instance)).into(),
);
}
let mir = match ecx.load_mir(instance.def) {
Ok(mir) => mir,
Err(EvalError { kind: EvalErrorKind::NoMirFor(path), .. }) => {
// some simple things like `malloc` might get accepted in the future
return Err(
ConstEvalError::NeedsRfc(format!("calling extern function `{}`", path))
.into(),
);
}
Err(other) => return Err(other),
};
let (return_place, return_to_block) = match destination {
Some((place, block)) => (place, StackPopCleanup::Goto(block)),
None => (Place::undef(), StackPopCleanup::None),
};
ecx.push_stack_frame(
instance,
span,
mir,
return_place,
return_to_block,
)?;
Ok(false)
}
fn call_intrinsic<'a>(
ecx: &mut EvalContext<'a, 'tcx, Self>,
instance: ty::Instance<'tcx>,
_args: &[ValTy<'tcx>],
dest: Place,
dest_layout: layout::TyLayout<'tcx>,
target: mir::BasicBlock,
) -> EvalResult<'tcx> {
let substs = instance.substs;
let intrinsic_name = &ecx.tcx.item_name(instance.def_id())[..];
match intrinsic_name {
"min_align_of" => {
let elem_ty = substs.type_at(0);
let elem_align = ecx.layout_of(elem_ty)?.align.abi();
let align_val = PrimVal::from_u128(elem_align as u128);
ecx.write_primval(dest, align_val, dest_layout.ty)?;
}
"size_of" => {
let ty = substs.type_at(0);
let size = ecx.layout_of(ty)?.size.bytes() as u128;
ecx.write_primval(dest, PrimVal::from_u128(size), dest_layout.ty)?;
}
name => return Err(ConstEvalError::NeedsRfc(format!("calling intrinsic `{}`", name)).into()),
}
ecx.goto_block(target);
// Since we pushed no stack frame, the main loop will act
// as if the call just completed and it's returning to the
// current frame.
Ok(())
}
fn try_ptr_op<'a>(
_ecx: &EvalContext<'a, 'tcx, Self>,
_bin_op: mir::BinOp,
left: PrimVal,
_left_ty: Ty<'tcx>,
right: PrimVal,
_right_ty: Ty<'tcx>,
) -> EvalResult<'tcx, Option<(PrimVal, bool)>> {
if left.is_bytes() && right.is_bytes() {
Ok(None)
} else {
Err(
ConstEvalError::NeedsRfc("Pointer arithmetic or comparison".to_string()).into(),
)
}
}
fn mark_static_initialized(m: !) -> EvalResult<'tcx> {
m
}
fn box_alloc<'a>(
_ecx: &mut EvalContext<'a, 'tcx, Self>,
_ty: Ty<'tcx>,
_dest: Place,
) -> EvalResult<'tcx> {
Err(
ConstEvalError::NeedsRfc("Heap allocations via `box` keyword".to_string()).into(),
)
}
fn global_item_with_linkage<'a>(
_ecx: &mut EvalContext<'a, 'tcx, Self>,
_instance: ty::Instance<'tcx>,
_mutability: Mutability,
) -> EvalResult<'tcx> {
Err(
ConstEvalError::NotConst("statics with `linkage` attribute".to_string()).into(),
)
}
}
pub fn const_eval_provider<'a, 'tcx>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
key: ty::ParamEnvAnd<'tcx, (DefId, &'tcx Substs<'tcx>)>,
) -> ::rustc::middle::const_val::EvalResult<'tcx> {
trace!("const eval: {:?}", key);
let (def_id, substs) = if let Some(resolved) = lookup_const_by_id(tcx, key) {
resolved
} else {
return Err(ConstEvalErr {
span: tcx.def_span(key.value.0),
kind: TypeckError
});
};
let tables = tcx.typeck_tables_of(def_id);
let body = if let Some(id) = tcx.hir.as_local_node_id(def_id) {
let body_id = tcx.hir.body_owned_by(id);
// Do match-check before building MIR
if tcx.check_match(def_id).is_err() {
return Err(ConstEvalErr {
span: tcx.def_span(key.value.0),
kind: CheckMatchError,
});
}
tcx.mir_const_qualif(def_id);
tcx.hir.body(body_id)
} else {
tcx.extern_const_body(def_id).body
};
// do not continue into miri if typeck errors occurred
// it will fail horribly
if tables.tainted_by_errors {
return Err(ConstEvalErr { span: body.value.span, kind: TypeckError })
}
trace!("running old const eval");
let old_result = ConstContext::new(tcx, key.param_env.and(substs), tables).eval(&body.value);
trace!("old const eval produced {:?}", old_result);
if tcx.sess.opts.debugging_opts.miri {
let instance = ty::Instance::new(def_id, substs);
trace!("const eval instance: {:?}, {:?}", instance, key.param_env);
let miri_result = ::interpret::eval_body(tcx, instance, key.param_env);
match (miri_result, old_result) {
((Err(err), ecx), Ok(ok)) => {
trace!("miri failed, ctfe returned {:?}", ok);
tcx.sess.span_warn(
tcx.def_span(key.value.0),
"miri failed to eval, while ctfe succeeded",
);
let () = unwrap_miri(&ecx, Err(err));
Ok(ok)
},
((Ok(_), _), Err(err)) => {
Err(err)
},
((Err(_), _), Err(err)) => Err(err),
((Ok((miri_val, miri_ty)), mut ecx), Ok(ctfe)) => {
check_ctfe_against_miri(&mut ecx, miri_val, miri_ty, ctfe.val);
Ok(ctfe)
}
}
} else {
old_result
}
}
fn check_ctfe_against_miri<'a, 'tcx>(
ecx: &mut EvalContext<'a, 'tcx, CompileTimeEvaluator>,
miri_val: PtrAndAlign,
miri_ty: Ty<'tcx>,
ctfe: ConstVal<'tcx>,
) {
use rustc::middle::const_val::ConstAggregate::*;
use rustc_const_math::ConstFloat;
use rustc::ty::TypeVariants::*;
match miri_ty.sty {
TyInt(int_ty) => {
let value = ecx.read_maybe_aligned(miri_val.aligned, |ectx| {
ectx.try_read_value(miri_val.ptr, miri_ty)
});
let prim = get_prim(ecx, value);
let c = ConstInt::new_signed_truncating(prim as i128,
int_ty,
ecx.tcx.sess.target.isize_ty);
let c = ConstVal::Integral(c);
assert_eq!(c, ctfe, "miri evaluated to {:?}, but ctfe yielded {:?}", c, ctfe);
},
TyUint(uint_ty) => {
let value = ecx.read_maybe_aligned(miri_val.aligned, |ectx| {
ectx.try_read_value(miri_val.ptr, miri_ty)
});
let prim = get_prim(ecx, value);
let c = ConstInt::new_unsigned_truncating(prim,
uint_ty,
ecx.tcx.sess.target.usize_ty);
let c = ConstVal::Integral(c);
assert_eq!(c, ctfe, "miri evaluated to {:?}, but ctfe yielded {:?}", c, ctfe);
},
TyFloat(ty) => {
let value = ecx.read_maybe_aligned(miri_val.aligned, |ectx| {
ectx.try_read_value(miri_val.ptr, miri_ty)
});
let prim = get_prim(ecx, value);
let f = ConstVal::Float(ConstFloat { bits: prim, ty });
assert_eq!(f, ctfe, "miri evaluated to {:?}, but ctfe yielded {:?}", f, ctfe);
},
TyBool => {
let value = ecx.read_maybe_aligned(miri_val.aligned, |ectx| {
ectx.try_read_value(miri_val.ptr, miri_ty)
});
let bits = get_prim(ecx, value);
if bits > 1 {
bug!("miri evaluated to {}, but expected a bool {:?}", bits, ctfe);
}
let b = ConstVal::Bool(bits == 1);
assert_eq!(b, ctfe, "miri evaluated to {:?}, but ctfe yielded {:?}", b, ctfe);
},
TyChar => {
let value = ecx.read_maybe_aligned(miri_val.aligned, |ectx| {
ectx.try_read_value(miri_val.ptr, miri_ty)
});
let bits = get_prim(ecx, value);
if let Some(cm) = ::std::char::from_u32(bits as u32) {
assert_eq!(
ConstVal::Char(cm), ctfe,
"miri evaluated to {:?}, but expected {:?}", cm, ctfe,
);
} else {
bug!("miri evaluated to {}, but expected a char {:?}", bits, ctfe);
}
},
TyStr => {
let value = ecx.read_maybe_aligned(miri_val.aligned, |ectx| {
ectx.try_read_value(miri_val.ptr, miri_ty)
});
if let Ok(Some(Value::ByValPair(PrimVal::Ptr(ptr), PrimVal::Bytes(len)))) = value {
let bytes = ecx
.memory
.read_bytes(ptr.into(), len as u64)
.expect("bad miri memory for str");
if let Ok(s) = ::std::str::from_utf8(bytes) {
if let ConstVal::Str(s2) = ctfe {
assert_eq!(s, s2, "miri produced {:?}, but expected {:?}", s, s2);
} else {
bug!("miri produced {:?}, but expected {:?}", s, ctfe);
}
} else {
bug!(
"miri failed to produce valid utf8 {:?}, while ctfe produced {:?}",
bytes,
ctfe,
);
}
} else {
bug!("miri evaluated to {:?}, but expected a str {:?}", value, ctfe);
}
},
TyArray(elem_ty, n) => {
let n = n.val.to_const_int().unwrap().to_u64().unwrap();
let size = ecx.layout_of(elem_ty).unwrap().size.bytes();
let vec: Vec<(ConstVal, Ty<'tcx>)> = match ctfe {
ConstVal::ByteStr(arr) => arr.data.iter().map(|&b| {
(ConstVal::Integral(ConstInt::U8(b)), ecx.tcx.types.u8)
}).collect(),
ConstVal::Aggregate(Array(v)) => {
v.iter().map(|c| (c.val, c.ty)).collect()
},
ConstVal::Aggregate(Repeat(v, n)) => {
vec![(v.val, v.ty); n as usize]
},
_ => bug!("miri produced {:?}, but ctfe yielded {:?}", miri_ty, ctfe),
};
for (i, elem) in vec.into_iter().enumerate() {
assert!((i as u64) < n);
let ptr = miri_val.offset(size * i as u64, &ecx).unwrap();
check_ctfe_against_miri(ecx, ptr, elem_ty, elem.0);
}
},
TyTuple(..) => {
let vec = match ctfe {
ConstVal::Aggregate(Tuple(v)) => v,
_ => bug!("miri produced {:?}, but ctfe yielded {:?}", miri_ty, ctfe),
};
let layout = ecx.layout_of(miri_ty).unwrap();
for (i, elem) in vec.into_iter().enumerate() {
let offset = layout.fields.offset(i);
let ptr = miri_val.offset(offset.bytes(), &ecx).unwrap();
check_ctfe_against_miri(ecx, ptr, elem.ty, elem.val);
}
},
TyAdt(def, _) => {
let (struct_variant, extra) = if def.is_enum() {
let discr = ecx.read_discriminant_value(
Place::Ptr { ptr: miri_val, extra: PlaceExtra::None },
miri_ty).unwrap();
let variant = def.discriminants(ecx.tcx).position(|variant_discr| {
variant_discr.to_u128_unchecked() == discr
}).expect("miri produced invalid enum discriminant");
(&def.variants[variant], PlaceExtra::DowncastVariant(variant))
} else {
(def.struct_variant(), PlaceExtra::None)
};
let vec = match ctfe {
ConstVal::Aggregate(Struct(v)) => v,
ConstVal::Variant(did) => {
assert_eq!(struct_variant.fields.len(), 0);
assert_eq!(did, struct_variant.did);
return;
},
ctfe => bug!("miri produced {:?}, but ctfe yielded {:?}", miri_ty, ctfe),
};
let layout = ecx.layout_of(miri_ty).unwrap();
for &(name, elem) in vec.into_iter() {
let field = struct_variant.fields.iter().position(|f| f.name == name).unwrap();
let (place, _) = ecx.place_field(
Place::Ptr { ptr: miri_val, extra },
Field::new(field),
layout,
).unwrap();
let ptr = place.to_ptr_extra_aligned().0;
check_ctfe_against_miri(ecx, ptr, elem.ty, elem.val);
}
},
TySlice(_) => bug!("miri produced a slice?"),
// not supported by ctfe
TyRawPtr(_) |
TyRef(..) => {}
TyDynamic(..) => bug!("miri produced a trait object"),
TyClosure(..) => bug!("miri produced a closure"),
TyGenerator(..) => bug!("miri produced a generator"),
TyNever => bug!("miri produced a value of the never type"),
TyProjection(_) => bug!("miri produced a projection"),
TyAnon(..) => bug!("miri produced an impl Trait type"),
TyParam(_) => bug!("miri produced an unmonomorphized type"),
TyInfer(_) => bug!("miri produced an uninferred type"),
TyError => bug!("miri produced a type error"),
TyForeign(_) => bug!("miri produced an extern type"),
// should be fine
TyFnDef(..) => {}
TyFnPtr(_) => {
let value = ecx.read_maybe_aligned(miri_val.aligned, |ectx| {
ectx.try_read_value(miri_val.ptr, miri_ty)
});
let ptr = match value {
Ok(Some(Value::ByVal(PrimVal::Ptr(ptr)))) => ptr,
value => bug!("expected fn ptr, got {:?}", value),
};
let inst = ecx.memory.get_fn(ptr).unwrap();
match ctfe {
ConstVal::Function(did, substs) => {
let ctfe = ty::Instance::resolve(
ecx.tcx,
ecx.param_env,
did,
substs,
).unwrap();
assert_eq!(inst, ctfe, "expected fn ptr {:?}, but got {:?}", ctfe, inst);
},
_ => bug!("ctfe produced {:?}, but miri produced function {:?}", ctfe, inst),
}
},
}
}
fn get_prim<'a, 'tcx>(
ecx: &mut EvalContext<'a, 'tcx, CompileTimeEvaluator>,
res: Result<Option<Value>, EvalError<'tcx>>,
) -> u128 {
match res {
Ok(Some(Value::ByVal(prim))) => unwrap_miri(ecx, prim.to_bytes()),
Err(err) => unwrap_miri(ecx, Err(err)),
val => bug!("got {:?}", val),
}
}
fn unwrap_miri<'a, 'tcx, T>(
ecx: &EvalContext<'a, 'tcx, CompileTimeEvaluator>,
res: Result<T, EvalError<'tcx>>,
) -> T {
match res {
Ok(val) => val,
Err(mut err) => {
ecx.report(&mut err);
ecx.tcx.sess.abort_if_errors();
bug!("{:#?}", err);
}
}
}

View File

@ -1,21 +1,24 @@
use std::collections::{HashMap, HashSet};
use std::fmt::Write;
use hir::def_id::DefId;
use hir::map::definitions::DefPathData;
use middle::const_val::ConstVal;
use middle::region;
use mir;
use traits::Reveal;
use ty::layout::{self, Size, Align, HasDataLayout, LayoutOf, TyLayout};
use ty::subst::{Subst, Substs, Kind};
use ty::{self, Ty, TyCtxt};
use rustc::hir::def_id::DefId;
use rustc::hir::map::definitions::DefPathData;
use rustc::middle::const_val::ConstVal;
use rustc::mir;
use rustc::traits::Reveal;
use rustc::ty::layout::{self, Size, Align, HasDataLayout, LayoutOf, TyLayout};
use rustc::ty::subst::{Subst, Substs, Kind};
use rustc::ty::{self, Ty, TyCtxt};
use rustc_data_structures::indexed_vec::Idx;
use syntax::codemap::{self, DUMMY_SP};
use syntax::ast::Mutability;
use rustc::mir::interpret::{
PtrAndAlign, DynamicLifetime, GlobalId, Value, Pointer, PrimVal, PrimValKind,
EvalError, EvalResult, EvalErrorKind, MemoryPointer,
};
use super::{EvalError, EvalResult, EvalErrorKind, GlobalId, Place, PlaceExtra, Memory,
MemoryPointer, HasMemory, MemoryKind, operator, PrimVal, PrimValKind, Value, Pointer,
use super::{Place, PlaceExtra, Memory,
HasMemory, MemoryKind, operator,
ValidationQuery, Machine};
pub struct EvalContext<'a, 'tcx: 'a, M: Machine<'tcx>> {
@ -102,12 +105,6 @@ pub enum StackPopCleanup {
None,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct DynamicLifetime {
pub frame: usize,
pub region: Option<region::Scope>, // "None" indicates "until the function ends"
}
#[derive(Copy, Clone, Debug)]
pub struct ResourceLimits {
pub memory_size: u64,
@ -144,25 +141,6 @@ impl<'tcx> ::std::ops::Deref for ValTy<'tcx> {
}
}
#[derive(Copy, Clone, Debug)]
pub struct PtrAndAlign {
pub ptr: Pointer,
/// Remember whether this place is *supposed* to be aligned.
pub aligned: bool,
}
impl PtrAndAlign {
pub fn to_ptr<'tcx>(self) -> EvalResult<'tcx, MemoryPointer> {
self.ptr.to_ptr()
}
pub fn offset<'tcx, C: HasDataLayout>(self, i: u64, cx: C) -> EvalResult<'tcx, Self> {
Ok(PtrAndAlign {
ptr: self.ptr.offset(i, cx)?,
aligned: self.aligned,
})
}
}
impl<'a, 'tcx, M: Machine<'tcx>> HasDataLayout for &'a EvalContext<'a, 'tcx, M> {
#[inline]
fn data_layout(&self) -> &layout::TargetDataLayout {
@ -268,7 +246,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
}
pub(super) fn const_to_value(&mut self, const_val: &ConstVal<'tcx>) -> EvalResult<'tcx, Value> {
use middle::const_val::ConstVal::*;
use rustc::middle::const_val::ConstVal::*;
let primval = match *const_val {
Integral(const_int) => PrimVal::Bytes(const_int.to_u128_unchecked()),
@ -410,14 +388,14 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
Ok((size.abi_align(align), align))
}
ty::TyDynamic(..) => {
let (_, vtable) = value.into_ptr_vtable_pair(&mut self.memory)?;
let (_, vtable) = self.into_ptr_vtable_pair(value)?;
// the second entry in the vtable is the dynamic size of the object.
self.read_size_and_align_from_vtable(vtable)
}
ty::TySlice(_) | ty::TyStr => {
let (elem_size, align) = layout.field(&self, 0)?.size_and_align();
let (_, len) = value.into_slice(&mut self.memory)?;
let (_, len) = self.into_slice(value)?;
Ok((elem_size * len, align))
}
@ -438,7 +416,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
/// Return the set of locals that have a storage annotation anywhere
fn collect_storage_annotations<'tcx>(mir: &'tcx mir::Mir<'tcx>) -> HashSet<mir::Local> {
use mir::StatementKind::*;
use rustc::mir::StatementKind::*;
let mut set = HashSet::new();
for block in mir.basic_blocks() {
@ -546,7 +524,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
let dest = self.eval_place(place)?;
let dest_ty = self.place_ty(place);
use mir::Rvalue::*;
use rustc::mir::Rvalue::*;
match *rvalue {
Use(ref operand) => {
let value = self.eval_operand(operand)?.value;
@ -700,7 +678,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
Cast(kind, ref operand, cast_ty) => {
debug_assert_eq!(self.monomorphize(cast_ty, self.substs()), dest_ty);
use mir::CastKind::*;
use rustc::mir::CastKind::*;
match kind {
Unsize => {
let src = self.eval_operand(operand)?;
@ -841,7 +819,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
}
pub fn eval_operand(&mut self, op: &mir::Operand<'tcx>) -> EvalResult<'tcx, ValTy<'tcx>> {
use mir::Operand::*;
use rustc::mir::Operand::*;
let ty = self.monomorphize(op.ty(self.mir(), self.tcx), self.substs());
match *op {
// FIXME: do some more logic on `move` to invalidate the old location
@ -854,7 +832,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
},
Constant(ref constant) => {
use mir::Literal;
use rustc::mir::Literal;
let mir::Constant { ref literal, .. } = **constant;
let value = match *literal {
Literal::Value { ref value } => self.const_to_value(&value.val)?,
@ -1271,7 +1249,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
ty::TyAdt(..) => {
match self.layout_of(ty)?.abi {
layout::Abi::Scalar(ref scalar) => {
use ty::layout::Primitive::*;
use rustc::ty::layout::Primitive::*;
match scalar.value {
Int(i, false) => PrimValKind::from_uint_size(i.size().bytes()),
Int(i, true) => PrimValKind::from_int_size(i.size().bytes()),
@ -1448,7 +1426,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
match (&src_pointee_ty.sty, &dest_pointee_ty.sty) {
(&ty::TyArray(_, length), &ty::TySlice(_)) => {
let ptr = src.into_ptr(&self.memory)?;
let ptr = self.into_ptr(src)?;
// u64 cast is from usize to u64, which is always good
let valty = ValTy {
value: ptr.to_value_with_len(length.val.to_const_int().unwrap().to_u64().unwrap() ),
@ -1473,7 +1451,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
);
let trait_ref = self.tcx.erase_regions(&trait_ref);
let vtable = self.get_vtable(src_pointee_ty, trait_ref)?;
let ptr = src.into_ptr(&self.memory)?;
let ptr = self.into_ptr(src)?;
let valty = ValTy {
value: ptr.to_value_with_vtable(vtable),
ty: dest_ty,
@ -1759,7 +1737,7 @@ pub fn resolve_drop_in_place<'a, 'tcx>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
ty: Ty<'tcx>,
) -> ty::Instance<'tcx> {
let def_id = tcx.require_lang_item(::middle::lang_items::DropInPlaceFnLangItem);
let def_id = tcx.require_lang_item(::rustc::middle::lang_items::DropInPlaceFnLangItem);
let substs = tcx.intern_substs(&[Kind::from(ty)]);
ty::Instance::resolve(tcx, ty::ParamEnv::empty(Reveal::All), def_id, substs).unwrap()
}

View File

@ -2,10 +2,11 @@
//! This separation exists to ensure that no fancy miri features like
//! interpreting common C functions leak into CTFE.
use super::{EvalResult, EvalContext, Place, PrimVal, ValTy};
use rustc::mir::interpret::{EvalResult, PrimVal};
use super::{EvalContext, Place, ValTy};
use mir;
use ty::{self, Ty};
use rustc::mir;
use rustc::ty::{self, Ty};
use syntax::codemap::Span;
use syntax::ast::Mutability;

View File

@ -1,26 +1,22 @@
use byteorder::{ReadBytesExt, WriteBytesExt, LittleEndian, BigEndian};
use std::collections::{btree_map, BTreeMap, HashMap, HashSet, VecDeque};
use std::{fmt, iter, ptr, mem, io};
use std::{ptr, mem, io};
use std::cell::Cell;
use ty::{Instance, TyCtxt};
use ty::layout::{self, TargetDataLayout, HasDataLayout};
use rustc::ty::{Instance, TyCtxt};
use rustc::ty::layout::{self, TargetDataLayout};
use syntax::ast::Mutability;
use middle::region;
use rustc::middle::region;
use super::{EvalResult, EvalErrorKind, PrimVal, Pointer, EvalContext, DynamicLifetime, Machine,
RangeMap, AbsPlace};
use rustc::mir::interpret::{MemoryPointer, AllocId, Allocation, AccessKind, UndefMask, PtrAndAlign, Value, DynamicLifetime, Pointer,
EvalResult, PrimVal, EvalErrorKind};
use super::{EvalContext, Machine, RangeMap, AbsPlace};
////////////////////////////////////////////////////////////////////////////////
// Locks
////////////////////////////////////////////////////////////////////////////////
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum AccessKind {
Read,
Write,
}
/// Information about a lock that is currently held.
#[derive(Clone, Debug)]
struct LockInfo<'tcx> {
@ -46,14 +42,9 @@ struct WriteLockId<'tcx> {
path: AbsPlace<'tcx>,
}
#[derive(Clone, Debug, PartialEq)]
pub enum Lock {
NoLock,
WriteLock(DynamicLifetime),
/// This should never be empty -- that would be a read lock held and nobody there to release it...
ReadLock(Vec<DynamicLifetime>),
}
use self::Lock::*;
use rustc::mir::interpret::Lock::*;
use rustc::mir::interpret::Lock;
impl<'tcx> Default for LockInfo<'tcx> {
fn default() -> Self {
@ -91,42 +82,6 @@ impl<'tcx> LockInfo<'tcx> {
// Allocations and pointers
////////////////////////////////////////////////////////////////////////////////
#[derive(Copy, Clone, Eq, Hash, Ord, PartialEq, PartialOrd, Debug)]
pub struct AllocId(u64);
impl fmt::Display for AllocId {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(Debug, Eq, PartialEq, Hash)]
pub struct Allocation {
/// The actual bytes of the allocation.
/// Note that the bytes of a pointer represent the offset of the pointer
pub bytes: Vec<u8>,
/// Maps from byte addresses to allocations.
/// Only the first byte of a pointer is inserted into the map.
pub relocations: BTreeMap<u64, AllocId>,
/// Denotes undefined memory. Reading from undefined memory is forbidden in miri
pub undef_mask: UndefMask,
/// The alignment of the allocation to detect unaligned reads.
pub align: u64,
}
impl Allocation {
pub fn from_bytes(slice: &[u8]) -> Self {
let mut undef_mask = UndefMask::new(0);
undef_mask.grow(slice.len() as u64, true);
Self {
bytes: slice.to_owned(),
relocations: BTreeMap::new(),
undef_mask,
align: 1,
}
}
}
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum MemoryKind<T> {
/// Error if deallocated except during a stack pop
@ -137,49 +92,6 @@ pub enum MemoryKind<T> {
Machine(T),
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct MemoryPointer {
pub alloc_id: AllocId,
pub offset: u64,
}
impl<'tcx> MemoryPointer {
pub fn new(alloc_id: AllocId, offset: u64) -> Self {
MemoryPointer { alloc_id, offset }
}
pub(crate) fn wrapping_signed_offset<C: HasDataLayout>(self, i: i64, cx: C) -> Self {
MemoryPointer::new(
self.alloc_id,
cx.data_layout().wrapping_signed_offset(self.offset, i),
)
}
pub fn overflowing_signed_offset<C: HasDataLayout>(self, i: i128, cx: C) -> (Self, bool) {
let (res, over) = cx.data_layout().overflowing_signed_offset(self.offset, i);
(MemoryPointer::new(self.alloc_id, res), over)
}
pub(crate) fn signed_offset<C: HasDataLayout>(self, i: i64, cx: C) -> EvalResult<'tcx, Self> {
Ok(MemoryPointer::new(
self.alloc_id,
cx.data_layout().signed_offset(self.offset, i)?,
))
}
pub fn overflowing_offset<C: HasDataLayout>(self, i: u64, cx: C) -> (Self, bool) {
let (res, over) = cx.data_layout().overflowing_offset(self.offset, i);
(MemoryPointer::new(self.alloc_id, res), over)
}
pub fn offset<C: HasDataLayout>(self, i: u64, cx: C) -> EvalResult<'tcx, Self> {
Ok(MemoryPointer::new(
self.alloc_id,
cx.data_layout().offset(self.offset, i)?,
))
}
}
////////////////////////////////////////////////////////////////////////////////
// Top-level interpreter memory
////////////////////////////////////////////////////////////////////////////////
@ -1477,93 +1389,6 @@ fn read_target_int(endianess: layout::Endian, mut source: &[u8]) -> Result<i128,
}
}
////////////////////////////////////////////////////////////////////////////////
// Undefined byte tracking
////////////////////////////////////////////////////////////////////////////////
type Block = u64;
const BLOCK_SIZE: u64 = 64;
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub struct UndefMask {
blocks: Vec<Block>,
len: u64,
}
impl UndefMask {
fn new(size: u64) -> Self {
let mut m = UndefMask {
blocks: vec![],
len: 0,
};
m.grow(size, false);
m
}
/// Check whether the range `start..end` (end-exclusive) is entirely defined.
pub fn is_range_defined(&self, start: u64, end: u64) -> bool {
if end > self.len {
return false;
}
for i in start..end {
if !self.get(i) {
return false;
}
}
true
}
fn set_range(&mut self, start: u64, end: u64, new_state: bool) {
let len = self.len;
if end > len {
self.grow(end - len, new_state);
}
self.set_range_inbounds(start, end, new_state);
}
fn set_range_inbounds(&mut self, start: u64, end: u64, new_state: bool) {
for i in start..end {
self.set(i, new_state);
}
}
fn get(&self, i: u64) -> bool {
let (block, bit) = bit_index(i);
(self.blocks[block] & 1 << bit) != 0
}
fn set(&mut self, i: u64, new_state: bool) {
let (block, bit) = bit_index(i);
if new_state {
self.blocks[block] |= 1 << bit;
} else {
self.blocks[block] &= !(1 << bit);
}
}
fn grow(&mut self, amount: u64, new_state: bool) {
let unused_trailing_bits = self.blocks.len() as u64 * BLOCK_SIZE - self.len;
if amount > unused_trailing_bits {
let additional_blocks = amount / BLOCK_SIZE + 1;
assert_eq!(additional_blocks as usize as u64, additional_blocks);
self.blocks.extend(
iter::repeat(0).take(additional_blocks as usize),
);
}
let start = self.len;
self.len += amount;
self.set_range_inbounds(start, start + amount, new_state);
}
}
fn bit_index(bits: u64) -> (usize, usize) {
let a = bits / BLOCK_SIZE;
let b = bits % BLOCK_SIZE;
assert_eq!(a as usize as u64, a);
assert_eq!(b as usize as u64, b);
(a as usize, b as usize)
}
////////////////////////////////////////////////////////////////////////////////
// Unaligned accesses
////////////////////////////////////////////////////////////////////////////////
@ -1608,6 +1433,73 @@ pub trait HasMemory<'a, 'tcx: 'a, M: Machine<'tcx>> {
self.memory().writes_are_aligned.set(old);
t
}
/// Convert the value into a pointer (or a pointer-sized integer). If the value is a ByRef,
/// this may have to perform a load.
fn into_ptr(
&self,
value: Value,
) -> EvalResult<'tcx, Pointer> {
Ok(match value {
Value::ByRef(PtrAndAlign { ptr, aligned }) => {
self.memory().read_maybe_aligned(aligned, |mem| mem.read_ptr_sized_unsigned(ptr.to_ptr()?))?
}
Value::ByVal(ptr) |
Value::ByValPair(ptr, _) => ptr,
}.into())
}
fn into_ptr_vtable_pair(
&self,
value: Value,
) -> EvalResult<'tcx, (Pointer, MemoryPointer)> {
match value {
Value::ByRef(PtrAndAlign {
ptr: ref_ptr,
aligned,
}) => {
self.memory().read_maybe_aligned(aligned, |mem| {
let ptr = mem.read_ptr_sized_unsigned(ref_ptr.to_ptr()?)?.into();
let vtable = mem.read_ptr_sized_unsigned(
ref_ptr.offset(mem.pointer_size(), &mem.tcx.data_layout)?.to_ptr()?,
)?.to_ptr()?;
Ok((ptr, vtable))
})
}
Value::ByValPair(ptr, vtable) => Ok((ptr.into(), vtable.to_ptr()?)),
Value::ByVal(PrimVal::Undef) => err!(ReadUndefBytes),
_ => bug!("expected ptr and vtable, got {:?}", value),
}
}
fn into_slice(
&self,
value: Value,
) -> EvalResult<'tcx, (Pointer, u64)> {
match value {
Value::ByRef(PtrAndAlign {
ptr: ref_ptr,
aligned,
}) => {
self.memory().read_maybe_aligned(aligned, |mem| {
let ptr = mem.read_ptr_sized_unsigned(ref_ptr.to_ptr()?)?.into();
let len = mem.read_ptr_sized_unsigned(
ref_ptr.offset(mem.pointer_size(), &mem.tcx.data_layout)?.to_ptr()?,
)?.to_bytes()? as u64;
Ok((ptr, len))
})
}
Value::ByValPair(ptr, val) => {
let len = val.to_u128()?;
assert_eq!(len as u64 as u128, len);
Ok((ptr.into(), len as u64))
}
Value::ByVal(PrimVal::Undef) => err!(ReadUndefBytes),
Value::ByVal(_) => bug!("expected ptr and length, got {:?}", value),
}
}
}
impl<'a, 'tcx, M: Machine<'tcx>> HasMemory<'a, 'tcx, M> for Memory<'a, 'tcx, M> {
@ -1634,55 +1526,6 @@ impl<'a, 'tcx, M: Machine<'tcx>> HasMemory<'a, 'tcx, M> for EvalContext<'a, 'tcx
}
}
////////////////////////////////////////////////////////////////////////////////
// Pointer arithmetic
////////////////////////////////////////////////////////////////////////////////
pub trait PointerArithmetic: layout::HasDataLayout {
// These are not supposed to be overriden.
//// Trunace the given value to the pointer size; also return whether there was an overflow
fn truncate_to_ptr(self, val: u128) -> (u64, bool) {
let max_ptr_plus_1 = 1u128 << self.data_layout().pointer_size.bits();
((val % max_ptr_plus_1) as u64, val >= max_ptr_plus_1)
}
// Overflow checking only works properly on the range from -u64 to +u64.
fn overflowing_signed_offset(self, val: u64, i: i128) -> (u64, bool) {
// FIXME: is it possible to over/underflow here?
if i < 0 {
// trickery to ensure that i64::min_value() works fine
// this formula only works for true negative values, it panics for zero!
let n = u64::max_value() - (i as u64) + 1;
val.overflowing_sub(n)
} else {
self.overflowing_offset(val, i as u64)
}
}
fn overflowing_offset(self, val: u64, i: u64) -> (u64, bool) {
let (res, over1) = val.overflowing_add(i);
let (res, over2) = self.truncate_to_ptr(res as u128);
(res, over1 || over2)
}
fn signed_offset<'tcx>(self, val: u64, i: i64) -> EvalResult<'tcx, u64> {
let (res, over) = self.overflowing_signed_offset(val, i as i128);
if over { err!(OverflowingMath) } else { Ok(res) }
}
fn offset<'tcx>(self, val: u64, i: u64) -> EvalResult<'tcx, u64> {
let (res, over) = self.overflowing_offset(val, i);
if over { err!(OverflowingMath) } else { Ok(res) }
}
fn wrapping_signed_offset(self, val: u64, i: i64) -> u64 {
self.overflowing_signed_offset(val, i as i128).0
}
}
impl<T: layout::HasDataLayout> PointerArithmetic for T {}
impl<'a, 'tcx, M: Machine<'tcx>> layout::HasDataLayout for &'a Memory<'a, 'tcx, M> {
#[inline]
fn data_layout(&self) -> &TargetDataLayout {

View File

@ -0,0 +1,29 @@
//! An interpreter for MIR used in CTFE and by miri
mod cast;
mod const_eval;
mod eval_context;
mod place;
mod validation;
mod machine;
mod memory;
mod operator;
mod range_map;
mod step;
mod terminator;
mod traits;
pub use self::eval_context::{EvalContext, Frame, ResourceLimits, StackPopCleanup,
TyAndPacked, ValTy};
pub use self::place::{Place, PlaceExtra};
pub use self::memory::{Memory, MemoryKind, HasMemory};
use self::range_map::RangeMap;
pub use self::const_eval::{eval_body_as_integer, eval_body, CompileTimeEvaluator, const_eval_provider};
pub use self::machine::Machine;
pub use self::validation::{ValidationQuery, AbsPlace};

View File

@ -1,12 +1,12 @@
use mir;
use ty::Ty;
use rustc::mir;
use rustc::ty::Ty;
use rustc_const_math::ConstFloat;
use syntax::ast::FloatTy;
use std::cmp::Ordering;
use super::{EvalResult, EvalContext, Place, Machine, ValTy};
use super::{EvalContext, Place, Machine, ValTy};
use super::value::{PrimVal, PrimValKind, Value, bytes_to_f32, bytes_to_f64};
use rustc::mir::interpret::{EvalResult, PrimVal, PrimValKind, Value, bytes_to_f32, bytes_to_f64};
impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
fn binop_with_overflow(
@ -67,7 +67,7 @@ macro_rules! int_arithmetic {
($kind:expr, $int_op:ident, $l:expr, $r:expr) => ({
let l = $l;
let r = $r;
use super::PrimValKind::*;
use rustc::mir::interpret::PrimValKind::*;
match $kind {
I8 => overflow!($int_op, l as i8, r as i8),
I16 => overflow!($int_op, l as i16, r as i16),
@ -115,8 +115,8 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
right: PrimVal,
right_ty: Ty<'tcx>,
) -> EvalResult<'tcx, (PrimVal, bool)> {
use mir::BinOp::*;
use super::PrimValKind::*;
use rustc::mir::BinOp::*;
use rustc::mir::interpret::PrimValKind::*;
let left_kind = self.ty_to_primval_kind(left_ty)?;
let right_kind = self.ty_to_primval_kind(right_ty)?;
@ -228,8 +228,8 @@ pub fn unary_op<'tcx>(
val: PrimVal,
val_kind: PrimValKind,
) -> EvalResult<'tcx, PrimVal> {
use mir::UnOp::*;
use super::PrimValKind::*;
use rustc::mir::UnOp::*;
use rustc::mir::interpret::PrimValKind::*;
let bytes = val.to_bytes()?;

View File

@ -1,9 +1,12 @@
use mir;
use ty::{self, Ty};
use ty::layout::{LayoutOf, TyLayout};
use rustc::mir;
use rustc::ty::{self, Ty};
use rustc::ty::layout::{LayoutOf, TyLayout};
use rustc_data_structures::indexed_vec::Idx;
use rustc::mir::interpret::{GlobalId, PtrAndAlign};
use super::{EvalResult, EvalContext, MemoryPointer, PrimVal, Value, Pointer, Machine, PtrAndAlign, ValTy};
use rustc::mir::interpret::{Value, PrimVal, EvalResult, Pointer, MemoryPointer};
use super::{EvalContext, Machine, ValTy};
use interpret::memory::HasMemory;
#[derive(Copy, Clone, Debug)]
pub enum Place {
@ -29,17 +32,6 @@ pub enum PlaceExtra {
DowncastVariant(usize),
}
/// Uniquely identifies a specific constant or static.
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub struct GlobalId<'tcx> {
/// For a constant or static, the `Instance` of the item itself.
/// For a promoted global, the `Instance` of the function they belong to.
pub instance: ty::Instance<'tcx>,
/// The index for promoted globals within their function's `Mir`.
pub promoted: Option<mir::Promoted>,
}
impl<'tcx> Place {
/// Produces an Place that will error if attempted to be read from
pub fn undef() -> Self {
@ -101,7 +93,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
&mut self,
place: &mir::Place<'tcx>,
) -> EvalResult<'tcx, Option<Value>> {
use mir::Place::*;
use rustc::mir::Place::*;
match *place {
// Might allow this in the future, right now there's no way to do this from Rust code anyway
Local(mir::RETURN_PLACE) => err!(ReadFromReturnPointer),
@ -126,7 +118,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
&mut self,
proj: &mir::PlaceProjection<'tcx>,
) -> EvalResult<'tcx, Option<Value>> {
use mir::ProjectionElem::*;
use rustc::mir::ProjectionElem::*;
let base = match self.try_read_place(&proj.base)? {
Some(base) => base,
None => return Ok(None),
@ -186,7 +178,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
}
pub fn eval_place(&mut self, mir_place: &mir::Place<'tcx>) -> EvalResult<'tcx, Place> {
use mir::Place::*;
use rustc::mir::Place::*;
let place = match *mir_place {
Local(mir::RETURN_PLACE) => self.frame().return_place,
Local(local) => Place::Local {
@ -289,20 +281,20 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
pub(super) fn val_to_place(&self, val: Value, ty: Ty<'tcx>) -> EvalResult<'tcx, Place> {
Ok(match self.tcx.struct_tail(ty).sty {
ty::TyDynamic(..) => {
let (ptr, vtable) = val.into_ptr_vtable_pair(&self.memory)?;
let (ptr, vtable) = self.into_ptr_vtable_pair(val)?;
Place::Ptr {
ptr: PtrAndAlign { ptr, aligned: true },
extra: PlaceExtra::Vtable(vtable),
}
}
ty::TyStr | ty::TySlice(_) => {
let (ptr, len) = val.into_slice(&self.memory)?;
let (ptr, len) = self.into_slice(val)?;
Place::Ptr {
ptr: PtrAndAlign { ptr, aligned: true },
extra: PlaceExtra::Length(len),
}
}
_ => Place::from_primval_ptr(val.into_ptr(&self.memory)?),
_ => Place::from_primval_ptr(self.into_ptr(val)?),
})
}
@ -349,7 +341,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
base_ty: Ty<'tcx>,
proj_elem: &mir::ProjectionElem<'tcx, mir::Local, Ty<'tcx>>,
) -> EvalResult<'tcx, Place> {
use mir::ProjectionElem::*;
use rustc::mir::ProjectionElem::*;
let (ptr, extra) = match *proj_elem {
Field(field, _) => {
let layout = self.layout_of(base_ty)?;

View File

@ -2,15 +2,16 @@
//!
//! The main entry point is the `step` method.
use hir;
use mir::visit::{Visitor, PlaceContext};
use mir;
use ty::{self, Instance};
use ty::layout::LayoutOf;
use middle::const_val::ConstVal;
use rustc::hir;
use rustc::mir::visit::{Visitor, PlaceContext};
use rustc::mir;
use rustc::ty::{self, Instance};
use rustc::ty::layout::LayoutOf;
use rustc::middle::const_val::ConstVal;
use rustc::mir::interpret::{PtrAndAlign, GlobalId};
use super::{EvalResult, EvalContext, StackPopCleanup, PtrAndAlign, GlobalId, Place,
Machine, EvalErrorKind};
use rustc::mir::interpret::{EvalResult, EvalErrorKind};
use super::{EvalContext, StackPopCleanup, Place, Machine};
use syntax::codemap::Span;
use syntax::ast::Mutability;
@ -92,7 +93,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
fn statement(&mut self, stmt: &mir::Statement<'tcx>) -> EvalResult<'tcx> {
trace!("{:?}", stmt);
use mir::StatementKind::*;
use rustc::mir::StatementKind::*;
// Some statements (e.g. box) push new stack frames. We have to record the stack frame number
// *before* executing the statement.

View File

@ -1,9 +1,9 @@
use mir::BasicBlock;
use ty::{self, Ty};
use rustc::mir::BasicBlock;
use rustc::ty::{self, Ty};
use syntax::codemap::Span;
use mir::interpret::{EvalResult, EvalContext, Place, PlaceExtra, PrimVal, Value,
Machine, ValTy};
use rustc::mir::interpret::{EvalResult, PrimVal, Value};
use interpret::{Machine, ValTy, EvalContext, Place, PlaceExtra};
impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
pub(crate) fn drop_place(

View File

@ -1,13 +1,15 @@
use mir;
use ty::{self, Ty};
use ty::layout::LayoutOf;
use rustc::mir;
use rustc::ty::{self, Ty};
use rustc::ty::layout::LayoutOf;
use syntax::codemap::Span;
use syntax::abi::Abi;
use super::{EvalResult, EvalContext, eval_context,
PtrAndAlign, Place, PrimVal, Value, Machine, ValTy};
use rustc::mir::interpret::{PtrAndAlign, EvalResult, PrimVal, Value};
use super::{EvalContext, eval_context,
Place, Machine, ValTy};
use rustc_data_structures::indexed_vec::Idx;
use interpret::memory::HasMemory;
mod drop;
@ -21,7 +23,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
&mut self,
terminator: &mir::Terminator<'tcx>,
) -> EvalResult<'tcx> {
use mir::TerminatorKind::*;
use rustc::mir::TerminatorKind::*;
match terminator.kind {
Return => {
self.dump_local(self.frame().return_place);
@ -138,7 +140,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
if expected == cond_val {
self.goto_block(target);
} else {
use mir::AssertMessage::*;
use rustc::mir::AssertMessage::*;
return match *msg {
BoundsCheck { ref len, ref index } => {
let span = terminator.source_info.span;
@ -401,7 +403,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
// cannot use the shim here, because that will only result in infinite recursion
ty::InstanceDef::Virtual(_, idx) => {
let ptr_size = self.memory.pointer_size();
let (ptr, vtable) = args[0].into_ptr_vtable_pair(&self.memory)?;
let (ptr, vtable) = self.into_ptr_vtable_pair(args[0].value)?;
let fn_ptr = self.memory.read_ptr_sized_unsigned(
vtable.offset(ptr_size * (idx as u64 + 3), &self)?
)?.to_ptr()?;

View File

@ -1,8 +1,9 @@
use ty::{self, Ty};
use ty::layout::{Size, Align, LayoutOf};
use rustc::ty::{self, Ty};
use rustc::ty::layout::{Size, Align, LayoutOf};
use syntax::ast::Mutability;
use super::{EvalResult, EvalContext, eval_context, MemoryPointer, Value, PrimVal,
use rustc::mir::interpret::{PrimVal, Value, MemoryPointer, EvalResult};
use super::{EvalContext, eval_context,
Machine};
impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {

View File

@ -1,17 +1,18 @@
use hir::{self, Mutability};
use hir::Mutability::*;
use mir::{self, ValidationOp, ValidationOperand};
use ty::{self, Ty, TypeFoldable, TyCtxt};
use ty::layout::LayoutOf;
use ty::subst::{Substs, Subst};
use traits;
use infer::InferCtxt;
use traits::Reveal;
use middle::region;
use rustc::hir::{self, Mutability};
use rustc::hir::Mutability::*;
use rustc::mir::{self, ValidationOp, ValidationOperand};
use rustc::ty::{self, Ty, TypeFoldable, TyCtxt};
use rustc::ty::layout::LayoutOf;
use rustc::ty::subst::{Substs, Subst};
use rustc::traits;
use rustc::infer::InferCtxt;
use rustc::traits::Reveal;
use rustc::middle::region;
use rustc_data_structures::indexed_vec::Idx;
use interpret::memory::HasMemory;
use super::{EvalError, EvalResult, EvalErrorKind, EvalContext, DynamicLifetime, AccessKind, Value,
Place, PlaceExtra, Machine, ValTy};
use super::{EvalContext, Place, PlaceExtra, Machine, ValTy};
use rustc::mir::interpret::{DynamicLifetime, AccessKind, EvalErrorKind, Value, EvalError, EvalResult};
pub type ValidationQuery<'tcx> = ValidationOperand<'tcx, (AbsPlace<'tcx>, Place)>;
@ -407,7 +408,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
// ADTs.
ty::TyAdt(def, substs) => {
use ty::layout::Variants;
use rustc::ty::layout::Variants;
match layout.variants {
Variants::Single { index } => {
def.variants[index].fields[i].ty(tcx, substs)
@ -469,7 +470,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
) -> EvalResult<'tcx> {
// Check alignment and non-NULLness
let (_, align) = self.size_and_align_of_dst(pointee_ty, val)?;
let ptr = val.into_ptr(&self.memory)?;
let ptr = self.into_ptr(val)?;
self.memory.check_align(ptr, align.abi(), None)?;
// Recurse
@ -491,9 +492,9 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
mut query: ValidationQuery<'tcx>,
mode: ValidationMode,
) -> EvalResult<'tcx> {
use ty::TypeVariants::*;
use ty::RegionKind::*;
use ty::AdtKind;
use rustc::ty::TypeVariants::*;
use rustc::ty::RegionKind::*;
use rustc::ty::AdtKind;
// No point releasing shared stuff.
if !mode.acquiring() && query.mutbl == MutImmutable {
@ -645,9 +646,8 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
self.validate_ptr(val, query.place.0, query.ty.boxed_ty(), query.re, query.mutbl, mode)
}
TyFnPtr(_sig) => {
let ptr = self.read_place(query.place.1)?
.into_ptr(&self.memory)?
.to_ptr()?;
let ptr = self.read_place(query.place.1)?;
let ptr = self.into_ptr(ptr)?.to_ptr()?;
self.memory.get_fn(ptr)?;
// TODO: Check if the signature matches (should be the same check as what terminator/mod.rs already does on call?).
Ok(())

View File

@ -25,7 +25,10 @@ Rust MIR: a lowered representation of Rust. Also: an experiment!
#![feature(decl_macro)]
#![feature(i128_type)]
#![feature(inclusive_range_syntax)]
#![feature(inclusive_range)]
#![feature(macro_vis_matcher)]
#![feature(match_default_bindings)]
#![feature(never_type)]
#![feature(range_contains)]
#![feature(rustc_diagnostic_macros)]
#![feature(placement_in_syntax)]
@ -48,6 +51,12 @@ extern crate syntax_pos;
extern crate rustc_const_math;
extern crate rustc_const_eval;
extern crate core; // for NonZero
extern crate log_settings;
#[macro_use]
extern crate lazy_static;
extern crate rustc_apfloat;
extern crate regex;
extern crate byteorder;
mod diagnostics;
@ -58,6 +67,7 @@ mod hair;
mod shim;
pub mod transform;
pub mod util;
mod interpret;
use rustc::ty::maps::Providers;
@ -65,6 +75,7 @@ pub fn provide(providers: &mut Providers) {
borrow_check::provide(providers);
shim::provide(providers);
transform::provide(providers);
providers.const_eval = interpret::const_eval_provider;
}
__build_diagnostic_array! { librustc_mir, DIAGNOSTICS }

View File

@ -67,6 +67,7 @@ fn filter_dirs(path: &Path) -> bool {
"src/tools/rustfmt",
"src/tools/miri",
"src/librustc/mir/interpret",
"src/librustc_mir/interpret",
];
skip.iter().any(|p| path.ends_with(p))
}