Skip to content

Reintroduce Undef and properly check constant value sizes #52712

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Aug 3, 2018
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions src/librustc/ich/impls_ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,11 @@ for ::mir::interpret::ConstValue<'gcx> {
}
}

impl_stable_hash_for!(enum mir::interpret::ScalarMaybeUndef {
Scalar(v),
Undef
});

impl_stable_hash_for!(enum mir::interpret::Value {
Scalar(v),
ScalarPair(a, b),
Expand Down Expand Up @@ -466,9 +471,9 @@ for ::mir::interpret::Scalar {

mem::discriminant(self).hash_stable(hcx, hasher);
match *self {
Bits { bits, defined } => {
Bits { bits, size } => {
bits.hash_stable(hcx, hasher);
defined.hash_stable(hcx, hasher);
size.hash_stable(hcx, hasher);
},
Ptr(ptr) => ptr.hash_stable(hcx, hasher),
}
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/mir/interpret/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ pub use self::error::{
FrameInfo, ConstEvalResult,
};

pub use self::value::{Scalar, Value, ConstValue};
pub use self::value::{Scalar, Value, ConstValue, ScalarMaybeUndef};

use std::fmt;
use mir;
Expand Down
186 changes: 100 additions & 86 deletions src/librustc/mir/interpret/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,41 +15,38 @@ pub enum ConstValue<'tcx> {
/// to allow HIR creation to happen for everything before needing to be able to run constant
/// evaluation
Unevaluated(DefId, &'tcx Substs<'tcx>),
/// Used only for types with layout::abi::Scalar ABI and ZSTs which use Scalar::undef()
/// Used only for types with layout::abi::Scalar ABI and ZSTs
Scalar(Scalar),
/// Used only for types with layout::abi::ScalarPair
ScalarPair(Scalar, Scalar),
///
/// The second field may be undef in case of `Option<usize>::None`
ScalarPair(Scalar, ScalarMaybeUndef),
/// Used only for the remaining cases. An allocation + offset into the allocation
ByRef(&'tcx Allocation, Size),
}

impl<'tcx> ConstValue<'tcx> {
#[inline]
pub fn from_byval_value(val: Value) -> Self {
match val {
pub fn from_byval_value(val: Value) -> EvalResult<'static, Self> {
Ok(match val {
Value::ByRef(..) => bug!(),
Value::ScalarPair(a, b) => ConstValue::ScalarPair(a, b),
Value::Scalar(val) => ConstValue::Scalar(val),
}
Value::ScalarPair(a, b) => ConstValue::ScalarPair(a.unwrap_or_err()?, b),
Value::Scalar(val) => ConstValue::Scalar(val.unwrap_or_err()?),
})
}

#[inline]
pub fn to_byval_value(&self) -> Option<Value> {
match *self {
ConstValue::Unevaluated(..) |
ConstValue::ByRef(..) => None,
ConstValue::ScalarPair(a, b) => Some(Value::ScalarPair(a, b)),
ConstValue::Scalar(val) => Some(Value::Scalar(val)),
ConstValue::ScalarPair(a, b) => Some(Value::ScalarPair(a.into(), b)),
ConstValue::Scalar(val) => Some(Value::Scalar(val.into())),
}
}

#[inline]
pub fn from_scalar(val: Scalar) -> Self {
ConstValue::Scalar(val)
}

#[inline]
pub fn to_scalar(&self) -> Option<Scalar> {
pub fn try_to_scalar(&self) -> Option<Scalar> {
match *self {
ConstValue::Unevaluated(..) |
ConstValue::ByRef(..) |
Expand All @@ -60,12 +57,12 @@ impl<'tcx> ConstValue<'tcx> {

#[inline]
pub fn to_bits(&self, size: Size) -> Option<u128> {
self.to_scalar()?.to_bits(size).ok()
self.try_to_scalar()?.to_bits(size).ok()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❤️ ? for Option

}

#[inline]
pub fn to_ptr(&self) -> Option<Pointer> {
self.to_scalar()?.to_ptr().ok()
self.try_to_scalar()?.to_ptr().ok()
}
}

Expand All @@ -81,8 +78,8 @@ impl<'tcx> ConstValue<'tcx> {
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, RustcEncodable, RustcDecodable, Hash)]
pub enum Value {
ByRef(Scalar, Align),
Scalar(Scalar),
ScalarPair(Scalar, Scalar),
Scalar(ScalarMaybeUndef),
ScalarPair(ScalarMaybeUndef, ScalarMaybeUndef),
}

impl<'tcx> ty::TypeFoldable<'tcx> for Value {
Expand All @@ -98,23 +95,23 @@ impl<'tcx> Scalar {
pub fn ptr_null<C: HasDataLayout>(cx: C) -> Self {
Scalar::Bits {
bits: 0,
defined: cx.data_layout().pointer_size.bits() as u8,
size: cx.data_layout().pointer_size.bytes() as u8,
}
}

pub fn to_value_with_len<C: HasDataLayout>(self, len: u64, cx: C) -> Value {
ScalarMaybeUndef::Scalar(self).to_value_with_len(len, cx)
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we have this, but not the vtable version?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the vtable one wasn't used anywhere

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think in terms of API consistency it still makes sense to have them both.


pub fn ptr_signed_offset<C: HasDataLayout>(self, i: i64, cx: C) -> EvalResult<'tcx, Self> {
let layout = cx.data_layout();
match self {
Scalar::Bits { bits, defined } => {
let pointer_size = layout.pointer_size.bits() as u8;
if defined < pointer_size {
err!(ReadUndefBytes)
} else {
Ok(Scalar::Bits {
bits: layout.signed_offset(bits as u64, i)? as u128,
defined: pointer_size,
})
}
Scalar::Bits { bits, size } => {
assert_eq!(size as u64, layout.pointer_size.bytes());
Ok(Scalar::Bits {
bits: layout.signed_offset(bits as u64, i)? as u128,
size,
})
}
Scalar::Ptr(ptr) => ptr.signed_offset(i, layout).map(Scalar::Ptr),
}
Expand All @@ -123,65 +120,43 @@ impl<'tcx> Scalar {
pub fn ptr_offset<C: HasDataLayout>(self, i: Size, cx: C) -> EvalResult<'tcx, Self> {
let layout = cx.data_layout();
match self {
Scalar::Bits { bits, defined } => {
let pointer_size = layout.pointer_size.bits() as u8;
if defined < pointer_size {
err!(ReadUndefBytes)
} else {
Ok(Scalar::Bits {
bits: layout.offset(bits as u64, i.bytes())? as u128,
defined: pointer_size,
})
}
Scalar::Bits { bits, size } => {
assert_eq!(size as u64, layout.pointer_size.bytes());
Ok(Scalar::Bits {
bits: layout.offset(bits as u64, i.bytes())? as u128,
size,
})
}
Scalar::Ptr(ptr) => ptr.offset(i, layout).map(Scalar::Ptr),
}
}

pub fn ptr_wrapping_signed_offset<C: HasDataLayout>(self, i: i64, cx: C) -> EvalResult<'tcx, Self> {
pub fn ptr_wrapping_signed_offset<C: HasDataLayout>(self, i: i64, cx: C) -> Self {
let layout = cx.data_layout();
match self {
Scalar::Bits { bits, defined } => {
let pointer_size = layout.pointer_size.bits() as u8;
if defined < pointer_size {
err!(ReadUndefBytes)
} else {
Ok(Scalar::Bits {
bits: layout.wrapping_signed_offset(bits as u64, i) as u128,
defined: pointer_size,
})
}
Scalar::Bits { bits, size } => {
assert_eq!(size as u64, layout.pointer_size.bytes());
Scalar::Bits {
bits: layout.wrapping_signed_offset(bits as u64, i) as u128,
size,
}
}
Scalar::Ptr(ptr) => Ok(Scalar::Ptr(ptr.wrapping_signed_offset(i, layout))),
Scalar::Ptr(ptr) => Scalar::Ptr(ptr.wrapping_signed_offset(i, layout)),
}
}

pub fn is_null_ptr<C: HasDataLayout>(self, cx: C) -> EvalResult<'tcx, bool> {
pub fn is_null_ptr<C: HasDataLayout>(self, cx: C) -> bool {
match self {
Scalar::Bits {
bits, defined,
} => if defined < cx.data_layout().pointer_size.bits() as u8 {
err!(ReadUndefBytes)
} else {
Ok(bits == 0)
Scalar::Bits { bits, size } => {
assert_eq!(size as u64, cx.data_layout().pointer_size.bytes());
bits == 0
},
Scalar::Ptr(_) => Ok(false),
Scalar::Ptr(_) => false,
}
}

pub fn to_value_with_len<C: HasDataLayout>(self, len: u64, cx: C) -> Value {
Value::ScalarPair(self, Scalar::Bits {
bits: len as u128,
defined: cx.data_layout().pointer_size.bits() as u8,
})
}

pub fn to_value_with_vtable(self, vtable: Pointer) -> Value {
Value::ScalarPair(self, Scalar::Ptr(vtable))
}

pub fn to_value(self) -> Value {
Value::Scalar(self)
Value::Scalar(ScalarMaybeUndef::Scalar(self))
}
}

Expand All @@ -199,8 +174,9 @@ impl From<Pointer> for Scalar {
pub enum Scalar {
/// The raw bytes of a simple value.
Bits {
/// The first `defined` number of bits are valid
defined: u8,
/// The first `size` bytes are the value.
/// Do not try to read less or more bytes that that
size: u8,
bits: u128,
},

Expand All @@ -210,25 +186,63 @@ pub enum Scalar {
Ptr(Pointer),
}

impl<'tcx> Scalar {
pub fn undef() -> Self {
Scalar::Bits { bits: 0, defined: 0 }
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, RustcEncodable, RustcDecodable, Hash)]
pub enum ScalarMaybeUndef {
Scalar(Scalar),
Undef,
}

impl From<Scalar> for ScalarMaybeUndef {
fn from(s: Scalar) -> Self {
ScalarMaybeUndef::Scalar(s)
}
}

impl ScalarMaybeUndef {
pub fn unwrap_or_err(self) -> EvalResult<'static, Scalar> {
match self {
ScalarMaybeUndef::Scalar(scalar) => Ok(scalar),
ScalarMaybeUndef::Undef => err!(ReadUndefBytes),
}
}

pub fn to_value_with_len<C: HasDataLayout>(self, len: u64, cx: C) -> Value {
Value::ScalarPair(self.into(), Scalar::Bits {
bits: len as u128,
size: cx.data_layout().pointer_size.bytes() as u8,
}.into())
}

pub fn to_value_with_vtable(self, vtable: Pointer) -> Value {
Value::ScalarPair(self.into(), Scalar::Ptr(vtable).into())
}

pub fn ptr_offset<C: HasDataLayout>(self, i: Size, cx: C) -> EvalResult<'tcx, Self> {
match self {
ScalarMaybeUndef::Scalar(scalar) => {
scalar.ptr_offset(i, cx).map(ScalarMaybeUndef::Scalar)
},
ScalarMaybeUndef::Undef => Ok(ScalarMaybeUndef::Undef)
}
}
}

impl<'tcx> Scalar {
pub fn from_bool(b: bool) -> Self {
// FIXME: can we make defined `1`?
Scalar::Bits { bits: b as u128, defined: 8 }
Scalar::Bits { bits: b as u128, size: 1 }
}

pub fn from_char(c: char) -> Self {
Scalar::Bits { bits: c as u128, defined: 32 }
Scalar::Bits { bits: c as u128, size: 4 }
}

pub fn to_bits(self, size: Size) -> EvalResult<'tcx, u128> {
pub fn to_bits(self, target_size: Size) -> EvalResult<'tcx, u128> {
match self {
Scalar::Bits { .. } if size.bits() == 0 => bug!("to_bits cannot be used with zsts"),
Scalar::Bits { bits, defined } if size.bits() <= defined as u64 => Ok(bits),
Scalar::Bits { .. } => err!(ReadUndefBytes),
Scalar::Bits { bits, size } => {
assert_eq!(target_size.bytes(), size as u64);
assert_ne!(size, 0, "to_bits cannot be used with zsts");
Ok(bits)
}
Scalar::Ptr(_) => err!(ReadPointerAsBytes),
}
}
Expand Down Expand Up @@ -256,8 +270,8 @@ impl<'tcx> Scalar {

pub fn to_bool(self) -> EvalResult<'tcx, bool> {
match self {
Scalar::Bits { bits: 0, defined: 8 } => Ok(false),
Scalar::Bits { bits: 1, defined: 8 } => Ok(true),
Scalar::Bits { bits: 0, size: 1 } => Ok(false),
Scalar::Bits { bits: 1, size: 1 } => Ok(true),
_ => err!(InvalidBool),
}
}
Expand Down
Loading