From acac58502b3d86c5808d8d152d7870f8c6423074 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 12 Dec 2017 17:14:49 +0100 Subject: [PATCH] Move large chunks of miri from rustc::mir::interpret to rustc_mir::interpret --- .travis.yml | 1 + src/Cargo.lock | 13 +- src/librustc/Cargo.toml | 2 - src/librustc/lib.rs | 3 - src/librustc/mir/interpret/const_eval.rs | 288 --------- src/librustc/mir/interpret/mod.rs | 272 +++++++- src/librustc/mir/interpret/value.rs | 95 +-- src/librustc_const_eval/eval.rs | 295 --------- src/librustc_const_eval/lib.rs | 1 - src/librustc_mir/Cargo.toml | 5 + .../mir => librustc_mir}/interpret/cast.rs | 13 +- src/librustc_mir/interpret/const_eval.rs | 587 ++++++++++++++++++ .../interpret/eval_context.rs | 74 +-- .../mir => librustc_mir}/interpret/machine.rs | 7 +- .../mir => librustc_mir}/interpret/memory.rs | 313 +++------- src/librustc_mir/interpret/mod.rs | 29 + .../interpret/operator.rs | 18 +- .../mir => librustc_mir}/interpret/place.rs | 36 +- .../interpret/range_map.rs | 0 .../mir => librustc_mir}/interpret/step.rs | 19 +- .../interpret/terminator/drop.rs | 8 +- .../interpret/terminator/mod.rs | 18 +- .../mir => librustc_mir}/interpret/traits.rs | 7 +- .../interpret/validation.rs | 40 +- src/librustc_mir/lib.rs | 11 + src/tools/tidy/src/lib.rs | 1 + 26 files changed, 1103 insertions(+), 1053 deletions(-) delete mode 100644 src/librustc/mir/interpret/const_eval.rs rename src/{librustc/mir => librustc_mir}/interpret/cast.rs (93%) create mode 100644 src/librustc_mir/interpret/const_eval.rs rename src/{librustc/mir => librustc_mir}/interpret/eval_context.rs (97%) rename src/{librustc/mir => librustc_mir}/interpret/machine.rs (95%) rename src/{librustc/mir => librustc_mir}/interpret/memory.rs (87%) create mode 100644 src/librustc_mir/interpret/mod.rs rename src/{librustc/mir => librustc_mir}/interpret/operator.rs (96%) rename src/{librustc/mir => librustc_mir}/interpret/place.rs (94%) rename src/{librustc/mir => librustc_mir}/interpret/range_map.rs (100%) rename src/{librustc/mir => librustc_mir}/interpret/step.rs (96%) rename src/{librustc/mir => librustc_mir}/interpret/terminator/drop.rs (93%) rename src/{librustc/mir => librustc_mir}/interpret/terminator/mod.rs (97%) rename src/{librustc/mir => librustc_mir}/interpret/traits.rs (94%) rename src/{librustc/mir => librustc_mir}/interpret/validation.rs (97%) diff --git a/.travis.yml b/.travis.yml index b2840ac3121f8..93c2834b7d873 100644 --- a/.travis.yml +++ b/.travis.yml @@ -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 diff --git a/src/Cargo.lock b/src/Cargo.lock index 10c94184f9372..37b5e57ebf444 100644 --- a/src/Cargo.lock +++ b/src/Cargo.lock @@ -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" diff --git a/src/librustc/Cargo.toml b/src/librustc/Cargo.toml index 4222e81d2a3dd..29eedc49be04b 100644 --- a/src/librustc/Cargo.toml +++ b/src/librustc/Cargo.toml @@ -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"]} diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index 0c3ffdc511def..c7d2d136af1b3 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -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; diff --git a/src/librustc/mir/interpret/const_eval.rs b/src/librustc/mir/interpret/const_eval.rs deleted file mode 100644 index dd8bf09586acc..0000000000000 --- a/src/librustc/mir/interpret/const_eval.rs +++ /dev/null @@ -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> 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(), - ) - } -} diff --git a/src/librustc/mir/interpret/mod.rs b/src/librustc/mir/interpret/mod.rs index 9c1a3f09432d7..c5d2ec1668c82 100644 --- a/src/librustc/mir/interpret/mod.rs +++ b/src/librustc/mir/interpret/mod.rs @@ -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), +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +pub struct DynamicLifetime { + pub frame: usize, + pub region: Option, // "None" indicates "until the function ends" +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum AccessKind { + Read, + Write, +} + +/// 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, +} + +//////////////////////////////////////////////////////////////////////////////// +// 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 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 } + } -use self::memory::{PointerArithmetic, Lock}; + pub(crate) fn wrapping_signed_offset(self, i: i64, cx: C) -> Self { + MemoryPointer::new( + self.alloc_id, + cx.data_layout().wrapping_signed_offset(self.offset, i), + ) + } -use self::range_map::RangeMap; + pub fn overflowing_signed_offset(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 use self::value::{PrimVal, PrimValKind, Value, Pointer}; + pub(crate) fn signed_offset(self, i: i64, cx: C) -> EvalResult<'tcx, Self> { + Ok(MemoryPointer::new( + self.alloc_id, + cx.data_layout().signed_offset(self.offset, i)?, + )) + } -pub use self::const_eval::{eval_body_as_integer, eval_body, CompileTimeEvaluator}; + pub fn overflowing_offset(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 use self::machine::Machine; + pub fn offset(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, + /// Maps from byte addresses to allocations. + /// Only the first byte of a pointer is inserted into the map. + pub relocations: BTreeMap, + /// 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; -pub use self::validation::{ValidationQuery, AbsPlace}; +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct UndefMask { + blocks: Vec, + 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) +} diff --git a/src/librustc/mir/interpret/value.rs b/src/librustc/mir/interpret/value.rs index 89dad0052d880..33b177b60a81b 100644 --- a/src/librustc/mir/interpret/value.rs +++ b/src/librustc/mir/interpret/value.rs @@ -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>( - &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>( - &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>( - &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 { diff --git a/src/librustc_const_eval/eval.rs b/src/librustc_const_eval/eval.rs index 95b6dc80b14a0..81cd63b5407c3 100644 --- a/src/librustc_const_eval/eval.rs +++ b/src/librustc_const_eval/eval.rs @@ -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, 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 { - match res { - Ok(val) => val, - Err(mut err) => { - ecx.report(&mut err); - ecx.tcx.sess.abort_if_errors(); - bug!("{:#?}", err); - } - } -} diff --git a/src/librustc_const_eval/lib.rs b/src/librustc_const_eval/lib.rs index d4110f0091aeb..9d636b48bd0c5 100644 --- a/src/librustc_const_eval/lib.rs +++ b/src/librustc_const_eval/lib.rs @@ -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 }; diff --git a/src/librustc_mir/Cargo.toml b/src/librustc_mir/Cargo.toml index b7a576babeb67..1846b753ffd5b 100644 --- a/src/librustc_mir/Cargo.toml +++ b/src/librustc_mir/Cargo.toml @@ -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" } diff --git a/src/librustc/mir/interpret/cast.rs b/src/librustc_mir/interpret/cast.rs similarity index 93% rename from src/librustc/mir/interpret/cast.rs rename to src/librustc_mir/interpret/cast.rs index e6d163df4d455..6f4a28fb28f01 100644 --- a/src/librustc/mir/interpret/cast.rs +++ b/src/librustc_mir/interpret/cast.rs @@ -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(_) | diff --git a/src/librustc_mir/interpret/const_eval.rs b/src/librustc_mir/interpret/const_eval.rs new file mode 100644 index 0000000000000..a78cd8477617f --- /dev/null +++ b/src/librustc_mir/interpret/const_eval.rs @@ -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> 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, 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 { + match res { + Ok(val) => val, + Err(mut err) => { + ecx.report(&mut err); + ecx.tcx.sess.abort_if_errors(); + bug!("{:#?}", err); + } + } +} diff --git a/src/librustc/mir/interpret/eval_context.rs b/src/librustc_mir/interpret/eval_context.rs similarity index 97% rename from src/librustc/mir/interpret/eval_context.rs rename to src/librustc_mir/interpret/eval_context.rs index f23946fbc1649..fd83213a26eec 100644 --- a/src/librustc/mir/interpret/eval_context.rs +++ b/src/librustc_mir/interpret/eval_context.rs @@ -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, // "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 { - 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() } diff --git a/src/librustc/mir/interpret/machine.rs b/src/librustc_mir/interpret/machine.rs similarity index 95% rename from src/librustc/mir/interpret/machine.rs rename to src/librustc_mir/interpret/machine.rs index 3fbc69b80e58c..c08deb636bb2f 100644 --- a/src/librustc/mir/interpret/machine.rs +++ b/src/librustc_mir/interpret/machine.rs @@ -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; diff --git a/src/librustc/mir/interpret/memory.rs b/src/librustc_mir/interpret/memory.rs similarity index 87% rename from src/librustc/mir/interpret/memory.rs rename to src/librustc_mir/interpret/memory.rs index 77796638a7b94..974979eda7fdf 100644 --- a/src/librustc/mir/interpret/memory.rs +++ b/src/librustc_mir/interpret/memory.rs @@ -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), -} -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, - /// Maps from byte addresses to allocations. - /// Only the first byte of a pointer is inserted into the map. - pub relocations: BTreeMap, - /// 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 { /// Error if deallocated except during a stack pop @@ -137,49 +92,6 @@ pub enum MemoryKind { 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(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(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(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(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(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, - 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 PointerArithmetic for T {} - impl<'a, 'tcx, M: Machine<'tcx>> layout::HasDataLayout for &'a Memory<'a, 'tcx, M> { #[inline] fn data_layout(&self) -> &TargetDataLayout { diff --git a/src/librustc_mir/interpret/mod.rs b/src/librustc_mir/interpret/mod.rs new file mode 100644 index 0000000000000..c6c8ad8b7c945 --- /dev/null +++ b/src/librustc_mir/interpret/mod.rs @@ -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}; diff --git a/src/librustc/mir/interpret/operator.rs b/src/librustc_mir/interpret/operator.rs similarity index 96% rename from src/librustc/mir/interpret/operator.rs rename to src/librustc_mir/interpret/operator.rs index 6058ac7e72958..6ab1aec38b863 100644 --- a/src/librustc/mir/interpret/operator.rs +++ b/src/librustc_mir/interpret/operator.rs @@ -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()?; diff --git a/src/librustc/mir/interpret/place.rs b/src/librustc_mir/interpret/place.rs similarity index 94% rename from src/librustc/mir/interpret/place.rs rename to src/librustc_mir/interpret/place.rs index 7a5f3e80dc9fa..538d768d3b0e4 100644 --- a/src/librustc/mir/interpret/place.rs +++ b/src/librustc_mir/interpret/place.rs @@ -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, -} - 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> { - 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> { - 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)?; diff --git a/src/librustc/mir/interpret/range_map.rs b/src/librustc_mir/interpret/range_map.rs similarity index 100% rename from src/librustc/mir/interpret/range_map.rs rename to src/librustc_mir/interpret/range_map.rs diff --git a/src/librustc/mir/interpret/step.rs b/src/librustc_mir/interpret/step.rs similarity index 96% rename from src/librustc/mir/interpret/step.rs rename to src/librustc_mir/interpret/step.rs index b67e38342e908..0e137f5cb5a6a 100644 --- a/src/librustc/mir/interpret/step.rs +++ b/src/librustc_mir/interpret/step.rs @@ -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. diff --git a/src/librustc/mir/interpret/terminator/drop.rs b/src/librustc_mir/interpret/terminator/drop.rs similarity index 93% rename from src/librustc/mir/interpret/terminator/drop.rs rename to src/librustc_mir/interpret/terminator/drop.rs index c24f18d42b0c3..5db46149834d2 100644 --- a/src/librustc/mir/interpret/terminator/drop.rs +++ b/src/librustc_mir/interpret/terminator/drop.rs @@ -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( diff --git a/src/librustc/mir/interpret/terminator/mod.rs b/src/librustc_mir/interpret/terminator/mod.rs similarity index 97% rename from src/librustc/mir/interpret/terminator/mod.rs rename to src/librustc_mir/interpret/terminator/mod.rs index a903c9e06e161..1cdfe1ff9ceac 100644 --- a/src/librustc/mir/interpret/terminator/mod.rs +++ b/src/librustc_mir/interpret/terminator/mod.rs @@ -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()?; diff --git a/src/librustc/mir/interpret/traits.rs b/src/librustc_mir/interpret/traits.rs similarity index 94% rename from src/librustc/mir/interpret/traits.rs rename to src/librustc_mir/interpret/traits.rs index 47ac287599575..c73b95c717c3d 100644 --- a/src/librustc/mir/interpret/traits.rs +++ b/src/librustc_mir/interpret/traits.rs @@ -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> { diff --git a/src/librustc/mir/interpret/validation.rs b/src/librustc_mir/interpret/validation.rs similarity index 97% rename from src/librustc/mir/interpret/validation.rs rename to src/librustc_mir/interpret/validation.rs index 86ad5399e8427..740a2c53e2f73 100644 --- a/src/librustc/mir/interpret/validation.rs +++ b/src/librustc_mir/interpret/validation.rs @@ -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(()) diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index 53f9b885ac6c6..1f90de087b0d7 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -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 } diff --git a/src/tools/tidy/src/lib.rs b/src/tools/tidy/src/lib.rs index 02f665e3991bc..3fd844f326184 100644 --- a/src/tools/tidy/src/lib.rs +++ b/src/tools/tidy/src/lib.rs @@ -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)) }