diff --git a/Cargo.lock b/Cargo.lock index ae148d2cd131135aab980236b1c5688053a5aea9..fdaeb0fac088793922ff36675b7ecd5fd1fc82b9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -21,15 +21,31 @@ dependencies = [ "serde_json 0.9.10 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "cfg-if" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "compiletest_rs" -version = "0.2.10" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ + "diff 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", + "filetime 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", + "getopts 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.30 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "diff" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "dtoa" version = "0.4.2" @@ -44,6 +60,21 @@ dependencies = [ "regex 0.1.80 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "filetime" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.30 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "getopts" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "itoa" version = "0.3.3" @@ -94,6 +125,29 @@ dependencies = [ "libc 0.2.30 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "miow" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "net2 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "net2" +version = "0.2.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.30 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "num-traits" version = "0.1.40" @@ -104,6 +158,11 @@ name = "quote" version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "redox_syscall" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "regex" version = "0.1.80" @@ -132,7 +191,7 @@ version = "0.0.1" dependencies = [ "byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "cargo_metadata 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "compiletest_rs 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", + "compiletest_rs 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.3.5 (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)", @@ -245,6 +304,15 @@ name = "winapi-build" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "ws2_32-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "z3-sys" version = "0.1.0" @@ -258,9 +326,13 @@ dependencies = [ "checksum aho-corasick 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ca972c2ea5f742bfce5687b9aef75506a764f61d37f8f649047846a9686ddb66" "checksum byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff81738b726f5d099632ceaffe7fb65b90212e8dce59d518729e7e8634032d3d" "checksum cargo_metadata 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "34fdab49a2904acb112c83b62f0118de3de3ce28e52a9188dec2858e43878f25" -"checksum compiletest_rs 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "2741d378feb7a434dba54228c89a70b4e427fee521de67cdda3750b8a0265f5a" +"checksum cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de" +"checksum compiletest_rs 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "562bafeec9aef1e3e08f1c5b0c542220bb80ff2894e5373a1f9d17c346412c66" +"checksum diff 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "3c2b69f912779fbb121ceb775d74d51e915af17aaebc38d28a592843a2dd0a3a" "checksum dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "09c3753c3db574d215cba4ea76018483895d7bff25a31b49ba45db21c48e50ab" "checksum env_logger 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "15abd780e45b3ea4f76b4e9a26ff4843258dd8a3eed2775a0e7368c2e7936c2f" +"checksum filetime 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "aa75ec8f7927063335a9583e7fa87b0110bb888cf766dc01b54c0ff70d760c8e" +"checksum getopts 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)" = "65922871abd2f101a2eb0eaebadc66668e54a87ad9c3dd82520b5f86ede5eff9" "checksum itoa 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ac17257442c2ed77dbc9fd555cf83c58b0c7f7d0e8f2ae08c0ac05c72842e1f6" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" "checksum lazy_static 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "cf186d1a8aa5f5bee5fd662bc9c1b949e0259e1bcc379d1f006847b0080c7417" @@ -269,8 +341,11 @@ dependencies = [ "checksum log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "880f77541efa6e5cc74e76910c9884d9859683118839d6a1dc3b11e63512565b" "checksum log_settings 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3d382732ea0fbc09790c4899db3255bdea0fc78b54bf234bd18a63bb603915b6" "checksum memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d8b629fb514376c675b98c1421e80b151d3817ac42d7c667717d282761418d20" +"checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" +"checksum net2 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)" = "3a80f842784ef6c9a958b68b7516bc7e35883c614004dd94959a4dca1b716c09" "checksum num-traits 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "99843c856d68d8b4313b03a17e33c4bb42ae8f6610ea81b28abe076ac721b9b0" "checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" +"checksum redox_syscall 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)" = "8dde11f18c108289bef24469638a04dce49da56084f2d50618b226e47eb04509" "checksum regex 0.1.80 (registry+https://github.com/rust-lang/crates.io-index)" = "4fd4ace6a8cf7860714a2c2280d6c1f7e6a413486c13298bbc86fd3da019402f" "checksum regex-syntax 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "f9ec002c35e86791825ed294b50008eea9ddfc8def4420124fbc6b08db834957" "checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" @@ -288,4 +363,5 @@ dependencies = [ "checksum va_list 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "35dd61c2a3fd97881058e5b401d981e8647d0630c64065b2a65c329049d6b5ef" "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" "checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" +"checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" "checksum z3-sys 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "09ad369c3bebfb2b5585060ce765c1e0c4896b5a3bc58dd3870e9f73c0209b98" diff --git a/Cargo.toml b/Cargo.toml index 092ada206c370ad6be7b9e22548138a34e9698d9..07604e0926cce177d41cabea39f072220b9c0c29 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,4 +24,4 @@ cargo_metadata = "0.1" seer-z3 = "0.1.0" [dev-dependencies] -compiletest_rs = "0.2.6" +compiletest_rs = "0.3.2" diff --git a/src/cast.rs b/src/cast.rs index 9f436c290499400f66d1a0bb55879c97f23b63db..f01e8ac5261dde844015399ae1cac9f597c33bf0 100644 --- a/src/cast.rs +++ b/src/cast.rs @@ -1,6 +1,7 @@ use rustc::ty::{self, Ty}; -use syntax::ast::{FloatTy, IntTy, UintTy}; +use syntax::ast::{IntTy, UintTy}; +use rustc_const_math::ConstFloat; use error::{EvalResult, EvalError}; use eval_context::EvalContext; use memory::{Pointer, SByte}; @@ -13,14 +14,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { src_ty: Ty<'tcx>, dest_ty: Ty<'tcx> ) -> EvalResult<'tcx, PrimVal> { - let kind = self.ty_to_primval_kind(src_ty)?; + let src_kind = self.ty_to_primval_kind(src_ty)?; use value::PrimValKind::*; match val { PrimVal::Abstract(mut sbytes) => { let dest_kind = self.ty_to_primval_kind(dest_ty)?; - if (kind.is_int() || kind == Char) && (dest_kind.is_int() || kind == Char) { - let src_size = kind.num_bytes(); + if (src_kind.is_int() || src_kind == Char) && (dest_kind.is_int() || src_kind == Char) { + let src_size = src_kind.num_bytes(); let dest_size = dest_kind.num_bytes(); for idx in dest_size .. src_size { sbytes[idx] = SByte::Concrete(0); @@ -28,7 +29,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // TODO(optimization): check to see if the cast has made // the value concrete. Ok(PrimVal::Abstract(sbytes)) - } else if kind == Bool && dest_kind.is_int() { + } else if src_kind == Bool && dest_kind.is_int() { let dest_kind = self.ty_to_primval_kind(dest_ty)?; let primval = self.memory.constraints.add_if_then_else( val, @@ -40,91 +41,92 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { unimplemented!() } } - _ => { - match kind { - F32 => self.cast_float(val.to_f32()? as f64, dest_ty), - F64 => self.cast_float(val.to_f64()?, dest_ty), - - I8 | I16 | I32 | I64 | I128 => self.cast_signed_int(val.to_i128()?, dest_ty), - - Bool | Char | U8 | U16 | U32 | U64 | U128 => self.cast_int(val.to_u128()?, dest_ty, false), + PrimVal::Undef => Ok(PrimVal::Undef), + PrimVal::Ptr(ptr) => self.cast_from_ptr(ptr, dest_ty), + val @ PrimVal::Bytes(_) => { + use super::PrimValKind::*; + match src_kind { + F32 => unimplemented!(),//self.cast_from_float(val.to_f32()?, dest_ty), + F64 => unimplemented!(),//self.cast_from_float(val.to_f64()?, dest_ty), + + I8 | I16 | I32 | I64 | I128 => { + self.cast_from_signed_int(val.to_i128()?, dest_ty) + } - FnPtr | Ptr => self.cast_ptr(val.to_ptr()?, dest_ty), + Bool | Char | U8 | U16 | U32 | U64 | U128 | FnPtr | Ptr => { + self.cast_from_int(val.to_u128()?, dest_ty, false) + } } } } } - fn cast_signed_int(&self, val: i128, ty: ty::Ty<'tcx>) -> EvalResult<'tcx, PrimVal> { - self.cast_int(val as u128, ty, val < 0) + fn cast_from_signed_int(&self, val: i128, ty: ty::Ty<'tcx>) -> EvalResult<'tcx, PrimVal> { + self.cast_from_int(val as u128, ty, val < 0) } - fn cast_int(&self, v: u128, ty: ty::Ty<'tcx>, negative: bool) -> EvalResult<'tcx, PrimVal> { - use rustc::ty::TypeVariants::*; - match ty.sty { - TyBool if v == 0 => Ok(PrimVal::from_bool(false)), - TyBool if v == 1 => Ok(PrimVal::from_bool(true)), - TyBool => Err(EvalError::InvalidBool), - - TyInt(IntTy::I8) => Ok(PrimVal::Bytes(v as i128 as i8 as u128)), - TyInt(IntTy::I16) => Ok(PrimVal::Bytes(v as i128 as i16 as u128)), - TyInt(IntTy::I32) => Ok(PrimVal::Bytes(v as i128 as i32 as u128)), - TyInt(IntTy::I64) => Ok(PrimVal::Bytes(v as i128 as i64 as u128)), - TyInt(IntTy::I128) => Ok(PrimVal::Bytes(v as u128)), - - TyUint(UintTy::U8) => Ok(PrimVal::Bytes(v as u8 as u128)), - TyUint(UintTy::U16) => Ok(PrimVal::Bytes(v as u16 as u128)), - TyUint(UintTy::U32) => Ok(PrimVal::Bytes(v as u32 as u128)), - TyUint(UintTy::U64) => Ok(PrimVal::Bytes(v as u64 as u128)), - TyUint(UintTy::U128) => Ok(PrimVal::Bytes(v)), - - TyInt(IntTy::Is) => { - let int_ty = self.tcx.sess.target.isize_ty; - let ty = self.tcx.mk_mach_int(int_ty); - self.cast_int(v, ty, negative) + fn int_to_int(&self, v: i128, ty: IntTy) -> u128 { + match ty { + IntTy::I8 => v as i8 as u128, + IntTy::I16 => v as i16 as u128, + IntTy::I32 => v as i32 as u128, + IntTy::I64 => v as i64 as u128, + IntTy::I128 => v as u128, + IntTy::Is => { + let ty = self.tcx.sess.target.isize_ty; + self.int_to_int(v, ty) } - - TyUint(UintTy::Us) => { - let uint_ty = self.tcx.sess.target.usize_ty; - let ty = self.tcx.mk_mach_uint(uint_ty); - self.cast_int(v, ty, negative) + } + } + fn int_to_uint(&self, v: u128, ty: UintTy) -> u128 { + match ty { + UintTy::U8 => v as u8 as u128, + UintTy::U16 => v as u16 as u128, + UintTy::U32 => v as u32 as u128, + UintTy::U64 => v as u64 as u128, + UintTy::U128 => v, + UintTy::Us => { + let ty = self.tcx.sess.target.usize_ty; + self.int_to_uint(v, ty) } - - TyFloat(FloatTy::F64) if negative => Ok(PrimVal::from_f64(v as i128 as f64)), - TyFloat(FloatTy::F64) => Ok(PrimVal::from_f64(v as f64)), - TyFloat(FloatTy::F32) if negative => Ok(PrimVal::from_f32(v as i128 as f32)), - TyFloat(FloatTy::F32) => Ok(PrimVal::from_f32(v as f32)), - - TyChar if v as u8 as u128 == v => Ok(PrimVal::Bytes(v)), - TyChar => Err(EvalError::InvalidChar(v)), - - TyRawPtr(_) => Ok(PrimVal::Bytes(v % (1 << self.memory.pointer_size()))), - - _ => Err(EvalError::Unimplemented(format!("int to {:?} cast", ty))), } } - fn cast_float(&self, val: f64, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> { + fn cast_from_int( + &self, + v: u128, + ty: ty::Ty<'tcx>, + negative: bool, + ) -> EvalResult<'tcx, PrimVal> { + trace!("cast_from_int: {}, {}, {}", v, ty, negative); use rustc::ty::TypeVariants::*; match ty.sty { - // Casting negative floats to unsigned integers yields zero. - TyUint(_) if val < 0.0 => self.cast_int(0, ty, false), - TyInt(_) if val < 0.0 => self.cast_int(val as i128 as u128, ty, true), + // 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))), + TyUint(ty) => Ok(PrimVal::Bytes(self.int_to_uint(v, ty))), + + TyFloat(fty) if negative => Ok(PrimVal::Bytes(ConstFloat::from_i128(v as i128, fty).bits)), + TyFloat(fty) => Ok(PrimVal::Bytes(ConstFloat::from_u128(v, fty).bits)), + + TyChar if v as u8 as u128 == v => Ok(PrimVal::Bytes(v)), + TyChar => return Err(EvalError::InvalidChar(v)), - TyInt(_) | ty::TyUint(_) => self.cast_int(val as u128, ty, false), + // No alignment check needed for raw pointers. But we have to truncate to target ptr size. + TyRawPtr(_) => Ok(PrimVal::Bytes(self.memory.truncate_to_ptr(v).0 as u128)), - TyFloat(FloatTy::F64) => Ok(PrimVal::from_f64(val)), - TyFloat(FloatTy::F32) => Ok(PrimVal::from_f32(val as f32)), - _ => Err(EvalError::Unimplemented(format!("float to {:?} cast", ty))), + _ => return Err(EvalError::Unimplemented(format!("int to {:?} cast", ty))), } } - fn cast_ptr(&self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> { + fn cast_from_ptr(&self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> { use rustc::ty::TypeVariants::*; match ty.sty { - TyRef(..) | TyRawPtr(_) | TyFnPtr(_) | TyInt(_) | TyUint(_) => - Ok(PrimVal::Ptr(ptr)), - _ => Err(EvalError::Unimplemented(format!("ptr to {:?} cast", ty))), + // Casting to a reference or fn pointer is not permitted by rustc, no need to support it here. + TyRawPtr(_) | + TyInt(IntTy::Is) | + TyUint(UintTy::Us) => Ok(PrimVal::Ptr(ptr)), + TyInt(_) | TyUint(_) => return Err(EvalError::ReadPointerAsBytes), + _ => return Err(EvalError::Unimplemented(format!("ptr to {:?} cast", ty))), } } } diff --git a/src/error.rs b/src/error.rs index b146e57e58f3b216de868314790d4fbf89252359..053a5c8beb62c1cfd9fcf492e443c9421c64be51 100644 --- a/src/error.rs +++ b/src/error.rs @@ -60,6 +60,7 @@ pub enum EvalError<'tcx> { Unreachable, Panic, ReadFromReturnPointer, + TypeckError, } pub type EvalResult<'tcx, T = ()> = Result<T, EvalError<'tcx>>; @@ -148,6 +149,8 @@ impl<'tcx> Error for EvalError<'tcx> { "the evaluated program panicked", EvalError::ReadFromReturnPointer => "tried to read from the return pointer", + EvalError::TypeckError => + "encountered constants with type errors, stopping evaluation", } } @@ -245,6 +248,7 @@ pub enum StaticEvalError { Unreachable, Panic, ReadFromReturnPointer, + TypeckError, } impl <'tcx> From<EvalError<'tcx>> for StaticEvalError { @@ -332,6 +336,8 @@ impl <'tcx> From<EvalError<'tcx>> for StaticEvalError { StaticEvalError::Panic, EvalError::ReadFromReturnPointer => StaticEvalError::ReadFromReturnPointer, + EvalError::TypeckError => + StaticEvalError::TypeckError, } } } diff --git a/src/eval_context.rs b/src/eval_context.rs index c91fd45792388d7574cf0ea22169eb8ed598359b..43cd9fe56640220796ae7af798b4b167d58abbe4 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -5,13 +5,12 @@ use rustc::hir::def_id::DefId; use rustc::middle::const_val::ConstVal; use rustc::mir; use rustc::traits::{Reveal}; -use rustc::ty::layout::{self, Layout, Size}; +use rustc::ty::layout::{self, Size, HasDataLayout, LayoutOf, TyLayout}; use rustc::ty::subst::{Subst, Substs, Kind}; use rustc::ty::{self, Ty, TyCtxt, TypeFoldable, Binder}; use rustc_data_structures::indexed_vec::Idx; use syntax::codemap::{self, DUMMY_SP}; use syntax::ast; -use syntax::abi::Abi; use error::{EvalError, EvalResult}; use lvalue::{Global, GlobalId, Lvalue, LvalueExtra}; @@ -150,6 +149,81 @@ impl Default for ResourceLimits { } } +#[derive(Copy, Clone, Debug)] +pub struct TyAndPacked<'tcx> { + pub ty: Ty<'tcx>, + pub packed: bool, +} + +#[derive(Copy, Clone, Debug)] +pub struct ValTy<'tcx> { + pub value: Value, + pub ty: Ty<'tcx>, +} + +impl<'tcx> ::std::ops::Deref for ValTy<'tcx> { + type Target = Value; + fn deref(&self) -> &Value { + &self.value + } +} + +#[derive(Copy, Clone, Debug)] +pub struct PtrAndAlign { + pub ptr: Pointer, + /// Remember whether this lvalue is *supposed* to be aligned. + pub aligned: bool, +} + +impl<'a, 'tcx> HasDataLayout for &'a EvalContext<'a, 'tcx> { + #[inline] + fn data_layout(&self) -> &layout::TargetDataLayout { + &self.tcx.data_layout + } +} + +impl<'c, 'b, 'a, 'tcx> HasDataLayout + for &'c &'b mut EvalContext<'a, 'tcx> { + #[inline] + fn data_layout(&self) -> &layout::TargetDataLayout { + &self.tcx.data_layout + } +} + +impl<'a, 'tcx> layout::HasTyCtxt<'tcx> for &'a EvalContext<'a, 'tcx> { + #[inline] + fn tcx<'b>(&'b self) -> TyCtxt<'b, 'tcx, 'tcx> { + self.tcx + } +} + +impl<'c, 'b, 'a, 'tcx> layout::HasTyCtxt<'tcx> + for &'c &'b mut EvalContext<'a, 'tcx> { + #[inline] + fn tcx<'d>(&'d self) -> TyCtxt<'d, 'tcx, 'tcx> { + self.tcx + } +} + +impl<'a, 'tcx> LayoutOf<Ty<'tcx>> for &'a EvalContext<'a, 'tcx> { + type TyLayout = EvalResult<'tcx, TyLayout<'tcx>>; + + fn layout_of(self, ty: Ty<'tcx>) -> Self::TyLayout { + (self.tcx, ::rustc::ty::ParamEnv::empty(Reveal::All)).layout_of(ty) + .map_err(|layout| EvalError::Layout(layout).into()) + } +} + +impl<'c, 'b, 'a, 'tcx> LayoutOf<Ty<'tcx>> + for &'c &'b mut EvalContext<'a, 'tcx> { + type TyLayout = EvalResult<'tcx, TyLayout<'tcx>>; + + #[inline] + fn layout_of(self, ty: Ty<'tcx>) -> Self::TyLayout { + (&**self).layout_of(ty) + } +} + impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, limits: ResourceLimits) -> Self { EvalContext { @@ -246,6 +320,16 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(Value::ByVal(primval)) } + pub(super) fn resolve(&self, def_id: DefId, substs: &'tcx Substs<'tcx>) -> EvalResult<'tcx, ty::Instance<'tcx>> { + let substs = self.tcx.trans_apply_param_substs(self.substs(), &substs); + ::rustc::ty::Instance::resolve( + self.tcx, + ::rustc::ty::ParamEnv::empty(Reveal::All), // XXX ? + def_id, + substs, + ).ok_or(EvalError::TypeckError.into()) // turn error prop into a panic to expose associated type in const issue + } + pub(super) fn type_is_sized(&self, ty: Ty<'tcx>) -> bool { // generics are weird, don't run this function on a generic assert!(!ty.needs_subst()); @@ -292,23 +376,25 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { if layout.is_unsized() { Ok(None) } else { - Ok(Some(layout.size(&self.tcx.data_layout).bytes())) + Ok(Some(layout.size.bytes())) } } fn type_align_with_substs(&self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx>) -> EvalResult<'tcx, u64> { - self.type_layout_with_substs(ty, substs).map(|layout| layout.align(&self.tcx.data_layout).abi()) + self.type_layout_with_substs(ty, substs).map(|layout| { + layout.align.abi() + }) } - pub(super) fn type_layout(&self, ty: Ty<'tcx>) -> EvalResult<'tcx, &'tcx Layout> { + pub(super) fn type_layout(&self, ty: Ty<'tcx>) -> EvalResult<'tcx, TyLayout<'tcx>> { self.type_layout_with_substs(ty, self.substs()) } - fn type_layout_with_substs(&self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx>) -> EvalResult<'tcx, &'tcx Layout> { + fn type_layout_with_substs(&self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx>) -> EvalResult<'tcx, TyLayout<'tcx>> { // TODO(solson): Is this inefficient? Needs investigation. let ty = self.monomorphize(ty, substs); - ty.layout(self.tcx, ty::ParamEnv::empty(Reveal::All)).map_err(EvalError::Layout) + self.layout_of(ty) } pub fn push_stack_frame( @@ -392,64 +478,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(()) } - pub fn assign_discr_and_fields< - V: IntoValTyPair<'tcx>, - J: IntoIterator<Item = V>, - >( - &mut self, - dest: Lvalue<'tcx>, - dest_ty: Ty<'tcx>, - discr_offset: u64, - operands: J, - discr_val: u128, - variant_idx: usize, - discr_size: u64, - ) -> EvalResult<'tcx> - where J::IntoIter: ExactSizeIterator, - { - // FIXME(solson) - let dest_ptr = self.force_allocation(dest)?.to_ptr()?; - - let discr_dest = dest_ptr.offset(discr_offset, self.memory.layout)?; - self.memory.write_uint(discr_dest, discr_val, discr_size)?; - - let dest = Lvalue::Ptr { - ptr: PrimVal::Ptr(dest_ptr), - extra: LvalueExtra::DowncastVariant(variant_idx), - }; - - self.assign_fields(dest, dest_ty, operands) - } - - pub fn assign_fields< - V: IntoValTyPair<'tcx>, - J: IntoIterator<Item = V>, - >( - &mut self, - dest: Lvalue<'tcx>, - dest_ty: Ty<'tcx>, - operands: J, - ) -> EvalResult<'tcx> - where J::IntoIter: ExactSizeIterator, - { - if self.type_size(dest_ty)? == Some(0) { - // zst assigning is a nop - return Ok(()); - } - if self.ty_to_primval_kind(dest_ty).is_ok() { - let mut iter = operands.into_iter(); - assert_eq!(iter.len(), 1); - let (value, value_ty) = iter.next().unwrap().into_val_ty_pair(self)?; - return self.write_value(value, dest, value_ty); - } - for (field_index, operand) in operands.into_iter().enumerate() { - let (value, value_ty) = operand.into_val_ty_pair(self)?; - let field_dest = self.lvalue_field(dest, field_index, dest_ty, value_ty)?; - self.write_value(value, field_dest, value_ty)?; - } - Ok(()) - } - /// Evaluate an assignment statement. /// /// There is no separate `eval_rvalue` function. Instead, the code for handling each rvalue @@ -461,7 +489,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ) -> EvalResult<'tcx> { let dest = self.eval_lvalue(lvalue)?; let dest_ty = self.lvalue_ty(lvalue); - let dest_layout = self.type_layout(dest_ty)?; use rustc::mir::Rvalue::*; match *rvalue { @@ -491,127 +518,28 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Aggregate(ref kind, ref operands) => { self.inc_step_counter_and_check_limit(operands.len() as u64)?; - use rustc::ty::layout::Layout::*; - match *dest_layout { - Univariant { ref variant, .. } => { - if variant.packed { - let ptr = self.force_allocation(dest)?.to_ptr_and_extra().0.to_ptr()?; - self.memory.mark_packed(ptr, variant.stride().bytes()); - } - self.assign_fields(dest, dest_ty, operands)?; - } - - Array { .. } => { - self.assign_fields(dest, dest_ty, operands)?; - } - - General { discr, ref variants, .. } => { - if let mir::AggregateKind::Adt(adt_def, variant, _, _) = **kind { - let discr_val = adt_def.discriminants(self.tcx) - .nth(variant) - .expect("broken mir: Adt variant id invalid") - .to_u128_unchecked(); - let discr_size = discr.size().bytes(); - if variants[variant].packed { - let ptr = self.force_allocation(dest)?.to_ptr_and_extra().0.to_ptr()?; - self.memory.mark_packed(ptr, variants[variant].stride().bytes()); - } - - self.assign_discr_and_fields( - dest, - dest_ty, - variants[variant].offsets[0].bytes(), - operands, - discr_val, - variant, - discr_size, - )?; - } else { - bug!("tried to assign {:?} to Layout::General", kind); - } - } - - RawNullablePointer { nndiscr, .. } => { - if let mir::AggregateKind::Adt(_, variant, _, _) = **kind { - if nndiscr == variant as u64 { - assert_eq!(operands.len(), 1); - let operand = &operands[0]; - let value = self.eval_operand(operand)?; - let value_ty = self.operand_ty(operand); - self.write_value(value, dest, value_ty)?; - } else { - if let Some(operand) = operands.get(0) { - assert_eq!(operands.len(), 1); - let operand_ty = self.operand_ty(operand); - assert_eq!(self.type_size(operand_ty)?, Some(0)); - } - self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?; - } - } else { - bug!("tried to assign {:?} to Layout::RawNullablePointer", kind); - } - } - - StructWrappedNullablePointer { nndiscr, ref nonnull, ref discrfield_source, .. } => { - if let mir::AggregateKind::Adt(_, variant, _, _) = **kind { - if nonnull.packed { - let ptr = self.force_allocation(dest)?.to_ptr_and_extra().0.to_ptr()?; - self.memory.mark_packed(ptr, nonnull.stride().bytes()); - } - if nndiscr == variant as u64 { - self.assign_fields(dest, dest_ty, operands)?; - } else { - for operand in operands { - let operand_ty = self.operand_ty(operand); - assert_eq!(self.type_size(operand_ty)?, Some(0)); - } - let (offset, ty) = self.nonnull_offset_and_ty(dest_ty, nndiscr, discrfield_source)?; - - // FIXME(solson) - let dest = self.force_allocation(dest)?.to_ptr()?; - - let dest = dest.offset(offset.bytes(), self.memory.layout)?; - let dest_size = self.type_size(ty)? - .expect("bad StructWrappedNullablePointer discrfield"); - self.memory.write_int(dest, 0, dest_size)?; - } + let mut layout = self.type_layout(dest_ty)?; + let (dest, active_field_index) = match **kind { + mir::AggregateKind::Adt(adt_def, variant_index, _, active_field_index) => { + self.write_discriminant_value(dest_ty, dest, variant_index)?; + layout = layout.for_variant(&self, variant_index); + if adt_def.is_enum() { + (self.lvalue_downcast(dest, variant_index)?, active_field_index) } else { - bug!("tried to assign {:?} to Layout::RawNullablePointer", kind); + (dest, active_field_index) } } + _ => (dest, None) + }; - CEnum { .. } => { - assert_eq!(operands.len(), 0); - if let mir::AggregateKind::Adt(adt_def, variant, _, _) = **kind { - let n = adt_def.discriminants(self.tcx) - .nth(variant) - .expect("broken mir: Adt variant index invalid") - .to_u128_unchecked(); - self.write_primval(dest, PrimVal::Bytes(n), dest_ty)?; - } else { - bug!("tried to assign {:?} to Layout::CEnum", kind); - } - } - - Vector { count, .. } => { - debug_assert_eq!(count, operands.len() as u64); - self.assign_fields(dest, dest_ty, operands)?; - } - - UntaggedUnion { .. } => { - assert_eq!(operands.len(), 1); - let operand = &operands[0]; - let value = self.eval_operand(operand)?; - let value_ty = self.operand_ty(operand); - self.write_value(value, dest, value_ty)?; - } - - _ => { - return Err(EvalError::Unimplemented(format!( - "can't handle destination layout {:?} when assigning {:?}", - dest_layout, - kind - ))); + for (i, operand) in operands.iter().enumerate() { + let value = self.eval_operand(operand)?; + let value_ty = self.operand_ty(operand); + // Ignore zero-sized fields. + if !self.type_layout(value_ty)?.is_zst() { + let field_index = active_field_index.unwrap_or(i); + let (field_dest, _) = self.lvalue_field(dest, mir::Field::new(field_index), layout)?; + self.write_value(value, field_dest, value_ty)?; } } } @@ -724,7 +652,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ReifyFnPointer => match self.operand_ty(operand).sty { ty::TyFnDef(def_id, substs) => { - let instance = resolve(self.tcx, def_id, substs); + let instance = self.resolve(def_id, substs)?; let fn_ptr = self.memory.create_fn_alloc(instance); self.write_value(Value::ByVal(PrimVal::Ptr(fn_ptr)), dest, dest_ty)?; }, @@ -753,16 +681,19 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Discriminant(ref lvalue) => { let lval = self.eval_lvalue(lvalue)?; let ty = self.lvalue_ty(lvalue); - let ptr = self.force_allocation(lval)?.to_ptr()?; - let discr_val = self.read_discriminant_value(ptr, ty)?; + let discr_val = self.read_discriminant_value(lval, ty)?; if let ty::TyAdt(adt_def, _) = ty.sty { - if adt_def.discriminants(self.tcx).all(|v| discr_val != v.to_u128_unchecked()) { + trace!("Read discriminant {}, valid discriminants {:?}", discr_val, adt_def.discriminants(self.tcx).collect::<Vec<_>>()); + if adt_def.discriminants(self.tcx).all(|v| { + discr_val != v.to_u128_unchecked() + }) + { return Err(EvalError::InvalidDiscriminant); } + self.write_primval(dest, PrimVal::Bytes(discr_val), dest_ty)?; } else { bug!("rustc only generates Rvalue::Discriminant for enums"); } - self.write_primval(dest, PrimVal::Bytes(discr_val), dest_ty)?; }, } @@ -782,109 +713,25 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - pub(super) fn nonnull_offset_and_ty( + /// Returns the field type and whether the field is packed + pub fn get_field_ty( &self, ty: Ty<'tcx>, - nndiscr: u64, - discrfield: &[u32], - ) -> EvalResult<'tcx, (Size, Ty<'tcx>)> { - // Skip the constant 0 at the start meant for LLVM GEP and the outer non-null variant - let path = discrfield.iter().skip(2).map(|&i| i as usize); - - // Handle the field index for the outer non-null variant. - let (inner_offset, inner_ty) = match ty.sty { - ty::TyAdt(adt_def, substs) => { - let variant = &adt_def.variants[nndiscr as usize]; - let index = discrfield[1]; - let field = &variant.fields[index as usize]; - (self.get_field_offset(ty, index as usize)?, field.ty(self.tcx, substs)) - } - _ => bug!("non-enum for StructWrappedNullablePointer: {}", ty), - }; - - self.field_path_offset_and_ty(inner_offset, inner_ty, path) - } - - fn field_path_offset_and_ty<I: Iterator<Item = usize>>( - &self, - mut offset: Size, - mut ty: Ty<'tcx>, - path: I, - ) -> EvalResult<'tcx, (Size, Ty<'tcx>)> { - // Skip the initial 0 intended for LLVM GEP. - for field_index in path { - let field_offset = self.get_field_offset(ty, field_index)?; - trace!("field_path_offset_and_ty: {}, {}, {:?}, {:?}", field_index, ty, field_offset, offset); - ty = self.get_field_ty(ty, field_index)?; - offset = offset.checked_add(field_offset, &self.tcx.data_layout).unwrap(); - } - - Ok((offset, ty)) - } - fn get_fat_field(&self, pointee_ty: Ty<'tcx>, field_index: usize) -> EvalResult<'tcx, Ty<'tcx>> { - match (field_index, &self.tcx.struct_tail(pointee_ty).sty) { - (1, &ty::TyStr) | - (1, &ty::TySlice(_)) => Ok(self.tcx.types.usize), - (1, &ty::TyDynamic(..)) | - (0, _) => Ok(self.tcx.mk_imm_ptr(self.tcx.types.u8)), - _ => bug!("invalid fat pointee type: {}", pointee_ty), - } - } - - pub fn get_field_ty(&self, ty: Ty<'tcx>, field_index: usize) -> EvalResult<'tcx, Ty<'tcx>> { - match ty.sty { - ty::TyAdt(adt_def, _) if adt_def.is_box() => self.get_fat_field(ty.boxed_ty(), field_index), - ty::TyAdt(adt_def, substs) => { - Ok(adt_def.struct_variant().fields[field_index].ty(self.tcx, substs)) - } - - ty::TyTuple(fields, _) => Ok(fields[field_index]), - - ty::TyRef(_, ref tam) | - ty::TyRawPtr(ref tam) => self.get_fat_field(tam.ty, field_index), - - ty::TyClosure(def_id, ref closure_substs) => - Ok(closure_substs.upvar_tys(def_id, self.tcx).nth(field_index).unwrap()), - - _ => Err(EvalError::Unimplemented(format!("can't handle type: {:?}, {:?}", ty, ty.sty))), - } + field_index: usize, + ) -> EvalResult<'tcx, TyAndPacked<'tcx>> { + let layout = self.type_layout(ty)?.field(self, field_index)?; + Ok(TyAndPacked { + ty: layout.ty, + packed: layout.is_packed() + }) } fn get_field_offset(&self, ty: Ty<'tcx>, field_index: usize) -> EvalResult<'tcx, Size> { - let layout = self.type_layout(ty)?; - - use rustc::ty::layout::Layout::*; - match *layout { - Univariant { ref variant, .. } => { - Ok(variant.offsets[field_index]) - } - FatPointer { .. } => { - let bytes = field_index as u64 * self.memory.pointer_size(); - Ok(Size::from_bytes(bytes)) - } - StructWrappedNullablePointer { ref nonnull, .. } => { - Ok(nonnull.offsets[field_index]) - } - _ => { - let msg = format!("can't handle type: {:?}, with layout: {:?}", ty, layout); - Err(EvalError::Unimplemented(msg)) - } - } + Ok(self.type_layout(ty)?.fields.offset(field_index)) } - pub fn get_field_count(&self, ty: Ty<'tcx>) -> EvalResult<'tcx, usize> { - let layout = self.type_layout(ty)?; - - use rustc::ty::layout::Layout::*; - match *layout { - Univariant { ref variant, .. } => Ok(variant.offsets.len()), - FatPointer { .. } => Ok(2), - StructWrappedNullablePointer { ref nonnull, .. } => Ok(nonnull.offsets.len()), - _ => { - let msg = format!("can't handle type: {:?}, with layout: {:?}", ty, layout); - Err(EvalError::Unimplemented(msg)) - } - } + pub fn get_field_count(&self, ty: Ty<'tcx>) -> EvalResult<'tcx, u64> { + Ok(self.type_layout(ty)?.fields.count() as u64) } pub(super) fn wrapping_pointer_offset(&self, ptr: PrimVal, pointee_ty: Ty<'tcx>, offset: i64) -> EvalResult<'tcx, PrimVal> { @@ -950,6 +797,99 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } + pub fn read_discriminant_value( + &mut self, + lvalue: Lvalue<'tcx>, + ty: Ty<'tcx>, + ) -> EvalResult<'tcx, u128> { + let layout = self.type_layout(ty)?; + match layout.variants { + layout::Variants::Single { index } => { + return Ok(index as u128); + } + layout::Variants::Tagged { .. } | + layout::Variants::NicheFilling { .. } => {}, + } + + let (discr_lvalue, discr) = self.lvalue_field(lvalue, mir::Field::new(0), layout)?; + let read_discr_lvalue = self.read_lvalue(discr_lvalue)?; + let raw_discr_primval = self.value_to_primval( + read_discr_lvalue, + discr.ty)?; + let discr_val = match layout.variants { + layout::Variants::Single { .. } => bug!(), + layout::Variants::Tagged { .. } => raw_discr_primval.to_bytes()?, + layout::Variants::NicheFilling { + dataful_variant, + ref niche_variants, + niche_start, + .. + } => { + match raw_discr_primval { + PrimVal::Bytes(raw_discr) => { + let variants_start = niche_variants.start as u128; + let variants_end = niche_variants.end as u128; + let discr = raw_discr.wrapping_sub(niche_start) + .wrapping_add(variants_start); + + if variants_start <= discr && discr <= variants_end { + discr + } else { + dataful_variant as u128 + } + } + _ => dataful_variant as u128, + } + } + }; + + Ok(discr_val) + } + + pub(crate) fn write_discriminant_value( + &mut self, + dest_ty: Ty<'tcx>, + dest: Lvalue<'tcx>, + variant_index: usize, + ) -> EvalResult<'tcx> { + let layout = self.type_layout(dest_ty)?; + + match layout.variants { + layout::Variants::Single { index } => { + if index != variant_index { + // If the layout of an enum is `Single`, all + // other variants are necessarily uninhabited. + assert_eq!(layout.for_variant(&self, variant_index).abi, + layout::Abi::Uninhabited); + } + } + layout::Variants::Tagged { .. } => { + let discr_val = dest_ty.ty_adt_def().unwrap() + .discriminant_for_variant(self.tcx, variant_index) + .to_u128_unchecked(); + + let (discr_dest, discr) = self.lvalue_field(dest, mir::Field::new(0), layout)?; + self.write_primval(discr_dest, PrimVal::Bytes(discr_val), discr.ty)?; + } + layout::Variants::NicheFilling { + dataful_variant, + ref niche_variants, + niche_start, + .. + } => { + if variant_index != dataful_variant { + let (niche_dest, niche) = + self.lvalue_field(dest, mir::Field::new(0), layout)?; + let niche_value = ((variant_index - niche_variants.start) as u128) + .wrapping_add(niche_start); + self.write_primval(niche_dest, PrimVal::Bytes(niche_value), niche.ty)?; + } + } + } + + Ok(()) + } + pub(super) fn operand_ty(&self, operand: &mir::Operand<'tcx>) -> Ty<'tcx> { self.monomorphize(operand.ty(self.mir(), self.tcx), self.substs()) } @@ -1143,20 +1083,33 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { a: PrimVal, b: PrimVal, ptr: Pointer, - mut ty: Ty<'tcx> + ty: Ty<'tcx> ) -> EvalResult<'tcx> { - while self.get_field_count(ty)? == 1 { - ty = self.get_field_ty(ty, 0)?; - } - assert_eq!(self.get_field_count(ty)?, 2); - let field_0 = self.get_field_offset(ty, 0)?.bytes(); - let field_1 = self.get_field_offset(ty, 1)?.bytes(); - let field_0_ty = self.get_field_ty(ty, 0)?; - let field_1_ty = self.get_field_ty(ty, 1)?; - let field_0_size = self.type_size(field_0_ty)?.expect("pair element type must be sized"); - let field_1_size = self.type_size(field_1_ty)?.expect("pair element type must be sized"); - self.memory.write_primval(PrimVal::Ptr(ptr.offset(field_0, self.memory.layout)?), a, field_0_size)?; - self.memory.write_primval(PrimVal::Ptr(ptr.offset(field_1, self.memory.layout)?), b, field_1_size)?; + let mut layout = self.type_layout(ty)?; + let mut _packed = layout.is_packed(); + + // er, this is kinda fishy + while layout.fields.count() != 2 + || layout.field(&self, 0)?.size.bytes() == 0 + || layout.field(&self, 1)?.size.bytes() == 0 { + layout = layout.field(&self, 0)?; + _packed |= layout.is_packed(); + } + + assert_eq!(layout.fields.count(), 2); + let field_0 = layout.field(&self, 0)?; + let field_1 = layout.field(&self, 1)?; + assert_eq!( + field_0.is_packed(), + field_1.is_packed(), + "the two fields must agree on being packed" + ); + _packed |= field_0.is_packed(); + let field_0_ptr = ptr.offset(layout.fields.offset(0).bytes(), (&self).data_layout())?.into(); + let field_1_ptr = ptr.offset(layout.fields.offset(1).bytes(), (&self).data_layout())?.into(); + + self.memory.write_primval(PrimVal::Ptr(field_0_ptr), a, field_0.size.bytes())?; + self.memory.write_primval(PrimVal::Ptr(field_1_ptr), b, field_1.size.bytes())?; Ok(()) } @@ -1203,41 +1156,19 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::TyAdt(ref def, _) if def.is_box() => PrimValKind::Ptr, - ty::TyAdt(ref def, substs) => { - use rustc::ty::layout::Layout::*; - match *self.type_layout(ty)? { - CEnum { discr, signed, .. } => { - let size = discr.size().bytes(); - if signed { - PrimValKind::from_int_size(size) - } else { - PrimValKind::from_uint_size(size) - } - } - - RawNullablePointer { value, .. } => { + ty::TyAdt(..) => { + match self.type_layout(ty)?.abi { + layout::Abi::Scalar(ref scalar) => { use rustc::ty::layout::Primitive::*; - match value { - // TODO(solson): Does signedness matter here? What should the sign be? - Int(int) => PrimValKind::from_uint_size(int.size().bytes()), + match scalar.value { + Int(i, false) => PrimValKind::from_uint_size(i.size().bytes()), + Int(i, true) => PrimValKind::from_int_size(i.size().bytes()), F32 => PrimValKind::F32, F64 => PrimValKind::F64, Pointer => PrimValKind::Ptr, } } - // represent single field structs as their single field - Univariant { .. } => { - // enums with just one variant are no different, but `.struct_variant()` doesn't work for enums - let variant = &def.variants[0]; - // FIXME: also allow structs with only a single non zst field - if variant.fields.len() == 1 { - return self.ty_to_primval_kind(variant.fields[0].ty(self.tcx, substs)); - } else { - return Err(EvalError::TypeNotPrimitive(ty)); - } - } - _ => return Err(EvalError::TypeNotPrimitive(ty)), } } @@ -1370,13 +1301,17 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { if def.is_box() { return self.read_ptr(ptr, ty.boxed_ty()).map(Some); } - use rustc::ty::layout::Layout::*; - if let CEnum { discr, signed, .. } = *self.type_layout(ty)? { - let size = discr.size().bytes(); - if signed { - PrimVal::from_i128(self.memory.read_int(ptr, size)?) + + if let layout::Abi::Scalar(ref scalar) = self.type_layout(ty)?.abi { + let mut signed = false; + if let layout::Int(_, s) = scalar.value { + signed = s; + } + let size = scalar.value.size(&self).bytes(); + if self.memory.points_to_concrete(ptr, size)? { + self.memory.read_primval(ptr, size, signed)? } else { - PrimVal::from_u128(self.memory.read_uint(ptr, size)?) + return Ok(None); } } else { return Ok(None); @@ -1461,8 +1396,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { return self.unsize_into_ptr(src, src_ty, dest, dest_ty, src_ty.boxed_ty(), dest_ty.boxed_ty()); } if self.ty_to_primval_kind(src_ty).is_ok() { - let sty = self.get_field_ty(src_ty, 0)?; - let dty = self.get_field_ty(dest_ty, 0)?; + let sty = self.get_field_ty(src_ty, 0)?.ty; + let dty = self.get_field_ty(dest_ty, 0)?.ty; return self.unsize_into(src, sty, dest, dty); } // unsizing of generic struct with pointer fields @@ -1595,7 +1530,7 @@ impl IntegerExt for layout::Integer { fn size(self) -> Size { use rustc::ty::layout::Integer::*; match self { - I1 | I8 => Size::from_bits(8), + I8 => Size::from_bits(8), I16 => Size::from_bits(16), I32 => Size::from_bits(32), I64 => Size::from_bits(64), @@ -1640,7 +1575,7 @@ pub fn resolve_closure<'a, 'tcx> ( substs: ty::ClosureSubsts<'tcx>, requested_kind: ty::ClosureKind, ) -> ty::Instance<'tcx> { - let actual_kind = tcx.closure_kind(def_id); + let actual_kind = substs.closure_kind(def_id, tcx); match needs_fn_once_adapter_shim(actual_kind, requested_kind) { Ok(true) => fn_once_adapter_instance(tcx, def_id, substs), _ => ty::Instance::new(def_id, substs.substs) @@ -1709,155 +1644,6 @@ fn needs_fn_once_adapter_shim(actual_closure_kind: ty::ClosureKind, } } -/// The point where linking happens. Resolve a (def_id, substs) -/// pair to an instance. -pub fn resolve<'a, 'tcx>( - tcx: TyCtxt<'a, 'tcx, 'tcx>, - def_id: DefId, - substs: &'tcx Substs<'tcx> -) -> ty::Instance<'tcx> { - debug!("resolve(def_id={:?}, substs={:?})", - def_id, substs); - let result = if let Some(trait_def_id) = tcx.trait_of_item(def_id) { - debug!(" => associated item, attempting to find impl"); - let item = tcx.associated_item(def_id); - resolve_associated_item(tcx, &item, trait_def_id, substs) - } else { - let item_type = def_ty(tcx, def_id, substs); - let def = match item_type.sty { - ty::TyFnDef(..) if { - let f = item_type.fn_sig(tcx); - f.abi() == Abi::RustIntrinsic || - f.abi() == Abi::PlatformIntrinsic - } => - { - debug!(" => intrinsic"); - ty::InstanceDef::Intrinsic(def_id) - } - _ => { - if Some(def_id) == tcx.lang_items().drop_in_place_fn() { - let ty = substs.type_at(0); - if needs_drop_glue(tcx, ty) { - debug!(" => nontrivial drop glue"); - ty::InstanceDef::DropGlue(def_id, Some(ty)) - } else { - debug!(" => trivial drop glue"); - ty::InstanceDef::DropGlue(def_id, None) - } - } else { - debug!(" => free item"); - ty::InstanceDef::Item(def_id) - } - } - }; - ty::Instance { def, substs } - }; - debug!("resolve(def_id={:?}, substs={:?}) = {}", - def_id, substs, result); - result -} - -pub fn needs_drop_glue<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, t: Ty<'tcx>) -> bool { - assert!(t.is_normalized_for_trans()); - - let t = tcx.erase_regions(&t); - - // FIXME (#22815): note that type_needs_drop conservatively - // approximates in some cases and may say a type expression - // requires drop glue when it actually does not. - // - // (In this case it is not clear whether any harm is done, i.e. - // erroneously returning `true` in some cases where we could have - // returned `false` does not appear unsound. The impact on - // code quality is unknown at this time.) - - let env = ty::ParamEnv::empty(Reveal::All); - if !t.needs_drop(tcx, env) { - return false; - } - match t.sty { - ty::TyAdt(def, _) if def.is_box() => { - let typ = t.boxed_ty(); - if !typ.needs_drop(tcx, env) && type_is_sized(tcx, typ) { - let layout = t.layout(tcx, env).unwrap(); - if layout.size(&tcx.data_layout).bytes() == 0 { - // `Box<ZeroSizeType>` does not allocate. - false - } else { - true - } - } else { - true - } - } - _ => true - } -} - -fn resolve_associated_item<'a, 'tcx>( - tcx: TyCtxt<'a, 'tcx, 'tcx>, - trait_item: &ty::AssociatedItem, - trait_id: DefId, - rcvr_substs: &'tcx Substs<'tcx> -) -> ty::Instance<'tcx> { - let def_id = trait_item.def_id; - debug!("resolve_associated_item(trait_item={:?}, \ - trait_id={:?}, \ - rcvr_substs={:?})", - def_id, trait_id, rcvr_substs); - - let trait_ref = ty::TraitRef::from_method(tcx, trait_id, rcvr_substs); - let vtbl = tcx.trans_fulfill_obligation((ty::ParamEnv::empty(Reveal::All), - ty::Binder(trait_ref))); - - // Now that we know which impl is being used, we can dispatch to - // the actual function: - match vtbl { - ::rustc::traits::VtableImpl(impl_data) => { - let (def_id, substs) = ::rustc::traits::find_associated_item( - tcx, trait_item, rcvr_substs, &impl_data); - let substs = tcx.erase_regions(&substs); - ty::Instance::new(def_id, substs) - } - ::rustc::traits::VtableClosure(closure_data) => { - let trait_closure_kind = tcx.lang_items().fn_trait_kind(trait_id).unwrap(); - resolve_closure(tcx, closure_data.closure_def_id, closure_data.substs, - trait_closure_kind) - } - ::rustc::traits::VtableFnPointer(ref data) => { - ty::Instance { - def: ty::InstanceDef::FnPtrShim(trait_item.def_id, data.fn_ty), - substs: rcvr_substs - } - } - ::rustc::traits::VtableObject(ref data) => { - let index = tcx.get_vtable_index_of_object_method(data, def_id); - ty::Instance { - def: ty::InstanceDef::Virtual(def_id, index), - substs: rcvr_substs - } - } - ::rustc::traits::VtableBuiltin(..) if Some(trait_id) == tcx.lang_items().clone_trait() => { - ty::Instance { - def: ty::InstanceDef::CloneShim(def_id, trait_ref.self_ty()), - substs: rcvr_substs, - } - } - _ => { - bug!("static call to invalid vtable: {:?}", vtbl) - } - } -} - -pub fn def_ty<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - def_id: DefId, - substs: &'tcx Substs<'tcx>) - -> Ty<'tcx> -{ - let ty = tcx.type_of(def_id); - apply_param_substs(tcx, substs, &ty) -} - /// Monomorphizes a type from the AST by first applying the in-scope /// substitutions and then normalizing any associated types. pub fn apply_param_substs<'a, 'tcx, T>(tcx: TyCtxt<'a, 'tcx, 'tcx>, @@ -1901,12 +1687,6 @@ impl<'a, 'tcx> ::rustc::ty::fold::TypeFolder<'tcx, 'tcx> for AssociatedTypeNorma } } -fn type_is_sized<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, ty: Ty<'tcx>) -> bool { - // generics are weird, don't run this function on a generic - assert!(!ty.needs_subst()); - ty.is_sized(tcx, ty::ParamEnv::empty(Reveal::All), DUMMY_SP) -} - pub fn resolve_drop_in_place<'a, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, ty: Ty<'tcx>, @@ -1914,5 +1694,5 @@ pub fn resolve_drop_in_place<'a, 'tcx>( { let def_id = tcx.require_lang_item(::rustc::middle::lang_items::DropInPlaceFnLangItem); let substs = tcx.intern_substs(&[Kind::from(ty)]); - resolve(tcx, def_id, substs) + ::rustc::ty::Instance::resolve(tcx, ::rustc::ty::ParamEnv::empty(Reveal::All), def_id, substs).unwrap() } diff --git a/src/lib.rs b/src/lib.rs index 98b18fe4a785cf6c94e8909a3b09d0e50005399f..67c0d3e0bf905ef5180d4b38ece6ca148d067c7c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,6 @@ #![feature( i128_type, + inclusive_range, rustc_private, )] diff --git a/src/lvalue.rs b/src/lvalue.rs index 9b9957c15affd663f5ee69468ba120e6b7d0e1a1..6e13155a3a8f2db787cd4c0a6dc329ec1d1c3937 100644 --- a/src/lvalue.rs +++ b/src/lvalue.rs @@ -1,5 +1,5 @@ use rustc::mir; -use rustc::ty::layout::{Size, Align}; +use rustc::ty::layout::{HasDataLayout, TyLayout}; use rustc::ty::{self, Ty}; use rustc_data_structures::indexed_vec::Idx; @@ -196,6 +196,19 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } + pub fn read_lvalue(&self, lvalue: Lvalue) -> EvalResult<'tcx, Value> { + match lvalue { + Lvalue::Ptr { ptr, extra } => { + assert_eq!(extra, LvalueExtra::None); + Ok(Value::ByRef(ptr.to_ptr()?)) + } + Lvalue::Local { frame, local } => self.stack[frame].get_local(local), + Lvalue::Global(cid) => { + Ok(self.globals.get(&cid).expect("global not cached").value) + } + } + } + pub(super) fn eval_lvalue(&mut self, mir_lvalue: &mir::Lvalue<'tcx>) -> EvalResult<'tcx, Lvalue<'tcx>> { use rustc::mir::Lvalue::*; let lvalue = match *mir_lvalue { @@ -207,7 +220,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Lvalue::Global(GlobalId { instance, promoted: None }) } - Projection(ref proj) => return self.eval_lvalue_projection(proj), + Projection(ref proj) => { + let ty = self.lvalue_ty(&proj.base); + let lvalue = self.eval_lvalue(&proj.base)?; + return self.eval_lvalue_projection(lvalue, ty, &proj.elem); + } }; if log_enabled!(::log::LogLevel::Trace) { @@ -220,216 +237,224 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub fn lvalue_field( &mut self, base: Lvalue<'tcx>, - field_index: usize, - base_ty: Ty<'tcx>, - field_ty: Ty<'tcx>, - ) -> EvalResult<'tcx, Lvalue<'tcx>> { - let base_layout = self.type_layout(base_ty)?; - - use rustc::ty::layout::Layout::*; - let (offset, packed) = match *base_layout { - Univariant { ref variant, .. } => { - (variant.offsets[field_index], variant.packed) - }, - - General { ref variants, .. } => { - let (_, base_extra) = base.to_ptr_and_extra(); - if let LvalueExtra::DowncastVariant(variant_idx) = base_extra { - // +1 for the discriminant, which is field 0 - (variants[variant_idx].offsets[field_index + 1], variants[variant_idx].packed) - } else { - bug!("field access on enum had no variant index"); - } - } - - RawNullablePointer { .. } => { - assert_eq!(field_index, 0); - return Ok(base); - } - - StructWrappedNullablePointer { ref nonnull, .. } => { - (nonnull.offsets[field_index], nonnull.packed) - } - - UntaggedUnion { .. } => return Ok(base), - - Vector { element, count } => { - let field = field_index as u64; - assert!(field < count); - let elem_size = element.size(&self.tcx.data_layout).bytes(); - (Size::from_bytes(field * elem_size), false) + field: mir::Field, + base_layout: TyLayout<'tcx>, + ) -> EvalResult<'tcx, (Lvalue<'tcx>, TyLayout<'tcx>)> { + let base_layout: TyLayout<'tcx> = match base { + Lvalue::Ptr { extra: LvalueExtra::DowncastVariant(n), .. } => { + base_layout.for_variant(&self, n) } + _ => base_layout, + }; + let field_index = field.index(); + let field = base_layout.field(&self, field_index)?; + let offset = base_layout.fields.offset(field_index); - // We treat arrays + fixed sized indexing like field accesses - Array { .. } => { - let field = field_index as u64; - let elem_size = match base_ty.sty { - ty::TyArray(elem_ty, n) => { - assert!(field < n.val.to_const_int().unwrap().to_u64().unwrap() as u64); - self.type_size(elem_ty)?.expect("array elements are sized") as u64 - }, - _ => bug!("lvalue_field: got Array layout but non-array type {:?}", base_ty), - }; - (Size::from_bytes(field * elem_size), false) + // Do not allocate in trivial cases + let (base_ptr, base_extra) = match base { + Lvalue::Ptr { ptr, extra } => (ptr, extra), + Lvalue::Local { frame, local } => { + match self.stack[frame].get_local(local)? { + // in case the field covers the entire type, just return the value + Value::ByVal(_) if offset.bytes() == 0 && + field.size == base_layout.size => { + return Ok((base, field)); + } + Value::ByRef { .. } | + Value::ByValPair(..) | + Value::ByVal(_) => self.force_allocation(base)?.to_ptr_and_extra(), + } } - - FatPointer { .. } => { - let bytes = field_index as u64 * self.memory.pointer_size(); - let offset = Size::from_bytes(bytes); - (offset, false) + Lvalue::Global(_cid) => { + self.force_allocation(base)?.to_ptr_and_extra() } - - _ => bug!("field access on non-product type: {:?}", base_layout), }; - let (base_ptr, base_extra) = self.force_allocation(base)?.to_ptr_and_extra(); - let offset = match base_extra { LvalueExtra::Vtable(tab) => { - let (_, align) = self.size_and_align_of_dst(base_ty, Value::ByValPair(base_ptr, PrimVal::Ptr(tab)))?; - offset.abi_align(Align::from_bytes(align, align).unwrap()).bytes() + let (_, align) = self.size_and_align_of_dst( + base_layout.ty, + base_ptr.to_ptr()?.to_value_with_vtable(tab), + )?; + offset.abi_align(align).bytes() } _ => offset.bytes(), }; - let ptr = match base_ptr.to_ptr()?.offset { - PointerOffset::Concrete(_) => base_ptr.offset(offset, self.memory.layout)?, - PointerOffset::Abstract(sbytes) => { - let new_offset = self.memory.constraints.add_binop_constraint( - mir::BinOp::Add, - PrimVal::Bytes(offset as u128), - PrimVal::Abstract(sbytes), - PrimValKind::U64); - if let PrimVal::Abstract(sb) = new_offset { - PrimVal::Ptr(Pointer::new_abstract(base_ptr.to_ptr()?.alloc_id, sb)) - } else { - unreachable!() - } - } - }; - - let field_ty = self.monomorphize(field_ty, self.substs()); + let ptr = base_ptr.to_ptr()?.offset(offset, (&self).data_layout())?; + // if we were unaligned, stay unaligned + // no matter what we were, if we are packed, we must not be aligned anymore + //ptr.aligned &= !base_layout.is_packed(); - if packed { - let size = self.type_size(field_ty)?.expect("packed struct must be sized"); - self.memory.mark_packed(ptr.to_ptr()?, size); - } - - let extra = if self.type_is_sized(field_ty) { + let extra = if !field.is_unsized() { LvalueExtra::None } else { match base_extra { LvalueExtra::None => bug!("expected fat pointer"), - LvalueExtra::DowncastVariant(..) => - bug!("Rust doesn't support unsized fields in enum variants"), + LvalueExtra::DowncastVariant(..) => { + bug!("Rust doesn't support unsized fields in enum variants") + } LvalueExtra::Vtable(_) | - LvalueExtra::Length(_) => {}, + LvalueExtra::Length(_) => {} } base_extra }; + Ok((Lvalue::Ptr { ptr: PrimVal::Ptr(ptr), extra }, field)) + } + + pub(super) fn val_to_lvalue(&self, val: Value, ty: Ty<'tcx>) -> EvalResult<'tcx, Lvalue<'tcx>> { + Ok(match self.tcx.struct_tail(ty).sty { + ty::TyDynamic(..) => { + let (ptr, vtable) = val.into_ptr_vtable_pair(&self.memory)?; + Lvalue::Ptr { + ptr: ptr, + extra: LvalueExtra::Vtable(vtable), + } + } + ty::TyStr | ty::TySlice(_) => { + let (ptr, len) = val.into_slice(&self.memory)?; + Lvalue::Ptr { + ptr: ptr, + extra: LvalueExtra::Length(len), + } + } + _ => Lvalue::from_primval_ptr(val.read_ptr(&self.memory)?), + }) + } + + pub(super) fn lvalue_index( + &mut self, + base: Lvalue<'tcx>, + outer_ty: Ty<'tcx>, + idx: PrimVal, + ) -> EvalResult<'tcx, Lvalue<'tcx>> { + // Taking the outer type here may seem odd; it's needed because for array types, the outer type gives away the length. + let base = self.force_allocation(base)?; + let (base_ptr, _) = base.to_ptr_and_extra(); + + let (elem_ty, len) = base.elem_ty_and_len(outer_ty); + let elem_size = self.type_size(elem_ty)?.expect( + "slice element must be sized", + ); + if idx.is_concrete() { + let n = idx.to_u64()?; + assert!( + n < len, + "Tried to access element {} of array/slice with length {}", + n, + len + ); + let ptr_primval = match (base_ptr.to_ptr(), elem_size) { + (Ok(p), _) => PrimVal::Ptr(p.offset(n * elem_size, (&self).data_layout())?), + (Err(_), 0) => base_ptr, + (Err(e), _) => return Err(e), + }; + Ok(Lvalue::Ptr { + ptr: ptr_primval, + extra: LvalueExtra::None, + }) + } else { + let ptr_primval = match (base_ptr.to_ptr(), elem_size) { + (Ok(p), _) => { + let byte_index = self.memory.constraints.add_binop_constraint( + mir::BinOp::Mul, + idx, + PrimVal::Bytes(elem_size as u128), + PrimValKind::U64); + + let base_offset_primval = match p.offset { + PointerOffset::Concrete(n) => PrimVal::Bytes(n as u128), + PointerOffset::Abstract(sbytes) => PrimVal::Abstract(sbytes), + }; + + match self.memory.constraints.add_binop_constraint( + mir::BinOp::Add, + base_offset_primval, + byte_index, + PrimValKind::U64) { + PrimVal::Abstract(sbytes) => { + PrimVal::Ptr(Pointer::new_abstract(p.alloc_id, sbytes)) + } + _ => unreachable!(), + } + + } + (Err(_), 0) => base_ptr, + (Err(e), _) => return Err(e), + }; + + Ok(Lvalue::Ptr { + ptr: ptr_primval, + extra: LvalueExtra::None, + }) + } + } + + pub(super) fn lvalue_downcast( + &mut self, + base: Lvalue<'tcx>, + variant: usize, + ) -> EvalResult<'tcx, Lvalue<'tcx>> { + // FIXME(solson) + let base = self.force_allocation(base)?; + let (ptr, _) = base.to_ptr_and_extra(); + let extra = LvalueExtra::DowncastVariant(variant); Ok(Lvalue::Ptr { ptr, extra }) } - fn eval_lvalue_projection( + pub(super) fn eval_lvalue_projection( &mut self, - proj: &mir::LvalueProjection<'tcx>, + base: Lvalue<'tcx>, + base_ty: Ty<'tcx>, + proj_elem: &mir::ProjectionElem<'tcx, mir::Local, Ty<'tcx>>, ) -> EvalResult<'tcx, Lvalue<'tcx>> { use rustc::mir::ProjectionElem::*; - let (ptr, extra): (PrimVal, _) = match proj.elem { - Field(field, field_ty) => { - let base = self.eval_lvalue(&proj.base)?; - let base_ty = self.lvalue_ty(&proj.base); - return self.lvalue_field(base, field.index(), base_ty, field_ty); + let (ptr, extra) = match *proj_elem { + Field(field, _) => { + let layout = self.type_layout(base_ty)?; + return Ok(self.lvalue_field(base, field, layout)?.0); } Downcast(_, variant) => { - let base = self.eval_lvalue(&proj.base)?; - let base_ty = self.lvalue_ty(&proj.base); - let base_layout = self.type_layout(base_ty)?; - // FIXME(solson) - let base = self.force_allocation(base)?; - let (base_ptr, base_extra) = base.to_ptr_and_extra(); - - use rustc::ty::layout::Layout::*; - let extra = match *base_layout { - General { .. } => LvalueExtra::DowncastVariant(variant), - RawNullablePointer { .. } | StructWrappedNullablePointer { .. } => base_extra, - _ => bug!("variant downcast on non-aggregate: {:?}", base_layout), - }; - (base_ptr, extra) + return self.lvalue_downcast(base, variant); } Deref => { - let base_ty = self.lvalue_ty(&proj.base); - let val = self.eval_and_read_lvalue(&proj.base)?; + let val = self.read_lvalue(base)?; let pointee_type = match base_ty.sty { ty::TyRawPtr(ref tam) | ty::TyRef(_, ref tam) => tam.ty, - ty::TyAdt(ref def, _) if def.is_box() => base_ty.boxed_ty(), + ty::TyAdt(def, _) if def.is_box() => base_ty.boxed_ty(), _ => bug!("can only deref pointer types"), }; trace!("deref to {} on {:?}", pointee_type, val); - match self.tcx.struct_tail(pointee_type).sty { - ty::TyDynamic(..) => { - let (ptr, vtable) = val.expect_ptr_vtable_pair(&self.memory)?; - (ptr, LvalueExtra::Vtable(vtable)) - }, - ty::TyStr | ty::TySlice(_) => { - let (ptr, len) = val.expect_slice(&self.memory)?; - (ptr, LvalueExtra::Length(len)) - }, - _ => (val.read_ptr(&self.memory)?, LvalueExtra::None), - } + return self.val_to_lvalue(val, pointee_type); } Index(local) => { - let base = self.eval_lvalue(&proj.base)?; - let base_ty = self.lvalue_ty(&proj.base); - // FIXME(solson) - let base = self.force_allocation(base)?; - let (base_ptr, _) = base.to_ptr_and_extra(); - - let (elem_ty, len) = base.elem_ty_and_len(base_ty); - let elem_size = self.type_size(elem_ty)?.expect("slice element must be sized"); - let value = self.frame().get_local(local)?; let ty = self.tcx.types.usize; - let primval = self.value_to_primval(value, ty)?; - if let PrimVal::Abstract(_sbytes) = primval { - // byte_offset = sbytes * elem_size - let byte_offset = self.memory.constraints.add_binop_constraint( - mir::BinOp::Mul, - primval, - PrimVal::Bytes(elem_size as u128), - PrimValKind::U64); - - if let PrimVal::Abstract(sbytes) = byte_offset { - (PrimVal::Ptr(Pointer::new_abstract(base_ptr.to_ptr()?.alloc_id, sbytes)), - LvalueExtra::None) - } else { - unreachable!() - } - } else { - let n = primval.to_u64()?; - assert!(n < len); - let ptr = base_ptr.offset(n * elem_size, self.memory.layout)?; - (ptr, LvalueExtra::None) - } + let idx = self.value_to_primval(value, ty)?; + return self.lvalue_index(base, base_ty, idx); } - ConstantIndex { offset, min_length, from_end } => { - let base = self.eval_lvalue(&proj.base)?; - let base_ty = self.lvalue_ty(&proj.base); + ConstantIndex { + offset, + min_length, + from_end, + } => { // FIXME(solson) let base = self.force_allocation(base)?; let (base_ptr, _) = base.to_ptr_and_extra(); let (elem_ty, n) = base.elem_ty_and_len(base_ty); - let elem_size = self.type_size(elem_ty)?.expect("sequence element must be sized"); + let elem_size = self.type_size(elem_ty)?.expect( + "sequence element must be sized", + ); assert!(n >= min_length as u64); let index = if from_end { @@ -438,27 +463,32 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { u64::from(offset) }; - let ptr = base_ptr.offset(index * elem_size, self.memory.layout)?; + let ptr = base_ptr.to_ptr()?.offset(index * elem_size, (&self).data_layout())?; (ptr, LvalueExtra::None) } Subslice { from, to } => { - let base = self.eval_lvalue(&proj.base)?; - let base_ty = self.lvalue_ty(&proj.base); // FIXME(solson) let base = self.force_allocation(base)?; let (base_ptr, _) = base.to_ptr_and_extra(); let (elem_ty, n) = base.elem_ty_and_len(base_ty); - let elem_size = self.type_size(elem_ty)?.expect("slice element must be sized"); + let elem_size = self.type_size(elem_ty)?.expect( + "slice element must be sized", + ); assert!(u64::from(from) <= n - u64::from(to)); - let ptr = base_ptr.offset(u64::from(from) * elem_size, self.memory.layout)?; - let extra = LvalueExtra::Length(PrimVal::from_u128((n - u64::from(to) - u64::from(from))as u128)); + let ptr = base_ptr.to_ptr()?.offset(u64::from(from) * elem_size, (&self).data_layout())?; + // sublicing arrays produces arrays + let extra = if self.type_is_sized(base_ty) { + LvalueExtra::None + } else { + LvalueExtra::Length(PrimVal::Bytes((n - u64::from(to) - u64::from(from)) as u128)) + }; (ptr, extra) } }; - Ok(Lvalue::Ptr { ptr, extra }) + Ok(Lvalue::Ptr { ptr: PrimVal::Ptr(ptr), extra }) } pub(super) fn lvalue_ty(&self, lvalue: &mir::Lvalue<'tcx>) -> Ty<'tcx> { diff --git a/src/memory.rs b/src/memory.rs index b06f5f1ca7ce2b82f62266d4a836c463bf41b089..7c6ba67e061cf47887142468d8cfe8c3aece3a41 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -7,7 +7,7 @@ use rustc::ty::layout::{self, TargetDataLayout}; use constraints::ConstraintContext; use error::{EvalError, EvalResult}; -use value::{self, PrimVal, PrimValKind}; +use value::{self, PrimVal, PrimValKind, Value}; //////////////////////////////////////////////////////////////////////////////// // Allocations and pointers @@ -140,6 +140,10 @@ impl Pointer { _ => unimplemented!(), } } + + pub fn to_value_with_vtable(self, vtable: Pointer) -> Value { + Value::ByValPair(PrimVal::Ptr(self), PrimVal::Ptr(vtable)) + } } //////////////////////////////////////////////////////////////////////////////// @@ -213,6 +217,12 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } } + //// Trunace the given value to the pointer size; also return whether there was an overflow + pub fn truncate_to_ptr(&self, val: u128) -> (u64, bool) { + let max_ptr_plus_1 = 1u128 << self.layout.pointer_size.bits(); + ((val % max_ptr_plus_1) as u64, val >= max_ptr_plus_1) + } + pub fn allocations(&self) -> ::std::collections::hash_map::Iter<AllocId, Allocation> { self.alloc_map.iter() } @@ -858,6 +868,42 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { Ok(()) } + pub fn read_primval(&self, ptr: Pointer, size: u64, signed: bool) -> EvalResult<'tcx, PrimVal> { + self.check_relocation_edges(ptr, size)?; // Make sure we don't read part of a pointer as a pointer + let endianess = self.endianness(); + let bytes = self.get_bytes_unchecked(ptr, size, self.int_align(size)?)?; + // Undef check happens *after* we established that the alignment is correct. + // We must not return Ok() for unaligned pointers! + if self.check_defined(ptr, size).is_err() { + return Ok(PrimVal::Undef.into()); + } + // Now we do the actual reading + let bytes = if signed { + read_target_int(endianess, bytes).unwrap() as u128 + } else { + read_target_uint(endianess, bytes).unwrap() + }; + // See if we got a pointer + if size != self.pointer_size() { + if self.relocations(ptr, size)?.count() != 0 { + return Err(EvalError::ReadPointerAsBytes); + } + } else { + let alloc = self.get(ptr.alloc_id)?; + match ptr.offset { + PointerOffset::Concrete(off) => { + match alloc.relocations.get(&off) { + Some(&alloc_id) => return Ok(PrimVal::Ptr(Pointer::new(alloc_id, bytes as u64))), + None => {}, + } + } + PointerOffset::Abstract(_) => unimplemented!(), + } + } + // We don't. Just return the bytes. + Ok(PrimVal::Bytes(bytes)) + } + pub fn read_ptr(&self, ptr: Pointer) -> EvalResult<'tcx, PrimVal> { let size = self.pointer_size(); if self.check_defined(ptr, size).is_err() { diff --git a/src/step.rs b/src/step.rs index 8895817647f735578ddb8552ef3207535c443b78..3eab447ed9ec17d0393e043ea7c5a0e676a60a56 100644 --- a/src/step.rs +++ b/src/step.rs @@ -7,7 +7,6 @@ use rustc::hir; use rustc::mir::visit::{Visitor, LvalueContext}; use rustc::mir; use rustc::traits::Reveal; -use rustc::ty::layout::Layout; use rustc::ty::{subst, self}; use rustc::middle::const_val::ConstVal; @@ -15,7 +14,6 @@ use error::{EvalResult, EvalError}; use eval_context::{EvalContext, StackPopCleanup}; use executor::FinishStep; use lvalue::{Global, GlobalId, Lvalue}; -use value::{Value, PrimVal}; use syntax::codemap::Span; impl<'a, 'tcx> EvalContext<'a, 'tcx> { @@ -86,32 +84,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match stmt.kind { Assign(ref lvalue, ref rvalue) => self.eval_rvalue_into_lvalue(rvalue, lvalue)?, - SetDiscriminant { ref lvalue, variant_index } => { + SetDiscriminant { + ref lvalue, + variant_index, + } => { let dest = self.eval_lvalue(lvalue)?; let dest_ty = self.lvalue_ty(lvalue); - let dest_layout = self.type_layout(dest_ty)?; - - match *dest_layout { - Layout::General { discr, .. } => { - // FIXME: I (oli-obk) think we need to check the - // `dest_ty` for the variant's discriminant and write - // instead of the variant index - // We don't have any tests actually going through these lines - let discr_ty = discr.to_ty(&self.tcx, false); - let discr_lval = self.lvalue_field(dest, 0, dest_ty, discr_ty)?; - - self.write_value(Value::ByVal(PrimVal::Bytes(variant_index as u128)), discr_lval, discr_ty)?; - } - - Layout::RawNullablePointer { nndiscr, .. } => { - use value::PrimVal; - if variant_index as u64 != nndiscr { - self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?; - } - } - - _ => bug!("SetDiscriminant on {} represented as {:#?}", dest_ty, dest_layout), - } + self.write_discriminant_value(dest_ty, dest, variant_index)?; } // Miri can safely ignore these. Only translation needs it. diff --git a/src/terminator/intrinsic.rs b/src/terminator/intrinsic.rs index 0fbc88becd0a973b1f4bead2c2884cbb814a633d..8fc92b9a0c45646f8a99e130d2bb096dbcf7b425 100644 --- a/src/terminator/intrinsic.rs +++ b/src/terminator/intrinsic.rs @@ -1,6 +1,6 @@ use rustc::mir; use rustc::traits::Reveal; -use rustc::ty::layout::{Layout, Size, Align}; +use rustc::ty::layout::{Size, Align}; use rustc::ty::subst::Substs; use rustc::ty::{self, Ty}; @@ -17,7 +17,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { args: &[mir::Operand<'tcx>], dest: Lvalue<'tcx>, dest_ty: Ty<'tcx>, - dest_layout: &'tcx Layout, + dest_layout: ty::layout::TyLayout<'tcx>, target: mir::BasicBlock, ) -> EvalResult<'tcx> { let arg_vals: EvalResult<Vec<Value>> = args.iter() @@ -179,7 +179,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "discriminant_value" => { let ty = instance.substs.type_at(0); let adt_ptr = arg_vals[0].read_ptr(&self.memory)?.to_ptr()?; - let discr_val = self.read_discriminant_value(adt_ptr, ty)?; + let discr_val = self.read_discriminant_value(Lvalue::from_ptr(adt_ptr), ty)?; self.write_primval(dest, PrimVal::Bytes(discr_val), dest_ty)?; } @@ -290,7 +290,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "pref_align_of" => { let ty = instance.substs.type_at(0); let layout = self.type_layout(ty)?; - let align = layout.align(&self.tcx.data_layout).pref(); + let align = layout.align.pref(); let align_val = PrimVal::from_u128(align as u128); self.write_primval(dest, align_val, dest_ty)?; } @@ -404,14 +404,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "size_of_val" => { let ty = instance.substs.type_at(0); let (size, _) = self.size_and_align_of_dst(ty, arg_vals[0])?; - self.write_primval(dest, PrimVal::from_u128(size as u128), dest_ty)?; + self.write_primval(dest, PrimVal::from_u128(size.bytes() as u128), dest_ty)?; } "min_align_of_val" | "align_of_val" => { let ty = instance.substs.type_at(0); let (_, align) = self.size_and_align_of_dst(ty, arg_vals[0])?; - self.write_primval(dest, PrimVal::from_u128(align as u128), dest_ty)?; + self.write_primval(dest, PrimVal::from_u128(align.abi() as u128), dest_ty)?; } "type_name" => { @@ -436,7 +436,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } "uninit" => { - let size = dest_layout.size(&self.tcx.data_layout).bytes(); + let size = dest_layout.size.bytes(); let uninit = |this: &mut Self, val: Value| { match val { Value::ByRef(ptr) => { @@ -481,41 +481,51 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(()) } + /// Return the size and aligment of the value at the given type. + /// Note that the value does not matter if the type is sized. For unsized types, + /// the value has to be a fat pointer, and we only care about the "extra" data in it. pub fn size_and_align_of_dst( - &self, + &mut self, ty: ty::Ty<'tcx>, value: Value, - ) -> EvalResult<'tcx, (u64, u64)> { - if let Some(size) = self.type_size(ty)? { - Ok((size as u64, self.type_align(ty)? as u64)) + ) -> EvalResult<'tcx, (Size, Align)> { + let layout = self.type_layout(ty)?; + if !layout.is_unsized() { + Ok(layout.size_and_align()) } else { match ty.sty { - ty::TyAdt(def, substs) => { + ty::TyAdt(..) | ty::TyTuple(..) => { // First get the size of all statically known fields. // Don't use type_of::sizing_type_of because that expects t to be sized, // and it also rounds up to alignment, which we want to avoid, // as the unsized field's alignment could be smaller. assert!(!ty.is_simd()); - let layout = self.type_layout(ty)?; debug!("DST {} layout: {:?}", ty, layout); - let (sized_size, sized_align) = match *layout { - ty::layout::Layout::Univariant { ref variant, .. } => { - (variant.offsets.last().map_or(0, |o| o.bytes()), variant.align) - } - _ => { - bug!("size_and_align_of_dst: expcted Univariant for `{}`, found {:#?}", - ty, layout); - } - }; - debug!("DST {} statically sized prefix size: {} align: {:?}", - ty, sized_size, sized_align); + let sized_size = layout.fields.offset(layout.fields.count() - 1); + let sized_align = layout.align; + debug!( + "DST {} statically sized prefix size: {:?} align: {:?}", + ty, + sized_size, + sized_align + ); // Recurse to get the size of the dynamically sized field (must be // the last field). - let last_field = def.struct_variant().fields.last().unwrap(); - let field_ty = self.field_ty(substs, last_field); - let (unsized_size, unsized_align) = self.size_and_align_of_dst(field_ty, value)?; + let (unsized_size, unsized_align) = match ty.sty { + ty::TyAdt(def, substs) => { + let last_field = def.struct_variant().fields.last().unwrap(); + let field_ty = self.field_ty(substs, last_field); + self.size_and_align_of_dst(field_ty, value)? + } + ty::TyTuple(ref types, _) => { + let field_ty = types.last().unwrap(); + let field_ty = self.tcx.fully_normalize_monormophic_ty(field_ty); + self.size_and_align_of_dst(field_ty, value)? + } + _ => bug!("We already checked that we know this type"), + }; // FIXME (#26403, #27023): We should be adding padding // to `sized_size` (to accommodate the `unsized_align` @@ -529,7 +539,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // Choose max of two known alignments (combined value must // be aligned according to more restrictive of the two). - let align = sized_align.max(Align::from_bytes(unsized_align, unsized_align).unwrap()); + let align = sized_align.max(unsized_align); // Issue #27023: must add any necessary padding to `size` // (to make it a multiple of `align`) before returning it. @@ -542,30 +552,25 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // // `(size + (align-1)) & -align` - let size = Size::from_bytes(size).abi_align(align).bytes(); - Ok((size, align.abi())) + Ok((size.abi_align(align), align)) } ty::TyDynamic(..) => { - let (_, vtable) = value.expect_ptr_vtable_pair(&self.memory)?; + let (_, vtable) = value.into_ptr_vtable_pair(&mut self.memory)?; // 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_ty = ty.sequence_element_type(self.tcx); - let elem_size = self.type_size(elem_ty)?.expect("slice element must be sized") as u64; - let (_, len) = value.expect_slice(&self.memory)?; - let align = self.type_align(elem_ty)?; - match len { - PrimVal::Bytes(n) => Ok(((n as u64) * elem_size, align as u64)), - _ => unimplemented!(), - } + let (elem_size, align) = layout.field(&self, 0)?.size_and_align(); + let (_, len) = value.into_slice(&mut self.memory)?; + Ok((elem_size * len.to_u64()?, align)) } _ => bug!("size_of_val::<{:?}>", ty), } } } + /// Returns the normalized type of a struct field fn field_ty( &self, diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index a4c558dcceae204d8a20ca5a249aa1bcf031347e..e382098afa8819b4daf23c9f1d1b7ca3929c7de3 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -1,17 +1,17 @@ use rustc::hir::def_id::DefId; use rustc::mir; use rustc::ty::{self, TypeVariants, Ty}; -use rustc::ty::layout::Layout; +use rustc::ty::layout::HasDataLayout; use syntax::codemap::Span; use syntax::attr; use syntax::abi::Abi; use constraints::Constraint; use error::{EvalError, EvalResult}; -use eval_context::{EvalContext, IntegerExt, StackPopCleanup, is_inhabited}; +use eval_context::{EvalContext, StackPopCleanup, ValTy, is_inhabited}; use executor::{FinishStep, FinishStepVariant}; use lvalue::Lvalue; -use memory::{Pointer, SByte}; +use memory::{SByte}; use value::{PrimVal, PrimValKind}; use value::Value; use rustc_data_structures::indexed_vec::Idx; @@ -128,7 +128,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } (instance, sig) }, - ty::TyFnDef(def_id, substs) => (::eval_context::resolve(self.tcx, def_id, substs), func_ty.fn_sig(self.tcx)), + ty::TyFnDef(def_id, substs) => (self.resolve(def_id, substs)?, func_ty.fn_sig(self.tcx)), _ => { let msg = format!("can't handle callee of type {:?}", func_ty); return Err(EvalError::Unimplemented(msg)); @@ -279,9 +279,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { if check_ty_compat(sig.output(), real_sig.output()) && real_sig.inputs_and_output.len() == 3 => { // First argument of real_sig must be a ZST let fst_ty = real_sig.inputs_and_output[0]; - let layout = self.type_layout(fst_ty)?; - let size = layout.size(&self.tcx.data_layout).bytes(); - if size == 0 { + if self.type_layout(fst_ty)?.is_zst() { // Second argument must be a tuple matching the argument list of sig let snd_ty = real_sig.inputs_and_output[1]; match snd_ty.sty { @@ -294,6 +292,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } _ => {} + }; // Nope, this doesn't work. @@ -382,7 +381,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { for arg in arg_operands { let arg_val = self.eval_operand(arg)?; let arg_ty = self.operand_ty(arg); - args.push((arg_val, arg_ty)); + args.push(ValTy { value: arg_val, ty: arg_ty }); } if self.eval_fn_call_inner( @@ -401,7 +400,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { trace!("arg_operands: {:?}", arg_operands); match sig.abi { Abi::Rust => { - for (arg_local, (arg_val, arg_ty)) in arg_locals.zip(args) { + for (arg_local, ValTy { value: arg_val, ty: arg_ty }) in arg_locals.zip(args) { let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?; self.write_value(arg_val, dest, arg_ty)?; } @@ -412,41 +411,54 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { { // write first argument let first_local = arg_locals.next().unwrap(); let dest = self.eval_lvalue(&mir::Lvalue::Local(first_local))?; - let (arg_val, arg_ty) = args.remove(0); - self.write_value(arg_val, dest, arg_ty)?; + self.write_value(args[0].value, dest, args[0].ty)?; } // unpack and write all other args - let (arg_val, arg_ty) = args.remove(0); - let layout = self.type_layout(arg_ty)?; - if let (&ty::TyTuple(fields, _), &Layout::Univariant { ref variant, .. }) = (&arg_ty.sty, layout) { - trace!("fields: {:?}", fields); - if self.frame().mir.args_iter().count() == fields.len() + 1 { - let offsets = variant.offsets.iter().map(|s| s.bytes()); - match arg_val { + let layout = self.type_layout(args[1].ty)?; + if let ty::TyTuple(..) = args[1].ty.sty { + if self.frame().mir.args_iter().count() == layout.fields.count() + 1 { + match args[1].value { Value::ByRef(ptr) => { - for ((offset, ty), arg_local) in offsets.zip(fields).zip(arg_locals) { - let arg = Value::ByRef(ptr.offset(offset, self.memory.layout)?); - let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?; - trace!("writing arg {:?} to {:?} (type: {})", arg, dest, ty); - self.write_value(arg, dest, ty)?; + for (i, arg_local) in arg_locals.enumerate() { + let field = layout.field(&self, i)?; + let offset = layout.fields.offset(i).bytes(); + let arg = Value::ByRef(ptr.offset(offset, (&self).data_layout())?); + let dest = + self.eval_lvalue(&mir::Lvalue::Local(arg_local))?; + trace!( + "writing arg {:?} to {:?} (type: {})", + arg, + dest, + field.ty + ); + self.write_value(arg, dest, field.ty)?; } - }, - Value::ByVal(PrimVal::Undef) => {}, + } + Value::ByVal(PrimVal::Undef) => {} other => { - assert_eq!(fields.len(), 1); - let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_locals.next().unwrap()))?; - self.write_value(other, dest, fields[0])?; + assert_eq!(layout.fields.count(), 1); + let dest = self.eval_lvalue(&mir::Lvalue::Local( + arg_locals.next().unwrap(), + ))?; + let field_ty = layout.field(&self, 0)?.ty; + self.write_value(other, dest, field_ty)?; } } } else { trace!("manual impl of rust-call ABI"); // called a manual impl of a rust-call function - let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_locals.next().unwrap()))?; - self.write_value(arg_val, dest, arg_ty)?; + let dest = self.eval_lvalue( + &mir::Lvalue::Local(arg_locals.next().unwrap()), + )?; + self.write_value(args[1].value, dest, args[1].ty)?; } } else { - bug!("rust-call ABI tuple argument was {:?}, {:?}", arg_ty, layout); + bug!( + "rust-call ABI tuple argument was {:#?}, {:#?}", + args[1].ty, + layout + ); } } _ => unimplemented!(), @@ -455,12 +467,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }, ty::InstanceDef::Virtual(_, idx) => { let ptr_size = self.memory.pointer_size(); - let (_, vtable) = self.eval_operand(&arg_operands[0])?.expect_ptr_vtable_pair(&self.memory)?; + let (_, vtable) = self.eval_operand(&arg_operands[0])?.into_ptr_vtable_pair(&self.memory)?; let fn_ptr = self.memory.read_ptr(vtable.offset(ptr_size * (idx as u64 + 3), self.memory.layout)?)?; let instance = self.memory.get_fn(fn_ptr.to_ptr()?)?; let mut arg_operands = arg_operands.to_vec(); let ty = self.operand_ty(&arg_operands[0]); - let ty = self.get_field_ty(ty, 0)?; + let ty = self.get_field_ty(ty, 0)?.ty; match arg_operands[0] { mir::Operand::Consume(ref mut lval) => *lval = lval.clone().field(mir::Field::new(0), ty), _ => bug!("virtual call first arg cannot be a constant"), @@ -498,8 +510,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.goto_block(block); return Ok(true); } - "<std::io::Stdin as std::io::Read>::read" | - "<std::io::Stdin as std::io::Read>::read_exact" => { + "<std::io::Stdin as std::io::Read>::read" => { let (lval, block) = destination.expect("Stdin::read() does not diverge"); let args_res: EvalResult<Vec<Value>> = arg_operands.iter() .map(|arg| self.eval_operand(arg)) @@ -532,6 +543,34 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.goto_block(block); return Ok(true); } + "<std::io::Stdin as std::io::Read>::read_exact" => { + let (lval, block) = destination.expect("Stdin::read() does not diverge"); + let args_res: EvalResult<Vec<Value>> = arg_operands.iter() + .map(|arg| self.eval_operand(arg)) + .collect(); + let args = args_res?; + + match args[1] { + Value::ByValPair(PrimVal::Ptr(ptr), PrimVal::Bytes(len)) => { + self.memory.write_fresh_abstract_bytes(ptr, len as u64)?; + } + _ => { + unimplemented!() + } + } + + let dest_ty = sig.output(); + + // FIXME make this more robust + self.write_discriminant_value( + dest_ty, + lval, + 0)?; + + self.goto_block(block); + return Ok(true); + } + "std::io::Stdin::lock" => { return Err( EvalError::Unimplemented( @@ -570,56 +609,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(false) } - pub fn read_discriminant_value(&self, adt_ptr: Pointer, adt_ty: Ty<'tcx>) -> EvalResult<'tcx, u128> { - use rustc::ty::layout::Layout::*; - let adt_layout = self.type_layout(adt_ty)?; - trace!("read_discriminant_value {:#?}", adt_layout); - - let discr_val = match *adt_layout { - General { discr, .. } | CEnum { discr, signed: false, .. } => { - let discr_size = discr.size().bytes(); - self.memory.read_uint(adt_ptr, discr_size)? - } - - CEnum { discr, signed: true, .. } => { - let discr_size = discr.size().bytes(); - self.memory.read_int(adt_ptr, discr_size)? as u128 - } - - RawNullablePointer { nndiscr, value } => { - let discr_size = value.size(&self.tcx.data_layout).bytes(); - trace!("rawnullablepointer with size {}", discr_size); - self.read_nonnull_discriminant_value(adt_ptr, nndiscr as u128, discr_size)? - } - - StructWrappedNullablePointer { nndiscr, ref discrfield_source, .. } => { - let (offset, ty) = self.nonnull_offset_and_ty(adt_ty, nndiscr, discrfield_source)?; - let nonnull = adt_ptr.offset(offset.bytes(), self.memory.layout)?; - trace!("struct wrapped nullable pointer type: {}", ty); - // only the pointer part of a fat pointer is used for this space optimization - let discr_size = self.type_size(ty)?.expect("bad StructWrappedNullablePointer discrfield"); - self.read_nonnull_discriminant_value(nonnull, nndiscr as u128, discr_size)? - } - - // The discriminant_value intrinsic returns 0 for non-sum types. - Array { .. } | FatPointer { .. } | Scalar { .. } | Univariant { .. } | - Vector { .. } | UntaggedUnion { .. } => 0, - }; - - Ok(discr_val) - } - - fn read_nonnull_discriminant_value(&self, ptr: Pointer, nndiscr: u128, discr_size: u64) -> EvalResult<'tcx, u128> { - trace!("read_nonnull_discriminant_value: {:?}, {}, {}", ptr, nndiscr, discr_size); - let not_null = match self.memory.read_uint(ptr, discr_size) { - Ok(0) => false, - Ok(_) | Err(EvalError::ReadPointerAsBytes) => true, - Err(e) => return Err(e), - }; - assert!(nndiscr == 0 || nndiscr == 1); - Ok(if not_null { nndiscr } else { 1 - nndiscr }) - } - /// Returns Ok() when the function was handled, fail otherwise fn call_missing_fn( &mut self, diff --git a/src/traits.rs b/src/traits.rs index c5302baa42e4f8fe701a2a4e7a10ac25ae83c74b..ebbae109c86d90cafe8ee180eb13a6c7b8492ed4 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -7,6 +7,7 @@ use value::{Value, PrimVal}; use rustc::hir::def_id::DefId; use rustc::ty::subst::Substs; use rustc::ty::{self, Ty}; +use rustc::ty::layout::{Size, Align, HasDataLayout}; use syntax::codemap::DUMMY_SP; use syntax::ast; @@ -62,7 +63,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { for (i, method) in self.tcx.vtable_methods(trait_ref).iter().enumerate() { if let Some((def_id, substs)) = *method { - let instance = ::eval_context::resolve(self.tcx, def_id, substs); + let instance = self.resolve(def_id, substs)?; let fn_ptr = self.memory.create_fn_alloc(instance); self.memory.write_ptr(vtable.offset(ptr_size * (3 + i as u64), self.memory.layout)?, fn_ptr)?; } @@ -86,11 +87,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.memory.get_fn(drop_fn).map(Some) } - pub fn read_size_and_align_from_vtable(&self, vtable: Pointer) -> EvalResult<'tcx, (u64, u64)> { + pub fn read_size_and_align_from_vtable( + &self, + vtable: Pointer, + ) -> EvalResult<'tcx, (Size, Align)> { let pointer_size = self.memory.pointer_size(); - let size = self.memory.read_usize(vtable.offset(pointer_size, self.memory.layout)?)?; - let align = self.memory.read_usize(vtable.offset(pointer_size * 2, self.memory.layout)?)?; - Ok((size, align)) + let size = self.memory.read_usize(vtable.offset(pointer_size, self.data_layout())?)?; + let align = self.memory.read_usize(vtable.offset(pointer_size * 2, self.data_layout())?)?; + Ok((Size::from_bytes(size), Align::from_bytes(align, align).unwrap())) } pub(crate) fn resolve_associated_const( diff --git a/src/value.rs b/src/value.rs index ba49541f7c0cd2c3a5a4e970d1ac0f24b0540575..c83e6630267f445411c33647edf8ae0826e4472d 100644 --- a/src/value.rs +++ b/src/value.rs @@ -83,7 +83,7 @@ impl<'a, 'tcx: 'a> Value { } } - pub(super) fn expect_ptr_vtable_pair( + pub(super) fn into_ptr_vtable_pair( &self, mem: &Memory<'a, 'tcx> ) -> EvalResult<'tcx, (PrimVal, Pointer)> { @@ -101,16 +101,16 @@ impl<'a, 'tcx: 'a> Value { } } - pub(super) fn expect_slice(&self, mem: &Memory<'a, 'tcx>) -> EvalResult<'tcx, (PrimVal, PrimVal)> { + pub(super) fn into_slice(&self, mem: &Memory<'a, 'tcx>) -> EvalResult<'tcx, (PrimVal, PrimVal)> { use self::Value::*; match *self { ByRef(ref_ptr) => { let ptr = mem.read_ptr(ref_ptr)?; let len = mem.read_usize(ref_ptr.offset(mem.pointer_size(), mem.layout)?)?; - Ok((ptr, PrimVal::from_u128(len as u128))) + Ok((ptr, PrimVal::Bytes(len as u128))) }, - ByValPair(ptr, len) => { - Ok((ptr, len)) + ByValPair(ptr, val) => { + Ok((ptr, val)) }, _ => unimplemented!(), } @@ -153,7 +153,7 @@ impl<'tcx> PrimVal { match self { PrimVal::Bytes(b) => Ok(b), PrimVal::Abstract(_) => unimplemented!(), - PrimVal::Ptr(_) => Err(EvalError::ReadPointerAsBytes), + PrimVal::Ptr(_) => Err(EvalError::ReadBytesAsPointer), PrimVal::Undef => Err(EvalError::ReadUndefBytes), } } diff --git a/tests/compile-fail/reference_to_packed.rs b/tests/compile-fail/reference_to_packed.rs deleted file mode 100644 index 119225f3e369d1cd679f9b9099bed7e4806588bc..0000000000000000000000000000000000000000 --- a/tests/compile-fail/reference_to_packed.rs +++ /dev/null @@ -1,16 +0,0 @@ -#![allow(dead_code, unused_variables)] - -#[repr(packed)] -struct Foo { - x: i32, - y: i32, -} - -fn main() { - let foo = Foo { - x: 42, - y: 99, - }; - let p = &foo.x; - let i = *p; //~ ERROR tried to access memory with alignment 1, but alignment 4 is required -} \ No newline at end of file diff --git a/tests/compile-fail/reference_to_packed_unsafe.rs b/tests/compile-fail/reference_to_packed_unsafe.rs deleted file mode 100644 index 5761f23b7dd41a6dc8b4648b58a13df1e9fb0b69..0000000000000000000000000000000000000000 --- a/tests/compile-fail/reference_to_packed_unsafe.rs +++ /dev/null @@ -1,16 +0,0 @@ -#![allow(dead_code, unused_variables)] - -#[repr(packed)] -struct Foo { - x: i32, - y: i32, -} - -fn main() { - let foo = Foo { - x: 42, - y: 99, - }; - let p: *const i32 = &foo.x; - let x = unsafe { *p + foo.x }; //~ ERROR tried to access memory with alignment 1, but alignment 4 is required -} diff --git a/tests/compile-fail/repeat.rs b/tests/compile-fail/repeat.rs deleted file mode 100644 index abe89e233e7cd824849d93b527c3a176949aa8c4..0000000000000000000000000000000000000000 --- a/tests/compile-fail/repeat.rs +++ /dev/null @@ -1,5 +0,0 @@ -fn main() { - let data: [u8; std::usize::MAX] = [42; std::usize::MAX]; - //~^ ERROR: rustc layout computation failed: SizeOverflow([u8; - assert_eq!(data.len(), 1024); -} diff --git a/tests/compiletest.rs b/tests/compiletest.rs index 4a2d5e5a206ac7e1ae9193df96c0a48eb1723367..47f944257e4d8411916c110015381c6adfb20112 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -3,6 +3,16 @@ extern crate compiletest_rs as compiletest; use std::path::{PathBuf, Path}; use std::io::Write; +fn dylib_env_var() -> &'static str { + if cfg!(windows) { + "PATH" + } else if cfg!(target_os = "macos") { + "DYLD_LIBRARY_PATH" + } else { + "LD_LIBRARY_PATH" + } +} + fn compile_fail(sysroot: &Path) { let flags = format!("--emit-error --sysroot {} -Dwarnings", sysroot.to_str().expect("non utf8 path")); for_all_targets(&sysroot, |target| { @@ -106,7 +116,7 @@ fn compile_test() { let libs = Path::new(&sysroot).join("lib"); let sysroot = libs.join("rustlib").join(&host).join("lib"); let paths = std::env::join_paths(&[libs, sysroot]).unwrap(); - cmd.env(compiletest::procsrv::dylib_env_var(), paths); + cmd.env(dylib_env_var(), paths); match cmd.output() { Ok(ref output) if output.status.success() => { diff --git a/tests/run-pass/packed_struct.rs b/tests/run-pass/packed_struct.rs deleted file mode 100644 index 5b3f09c0dd096bfd264939d5e6d799a53f663699..0000000000000000000000000000000000000000 --- a/tests/run-pass/packed_struct.rs +++ /dev/null @@ -1,19 +0,0 @@ -#[repr(packed)] -struct S { - a: i32, - b: i64, -} - -fn main() { - let x = S { - a: 42, - b: 99, - }; - let a = x.a; - let b = x.b; - assert_eq!(a, 42); - assert_eq!(b, 99); - // can't do `assert_eq!(x.a, 42)`, because `assert_eq!` takes a reference - assert_eq!({x.a}, 42); - assert_eq!({x.b}, 99); -} diff --git a/tests/run-pass/u128.rs b/tests/run-pass/u128.rs deleted file mode 100644 index bd68157e4bc22745b8919bbf83303556fa89e07b..0000000000000000000000000000000000000000 --- a/tests/run-pass/u128.rs +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright 2016 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or -// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// ignore-stage0 -// ignore-stage1 - -// ignore-emscripten - -#![feature(i128_type)] - -fn b<T>(t: T) -> T { t } - -fn main() { - let x: u128 = 0xFFFF_FFFF_FFFF_FFFF__FFFF_FFFF_FFFF_FFFF; - assert_eq!(0, !x); - assert_eq!(0, !x); - let y: u128 = 0xFFFF_FFFF_FFFF_FFFF__FFFF_FFFF_FFFF_FFFE; - assert_eq!(!1, y); - assert_eq!(x, y | 1); - assert_eq!(0xFAFF_0000_FF8F_0000__FFFF_0000_FFFF_FFFE, - y & - 0xFAFF_0000_FF8F_0000__FFFF_0000_FFFF_FFFF); - let z: u128 = 0xABCD_EF; - assert_eq!(z * z, 0x734C_C2F2_A521); - assert_eq!(z * z * z * z, 0x33EE_0E2A_54E2_59DA_A0E7_8E41); - assert_eq!(z + z + z + z, 0x2AF3_7BC); - let k: u128 = 0x1234_5678_9ABC_DEFF_EDCB_A987_6543_210; - assert_eq!(k + k, 0x2468_ACF1_3579_BDFF_DB97_530E_CA86_420); - assert_eq!(0, k - k); - assert_eq!(0x1234_5678_9ABC_DEFF_EDCB_A987_5A86_421, k - z); - assert_eq!(0x1000_0000_0000_0000_0000_0000_0000_000, - k - 0x234_5678_9ABC_DEFF_EDCB_A987_6543_210); - assert_eq!(0x6EF5_DE4C_D3BC_2AAA_3BB4_CC5D_D6EE_8, k / 42); - assert_eq!(0, k % 42); - assert_eq!(15, z % 42); - assert_eq!(0x169D_A8020_CEC18, k % 0x3ACB_FE49_FF24_AC); - assert_eq!(0x91A2_B3C4_D5E6_F7, k >> 65); - assert_eq!(0xFDB9_7530_ECA8_6420_0000_0000_0000_0000, k << 65); - assert!(k > z); - assert!(y > k); - assert!(y < x); - assert_eq!(x as u64, !0); - assert_eq!(z as u64, 0xABCD_EF); - assert_eq!(k as u64, 0xFEDC_BA98_7654_3210); - assert_eq!(k as i128, 0x1234_5678_9ABC_DEFF_EDCB_A987_6543_210); - assert_eq!((z as f64) as u128, z); - assert_eq!((z as f32) as u128, z); - assert_eq!((z as f64 * 16.0) as u128, z * 16); - assert_eq!((z as f32 * 16.0) as u128, z * 16); - let l :u128 = 432 << 100; - assert_eq!((l as f32) as u128, l); - assert_eq!((l as f64) as u128, l); - // formatting - let j: u128 = 1 << 67; - /* - assert_eq!("147573952589676412928", format!("{}", j)); - assert_eq!("80000000000000000", format!("{:x}", j)); - assert_eq!("20000000000000000000000", format!("{:o}", j)); - assert_eq!("10000000000000000000000000000000000000000000000000000000000000000000", - format!("{:b}", j)); - assert_eq!("340282366920938463463374607431768211455", - format!("{}", u128::max_value())); - assert_eq!("147573952589676412928", format!("{:?}", j)); - */ - // common traits - assert_eq!(x, b(x.clone())); - // overflow checks - assert_eq!((z).checked_mul(z), Some(0x734C_C2F2_A521)); - assert_eq!((k).checked_mul(k), None); - let l: u128 = b(u128::max_value() - 10); - let o: u128 = b(17); - assert_eq!(l.checked_add(b(11)), None); - assert_eq!(l.checked_sub(l), Some(0)); - assert_eq!(o.checked_sub(b(18)), None); - assert_eq!(b(1u128).checked_shl(b(127)), Some(1 << 127)); - assert_eq!(o.checked_shl(b(128)), None); -}