Move large chunks of miri from rustc::mir::interpret to rustc_mir::interpret
This commit is contained in:
parent
8c2ec689c1
commit
acac58502b
@ -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
13
src/Cargo.lock
generated
@ -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"
|
||||
|
@ -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"]}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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(),
|
||||
)
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
};
|
||||
|
@ -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" }
|
||||
|
@ -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(_) |
|
587
src/librustc_mir/interpret/const_eval.rs
Normal file
587
src/librustc_mir/interpret/const_eval.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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()
|
||||
}
|
@ -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;
|
||||
|
@ -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 {
|
29
src/librustc_mir/interpret/mod.rs
Normal file
29
src/librustc_mir/interpret/mod.rs
Normal 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};
|
@ -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()?;
|
||||
|
@ -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)?;
|
@ -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.
|
@ -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(
|
@ -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()?;
|
@ -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> {
|
@ -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(())
|
@ -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 }
|
||||
|
@ -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))
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user