From edf94c5f1b74bec279333e1e7f513096cccbee05 Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Fri, 19 Oct 2018 14:40:58 +0200 Subject: [PATCH 01/24] Update function names in comments --- src/librustc_mir/const_eval.rs | 2 +- src/librustc_mir/interpret/memory.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/librustc_mir/const_eval.rs b/src/librustc_mir/const_eval.rs index bc917140bbd67..ebb2b4e68fca6 100644 --- a/src/librustc_mir/const_eval.rs +++ b/src/librustc_mir/const_eval.rs @@ -129,7 +129,7 @@ pub fn op_to_const<'tcx>( assert!(alloc.bytes.len() as u64 - ptr.offset.bytes() >= op.layout.size.bytes()); let mut alloc = alloc.clone(); alloc.align = align; - // FIXME shouldnt it be the case that `mark_static_initialized` has already + // FIXME shouldnt it be the case that `intern_static` has already // interned this? I thought that is the entire point of that `FinishStatic` stuff? let alloc = ecx.tcx.intern_const_alloc(alloc); ConstValue::ByRef(ptr.alloc_id, alloc, ptr.offset) diff --git a/src/librustc_mir/interpret/memory.rs b/src/librustc_mir/interpret/memory.rs index 9adca6c429798..7661b70febd08 100644 --- a/src/librustc_mir/interpret/memory.rs +++ b/src/librustc_mir/interpret/memory.rs @@ -707,7 +707,7 @@ where mutability: Mutability, ) -> EvalResult<'tcx> { trace!( - "mark_static_initialized {:?}, mutability: {:?}", + "intern_static {:?}, mutability: {:?}", alloc_id, mutability ); From 856eaed501cb8403e9944d70744f3090769a6e2c Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Sun, 21 Oct 2018 19:36:03 +0200 Subject: [PATCH 02/24] Simplify `ConstValue` --- src/librustc/ich/impls_ty.rs | 13 +- src/librustc/mir/interpret/error.rs | 8 +- src/librustc/mir/interpret/mod.rs | 142 ++++++++++++++++++++- src/librustc/mir/interpret/value.rs | 130 +++++++++++++++---- src/librustc/mir/mod.rs | 120 +++++++++-------- src/librustc/traits/error_reporting.rs | 4 +- src/librustc/ty/structural_impls.rs | 8 +- src/librustc/ty/sty.rs | 56 ++++---- src/librustc_codegen_llvm/mir/constant.rs | 8 +- src/librustc_codegen_llvm/mir/operand.rs | 45 +------ src/librustc_lint/builtin.rs | 4 +- src/librustc_mir/const_eval.rs | 94 +++++--------- src/librustc_mir/hair/cx/expr.rs | 4 +- src/librustc_mir/hair/cx/mod.rs | 38 +++--- src/librustc_mir/hair/pattern/_match.rs | 20 +-- src/librustc_mir/hair/pattern/mod.rs | 101 +++++++-------- src/librustc_mir/interpret/memory.rs | 4 +- src/librustc_mir/interpret/operand.rs | 58 +-------- src/librustc_mir/interpret/place.rs | 37 +++++- src/librustc_mir/monomorphize/collector.rs | 11 +- src/librustc_mir/shim.rs | 5 +- src/librustc_mir/transform/const_prop.rs | 8 +- 22 files changed, 516 insertions(+), 402 deletions(-) diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs index 642eb11006649..5ca83be93d67d 100644 --- a/src/librustc/ich/impls_ty.rs +++ b/src/librustc/ich/impls_ty.rs @@ -375,13 +375,6 @@ for ::mir::interpret::ConstValue<'gcx> { def_id.hash_stable(hcx, hasher); substs.hash_stable(hcx, hasher); } - Scalar(val) => { - val.hash_stable(hcx, hasher); - } - ScalarPair(a, b) => { - a.hash_stable(hcx, hasher); - b.hash_stable(hcx, hasher); - } ByRef(id, alloc, offset) => { id.hash_stable(hcx, hasher); alloc.hash_stable(hcx, hasher); @@ -512,7 +505,7 @@ for ::mir::interpret::EvalErrorKind<'gcx, O> { hasher: &mut StableHasher) { use mir::interpret::EvalErrorKind::*; - mem::discriminant(&self).hash_stable(hcx, hasher); + mem::discriminant(self).hash_stable(hcx, hasher); match *self { FunctionArgCountMismatch | @@ -577,11 +570,11 @@ for ::mir::interpret::EvalErrorKind<'gcx, O> { NoMirFor(ref s) => s.hash_stable(hcx, hasher), UnterminatedCString(ptr) => ptr.hash_stable(hcx, hasher), PointerOutOfBounds { - ptr, + offset, access, allocation_size, } => { - ptr.hash_stable(hcx, hasher); + offset.hash_stable(hcx, hasher); access.hash_stable(hcx, hasher); allocation_size.hash_stable(hcx, hasher) }, diff --git a/src/librustc/mir/interpret/error.rs b/src/librustc/mir/interpret/error.rs index fe466e247c917..1b5409a6a2d0b 100644 --- a/src/librustc/mir/interpret/error.rs +++ b/src/librustc/mir/interpret/error.rs @@ -197,7 +197,7 @@ pub enum EvalErrorKind<'tcx, O> { InvalidBool, InvalidDiscriminant(u128), PointerOutOfBounds { - ptr: Pointer, + offset: Size, access: bool, allocation_size: Size, }, @@ -440,10 +440,10 @@ impl<'tcx, O: fmt::Debug> fmt::Debug for EvalErrorKind<'tcx, O> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { use self::EvalErrorKind::*; match *self { - PointerOutOfBounds { ptr, access, allocation_size } => { - write!(f, "{} at offset {}, outside bounds of allocation {} which has size {}", + PointerOutOfBounds { offset, access, allocation_size } => { + write!(f, "{} at offset {}, outside bounds of allocation with size {}", if access { "memory access" } else { "pointer computed" }, - ptr.offset.bytes(), ptr.alloc_id, allocation_size.bytes()) + offset.bytes(), allocation_size.bytes()) }, MemoryLockViolation { ptr, len, frame, access, ref lock } => { write!(f, "{:?} access by frame {} at {:?}, size {}, is in conflict with lock {:?}", diff --git a/src/librustc/mir/interpret/mod.rs b/src/librustc/mir/interpret/mod.rs index 4c2b2b2d41d1b..8f33f15910a76 100644 --- a/src/librustc/mir/interpret/mod.rs +++ b/src/librustc/mir/interpret/mod.rs @@ -28,7 +28,7 @@ pub use self::value::{Scalar, ConstValue}; use std::fmt; use mir; use hir::def_id::DefId; -use ty::{self, TyCtxt, Instance}; +use ty::{self, TyCtxt, Instance, Ty, ParamEnvAnd}; use ty::layout::{self, Align, HasDataLayout, Size}; use middle::region; use std::iter; @@ -545,7 +545,7 @@ pub struct Allocation { pub extra: Extra, } -impl Allocation { +impl Allocation { /// Creates a read-only allocation initialized by the given bytes pub fn from_bytes(slice: &[u8], align: Align) -> Self { let mut undef_mask = UndefMask::new(Size::ZERO); @@ -575,6 +575,144 @@ impl Allocation { extra: Extra::default(), } } + + #[inline] + pub fn size(&self) -> Size { + Size::from_bytes(self.bytes.len() as u64) + } + + pub fn check_align( + &self, + offset: Size, + required_align: Align, + ) -> EvalResult<'tcx> { + if self.align.abi() > required_align.abi() { + return err!(AlignmentCheckFailed { + has: self.align, + required: required_align, + }); + } + let offset = offset.bytes(); + if offset % required_align.abi() == 0 { + Ok(()) + } else { + let has = offset % required_align.abi(); + err!(AlignmentCheckFailed { + has: Align::from_bytes(has, has).unwrap(), + required: required_align, + }) + } + } + + pub fn check_bounds( + &self, + offset: Size, + size: Size, + access: bool, + ) -> EvalResult<'tcx> { + let end = offset + size; + let allocation_size = self.size(); + if end > allocation_size { + err!(PointerOutOfBounds { offset, access, allocation_size }) + } else { + Ok(()) + } + } + + pub fn check_defined( + &self, + offset: Size, + size: Size, + ) -> EvalResult<'tcx> { + self.undef_mask.is_range_defined( + offset, + offset + size, + ).or_else(|idx| err!(ReadUndefBytes(idx))) + } + + pub fn check_relocations( + &self, + hdl: impl HasDataLayout, + offset: Size, + size: Size, + ) -> EvalResult<'tcx> { + if self.relocations(hdl, offset, size)?.len() != 0 { + err!(ReadPointerAsBytes) + } else { + Ok(()) + } + } + + pub fn relocations( + &self, + hdl: impl HasDataLayout, + offset: Size, + size: Size, + ) -> EvalResult<'tcx, &[(Size, (Tag, AllocId))]> { + // We have to go back `pointer_size - 1` bytes, as that one would still overlap with + // the beginning of this range. + let start = offset.bytes().saturating_sub(hdl.pointer_size().bytes() - 1); + let end = offset + size; // this does overflow checking + Ok(self.relocations.range(Size::from_bytes(start)..end)) + } + + pub fn get_bytes( + &self, + hdl: impl HasDataLayout, + offset: Size, + size: Size, + required_align: Align, + ) -> EvalResult<'tcx, &[u8]> { + self.check_align(offset, required_align)?; + self.check_bounds(offset, size, true)?; + self.check_defined(offset, size)?; + self.check_relocations(hdl, offset, size)?; + Ok(self.bytes_ignoring_relocations_and_undef(offset, size)) + } + + pub fn read_bits( + &self, + tcx: TyCtxt<'_, '_, 'tcx>, + offset: Size, + ty: ParamEnvAnd<'tcx, Ty<'tcx>>, + ) -> EvalResult<'tcx, u128> { + let ty = tcx.lift_to_global(&ty).unwrap(); + let layout = tcx.layout_of(ty).unwrap_or_else(|e| { + panic!("could not compute layout for {:?}: {:?}", ty, e) + }); + let bytes = self.get_bytes(tcx, offset, layout.size, layout.align)?; + Ok(read_target_uint(tcx.data_layout.endian, bytes).unwrap()) + } + + pub fn read_scalar( + &self, + hdl: impl HasDataLayout, + offset: Size, + ) -> EvalResult<'tcx, Scalar> { + let size = hdl.data_layout().pointer_size; + let required_align = hdl.data_layout().pointer_align; + self.check_align(offset, required_align)?; + self.check_bounds(offset, size, true)?; + self.check_defined(offset, size)?; + let bytes = self.bytes_ignoring_relocations_and_undef(offset, size); + let offset = read_target_uint(hdl.data_layout().endian, &bytes).unwrap(); + let offset = Size::from_bytes(offset as u64); + if let Some(&(tag, alloc_id)) = self.relocations.get(&offset) { + Ok(Pointer::new_with_tag(alloc_id, offset, tag).into()) + } else { + Ok(Scalar::Bits { + bits: offset.bytes() as u128, + size: size.bytes() as u8, + }) + } + } + + fn bytes_ignoring_relocations_and_undef(&self, offset: Size, size: Size) -> &[u8] { + let end = offset + size; + let offset = offset.bytes() as usize; + let end = end.bytes() as usize; + &self.bytes[offset..end] + } } impl<'tcx> ::serialize::UseSpecializedDecodable for &'tcx Allocation {} diff --git a/src/librustc/mir/interpret/value.rs b/src/librustc/mir/interpret/value.rs index 9e54b146fd02a..387423c9af460 100644 --- a/src/librustc/mir/interpret/value.rs +++ b/src/librustc/mir/interpret/value.rs @@ -10,8 +10,9 @@ #![allow(unknown_lints)] -use ty::layout::{HasDataLayout, Size}; +use ty::layout::{HasDataLayout, Align, Size, TyLayout}; use ty::subst::Substs; +use ty; use hir::def_id::DefId; use super::{EvalResult, Pointer, PointerArithmetic, Allocation, AllocId, sign_extend, truncate}; @@ -25,16 +26,6 @@ pub enum ConstValue<'tcx> { /// evaluation Unevaluated(DefId, &'tcx Substs<'tcx>), - /// Used only for types with layout::abi::Scalar ABI and ZSTs - /// - /// Not using the enum `Value` to encode that this must not be `Undef` - Scalar(Scalar), - - /// Used only for *fat pointers* with layout::abi::ScalarPair - /// - /// Needed for pattern matching code related to slices and strings. - ScalarPair(Scalar, Scalar), - /// An allocation + offset into the allocation. /// Invariant: The AllocId matches the allocation. ByRef(AllocId, &'tcx Allocation, Size), @@ -42,40 +33,123 @@ pub enum ConstValue<'tcx> { impl<'tcx> ConstValue<'tcx> { #[inline] - pub fn try_to_scalar(&self) -> Option { - match *self { - ConstValue::Unevaluated(..) | - ConstValue::ByRef(..) | - ConstValue::ScalarPair(..) => None, - ConstValue::Scalar(val) => Some(val), + pub fn try_as_by_ref(&self) -> Option<(AllocId, &'tcx Allocation, Size)> { + match self { + ConstValue::Unevaluated(..) => None, + ConstValue::ByRef(a, b, c) => Some((*a, *b, *c)), } } #[inline] - pub fn try_to_bits(&self, size: Size) -> Option { - self.try_to_scalar()?.to_bits(size).ok() + /// if this is ByRef, return the same thing but with the offset increased by `n` + pub fn try_offset(&self, n: Size) -> Option { + let (id, alloc, offset) = self.try_as_by_ref()?; + Some(ConstValue::ByRef(id, alloc, offset + n)) + } + + #[inline] + pub fn try_get_bytes(&self, hdl: impl HasDataLayout, n: Size, align: Align) -> Option<&[u8]> { + let (_, alloc, offset) = self.try_as_by_ref()?; + alloc.get_bytes(hdl, offset, n, align).ok() + } + + #[inline] + pub fn try_to_bits(&self, hdl: impl HasDataLayout, layout: TyLayout<'tcx>) -> Option { + let bytes = self.try_get_bytes(hdl, layout.size, layout.align)?; + let endian = hdl.data_layout().endian; + super::read_target_uint(endian, &bytes).ok() } #[inline] - pub fn try_to_ptr(&self) -> Option { - self.try_to_scalar()?.to_ptr().ok() + pub fn try_to_usize(&self, hdl: impl HasDataLayout) -> Option { + let size = hdl.data_layout().pointer_size; + let align = hdl.data_layout().pointer_align; + let bytes = self.try_get_bytes(hdl, size, align)?; + let endian = hdl.data_layout().endian; + super::read_target_uint(endian, &bytes).ok() + } + + #[inline] + pub fn try_to_ptr( + &self, + hdl: impl HasDataLayout, + ) -> Option { + let (_, alloc, offset) = self.try_as_by_ref()?; + alloc.read_scalar(hdl, offset).ok()?.to_ptr().ok() + } + + /// e.g. for vtables, fat pointers or single pointers + #[inline] + pub fn new_pointer_list( + list: &[Scalar], + tcx: ty::TyCtxt<'_, '_, 'tcx>, + ) -> Self { + let ps = tcx.data_layout().pointer_size; + let mut alloc = Allocation::undef( + ps * list.len() as u64, + tcx.data_layout().pointer_align, + ); + alloc.undef_mask.set_range_inbounds(Size::ZERO, ps * list.len() as u64, true); + for (i, s) in list.iter().enumerate() { + let (int, ptr) = match s { + Scalar::Bits { bits, size } => { + assert!(*size as u64 == ps.bytes()); + (*bits as u64, None) + } + Scalar::Ptr(ptr) => (ptr.offset.bytes(), Some(ptr)), + }; + let i = i * ps.bytes() as usize; + let j = i + ps.bytes() as usize; + super::write_target_uint( + tcx.data_layout().endian, + &mut alloc.bytes[i..j], + int.into(), + ).unwrap(); + if let Some(ptr) = ptr { + alloc.relocations.insert( + ps * i as u64, + (ptr.tag, ptr.alloc_id), + ); + } + } + Self::from_allocation(tcx, alloc) + } + + #[inline] + pub fn from_allocation( + tcx: ty::TyCtxt<'_, '_, 'tcx>, + alloc: Allocation, + ) -> Self { + let alloc = tcx.intern_const_alloc(alloc); + let alloc_id = tcx.alloc_map.lock().allocate(alloc); + ConstValue::ByRef(alloc_id, alloc, Size::ZERO) } #[inline] pub fn new_slice( val: Scalar, len: u64, - cx: impl HasDataLayout + tcx: ty::TyCtxt<'_, '_, 'tcx>, ) -> Self { - ConstValue::ScalarPair(val, Scalar::Bits { - bits: len as u128, - size: cx.data_layout().pointer_size.bytes() as u8, - }) + Self::new_pointer_list( + &[ + val, + Scalar::Bits { + bits: len as u128, + size: tcx.data_layout.pointer_size.bytes() as u8, + }, + ], + tcx, + ) } #[inline] - pub fn new_dyn_trait(val: Scalar, vtable: Pointer) -> Self { - ConstValue::ScalarPair(val, Scalar::Ptr(vtable)) + pub fn new_dyn_trait( + val: Scalar, + vtable: Pointer, + tcx: ty::TyCtxt<'_, '_, 'tcx>, + ) -> Self { + Self::new_pointer_list(&[val, vtable.into()], tcx) } } diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index 62b5327ae4692..7a06c0f80996f 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -38,7 +38,8 @@ use syntax::symbol::InternedString; use syntax_pos::{Span, DUMMY_SP}; use ty::fold::{TypeFoldable, TypeFolder, TypeVisitor}; use ty::subst::{CanonicalUserSubsts, Subst, Substs}; -use ty::{self, AdtDef, CanonicalTy, ClosureSubsts, GeneratorSubsts, Region, Ty, TyCtxt}; +use ty::layout::{Size, Align}; +use ty::{self, AdtDef, CanonicalTy, ClosureSubsts, GeneratorSubsts, Region, Ty, TyCtxt, ParamEnv}; use util::ppaux; pub use mir::interpret::AssertMessage; @@ -1589,25 +1590,16 @@ impl<'tcx> TerminatorKind<'tcx> { switch_ty, .. } => { - let size = ty::tls::with(|tcx| { - let param_env = ty::ParamEnv::empty(); - let switch_ty = tcx.lift_to_global(&switch_ty).unwrap(); - tcx.layout_of(param_env.and(switch_ty)).unwrap().size - }); values .iter() .map(|&u| { let mut s = String::new(); - let c = ty::Const { - val: ConstValue::Scalar( - Scalar::Bits { - bits: u, - size: size.bytes() as u8, - }.into(), - ), - ty: switch_ty, - }; - fmt_const_val(&mut s, &c).unwrap(); + ty::tls::with(|tcx| { + let param_env = ty::ParamEnv::empty(); + let switch_ty = tcx.lift_to_global(&switch_ty).unwrap(); + let c = ty::Const::from_bits(tcx, u, param_env.and(switch_ty)); + fmt_const_val(&mut s, c).unwrap(); + }); s.into() }).chain(iter::once("otherwise".into())) .collect() @@ -2113,7 +2105,7 @@ impl<'tcx> Operand<'tcx> { span, ty, user_ty: None, - literal: ty::Const::zero_sized(tcx, ty), + literal: ty::Const::zero_sized(tcx, ParamEnv::empty().and(ty)), }) } @@ -2456,15 +2448,30 @@ pub fn fmt_const_val(f: &mut impl Write, const_val: &ty::Const<'_>) -> fmt::Resu use ty::TyKind::*; let value = const_val.val; let ty = const_val.ty; - // print some primitives - if let ConstValue::Scalar(Scalar::Bits { bits, .. }) = value { + // print function definitons + if let FnDef(did, _) = ty.sty { + return write!(f, "{}", item_path_str(did)); + } + if let ConstValue::ByRef(_, alloc, offset) = value { + let bits = || ty::tls::with(|tcx| alloc.read_bits( + tcx, + offset, + ParamEnv::reveal_all().and(tcx.lift_to_global(&ty).unwrap()), + ).ok()); match ty.sty { - Bool if bits == 0 => return write!(f, "false"), - Bool if bits == 1 => return write!(f, "true"), - Float(ast::FloatTy::F32) => return write!(f, "{}f32", Single::from_bits(bits)), - Float(ast::FloatTy::F64) => return write!(f, "{}f64", Double::from_bits(bits)), - Uint(ui) => return write!(f, "{:?}{}", bits, ui), - Int(i) => { + // print some primitives + Bool if bits() == Some(0) => return write!(f, "false"), + Bool if bits() == Some(1) => return write!(f, "true"), + Float(ast::FloatTy::F32) => if let Some(bits) = bits() { + return write!(f, "{}f32", Single::from_bits(bits)) + }, + Float(ast::FloatTy::F64) => if let Some(bits) = bits() { + return write!(f, "{}f64", Double::from_bits(bits)) + }, + Uint(ui) => if let Some(bits) = bits() { + return write!(f, "{:?}{}", bits, ui) + }, + Int(i) => if let Some(bits) = bits() { let bit_width = ty::tls::with(|tcx| { let ty = tcx.lift_to_global(&ty).unwrap(); tcx.layout_of(ty::ParamEnv::empty().and(ty)) @@ -2474,34 +2481,45 @@ pub fn fmt_const_val(f: &mut impl Write, const_val: &ty::Const<'_>) -> fmt::Resu }); let shift = 128 - bit_width; return write!(f, "{:?}{}", ((bits as i128) << shift) >> shift, i); - } - Char => return write!(f, "{:?}", ::std::char::from_u32(bits as u32).unwrap()), - _ => {} - } - } - // print function definitons - if let FnDef(did, _) = ty.sty { - return write!(f, "{}", item_path_str(did)); - } - // print string literals - if let ConstValue::ScalarPair(ptr, len) = value { - if let Scalar::Ptr(ptr) = ptr { - if let Scalar::Bits { bits: len, .. } = len { - if let Ref(_, &ty::TyS { sty: Str, .. }, _) = ty.sty { - return ty::tls::with(|tcx| { - let alloc = tcx.alloc_map.lock().get(ptr.alloc_id); - if let Some(interpret::AllocType::Memory(alloc)) = alloc { - assert_eq!(len as usize as u128, len); - let slice = - &alloc.bytes[(ptr.offset.bytes() as usize)..][..(len as usize)]; - let s = ::std::str::from_utf8(slice).expect("non utf8 str from miri"); - write!(f, "{:?}", s) - } else { - write!(f, "pointer to erroneous constant {:?}, {:?}", ptr, len) - } - }); + }, + Char => if let Some(bits) = bits() { + return write!(f, "{:?}", ::std::char::from_u32(bits as u32).unwrap()) + }, + // print string literals + Ref(_, &ty::TyS { sty: Str, .. }, _) => { + let ptr = ty::tls::with(|tcx| alloc.read_scalar(tcx, offset)); + let ptr = ptr.and_then(Scalar::to_ptr); + if let Ok(ptr) = ptr { + let len = ty::tls::with(|tcx| alloc.read_bits( + tcx, + offset, + ParamEnv::reveal_all().and(tcx.types.usize), + ).ok()); + if let Some(len) = len { + return ty::tls::with(|tcx| { + let alloc = tcx.alloc_map.lock().get(ptr.alloc_id); + if let Some(interpret::AllocType::Memory(alloc)) = alloc { + assert_eq!(len as u64 as u128, len); + if let Ok(slice) = alloc.get_bytes( + tcx, + ptr.offset, + Size::from_bytes(len as u64), + Align::from_bytes(1, 1).unwrap(), + ) { + let s = ::std::str::from_utf8(slice) + .expect("non utf8 str from miri"); + write!(f, "{:?}", s) + } else { + write!(f, "string containing undef or ptrs") + } + } else { + write!(f, "pointer to erroneous constant {:?}, {:?}", ptr, len) + } + }); + } } } + _ => {} } } // just raw dump everything else diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs index dc0039926448c..f959cec964acc 100644 --- a/src/librustc/traits/error_reporting.rs +++ b/src/librustc/traits/error_reporting.rs @@ -428,9 +428,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { Some(format!("[{}]", self.tcx.type_of(def.did).to_string())), )); let tcx = self.tcx; - if let Some(len) = len.val.try_to_scalar().and_then(|scalar| { - scalar.to_usize(tcx).ok() - }) { + if let Some(len) = len.val.try_to_usize(tcx) { flags.push(( "_Self".to_owned(), Some(format!("[{}; {}]", self.tcx.type_of(def.did).to_string(), len)), diff --git a/src/librustc/ty/structural_impls.rs b/src/librustc/ty/structural_impls.rs index 273799bd4bd34..6620a45346688 100644 --- a/src/librustc/ty/structural_impls.rs +++ b/src/librustc/ty/structural_impls.rs @@ -507,10 +507,10 @@ impl<'a, 'tcx, O: Lift<'tcx>> Lift<'tcx> for interpret::EvalErrorKind<'a, O> { InvalidBool => InvalidBool, InvalidDiscriminant(val) => InvalidDiscriminant(val), PointerOutOfBounds { - ptr, + offset, access, allocation_size, - } => PointerOutOfBounds { ptr, access, allocation_size }, + } => PointerOutOfBounds { offset, access, allocation_size }, InvalidNullPointerUsage => InvalidNullPointerUsage, ReadPointerAsBytes => ReadPointerAsBytes, ReadBytesAsPointer => ReadBytesAsPointer, @@ -1153,8 +1153,6 @@ EnumTypeFoldableImpl! { impl<'tcx> TypeFoldable<'tcx> for ConstValue<'tcx> { fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { match *self { - ConstValue::Scalar(v) => ConstValue::Scalar(v), - ConstValue::ScalarPair(a, b) => ConstValue::ScalarPair(a, b), ConstValue::ByRef(id, alloc, offset) => ConstValue::ByRef(id, alloc, offset), ConstValue::Unevaluated(def_id, substs) => { ConstValue::Unevaluated(def_id, substs.fold_with(folder)) @@ -1164,8 +1162,6 @@ impl<'tcx> TypeFoldable<'tcx> for ConstValue<'tcx> { fn super_visit_with>(&self, visitor: &mut V) -> bool { match *self { - ConstValue::Scalar(_) | - ConstValue::ScalarPair(_, _) | ConstValue::ByRef(_, _, _) => false, ConstValue::Unevaluated(_, substs) => substs.visit_with(visitor), } diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index 8289158387015..0c387b5addfc4 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -12,7 +12,7 @@ use hir::def_id::DefId; -use mir::interpret::ConstValue; +use mir::interpret::{ConstValue, Allocation, write_target_uint}; use middle::region; use polonius_engine::Atom; use rustc_data_structures::indexed_vec::Idx; @@ -20,7 +20,6 @@ use ty::subst::{Substs, Subst, Kind, UnpackedKind}; use ty::{self, AdtDef, TypeFlags, Ty, TyCtxt, TypeFoldable}; use ty::{List, TyS, ParamEnvAnd, ParamEnv}; use util::captures::Captures; -use mir::interpret::{Scalar, Pointer}; use std::iter; use std::cmp::Ordering; @@ -1969,15 +1968,6 @@ impl<'tcx> Const<'tcx> { }) } - #[inline] - pub fn from_scalar( - tcx: TyCtxt<'_, '_, 'tcx>, - val: Scalar, - ty: Ty<'tcx>, - ) -> &'tcx Self { - Self::from_const_value(tcx, ConstValue::Scalar(val), ty) - } - #[inline] pub fn from_bits( tcx: TyCtxt<'_, '_, 'tcx>, @@ -1985,18 +1975,33 @@ impl<'tcx> Const<'tcx> { ty: ParamEnvAnd<'tcx, Ty<'tcx>>, ) -> &'tcx Self { let ty = tcx.lift_to_global(&ty).unwrap(); - let size = tcx.layout_of(ty).unwrap_or_else(|e| { + let layout = tcx.layout_of(ty).unwrap_or_else(|e| { panic!("could not compute layout for {:?}: {:?}", ty, e) - }).size; - let shift = 128 - size.bits(); - let truncated = (bits << shift) >> shift; - assert_eq!(truncated, bits, "from_bits called with untruncated value"); - Self::from_scalar(tcx, Scalar::Bits { bits, size: size.bytes() as u8 }, ty.value) + }); + let mut bytes = [0_u8; 16]; + let endian = tcx.data_layout.endian; + write_target_uint(endian, &mut bytes, bits).unwrap(); + Self::from_bytes(tcx, &bytes, layout) + } + + #[inline] + pub fn from_bytes( + tcx: TyCtxt<'_, '_, 'tcx>, + bytes: &[u8], + layout: ty::layout::TyLayout<'tcx>, + ) -> &'tcx Self { + let alloc = Allocation::from_bytes(&bytes, layout.align); + let const_val = ConstValue::from_allocation(tcx, alloc); + Self::from_const_value(tcx, const_val, layout.ty) } #[inline] - pub fn zero_sized(tcx: TyCtxt<'_, '_, 'tcx>, ty: Ty<'tcx>) -> &'tcx Self { - Self::from_scalar(tcx, Scalar::Bits { bits: 0, size: 0 }, ty) + pub fn zero_sized(tcx: TyCtxt<'_, '_, 'tcx>, ty: ParamEnvAnd<'tcx, Ty<'tcx>>,) -> &'tcx Self { + let ty = tcx.lift_to_global(&ty).unwrap(); + let layout = tcx.layout_of(ty).unwrap_or_else(|e| { + panic!("could not compute layout for {:?}: {:?}", ty, e) + }); + Self::from_bytes(tcx, &[], layout) } #[inline] @@ -2019,13 +2024,8 @@ impl<'tcx> Const<'tcx> { return None; } let ty = tcx.lift_to_global(&ty).unwrap(); - let size = tcx.layout_of(ty).ok()?.size; - self.val.try_to_bits(size) - } - - #[inline] - pub fn to_ptr(&self) -> Option { - self.val.try_to_ptr() + let layout = tcx.layout_of(ty).ok()?; + self.val.try_to_bits(tcx, layout) } #[inline] @@ -2036,8 +2036,8 @@ impl<'tcx> Const<'tcx> { ) -> Option { assert_eq!(self.ty, ty.value); let ty = tcx.lift_to_global(&ty).unwrap(); - let size = tcx.layout_of(ty).ok()?.size; - self.val.try_to_bits(size) + let layout = tcx.layout_of(ty).ok()?; + self.val.try_to_bits(tcx, layout) } #[inline] diff --git a/src/librustc_codegen_llvm/mir/constant.rs b/src/librustc_codegen_llvm/mir/constant.rs index 9f0f744389089..47a1f3402b4fb 100644 --- a/src/librustc_codegen_llvm/mir/constant.rs +++ b/src/librustc_codegen_llvm/mir/constant.rs @@ -194,14 +194,18 @@ impl FunctionCx<'a, 'll, 'tcx> { mir::Field::new(field as usize), c, )?; - if let Some(prim) = field.val.try_to_scalar() { + // FIXME(oli-obk): are these indices always usize? + if let Some(prim) = field.val.try_to_usize(bx.tcx()) { let layout = bx.cx.layout_of(field_ty); let scalar = match layout.abi { layout::Abi::Scalar(ref x) => x, _ => bug!("from_const: invalid ByVal layout: {:#?}", layout) }; Ok(scalar_to_llvm( - bx.cx, prim, scalar, + bx.cx, Scalar::Bits { + bits: prim, + size: layout.size.bytes() as u8, + }, scalar, layout.immediate_llvm_type(bx.cx), )) } else { diff --git a/src/librustc_codegen_llvm/mir/operand.rs b/src/librustc_codegen_llvm/mir/operand.rs index ab43531240f3f..f239d9571419d 100644 --- a/src/librustc_codegen_llvm/mir/operand.rs +++ b/src/librustc_codegen_llvm/mir/operand.rs @@ -25,7 +25,6 @@ use glue; use std::fmt; use super::{FunctionCx, LocalRef}; -use super::constant::scalar_to_llvm; use super::place::PlaceRef; /// The representation of a Rust value. The enum variant is in fact @@ -86,50 +85,12 @@ impl OperandRef<'ll, 'tcx> { return Ok(OperandRef::new_zst(bx.cx, layout)); } - let val = match val.val { + match val.val { ConstValue::Unevaluated(..) => bug!(), - ConstValue::Scalar(x) => { - let scalar = match layout.abi { - layout::Abi::Scalar(ref x) => x, - _ => bug!("from_const: invalid ByVal layout: {:#?}", layout) - }; - let llval = scalar_to_llvm( - bx.cx, - x, - scalar, - layout.immediate_llvm_type(bx.cx), - ); - OperandValue::Immediate(llval) - }, - ConstValue::ScalarPair(a, b) => { - let (a_scalar, b_scalar) = match layout.abi { - layout::Abi::ScalarPair(ref a, ref b) => (a, b), - _ => bug!("from_const: invalid ScalarPair layout: {:#?}", layout) - }; - let a_llval = scalar_to_llvm( - bx.cx, - a, - a_scalar, - layout.scalar_pair_element_llvm_type(bx.cx, 0, true), - ); - let b_layout = layout.scalar_pair_element_llvm_type(bx.cx, 1, true); - let b_llval = scalar_to_llvm( - bx.cx, - b, - b_scalar, - b_layout, - ); - OperandValue::Pair(a_llval, b_llval) - }, ConstValue::ByRef(_, alloc, offset) => { - return Ok(PlaceRef::from_const_alloc(bx, layout, alloc, offset).load(bx)); + Ok(PlaceRef::from_const_alloc(bx, layout, alloc, offset).load(bx)) }, - }; - - Ok(OperandRef { - val, - layout - }) + } } /// Asserts that this operand refers to a scalar and returns diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index bc5f688729c36..5454670d1be48 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -1557,8 +1557,8 @@ fn validate_const<'a, 'tcx>( ) { let ecx = ::rustc_mir::const_eval::mk_eval_cx(tcx, gid.instance, param_env).unwrap(); let result = (|| { - let op = ecx.const_to_op(constant)?; - let mut ref_tracking = ::rustc_mir::interpret::RefTracking::new(op); + let mplace = ecx.const_to_mplace(constant)?; + let mut ref_tracking = ::rustc_mir::interpret::RefTracking::new(mplace.into()); while let Some((op, mut path)) = ref_tracking.todo.pop() { ecx.validate_operand( op, diff --git a/src/librustc_mir/const_eval.rs b/src/librustc_mir/const_eval.rs index ebb2b4e68fca6..2dcf5a9175045 100644 --- a/src/librustc_mir/const_eval.rs +++ b/src/librustc_mir/const_eval.rs @@ -29,7 +29,7 @@ use syntax::ast::Mutability; use syntax::source_map::{Span, DUMMY_SP}; use interpret::{self, - PlaceTy, MemPlace, OpTy, Operand, Value, Pointer, Scalar, ConstValue, + PlaceTy, MemPlace, MPlaceTy, OpTy, Operand, Value, Pointer, Scalar, ConstValue, EvalResult, EvalError, EvalErrorKind, GlobalId, EvalContext, StackPopCleanup, Allocation, AllocId, MemoryKind, snapshot, @@ -91,55 +91,29 @@ pub(crate) fn eval_promoted<'a, 'mir, 'tcx>( cid: GlobalId<'tcx>, mir: &'mir mir::Mir<'tcx>, param_env: ty::ParamEnv<'tcx>, -) -> EvalResult<'tcx, OpTy<'tcx>> { +) -> EvalResult<'tcx, MPlaceTy<'tcx>> { let mut ecx = mk_borrowck_eval_cx(tcx, cid.instance, mir, DUMMY_SP).unwrap(); eval_body_using_ecx(&mut ecx, cid, Some(mir), param_env) } -pub fn op_to_const<'tcx>( +pub fn mplace_to_const<'tcx>( ecx: &CompileTimeEvalContext<'_, '_, 'tcx>, - op: OpTy<'tcx>, - may_normalize: bool, + mplace: MPlaceTy<'tcx>, ) -> EvalResult<'tcx, &'tcx ty::Const<'tcx>> { - // We do not normalize just any data. Only scalar layout and fat pointers. - let normalize = may_normalize - && match op.layout.abi { - layout::Abi::Scalar(..) => true, - layout::Abi::ScalarPair(..) => { - // Must be a fat pointer - op.layout.ty.builtin_deref(true).is_some() - }, - _ => false, - }; - let normalized_op = if normalize { - ecx.try_read_value(op)? - } else { - match op.op { - Operand::Indirect(mplace) => Err(mplace), - Operand::Immediate(val) => Ok(val) - } - }; - let val = match normalized_op { - Err(MemPlace { ptr, align, meta }) => { - // extract alloc-offset pair - assert!(meta.is_none()); - let ptr = ptr.to_ptr()?; - let alloc = ecx.memory.get(ptr.alloc_id)?; - assert!(alloc.align.abi() >= align.abi()); - assert!(alloc.bytes.len() as u64 - ptr.offset.bytes() >= op.layout.size.bytes()); - let mut alloc = alloc.clone(); - alloc.align = align; - // FIXME shouldnt it be the case that `intern_static` has already - // interned this? I thought that is the entire point of that `FinishStatic` stuff? - let alloc = ecx.tcx.intern_const_alloc(alloc); - ConstValue::ByRef(ptr.alloc_id, alloc, ptr.offset) - }, - Ok(Value::Scalar(x)) => - ConstValue::Scalar(x.not_undef()?), - Ok(Value::ScalarPair(a, b)) => - ConstValue::ScalarPair(a.not_undef()?, b.not_undef()?), - }; - Ok(ty::Const::from_const_value(ecx.tcx.tcx, val, op.layout.ty)) + let MemPlace { ptr, align, meta } = *mplace; + // extract alloc-offset pair + assert!(meta.is_none()); + let ptr = ptr.to_ptr()?; + let alloc = ecx.memory.get(ptr.alloc_id)?; + assert!(alloc.align.abi() >= align.abi()); + assert!(alloc.bytes.len() as u64 - ptr.offset.bytes() >= mplace.layout.size.bytes()); + let mut alloc = alloc.clone(); + alloc.align = align; + // FIXME shouldnt it be the case that `intern_static` has already + // interned this? I thought that is the entire point of that `FinishStatic` stuff? + let alloc = ecx.tcx.intern_const_alloc(alloc); + let val = ConstValue::ByRef(ptr.alloc_id, alloc, ptr.offset); + Ok(ty::Const::from_const_value(ecx.tcx.tcx, val, mplace.layout.ty)) } fn eval_body_and_ecx<'a, 'mir, 'tcx>( @@ -147,7 +121,7 @@ fn eval_body_and_ecx<'a, 'mir, 'tcx>( cid: GlobalId<'tcx>, mir: Option<&'mir mir::Mir<'tcx>>, param_env: ty::ParamEnv<'tcx>, -) -> (EvalResult<'tcx, OpTy<'tcx>>, CompileTimeEvalContext<'a, 'mir, 'tcx>) { +) -> (EvalResult<'tcx, MPlaceTy<'tcx>>, CompileTimeEvalContext<'a, 'mir, 'tcx>) { // we start out with the best span we have // and try improving it down the road when more information is available let span = tcx.def_span(cid.instance.def_id()); @@ -163,7 +137,7 @@ fn eval_body_using_ecx<'mir, 'tcx>( cid: GlobalId<'tcx>, mir: Option<&'mir mir::Mir<'tcx>>, param_env: ty::ParamEnv<'tcx>, -) -> EvalResult<'tcx, OpTy<'tcx>> { +) -> EvalResult<'tcx, MPlaceTy<'tcx>> { debug!("eval_body_using_ecx: {:?}, {:?}", cid, param_env); let tcx = ecx.tcx.tcx; let mut mir = match mir { @@ -203,7 +177,7 @@ fn eval_body_using_ecx<'mir, 'tcx>( ecx.memory.intern_static(ret.ptr.to_ptr()?.alloc_id, mutability)?; debug!("eval_body_using_ecx done: {:?}", *ret); - Ok(ret.into()) + Ok(ret) } impl<'tcx> Into> for ConstEvalError { @@ -497,17 +471,17 @@ pub fn const_field<'a, 'tcx>( let ecx = mk_eval_cx(tcx, instance, param_env).unwrap(); let result = (|| { // get the operand again - let op = ecx.const_to_op(value)?; + let mplace = ecx.const_to_mplace(value)?; // downcast let down = match variant { - None => op, - Some(variant) => ecx.operand_downcast(op, variant)? + None => mplace, + Some(variant) => ecx.mplace_downcast(mplace, variant)? }; // then project - let field = ecx.operand_field(down, field.index() as u64)?; + let field = ecx.mplace_field(down, field.index() as u64)?; // and finally move back to the const world, always normalizing because // this is not called for statics. - op_to_const(&ecx, field, true) + mplace_to_const(&ecx, field) })(); result.map_err(|err| { let (trace, span) = ecx.generate_stacktrace(None); @@ -527,8 +501,8 @@ pub fn const_variant_index<'a, 'tcx>( ) -> EvalResult<'tcx, usize> { trace!("const_variant_index: {:?}, {:?}", instance, val); let ecx = mk_eval_cx(tcx, instance, param_env).unwrap(); - let op = ecx.const_to_op(val)?; - Ok(ecx.read_discriminant(op)?.1) + let mplace = ecx.const_to_mplace(val)?; + Ok(ecx.read_discriminant(mplace.into())?.1) } pub fn const_to_allocation_provider<'a, 'tcx>( @@ -583,16 +557,8 @@ pub fn const_eval_provider<'a, 'tcx>( }; let (res, ecx) = eval_body_and_ecx(tcx, cid, None, key.param_env); - res.and_then(|op| { - let normalize = tcx.is_static(def_id).is_none() && cid.promoted.is_none(); - if !normalize { - // Sanity check: These must always be a MemPlace - match op.op { - Operand::Indirect(_) => { /* all is good */ }, - Operand::Immediate(_) => bug!("const eval gave us an Immediate"), - } - } - op_to_const(&ecx, op, normalize) + res.and_then(|mplace| { + mplace_to_const(&ecx, mplace) }).map_err(|err| { let (trace, span) = ecx.generate_stacktrace(None); let err = ConstEvalErr { diff --git a/src/librustc_mir/hair/cx/expr.rs b/src/librustc_mir/hair/cx/expr.rs index 1df5f78975139..2074952f91e2d 100644 --- a/src/librustc_mir/hair/cx/expr.rs +++ b/src/librustc_mir/hair/cx/expr.rs @@ -851,7 +851,7 @@ fn method_callee<'a, 'gcx, 'tcx>( ty, span, kind: ExprKind::Literal { - literal: ty::Const::zero_sized(cx.tcx(), ty), + literal: ty::Const::zero_sized(cx.tcx(), cx.param_env.and(ty)), user_ty, }, } @@ -912,7 +912,7 @@ fn convert_path_expr<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, ExprKind::Literal { literal: ty::Const::zero_sized( cx.tcx, - cx.tables().node_id_to_type(expr.hir_id), + cx.param_env.and(cx.tables().node_id_to_type(expr.hir_id)), ), user_ty, } diff --git a/src/librustc_mir/hair/cx/mod.rs b/src/librustc_mir/hair/cx/mod.rs index 5f798135966d4..1a90ed12430c4 100644 --- a/src/librustc_mir/hair/cx/mod.rs +++ b/src/librustc_mir/hair/cx/mod.rs @@ -140,8 +140,8 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> { ) -> &'tcx ty::Const<'tcx> { trace!("const_eval_literal: {:#?}, {:?}, {:?}, {:?}", lit, ty, sp, neg); - let parse_float = |num, fty| -> ConstValue<'tcx> { - parse_float(num, fty, neg).unwrap_or_else(|_| { + let parse_float = |num, fty| -> &ty::Const<'tcx> { + parse_float(self.tcx, num, fty, neg).unwrap_or_else(|_| { // FIXME(#31407) this is only necessary because float parsing is buggy self.tcx.sess.span_fatal(sp, "could not evaluate float literal (see issue #31407)"); }) @@ -154,27 +154,27 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> { let shift = 128 - width.bits(); let result = (n << shift) >> shift; trace!("trunc result: {}", result); - ConstValue::Scalar(Scalar::Bits { - bits: result, - size: width.bytes() as u8, - }) + result }; use rustc::mir::interpret::*; - let lit = match *lit { + let bits = match *lit { LitKind::Str(ref s, _) => { let s = s.as_str(); let id = self.tcx.allocate_bytes(s.as_bytes()); - ConstValue::new_slice(Scalar::Ptr(id.into()), s.len() as u64, self.tcx) + let const_value = ConstValue::new_slice( + Scalar::Ptr(id.into()), + s.len() as u64, + self.tcx, + ); + return ty::Const::from_const_value(self.tcx, const_value, ty); }, LitKind::ByteStr(ref data) => { let id = self.tcx.allocate_bytes(data); - ConstValue::Scalar(Scalar::Ptr(id.into())) + let const_value = ConstValue::new_pointer_list(&[Scalar::Ptr(id.into())], self.tcx); + return ty::Const::from_const_value(self.tcx, const_value, ty); }, - LitKind::Byte(n) => ConstValue::Scalar(Scalar::Bits { - bits: n as u128, - size: 1, - }), + LitKind::Byte(n) => n as u128, LitKind::Int(n, _) if neg => { let n = n as i128; let n = n.overflowing_neg().0; @@ -182,19 +182,19 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> { }, LitKind::Int(n, _) => trunc(n), LitKind::Float(n, fty) => { - parse_float(n, fty) + return parse_float(n, fty); } LitKind::FloatUnsuffixed(n) => { let fty = match ty.sty { ty::Float(fty) => fty, _ => bug!() }; - parse_float(n, fty) + return parse_float(n, fty); } - LitKind::Bool(b) => ConstValue::Scalar(Scalar::from_bool(b)), - LitKind::Char(c) => ConstValue::Scalar(Scalar::from_char(c)), + LitKind::Bool(b) => b as u128, + LitKind::Char(c) => c as u128, }; - ty::Const::from_const_value(self.tcx, lit, ty) + ty::Const::from_bits(self.tcx, bits, ty::ParamEnv::reveal_all().and(ty)) } pub fn pattern_from_hir(&mut self, p: &hir::Pat) -> Pattern<'tcx> { @@ -221,7 +221,7 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> { if item.kind == ty::AssociatedKind::Method && item.ident.name == method_name { let method_ty = self.tcx.type_of(item.def_id); let method_ty = method_ty.subst(self.tcx, substs); - return (method_ty, ty::Const::zero_sized(self.tcx, method_ty)); + return (method_ty, ty::Const::zero_sized(self.tcx, self.param_env.and(method_ty))); } } diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 04a297d0a8317..48fc66d2ee536 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -182,7 +182,6 @@ use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; use rustc::ty::layout::{Integer, IntegerExt}; use rustc::mir::Field; -use rustc::mir::interpret::ConstValue; use rustc::util::common::ErrorReported; use syntax::attr::{SignedInt, UnsignedInt}; @@ -341,7 +340,7 @@ impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> { box PatternKind::Constant { value: const_val } => { - if let Some(ptr) = const_val.to_ptr() { + if let Some(ptr) = const_val.val.try_to_ptr(tcx) { let is_array_ptr = const_val.ty .builtin_deref(true) .and_then(|t| t.ty.builtin_index()) @@ -767,7 +766,7 @@ fn max_slice_length<'p, 'a: 'p, 'tcx: 'a, I>( for row in patterns { match *row.kind { PatternKind::Constant { value } => { - if let Some(ptr) = value.to_ptr() { + if let Some(ptr) = value.val.try_to_ptr(cx.tcx) { let is_array_ptr = value.ty .builtin_deref(true) .and_then(|t| t.ty.builtin_index()) @@ -1332,7 +1331,7 @@ fn constructor_sub_pattern_tys<'a, 'tcx: 'a>(cx: &MatchCheckCtxt<'a, 'tcx>, fn slice_pat_covered_by_constructor<'tcx>( tcx: TyCtxt<'_, 'tcx, '_>, - _span: Span, + span: Span, ctor: &Constructor, prefix: &[Pattern<'tcx>], slice: &Option>, @@ -1340,12 +1339,7 @@ fn slice_pat_covered_by_constructor<'tcx>( ) -> Result { let data: &[u8] = match *ctor { ConstantValue(const_val) => { - let val = match const_val.val { - ConstValue::Unevaluated(..) | - ConstValue::ByRef(..) => bug!("unexpected ConstValue: {:?}", const_val), - ConstValue::Scalar(val) | ConstValue::ScalarPair(val, _) => val, - }; - if let Ok(ptr) = val.to_ptr() { + if let Some(ptr) = const_val.val.try_to_ptr(tcx) { let is_array_ptr = const_val.ty .builtin_deref(true) .and_then(|t| t.ty.builtin_index()) @@ -1353,10 +1347,10 @@ fn slice_pat_covered_by_constructor<'tcx>( assert!(is_array_ptr); tcx.alloc_map.lock().unwrap_memory(ptr.alloc_id).bytes.as_ref() } else { - bug!("unexpected non-ptr ConstantValue") + span_bug!(span, "unexpected non-ptr ConstantValue") } } - _ => bug!() + _ => span_bug!(span, "bad slice ctor: {:#?}", ctor), }; let pat_len = prefix.len() + suffix.len(); @@ -1658,7 +1652,7 @@ fn specialize<'p, 'a: 'p, 'tcx: 'a>( PatternKind::Constant { value } => { match *constructor { Slice(..) => { - if let Some(ptr) = value.to_ptr() { + if let Some(ptr) = value.val.try_to_ptr(cx.tcx) { let is_array_ptr = value.ty .builtin_deref(true) .and_then(|t| t.ty.builtin_index()) diff --git a/src/librustc_mir/hair/pattern/mod.rs b/src/librustc_mir/hair/pattern/mod.rs index cb974366a3029..a45090f315b05 100644 --- a/src/librustc_mir/hair/pattern/mod.rs +++ b/src/librustc_mir/hair/pattern/mod.rs @@ -21,8 +21,9 @@ use const_eval::{const_field, const_variant_index}; use hair::util::UserAnnotatedTyHelpers; use rustc::mir::{fmt_const_val, Field, BorrowKind, Mutability, UserTypeAnnotation}; -use rustc::mir::interpret::{Scalar, GlobalId, ConstValue, sign_extend}; -use rustc::ty::{self, Region, TyCtxt, AdtDef, Ty}; +use rustc::mir::interpret::{GlobalId, sign_extend}; +use rustc::ty::{self, Region, TyCtxt, AdtDef, Ty, ParamEnv}; +use rustc::ty::layout::{Size, Align}; use rustc::ty::subst::{Substs, Kind}; use rustc::hir::{self, PatKind, RangeEnd}; use rustc::hir::def::{Def, CtorKind}; @@ -1148,31 +1149,35 @@ pub fn compare_const_vals<'a, 'tcx>( if let ty::Ref(_, rty, _) = ty.value.sty { if let ty::Str = rty.sty { - match (a.val, b.val) { - ( - ConstValue::ScalarPair( - Scalar::Ptr(ptr_a), - len_a, - ), - ConstValue::ScalarPair( - Scalar::Ptr(ptr_b), - len_b, - ), - ) if ptr_a.offset.bytes() == 0 && ptr_b.offset.bytes() == 0 => { - if let Ok(len_a) = len_a.to_bits(tcx.data_layout.pointer_size) { - if let Ok(len_b) = len_b.to_bits(tcx.data_layout.pointer_size) { - if len_a == len_b { - let map = tcx.alloc_map.lock(); - let alloc_a = map.unwrap_memory(ptr_a.alloc_id); - let alloc_b = map.unwrap_memory(ptr_b.alloc_id); - if alloc_a.bytes.len() as u128 == len_a { - return from_bool(alloc_a == alloc_b); - } - } + if let (Some(ptr_a), Some(ptr_b)) = (a.val.try_to_ptr(tcx), b.val.try_to_ptr(tcx)) { + let len_a = a + .val + .try_offset(tcx.data_layout.pointer_size) + .and_then(|val| val.try_to_usize(tcx)); + let len_b = b + .val + .try_offset(tcx.data_layout.pointer_size) + .and_then(|val| val.try_to_usize(tcx)); + if let (Some(len_a), Some(len_b)) = (len_a, len_b) { + if len_a == len_b { + let map = tcx.alloc_map.lock(); + let alloc_a = map.unwrap_memory(ptr_a.alloc_id).get_bytes( + tcx, + ptr_a.offset, + Size::from_bytes(len_a as u64), + Align::from_bytes(1, 1).unwrap(), + ); + let alloc_b = map.unwrap_memory(ptr_b.alloc_id).get_bytes( + tcx, + ptr_b.offset, + Size::from_bytes(len_b as u64), + Align::from_bytes(1, 1).unwrap(), + ); + if let (Ok(alloc_a), Ok(alloc_b)) = (alloc_a, alloc_b) { + return from_bool(alloc_a == alloc_b) } } } - _ => (), } } } @@ -1193,22 +1198,20 @@ fn lit_to_const<'a, 'tcx>(lit: &'tcx ast::LitKind, neg: bool) -> Result<&'tcx ty::Const<'tcx>, LitToConstError> { use syntax::ast::*; - use rustc::mir::interpret::*; - let lit = match *lit { + let bits = match *lit { LitKind::Str(ref s, _) => { let s = s.as_str(); let id = tcx.allocate_bytes(s.as_bytes()); - ConstValue::new_slice(Scalar::Ptr(id.into()), s.len() as u64, tcx) + let const_value = ConstValue::new_slice(Scalar::Ptr(id.into()), s.len() as u64, tcx); + return Ok(ty::Const::from_const_value(tcx, const_value, ty)); }, LitKind::ByteStr(ref data) => { let id = tcx.allocate_bytes(data); - ConstValue::Scalar(Scalar::Ptr(id.into())) + let const_value = ConstValue::new_pointer_list(&[Scalar::Ptr(id.into())], tcx); + return Ok(ty::Const::from_const_value(tcx, const_value, ty)); }, - LitKind::Byte(n) => ConstValue::Scalar(Scalar::Bits { - bits: n as u128, - size: 1, - }), + LitKind::Byte(n) => n as u128, LitKind::Int(n, _) => { enum Int { Signed(IntTy), @@ -1226,7 +1229,7 @@ fn lit_to_const<'a, 'tcx>(lit: &'tcx ast::LitKind, }; // This converts from LitKind::Int (which is sign extended) to // Scalar::Bytes (which is zero extended) - let n = match ity { + match ity { // FIXME(oli-obk): are these casts correct? Int::Signed(IntTy::I8) if neg => (n as i8).overflowing_neg().0 as u8 as u128, @@ -1244,38 +1247,34 @@ fn lit_to_const<'a, 'tcx>(lit: &'tcx ast::LitKind, Int::Signed(IntTy::I64) | Int::Unsigned(UintTy::U64) => n as u64 as u128, Int::Signed(IntTy::I128)| Int::Unsigned(UintTy::U128) => n, _ => bug!(), - }; - let size = tcx.layout_of(ty::ParamEnv::empty().and(ty)).unwrap().size.bytes() as u8; - ConstValue::Scalar(Scalar::Bits { - bits: n, - size, - }) + } }, LitKind::Float(n, fty) => { - parse_float(n, fty, neg).map_err(|_| LitToConstError::UnparseableFloat)? + return parse_float(tcx, n, fty, neg).map_err(|_| LitToConstError::UnparseableFloat); } LitKind::FloatUnsuffixed(n) => { let fty = match ty.sty { ty::Float(fty) => fty, _ => bug!() }; - parse_float(n, fty, neg).map_err(|_| LitToConstError::UnparseableFloat)? + return parse_float(tcx, n, fty, neg).map_err(|_| LitToConstError::UnparseableFloat); } - LitKind::Bool(b) => ConstValue::Scalar(Scalar::from_bool(b)), - LitKind::Char(c) => ConstValue::Scalar(Scalar::from_char(c)), + LitKind::Bool(b) => b as u128, + LitKind::Char(c) => c as u128, }; - Ok(ty::Const::from_const_value(tcx, lit, ty)) + Ok(ty::Const::from_bits(tcx, bits, ParamEnv::reveal_all().and(ty))) } -pub fn parse_float<'tcx>( +pub fn parse_float( + tcx: TyCtxt<'_, 'tcx, 'tcx>, num: Symbol, fty: ast::FloatTy, neg: bool, -) -> Result, ()> { +) -> Result<&'tcx ty::Const<'tcx>, ()> { let num = num.as_str(); use rustc_apfloat::ieee::{Single, Double}; use rustc_apfloat::Float; - let (bits, size) = match fty { + match fty { ast::FloatTy::F32 => { num.parse::().map_err(|_| ())?; let mut f = num.parse::().unwrap_or_else(|e| { @@ -1284,7 +1283,7 @@ pub fn parse_float<'tcx>( if neg { f = -f; } - (f.to_bits(), 4) + Ok(ty::Const::from_bits(tcx, f.to_bits(), ParamEnv::reveal_all().and(tcx.types.f32))) } ast::FloatTy::F64 => { num.parse::().map_err(|_| ())?; @@ -1294,9 +1293,7 @@ pub fn parse_float<'tcx>( if neg { f = -f; } - (f.to_bits(), 8) + Ok(ty::Const::from_bits(tcx, f.to_bits(), ParamEnv::reveal_all().and(tcx.types.f64))) } - }; - - Ok(ConstValue::Scalar(Scalar::Bits { bits, size })) + } } diff --git a/src/librustc_mir/interpret/memory.rs b/src/librustc_mir/interpret/memory.rs index 7661b70febd08..3d3561150b0ce 100644 --- a/src/librustc_mir/interpret/memory.rs +++ b/src/librustc_mir/interpret/memory.rs @@ -261,7 +261,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { // of some (potentially dead) allocation. if ptr.offset > size { return err!(PointerOutOfBounds { - ptr: ptr.erase_tag(), + offset: ptr.offset, access: true, allocation_size: size, }); @@ -310,7 +310,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { let allocation_size = alloc.bytes.len() as u64; if ptr.offset.bytes() > allocation_size { return err!(PointerOutOfBounds { - ptr: ptr.erase_tag(), + offset: ptr.offset, access, allocation_size: Size::from_bytes(allocation_size), }); diff --git a/src/librustc_mir/interpret/operand.rs b/src/librustc_mir/interpret/operand.rs index 71b2f4b53a60c..ede5a6839b082 100644 --- a/src/librustc_mir/interpret/operand.rs +++ b/src/librustc_mir/interpret/operand.rs @@ -13,12 +13,12 @@ use std::convert::TryInto; -use rustc::{mir, ty}; -use rustc::ty::layout::{self, Size, LayoutOf, TyLayout, HasDataLayout, IntegerExt}; +use rustc::mir; +use rustc::ty::layout::{self, Size, TyLayout, HasDataLayout, IntegerExt}; use rustc::mir::interpret::{ - GlobalId, AllocId, - ConstValue, Pointer, Scalar, + AllocId, + Pointer, Scalar, EvalResult, EvalErrorKind }; use super::{EvalContext, Machine, MemPlace, MPlaceTy, MemoryKind}; @@ -628,12 +628,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> self.eval_place_to_op(place, layout)?, Constant(ref constant) => { - let layout = from_known_layout(layout, || { - let ty = self.monomorphize(mir_op.ty(self.mir(), *self.tcx), self.substs()); - self.layout_of(ty) - })?; - let op = self.const_value_to_op(constant.literal.val)?; - OpTy { op, layout } + self.const_to_mplace(constant.literal)?.into() } }; trace!("{:?}: {:?}", mir_op, *op); @@ -650,49 +645,6 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> .collect() } - // Also used e.g. when miri runs into a constant. - pub(super) fn const_value_to_op( - &self, - val: ConstValue<'tcx>, - ) -> EvalResult<'tcx, Operand> { - trace!("const_value_to_op: {:?}", val); - match val { - ConstValue::Unevaluated(def_id, substs) => { - let instance = self.resolve(def_id, substs)?; - self.global_to_op(GlobalId { - instance, - promoted: None, - }) - } - ConstValue::ByRef(id, alloc, offset) => { - // We rely on mutability being set correctly in that allocation to prevent writes - // where none should happen -- and for `static mut`, we copy on demand anyway. - Ok(Operand::Indirect( - MemPlace::from_ptr(Pointer::new(id, offset), alloc.align) - ).with_default_tag()) - }, - ConstValue::ScalarPair(a, b) => - Ok(Operand::Immediate(Value::ScalarPair(a.into(), b.into())).with_default_tag()), - ConstValue::Scalar(x) => - Ok(Operand::Immediate(Value::Scalar(x.into())).with_default_tag()), - } - } - pub fn const_to_op( - &self, - cnst: &ty::Const<'tcx>, - ) -> EvalResult<'tcx, OpTy<'tcx, M::PointerTag>> { - let op = self.const_value_to_op(cnst.val)?; - Ok(OpTy { op, layout: self.layout_of(cnst.ty)? }) - } - - pub(super) fn global_to_op( - &self, - gid: GlobalId<'tcx> - ) -> EvalResult<'tcx, Operand> { - let cv = self.const_eval(gid)?; - self.const_value_to_op(cv.val) - } - /// Read discriminant, return the runtime value as well as the variant index. pub fn read_discriminant( &self, diff --git a/src/librustc_mir/interpret/place.rs b/src/librustc_mir/interpret/place.rs index a4bb15662d8b2..167d9d8ff7fea 100644 --- a/src/librustc_mir/interpret/place.rs +++ b/src/librustc_mir/interpret/place.rs @@ -20,7 +20,8 @@ use rustc::ty::{self, Ty}; use rustc::ty::layout::{self, Size, Align, LayoutOf, TyLayout, HasDataLayout}; use rustc::mir::interpret::{ - GlobalId, AllocId, Allocation, Scalar, EvalResult, Pointer, PointerArithmetic + GlobalId, AllocId, Allocation, Scalar, EvalResult, Pointer, PointerArithmetic, + ConstValue, }; use super::{ EvalContext, Machine, AllocMap, @@ -525,6 +526,37 @@ where }) } + // Also used e.g. when miri runs into a constant. + pub fn const_to_mplace( + &self, + val: &ty::Const<'tcx>, + ) -> EvalResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { + trace!("const_value_to_mplace: {:?}", val); + let mplace = match val.val { + ConstValue::Unevaluated(def_id, substs) => { + let instance = self.resolve(def_id, substs)?; + self.global_to_mplace(GlobalId { + instance, + promoted: None, + })? + } + ConstValue::ByRef(id, alloc, offset) => { + // We rely on mutability being set correctly in that allocation to prevent writes + // where none should happen -- and for `static mut`, we copy on demand anyway. + MemPlace::from_ptr(Pointer::new(id, offset), alloc.align).with_default_tag() + } + }; + Ok(MPlaceTy { mplace, layout: self.layout_of(val.ty)? }) + } + + fn global_to_mplace( + &self, + gid: GlobalId<'tcx> + ) -> EvalResult<'tcx, MemPlace> { + let cv = self.const_eval(gid)?; + Ok(*self.const_to_mplace(cv)?) + } + /// Evaluate statics and promoteds to an `MPlace`. Used to share some code between /// `eval_place` and `eval_place_to_op`. pub(super) fn eval_place_to_mplace( @@ -535,11 +567,10 @@ where Ok(match *mir_place { Promoted(ref promoted) => { let instance = self.frame().instance; - let op = self.global_to_op(GlobalId { + let mplace = self.global_to_mplace(GlobalId { instance, promoted: Some(promoted.0), })?; - let mplace = op.to_mem_place(); // these are always in memory let ty = self.monomorphize(promoted.1, self.substs()); MPlaceTy { mplace, diff --git a/src/librustc_mir/monomorphize/collector.rs b/src/librustc_mir/monomorphize/collector.rs index 6b60b5340eee7..dda56554931f6 100644 --- a/src/librustc_mir/monomorphize/collector.rs +++ b/src/librustc_mir/monomorphize/collector.rs @@ -202,7 +202,7 @@ use rustc::session::config; use rustc::mir::{self, Location, Promoted}; use rustc::mir::visit::Visitor as MirVisitor; use rustc::mir::mono::MonoItem; -use rustc::mir::interpret::{Scalar, GlobalId, AllocType}; +use rustc::mir::interpret::{GlobalId, AllocType}; use monomorphize::{self, Instance}; use rustc::util::nodemap::{FxHashSet, FxHashMap, DefIdMap}; @@ -1261,19 +1261,10 @@ fn collect_const<'a, 'tcx>( }; match val { ConstValue::Unevaluated(..) => bug!("const eval yielded unevaluated const"), - ConstValue::ScalarPair(Scalar::Ptr(a), Scalar::Ptr(b)) => { - collect_miri(tcx, a.alloc_id, output); - collect_miri(tcx, b.alloc_id, output); - } - ConstValue::ScalarPair(_, Scalar::Ptr(ptr)) | - ConstValue::ScalarPair(Scalar::Ptr(ptr), _) | - ConstValue::Scalar(Scalar::Ptr(ptr)) => - collect_miri(tcx, ptr.alloc_id, output), ConstValue::ByRef(_id, alloc, _offset) => { for &((), id) in alloc.relocations.values() { collect_miri(tcx, id, output); } } - _ => {}, } } diff --git a/src/librustc_mir/shim.rs b/src/librustc_mir/shim.rs index 4b26094b9fc28..d627418833650 100644 --- a/src/librustc_mir/shim.rs +++ b/src/librustc_mir/shim.rs @@ -445,7 +445,7 @@ impl<'a, 'tcx> CloneShimBuilder<'a, 'tcx> { span: self.span, ty: func_ty, user_ty: None, - literal: ty::Const::zero_sized(self.tcx, func_ty), + literal: ty::Const::zero_sized(self.tcx, self.tcx.param_env(self.def_id).and(func_ty)), }); let ref_loc = self.make_place( @@ -688,6 +688,7 @@ fn build_call_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let sig = tcx.fn_sig(def_id); let sig = tcx.erase_late_bound_regions(&sig); let span = tcx.def_span(def_id); + let param_env = tcx.param_env(def_id); debug!("build_call_shim: sig={:?}", sig); @@ -733,7 +734,7 @@ fn build_call_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, span, ty, user_ty: None, - literal: ty::Const::zero_sized(tcx, ty), + literal: ty::Const::zero_sized(tcx, param_env.and(ty)), }), vec![rcvr]) } diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index 626baf207eebc..08d54731e1572 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/src/librustc_mir/transform/const_prop.rs @@ -259,9 +259,9 @@ impl<'a, 'mir, 'tcx> ConstPropagator<'a, 'mir, 'tcx> { source_info: SourceInfo, ) -> Option> { self.ecx.tcx.span = source_info.span; - match self.ecx.const_to_op(c.literal) { - Ok(op) => { - Some((op, c.span)) + match self.ecx.const_to_mplace(c.literal) { + Ok(mplace) => { + Some((mplace.into(), c.span)) }, Err(error) => { let (stacktrace, span) = self.ecx.generate_stacktrace(None); @@ -314,7 +314,7 @@ impl<'a, 'mir, 'tcx> ConstPropagator<'a, 'mir, 'tcx> { eval_promoted(this.tcx, cid, this.mir, this.param_env) })?; trace!("evaluated promoted {:?} to {:?}", promoted, res); - Some((res, source_info.span)) + Some((res.into(), source_info.span)) }, _ => None, } From fe4e9504c9984c6fc51f62d49e4d7277957775f4 Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Sun, 21 Oct 2018 19:36:48 +0200 Subject: [PATCH 03/24] Make the HashStable impl of Allocation safer for future changes of the type --- src/librustc/ich/impls_ty.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs index 5ca83be93d67d..dec09e6ab7023 100644 --- a/src/librustc/ich/impls_ty.rs +++ b/src/librustc/ich/impls_ty.rs @@ -456,13 +456,15 @@ impl<'a> HashStable> for mir::interpret::Allocation { hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher, ) { - self.bytes.hash_stable(hcx, hasher); - for reloc in self.relocations.iter() { + trace!("hashing allocation {:?}", self); + let mir::interpret::Allocation { bytes, relocations, undef_mask, align, mutability } = self; + bytes.hash_stable(hcx, hasher); + for reloc in relocations.iter() { reloc.hash_stable(hcx, hasher); } - self.undef_mask.hash_stable(hcx, hasher); - self.align.hash_stable(hcx, hasher); - self.mutability.hash_stable(hcx, hasher); + undef_mask.hash_stable(hcx, hasher); + align.hash_stable(hcx, hasher); + mutability.hash_stable(hcx, hasher); } } From 04fc561d52ffb3bbaa5708080c984d19d7397130 Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Sun, 21 Oct 2018 19:37:15 +0200 Subject: [PATCH 04/24] The `Hash` and `Eq` impls of `ConstValue` were inherently broken --- src/librustc/mir/interpret/mod.rs | 64 +++++++++++++++++++++++++++---- src/librustc/ty/instance.rs | 4 +- 2 files changed, 58 insertions(+), 10 deletions(-) diff --git a/src/librustc/mir/interpret/mod.rs b/src/librustc/mir/interpret/mod.rs index 8f33f15910a76..ee2364cceccbc 100644 --- a/src/librustc/mir/interpret/mod.rs +++ b/src/librustc/mir/interpret/mod.rs @@ -34,7 +34,8 @@ use middle::region; use std::iter; use std::io; use std::ops::{Deref, DerefMut}; -use std::hash::Hash; +use std::cmp; +use std::hash::{Hash, Hasher}; use syntax::ast::Mutability; use rustc_serialize::{Encoder, Decodable, Encodable}; use rustc_data_structures::sorted_map::SortedMap; @@ -217,9 +218,56 @@ impl<'tcx, Tag> Pointer { } -#[derive(Copy, Clone, Eq, Hash, Ord, PartialEq, PartialOrd, Debug)] +#[derive(Copy, Clone, Debug)] pub struct AllocId(pub u64); +impl Eq for AllocId {} + +impl Hash for AllocId { + fn hash(&self, state: &mut H) { + ty::tls::with(|tcx| { + let alloc_type = tcx.alloc_map.lock().get(*self); + match alloc_type { + Some(alloc_type) => alloc_type.hash(state), + None => self.0.hash(state), + } + }) + } +} + +impl PartialEq for AllocId { + fn eq(&self, other: &Self) -> bool { + ty::tls::with(|tcx| { + let (s, o) = { + let map = tcx.alloc_map.lock(); + (map.get(*self), map.get(*other)) + }; + s == o + }) + } +} + +impl Ord for AllocId { + fn cmp(&self, other: &Self) -> cmp::Ordering { + match self.partial_cmp(other) { + Some(ord) => ord, + None => self.0.cmp(&other.0) + } + } +} + +impl PartialOrd for AllocId { + fn partial_cmp(&self, other: &Self) -> Option { + ty::tls::with(|tcx| { + let (s, o) = { + let map = tcx.alloc_map.lock(); + (map.get(*self)?, map.get(*other)?) + }; + s.partial_cmp(&o) + }) + } +} + impl ::rustc_serialize::UseSpecializedEncodable for AllocId {} impl ::rustc_serialize::UseSpecializedDecodable for AllocId {} @@ -427,7 +475,7 @@ impl fmt::Display for AllocId { } } -#[derive(Debug, Clone, Eq, PartialEq, Hash, RustcDecodable, RustcEncodable)] +#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, RustcDecodable, RustcEncodable)] pub enum AllocType<'tcx, M> { /// The alloc id is used as a function pointer Function(Instance<'tcx>), @@ -440,7 +488,7 @@ pub enum AllocType<'tcx, M> { pub struct AllocMap<'tcx, M> { /// Lets you know what an AllocId refers to - id_to_type: FxHashMap>, + id_to_type: FxHashMap>, /// Used to ensure that functions and statics only get one associated AllocId type_interner: FxHashMap, AllocId>, @@ -479,7 +527,7 @@ impl<'tcx, M: fmt::Debug + Eq + Hash + Clone> AllocMap<'tcx, M> { } let id = self.reserve(); debug!("creating alloc_type {:?} with id {}", alloc_type, id); - self.id_to_type.insert(id, alloc_type.clone()); + self.id_to_type.insert(id.0, alloc_type.clone()); self.type_interner.insert(alloc_type, id); id } @@ -492,7 +540,7 @@ impl<'tcx, M: fmt::Debug + Eq + Hash + Clone> AllocMap<'tcx, M> { } pub fn get(&self, id: AllocId) -> Option> { - self.id_to_type.get(&id).cloned() + self.id_to_type.get(&id.0).cloned() } pub fn unwrap_memory(&self, id: AllocId) -> M { @@ -513,13 +561,13 @@ impl<'tcx, M: fmt::Debug + Eq + Hash + Clone> AllocMap<'tcx, M> { } pub fn set_id_memory(&mut self, id: AllocId, mem: M) { - if let Some(old) = self.id_to_type.insert(id, AllocType::Memory(mem)) { + if let Some(old) = self.id_to_type.insert(id.0, AllocType::Memory(mem)) { bug!("tried to set allocation id {}, but it was already existing as {:#?}", id, old); } } pub fn set_id_same_memory(&mut self, id: AllocId, mem: M) { - self.id_to_type.insert_same(id, AllocType::Memory(mem)); + self.id_to_type.insert_same(id.0, AllocType::Memory(mem)); } } diff --git a/src/librustc/ty/instance.rs b/src/librustc/ty/instance.rs index 3d205215d64c6..df073a73301c3 100644 --- a/src/librustc/ty/instance.rs +++ b/src/librustc/ty/instance.rs @@ -16,13 +16,13 @@ use util::ppaux; use std::fmt; -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)] +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, RustcEncodable, RustcDecodable)] pub struct Instance<'tcx> { pub def: InstanceDef<'tcx>, pub substs: &'tcx Substs<'tcx>, } -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)] +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, RustcEncodable, RustcDecodable)] pub enum InstanceDef<'tcx> { Item(DefId), Intrinsic(DefId), From b11b3cb40e64992e728df73f40ce54ee8fa4566d Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Sun, 21 Oct 2018 20:18:39 +0200 Subject: [PATCH 05/24] Fixup a bunch of things for "simplify const value" --- src/librustc_mir/interpret/operand.rs | 11 ++++++++--- src/librustc_mir/interpret/place.rs | 25 ++++++++++++++++--------- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/src/librustc_mir/interpret/operand.rs b/src/librustc_mir/interpret/operand.rs index ede5a6839b082..89b717e031441 100644 --- a/src/librustc_mir/interpret/operand.rs +++ b/src/librustc_mir/interpret/operand.rs @@ -14,7 +14,7 @@ use std::convert::TryInto; use rustc::mir; -use rustc::ty::layout::{self, Size, TyLayout, HasDataLayout, IntegerExt}; +use rustc::ty::layout::{self, Size, LayoutOf, TyLayout, HasDataLayout, IntegerExt}; use rustc::mir::interpret::{ AllocId, @@ -338,7 +338,7 @@ impl<'tcx, Tag> OpTy<'tcx, Tag> // Use the existing layout if given (but sanity check in debug mode), // or compute the layout. #[inline(always)] -fn from_known_layout<'tcx>( +pub(super) fn from_known_layout<'tcx>( layout: Option>, compute: impl FnOnce() -> EvalResult<'tcx, TyLayout<'tcx>> ) -> EvalResult<'tcx, TyLayout<'tcx>> { @@ -628,7 +628,12 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> self.eval_place_to_op(place, layout)?, Constant(ref constant) => { - self.const_to_mplace(constant.literal)?.into() + let layout = super::operand::from_known_layout(layout, || { + let ty = self.monomorphize(mir_op.ty(self.mir(), *self.tcx), self.substs()); + self.layout_of(ty) + })?; + let op = Operand::Indirect(self.const_value_to_mplace(constant.literal.val)?); + OpTy { op, layout } } }; trace!("{:?}: {:?}", mir_op, *op); diff --git a/src/librustc_mir/interpret/place.rs b/src/librustc_mir/interpret/place.rs index 167d9d8ff7fea..d880e3893a1e8 100644 --- a/src/librustc_mir/interpret/place.rs +++ b/src/librustc_mir/interpret/place.rs @@ -527,26 +527,33 @@ where } // Also used e.g. when miri runs into a constant. - pub fn const_to_mplace( + pub fn const_value_to_mplace( &self, - val: &ty::Const<'tcx>, - ) -> EvalResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { + val: ConstValue<'tcx>, + ) -> EvalResult<'tcx, MemPlace> { trace!("const_value_to_mplace: {:?}", val); - let mplace = match val.val { + match val { ConstValue::Unevaluated(def_id, substs) => { let instance = self.resolve(def_id, substs)?; self.global_to_mplace(GlobalId { instance, promoted: None, - })? + }) } ConstValue::ByRef(id, alloc, offset) => { // We rely on mutability being set correctly in that allocation to prevent writes // where none should happen -- and for `static mut`, we copy on demand anyway. - MemPlace::from_ptr(Pointer::new(id, offset), alloc.align).with_default_tag() + Ok(MemPlace::from_ptr(Pointer::new(id, offset), alloc.align).with_default_tag()) } - }; - Ok(MPlaceTy { mplace, layout: self.layout_of(val.ty)? }) + } + } + + pub fn const_to_mplace( + &self, + cnst: &ty::Const<'tcx>, + ) -> EvalResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { + let mplace = self.const_value_to_mplace(cnst.val)?; + Ok(MPlaceTy { mplace, layout: self.layout_of(cnst.ty)? }) } fn global_to_mplace( @@ -554,7 +561,7 @@ where gid: GlobalId<'tcx> ) -> EvalResult<'tcx, MemPlace> { let cv = self.const_eval(gid)?; - Ok(*self.const_to_mplace(cv)?) + self.const_value_to_mplace(cv.val) } /// Evaluate statics and promoteds to an `MPlace`. Used to share some code between From 3aecb0d42a6e79fdd0e6bf7186cb04ac2ebc4282 Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Sun, 21 Oct 2018 22:57:48 +0200 Subject: [PATCH 06/24] Fix some simd intrinsics --- src/librustc/mir/interpret/mod.rs | 33 ++++++------ src/librustc/mir/interpret/value.rs | 4 +- src/librustc/mir/mod.rs | 4 +- src/librustc_codegen_llvm/mir/constant.rs | 4 +- src/librustc_codegen_llvm/mir/operand.rs | 64 +++++++++++++++++++++-- 5 files changed, 85 insertions(+), 24 deletions(-) diff --git a/src/librustc/mir/interpret/mod.rs b/src/librustc/mir/interpret/mod.rs index ee2364cceccbc..c424b8ec2d711 100644 --- a/src/librustc/mir/interpret/mod.rs +++ b/src/librustc/mir/interpret/mod.rs @@ -684,24 +684,24 @@ impl Allocation { offset: Size, size: Size, ) -> EvalResult<'tcx> { - if self.relocations(hdl, offset, size)?.len() != 0 { + if self.relocations(hdl, offset, size).len() != 0 { err!(ReadPointerAsBytes) } else { Ok(()) } } - pub fn relocations( + fn relocations( &self, hdl: impl HasDataLayout, offset: Size, size: Size, - ) -> EvalResult<'tcx, &[(Size, (Tag, AllocId))]> { + ) -> &[(Size, (Tag, AllocId))] { // We have to go back `pointer_size - 1` bytes, as that one would still overlap with // the beginning of this range. - let start = offset.bytes().saturating_sub(hdl.pointer_size().bytes() - 1); + let start = offset.bytes().saturating_sub(hdl.data_layout().pointer_size.bytes() - 1); let end = offset + size; // this does overflow checking - Ok(self.relocations.range(Size::from_bytes(start)..end)) + self.relocations.range(Size::from_bytes(start)..end) } pub fn get_bytes( @@ -736,22 +736,23 @@ impl Allocation { &self, hdl: impl HasDataLayout, offset: Size, + size: Size, + align: Align, ) -> EvalResult<'tcx, Scalar> { - let size = hdl.data_layout().pointer_size; - let required_align = hdl.data_layout().pointer_align; - self.check_align(offset, required_align)?; + self.check_align(offset, align)?; self.check_bounds(offset, size, true)?; self.check_defined(offset, size)?; let bytes = self.bytes_ignoring_relocations_and_undef(offset, size); - let offset = read_target_uint(hdl.data_layout().endian, &bytes).unwrap(); - let offset = Size::from_bytes(offset as u64); - if let Some(&(tag, alloc_id)) = self.relocations.get(&offset) { - Ok(Pointer::new_with_tag(alloc_id, offset, tag).into()) - } else { - Ok(Scalar::Bits { - bits: offset.bytes() as u128, + let int = read_target_uint(hdl.data_layout().endian, &bytes).unwrap(); + match self.relocations(hdl, offset, size) { + &[(_, (tag, alloc_id))] if size == hdl.data_layout().pointer_size => { + Ok(Pointer::new_with_tag(alloc_id, Size::from_bytes(int as u64), tag).into()) + }, + &[] => Ok(Scalar::Bits { + bits: int, size: size.bytes() as u8, - }) + }), + _ => err!(ReadPointerAsBytes), } } diff --git a/src/librustc/mir/interpret/value.rs b/src/librustc/mir/interpret/value.rs index 387423c9af460..2aa9d60341f5f 100644 --- a/src/librustc/mir/interpret/value.rs +++ b/src/librustc/mir/interpret/value.rs @@ -75,7 +75,9 @@ impl<'tcx> ConstValue<'tcx> { hdl: impl HasDataLayout, ) -> Option { let (_, alloc, offset) = self.try_as_by_ref()?; - alloc.read_scalar(hdl, offset).ok()?.to_ptr().ok() + let size = hdl.data_layout().pointer_size; + let required_align = hdl.data_layout().pointer_align; + alloc.read_scalar(hdl, offset, size, required_align).ok()?.to_ptr().ok() } /// e.g. for vtables, fat pointers or single pointers diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index 7a06c0f80996f..9c0b389a23d30 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -2487,7 +2487,9 @@ pub fn fmt_const_val(f: &mut impl Write, const_val: &ty::Const<'_>) -> fmt::Resu }, // print string literals Ref(_, &ty::TyS { sty: Str, .. }, _) => { - let ptr = ty::tls::with(|tcx| alloc.read_scalar(tcx, offset)); + let ptr = ty::tls::with(|tcx| alloc.read_scalar( + tcx, offset, tcx.data_layout.pointer_size, tcx.data_layout.pointer_align, + )); let ptr = ptr.and_then(Scalar::to_ptr); if let Ok(ptr) = ptr { let len = ty::tls::with(|tcx| alloc.read_bits( diff --git a/src/librustc_codegen_llvm/mir/constant.rs b/src/librustc_codegen_llvm/mir/constant.rs index 47a1f3402b4fb..13ce212ea4d7f 100644 --- a/src/librustc_codegen_llvm/mir/constant.rs +++ b/src/librustc_codegen_llvm/mir/constant.rs @@ -181,6 +181,7 @@ impl FunctionCx<'a, 'll, 'tcx> { constant .and_then(|c| { let field_ty = c.ty.builtin_index().unwrap(); + let layout = bx.cx.layout_of(field_ty); let fields = match c.ty.sty { ty::Array(_, n) => n.unwrap_usize(bx.tcx()), ref other => bug!("invalid simd shuffle type: {}", other), @@ -195,8 +196,7 @@ impl FunctionCx<'a, 'll, 'tcx> { c, )?; // FIXME(oli-obk): are these indices always usize? - if let Some(prim) = field.val.try_to_usize(bx.tcx()) { - let layout = bx.cx.layout_of(field_ty); + if let Some(prim) = field.val.try_to_bits(bx.tcx(), layout) { let scalar = match layout.abi { layout::Abi::Scalar(ref x) => x, _ => bug!("from_const: invalid ByVal layout: {:#?}", layout) diff --git a/src/librustc_codegen_llvm/mir/operand.rs b/src/librustc_codegen_llvm/mir/operand.rs index f239d9571419d..71029172f6f74 100644 --- a/src/librustc_codegen_llvm/mir/operand.rs +++ b/src/librustc_codegen_llvm/mir/operand.rs @@ -13,6 +13,7 @@ use rustc::mir; use rustc::ty; use rustc::ty::layout::{self, Align, LayoutOf, TyLayout}; use rustc_data_structures::sync::Lrc; +use syntax_pos::Span; use base; use common::{CodegenCx, C_undef, C_usize}; @@ -25,6 +26,7 @@ use glue; use std::fmt; use super::{FunctionCx, LocalRef}; +use super::constant::scalar_to_llvm; use super::place::PlaceRef; /// The representation of a Rust value. The enum variant is in fact @@ -77,6 +79,7 @@ impl OperandRef<'ll, 'tcx> { } pub fn from_const(bx: &Builder<'a, 'll, 'tcx>, + span: Span, val: &'tcx ty::Const<'tcx>) -> Result, Lrc>> { let layout = bx.cx.layout_of(val.ty); @@ -85,12 +88,65 @@ impl OperandRef<'ll, 'tcx> { return Ok(OperandRef::new_zst(bx.cx, layout)); } - match val.val { + let econv = |err| ConstEvalErr { + error: err, + stacktrace: Vec::new(), + span, + }; + + let val = match val.val { ConstValue::Unevaluated(..) => bug!(), ConstValue::ByRef(_, alloc, offset) => { - Ok(PlaceRef::from_const_alloc(bx, layout, alloc, offset).load(bx)) + // FIXME: the first two arms are needed for simd_simple_float_intrinsic which reads + // the constants back from llvm values. We can probably do better. + match layout.abi { + layout::Abi::Scalar(ref scalar) => { + let x = alloc.read_scalar( + bx.tcx(), offset, layout.size, layout.align, + ).map_err(econv)?; + let llval = scalar_to_llvm( + bx.cx, + x, + scalar, + layout.immediate_llvm_type(bx.cx), + ); + OperandValue::Immediate(llval) + }, + layout::Abi::ScalarPair(ref a_scalar, ref b_scalar) => { + let a_size = a_scalar.value.size(bx.tcx()); + let a = alloc.read_scalar( + bx.tcx(), offset, a_size, a_scalar.value.align(bx.tcx()), + ).map_err(econv)?; + let b_align = b_scalar.value.align(bx.tcx()); + let b_offset = offset + a_size.abi_align(b_align); + let b_size = b_scalar.value.size(bx.tcx()); + let b = alloc.read_scalar( + bx.tcx(), b_offset, b_size, b_align, + ).map_err(econv)?; + let a_llval = scalar_to_llvm( + bx.cx, + a, + a_scalar, + layout.scalar_pair_element_llvm_type(bx.cx, 0, true), + ); + let b_layout = layout.scalar_pair_element_llvm_type(bx.cx, 1, true); + let b_llval = scalar_to_llvm( + bx.cx, + b, + b_scalar, + b_layout, + ); + OperandValue::Pair(a_llval, b_llval) + }, + _ => return Ok(PlaceRef::from_const_alloc(bx, layout, alloc, offset).load(bx)), + } }, - } + }; + + Ok(OperandRef { + val, + layout + }) } /// Asserts that this operand refers to a scalar and returns @@ -383,7 +439,7 @@ impl FunctionCx<'a, 'll, 'tcx> { mir::Operand::Constant(ref constant) => { let ty = self.monomorphize(&constant.ty); self.eval_mir_constant(bx, constant) - .and_then(|c| OperandRef::from_const(bx, c)) + .and_then(|c| OperandRef::from_const(bx, constant.span, c)) .unwrap_or_else(|err| { err.report_as_error( bx.tcx().at(constant.span), From 8d423765d164f42c3878d732ffb99e6966a5c6dd Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Sun, 21 Oct 2018 22:58:17 +0200 Subject: [PATCH 07/24] Fix an ICE that can occur while errors are already being emitted --- src/librustc/ty/sty.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index 0c387b5addfc4..4d7afb65584b0 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -1975,8 +1975,12 @@ impl<'tcx> Const<'tcx> { ty: ParamEnvAnd<'tcx, Ty<'tcx>>, ) -> &'tcx Self { let ty = tcx.lift_to_global(&ty).unwrap(); - let layout = tcx.layout_of(ty).unwrap_or_else(|e| { - panic!("could not compute layout for {:?}: {:?}", ty, e) + let layout = tcx.layout_of(ty).unwrap_or_else(|_| { + // FIXME: add delay_span_bug call, we can only get here if there are errors + // we produce a weird dummy layout with somewhat sane values + let mut layout = tcx.layout_of(ParamEnv::reveal_all().and(tcx.types.u128)).unwrap(); + layout.ty = ty.value; + layout }); let mut bytes = [0_u8; 16]; let endian = tcx.data_layout.endian; From 847f5b9259a212d9cd7381fcbf6617e86d13b290 Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Sun, 21 Oct 2018 22:59:07 +0200 Subject: [PATCH 08/24] Const prop is only interested in immediate constants right now --- src/librustc_mir/interpret/operand.rs | 4 +-- src/librustc_mir/transform/const_prop.rs | 38 +++++++++++++----------- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/src/librustc_mir/interpret/operand.rs b/src/librustc_mir/interpret/operand.rs index 89b717e031441..fcea962182e73 100644 --- a/src/librustc_mir/interpret/operand.rs +++ b/src/librustc_mir/interpret/operand.rs @@ -359,7 +359,7 @@ pub(super) fn from_known_layout<'tcx>( impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { /// Try reading a value in memory; this is interesting particularily for ScalarPair. /// Return None if the layout does not permit loading this as a value. - pub(super) fn try_read_value_from_mplace( + pub(crate) fn try_read_value_from_mplace( &self, mplace: MPlaceTy<'tcx, M::PointerTag>, ) -> EvalResult<'tcx, Option>> { @@ -403,7 +403,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> /// Note that for a given layout, this operation will either always fail or always /// succeed! Whether it succeeds depends on whether the layout can be represented /// in a `Value`, not on which data is stored there currently. - pub(crate) fn try_read_value( + pub(super)fn try_read_value( &self, src: OpTy<'tcx, M::PointerTag>, ) -> EvalResult<'tcx, Result, MemPlace>> { diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index 08d54731e1572..896f0a6492096 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/src/librustc_mir/transform/const_prop.rs @@ -259,24 +259,28 @@ impl<'a, 'mir, 'tcx> ConstPropagator<'a, 'mir, 'tcx> { source_info: SourceInfo, ) -> Option> { self.ecx.tcx.span = source_info.span; - match self.ecx.const_to_mplace(c.literal) { - Ok(mplace) => { - Some((mplace.into(), c.span)) - }, - Err(error) => { - let (stacktrace, span) = self.ecx.generate_stacktrace(None); - let err = ConstEvalErr { - span, - error, - stacktrace, - }; - err.report_as_error( - self.tcx.at(source_info.span), - "could not evaluate constant", - ); - None + let error = match self.ecx.const_to_mplace(c.literal) { + Ok(mplacety) => match self.ecx.try_read_value_from_mplace(mplacety) { + Err(error) => error, + Ok(Some(val)) => return Some((OpTy { + op: interpret::Operand::Immediate(val), + layout: mplacety.layout, + }, c.span)), + Ok(None) => return Some((mplacety.into(), c.span)), }, - } + Err(error) => error, + }; + let (stacktrace, span) = self.ecx.generate_stacktrace(None); + let err = ConstEvalErr { + span, + error, + stacktrace, + }; + err.report_as_error( + self.tcx.at(source_info.span), + "could not evaluate constant", + ); + None } fn eval_place(&mut self, place: &Place<'tcx>, source_info: SourceInfo) -> Option> { From e52644c0e6b997971dcfd94c69383574f887a5e7 Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Mon, 22 Oct 2018 00:54:44 +0200 Subject: [PATCH 09/24] Update a few tests with their improved diagnostic messages --- src/test/ui/consts/const-err4.rs | 2 +- src/test/ui/consts/const-err4.stderr | 4 ++-- .../const-pointer-values-in-various-types.rs | 4 ++-- .../const-pointer-values-in-various-types.stderr | 12 ++++++++---- .../ui/consts/const-eval/union-const-eval-field.rs | 2 +- .../consts/const-eval/union-const-eval-field.stderr | 9 +++++---- src/test/ui/consts/const-eval/union-ice.rs | 2 +- src/test/ui/consts/const-eval/union-ice.stderr | 8 ++++---- 8 files changed, 24 insertions(+), 19 deletions(-) diff --git a/src/test/ui/consts/const-err4.rs b/src/test/ui/consts/const-err4.rs index 10376d5780908..09ebf1681c5e0 100644 --- a/src/test/ui/consts/const-err4.rs +++ b/src/test/ui/consts/const-err4.rs @@ -16,7 +16,7 @@ union Foo { enum Bar { Boo = [unsafe { Foo { b: () }.a }; 4][3], - //~^ ERROR could not evaluate enum discriminant + //~^ ERROR constant evaluation of enum discriminant resulted in non-integer } fn main() { diff --git a/src/test/ui/consts/const-err4.stderr b/src/test/ui/consts/const-err4.stderr index dc64737b22f5e..e6169c641b9af 100644 --- a/src/test/ui/consts/const-err4.stderr +++ b/src/test/ui/consts/const-err4.stderr @@ -1,8 +1,8 @@ -error[E0080]: could not evaluate enum discriminant +error[E0080]: constant evaluation of enum discriminant resulted in non-integer --> $DIR/const-err4.rs:18:11 | LL | Boo = [unsafe { Foo { b: () }.a }; 4][3], - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ attempted to read undefined bytes + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/consts/const-eval/const-pointer-values-in-various-types.rs b/src/test/ui/consts/const-eval/const-pointer-values-in-various-types.rs index dc84e2a88d64a..dfa808ff97af8 100644 --- a/src/test/ui/consts/const-eval/const-pointer-values-in-various-types.rs +++ b/src/test/ui/consts/const-eval/const-pointer-values-in-various-types.rs @@ -37,7 +37,7 @@ fn main() { //~^ ERROR this constant likely exhibits undefined behavior const I32_REF_U128_UNION: u128 = unsafe { Nonsense { int_32_ref: &3 }.uint_128 }; - //~^ ERROR this constant cannot be used + //~^ ERROR this constant likely exhibits undefined behavior const I32_REF_I8_UNION: i8 = unsafe { Nonsense { int_32_ref: &3 }.int_8 }; //~^ ERROR this constant cannot be used @@ -52,7 +52,7 @@ fn main() { //~^ ERROR this constant likely exhibits undefined behavior const I32_REF_I128_UNION: i128 = unsafe { Nonsense { int_32_ref: &3 }.int_128 }; - //~^ ERROR this constant cannot be used + //~^ ERROR this constant likely exhibits undefined behavior const I32_REF_F32_UNION: f32 = unsafe { Nonsense { int_32_ref: &3 }.float_32 }; //~^ ERROR this constant cannot be used diff --git a/src/test/ui/consts/const-eval/const-pointer-values-in-various-types.stderr b/src/test/ui/consts/const-eval/const-pointer-values-in-various-types.stderr index 7be9345b6b423..4a08a74297e92 100644 --- a/src/test/ui/consts/const-eval/const-pointer-values-in-various-types.stderr +++ b/src/test/ui/consts/const-eval/const-pointer-values-in-various-types.stderr @@ -40,11 +40,13 @@ LL | const I32_REF_U64_UNION: u64 = unsafe { Nonsense { int_32_ref: &3 }.uin | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior -error: this constant cannot be used +error[E0080]: this constant likely exhibits undefined behavior --> $DIR/const-pointer-values-in-various-types.rs:39:5 | LL | const I32_REF_U128_UNION: u128 = unsafe { Nonsense { int_32_ref: &3 }.uint_128 }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ attempted to read undefined bytes + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized bytes, but expected initialized plain bits + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior error: this constant cannot be used --> $DIR/const-pointer-values-in-various-types.rs:42:5 @@ -78,11 +80,13 @@ LL | const I32_REF_I64_UNION: i64 = unsafe { Nonsense { int_32_ref: &3 }.int | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior -error: this constant cannot be used +error[E0080]: this constant likely exhibits undefined behavior --> $DIR/const-pointer-values-in-various-types.rs:54:5 | LL | const I32_REF_I128_UNION: i128 = unsafe { Nonsense { int_32_ref: &3 }.int_128 }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ attempted to read undefined bytes + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized bytes, but expected initialized plain bits + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior error: this constant cannot be used --> $DIR/const-pointer-values-in-various-types.rs:57:5 diff --git a/src/test/ui/consts/const-eval/union-const-eval-field.rs b/src/test/ui/consts/const-eval/union-const-eval-field.rs index 5723f4a4159aa..3515977a92e27 100644 --- a/src/test/ui/consts/const-eval/union-const-eval-field.rs +++ b/src/test/ui/consts/const-eval/union-const-eval-field.rs @@ -34,7 +34,7 @@ const fn read_field2() -> Field2 { } const fn read_field3() -> Field3 { - const FIELD3: Field3 = unsafe { UNION.field3 }; //~ ERROR cannot be used + const FIELD3: Field3 = unsafe { UNION.field3 }; //~ ERROR likely exhibits undefined behavior FIELD3 } diff --git a/src/test/ui/consts/const-eval/union-const-eval-field.stderr b/src/test/ui/consts/const-eval/union-const-eval-field.stderr index 811450c8cba47..990df9e3460eb 100644 --- a/src/test/ui/consts/const-eval/union-const-eval-field.stderr +++ b/src/test/ui/consts/const-eval/union-const-eval-field.stderr @@ -1,10 +1,11 @@ -error: this constant cannot be used +error[E0080]: this constant likely exhibits undefined behavior --> $DIR/union-const-eval-field.rs:37:5 | -LL | const FIELD3: Field3 = unsafe { UNION.field3 }; //~ ERROR cannot be used - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ attempted to read undefined bytes +LL | const FIELD3: Field3 = unsafe { UNION.field3 }; //~ ERROR likely exhibits undefined behavior + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized bytes, but expected initialized plain bits | - = note: #[deny(const_err)] on by default + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior error: aborting due to previous error +For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-eval/union-ice.rs b/src/test/ui/consts/const-eval/union-ice.rs index 5d50004e5549d..ec053ec917ea4 100644 --- a/src/test/ui/consts/const-eval/union-ice.rs +++ b/src/test/ui/consts/const-eval/union-ice.rs @@ -20,7 +20,7 @@ union DummyUnion { const UNION: DummyUnion = DummyUnion { field1: 1065353216 }; -const FIELD3: Field3 = unsafe { UNION.field3 }; //~ ERROR this constant cannot be used +const FIELD3: Field3 = unsafe { UNION.field3 }; //~ ERROR likely exhibits undefined behavior const FIELD_PATH: Struct = Struct { //~ ERROR this constant likely exhibits undefined behavior a: 42, diff --git a/src/test/ui/consts/const-eval/union-ice.stderr b/src/test/ui/consts/const-eval/union-ice.stderr index 4484dd6a14740..ea8942c1bd358 100644 --- a/src/test/ui/consts/const-eval/union-ice.stderr +++ b/src/test/ui/consts/const-eval/union-ice.stderr @@ -1,10 +1,10 @@ -error: this constant cannot be used +error[E0080]: this constant likely exhibits undefined behavior --> $DIR/union-ice.rs:23:1 | -LL | const FIELD3: Field3 = unsafe { UNION.field3 }; //~ ERROR this constant cannot be used - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ attempted to read undefined bytes +LL | const FIELD3: Field3 = unsafe { UNION.field3 }; //~ ERROR likely exhibits undefined behavior + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized bytes, but expected initialized plain bits | - = note: #[deny(const_err)] on by default + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior error[E0080]: this constant likely exhibits undefined behavior --> $DIR/union-ice.rs:25:1 From 8246dd48d1f402d5800c0cf7b5de2b2cb5659f24 Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Mon, 22 Oct 2018 01:03:15 +0200 Subject: [PATCH 10/24] Alignment check facepalm --- src/librustc/mir/interpret/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc/mir/interpret/mod.rs b/src/librustc/mir/interpret/mod.rs index c424b8ec2d711..ff2e8e775998f 100644 --- a/src/librustc/mir/interpret/mod.rs +++ b/src/librustc/mir/interpret/mod.rs @@ -634,7 +634,7 @@ impl Allocation { offset: Size, required_align: Align, ) -> EvalResult<'tcx> { - if self.align.abi() > required_align.abi() { + if self.align.abi() < required_align.abi() { return err!(AlignmentCheckFailed { has: self.align, required: required_align, From 5620d6843f1313130e0fc7c78d7d17bc27ad0dbd Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Mon, 22 Oct 2018 01:18:34 +0200 Subject: [PATCH 11/24] Enums can produce scalar pairs with undefs in one of the elements --- src/librustc_codegen_llvm/mir/operand.rs | 26 ------------------------ 1 file changed, 26 deletions(-) diff --git a/src/librustc_codegen_llvm/mir/operand.rs b/src/librustc_codegen_llvm/mir/operand.rs index 71029172f6f74..9fac44bf02987 100644 --- a/src/librustc_codegen_llvm/mir/operand.rs +++ b/src/librustc_codegen_llvm/mir/operand.rs @@ -112,32 +112,6 @@ impl OperandRef<'ll, 'tcx> { ); OperandValue::Immediate(llval) }, - layout::Abi::ScalarPair(ref a_scalar, ref b_scalar) => { - let a_size = a_scalar.value.size(bx.tcx()); - let a = alloc.read_scalar( - bx.tcx(), offset, a_size, a_scalar.value.align(bx.tcx()), - ).map_err(econv)?; - let b_align = b_scalar.value.align(bx.tcx()); - let b_offset = offset + a_size.abi_align(b_align); - let b_size = b_scalar.value.size(bx.tcx()); - let b = alloc.read_scalar( - bx.tcx(), b_offset, b_size, b_align, - ).map_err(econv)?; - let a_llval = scalar_to_llvm( - bx.cx, - a, - a_scalar, - layout.scalar_pair_element_llvm_type(bx.cx, 0, true), - ); - let b_layout = layout.scalar_pair_element_llvm_type(bx.cx, 1, true); - let b_llval = scalar_to_llvm( - bx.cx, - b, - b_scalar, - b_layout, - ); - OperandValue::Pair(a_llval, b_llval) - }, _ => return Ok(PlaceRef::from_const_alloc(bx, layout, alloc, offset).load(bx)), } }, From fc6472c78cceff37c82dbf5987d8cfbca4dc48c3 Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Mon, 22 Oct 2018 08:34:27 +0200 Subject: [PATCH 12/24] Hide a constructor function that is unused outside this module --- src/librustc/ty/sty.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index 4d7afb65584b0..ade52b3be8563 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -1989,7 +1989,7 @@ impl<'tcx> Const<'tcx> { } #[inline] - pub fn from_bytes( + fn from_bytes( tcx: TyCtxt<'_, '_, 'tcx>, bytes: &[u8], layout: ty::layout::TyLayout<'tcx>, From 336094f54250a4e18f0dc5598a1d10dbfb9a78bc Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Mon, 22 Oct 2018 08:34:50 +0200 Subject: [PATCH 13/24] Use a more appropriate parameter environment --- src/librustc/mir/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index 9c0b389a23d30..c7d2a9f377b1b 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -2105,7 +2105,7 @@ impl<'tcx> Operand<'tcx> { span, ty, user_ty: None, - literal: ty::Const::zero_sized(tcx, ParamEnv::empty().and(ty)), + literal: ty::Const::zero_sized(tcx, tcx.param_env(def_id).and(ty)), }) } From b4ba332acf6a57fc51e4e718efec65477d5c1926 Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Mon, 22 Oct 2018 08:50:24 +0200 Subject: [PATCH 14/24] Don't resolve `impl Trait` in function def constants to the concrete type --- src/librustc/mir/mod.rs | 2 +- src/librustc/ty/sty.rs | 10 ++++------ src/librustc_mir/hair/cx/expr.rs | 6 +++--- src/librustc_mir/hair/cx/mod.rs | 2 +- src/librustc_mir/shim.rs | 5 ++--- 5 files changed, 11 insertions(+), 14 deletions(-) diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index c7d2a9f377b1b..a5364e32943fd 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -2105,7 +2105,7 @@ impl<'tcx> Operand<'tcx> { span, ty, user_ty: None, - literal: ty::Const::zero_sized(tcx, tcx.param_env(def_id).and(ty)), + literal: ty::Const::fn_def(tcx, ty), }) } diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index ade52b3be8563..98c33779ed448 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -2000,12 +2000,10 @@ impl<'tcx> Const<'tcx> { } #[inline] - pub fn zero_sized(tcx: TyCtxt<'_, '_, 'tcx>, ty: ParamEnvAnd<'tcx, Ty<'tcx>>,) -> &'tcx Self { - let ty = tcx.lift_to_global(&ty).unwrap(); - let layout = tcx.layout_of(ty).unwrap_or_else(|e| { - panic!("could not compute layout for {:?}: {:?}", ty, e) - }); - Self::from_bytes(tcx, &[], layout) + pub fn fn_def(tcx: TyCtxt<'_, '_, 'tcx>, ty: Ty<'tcx>) -> &'tcx Self { + let alloc = Allocation::from_bytes(&[], ty::layout::Align::from_bytes(1, 1).unwrap()); + let const_val = ConstValue::from_allocation(tcx, alloc); + Self::from_const_value(tcx, const_val, ty) } #[inline] diff --git a/src/librustc_mir/hair/cx/expr.rs b/src/librustc_mir/hair/cx/expr.rs index 2074952f91e2d..cc733f14ee13f 100644 --- a/src/librustc_mir/hair/cx/expr.rs +++ b/src/librustc_mir/hair/cx/expr.rs @@ -851,7 +851,7 @@ fn method_callee<'a, 'gcx, 'tcx>( ty, span, kind: ExprKind::Literal { - literal: ty::Const::zero_sized(cx.tcx(), cx.param_env.and(ty)), + literal: ty::Const::fn_def(cx.tcx(), ty), user_ty, }, } @@ -910,9 +910,9 @@ fn convert_path_expr<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, Def::SelfCtor(..) => { let user_ty = user_substs_applied_to_def(cx, expr.hir_id, &def); ExprKind::Literal { - literal: ty::Const::zero_sized( + literal: ty::Const::fn_def( cx.tcx, - cx.param_env.and(cx.tables().node_id_to_type(expr.hir_id)), + cx.tables().node_id_to_type(expr.hir_id), ), user_ty, } diff --git a/src/librustc_mir/hair/cx/mod.rs b/src/librustc_mir/hair/cx/mod.rs index 1a90ed12430c4..ccecaab888350 100644 --- a/src/librustc_mir/hair/cx/mod.rs +++ b/src/librustc_mir/hair/cx/mod.rs @@ -221,7 +221,7 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> { if item.kind == ty::AssociatedKind::Method && item.ident.name == method_name { let method_ty = self.tcx.type_of(item.def_id); let method_ty = method_ty.subst(self.tcx, substs); - return (method_ty, ty::Const::zero_sized(self.tcx, self.param_env.and(method_ty))); + return (method_ty, ty::Const::fn_def(self.tcx, method_ty)); } } diff --git a/src/librustc_mir/shim.rs b/src/librustc_mir/shim.rs index d627418833650..52afec819b5ff 100644 --- a/src/librustc_mir/shim.rs +++ b/src/librustc_mir/shim.rs @@ -445,7 +445,7 @@ impl<'a, 'tcx> CloneShimBuilder<'a, 'tcx> { span: self.span, ty: func_ty, user_ty: None, - literal: ty::Const::zero_sized(self.tcx, self.tcx.param_env(self.def_id).and(func_ty)), + literal: ty::Const::fn_def(self.tcx, func_ty), }); let ref_loc = self.make_place( @@ -688,7 +688,6 @@ fn build_call_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let sig = tcx.fn_sig(def_id); let sig = tcx.erase_late_bound_regions(&sig); let span = tcx.def_span(def_id); - let param_env = tcx.param_env(def_id); debug!("build_call_shim: sig={:?}", sig); @@ -734,7 +733,7 @@ fn build_call_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, span, ty, user_ty: None, - literal: ty::Const::zero_sized(tcx, param_env.and(ty)), + literal: ty::Const::fn_def(tcx, ty), }), vec![rcvr]) } From dfc5d260e10eebe0bb2b3b78a67fc822ed6b3456 Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Mon, 22 Oct 2018 09:12:06 +0200 Subject: [PATCH 15/24] Constants carry their type with them --- src/librustc/ty/sty.rs | 5 ++--- src/librustc_mir/build/matches/mod.rs | 3 +-- src/librustc_mir/build/matches/test.rs | 4 +--- src/librustc_mir/hair/pattern/_match.rs | 2 +- 4 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index 98c33779ed448..93c59d89be338 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -2060,10 +2060,9 @@ impl<'tcx> Const<'tcx> { pub fn unwrap_bits( &self, tcx: TyCtxt<'_, '_, '_>, - ty: ParamEnvAnd<'tcx, Ty<'tcx>>, ) -> u128 { - self.assert_bits(tcx, ty).unwrap_or_else(|| - bug!("expected bits of {}, got {:#?}", ty.value, self)) + self.assert_bits(tcx, ParamEnv::empty().and(self.ty)).unwrap_or_else(|| + bug!("expected bits of {}, got {:#?}", self.ty, self)) } #[inline] diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs index a864b39e15791..a389427c12b40 100644 --- a/src/librustc_mir/build/matches/mod.rs +++ b/src/librustc_mir/build/matches/mod.rs @@ -978,15 +978,14 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // available match test.kind { TestKind::SwitchInt { - switch_ty, ref mut options, ref mut indices, + .. } => { for candidate in candidates.iter() { if !self.add_cases_to_switch( &match_pair.place, candidate, - switch_ty, options, indices, ) { diff --git a/src/librustc_mir/build/matches/test.rs b/src/librustc_mir/build/matches/test.rs index c7da9c4fbd792..01520109ae23f 100644 --- a/src/librustc_mir/build/matches/test.rs +++ b/src/librustc_mir/build/matches/test.rs @@ -112,7 +112,6 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { pub fn add_cases_to_switch<'pat>(&mut self, test_place: &Place<'tcx>, candidate: &Candidate<'pat, 'tcx>, - switch_ty: Ty<'tcx>, options: &mut Vec, indices: &mut FxHashMap<&'tcx ty::Const<'tcx>, usize>) -> bool @@ -124,10 +123,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { match *match_pair.pattern.kind { PatternKind::Constant { value } => { - let switch_ty = ty::ParamEnv::empty().and(switch_ty); indices.entry(value) .or_insert_with(|| { - options.push(value.unwrap_bits(self.hir.tcx(), switch_ty)); + options.push(value.unwrap_bits(self.hir.tcx())); options.len() - 1 }); true diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 48fc66d2ee536..db137970c9769 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -1364,7 +1364,7 @@ fn slice_pat_covered_by_constructor<'tcx>( { match pat.kind { box PatternKind::Constant { value } => { - let b = value.unwrap_bits(tcx, ty::ParamEnv::empty().and(pat.ty)); + let b = value.unwrap_bits(tcx); assert_eq!(b as u8 as u128, b); if b as u8 != *ch { return Ok(false); From c50d77da4d47040d0d292f89df573dc2e20a1a8d Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Mon, 22 Oct 2018 09:29:55 +0200 Subject: [PATCH 16/24] Alignment of `const_field` results must be of the field's type We're taking the value of the field, any future accesses to the constant will not know about this, so we need to adjust the allocation's alignment. --- src/librustc_mir/const_eval.rs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/librustc_mir/const_eval.rs b/src/librustc_mir/const_eval.rs index 2dcf5a9175045..e6db9f5382501 100644 --- a/src/librustc_mir/const_eval.rs +++ b/src/librustc_mir/const_eval.rs @@ -100,15 +100,19 @@ pub fn mplace_to_const<'tcx>( ecx: &CompileTimeEvalContext<'_, '_, 'tcx>, mplace: MPlaceTy<'tcx>, ) -> EvalResult<'tcx, &'tcx ty::Const<'tcx>> { - let MemPlace { ptr, align, meta } = *mplace; // extract alloc-offset pair - assert!(meta.is_none()); - let ptr = ptr.to_ptr()?; + assert!(mplace.meta.is_none()); + let ptr = mplace.ptr.to_ptr()?; let alloc = ecx.memory.get(ptr.alloc_id)?; - assert!(alloc.align.abi() >= align.abi()); + assert!(alloc.align.abi() >= mplace.align.abi()); assert!(alloc.bytes.len() as u64 - ptr.offset.bytes() >= mplace.layout.size.bytes()); + // FIXME: only clone the parts that interest us (starting at offset, going to offset + size) let mut alloc = alloc.clone(); - alloc.align = align; + // we take `mplace.layout.align` instead of `mplace.align` + // as this function is essentially copying the value + // out of the larger allocation, so we lose all information about + // potential surrounding types with different alignment. + alloc.align = mplace.layout.align; // FIXME shouldnt it be the case that `intern_static` has already // interned this? I thought that is the entire point of that `FinishStatic` stuff? let alloc = ecx.tcx.intern_const_alloc(alloc); From 1d14a8495a4dd989fe3b3a9812bd0c73f742888e Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Mon, 22 Oct 2018 09:41:58 +0200 Subject: [PATCH 17/24] Add a debugging aid --- src/librustc/mir/interpret/value.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/librustc/mir/interpret/value.rs b/src/librustc/mir/interpret/value.rs index 2aa9d60341f5f..8aab7f9e3d154 100644 --- a/src/librustc/mir/interpret/value.rs +++ b/src/librustc/mir/interpret/value.rs @@ -50,6 +50,7 @@ impl<'tcx> ConstValue<'tcx> { #[inline] pub fn try_get_bytes(&self, hdl: impl HasDataLayout, n: Size, align: Align) -> Option<&[u8]> { let (_, alloc, offset) = self.try_as_by_ref()?; + trace!("{:?}", alloc.get_bytes(hdl, offset, n, align)); alloc.get_bytes(hdl, offset, n, align).ok() } From 9fbce3928ecf743a1301e6020474d7408ca52111 Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Mon, 22 Oct 2018 09:55:25 +0200 Subject: [PATCH 18/24] Change some useless `ParamEnv::empty` to `reveal_all` --- src/librustc/ty/sty.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index 93c59d89be338..fb4e6997318dd 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -2008,12 +2008,12 @@ impl<'tcx> Const<'tcx> { #[inline] pub fn from_bool(tcx: TyCtxt<'_, '_, 'tcx>, v: bool) -> &'tcx Self { - Self::from_bits(tcx, v as u128, ParamEnv::empty().and(tcx.types.bool)) + Self::from_bits(tcx, v as u128, ParamEnv::reveal_all().and(tcx.types.bool)) } #[inline] pub fn from_usize(tcx: TyCtxt<'_, '_, 'tcx>, n: u64) -> &'tcx Self { - Self::from_bits(tcx, n as u128, ParamEnv::empty().and(tcx.types.usize)) + Self::from_bits(tcx, n as u128, ParamEnv::reveal_all().and(tcx.types.usize)) } #[inline] @@ -2044,7 +2044,7 @@ impl<'tcx> Const<'tcx> { #[inline] pub fn assert_bool(&self, tcx: TyCtxt<'_, '_, '_>) -> Option { - self.assert_bits(tcx, ParamEnv::empty().and(tcx.types.bool)).and_then(|v| match v { + self.assert_bits(tcx, ParamEnv::reveal_all().and(tcx.types.bool)).and_then(|v| match v { 0 => Some(false), 1 => Some(true), _ => None, @@ -2053,7 +2053,7 @@ impl<'tcx> Const<'tcx> { #[inline] pub fn assert_usize(&self, tcx: TyCtxt<'_, '_, '_>) -> Option { - self.assert_bits(tcx, ParamEnv::empty().and(tcx.types.usize)).map(|v| v as u64) + self.assert_bits(tcx, ParamEnv::reveal_all().and(tcx.types.usize)).map(|v| v as u64) } #[inline] From 1f2fcee3f41af0a05ecb833e7ce182f1a77d7d61 Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Mon, 22 Oct 2018 09:56:33 +0200 Subject: [PATCH 19/24] Improve debugging output of layout computation failure --- src/librustc/ty/sty.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index fb4e6997318dd..7ad2a94e8f1b1 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -1975,9 +1975,10 @@ impl<'tcx> Const<'tcx> { ty: ParamEnvAnd<'tcx, Ty<'tcx>>, ) -> &'tcx Self { let ty = tcx.lift_to_global(&ty).unwrap(); - let layout = tcx.layout_of(ty).unwrap_or_else(|_| { + let layout = tcx.layout_of(ty).unwrap_or_else(|e| { // FIXME: add delay_span_bug call, we can only get here if there are errors // we produce a weird dummy layout with somewhat sane values + error!("failed to compute layout of {:?}: {:?}", ty, e); let mut layout = tcx.layout_of(ParamEnv::reveal_all().and(tcx.types.u128)).unwrap(); layout.ty = ty.value; layout From e3bbe6d61750bf7105f9355b847373d96ca429f9 Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Mon, 22 Oct 2018 10:09:51 +0200 Subject: [PATCH 20/24] Shrink allocations to fit the type at hand --- src/librustc/ty/sty.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index 7ad2a94e8f1b1..561c33762e654 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -1986,7 +1986,7 @@ impl<'tcx> Const<'tcx> { let mut bytes = [0_u8; 16]; let endian = tcx.data_layout.endian; write_target_uint(endian, &mut bytes, bits).unwrap(); - Self::from_bytes(tcx, &bytes, layout) + Self::from_bytes(tcx, &bytes[0..(layout.size.bytes() as usize)], layout) } #[inline] From 6ff70dac56307b9c6c741ea79b3b7cb8ad71c105 Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Mon, 22 Oct 2018 11:52:21 +0200 Subject: [PATCH 21/24] Resize `Allocation`s when doing field accesses into them Otherwise the `Eq` impl of `ty::Const` is totally screwed up. We should do this properly as noted in the code, but that's another major refactoring and this PR is already big enough --- src/librustc_mir/const_eval.rs | 48 ++++++++++++++++++++++++++-------- 1 file changed, 37 insertions(+), 11 deletions(-) diff --git a/src/librustc_mir/const_eval.rs b/src/librustc_mir/const_eval.rs index e6db9f5382501..d2dd126809896 100644 --- a/src/librustc_mir/const_eval.rs +++ b/src/librustc_mir/const_eval.rs @@ -17,10 +17,10 @@ use std::hash::Hash; use std::collections::hash_map::Entry; use rustc::hir::{self, def_id::DefId}; -use rustc::mir::interpret::ConstEvalErr; +use rustc::mir::interpret::{Relocations, UndefMask, ConstEvalErr}; use rustc::mir; use rustc::ty::{self, Ty, TyCtxt, Instance, query::TyCtxtAt}; -use rustc::ty::layout::{self, Size, LayoutOf, TyLayout}; +use rustc::ty::layout::{Size, LayoutOf, TyLayout}; use rustc::ty::subst::Subst; use rustc_data_structures::indexed_vec::IndexVec; use rustc_data_structures::fx::FxHashMap; @@ -106,17 +106,43 @@ pub fn mplace_to_const<'tcx>( let alloc = ecx.memory.get(ptr.alloc_id)?; assert!(alloc.align.abi() >= mplace.align.abi()); assert!(alloc.bytes.len() as u64 - ptr.offset.bytes() >= mplace.layout.size.bytes()); - // FIXME: only clone the parts that interest us (starting at offset, going to offset + size) - let mut alloc = alloc.clone(); - // we take `mplace.layout.align` instead of `mplace.align` - // as this function is essentially copying the value - // out of the larger allocation, so we lose all information about - // potential surrounding types with different alignment. - alloc.align = mplace.layout.align; + // FIXME: stop cloning and refer to parts of allocations by giving `ConstValue::ByRef` fields + // for alignment overrides and size of the referred to part + let mut new_alloc = Allocation { + bytes: alloc + .bytes[ptr.offset.bytes() as usize..][..mplace.layout.size.bytes() as usize] + .to_owned(), + mutability: Mutability::Immutable, + relocations: Relocations::from_presorted(alloc + .relocations + .iter() + .filter_map(|&(offset, (tag, id))| if offset < ptr.offset { + None + } else { + Some(( + offset - ptr.offset, + (tag, id) + )) + }) + .collect()), + undef_mask: UndefMask::new(mplace.layout.size), + // we take `mplace.layout.align` instead of `mplace.align` + // as this function is essentially copying the value + // out of the larger allocation, so we lose all information about + // potential surrounding types with different alignment. + align: mplace.layout.align, + }; + for i in 0..mplace.layout.size.bytes() { + let i = Size::from_bytes(i); + let j = i + ptr.offset; + new_alloc.undef_mask.set(i, alloc.undef_mask.get(j)); + } + // FIXME shouldnt it be the case that `intern_static` has already // interned this? I thought that is the entire point of that `FinishStatic` stuff? - let alloc = ecx.tcx.intern_const_alloc(alloc); - let val = ConstValue::ByRef(ptr.alloc_id, alloc, ptr.offset); + let new_alloc = ecx.tcx.intern_const_alloc(new_alloc); + let alloc_id = ecx.tcx.alloc_map.lock().allocate(new_alloc); + let val = ConstValue::ByRef(alloc_id, new_alloc, Size::ZERO); Ok(ty::Const::from_const_value(ecx.tcx.tcx, val, mplace.layout.ty)) } From 7b526d88ac4a384ba63fa9a778d95edb5c845396 Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Mon, 22 Oct 2018 12:42:53 +0200 Subject: [PATCH 22/24] Read the slice length from the correct place in the fat pointer --- src/librustc/mir/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index a5364e32943fd..eed1f73827769 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -2494,7 +2494,7 @@ pub fn fmt_const_val(f: &mut impl Write, const_val: &ty::Const<'_>) -> fmt::Resu if let Ok(ptr) = ptr { let len = ty::tls::with(|tcx| alloc.read_bits( tcx, - offset, + offset + tcx.data_layout.pointer_size, ParamEnv::reveal_all().and(tcx.types.usize), ).ok()); if let Some(len) = len { From 9df63e92eea02d43e26808094b41e02299b3b613 Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Mon, 22 Oct 2018 13:07:49 +0200 Subject: [PATCH 23/24] Global Allocations don't have an `extra` value --- src/librustc/ich/impls_ty.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs index dec09e6ab7023..110de4ad8d4c3 100644 --- a/src/librustc/ich/impls_ty.rs +++ b/src/librustc/ich/impls_ty.rs @@ -457,7 +457,10 @@ impl<'a> HashStable> for mir::interpret::Allocation { hasher: &mut StableHasher, ) { trace!("hashing allocation {:?}", self); - let mir::interpret::Allocation { bytes, relocations, undef_mask, align, mutability } = self; + let mir::interpret::Allocation { + bytes, relocations, undef_mask, align, mutability, + extra: (), + } = self; bytes.hash_stable(hcx, hasher); for reloc in relocations.iter() { reloc.hash_stable(hcx, hasher); From a7a92e270dbaccd8a6a037d062eff9d53ed69632 Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Mon, 22 Oct 2018 13:20:44 +0200 Subject: [PATCH 24/24] Rebase fallout --- src/librustc_mir/const_eval.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/librustc_mir/const_eval.rs b/src/librustc_mir/const_eval.rs index d2dd126809896..cc23d8084e6e9 100644 --- a/src/librustc_mir/const_eval.rs +++ b/src/librustc_mir/const_eval.rs @@ -29,7 +29,7 @@ use syntax::ast::Mutability; use syntax::source_map::{Span, DUMMY_SP}; use interpret::{self, - PlaceTy, MemPlace, MPlaceTy, OpTy, Operand, Value, Pointer, Scalar, ConstValue, + PlaceTy, MPlaceTy, OpTy, Pointer, Scalar, ConstValue, EvalResult, EvalError, EvalErrorKind, GlobalId, EvalContext, StackPopCleanup, Allocation, AllocId, MemoryKind, snapshot, @@ -131,6 +131,7 @@ pub fn mplace_to_const<'tcx>( // out of the larger allocation, so we lose all information about // potential surrounding types with different alignment. align: mplace.layout.align, + extra: (), }; for i in 0..mplace.layout.size.bytes() { let i = Size::from_bytes(i);