Auto merge of #53424 - RalfJung:miri-refactor, r=oli-obk
CTFE engine refactor * Value gets renamed to `Operand`, so that now `interpret::{Place, Operand}` are the "dynamic" versions of `mir::{Place, Operand}`. * `Operand` and `Place` share the data for their "stuff is in memory"-base in a new type, `MemPlace`. This also makes it possible to give some more precise types in other areas. Both `Operand` and `MemPlace` have methods available to project into fields (and other kinds of projections) without causing further allocations. * The type for "a `Scalar` or a `ScalarPair`" is called `Value`, and again used to give some more precise types. * All of these have versions with an attached layout, so that we can more often drag the layout along instead of recomputing it. This lets us get rid of `PlaceExtra::Downcast`. `MPlaceTy` and `PlaceTy` can only be constructed in place.rs, making sure the layout is handled properly. (The same should eventually be done for `ValTy` and `OpTy`.) This is used to check, when copying an operand to a place, that the sizes match (which caught a bunch of bugs). * All the high-level functions to write typed memory take a `Place`, and live in `place.rs`. All the high-level typed functions to read typed memory take an `Operand`, and live in `operands.rs`. * Remove `cur_frame` and handling of signedess from memory (catching a bug in the float casting code). * [Only functional change] Enable sanity check to recurse below dyn traits and slices. r? @oli-obk Cc @eddyb
This commit is contained in:
commit
674ef668f1
39 changed files with 2863 additions and 2480 deletions
|
@ -632,7 +632,7 @@ define_dep_nodes!( <'tcx>
|
|||
// queries). Making them anonymous avoids hashing the result, which
|
||||
// may save a bit of time.
|
||||
[anon] EraseRegionsTy { ty: Ty<'tcx> },
|
||||
[anon] ConstValueToAllocation { val: &'tcx ty::Const<'tcx> },
|
||||
[anon] ConstToAllocation { val: &'tcx ty::Const<'tcx> },
|
||||
|
||||
[input] Freevars(DefId),
|
||||
[input] MaybeUnusedTraitImport(DefId),
|
||||
|
|
|
@ -397,12 +397,6 @@ impl_stable_hash_for!(enum mir::interpret::ScalarMaybeUndef {
|
|||
Undef
|
||||
});
|
||||
|
||||
impl_stable_hash_for!(enum mir::interpret::Value {
|
||||
Scalar(v),
|
||||
ScalarPair(a, b),
|
||||
ByRef(ptr, align)
|
||||
});
|
||||
|
||||
impl_stable_hash_for!(struct mir::interpret::Pointer {
|
||||
alloc_id,
|
||||
offset
|
||||
|
|
|
@ -13,7 +13,7 @@ pub use self::error::{
|
|||
FrameInfo, ConstEvalResult,
|
||||
};
|
||||
|
||||
pub use self::value::{Scalar, Value, ConstValue, ScalarMaybeUndef};
|
||||
pub use self::value::{Scalar, ConstValue, ScalarMaybeUndef};
|
||||
|
||||
use std::fmt;
|
||||
use mir;
|
||||
|
@ -135,7 +135,7 @@ impl<'tcx> Pointer {
|
|||
Pointer { alloc_id, offset }
|
||||
}
|
||||
|
||||
pub(crate) fn wrapping_signed_offset<C: HasDataLayout>(self, i: i64, cx: C) -> Self {
|
||||
pub fn wrapping_signed_offset<C: HasDataLayout>(self, i: i64, cx: C) -> Self {
|
||||
Pointer::new(
|
||||
self.alloc_id,
|
||||
Size::from_bytes(cx.data_layout().wrapping_signed_offset(self.offset.bytes(), i)),
|
||||
|
@ -147,7 +147,7 @@ impl<'tcx> Pointer {
|
|||
(Pointer::new(self.alloc_id, Size::from_bytes(res)), over)
|
||||
}
|
||||
|
||||
pub(crate) fn signed_offset<C: HasDataLayout>(self, i: i64, cx: C) -> EvalResult<'tcx, Self> {
|
||||
pub fn signed_offset<C: HasDataLayout>(self, i: i64, cx: C) -> EvalResult<'tcx, Self> {
|
||||
Ok(Pointer::new(
|
||||
self.alloc_id,
|
||||
Size::from_bytes(cx.data_layout().signed_offset(self.offset.bytes(), i)?),
|
||||
|
@ -567,18 +567,6 @@ pub fn write_target_uint(
|
|||
}
|
||||
}
|
||||
|
||||
pub fn write_target_int(
|
||||
endianness: layout::Endian,
|
||||
mut target: &mut [u8],
|
||||
data: i128,
|
||||
) -> Result<(), io::Error> {
|
||||
let len = target.len();
|
||||
match endianness {
|
||||
layout::Endian::Little => target.write_int128::<LittleEndian>(data, len),
|
||||
layout::Endian::Big => target.write_int128::<BigEndian>(data, len),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_target_uint(endianness: layout::Endian, mut source: &[u8]) -> Result<u128, io::Error> {
|
||||
match endianness {
|
||||
layout::Endian::Little => source.read_uint128::<LittleEndian>(source.len()),
|
||||
|
@ -586,6 +574,26 @@ pub fn read_target_uint(endianness: layout::Endian, mut source: &[u8]) -> Result
|
|||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Methods to faciliate working with signed integers stored in a u128
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
pub fn sign_extend(value: u128, size: Size) -> u128 {
|
||||
let size = size.bits();
|
||||
// sign extend
|
||||
let shift = 128 - size;
|
||||
// shift the unsigned value to the left
|
||||
// and back to the right as signed (essentially fills with FF on the left)
|
||||
(((value << shift) as i128) >> shift) as u128
|
||||
}
|
||||
|
||||
pub fn truncate(value: u128, size: Size) -> u128 {
|
||||
let size = size.bits();
|
||||
let shift = 128 - size;
|
||||
// truncate (shift left to drop out leftover values, shift right to fill with zeroes)
|
||||
(value << shift) >> shift
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Undefined byte tracking
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
#![allow(unknown_lints)]
|
||||
|
||||
use ty::layout::{Align, HasDataLayout, Size};
|
||||
use ty;
|
||||
use ty::layout::{HasDataLayout, Size};
|
||||
use ty::subst::Substs;
|
||||
use hir::def_id::DefId;
|
||||
|
||||
use super::{EvalResult, Pointer, PointerArithmetic, Allocation};
|
||||
|
||||
/// Represents a constant value in Rust. Scalar and ScalarPair are optimizations which
|
||||
/// matches Value's optimizations for easy conversions between these two types
|
||||
/// matches the LocalValue optimizations for easy conversions between Value and ConstValue.
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, RustcEncodable, RustcDecodable, Hash)]
|
||||
pub enum ConstValue<'tcx> {
|
||||
/// Never returned from the `const_eval` query, but the HIR contains these frequently in order
|
||||
|
@ -16,6 +15,8 @@ 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 types with layout::abi::ScalarPair
|
||||
///
|
||||
|
@ -26,25 +27,6 @@ pub enum ConstValue<'tcx> {
|
|||
}
|
||||
|
||||
impl<'tcx> ConstValue<'tcx> {
|
||||
#[inline]
|
||||
pub fn from_byval_value(val: Value) -> EvalResult<'static, Self> {
|
||||
Ok(match val {
|
||||
Value::ByRef(..) => bug!(),
|
||||
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.into(), b)),
|
||||
ConstValue::Scalar(val) => Some(Value::Scalar(val.into())),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn try_to_scalar(&self) -> Option<Scalar> {
|
||||
match *self {
|
||||
|
@ -56,58 +38,44 @@ impl<'tcx> ConstValue<'tcx> {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_bits(&self, size: Size) -> Option<u128> {
|
||||
pub fn try_to_bits(&self, size: Size) -> Option<u128> {
|
||||
self.try_to_scalar()?.to_bits(size).ok()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_ptr(&self) -> Option<Pointer> {
|
||||
pub fn try_to_ptr(&self) -> Option<Pointer> {
|
||||
self.try_to_scalar()?.to_ptr().ok()
|
||||
}
|
||||
|
||||
pub fn new_slice(
|
||||
val: Scalar,
|
||||
len: u64,
|
||||
cx: impl HasDataLayout
|
||||
) -> Self {
|
||||
ConstValue::ScalarPair(val, Scalar::Bits {
|
||||
bits: len as u128,
|
||||
size: cx.data_layout().pointer_size.bytes() as u8,
|
||||
}.into())
|
||||
}
|
||||
|
||||
/// A `Value` represents a single self-contained Rust value.
|
||||
///
|
||||
/// A `Value` can either refer to a block of memory inside an allocation (`ByRef`) or to a primitive
|
||||
/// value held directly, outside of any allocation (`Scalar`). For `ByRef`-values, we remember
|
||||
/// whether the pointer is supposed to be aligned or not (also see Place).
|
||||
///
|
||||
/// For optimization of a few very common cases, there is also a representation for a pair of
|
||||
/// primitive values (`ScalarPair`). It allows Miri to avoid making allocations for checked binary
|
||||
/// operations and fat pointers. This idea was taken from rustc's codegen.
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, RustcEncodable, RustcDecodable, Hash)]
|
||||
pub enum Value {
|
||||
ByRef(Scalar, Align),
|
||||
Scalar(ScalarMaybeUndef),
|
||||
ScalarPair(ScalarMaybeUndef, ScalarMaybeUndef),
|
||||
}
|
||||
|
||||
impl<'tcx> ty::TypeFoldable<'tcx> for Value {
|
||||
fn super_fold_with<'gcx: 'tcx, F: ty::fold::TypeFolder<'gcx, 'tcx>>(&self, _: &mut F) -> Self {
|
||||
*self
|
||||
}
|
||||
fn super_visit_with<V: ty::fold::TypeVisitor<'tcx>>(&self, _: &mut V) -> bool {
|
||||
false
|
||||
pub fn new_dyn_trait(val: Scalar, vtable: Pointer) -> Self {
|
||||
ConstValue::ScalarPair(val, Scalar::Ptr(vtable).into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Scalar {
|
||||
pub fn ptr_null<C: HasDataLayout>(cx: C) -> Self {
|
||||
pub fn ptr_null(cx: impl HasDataLayout) -> Self {
|
||||
Scalar::Bits {
|
||||
bits: 0,
|
||||
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)
|
||||
pub fn zst() -> Self {
|
||||
Scalar::Bits { bits: 0, size: 0 }
|
||||
}
|
||||
|
||||
pub fn to_value_with_vtable(self, vtable: Pointer) -> Value {
|
||||
ScalarMaybeUndef::Scalar(self).to_value_with_vtable(vtable)
|
||||
}
|
||||
|
||||
pub fn ptr_signed_offset<C: HasDataLayout>(self, i: i64, cx: C) -> EvalResult<'tcx, Self> {
|
||||
pub fn ptr_signed_offset(self, i: i64, cx: impl HasDataLayout) -> EvalResult<'tcx, Self> {
|
||||
let layout = cx.data_layout();
|
||||
match self {
|
||||
Scalar::Bits { bits, size } => {
|
||||
|
@ -121,7 +89,7 @@ impl<'tcx> Scalar {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn ptr_offset<C: HasDataLayout>(self, i: Size, cx: C) -> EvalResult<'tcx, Self> {
|
||||
pub fn ptr_offset(self, i: Size, cx: impl HasDataLayout) -> EvalResult<'tcx, Self> {
|
||||
let layout = cx.data_layout();
|
||||
match self {
|
||||
Scalar::Bits { bits, size } => {
|
||||
|
@ -135,7 +103,7 @@ impl<'tcx> Scalar {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn ptr_wrapping_signed_offset<C: HasDataLayout>(self, i: i64, cx: C) -> Self {
|
||||
pub fn ptr_wrapping_signed_offset(self, i: i64, cx: impl HasDataLayout) -> Self {
|
||||
let layout = cx.data_layout();
|
||||
match self {
|
||||
Scalar::Bits { bits, size } => {
|
||||
|
@ -149,7 +117,7 @@ impl<'tcx> Scalar {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn is_null_ptr<C: HasDataLayout>(self, cx: C) -> bool {
|
||||
pub fn is_null_ptr(self, cx: impl HasDataLayout) -> bool {
|
||||
match self {
|
||||
Scalar::Bits { bits, size } => {
|
||||
assert_eq!(size as u64, cx.data_layout().pointer_size.bytes());
|
||||
|
@ -159,79 +127,6 @@ impl<'tcx> Scalar {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn to_value(self) -> Value {
|
||||
Value::Scalar(ScalarMaybeUndef::Scalar(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Pointer> for Scalar {
|
||||
fn from(ptr: Pointer) -> Self {
|
||||
Scalar::Ptr(ptr)
|
||||
}
|
||||
}
|
||||
|
||||
/// A `Scalar` represents an immediate, primitive value existing outside of a
|
||||
/// `memory::Allocation`. It is in many ways like a small chunk of a `Allocation`, up to 8 bytes in
|
||||
/// size. Like a range of bytes in an `Allocation`, a `Scalar` can either represent the raw bytes
|
||||
/// of a simple value or a pointer into another `Allocation`
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, RustcEncodable, RustcDecodable, Hash)]
|
||||
pub enum Scalar {
|
||||
/// The raw bytes of a simple value.
|
||||
Bits {
|
||||
/// The first `size` bytes are the value.
|
||||
/// Do not try to read less or more bytes that that
|
||||
size: u8,
|
||||
bits: u128,
|
||||
},
|
||||
|
||||
/// A pointer into an `Allocation`. An `Allocation` in the `memory` module has a list of
|
||||
/// relocations, but a `Scalar` is only large enough to contain one, so we just represent the
|
||||
/// relocation and its associated offset together as a `Pointer` here.
|
||||
Ptr(Pointer),
|
||||
}
|
||||
|
||||
#[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, 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, 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 {
|
||||
Scalar::Bits { bits: b as u128, size: 1 }
|
||||
}
|
||||
|
@ -253,6 +148,7 @@ impl<'tcx> Scalar {
|
|||
|
||||
pub fn to_ptr(self) -> EvalResult<'tcx, Pointer> {
|
||||
match self {
|
||||
Scalar::Bits { bits: 0, .. } => err!(InvalidNullPointerUsage),
|
||||
Scalar::Bits { .. } => err!(ReadBytesAsPointer),
|
||||
Scalar::Ptr(p) => Ok(p),
|
||||
}
|
||||
|
@ -280,3 +176,64 @@ impl<'tcx> Scalar {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Pointer> for Scalar {
|
||||
#[inline(always)]
|
||||
fn from(ptr: Pointer) -> Self {
|
||||
Scalar::Ptr(ptr)
|
||||
}
|
||||
}
|
||||
|
||||
/// A `Scalar` represents an immediate, primitive value existing outside of a
|
||||
/// `memory::Allocation`. It is in many ways like a small chunk of a `Allocation`, up to 8 bytes in
|
||||
/// size. Like a range of bytes in an `Allocation`, a `Scalar` can either represent the raw bytes
|
||||
/// of a simple value or a pointer into another `Allocation`
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, RustcEncodable, RustcDecodable, Hash)]
|
||||
pub enum Scalar {
|
||||
/// The raw bytes of a simple value.
|
||||
Bits {
|
||||
/// The first `size` bytes are the value.
|
||||
/// Do not try to read less or more bytes that that. The remaining bytes must be 0.
|
||||
size: u8,
|
||||
bits: u128,
|
||||
},
|
||||
|
||||
/// A pointer into an `Allocation`. An `Allocation` in the `memory` module has a list of
|
||||
/// relocations, but a `Scalar` is only large enough to contain one, so we just represent the
|
||||
/// relocation and its associated offset together as a `Pointer` here.
|
||||
Ptr(Pointer),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, RustcEncodable, RustcDecodable, Hash)]
|
||||
pub enum ScalarMaybeUndef {
|
||||
Scalar(Scalar),
|
||||
Undef,
|
||||
}
|
||||
|
||||
impl From<Scalar> for ScalarMaybeUndef {
|
||||
#[inline(always)]
|
||||
fn from(s: Scalar) -> Self {
|
||||
ScalarMaybeUndef::Scalar(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> ScalarMaybeUndef {
|
||||
pub fn not_undef(self) -> EvalResult<'static, Scalar> {
|
||||
match self {
|
||||
ScalarMaybeUndef::Scalar(scalar) => Ok(scalar),
|
||||
ScalarMaybeUndef::Undef => err!(ReadUndefBytes),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_ptr(self) -> EvalResult<'tcx, Pointer> {
|
||||
self.not_undef()?.to_ptr()
|
||||
}
|
||||
|
||||
pub fn to_bits(self, target_size: Size) -> EvalResult<'tcx, u128> {
|
||||
self.not_undef()?.to_bits(target_size)
|
||||
}
|
||||
|
||||
pub fn to_bool(self) -> EvalResult<'tcx, bool> {
|
||||
self.not_undef()?.to_bool()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ use hir::def::CtorKind;
|
|||
use hir::def_id::DefId;
|
||||
use hir::{self, HirId, InlineAsm};
|
||||
use middle::region;
|
||||
use mir::interpret::{EvalErrorKind, Scalar, Value, ScalarMaybeUndef};
|
||||
use mir::interpret::{EvalErrorKind, Scalar, ScalarMaybeUndef, ConstValue};
|
||||
use mir::visit::MirVisitable;
|
||||
use rustc_apfloat::ieee::{Double, Single};
|
||||
use rustc_apfloat::Float;
|
||||
|
@ -1469,14 +1469,14 @@ impl<'tcx> TerminatorKind<'tcx> {
|
|||
.iter()
|
||||
.map(|&u| {
|
||||
let mut s = String::new();
|
||||
print_miri_value(
|
||||
Scalar::Bits {
|
||||
let c = ty::Const {
|
||||
val: ConstValue::Scalar(Scalar::Bits {
|
||||
bits: u,
|
||||
size: size.bytes() as u8,
|
||||
}.to_value(),
|
||||
switch_ty,
|
||||
&mut s,
|
||||
).unwrap();
|
||||
}.into()),
|
||||
ty: switch_ty,
|
||||
};
|
||||
fmt_const_val(&mut s, &c).unwrap();
|
||||
s.into()
|
||||
})
|
||||
.chain(iter::once(String::from("otherwise").into()))
|
||||
|
@ -2220,18 +2220,12 @@ impl<'tcx> Debug for Constant<'tcx> {
|
|||
}
|
||||
|
||||
/// Write a `ConstValue` in a way closer to the original source code than the `Debug` output.
|
||||
pub fn fmt_const_val<W: Write>(fmt: &mut W, const_val: &ty::Const) -> fmt::Result {
|
||||
if let Some(value) = const_val.to_byval_value() {
|
||||
print_miri_value(value, const_val.ty, fmt)
|
||||
} else {
|
||||
write!(fmt, "{:?}:{}", const_val.val, const_val.ty)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print_miri_value<'tcx, W: Write>(value: Value, ty: Ty<'tcx>, f: &mut W) -> fmt::Result {
|
||||
pub fn fmt_const_val(f: &mut impl Write, const_val: &ty::Const) -> fmt::Result {
|
||||
use ty::TypeVariants::*;
|
||||
let value = const_val.val;
|
||||
let ty = const_val.ty;
|
||||
// print some primitives
|
||||
if let Value::Scalar(ScalarMaybeUndef::Scalar(Scalar::Bits { bits, .. })) = value {
|
||||
if let ConstValue::Scalar(Scalar::Bits { bits, .. }) = value {
|
||||
match ty.sty {
|
||||
TyBool if bits == 0 => return write!(f, "false"),
|
||||
TyBool if bits == 1 => return write!(f, "true"),
|
||||
|
@ -2258,8 +2252,8 @@ pub fn print_miri_value<'tcx, W: Write>(value: Value, ty: Ty<'tcx>, f: &mut W) -
|
|||
return write!(f, "{}", item_path_str(did));
|
||||
}
|
||||
// print string literals
|
||||
if let Value::ScalarPair(ptr, len) = value {
|
||||
if let ScalarMaybeUndef::Scalar(Scalar::Ptr(ptr)) = ptr {
|
||||
if let ConstValue::ScalarPair(ptr, len) = value {
|
||||
if let Scalar::Ptr(ptr) = ptr {
|
||||
if let ScalarMaybeUndef::Scalar(Scalar::Bits { bits: len, .. }) = len {
|
||||
if let TyRef(_, &ty::TyS { sty: TyStr, .. }, _) = ty.sty {
|
||||
return ty::tls::with(|tcx| {
|
||||
|
|
|
@ -198,9 +198,9 @@ impl<'tcx> QueryDescription<'tcx> for queries::super_predicates_of<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'tcx> QueryDescription<'tcx> for queries::const_value_to_allocation<'tcx> {
|
||||
impl<'tcx> QueryDescription<'tcx> for queries::const_to_allocation<'tcx> {
|
||||
fn describe(_tcx: TyCtxt, val: &'tcx ty::Const<'tcx>) -> String {
|
||||
format!("converting value `{:?}` to an allocation", val)
|
||||
format!("converting constant `{:?}` to an allocation", val)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -287,8 +287,8 @@ define_queries! { <'tcx>
|
|||
[] fn const_eval: const_eval_dep_node(ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>)
|
||||
-> ConstEvalResult<'tcx>,
|
||||
|
||||
/// Converts a constant value to an constant allocation
|
||||
[] fn const_value_to_allocation: const_value_to_allocation(
|
||||
/// Converts a constant value to a constant allocation
|
||||
[] fn const_to_allocation: const_to_allocation(
|
||||
&'tcx ty::Const<'tcx>
|
||||
) -> &'tcx Allocation,
|
||||
},
|
||||
|
@ -706,10 +706,10 @@ fn erase_regions_ty<'tcx>(ty: Ty<'tcx>) -> DepConstructor<'tcx> {
|
|||
DepConstructor::EraseRegionsTy { ty }
|
||||
}
|
||||
|
||||
fn const_value_to_allocation<'tcx>(
|
||||
fn const_to_allocation<'tcx>(
|
||||
val: &'tcx ty::Const<'tcx>,
|
||||
) -> DepConstructor<'tcx> {
|
||||
DepConstructor::ConstValueToAllocation { val }
|
||||
DepConstructor::ConstToAllocation { val }
|
||||
}
|
||||
|
||||
fn type_param_predicates<'tcx>((item_id, param_id): (DefId, DefId)) -> DepConstructor<'tcx> {
|
||||
|
|
|
@ -1062,7 +1062,7 @@ pub fn force_from_dep_node<'a, 'gcx, 'lcx>(tcx: TyCtxt<'a, 'gcx, 'lcx>,
|
|||
DepKind::FulfillObligation |
|
||||
DepKind::VtableMethods |
|
||||
DepKind::EraseRegionsTy |
|
||||
DepKind::ConstValueToAllocation |
|
||||
DepKind::ConstToAllocation |
|
||||
DepKind::NormalizeProjectionTy |
|
||||
DepKind::NormalizeTyAfterErasingRegions |
|
||||
DepKind::ImpliedOutlivesBounds |
|
||||
|
|
|
@ -20,7 +20,7 @@ use ty::subst::{Substs, Subst, Kind, UnpackedKind};
|
|||
use ty::{self, AdtDef, TypeFlags, Ty, TyCtxt, TypeFoldable};
|
||||
use ty::{Slice, TyS, ParamEnvAnd, ParamEnv};
|
||||
use util::captures::Captures;
|
||||
use mir::interpret::{Scalar, Pointer, Value};
|
||||
use mir::interpret::{Scalar, Pointer};
|
||||
|
||||
use std::iter;
|
||||
use std::cmp::Ordering;
|
||||
|
@ -1973,17 +1973,12 @@ impl<'tcx> Const<'tcx> {
|
|||
}
|
||||
let ty = tcx.lift_to_global(&ty).unwrap();
|
||||
let size = tcx.layout_of(ty).ok()?.size;
|
||||
self.val.to_bits(size)
|
||||
self.val.try_to_bits(size)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_ptr(&self) -> Option<Pointer> {
|
||||
self.val.to_ptr()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_byval_value(&self) -> Option<Value> {
|
||||
self.val.to_byval_value()
|
||||
self.val.try_to_ptr()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -1995,7 +1990,7 @@ impl<'tcx> Const<'tcx> {
|
|||
assert_eq!(self.ty, ty.value);
|
||||
let ty = tcx.lift_to_global(&ty).unwrap();
|
||||
let size = tcx.layout_of(ty).ok()?.size;
|
||||
self.val.to_bits(size)
|
||||
self.val.try_to_bits(size)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
|
@ -1359,6 +1359,7 @@ fn describe_enum_variant(
|
|||
// If this is not a univariant enum, there is also the discriminant field.
|
||||
let (discr_offset, discr_arg) = match discriminant_info {
|
||||
RegularDiscriminant(_) => {
|
||||
// We have the layout of an enum variant, we need the layout of the outer enum
|
||||
let enum_layout = cx.layout_of(layout.ty);
|
||||
(Some(enum_layout.fields.offset(0)),
|
||||
Some(("RUST$ENUM$DISR".to_string(), enum_layout.field(cx, 0).ty)))
|
||||
|
|
|
@ -9,8 +9,8 @@
|
|||
// except according to those terms.
|
||||
|
||||
use llvm;
|
||||
use rustc::mir::interpret::ConstEvalErr;
|
||||
use rustc_mir::interpret::{read_target_uint, const_val_field};
|
||||
use rustc::mir::interpret::{ConstEvalErr, read_target_uint};
|
||||
use rustc_mir::interpret::{const_field};
|
||||
use rustc::hir::def_id::DefId;
|
||||
use rustc::mir;
|
||||
use rustc_data_structures::indexed_vec::Idx;
|
||||
|
@ -186,7 +186,7 @@ impl FunctionCx<'a, 'll, 'tcx> {
|
|||
ref other => bug!("invalid simd shuffle type: {}", other),
|
||||
};
|
||||
let values: Result<Vec<_>, Lrc<_>> = (0..fields).map(|field| {
|
||||
let field = const_val_field(
|
||||
let field = const_field(
|
||||
bx.tcx(),
|
||||
ty::ParamEnv::reveal_all(),
|
||||
self.instance,
|
||||
|
|
|
@ -1615,21 +1615,20 @@ fn validate_const<'a, 'tcx>(
|
|||
) {
|
||||
let mut ecx = ::rustc_mir::interpret::mk_eval_cx(tcx, gid.instance, param_env).unwrap();
|
||||
let result = (|| {
|
||||
let val = ecx.const_to_value(constant.val)?;
|
||||
use rustc_target::abi::LayoutOf;
|
||||
use rustc_mir::interpret::OpTy;
|
||||
|
||||
let op = ecx.const_value_to_op(constant.val)?;
|
||||
let layout = ecx.layout_of(constant.ty)?;
|
||||
let place = ecx.allocate_place_for_value(val, layout, None)?;
|
||||
let ptr = place.to_ptr()?;
|
||||
let mut todo = vec![(ptr, layout.ty, String::new())];
|
||||
let place = ecx.allocate_op(OpTy { op, layout })?.into();
|
||||
|
||||
let mut todo = vec![(place, Vec::new())];
|
||||
let mut seen = FxHashSet();
|
||||
seen.insert((ptr, layout.ty));
|
||||
while let Some((ptr, ty, path)) = todo.pop() {
|
||||
let layout = ecx.layout_of(ty)?;
|
||||
ecx.validate_ptr_target(
|
||||
ptr,
|
||||
layout.align,
|
||||
layout,
|
||||
path,
|
||||
seen.insert(place);
|
||||
while let Some((place, mut path)) = todo.pop() {
|
||||
ecx.validate_mplace(
|
||||
place,
|
||||
&mut path,
|
||||
&mut seen,
|
||||
&mut todo,
|
||||
)?;
|
||||
|
|
|
@ -167,8 +167,7 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> {
|
|||
LitKind::Str(ref s, _) => {
|
||||
let s = s.as_str();
|
||||
let id = self.tcx.allocate_bytes(s.as_bytes());
|
||||
let value = Scalar::Ptr(id.into()).to_value_with_len(s.len() as u64, self.tcx);
|
||||
ConstValue::from_byval_value(value).unwrap()
|
||||
ConstValue::new_slice(Scalar::Ptr(id.into()), s.len() as u64, self.tcx)
|
||||
},
|
||||
LitKind::ByteStr(ref data) => {
|
||||
let id = self.tcx.allocate_bytes(data);
|
||||
|
|
|
@ -16,10 +16,10 @@ mod check_match;
|
|||
pub use self::check_match::check_crate;
|
||||
pub(crate) use self::check_match::check_match;
|
||||
|
||||
use interpret::{const_val_field, const_variant_index, self};
|
||||
use interpret::{const_field, const_variant_index};
|
||||
|
||||
use rustc::mir::{fmt_const_val, Field, BorrowKind, Mutability};
|
||||
use rustc::mir::interpret::{Scalar, GlobalId, ConstValue};
|
||||
use rustc::mir::interpret::{Scalar, GlobalId, ConstValue, sign_extend};
|
||||
use rustc::ty::{self, TyCtxt, AdtDef, Ty, Region};
|
||||
use rustc::ty::subst::{Substs, Kind};
|
||||
use rustc::hir::{self, PatKind, RangeEnd};
|
||||
|
@ -795,7 +795,7 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
|
|||
debug!("const_to_pat: cv={:#?}", cv);
|
||||
let adt_subpattern = |i, variant_opt| {
|
||||
let field = Field::new(i);
|
||||
let val = const_val_field(
|
||||
let val = const_field(
|
||||
self.tcx, self.param_env, instance,
|
||||
variant_opt, field, cv,
|
||||
).expect("field access failed");
|
||||
|
@ -1085,8 +1085,9 @@ pub fn compare_const_vals<'a, 'tcx>(
|
|||
},
|
||||
ty::TyInt(_) => {
|
||||
let layout = tcx.layout_of(ty).ok()?;
|
||||
let a = interpret::sign_extend(a, layout);
|
||||
let b = interpret::sign_extend(b, layout);
|
||||
assert!(layout.abi.is_signed());
|
||||
let a = sign_extend(a, layout.size);
|
||||
let b = sign_extend(b, layout.size);
|
||||
Some((a as i128).cmp(&(b as i128)))
|
||||
},
|
||||
_ => Some(a.cmp(&b)),
|
||||
|
@ -1106,8 +1107,8 @@ pub fn compare_const_vals<'a, 'tcx>(
|
|||
len_b,
|
||||
),
|
||||
) if ptr_a.offset.bytes() == 0 && ptr_b.offset.bytes() == 0 => {
|
||||
let len_a = len_a.unwrap_or_err().ok();
|
||||
let len_b = len_b.unwrap_or_err().ok();
|
||||
let len_a = len_a.not_undef().ok();
|
||||
let len_b = len_b.not_undef().ok();
|
||||
if len_a.is_none() || len_b.is_none() {
|
||||
tcx.sess.struct_err("str slice len is undef").delay_as_bug();
|
||||
}
|
||||
|
@ -1153,8 +1154,7 @@ fn lit_to_const<'a, 'tcx>(lit: &'tcx ast::LitKind,
|
|||
LitKind::Str(ref s, _) => {
|
||||
let s = s.as_str();
|
||||
let id = tcx.allocate_bytes(s.as_bytes());
|
||||
let value = Scalar::Ptr(id.into()).to_value_with_len(s.len() as u64, tcx);
|
||||
ConstValue::from_byval_value(value).unwrap()
|
||||
ConstValue::new_slice(Scalar::Ptr(id.into()), s.len() as u64, tcx)
|
||||
},
|
||||
LitKind::ByteStr(ref data) => {
|
||||
let id = tcx.allocate_bytes(data);
|
||||
|
|
|
@ -1,87 +1,85 @@
|
|||
use rustc::ty::{self, Ty};
|
||||
use rustc::ty::layout::{self, LayoutOf, TyLayout};
|
||||
use rustc::ty::{self, Ty, TypeAndMut};
|
||||
use rustc::ty::layout::{self, TyLayout, Size};
|
||||
use syntax::ast::{FloatTy, IntTy, UintTy};
|
||||
|
||||
use rustc_apfloat::ieee::{Single, Double};
|
||||
use super::{EvalContext, Machine};
|
||||
use rustc::mir::interpret::{Scalar, EvalResult, Pointer, PointerArithmetic, Value, EvalErrorKind};
|
||||
use rustc::mir::interpret::{
|
||||
Scalar, EvalResult, Pointer, PointerArithmetic, EvalErrorKind,
|
||||
truncate, sign_extend
|
||||
};
|
||||
use rustc::mir::CastKind;
|
||||
use rustc_apfloat::Float;
|
||||
use interpret::eval_context::ValTy;
|
||||
use interpret::Place;
|
||||
|
||||
use super::{EvalContext, Machine, PlaceTy, OpTy, Value};
|
||||
|
||||
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
||||
fn type_is_fat_ptr(&self, ty: Ty<'tcx>) -> bool {
|
||||
match ty.sty {
|
||||
ty::TyRawPtr(ty::TypeAndMut { ty, .. }) |
|
||||
ty::TyRef(_, ty, _) => !self.type_is_sized(ty),
|
||||
ty::TyAdt(def, _) if def.is_box() => !self.type_is_sized(ty.boxed_ty()),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
crate fn cast(
|
||||
&mut self,
|
||||
src: ValTy<'tcx>,
|
||||
src: OpTy<'tcx>,
|
||||
kind: CastKind,
|
||||
dest_ty: Ty<'tcx>,
|
||||
dest: Place,
|
||||
dest: PlaceTy<'tcx>,
|
||||
) -> EvalResult<'tcx> {
|
||||
let src_layout = self.layout_of(src.ty)?;
|
||||
let dst_layout = self.layout_of(dest_ty)?;
|
||||
let src_layout = src.layout;
|
||||
let dst_layout = dest.layout;
|
||||
use rustc::mir::CastKind::*;
|
||||
match kind {
|
||||
Unsize => {
|
||||
self.unsize_into(src.value, src_layout, dest, dst_layout)?;
|
||||
self.unsize_into(src, dest)?;
|
||||
}
|
||||
|
||||
Misc => {
|
||||
if self.type_is_fat_ptr(src.ty) {
|
||||
match (src.value, self.type_is_fat_ptr(dest_ty)) {
|
||||
(Value::ByRef { .. }, _) |
|
||||
let src = self.read_value(src)?;
|
||||
if self.type_is_fat_ptr(src_layout.ty) {
|
||||
match (src.value, self.type_is_fat_ptr(dest.layout.ty)) {
|
||||
// pointers to extern types
|
||||
(Value::Scalar(_),_) |
|
||||
// slices and trait objects to other slices/trait objects
|
||||
(Value::ScalarPair(..), true) => {
|
||||
let valty = ValTy {
|
||||
value: src.value,
|
||||
ty: dest_ty,
|
||||
};
|
||||
self.write_value(valty, dest)?;
|
||||
// No change to value
|
||||
self.write_value(src.value, dest)?;
|
||||
}
|
||||
// slices and trait objects to thin pointers (dropping the metadata)
|
||||
(Value::ScalarPair(data, _), false) => {
|
||||
let valty = ValTy {
|
||||
value: Value::Scalar(data),
|
||||
ty: dest_ty,
|
||||
};
|
||||
self.write_value(valty, dest)?;
|
||||
self.write_scalar(data, dest)?;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let src_layout = self.layout_of(src.ty)?;
|
||||
match src_layout.variants {
|
||||
layout::Variants::Single { index } => {
|
||||
if let Some(def) = src.ty.ty_adt_def() {
|
||||
if let Some(def) = src_layout.ty.ty_adt_def() {
|
||||
let discr_val = def
|
||||
.discriminant_for_variant(*self.tcx, index)
|
||||
.val;
|
||||
return self.write_scalar(
|
||||
dest,
|
||||
Scalar::Bits {
|
||||
bits: discr_val,
|
||||
size: dst_layout.size.bytes() as u8,
|
||||
},
|
||||
dest_ty);
|
||||
dest);
|
||||
}
|
||||
}
|
||||
layout::Variants::Tagged { .. } |
|
||||
layout::Variants::NicheFilling { .. } => {},
|
||||
}
|
||||
|
||||
let src_val = self.value_to_scalar(src)?;
|
||||
let dest_val = self.cast_scalar(src_val, src_layout, dst_layout)?;
|
||||
let valty = ValTy {
|
||||
value: Value::Scalar(dest_val.into()),
|
||||
ty: dest_ty,
|
||||
};
|
||||
self.write_value(valty, dest)?;
|
||||
let src = src.to_scalar()?;
|
||||
let dest_val = self.cast_scalar(src, src_layout, dest.layout)?;
|
||||
self.write_scalar(dest_val, dest)?;
|
||||
}
|
||||
}
|
||||
|
||||
ReifyFnPointer => {
|
||||
match src.ty.sty {
|
||||
// The src operand does not matter, just its type
|
||||
match src_layout.ty.sty {
|
||||
ty::TyFnDef(def_id, substs) => {
|
||||
if self.tcx.has_attr(def_id, "rustc_args_required_const") {
|
||||
bug!("reifying a fn ptr that requires \
|
||||
|
@ -94,29 +92,26 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
|||
substs,
|
||||
).ok_or_else(|| EvalErrorKind::TooGeneric.into());
|
||||
let fn_ptr = self.memory.create_fn_alloc(instance?);
|
||||
let valty = ValTy {
|
||||
value: Value::Scalar(Scalar::Ptr(fn_ptr.into()).into()),
|
||||
ty: dest_ty,
|
||||
};
|
||||
self.write_value(valty, dest)?;
|
||||
self.write_scalar(Scalar::Ptr(fn_ptr.into()), dest)?;
|
||||
}
|
||||
ref other => bug!("reify fn pointer on {:?}", other),
|
||||
}
|
||||
}
|
||||
|
||||
UnsafeFnPointer => {
|
||||
match dest_ty.sty {
|
||||
let src = self.read_value(src)?;
|
||||
match dest.layout.ty.sty {
|
||||
ty::TyFnPtr(_) => {
|
||||
let mut src = src;
|
||||
src.ty = dest_ty;
|
||||
self.write_value(src, dest)?;
|
||||
// No change to value
|
||||
self.write_value(*src, dest)?;
|
||||
}
|
||||
ref other => bug!("fn to unsafe fn cast on {:?}", other),
|
||||
}
|
||||
}
|
||||
|
||||
ClosureFnPointer => {
|
||||
match src.ty.sty {
|
||||
// The src operand does not matter, just its type
|
||||
match src_layout.ty.sty {
|
||||
ty::TyClosure(def_id, substs) => {
|
||||
let substs = self.tcx.subst_and_normalize_erasing_regions(
|
||||
self.substs(),
|
||||
|
@ -130,11 +125,8 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
|||
ty::ClosureKind::FnOnce,
|
||||
);
|
||||
let fn_ptr = self.memory.create_fn_alloc(instance);
|
||||
let valty = ValTy {
|
||||
value: Value::Scalar(Scalar::Ptr(fn_ptr.into()).into()),
|
||||
ty: dest_ty,
|
||||
};
|
||||
self.write_value(valty, dest)?;
|
||||
let val = Value::Scalar(Scalar::Ptr(fn_ptr.into()).into());
|
||||
self.write_value(val, dest)?;
|
||||
}
|
||||
ref other => bug!("closure fn pointer on {:?}", other),
|
||||
}
|
||||
|
@ -155,12 +147,27 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
|||
match val {
|
||||
Scalar::Ptr(ptr) => self.cast_from_ptr(ptr, dest_layout.ty),
|
||||
Scalar::Bits { bits, size } => {
|
||||
assert_eq!(size as u64, src_layout.size.bytes());
|
||||
match src_layout.ty.sty {
|
||||
TyFloat(fty) => self.cast_from_float(bits, fty, dest_layout.ty),
|
||||
_ => self.cast_from_int(bits, src_layout, dest_layout),
|
||||
debug_assert_eq!(size as u64, src_layout.size.bytes());
|
||||
debug_assert_eq!(truncate(bits, Size::from_bytes(size.into())), bits,
|
||||
"Unexpected value of size {} before casting", size);
|
||||
|
||||
let res = match src_layout.ty.sty {
|
||||
TyFloat(fty) => self.cast_from_float(bits, fty, dest_layout.ty)?,
|
||||
_ => self.cast_from_int(bits, src_layout, dest_layout)?,
|
||||
};
|
||||
|
||||
// Sanity check
|
||||
match res {
|
||||
Scalar::Ptr(_) => bug!("Fabricated a ptr value from an int...?"),
|
||||
Scalar::Bits { bits, size } => {
|
||||
debug_assert_eq!(size as u64, dest_layout.size.bytes());
|
||||
debug_assert_eq!(truncate(bits, Size::from_bytes(size.into())), bits,
|
||||
"Unexpected value of size {} after casting", size);
|
||||
}
|
||||
}
|
||||
// Done
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -229,30 +236,31 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
|||
// float -> uint
|
||||
TyUint(t) => {
|
||||
let width = t.bit_width().unwrap_or(self.memory.pointer_size().bits() as usize);
|
||||
match fty {
|
||||
FloatTy::F32 => Ok(Scalar::Bits {
|
||||
bits: Single::from_bits(bits).to_u128(width).value,
|
||||
let v = match fty {
|
||||
FloatTy::F32 => Single::from_bits(bits).to_u128(width).value,
|
||||
FloatTy::F64 => Double::from_bits(bits).to_u128(width).value,
|
||||
};
|
||||
// This should already fit the bit width
|
||||
Ok(Scalar::Bits {
|
||||
bits: v,
|
||||
size: (width / 8) as u8,
|
||||
}),
|
||||
FloatTy::F64 => Ok(Scalar::Bits {
|
||||
bits: Double::from_bits(bits).to_u128(width).value,
|
||||
size: (width / 8) as u8,
|
||||
}),
|
||||
}
|
||||
})
|
||||
},
|
||||
// float -> int
|
||||
TyInt(t) => {
|
||||
let width = t.bit_width().unwrap_or(self.memory.pointer_size().bits() as usize);
|
||||
match fty {
|
||||
FloatTy::F32 => Ok(Scalar::Bits {
|
||||
bits: Single::from_bits(bits).to_i128(width).value as u128,
|
||||
let v = match fty {
|
||||
FloatTy::F32 => Single::from_bits(bits).to_i128(width).value,
|
||||
FloatTy::F64 => Double::from_bits(bits).to_i128(width).value,
|
||||
};
|
||||
// We got an i128, but we may need something smaller. We have to truncate ourselves.
|
||||
let truncated = truncate(v as u128, Size::from_bits(width as u64));
|
||||
assert_eq!(sign_extend(truncated, Size::from_bits(width as u64)) as i128, v,
|
||||
"truncating and extending changed the value?!?");
|
||||
Ok(Scalar::Bits {
|
||||
bits: truncated,
|
||||
size: (width / 8) as u8,
|
||||
}),
|
||||
FloatTy::F64 => Ok(Scalar::Bits {
|
||||
bits: Double::from_bits(bits).to_i128(width).value as u128,
|
||||
size: (width / 8) as u8,
|
||||
}),
|
||||
}
|
||||
})
|
||||
},
|
||||
// f64 -> f32
|
||||
TyFloat(FloatTy::F32) if fty == FloatTy::F64 => {
|
||||
|
@ -292,4 +300,111 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
|||
_ => err!(Unimplemented(format!("ptr to {:?} cast", ty))),
|
||||
}
|
||||
}
|
||||
|
||||
fn unsize_into_ptr(
|
||||
&mut self,
|
||||
src: OpTy<'tcx>,
|
||||
dest: PlaceTy<'tcx>,
|
||||
// The pointee types
|
||||
sty: Ty<'tcx>,
|
||||
dty: Ty<'tcx>,
|
||||
) -> EvalResult<'tcx> {
|
||||
// A<Struct> -> A<Trait> conversion
|
||||
let (src_pointee_ty, dest_pointee_ty) = self.tcx.struct_lockstep_tails(sty, dty);
|
||||
|
||||
match (&src_pointee_ty.sty, &dest_pointee_ty.sty) {
|
||||
(&ty::TyArray(_, length), &ty::TySlice(_)) => {
|
||||
let ptr = self.read_value(src)?.to_scalar_ptr()?;
|
||||
// u64 cast is from usize to u64, which is always good
|
||||
let val = Value::new_slice(ptr, length.unwrap_usize(self.tcx.tcx), self.tcx.tcx);
|
||||
self.write_value(val, dest)
|
||||
}
|
||||
(&ty::TyDynamic(..), &ty::TyDynamic(..)) => {
|
||||
// For now, upcasts are limited to changes in marker
|
||||
// traits, and hence never actually require an actual
|
||||
// change to the vtable.
|
||||
self.copy_op(src, dest)
|
||||
}
|
||||
(_, &ty::TyDynamic(ref data, _)) => {
|
||||
// Initial cast from sized to dyn trait
|
||||
let trait_ref = data.principal().unwrap().with_self_ty(
|
||||
*self.tcx,
|
||||
src_pointee_ty,
|
||||
);
|
||||
let trait_ref = self.tcx.erase_regions(&trait_ref);
|
||||
let vtable = self.get_vtable(src_pointee_ty, trait_ref)?;
|
||||
let ptr = self.read_value(src)?.to_scalar_ptr()?;
|
||||
let val = Value::new_dyn_trait(ptr, vtable);
|
||||
self.write_value(val, dest)
|
||||
}
|
||||
|
||||
_ => bug!("invalid unsizing {:?} -> {:?}", src.layout.ty, dest.layout.ty),
|
||||
}
|
||||
}
|
||||
|
||||
fn unsize_into(
|
||||
&mut self,
|
||||
src: OpTy<'tcx>,
|
||||
dest: PlaceTy<'tcx>,
|
||||
) -> EvalResult<'tcx> {
|
||||
match (&src.layout.ty.sty, &dest.layout.ty.sty) {
|
||||
(&ty::TyRef(_, s, _), &ty::TyRef(_, d, _)) |
|
||||
(&ty::TyRef(_, s, _), &ty::TyRawPtr(TypeAndMut { ty: d, .. })) |
|
||||
(&ty::TyRawPtr(TypeAndMut { ty: s, .. }),
|
||||
&ty::TyRawPtr(TypeAndMut { ty: d, .. })) => {
|
||||
self.unsize_into_ptr(src, dest, s, d)
|
||||
}
|
||||
(&ty::TyAdt(def_a, _), &ty::TyAdt(def_b, _)) => {
|
||||
assert_eq!(def_a, def_b);
|
||||
if def_a.is_box() || def_b.is_box() {
|
||||
if !def_a.is_box() || !def_b.is_box() {
|
||||
bug!("invalid unsizing between {:?} -> {:?}", src.layout, dest.layout);
|
||||
}
|
||||
return self.unsize_into_ptr(
|
||||
src,
|
||||
dest,
|
||||
src.layout.ty.boxed_ty(),
|
||||
dest.layout.ty.boxed_ty(),
|
||||
);
|
||||
}
|
||||
|
||||
// unsizing of generic struct with pointer fields
|
||||
// Example: `Arc<T>` -> `Arc<Trait>`
|
||||
// here we need to increase the size of every &T thin ptr field to a fat ptr
|
||||
for i in 0..src.layout.fields.count() {
|
||||
let dst_field = self.place_field(dest, i as u64)?;
|
||||
if dst_field.layout.is_zst() {
|
||||
continue;
|
||||
}
|
||||
let src_field = match src.try_as_mplace() {
|
||||
Ok(mplace) => {
|
||||
let src_field = self.mplace_field(mplace, i as u64)?;
|
||||
src_field.into()
|
||||
}
|
||||
Err(..) => {
|
||||
let src_field_layout = src.layout.field(&self, i)?;
|
||||
// this must be a field covering the entire thing
|
||||
assert_eq!(src.layout.fields.offset(i).bytes(), 0);
|
||||
assert_eq!(src_field_layout.size, src.layout.size);
|
||||
// just sawp out the layout
|
||||
OpTy { op: src.op, layout: src_field_layout }
|
||||
}
|
||||
};
|
||||
if src_field.layout.ty == dst_field.layout.ty {
|
||||
self.copy_op(src_field, dst_field)?;
|
||||
} else {
|
||||
self.unsize_into(src_field, dst_field)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
_ => {
|
||||
bug!(
|
||||
"unsize_into: invalid conversion: {:?} -> {:?}",
|
||||
src.layout,
|
||||
dest.layout
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,12 +2,12 @@ use std::fmt;
|
|||
use std::error::Error;
|
||||
|
||||
use rustc::hir;
|
||||
use rustc::mir::interpret::{ConstEvalErr, ScalarMaybeUndef};
|
||||
use rustc::mir::interpret::ConstEvalErr;
|
||||
use rustc::mir;
|
||||
use rustc::ty::{self, TyCtxt, Ty, Instance};
|
||||
use rustc::ty::layout::{self, LayoutOf, Primitive, TyLayout};
|
||||
use rustc::ty::{self, TyCtxt, Instance};
|
||||
use rustc::ty::layout::{LayoutOf, Primitive, TyLayout};
|
||||
use rustc::ty::subst::Subst;
|
||||
use rustc_data_structures::indexed_vec::IndexVec;
|
||||
use rustc_data_structures::indexed_vec::{IndexVec, Idx};
|
||||
|
||||
use syntax::ast::Mutability;
|
||||
use syntax::source_map::Span;
|
||||
|
@ -15,9 +15,12 @@ use syntax::source_map::DUMMY_SP;
|
|||
|
||||
use rustc::mir::interpret::{
|
||||
EvalResult, EvalError, EvalErrorKind, GlobalId,
|
||||
Value, Scalar, AllocId, Allocation, ConstValue,
|
||||
Scalar, AllocId, Allocation, ConstValue,
|
||||
};
|
||||
use super::{
|
||||
Place, PlaceExtra, PlaceTy, MemPlace, OpTy, Operand, Value,
|
||||
EvalContext, StackPopCleanup, Memory, MemoryKind
|
||||
};
|
||||
use super::{Place, EvalContext, StackPopCleanup, ValTy, Memory, MemoryKind};
|
||||
|
||||
pub fn mk_borrowck_eval_cx<'a, 'mir, 'tcx>(
|
||||
tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
|
@ -35,7 +38,7 @@ pub fn mk_borrowck_eval_cx<'a, 'mir, 'tcx>(
|
|||
instance,
|
||||
span,
|
||||
mir,
|
||||
return_place: Place::undef(),
|
||||
return_place: Place::null(tcx),
|
||||
return_to_block: StackPopCleanup::None,
|
||||
stmt: 0,
|
||||
});
|
||||
|
@ -56,7 +59,7 @@ pub fn mk_eval_cx<'a, 'tcx>(
|
|||
instance,
|
||||
mir.span,
|
||||
mir,
|
||||
Place::undef(),
|
||||
Place::null(tcx),
|
||||
StackPopCleanup::None,
|
||||
)?;
|
||||
Ok(ecx)
|
||||
|
@ -67,39 +70,51 @@ pub fn eval_promoted<'a, 'mir, 'tcx>(
|
|||
cid: GlobalId<'tcx>,
|
||||
mir: &'mir mir::Mir<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
) -> EvalResult<'tcx, (Value, Scalar, TyLayout<'tcx>)> {
|
||||
) -> EvalResult<'tcx, OpTy<'tcx>> {
|
||||
ecx.with_fresh_body(|ecx| {
|
||||
eval_body_using_ecx(ecx, cid, Some(mir), param_env)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn value_to_const_value<'tcx>(
|
||||
pub fn op_to_const<'tcx>(
|
||||
ecx: &EvalContext<'_, '_, 'tcx, CompileTimeEvaluator>,
|
||||
val: Value,
|
||||
layout: TyLayout<'tcx>,
|
||||
op: OpTy<'tcx>,
|
||||
normalize: bool,
|
||||
) -> EvalResult<'tcx, &'tcx ty::Const<'tcx>> {
|
||||
match (val, &layout.abi) {
|
||||
(Value::Scalar(ScalarMaybeUndef::Scalar(Scalar::Bits { size: 0, ..})), _) if layout.is_zst() => {},
|
||||
(Value::ByRef(..), _) |
|
||||
(Value::Scalar(_), &layout::Abi::Scalar(_)) |
|
||||
(Value::ScalarPair(..), &layout::Abi::ScalarPair(..)) => {},
|
||||
_ => bug!("bad value/layout combo: {:#?}, {:#?}", val, layout),
|
||||
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 val {
|
||||
Value::Scalar(val) => ConstValue::Scalar(val.unwrap_or_err()?),
|
||||
Value::ScalarPair(a, b) => ConstValue::ScalarPair(a.unwrap_or_err()?, b),
|
||||
Value::ByRef(ptr, align) => {
|
||||
let ptr = ptr.to_ptr().unwrap();
|
||||
};
|
||||
let val = match normalized_op {
|
||||
Err(MemPlace { ptr, align, extra }) => {
|
||||
// extract alloc-offset pair
|
||||
assert_eq!(extra, PlaceExtra::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() >= layout.size.bytes());
|
||||
assert!(alloc.bytes.len() as u64 - ptr.offset.bytes() >= op.layout.size.bytes());
|
||||
let mut alloc = alloc.clone();
|
||||
alloc.align = align;
|
||||
let alloc = ecx.tcx.intern_const_alloc(alloc);
|
||||
ConstValue::ByRef(alloc, ptr.offset)
|
||||
}
|
||||
},
|
||||
Ok(Value::Scalar(x)) =>
|
||||
ConstValue::Scalar(x.not_undef()?),
|
||||
Ok(Value::ScalarPair(a, b)) =>
|
||||
ConstValue::ScalarPair(a.not_undef()?, b),
|
||||
};
|
||||
Ok(ty::Const::from_const_value(ecx.tcx.tcx, val, layout.ty))
|
||||
Ok(ty::Const::from_const_value(ecx.tcx.tcx, val, op.layout.ty))
|
||||
}
|
||||
pub fn const_to_op<'tcx>(
|
||||
ecx: &mut EvalContext<'_, '_, 'tcx, CompileTimeEvaluator>,
|
||||
cnst: &'tcx ty::Const<'tcx>,
|
||||
) -> EvalResult<'tcx, OpTy<'tcx>> {
|
||||
let op = ecx.const_value_to_op(cnst.val)?;
|
||||
Ok(OpTy { op, layout: ecx.layout_of(cnst.ty)? })
|
||||
}
|
||||
|
||||
fn eval_body_and_ecx<'a, 'mir, 'tcx>(
|
||||
|
@ -107,7 +122,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, (Value, Scalar, TyLayout<'tcx>)>, EvalContext<'a, 'mir, 'tcx, CompileTimeEvaluator>) {
|
||||
) -> (EvalResult<'tcx, OpTy<'tcx>>, EvalContext<'a, 'mir, 'tcx, CompileTimeEvaluator>) {
|
||||
debug!("eval_body_and_ecx: {:?}, {:?}", cid, param_env);
|
||||
// we start out with the best span we have
|
||||
// and try improving it down the road when more information is available
|
||||
|
@ -118,12 +133,13 @@ fn eval_body_and_ecx<'a, 'mir, 'tcx>(
|
|||
(r, ecx)
|
||||
}
|
||||
|
||||
// Returns a pointer to where the result lives
|
||||
fn eval_body_using_ecx<'a, 'mir, 'tcx>(
|
||||
ecx: &mut EvalContext<'a, 'mir, 'tcx, CompileTimeEvaluator>,
|
||||
cid: GlobalId<'tcx>,
|
||||
mir: Option<&'mir mir::Mir<'tcx>>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
) -> EvalResult<'tcx, (Value, Scalar, TyLayout<'tcx>)> {
|
||||
) -> EvalResult<'tcx, OpTy<'tcx>> {
|
||||
debug!("eval_body: {:?}, {:?}", cid, param_env);
|
||||
let tcx = ecx.tcx.tcx;
|
||||
let mut mir = match mir {
|
||||
|
@ -135,11 +151,7 @@ fn eval_body_using_ecx<'a, 'mir, 'tcx>(
|
|||
}
|
||||
let layout = ecx.layout_of(mir.return_ty().subst(tcx, cid.instance.substs))?;
|
||||
assert!(!layout.is_unsized());
|
||||
let ptr = ecx.memory.allocate(
|
||||
layout.size,
|
||||
layout.align,
|
||||
MemoryKind::Stack,
|
||||
)?;
|
||||
let ret = ecx.allocate(layout, MemoryKind::Stack)?;
|
||||
let internally_mutable = !layout.ty.is_freeze(tcx, param_env, mir.span);
|
||||
let is_static = tcx.is_static(cid.instance.def_id());
|
||||
let mutability = if is_static == Some(hir::Mutability::MutMutable) || internally_mutable {
|
||||
|
@ -156,19 +168,14 @@ fn eval_body_using_ecx<'a, 'mir, 'tcx>(
|
|||
cid.instance,
|
||||
mir.span,
|
||||
mir,
|
||||
Place::from_ptr(ptr, layout.align),
|
||||
Place::Ptr(*ret),
|
||||
cleanup,
|
||||
)?;
|
||||
|
||||
// The main interpreter loop.
|
||||
while ecx.step()? {}
|
||||
let ptr = ptr.into();
|
||||
// always try to read the value and report errors
|
||||
let value = match ecx.try_read_value(ptr, layout.align, layout.ty)? {
|
||||
Some(val) if is_static.is_none() && cid.promoted.is_none() => val,
|
||||
// point at the allocation
|
||||
_ => Value::ByRef(ptr, layout.align),
|
||||
};
|
||||
Ok((value, ptr, layout))
|
||||
|
||||
Ok(ret.into())
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||
|
@ -222,14 +229,14 @@ impl<'mir, 'tcx> super::Machine<'mir, 'tcx> for CompileTimeEvaluator {
|
|||
fn eval_fn_call<'a>(
|
||||
ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
|
||||
instance: ty::Instance<'tcx>,
|
||||
destination: Option<(Place, mir::BasicBlock)>,
|
||||
args: &[ValTy<'tcx>],
|
||||
destination: Option<(PlaceTy<'tcx>, mir::BasicBlock)>,
|
||||
args: &[OpTy<'tcx>],
|
||||
span: Span,
|
||||
sig: ty::FnSig<'tcx>,
|
||||
) -> EvalResult<'tcx, bool> {
|
||||
debug!("eval_fn_call: {:?}", instance);
|
||||
if !ecx.tcx.is_const_fn(instance.def_id()) {
|
||||
let def_id = instance.def_id();
|
||||
// Some fn calls are actually BinOp intrinsics
|
||||
let (op, oflo) = if let Some(op) = ecx.tcx.is_binop_lang_item(def_id) {
|
||||
op
|
||||
} else {
|
||||
|
@ -238,11 +245,12 @@ impl<'mir, 'tcx> super::Machine<'mir, 'tcx> for CompileTimeEvaluator {
|
|||
);
|
||||
};
|
||||
let (dest, bb) = destination.expect("128 lowerings can't diverge");
|
||||
let dest_ty = sig.output();
|
||||
let l = ecx.read_value(args[0])?;
|
||||
let r = ecx.read_value(args[1])?;
|
||||
if oflo {
|
||||
ecx.intrinsic_with_overflow(op, args[0], args[1], dest, dest_ty)?;
|
||||
ecx.binop_with_overflow(op, l, r, dest)?;
|
||||
} else {
|
||||
ecx.intrinsic_overflowing(op, args[0], args[1], dest, dest_ty)?;
|
||||
ecx.binop_ignore_overflow(op, l, r, dest)?;
|
||||
}
|
||||
ecx.goto_block(bb);
|
||||
return Ok(true);
|
||||
|
@ -260,8 +268,8 @@ impl<'mir, 'tcx> super::Machine<'mir, 'tcx> for CompileTimeEvaluator {
|
|||
}
|
||||
};
|
||||
let (return_place, return_to_block) = match destination {
|
||||
Some((place, block)) => (place, StackPopCleanup::Goto(block)),
|
||||
None => (Place::undef(), StackPopCleanup::None),
|
||||
Some((place, block)) => (*place, StackPopCleanup::Goto(block)),
|
||||
None => (Place::null(&ecx), StackPopCleanup::None),
|
||||
};
|
||||
|
||||
ecx.push_stack_frame(
|
||||
|
@ -279,9 +287,8 @@ impl<'mir, 'tcx> super::Machine<'mir, 'tcx> for CompileTimeEvaluator {
|
|||
fn call_intrinsic<'a>(
|
||||
ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
|
||||
instance: ty::Instance<'tcx>,
|
||||
args: &[ValTy<'tcx>],
|
||||
dest: Place,
|
||||
dest_layout: layout::TyLayout<'tcx>,
|
||||
args: &[OpTy<'tcx>],
|
||||
dest: PlaceTy<'tcx>,
|
||||
target: mir::BasicBlock,
|
||||
) -> EvalResult<'tcx> {
|
||||
let substs = instance.substs;
|
||||
|
@ -293,9 +300,9 @@ impl<'mir, 'tcx> super::Machine<'mir, 'tcx> for CompileTimeEvaluator {
|
|||
let elem_align = ecx.layout_of(elem_ty)?.align.abi();
|
||||
let align_val = Scalar::Bits {
|
||||
bits: elem_align as u128,
|
||||
size: dest_layout.size.bytes() as u8,
|
||||
size: dest.layout.size.bytes() as u8,
|
||||
};
|
||||
ecx.write_scalar(dest, align_val, dest_layout.ty)?;
|
||||
ecx.write_scalar(align_val, dest)?;
|
||||
}
|
||||
|
||||
"size_of" => {
|
||||
|
@ -303,9 +310,9 @@ impl<'mir, 'tcx> super::Machine<'mir, 'tcx> for CompileTimeEvaluator {
|
|||
let size = ecx.layout_of(ty)?.size.bytes() as u128;
|
||||
let size_val = Scalar::Bits {
|
||||
bits: size,
|
||||
size: dest_layout.size.bytes() as u8,
|
||||
size: dest.layout.size.bytes() as u8,
|
||||
};
|
||||
ecx.write_scalar(dest, size_val, dest_layout.ty)?;
|
||||
ecx.write_scalar(size_val, dest)?;
|
||||
}
|
||||
|
||||
"type_id" => {
|
||||
|
@ -313,14 +320,14 @@ impl<'mir, 'tcx> super::Machine<'mir, 'tcx> for CompileTimeEvaluator {
|
|||
let type_id = ecx.tcx.type_id_hash(ty) as u128;
|
||||
let id_val = Scalar::Bits {
|
||||
bits: type_id,
|
||||
size: dest_layout.size.bytes() as u8,
|
||||
size: dest.layout.size.bytes() as u8,
|
||||
};
|
||||
ecx.write_scalar(dest, id_val, dest_layout.ty)?;
|
||||
ecx.write_scalar(id_val, dest)?;
|
||||
}
|
||||
"ctpop" | "cttz" | "cttz_nonzero" | "ctlz" | "ctlz_nonzero" | "bswap" => {
|
||||
let ty = substs.type_at(0);
|
||||
let layout_of = ecx.layout_of(ty)?;
|
||||
let bits = ecx.value_to_scalar(args[0])?.to_bits(layout_of.size)?;
|
||||
let bits = ecx.read_scalar(args[0])?.to_bits(layout_of.size)?;
|
||||
let kind = match layout_of.abi {
|
||||
ty::layout::Abi::Scalar(ref scalar) => scalar.value,
|
||||
_ => Err(::rustc::mir::interpret::EvalErrorKind::TypeNotPrimitive(ty))?,
|
||||
|
@ -333,7 +340,7 @@ impl<'mir, 'tcx> super::Machine<'mir, 'tcx> for CompileTimeEvaluator {
|
|||
} else {
|
||||
numeric_intrinsic(intrinsic_name, bits, kind)?
|
||||
};
|
||||
ecx.write_scalar(dest, out_val, ty)?;
|
||||
ecx.write_scalar(out_val, dest)?;
|
||||
}
|
||||
|
||||
name => return Err(
|
||||
|
@ -353,9 +360,9 @@ impl<'mir, 'tcx> super::Machine<'mir, 'tcx> for CompileTimeEvaluator {
|
|||
_ecx: &EvalContext<'a, 'mir, 'tcx, Self>,
|
||||
_bin_op: mir::BinOp,
|
||||
left: Scalar,
|
||||
_left_ty: Ty<'tcx>,
|
||||
_left_layout: TyLayout<'tcx>,
|
||||
right: Scalar,
|
||||
_right_ty: Ty<'tcx>,
|
||||
_right_layout: TyLayout<'tcx>,
|
||||
) -> EvalResult<'tcx, Option<(Scalar, bool)>> {
|
||||
if left.is_bits() && right.is_bits() {
|
||||
Ok(None)
|
||||
|
@ -387,8 +394,7 @@ impl<'mir, 'tcx> super::Machine<'mir, 'tcx> for CompileTimeEvaluator {
|
|||
|
||||
fn box_alloc<'a>(
|
||||
_ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
|
||||
_ty: Ty<'tcx>,
|
||||
_dest: Place,
|
||||
_dest: PlaceTy<'tcx>,
|
||||
) -> EvalResult<'tcx> {
|
||||
Err(
|
||||
ConstEvalError::NeedsRfc("heap allocations via `box` keyword".to_string()).into(),
|
||||
|
@ -406,7 +412,8 @@ impl<'mir, 'tcx> super::Machine<'mir, 'tcx> for CompileTimeEvaluator {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn const_val_field<'a, 'tcx>(
|
||||
/// Project to a field of a (variant of a) const
|
||||
pub fn const_field<'a, 'tcx>(
|
||||
tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
instance: ty::Instance<'tcx>,
|
||||
|
@ -414,30 +421,21 @@ pub fn const_val_field<'a, 'tcx>(
|
|||
field: mir::Field,
|
||||
value: &'tcx ty::Const<'tcx>,
|
||||
) -> ::rustc::mir::interpret::ConstEvalResult<'tcx> {
|
||||
trace!("const_val_field: {:?}, {:?}, {:?}", instance, field, value);
|
||||
trace!("const_field: {:?}, {:?}, {:?}", instance, field, value);
|
||||
let mut ecx = mk_eval_cx(tcx, instance, param_env).unwrap();
|
||||
let result = (|| {
|
||||
let ty = value.ty;
|
||||
let value = ecx.const_to_value(value.val)?;
|
||||
let layout = ecx.layout_of(ty)?;
|
||||
let place = ecx.allocate_place_for_value(value, layout, variant)?;
|
||||
let (place, layout) = ecx.place_field(place, field, layout)?;
|
||||
let (ptr, align) = place.to_ptr_align();
|
||||
let mut new_value = Value::ByRef(ptr.unwrap_or_err()?, align);
|
||||
new_value = ecx.try_read_by_ref(new_value, layout.ty)?;
|
||||
use rustc_data_structures::indexed_vec::Idx;
|
||||
match (value, new_value) {
|
||||
(Value::Scalar(_), Value::ByRef(..)) |
|
||||
(Value::ScalarPair(..), Value::ByRef(..)) |
|
||||
(Value::Scalar(_), Value::ScalarPair(..)) => bug!(
|
||||
"field {} of {:?} yielded {:?}",
|
||||
field.index(),
|
||||
value,
|
||||
new_value,
|
||||
),
|
||||
_ => {},
|
||||
}
|
||||
value_to_const_value(&ecx, new_value, layout)
|
||||
// get the operand again
|
||||
let op = const_to_op(&mut ecx, value)?;
|
||||
// downcast
|
||||
let down = match variant {
|
||||
None => op,
|
||||
Some(variant) => ecx.operand_downcast(op, variant)?
|
||||
};
|
||||
// then project
|
||||
let field = ecx.operand_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)
|
||||
})();
|
||||
result.map_err(|err| {
|
||||
let (trace, span) = ecx.generate_stacktrace(None);
|
||||
|
@ -457,21 +455,11 @@ pub fn const_variant_index<'a, 'tcx>(
|
|||
) -> EvalResult<'tcx, usize> {
|
||||
trace!("const_variant_index: {:?}, {:?}", instance, val);
|
||||
let mut ecx = mk_eval_cx(tcx, instance, param_env).unwrap();
|
||||
let value = ecx.const_to_value(val.val)?;
|
||||
let layout = ecx.layout_of(val.ty)?;
|
||||
let (ptr, align) = match value {
|
||||
Value::ScalarPair(..) | Value::Scalar(_) => {
|
||||
let ptr = ecx.memory.allocate(layout.size, layout.align, MemoryKind::Stack)?.into();
|
||||
ecx.write_value_to_ptr(value, ptr, layout.align, val.ty)?;
|
||||
(ptr, layout.align)
|
||||
},
|
||||
Value::ByRef(ptr, align) => (ptr, align),
|
||||
};
|
||||
let place = Place::from_scalar_ptr(ptr.into(), align);
|
||||
ecx.read_discriminant_as_variant_index(place, layout)
|
||||
let op = const_to_op(&mut ecx, val)?;
|
||||
ecx.read_discriminant_as_variant_index(op)
|
||||
}
|
||||
|
||||
pub fn const_value_to_allocation_provider<'a, 'tcx>(
|
||||
pub fn const_to_allocation_provider<'a, 'tcx>(
|
||||
tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
val: &'tcx ty::Const<'tcx>,
|
||||
) -> &'tcx Allocation {
|
||||
|
@ -488,11 +476,11 @@ pub fn const_value_to_allocation_provider<'a, 'tcx>(
|
|||
ty::ParamEnv::reveal_all(),
|
||||
CompileTimeEvaluator,
|
||||
());
|
||||
let value = ecx.const_to_value(val.val)?;
|
||||
let layout = ecx.layout_of(val.ty)?;
|
||||
let ptr = ecx.memory.allocate(layout.size, layout.align, MemoryKind::Stack)?;
|
||||
ecx.write_value_to_ptr(value, ptr.into(), layout.align, val.ty)?;
|
||||
let alloc = ecx.memory.get(ptr.alloc_id)?;
|
||||
let op = const_to_op(&mut ecx, val)?;
|
||||
// Make a new allocation, copy things there
|
||||
let ptr = ecx.allocate(op.layout, MemoryKind::Stack)?;
|
||||
ecx.copy_op(op, ptr.into())?;
|
||||
let alloc = ecx.memory.get(ptr.to_ptr()?.alloc_id)?;
|
||||
Ok(tcx.intern_const_alloc(alloc.clone()))
|
||||
};
|
||||
result().expect("unable to convert ConstValue to Allocation")
|
||||
|
@ -534,11 +522,16 @@ pub fn const_eval_provider<'a, 'tcx>(
|
|||
};
|
||||
|
||||
let (res, ecx) = eval_body_and_ecx(tcx, cid, None, key.param_env);
|
||||
res.and_then(|(mut val, _, layout)| {
|
||||
if tcx.is_static(def_id).is_none() && cid.promoted.is_none() {
|
||||
val = ecx.try_read_by_ref(val, layout.ty)?;
|
||||
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"),
|
||||
}
|
||||
value_to_const_value(&ecx, val, layout)
|
||||
}
|
||||
op_to_const(&ecx, op, normalize)
|
||||
}).map_err(|err| {
|
||||
let (trace, span) = ecx.generate_stacktrace(None);
|
||||
let err = ConstEvalErr {
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -5,10 +5,10 @@
|
|||
use std::hash::Hash;
|
||||
|
||||
use rustc::mir::interpret::{AllocId, EvalResult, Scalar, Pointer, AccessKind, GlobalId};
|
||||
use super::{EvalContext, Place, ValTy, Memory};
|
||||
use super::{EvalContext, PlaceTy, OpTy, Memory};
|
||||
|
||||
use rustc::mir;
|
||||
use rustc::ty::{self, Ty};
|
||||
use rustc::ty::{self, layout::TyLayout};
|
||||
use rustc::ty::layout::Size;
|
||||
use syntax::source_map::Span;
|
||||
use syntax::ast::Mutability;
|
||||
|
@ -31,19 +31,17 @@ pub trait Machine<'mir, 'tcx>: Clone + Eq + Hash {
|
|||
fn eval_fn_call<'a>(
|
||||
ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
|
||||
instance: ty::Instance<'tcx>,
|
||||
destination: Option<(Place, mir::BasicBlock)>,
|
||||
args: &[ValTy<'tcx>],
|
||||
destination: Option<(PlaceTy<'tcx>, mir::BasicBlock)>,
|
||||
args: &[OpTy<'tcx>],
|
||||
span: Span,
|
||||
sig: ty::FnSig<'tcx>,
|
||||
) -> EvalResult<'tcx, bool>;
|
||||
|
||||
/// directly process an intrinsic without pushing a stack frame.
|
||||
fn call_intrinsic<'a>(
|
||||
ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
|
||||
instance: ty::Instance<'tcx>,
|
||||
args: &[ValTy<'tcx>],
|
||||
dest: Place,
|
||||
dest_layout: ty::layout::TyLayout<'tcx>,
|
||||
args: &[OpTy<'tcx>],
|
||||
dest: PlaceTy<'tcx>,
|
||||
target: mir::BasicBlock,
|
||||
) -> EvalResult<'tcx>;
|
||||
|
||||
|
@ -57,9 +55,9 @@ pub trait Machine<'mir, 'tcx>: Clone + Eq + Hash {
|
|||
ecx: &EvalContext<'a, 'mir, 'tcx, Self>,
|
||||
bin_op: mir::BinOp,
|
||||
left: Scalar,
|
||||
left_ty: Ty<'tcx>,
|
||||
left_layout: TyLayout<'tcx>,
|
||||
right: Scalar,
|
||||
right_ty: Ty<'tcx>,
|
||||
right_layout: TyLayout<'tcx>,
|
||||
) -> EvalResult<'tcx, Option<(Scalar, bool)>>;
|
||||
|
||||
/// Called when trying to mark machine defined `MemoryKinds` as static
|
||||
|
@ -81,8 +79,7 @@ pub trait Machine<'mir, 'tcx>: Clone + Eq + Hash {
|
|||
/// Returns a pointer to the allocated memory
|
||||
fn box_alloc<'a>(
|
||||
ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
|
||||
ty: Ty<'tcx>,
|
||||
dest: Place,
|
||||
dest: PlaceTy<'tcx>,
|
||||
) -> EvalResult<'tcx>;
|
||||
|
||||
/// Called when trying to access a global declared with a `linkage` attribute
|
||||
|
|
|
@ -1,3 +1,11 @@
|
|||
//! The memory subsystem.
|
||||
//!
|
||||
//! Generally, we use `Pointer` to denote memory addresses. However, some operations
|
||||
//! have a "size"-like parameter, and they take `Scalar` for the address because
|
||||
//! if the size is 0, then the pointer can also be a (properly aligned, non-NULL)
|
||||
//! integer. It is crucial that these operations call `check_align` *before*
|
||||
//! short-circuiting the empty case!
|
||||
|
||||
use std::collections::VecDeque;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::ptr;
|
||||
|
@ -7,15 +15,16 @@ use rustc::ty::Instance;
|
|||
use rustc::ty::ParamEnv;
|
||||
use rustc::ty::query::TyCtxtAt;
|
||||
use rustc::ty::layout::{self, Align, TargetDataLayout, Size};
|
||||
use rustc::mir::interpret::{Pointer, AllocId, Allocation, AccessKind, Value, ScalarMaybeUndef,
|
||||
EvalResult, Scalar, EvalErrorKind, GlobalId, AllocType};
|
||||
pub use rustc::mir::interpret::{write_target_uint, write_target_int, read_target_uint};
|
||||
use rustc::mir::interpret::{Pointer, AllocId, Allocation, AccessKind, ScalarMaybeUndef,
|
||||
EvalResult, Scalar, EvalErrorKind, GlobalId, AllocType, truncate};
|
||||
pub use rustc::mir::interpret::{write_target_uint, read_target_uint};
|
||||
use rustc_data_structures::fx::{FxHashSet, FxHashMap, FxHasher};
|
||||
|
||||
use syntax::ast::Mutability;
|
||||
|
||||
use super::{EvalContext, Machine};
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Allocations and pointers
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -43,9 +52,6 @@ pub struct Memory<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> {
|
|||
/// Actual memory allocations (arbitrary bytes, may contain pointers into other allocations).
|
||||
alloc_map: FxHashMap<AllocId, Allocation>,
|
||||
|
||||
/// The current stack frame. Used to check accesses against locks.
|
||||
pub cur_frame: usize,
|
||||
|
||||
pub tcx: TyCtxtAt<'a, 'tcx, 'tcx>,
|
||||
}
|
||||
|
||||
|
@ -63,14 +69,12 @@ impl<'a, 'mir, 'tcx, M> PartialEq for Memory<'a, 'mir, 'tcx, M>
|
|||
data,
|
||||
alloc_kind,
|
||||
alloc_map,
|
||||
cur_frame,
|
||||
tcx: _,
|
||||
} = self;
|
||||
|
||||
*data == other.data
|
||||
&& *alloc_kind == other.alloc_kind
|
||||
&& *alloc_map == other.alloc_map
|
||||
&& *cur_frame == other.cur_frame
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -83,12 +87,10 @@ impl<'a, 'mir, 'tcx, M> Hash for Memory<'a, 'mir, 'tcx, M>
|
|||
data,
|
||||
alloc_kind: _,
|
||||
alloc_map: _,
|
||||
cur_frame,
|
||||
tcx: _,
|
||||
} = self;
|
||||
|
||||
data.hash(state);
|
||||
cur_frame.hash(state);
|
||||
|
||||
// We ignore some fields which don't change between evaluation steps.
|
||||
|
||||
|
@ -114,7 +116,6 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
|
|||
alloc_kind: FxHashMap::default(),
|
||||
alloc_map: FxHashMap::default(),
|
||||
tcx,
|
||||
cur_frame: usize::max_value(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -264,7 +265,8 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
|
|||
self.tcx.data_layout.endian
|
||||
}
|
||||
|
||||
/// Check that the pointer is aligned AND non-NULL.
|
||||
/// Check that the pointer is aligned AND non-NULL. This supports scalars
|
||||
/// for the benefit of other parts of miri that need to check alignment even for ZST.
|
||||
pub fn check_align(&self, ptr: Scalar, required_align: Align) -> EvalResult<'tcx> {
|
||||
// Check non-NULL/Undef, extract offset
|
||||
let (offset, alloc_align) = match ptr {
|
||||
|
@ -301,6 +303,9 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Check if the pointer is "in-bounds". Notice that a pointer pointing at the end
|
||||
/// of an allocation (i.e., at the first *inaccessible* location) *is* considered
|
||||
/// in-bounds! This follows C's/LLVM's rules.
|
||||
pub fn check_bounds(&self, ptr: Pointer, access: bool) -> EvalResult<'tcx> {
|
||||
let alloc = self.get(ptr.alloc_id)?;
|
||||
let allocation_size = alloc.bytes.len() as u64;
|
||||
|
@ -331,7 +336,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
|
|||
assert!(self.tcx.is_static(def_id).is_some());
|
||||
EvalErrorKind::ReferencedConstant(err).into()
|
||||
}).map(|val| {
|
||||
self.tcx.const_value_to_allocation(val)
|
||||
self.tcx.const_to_allocation(val)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -499,6 +504,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
|
|||
|
||||
/// Byte accessors
|
||||
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
|
||||
/// This checks alignment!
|
||||
fn get_bytes_unchecked(
|
||||
&self,
|
||||
ptr: Pointer,
|
||||
|
@ -519,6 +525,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
|
|||
Ok(&alloc.bytes[offset..offset + size.bytes() as usize])
|
||||
}
|
||||
|
||||
/// This checks alignment!
|
||||
fn get_bytes_unchecked_mut(
|
||||
&mut self,
|
||||
ptr: Pointer,
|
||||
|
@ -556,7 +563,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
|
|||
) -> EvalResult<'tcx, &mut [u8]> {
|
||||
assert_ne!(size.bytes(), 0);
|
||||
self.clear_relocations(ptr, size)?;
|
||||
self.mark_definedness(ptr.into(), size, true)?;
|
||||
self.mark_definedness(ptr, size, true)?;
|
||||
self.get_bytes_unchecked_mut(ptr, size, align)
|
||||
}
|
||||
}
|
||||
|
@ -635,10 +642,10 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
|
|||
length: u64,
|
||||
nonoverlapping: bool,
|
||||
) -> EvalResult<'tcx> {
|
||||
// Empty accesses don't need to be valid pointers, but they should still be aligned
|
||||
if size.bytes() == 0 {
|
||||
// Nothing to do for ZST, other than checking alignment and non-NULLness.
|
||||
self.check_align(src, src_align)?;
|
||||
self.check_align(dest, dest_align)?;
|
||||
if size.bytes() == 0 {
|
||||
return Ok(());
|
||||
}
|
||||
let src = src.to_ptr()?;
|
||||
|
@ -664,6 +671,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
|
|||
new_relocations
|
||||
};
|
||||
|
||||
// This also checks alignment.
|
||||
let src_bytes = self.get_bytes_unchecked(src, size, src_align)?.as_ptr();
|
||||
let dest_bytes = self.get_bytes_mut(dest, size * length, dest_align)?.as_mut_ptr();
|
||||
|
||||
|
@ -721,8 +729,8 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
|
|||
pub fn read_bytes(&self, ptr: Scalar, size: Size) -> EvalResult<'tcx, &[u8]> {
|
||||
// Empty accesses don't need to be valid pointers, but they should still be non-NULL
|
||||
let align = Align::from_bytes(1, 1).unwrap();
|
||||
self.check_align(ptr, align)?;
|
||||
if size.bytes() == 0 {
|
||||
self.check_align(ptr, align)?;
|
||||
return Ok(&[]);
|
||||
}
|
||||
self.get_bytes(ptr.to_ptr()?, size, align)
|
||||
|
@ -731,8 +739,8 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
|
|||
pub fn write_bytes(&mut self, ptr: Scalar, src: &[u8]) -> EvalResult<'tcx> {
|
||||
// Empty accesses don't need to be valid pointers, but they should still be non-NULL
|
||||
let align = Align::from_bytes(1, 1).unwrap();
|
||||
self.check_align(ptr, align)?;
|
||||
if src.is_empty() {
|
||||
self.check_align(ptr, align)?;
|
||||
return Ok(());
|
||||
}
|
||||
let bytes = self.get_bytes_mut(ptr.to_ptr()?, Size::from_bytes(src.len() as u64), align)?;
|
||||
|
@ -743,8 +751,8 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
|
|||
pub fn write_repeat(&mut self, ptr: Scalar, val: u8, count: Size) -> EvalResult<'tcx> {
|
||||
// Empty accesses don't need to be valid pointers, but they should still be non-NULL
|
||||
let align = Align::from_bytes(1, 1).unwrap();
|
||||
self.check_align(ptr, align)?;
|
||||
if count.bytes() == 0 {
|
||||
self.check_align(ptr, align)?;
|
||||
return Ok(());
|
||||
}
|
||||
let bytes = self.get_bytes_mut(ptr.to_ptr()?, count, align)?;
|
||||
|
@ -754,9 +762,11 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Read a *non-ZST* scalar
|
||||
pub fn read_scalar(&self, ptr: Pointer, ptr_align: Align, size: Size) -> EvalResult<'tcx, ScalarMaybeUndef> {
|
||||
self.check_relocation_edges(ptr, size)?; // Make sure we don't read part of a pointer as a pointer
|
||||
let endianness = self.endianness();
|
||||
// get_bytes_unchecked tests alignment
|
||||
let bytes = self.get_bytes_unchecked(ptr, size, ptr_align.min(self.int_align(size)))?;
|
||||
// Undef check happens *after* we established that the alignment is correct.
|
||||
// We must not return Ok() for unaligned pointers!
|
||||
|
@ -789,17 +799,15 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
|
|||
self.read_scalar(ptr, ptr_align, self.pointer_size())
|
||||
}
|
||||
|
||||
/// Write a *non-ZST* scalar
|
||||
pub fn write_scalar(
|
||||
&mut self,
|
||||
ptr: Scalar,
|
||||
ptr: Pointer,
|
||||
ptr_align: Align,
|
||||
val: ScalarMaybeUndef,
|
||||
type_size: Size,
|
||||
type_align: Align,
|
||||
signed: bool,
|
||||
) -> EvalResult<'tcx> {
|
||||
let endianness = self.endianness();
|
||||
self.check_align(ptr, ptr_align)?;
|
||||
|
||||
let val = match val {
|
||||
ScalarMaybeUndef::Scalar(scalar) => scalar,
|
||||
|
@ -812,28 +820,19 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
|
|||
val.offset.bytes() as u128
|
||||
}
|
||||
|
||||
Scalar::Bits { size: 0, .. } => {
|
||||
// nothing to do for ZSTs
|
||||
assert_eq!(type_size.bytes(), 0);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
Scalar::Bits { bits, size } => {
|
||||
assert_eq!(size as u64, type_size.bytes());
|
||||
assert_eq!(truncate(bits, Size::from_bytes(size.into())), bits,
|
||||
"Unexpected value of size {} when writing to memory", size);
|
||||
bits
|
||||
},
|
||||
};
|
||||
|
||||
let ptr = ptr.to_ptr()?;
|
||||
|
||||
{
|
||||
let dst = self.get_bytes_mut(ptr, type_size, ptr_align.min(type_align))?;
|
||||
if signed {
|
||||
write_target_int(endianness, dst, bytes as i128).unwrap();
|
||||
} else {
|
||||
// get_bytes_mut checks alignment
|
||||
let dst = self.get_bytes_mut(ptr, type_size, ptr_align)?;
|
||||
write_target_uint(endianness, dst, bytes).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
// See if we have to also write a relocation
|
||||
match val {
|
||||
|
@ -849,9 +848,9 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn write_ptr_sized_unsigned(&mut self, ptr: Pointer, ptr_align: Align, val: ScalarMaybeUndef) -> EvalResult<'tcx> {
|
||||
pub fn write_ptr_sized(&mut self, ptr: Pointer, ptr_align: Align, val: ScalarMaybeUndef) -> EvalResult<'tcx> {
|
||||
let ptr_size = self.pointer_size();
|
||||
self.write_scalar(ptr.into(), ptr_align, val, ptr_size, ptr_align, false)
|
||||
self.write_scalar(ptr.into(), ptr_align, val, ptr_size)
|
||||
}
|
||||
|
||||
fn int_align(&self, size: Size) -> Align {
|
||||
|
@ -967,14 +966,13 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
|
|||
|
||||
pub fn mark_definedness(
|
||||
&mut self,
|
||||
ptr: Scalar,
|
||||
ptr: Pointer,
|
||||
size: Size,
|
||||
new_state: bool,
|
||||
) -> EvalResult<'tcx> {
|
||||
if size.bytes() == 0 {
|
||||
return Ok(());
|
||||
}
|
||||
let ptr = ptr.to_ptr()?;
|
||||
let alloc = self.get_mut(ptr.alloc_id)?;
|
||||
alloc.undef_mask.set_range(
|
||||
ptr.offset,
|
||||
|
@ -992,63 +990,6 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
|
|||
pub trait HasMemory<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> {
|
||||
fn memory_mut(&mut self) -> &mut Memory<'a, 'mir, 'tcx, M>;
|
||||
fn memory(&self) -> &Memory<'a, 'mir, 'tcx, M>;
|
||||
|
||||
/// Convert the value into a pointer (or a pointer-sized integer). If the value is a ByRef,
|
||||
/// this may have to perform a load.
|
||||
fn into_ptr(
|
||||
&self,
|
||||
value: Value,
|
||||
) -> EvalResult<'tcx, ScalarMaybeUndef> {
|
||||
Ok(match value {
|
||||
Value::ByRef(ptr, align) => {
|
||||
self.memory().read_ptr_sized(ptr.to_ptr()?, align)?
|
||||
}
|
||||
Value::Scalar(ptr) |
|
||||
Value::ScalarPair(ptr, _) => ptr,
|
||||
}.into())
|
||||
}
|
||||
|
||||
fn into_ptr_vtable_pair(
|
||||
&self,
|
||||
value: Value,
|
||||
) -> EvalResult<'tcx, (ScalarMaybeUndef, Pointer)> {
|
||||
match value {
|
||||
Value::ByRef(ref_ptr, align) => {
|
||||
let mem = self.memory();
|
||||
let ptr = mem.read_ptr_sized(ref_ptr.to_ptr()?, align)?.into();
|
||||
let vtable = mem.read_ptr_sized(
|
||||
ref_ptr.ptr_offset(mem.pointer_size(), &mem.tcx.data_layout)?.to_ptr()?,
|
||||
align
|
||||
)?.unwrap_or_err()?.to_ptr()?;
|
||||
Ok((ptr, vtable))
|
||||
}
|
||||
|
||||
Value::ScalarPair(ptr, vtable) => Ok((ptr, vtable.unwrap_or_err()?.to_ptr()?)),
|
||||
_ => bug!("expected ptr and vtable, got {:?}", value),
|
||||
}
|
||||
}
|
||||
|
||||
fn into_slice(
|
||||
&self,
|
||||
value: Value,
|
||||
) -> EvalResult<'tcx, (ScalarMaybeUndef, u64)> {
|
||||
match value {
|
||||
Value::ByRef(ref_ptr, align) => {
|
||||
let mem = self.memory();
|
||||
let ptr = mem.read_ptr_sized(ref_ptr.to_ptr()?, align)?.into();
|
||||
let len = mem.read_ptr_sized(
|
||||
ref_ptr.ptr_offset(mem.pointer_size(), &mem.tcx.data_layout)?.to_ptr()?,
|
||||
align
|
||||
)?.unwrap_or_err()?.to_bits(mem.pointer_size())? as u64;
|
||||
Ok((ptr, len))
|
||||
}
|
||||
Value::ScalarPair(ptr, val) => {
|
||||
let len = val.unwrap_or_err()?.to_bits(self.memory().pointer_size())?;
|
||||
Ok((ptr, len as u64))
|
||||
}
|
||||
Value::Scalar(_) => bug!("expected ptr and length, got {:?}", value),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> HasMemory<'a, 'mir, 'tcx, M> for Memory<'a, 'mir, 'tcx, M> {
|
||||
|
|
|
@ -1,22 +1,23 @@
|
|||
//! An interpreter for MIR used in CTFE and by miri
|
||||
|
||||
mod cast;
|
||||
mod const_eval;
|
||||
mod eval_context;
|
||||
mod place;
|
||||
mod operand;
|
||||
mod machine;
|
||||
mod memory;
|
||||
mod operator;
|
||||
mod step;
|
||||
mod terminator;
|
||||
mod traits;
|
||||
mod const_eval;
|
||||
mod validity;
|
||||
|
||||
pub use self::eval_context::{
|
||||
EvalContext, Frame, StackPopCleanup,
|
||||
TyAndPacked, ValTy,
|
||||
EvalContext, Frame, StackPopCleanup, LocalValue,
|
||||
};
|
||||
|
||||
pub use self::place::{Place, PlaceExtra};
|
||||
pub use self::place::{Place, PlaceExtra, PlaceTy, MemPlace, MPlaceTy};
|
||||
|
||||
pub use self::memory::{Memory, MemoryKind, HasMemory};
|
||||
|
||||
|
@ -25,32 +26,13 @@ pub use self::const_eval::{
|
|||
mk_borrowck_eval_cx,
|
||||
mk_eval_cx,
|
||||
CompileTimeEvaluator,
|
||||
const_value_to_allocation_provider,
|
||||
const_to_allocation_provider,
|
||||
const_eval_provider,
|
||||
const_val_field,
|
||||
const_field,
|
||||
const_variant_index,
|
||||
value_to_const_value,
|
||||
op_to_const,
|
||||
};
|
||||
|
||||
pub use self::machine::Machine;
|
||||
|
||||
pub use self::memory::{write_target_uint, write_target_int, read_target_uint};
|
||||
|
||||
use rustc::ty::layout::TyLayout;
|
||||
|
||||
pub fn sign_extend(value: u128, layout: TyLayout<'_>) -> u128 {
|
||||
let size = layout.size.bits();
|
||||
assert!(layout.abi.is_signed());
|
||||
// sign extend
|
||||
let shift = 128 - size;
|
||||
// shift the unsigned value to the left
|
||||
// and back to the right as signed (essentially fills with FF on the left)
|
||||
(((value << shift) as i128) >> shift) as u128
|
||||
}
|
||||
|
||||
pub fn truncate(value: u128, layout: TyLayout<'_>) -> u128 {
|
||||
let size = layout.size.bits();
|
||||
let shift = 128 - size;
|
||||
// truncate (shift left to drop out leftover values, shift right to fill with zeroes)
|
||||
(value << shift) >> shift
|
||||
}
|
||||
pub use self::operand::{Value, ValTy, Operand, OpTy};
|
||||
|
|
614
src/librustc_mir/interpret/operand.rs
Normal file
614
src/librustc_mir/interpret/operand.rs
Normal file
|
@ -0,0 +1,614 @@
|
|||
//! Functions concerning immediate values and operands, and reading from operands.
|
||||
//! All high-level functions to read from memory work on operands as sources.
|
||||
|
||||
use std::convert::TryInto;
|
||||
|
||||
use rustc::mir;
|
||||
use rustc::ty::layout::{self, Align, LayoutOf, TyLayout, HasDataLayout, IntegerExt};
|
||||
use rustc_data_structures::indexed_vec::Idx;
|
||||
|
||||
use rustc::mir::interpret::{
|
||||
GlobalId, ConstValue, Scalar, EvalResult, Pointer, ScalarMaybeUndef, EvalErrorKind
|
||||
};
|
||||
use super::{EvalContext, Machine, MemPlace, MPlaceTy, PlaceExtra, MemoryKind};
|
||||
|
||||
/// A `Value` represents a single immediate self-contained Rust value.
|
||||
///
|
||||
/// For optimization of a few very common cases, there is also a representation for a pair of
|
||||
/// primitive values (`ScalarPair`). It allows Miri to avoid making allocations for checked binary
|
||||
/// operations and fat pointers. This idea was taken from rustc's codegen.
|
||||
/// In particular, thanks to `ScalarPair`, arithmetic operations and casts can be entirely
|
||||
/// defined on `Value`, and do not have to work with a `Place`.
|
||||
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub enum Value {
|
||||
Scalar(ScalarMaybeUndef),
|
||||
ScalarPair(ScalarMaybeUndef, ScalarMaybeUndef),
|
||||
}
|
||||
|
||||
impl<'tcx> Value {
|
||||
pub fn new_slice(
|
||||
val: Scalar,
|
||||
len: u64,
|
||||
cx: impl HasDataLayout
|
||||
) -> Self {
|
||||
Value::ScalarPair(val.into(), Scalar::Bits {
|
||||
bits: len as u128,
|
||||
size: cx.data_layout().pointer_size.bytes() as u8,
|
||||
}.into())
|
||||
}
|
||||
|
||||
pub fn new_dyn_trait(val: Scalar, vtable: Pointer) -> Self {
|
||||
Value::ScalarPair(val.into(), Scalar::Ptr(vtable).into())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_scalar_or_undef(self) -> ScalarMaybeUndef {
|
||||
match self {
|
||||
Value::Scalar(val) => val,
|
||||
Value::ScalarPair(..) => bug!("Got a fat pointer where a scalar was expected"),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_scalar(self) -> EvalResult<'tcx, Scalar> {
|
||||
self.to_scalar_or_undef().not_undef()
|
||||
}
|
||||
|
||||
/// Convert the value into a pointer (or a pointer-sized integer).
|
||||
/// Throws away the second half of a ScalarPair!
|
||||
#[inline]
|
||||
pub fn to_scalar_ptr(self) -> EvalResult<'tcx, Scalar> {
|
||||
match self {
|
||||
Value::Scalar(ptr) |
|
||||
Value::ScalarPair(ptr, _) => ptr.not_undef(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_scalar_dyn_trait(self) -> EvalResult<'tcx, (Scalar, Pointer)> {
|
||||
match self {
|
||||
Value::ScalarPair(ptr, vtable) =>
|
||||
Ok((ptr.not_undef()?, vtable.to_ptr()?)),
|
||||
_ => bug!("expected ptr and vtable, got {:?}", self),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_scalar_slice(self, cx: impl HasDataLayout) -> EvalResult<'tcx, (Scalar, u64)> {
|
||||
match self {
|
||||
Value::ScalarPair(ptr, val) => {
|
||||
let len = val.to_bits(cx.data_layout().pointer_size)?;
|
||||
Ok((ptr.not_undef()?, len as u64))
|
||||
}
|
||||
_ => bug!("expected ptr and length, got {:?}", self),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ScalarPair needs a type to interpret, so we often have a value and a type together
|
||||
// as input for binary and cast operations.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct ValTy<'tcx> {
|
||||
pub value: Value,
|
||||
pub layout: TyLayout<'tcx>,
|
||||
}
|
||||
|
||||
impl<'tcx> ::std::ops::Deref for ValTy<'tcx> {
|
||||
type Target = Value;
|
||||
#[inline(always)]
|
||||
fn deref(&self) -> &Value {
|
||||
&self.value
|
||||
}
|
||||
}
|
||||
|
||||
/// An `Operand` is the result of computing a `mir::Operand`. It can be immediate,
|
||||
/// or still in memory. The latter is an optimization, to delay reading that chunk of
|
||||
/// memory and to avoid having to store arbitrary-sized data here.
|
||||
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub enum Operand {
|
||||
Immediate(Value),
|
||||
Indirect(MemPlace),
|
||||
}
|
||||
|
||||
impl Operand {
|
||||
#[inline]
|
||||
pub fn from_ptr(ptr: Pointer, align: Align) -> Self {
|
||||
Operand::Indirect(MemPlace::from_ptr(ptr, align))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn from_scalar_value(val: Scalar) -> Self {
|
||||
Operand::Immediate(Value::Scalar(val.into()))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_mem_place(self) -> MemPlace {
|
||||
match self {
|
||||
Operand::Indirect(mplace) => mplace,
|
||||
_ => bug!("to_mem_place: expected Operand::Indirect, got {:?}", self),
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_immediate(self) -> Value {
|
||||
match self {
|
||||
Operand::Immediate(val) => val,
|
||||
_ => bug!("to_immediate: expected Operand::Immediate, got {:?}", self),
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct OpTy<'tcx> {
|
||||
pub op: Operand,
|
||||
pub layout: TyLayout<'tcx>,
|
||||
}
|
||||
|
||||
impl<'tcx> ::std::ops::Deref for OpTy<'tcx> {
|
||||
type Target = Operand;
|
||||
#[inline(always)]
|
||||
fn deref(&self) -> &Operand {
|
||||
&self.op
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> From<MPlaceTy<'tcx>> for OpTy<'tcx> {
|
||||
#[inline(always)]
|
||||
fn from(mplace: MPlaceTy<'tcx>) -> Self {
|
||||
OpTy {
|
||||
op: Operand::Indirect(*mplace),
|
||||
layout: mplace.layout
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> From<ValTy<'tcx>> for OpTy<'tcx> {
|
||||
#[inline(always)]
|
||||
fn from(val: ValTy<'tcx>) -> Self {
|
||||
OpTy {
|
||||
op: Operand::Immediate(val.value),
|
||||
layout: val.layout
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> OpTy<'tcx> {
|
||||
#[inline]
|
||||
pub fn from_ptr(ptr: Pointer, align: Align, layout: TyLayout<'tcx>) -> Self {
|
||||
OpTy { op: Operand::from_ptr(ptr, align), layout }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn from_aligned_ptr(ptr: Pointer, layout: TyLayout<'tcx>) -> Self {
|
||||
OpTy { op: Operand::from_ptr(ptr, layout.align), layout }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn from_scalar_value(val: Scalar, layout: TyLayout<'tcx>) -> Self {
|
||||
OpTy { op: Operand::Immediate(Value::Scalar(val.into())), layout }
|
||||
}
|
||||
}
|
||||
|
||||
// Use the existing layout if given (but sanity check in debug mode),
|
||||
// or compute the layout.
|
||||
#[inline(always)]
|
||||
fn from_known_layout<'tcx>(
|
||||
layout: Option<TyLayout<'tcx>>,
|
||||
compute: impl FnOnce() -> EvalResult<'tcx, TyLayout<'tcx>>
|
||||
) -> EvalResult<'tcx, TyLayout<'tcx>> {
|
||||
match layout {
|
||||
None => compute(),
|
||||
Some(layout) => {
|
||||
if cfg!(debug_assertions) {
|
||||
let layout2 = compute()?;
|
||||
assert_eq!(layout.details, layout2.details,
|
||||
"Mismatch in layout of supposedly equal-layout types {:?} and {:?}",
|
||||
layout.ty, layout2.ty);
|
||||
}
|
||||
Ok(layout)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'mir, 'tcx, M: Machine<'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.
|
||||
fn try_read_value_from_mplace(
|
||||
&self,
|
||||
mplace: MPlaceTy<'tcx>,
|
||||
) -> EvalResult<'tcx, Option<Value>> {
|
||||
if mplace.extra != PlaceExtra::None {
|
||||
return Ok(None);
|
||||
}
|
||||
let (ptr, ptr_align) = mplace.to_scalar_ptr_align();
|
||||
|
||||
if mplace.layout.size.bytes() == 0 {
|
||||
// Not all ZSTs have a layout we would handle below, so just short-circuit them
|
||||
// all here.
|
||||
self.memory.check_align(ptr, ptr_align)?;
|
||||
return Ok(Some(Value::Scalar(Scalar::zst().into())));
|
||||
}
|
||||
|
||||
let ptr = ptr.to_ptr()?;
|
||||
match mplace.layout.abi {
|
||||
layout::Abi::Scalar(..) => {
|
||||
let scalar = self.memory.read_scalar(ptr, ptr_align, mplace.layout.size)?;
|
||||
Ok(Some(Value::Scalar(scalar)))
|
||||
}
|
||||
layout::Abi::ScalarPair(ref a, ref b) => {
|
||||
let (a, b) = (&a.value, &b.value);
|
||||
let (a_size, b_size) = (a.size(self), b.size(self));
|
||||
let a_ptr = ptr;
|
||||
let b_offset = a_size.abi_align(b.align(self));
|
||||
assert!(b_offset.bytes() > 0); // we later use the offset to test which field to use
|
||||
let b_ptr = ptr.offset(b_offset, self)?.into();
|
||||
let a_val = self.memory.read_scalar(a_ptr, ptr_align, a_size)?;
|
||||
let b_val = self.memory.read_scalar(b_ptr, ptr_align, b_size)?;
|
||||
Ok(Some(Value::ScalarPair(a_val, b_val)))
|
||||
}
|
||||
_ => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
/// Try returning an immediate value for the operand.
|
||||
/// If the layout does not permit loading this as a value, return where in memory
|
||||
/// we can find the data.
|
||||
/// 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(super) fn try_read_value(
|
||||
&self,
|
||||
src: OpTy<'tcx>,
|
||||
) -> EvalResult<'tcx, Result<Value, MemPlace>> {
|
||||
Ok(match src.try_as_mplace() {
|
||||
Ok(mplace) => {
|
||||
if let Some(val) = self.try_read_value_from_mplace(mplace)? {
|
||||
Ok(val)
|
||||
} else {
|
||||
Err(*mplace)
|
||||
}
|
||||
},
|
||||
Err(val) => Ok(val),
|
||||
})
|
||||
}
|
||||
|
||||
/// Read a value from a place, asserting that that is possible with the given layout.
|
||||
#[inline(always)]
|
||||
pub fn read_value(&self, op: OpTy<'tcx>) -> EvalResult<'tcx, ValTy<'tcx>> {
|
||||
if let Ok(value) = self.try_read_value(op)? {
|
||||
Ok(ValTy { value, layout: op.layout })
|
||||
} else {
|
||||
bug!("primitive read failed for type: {:?}", op.layout.ty);
|
||||
}
|
||||
}
|
||||
|
||||
/// Read a scalar from a place
|
||||
pub fn read_scalar(&self, op: OpTy<'tcx>) -> EvalResult<'tcx, ScalarMaybeUndef> {
|
||||
match *self.read_value(op)? {
|
||||
Value::ScalarPair(..) => bug!("got ScalarPair for type: {:?}", op.layout.ty),
|
||||
Value::Scalar(val) => Ok(val),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn uninit_operand(&mut self, layout: TyLayout<'tcx>) -> EvalResult<'tcx, Operand> {
|
||||
// This decides which types we will use the Immediate optimization for, and hence should
|
||||
// match what `try_read_value` and `eval_place_to_op` support.
|
||||
if layout.is_zst() {
|
||||
return Ok(Operand::Immediate(Value::Scalar(Scalar::zst().into())));
|
||||
}
|
||||
|
||||
Ok(match layout.abi {
|
||||
layout::Abi::Scalar(..) =>
|
||||
Operand::Immediate(Value::Scalar(ScalarMaybeUndef::Undef)),
|
||||
layout::Abi::ScalarPair(..) =>
|
||||
Operand::Immediate(Value::ScalarPair(
|
||||
ScalarMaybeUndef::Undef,
|
||||
ScalarMaybeUndef::Undef,
|
||||
)),
|
||||
_ => {
|
||||
trace!("Forcing allocation for local of type {:?}", layout.ty);
|
||||
Operand::Indirect(
|
||||
*self.allocate(layout, MemoryKind::Stack)?
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Projection functions
|
||||
pub fn operand_field(
|
||||
&self,
|
||||
op: OpTy<'tcx>,
|
||||
field: u64,
|
||||
) -> EvalResult<'tcx, OpTy<'tcx>> {
|
||||
let base = match op.try_as_mplace() {
|
||||
Ok(mplace) => {
|
||||
// The easy case
|
||||
let field = self.mplace_field(mplace, field)?;
|
||||
return Ok(field.into());
|
||||
},
|
||||
Err(value) => value
|
||||
};
|
||||
|
||||
let field = field.try_into().unwrap();
|
||||
let field_layout = op.layout.field(self, field)?;
|
||||
if field_layout.size.bytes() == 0 {
|
||||
let val = Value::Scalar(Scalar::zst().into());
|
||||
return Ok(OpTy { op: Operand::Immediate(val), layout: field_layout });
|
||||
}
|
||||
let offset = op.layout.fields.offset(field);
|
||||
let value = match base {
|
||||
// the field covers the entire type
|
||||
_ if offset.bytes() == 0 && field_layout.size == op.layout.size => base,
|
||||
// extract fields from types with `ScalarPair` ABI
|
||||
Value::ScalarPair(a, b) => {
|
||||
let val = if offset.bytes() == 0 { a } else { b };
|
||||
Value::Scalar(val)
|
||||
},
|
||||
Value::Scalar(val) =>
|
||||
bug!("field access on non aggregate {:#?}, {:#?}", val, op.layout),
|
||||
};
|
||||
Ok(OpTy { op: Operand::Immediate(value), layout: field_layout })
|
||||
}
|
||||
|
||||
pub(super) fn operand_downcast(
|
||||
&self,
|
||||
op: OpTy<'tcx>,
|
||||
variant: usize,
|
||||
) -> EvalResult<'tcx, OpTy<'tcx>> {
|
||||
// Downcasts only change the layout
|
||||
Ok(match op.try_as_mplace() {
|
||||
Ok(mplace) => {
|
||||
self.mplace_downcast(mplace, variant)?.into()
|
||||
},
|
||||
Err(..) => {
|
||||
let layout = op.layout.for_variant(self, variant);
|
||||
OpTy { layout, ..op }
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Take an operand, representing a pointer, and dereference it -- that
|
||||
// will always be a MemPlace.
|
||||
pub(super) fn deref_operand(
|
||||
&self,
|
||||
src: OpTy<'tcx>,
|
||||
) -> EvalResult<'tcx, MPlaceTy<'tcx>> {
|
||||
let val = self.read_value(src)?;
|
||||
trace!("deref to {} on {:?}", val.layout.ty, val);
|
||||
Ok(self.ref_to_mplace(val)?)
|
||||
}
|
||||
|
||||
pub fn operand_projection(
|
||||
&self,
|
||||
base: OpTy<'tcx>,
|
||||
proj_elem: &mir::PlaceElem<'tcx>,
|
||||
) -> EvalResult<'tcx, OpTy<'tcx>> {
|
||||
use rustc::mir::ProjectionElem::*;
|
||||
Ok(match *proj_elem {
|
||||
Field(field, _) => self.operand_field(base, field.index() as u64)?,
|
||||
Downcast(_, variant) => self.operand_downcast(base, variant)?,
|
||||
Deref => self.deref_operand(base)?.into(),
|
||||
// The rest should only occur as mplace, we do not use Immediates for types
|
||||
// allowing such operations. This matches place_projection forcing an allocation.
|
||||
Subslice { .. } | ConstantIndex { .. } | Index(_) => {
|
||||
let mplace = base.to_mem_place();
|
||||
self.mplace_projection(mplace, proj_elem)?.into()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Evaluate a place with the goal of reading from it. This lets us sometimes
|
||||
// avoid allocations. If you already know the layout, you can pass it in
|
||||
// to avoid looking it up again.
|
||||
fn eval_place_to_op(
|
||||
&mut self,
|
||||
mir_place: &mir::Place<'tcx>,
|
||||
layout: Option<TyLayout<'tcx>>,
|
||||
) -> EvalResult<'tcx, OpTy<'tcx>> {
|
||||
use rustc::mir::Place::*;
|
||||
Ok(match *mir_place {
|
||||
Local(mir::RETURN_PLACE) => return err!(ReadFromReturnPointer),
|
||||
Local(local) => {
|
||||
let op = *self.frame().locals[local].access()?;
|
||||
let layout = from_known_layout(layout,
|
||||
|| self.layout_of_local(self.cur_frame(), local))?;
|
||||
OpTy { op, layout }
|
||||
},
|
||||
|
||||
Projection(ref proj) => {
|
||||
let op = self.eval_place_to_op(&proj.base, None)?;
|
||||
self.operand_projection(op, &proj.elem)?
|
||||
}
|
||||
|
||||
// Everything else is an mplace, so we just call `eval_place`.
|
||||
// Note that getting an mplace for a static aways requires `&mut`,
|
||||
// so this does not "cost" us anything in terms if mutability.
|
||||
Promoted(_) | Static(_) => {
|
||||
let place = self.eval_place(mir_place)?;
|
||||
place.to_mem_place().into()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Evaluate the operand, returning a place where you can then find the data.
|
||||
/// if you already know the layout, you can save two some table lookups
|
||||
/// by passing it in here.
|
||||
pub fn eval_operand(
|
||||
&mut self,
|
||||
mir_op: &mir::Operand<'tcx>,
|
||||
layout: Option<TyLayout<'tcx>>,
|
||||
) -> EvalResult<'tcx, OpTy<'tcx>> {
|
||||
use rustc::mir::Operand::*;
|
||||
let op = match *mir_op {
|
||||
// FIXME: do some more logic on `move` to invalidate the old location
|
||||
Copy(ref place) |
|
||||
Move(ref place) =>
|
||||
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 }
|
||||
}
|
||||
};
|
||||
trace!("{:?}: {:?}", mir_op, *op);
|
||||
Ok(op)
|
||||
}
|
||||
|
||||
/// Evaluate a bunch of operands at once
|
||||
pub(crate) fn eval_operands(
|
||||
&mut self,
|
||||
ops: &[mir::Operand<'tcx>],
|
||||
) -> EvalResult<'tcx, Vec<OpTy<'tcx>>> {
|
||||
ops.into_iter()
|
||||
.map(|op| self.eval_operand(op, None))
|
||||
.collect()
|
||||
}
|
||||
|
||||
// Also used e.g. when miri runs into a constant.
|
||||
// Unfortunately, this needs an `&mut` to be able to allocate a copy of a `ByRef`
|
||||
// constant. This bleeds up to `eval_operand` needing `&mut`.
|
||||
pub fn const_value_to_op(
|
||||
&mut self,
|
||||
val: ConstValue<'tcx>,
|
||||
) -> EvalResult<'tcx, Operand> {
|
||||
match val {
|
||||
ConstValue::Unevaluated(def_id, substs) => {
|
||||
let instance = self.resolve(def_id, substs)?;
|
||||
self.global_to_op(GlobalId {
|
||||
instance,
|
||||
promoted: None,
|
||||
})
|
||||
}
|
||||
ConstValue::ByRef(alloc, offset) => {
|
||||
// FIXME: Allocate new AllocId for all constants inside
|
||||
let id = self.memory.allocate_value(alloc.clone(), MemoryKind::Stack)?;
|
||||
Ok(Operand::from_ptr(Pointer::new(id, offset), alloc.align))
|
||||
},
|
||||
ConstValue::ScalarPair(a, b) =>
|
||||
Ok(Operand::Immediate(Value::ScalarPair(a.into(), b))),
|
||||
ConstValue::Scalar(x) =>
|
||||
Ok(Operand::Immediate(Value::Scalar(x.into()))),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn global_to_op(&mut self, gid: GlobalId<'tcx>) -> EvalResult<'tcx, Operand> {
|
||||
let cv = self.const_eval(gid)?;
|
||||
self.const_value_to_op(cv.val)
|
||||
}
|
||||
|
||||
/// We cannot do self.read_value(self.eval_operand) due to eval_operand taking &mut self,
|
||||
/// so this helps avoid unnecessary let.
|
||||
#[inline]
|
||||
pub fn eval_operand_and_read_value(
|
||||
&mut self,
|
||||
op: &mir::Operand<'tcx>,
|
||||
layout: Option<TyLayout<'tcx>>,
|
||||
) -> EvalResult<'tcx, ValTy<'tcx>> {
|
||||
let op = self.eval_operand(op, layout)?;
|
||||
self.read_value(op)
|
||||
}
|
||||
|
||||
/// reads a tag and produces the corresponding variant index
|
||||
pub fn read_discriminant_as_variant_index(
|
||||
&self,
|
||||
rval: OpTy<'tcx>,
|
||||
) -> EvalResult<'tcx, usize> {
|
||||
match rval.layout.variants {
|
||||
layout::Variants::Single { index } => Ok(index),
|
||||
layout::Variants::Tagged { .. } => {
|
||||
let discr_val = self.read_discriminant_value(rval)?;
|
||||
rval.layout.ty
|
||||
.ty_adt_def()
|
||||
.expect("tagged layout for non adt")
|
||||
.discriminants(self.tcx.tcx)
|
||||
.position(|var| var.val == discr_val)
|
||||
.ok_or_else(|| EvalErrorKind::InvalidDiscriminant.into())
|
||||
}
|
||||
layout::Variants::NicheFilling { .. } => {
|
||||
let discr_val = self.read_discriminant_value(rval)?;
|
||||
assert_eq!(discr_val as usize as u128, discr_val);
|
||||
Ok(discr_val as usize)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_discriminant_value(
|
||||
&self,
|
||||
rval: OpTy<'tcx>,
|
||||
) -> EvalResult<'tcx, u128> {
|
||||
trace!("read_discriminant_value {:#?}", rval.layout);
|
||||
if rval.layout.abi == layout::Abi::Uninhabited {
|
||||
return err!(Unreachable);
|
||||
}
|
||||
|
||||
match rval.layout.variants {
|
||||
layout::Variants::Single { index } => {
|
||||
let discr_val = rval.layout.ty.ty_adt_def().map_or(
|
||||
index as u128,
|
||||
|def| def.discriminant_for_variant(*self.tcx, index).val);
|
||||
return Ok(discr_val);
|
||||
}
|
||||
layout::Variants::Tagged { .. } |
|
||||
layout::Variants::NicheFilling { .. } => {},
|
||||
}
|
||||
let discr_op = self.operand_field(rval, 0)?;
|
||||
let discr_val = self.read_value(discr_op)?;
|
||||
trace!("discr value: {:?}", discr_val);
|
||||
let raw_discr = discr_val.to_scalar()?;
|
||||
Ok(match rval.layout.variants {
|
||||
layout::Variants::Single { .. } => bug!(),
|
||||
// FIXME: We should catch invalid discriminants here!
|
||||
layout::Variants::Tagged { .. } => {
|
||||
if discr_val.layout.ty.is_signed() {
|
||||
let i = raw_discr.to_bits(discr_val.layout.size)? as i128;
|
||||
// going from layout tag type to typeck discriminant type
|
||||
// requires first sign extending with the layout discriminant
|
||||
let shift = 128 - discr_val.layout.size.bits();
|
||||
let sexted = (i << shift) >> shift;
|
||||
// and then zeroing with the typeck discriminant type
|
||||
let discr_ty = rval.layout.ty
|
||||
.ty_adt_def().expect("tagged layout corresponds to adt")
|
||||
.repr
|
||||
.discr_type();
|
||||
let discr_ty = layout::Integer::from_attr(self.tcx.tcx, discr_ty);
|
||||
let shift = 128 - discr_ty.size().bits();
|
||||
let truncatee = sexted as u128;
|
||||
(truncatee << shift) >> shift
|
||||
} else {
|
||||
raw_discr.to_bits(discr_val.layout.size)?
|
||||
}
|
||||
},
|
||||
layout::Variants::NicheFilling {
|
||||
dataful_variant,
|
||||
ref niche_variants,
|
||||
niche_start,
|
||||
..
|
||||
} => {
|
||||
let variants_start = *niche_variants.start() as u128;
|
||||
let variants_end = *niche_variants.end() as u128;
|
||||
match raw_discr {
|
||||
Scalar::Ptr(_) => {
|
||||
assert!(niche_start == 0);
|
||||
assert!(variants_start == variants_end);
|
||||
dataful_variant as u128
|
||||
},
|
||||
Scalar::Bits { bits: raw_discr, size } => {
|
||||
assert_eq!(size as u64, discr_val.layout.size.bytes());
|
||||
let discr = raw_discr.wrapping_sub(niche_start)
|
||||
.wrapping_add(variants_start);
|
||||
if variants_start <= discr && discr <= variants_end {
|
||||
discr
|
||||
} else {
|
||||
dataful_variant as u128
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
|
@ -1,58 +1,39 @@
|
|||
use rustc::mir;
|
||||
use rustc::ty::{self, Ty, layout};
|
||||
use rustc::ty::{self, layout::{self, TyLayout}};
|
||||
use syntax::ast::FloatTy;
|
||||
use rustc::ty::layout::{LayoutOf, TyLayout};
|
||||
use rustc_apfloat::ieee::{Double, Single};
|
||||
use rustc_apfloat::Float;
|
||||
use rustc::mir::interpret::{EvalResult, Scalar};
|
||||
|
||||
use super::{EvalContext, Place, Machine, ValTy};
|
||||
use super::{EvalContext, PlaceTy, Value, Machine, ValTy};
|
||||
|
||||
use rustc::mir::interpret::{EvalResult, Scalar, Value};
|
||||
|
||||
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
||||
fn binop_with_overflow(
|
||||
&self,
|
||||
op: mir::BinOp,
|
||||
left: ValTy<'tcx>,
|
||||
right: ValTy<'tcx>,
|
||||
) -> EvalResult<'tcx, (Scalar, bool)> {
|
||||
let left_val = self.value_to_scalar(left)?;
|
||||
let right_val = self.value_to_scalar(right)?;
|
||||
self.binary_op(op, left_val, left.ty, right_val, right.ty)
|
||||
}
|
||||
|
||||
/// Applies the binary operation `op` to the two operands and writes a tuple of the result
|
||||
/// and a boolean signifying the potential overflow to the destination.
|
||||
pub fn intrinsic_with_overflow(
|
||||
pub fn binop_with_overflow(
|
||||
&mut self,
|
||||
op: mir::BinOp,
|
||||
left: ValTy<'tcx>,
|
||||
right: ValTy<'tcx>,
|
||||
dest: Place,
|
||||
dest_ty: Ty<'tcx>,
|
||||
dest: PlaceTy<'tcx>,
|
||||
) -> EvalResult<'tcx> {
|
||||
let (val, overflowed) = self.binop_with_overflow(op, left, right)?;
|
||||
let (val, overflowed) = self.binary_op(op, left, right)?;
|
||||
let val = Value::ScalarPair(val.into(), Scalar::from_bool(overflowed).into());
|
||||
let valty = ValTy {
|
||||
value: val,
|
||||
ty: dest_ty,
|
||||
};
|
||||
self.write_value(valty, dest)
|
||||
self.write_value(val, dest)
|
||||
}
|
||||
|
||||
/// Applies the binary operation `op` to the arguments and writes the result to the
|
||||
/// destination. Returns `true` if the operation overflowed.
|
||||
pub fn intrinsic_overflowing(
|
||||
/// destination.
|
||||
pub fn binop_ignore_overflow(
|
||||
&mut self,
|
||||
op: mir::BinOp,
|
||||
left: ValTy<'tcx>,
|
||||
right: ValTy<'tcx>,
|
||||
dest: Place,
|
||||
dest_ty: Ty<'tcx>,
|
||||
) -> EvalResult<'tcx, bool> {
|
||||
let (val, overflowed) = self.binop_with_overflow(op, left, right)?;
|
||||
self.write_scalar(dest, val, dest_ty)?;
|
||||
Ok(overflowed)
|
||||
dest: PlaceTy<'tcx>,
|
||||
) -> EvalResult<'tcx> {
|
||||
let (val, _overflowed) = self.binary_op(op, left, right)?;
|
||||
self.write_scalar(val, dest)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -61,29 +42,29 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
|||
pub fn binary_op(
|
||||
&self,
|
||||
bin_op: mir::BinOp,
|
||||
left: Scalar,
|
||||
left_ty: Ty<'tcx>,
|
||||
right: Scalar,
|
||||
right_ty: Ty<'tcx>,
|
||||
ValTy { value: left, layout: left_layout }: ValTy<'tcx>,
|
||||
ValTy { value: right, layout: right_layout }: ValTy<'tcx>,
|
||||
) -> EvalResult<'tcx, (Scalar, bool)> {
|
||||
use rustc::mir::BinOp::*;
|
||||
|
||||
let left_layout = self.layout_of(left_ty)?;
|
||||
let right_layout = self.layout_of(right_ty)?;
|
||||
let left = left.to_scalar()?;
|
||||
let right = right.to_scalar()?;
|
||||
|
||||
let left_kind = match left_layout.abi {
|
||||
layout::Abi::Scalar(ref scalar) => scalar.value,
|
||||
_ => return err!(TypeNotPrimitive(left_ty)),
|
||||
_ => return err!(TypeNotPrimitive(left_layout.ty)),
|
||||
};
|
||||
let right_kind = match right_layout.abi {
|
||||
layout::Abi::Scalar(ref scalar) => scalar.value,
|
||||
_ => return err!(TypeNotPrimitive(right_ty)),
|
||||
_ => return err!(TypeNotPrimitive(right_layout.ty)),
|
||||
};
|
||||
trace!("Running binary op {:?}: {:?} ({:?}), {:?} ({:?})", bin_op, left, left_kind, right, right_kind);
|
||||
|
||||
// I: Handle operations that support pointers
|
||||
if !left_kind.is_float() && !right_kind.is_float() {
|
||||
if let Some(handled) = M::try_ptr_op(self, bin_op, left, left_ty, right, right_ty)? {
|
||||
if let Some(handled) =
|
||||
M::try_ptr_op(self, bin_op, left, left_layout, right, right_layout)?
|
||||
{
|
||||
return Ok(handled);
|
||||
}
|
||||
}
|
||||
|
@ -188,7 +169,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
|||
}
|
||||
}
|
||||
|
||||
if let ty::TyFloat(fty) = left_ty.sty {
|
||||
if let ty::TyFloat(fty) = left_layout.ty.sty {
|
||||
macro_rules! float_math {
|
||||
($ty:path, $size:expr) => {{
|
||||
let l = <$ty>::from_bits(l);
|
||||
|
@ -220,7 +201,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
|||
}
|
||||
}
|
||||
|
||||
let size = self.layout_of(left_ty).unwrap().size.bytes() as u8;
|
||||
let size = left_layout.size.bytes() as u8;
|
||||
|
||||
// only ints left
|
||||
let val = match bin_op {
|
||||
|
@ -260,9 +241,9 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
|||
"unimplemented binary op {:?}: {:?} ({:?}), {:?} ({:?})",
|
||||
bin_op,
|
||||
left,
|
||||
left_ty,
|
||||
left_layout.ty,
|
||||
right,
|
||||
right_ty,
|
||||
right_layout.ty,
|
||||
);
|
||||
return err!(Unimplemented(msg));
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -3,10 +3,38 @@
|
|||
//! The main entry point is the `step` method.
|
||||
|
||||
use rustc::mir;
|
||||
use rustc::ty::layout::LayoutOf;
|
||||
use rustc::mir::interpret::{EvalResult, Scalar};
|
||||
|
||||
use rustc::mir::interpret::EvalResult;
|
||||
use super::{EvalContext, Machine};
|
||||
|
||||
/// Classify whether an operator is "left-homogeneous", i.e. the LHS has the
|
||||
/// same type as the result.
|
||||
#[inline]
|
||||
fn binop_left_homogeneous(op: mir::BinOp) -> bool {
|
||||
use rustc::mir::BinOp::*;
|
||||
match op {
|
||||
Add | Sub | Mul | Div | Rem | BitXor | BitAnd | BitOr |
|
||||
Offset | Shl | Shr =>
|
||||
true,
|
||||
Eq | Ne | Lt | Le | Gt | Ge =>
|
||||
false,
|
||||
}
|
||||
}
|
||||
/// Classify whether an operator is "right-homogeneous", i.e. the RHS has the
|
||||
/// same type as the LHS.
|
||||
#[inline]
|
||||
fn binop_right_homogeneous(op: mir::BinOp) -> bool {
|
||||
use rustc::mir::BinOp::*;
|
||||
match op {
|
||||
Add | Sub | Mul | Div | Rem | BitXor | BitAnd | BitOr |
|
||||
Eq | Ne | Lt | Le | Gt | Ge =>
|
||||
true,
|
||||
Offset | Shl | Shr =>
|
||||
false,
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
||||
pub fn inc_step_counter_and_detect_loops(&mut self) -> EvalResult<'tcx, ()> {
|
||||
/// The number of steps between loop detector snapshots.
|
||||
|
@ -66,7 +94,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
|||
}
|
||||
|
||||
fn statement(&mut self, stmt: &mir::Statement<'tcx>) -> EvalResult<'tcx> {
|
||||
trace!("{:?}", stmt);
|
||||
debug!("{:?}", stmt);
|
||||
|
||||
use rustc::mir::StatementKind::*;
|
||||
|
||||
|
@ -84,8 +112,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
|||
variant_index,
|
||||
} => {
|
||||
let dest = self.eval_place(place)?;
|
||||
let dest_ty = self.place_ty(place);
|
||||
self.write_discriminant_value(dest_ty, dest, variant_index)?;
|
||||
self.write_discriminant_value(variant_index, dest)?;
|
||||
}
|
||||
|
||||
// Mark locals as alive
|
||||
|
@ -96,7 +123,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
|||
|
||||
// Mark locals as dead
|
||||
StorageDead(local) => {
|
||||
let old_val = self.frame_mut().storage_dead(local);
|
||||
let old_val = self.storage_dead(local);
|
||||
self.deallocate_local(old_val)?;
|
||||
}
|
||||
|
||||
|
@ -127,13 +154,172 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Evaluate an assignment statement.
|
||||
///
|
||||
/// There is no separate `eval_rvalue` function. Instead, the code for handling each rvalue
|
||||
/// type writes its results directly into the memory specified by the place.
|
||||
fn eval_rvalue_into_place(
|
||||
&mut self,
|
||||
rvalue: &mir::Rvalue<'tcx>,
|
||||
place: &mir::Place<'tcx>,
|
||||
) -> EvalResult<'tcx> {
|
||||
let dest = self.eval_place(place)?;
|
||||
|
||||
use rustc::mir::Rvalue::*;
|
||||
match *rvalue {
|
||||
Use(ref operand) => {
|
||||
// Avoid recomputing the layout
|
||||
let op = self.eval_operand(operand, Some(dest.layout))?;
|
||||
self.copy_op(op, dest)?;
|
||||
}
|
||||
|
||||
BinaryOp(bin_op, ref left, ref right) => {
|
||||
let layout = if binop_left_homogeneous(bin_op) { Some(dest.layout) } else { None };
|
||||
let left = self.eval_operand_and_read_value(left, layout)?;
|
||||
let layout = if binop_right_homogeneous(bin_op) { Some(left.layout) } else { None };
|
||||
let right = self.eval_operand_and_read_value(right, layout)?;
|
||||
self.binop_ignore_overflow(
|
||||
bin_op,
|
||||
left,
|
||||
right,
|
||||
dest,
|
||||
)?;
|
||||
}
|
||||
|
||||
CheckedBinaryOp(bin_op, ref left, ref right) => {
|
||||
// Due to the extra boolean in the result, we can never reuse the `dest.layout`.
|
||||
let left = self.eval_operand_and_read_value(left, None)?;
|
||||
let layout = if binop_right_homogeneous(bin_op) { Some(left.layout) } else { None };
|
||||
let right = self.eval_operand_and_read_value(right, layout)?;
|
||||
self.binop_with_overflow(
|
||||
bin_op,
|
||||
left,
|
||||
right,
|
||||
dest,
|
||||
)?;
|
||||
}
|
||||
|
||||
UnaryOp(un_op, ref operand) => {
|
||||
// The operand always has the same type as the result.
|
||||
let val = self.eval_operand_and_read_value(operand, Some(dest.layout))?;
|
||||
let val = self.unary_op(un_op, val.to_scalar()?, dest.layout)?;
|
||||
self.write_scalar(val, dest)?;
|
||||
}
|
||||
|
||||
Aggregate(ref kind, ref operands) => {
|
||||
let (dest, active_field_index) = match **kind {
|
||||
mir::AggregateKind::Adt(adt_def, variant_index, _, active_field_index) => {
|
||||
self.write_discriminant_value(variant_index, dest)?;
|
||||
if adt_def.is_enum() {
|
||||
(self.place_downcast(dest, variant_index)?, active_field_index)
|
||||
} else {
|
||||
(dest, active_field_index)
|
||||
}
|
||||
}
|
||||
_ => (dest, None)
|
||||
};
|
||||
|
||||
for (i, operand) in operands.iter().enumerate() {
|
||||
let op = self.eval_operand(operand, None)?;
|
||||
// Ignore zero-sized fields.
|
||||
if !op.layout.is_zst() {
|
||||
let field_index = active_field_index.unwrap_or(i);
|
||||
let field_dest = self.place_field(dest, field_index as u64)?;
|
||||
self.copy_op(op, field_dest)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Repeat(ref operand, _) => {
|
||||
let op = self.eval_operand(operand, None)?;
|
||||
let dest = self.force_allocation(dest)?;
|
||||
let length = dest.len();
|
||||
|
||||
if length > 0 {
|
||||
// write the first
|
||||
let first = self.mplace_field(dest, 0)?;
|
||||
self.copy_op(op, first.into())?;
|
||||
|
||||
if length > 1 {
|
||||
// copy the rest
|
||||
let (dest, dest_align) = first.to_scalar_ptr_align();
|
||||
let rest = dest.ptr_offset(first.layout.size, &self)?;
|
||||
self.memory.copy_repeatedly(
|
||||
dest, dest_align, rest, dest_align, first.layout.size, length - 1, true
|
||||
)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Len(ref place) => {
|
||||
// FIXME(CTFE): don't allow computing the length of arrays in const eval
|
||||
let src = self.eval_place(place)?;
|
||||
let mplace = self.force_allocation(src)?;
|
||||
let len = mplace.len();
|
||||
let size = self.memory.pointer_size().bytes() as u8;
|
||||
self.write_scalar(
|
||||
Scalar::Bits {
|
||||
bits: len as u128,
|
||||
size,
|
||||
},
|
||||
dest,
|
||||
)?;
|
||||
}
|
||||
|
||||
Ref(_, _, ref place) => {
|
||||
let src = self.eval_place(place)?;
|
||||
let val = self.force_allocation(src)?.to_ref(&self);
|
||||
self.write_value(val, dest)?;
|
||||
}
|
||||
|
||||
NullaryOp(mir::NullOp::Box, _) => {
|
||||
M::box_alloc(self, dest)?;
|
||||
}
|
||||
|
||||
NullaryOp(mir::NullOp::SizeOf, ty) => {
|
||||
let ty = self.monomorphize(ty, self.substs());
|
||||
let layout = self.layout_of(ty)?;
|
||||
assert!(!layout.is_unsized(),
|
||||
"SizeOf nullary MIR operator called for unsized type");
|
||||
let size = self.memory.pointer_size().bytes() as u8;
|
||||
self.write_scalar(
|
||||
Scalar::Bits {
|
||||
bits: layout.size.bytes() as u128,
|
||||
size,
|
||||
},
|
||||
dest,
|
||||
)?;
|
||||
}
|
||||
|
||||
Cast(kind, ref operand, cast_ty) => {
|
||||
debug_assert_eq!(self.monomorphize(cast_ty, self.substs()), dest.layout.ty);
|
||||
let src = self.eval_operand(operand, None)?;
|
||||
self.cast(src, kind, dest)?;
|
||||
}
|
||||
|
||||
Discriminant(ref place) => {
|
||||
let place = self.eval_place(place)?;
|
||||
let discr_val = self.read_discriminant_value(self.place_to_op(place)?)?;
|
||||
let size = dest.layout.size.bytes() as u8;
|
||||
self.write_scalar(Scalar::Bits {
|
||||
bits: discr_val,
|
||||
size,
|
||||
}, dest)?;
|
||||
}
|
||||
}
|
||||
|
||||
self.dump_place(*dest);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn terminator(&mut self, terminator: &mir::Terminator<'tcx>) -> EvalResult<'tcx> {
|
||||
trace!("{:?}", terminator.kind);
|
||||
debug!("{:?}", terminator.kind);
|
||||
self.tcx.span = terminator.source_info.span;
|
||||
self.memory.tcx.span = terminator.source_info.span;
|
||||
self.eval_terminator(terminator)?;
|
||||
if !self.stack.is_empty() {
|
||||
trace!("// {:?}", self.frame().block);
|
||||
debug!("// {:?}", self.frame().block);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -1,77 +1,54 @@
|
|||
use rustc::mir::BasicBlock;
|
||||
use rustc::ty::{self, Ty};
|
||||
use rustc::ty::{self, layout::LayoutOf};
|
||||
use syntax::source_map::Span;
|
||||
|
||||
use rustc::mir::interpret::{EvalResult, Value};
|
||||
use interpret::{Machine, ValTy, EvalContext, Place, PlaceExtra};
|
||||
use rustc::mir::interpret::EvalResult;
|
||||
use interpret::{Machine, EvalContext, PlaceTy, PlaceExtra, OpTy, Operand};
|
||||
|
||||
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
||||
pub(crate) fn drop_place(
|
||||
pub(crate) fn drop_in_place(
|
||||
&mut self,
|
||||
place: Place,
|
||||
place: PlaceTy<'tcx>,
|
||||
instance: ty::Instance<'tcx>,
|
||||
ty: Ty<'tcx>,
|
||||
span: Span,
|
||||
target: BasicBlock,
|
||||
) -> EvalResult<'tcx> {
|
||||
trace!("drop_place: {:#?}", place);
|
||||
trace!("drop_in_place: {:?},\n {:?}, {:?}", *place, place.layout.ty, instance);
|
||||
// We take the address of the object. This may well be unaligned, which is fine for us here.
|
||||
// However, unaligned accesses will probably make the actual drop implementation fail -- a problem shared
|
||||
// by rustc.
|
||||
let val = match self.force_allocation(place)? {
|
||||
Place::Ptr {
|
||||
ptr,
|
||||
align: _,
|
||||
extra: PlaceExtra::Vtable(vtable),
|
||||
} => ptr.to_value_with_vtable(vtable),
|
||||
Place::Ptr {
|
||||
ptr,
|
||||
align: _,
|
||||
extra: PlaceExtra::Length(len),
|
||||
} => ptr.to_value_with_len(len, self.tcx.tcx),
|
||||
Place::Ptr {
|
||||
ptr,
|
||||
align: _,
|
||||
extra: PlaceExtra::None,
|
||||
} => Value::Scalar(ptr),
|
||||
_ => bug!("force_allocation broken"),
|
||||
};
|
||||
self.drop(val, instance, ty, span, target)
|
||||
}
|
||||
let place = self.force_allocation(place)?;
|
||||
|
||||
fn drop(
|
||||
&mut self,
|
||||
arg: Value,
|
||||
instance: ty::Instance<'tcx>,
|
||||
ty: Ty<'tcx>,
|
||||
span: Span,
|
||||
target: BasicBlock,
|
||||
) -> EvalResult<'tcx> {
|
||||
trace!("drop: {:#?}, {:?}, {:?}", arg, ty.sty, instance.def);
|
||||
|
||||
let instance = match ty.sty {
|
||||
let (instance, place) = match place.layout.ty.sty {
|
||||
ty::TyDynamic(..) => {
|
||||
if let Value::ScalarPair(_, vtable) = arg {
|
||||
self.read_drop_type_from_vtable(vtable.unwrap_or_err()?.to_ptr()?)?
|
||||
} else {
|
||||
bug!("expected fat ptr, got {:?}", arg);
|
||||
// Dropping a trait object.
|
||||
let vtable = match place.extra {
|
||||
PlaceExtra::Vtable(vtable) => vtable,
|
||||
_ => bug!("Expected vtable when dropping {:#?}", place),
|
||||
};
|
||||
let place = self.unpack_unsized_mplace(place)?;
|
||||
let instance = self.read_drop_type_from_vtable(vtable)?;
|
||||
(instance, place)
|
||||
}
|
||||
}
|
||||
_ => instance,
|
||||
_ => (instance, place),
|
||||
};
|
||||
|
||||
// the drop function expects a reference to the value
|
||||
let valty = ValTy {
|
||||
value: arg,
|
||||
ty: self.tcx.mk_mut_ptr(ty),
|
||||
let fn_sig = instance.ty(*self.tcx).fn_sig(*self.tcx);
|
||||
let fn_sig = self.tcx.normalize_erasing_late_bound_regions(self.param_env, &fn_sig);
|
||||
|
||||
let arg = OpTy {
|
||||
op: Operand::Immediate(place.to_ref(&self)),
|
||||
layout: self.layout_of(self.tcx.mk_mut_ptr(place.layout.ty))?,
|
||||
};
|
||||
|
||||
let fn_sig = self.tcx.fn_sig(instance.def_id()).skip_binder().clone();
|
||||
// This should always be (), but getting it from the sig seems
|
||||
// easier than creating a layout of ().
|
||||
let dest = PlaceTy::null(&self, self.layout_of(fn_sig.output())?);
|
||||
|
||||
self.eval_fn_call(
|
||||
instance,
|
||||
Some((Place::undef(), target)),
|
||||
&[valty],
|
||||
Some((dest, target)),
|
||||
&[arg],
|
||||
span,
|
||||
fn_sig,
|
||||
)
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
use rustc::mir;
|
||||
use rustc::ty::{self, Ty};
|
||||
use rustc::ty::layout::{LayoutOf, Size};
|
||||
use rustc::ty::layout::LayoutOf;
|
||||
use syntax::source_map::Span;
|
||||
use rustc_target::spec::abi::Abi;
|
||||
|
||||
use rustc::mir::interpret::{EvalResult, Scalar, Value};
|
||||
use super::{EvalContext, Place, Machine, ValTy};
|
||||
use rustc::mir::interpret::{EvalResult, Scalar};
|
||||
use super::{EvalContext, Machine, Value, OpTy, PlaceTy, ValTy, Operand};
|
||||
|
||||
use rustc_data_structures::indexed_vec::Idx;
|
||||
use interpret::memory::HasMemory;
|
||||
|
||||
mod drop;
|
||||
|
||||
|
@ -25,7 +24,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
|||
use rustc::mir::TerminatorKind::*;
|
||||
match terminator.kind {
|
||||
Return => {
|
||||
self.dump_local(self.frame().return_place);
|
||||
self.dump_place(self.frame().return_place);
|
||||
self.pop_stack_frame()?
|
||||
}
|
||||
|
||||
|
@ -37,22 +36,21 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
|||
ref targets,
|
||||
..
|
||||
} => {
|
||||
let discr_val = self.eval_operand(discr)?;
|
||||
let discr_prim = self.value_to_scalar(discr_val)?;
|
||||
let discr_layout = self.layout_of(discr_val.ty).unwrap();
|
||||
trace!("SwitchInt({:?}, {:#?})", discr_prim, discr_layout);
|
||||
let discr_val = self.eval_operand(discr, None)?;
|
||||
let discr = self.read_value(discr_val)?;
|
||||
trace!("SwitchInt({:?})", *discr);
|
||||
|
||||
// Branch to the `otherwise` case by default, if no match is found.
|
||||
let mut target_block = targets[targets.len() - 1];
|
||||
|
||||
for (index, &const_int) in values.iter().enumerate() {
|
||||
// Compare using binary_op
|
||||
let const_int = Scalar::Bits { bits: const_int, size: discr_layout.size.bytes() as u8 };
|
||||
let res = self.binary_op(mir::BinOp::Eq,
|
||||
discr_prim, discr_val.ty,
|
||||
const_int, discr_val.ty
|
||||
let const_int = Scalar::Bits { bits: const_int, size: discr.layout.size.bytes() as u8 };
|
||||
let (res, _) = self.binary_op(mir::BinOp::Eq,
|
||||
discr,
|
||||
ValTy { value: Value::Scalar(const_int.into()), layout: discr.layout }
|
||||
)?;
|
||||
if res.0.to_bits(Size::from_bytes(1))? != 0 {
|
||||
if res.to_bool()? {
|
||||
target_block = targets[index];
|
||||
break;
|
||||
}
|
||||
|
@ -72,10 +70,10 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
|||
None => None,
|
||||
};
|
||||
|
||||
let func = self.eval_operand(func)?;
|
||||
let (fn_def, sig) = match func.ty.sty {
|
||||
let func = self.eval_operand(func, None)?;
|
||||
let (fn_def, sig) = match func.layout.ty.sty {
|
||||
ty::TyFnPtr(sig) => {
|
||||
let fn_ptr = self.value_to_scalar(func)?.to_ptr()?;
|
||||
let fn_ptr = self.read_scalar(func)?.to_ptr()?;
|
||||
let instance = self.memory.get_fn(fn_ptr)?;
|
||||
let instance_ty = instance.ty(*self.tcx);
|
||||
match instance_ty.sty {
|
||||
|
@ -99,14 +97,14 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
|||
}
|
||||
ty::TyFnDef(def_id, substs) => (
|
||||
self.resolve(def_id, substs)?,
|
||||
func.ty.fn_sig(*self.tcx),
|
||||
func.layout.ty.fn_sig(*self.tcx),
|
||||
),
|
||||
_ => {
|
||||
let msg = format!("can't handle callee of type {:?}", func.ty);
|
||||
let msg = format!("can't handle callee of type {:?}", func.layout.ty);
|
||||
return err!(Unimplemented(msg));
|
||||
}
|
||||
};
|
||||
let args = self.operands_to_args(args)?;
|
||||
let args = self.eval_operands(args)?;
|
||||
let sig = self.tcx.normalize_erasing_late_bound_regions(
|
||||
ty::ParamEnv::reveal_all(),
|
||||
&sig,
|
||||
|
@ -114,7 +112,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
|||
self.eval_fn_call(
|
||||
fn_def,
|
||||
destination,
|
||||
&args,
|
||||
&args[..],
|
||||
terminator.source_info.span,
|
||||
sig,
|
||||
)?;
|
||||
|
@ -127,19 +125,13 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
|||
} => {
|
||||
// FIXME(CTFE): forbid drop in const eval
|
||||
let place = self.eval_place(location)?;
|
||||
let ty = self.place_ty(location);
|
||||
let ty = self.tcx.subst_and_normalize_erasing_regions(
|
||||
self.substs(),
|
||||
ty::ParamEnv::reveal_all(),
|
||||
&ty,
|
||||
);
|
||||
let ty = place.layout.ty;
|
||||
trace!("TerminatorKind::drop: {:?}, type {}", location, ty);
|
||||
|
||||
let instance = ::monomorphize::resolve_drop_in_place(*self.tcx, ty);
|
||||
self.drop_place(
|
||||
self.drop_in_place(
|
||||
place,
|
||||
instance,
|
||||
ty,
|
||||
terminator.source_info.span,
|
||||
target,
|
||||
)?;
|
||||
|
@ -152,18 +144,18 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
|||
target,
|
||||
..
|
||||
} => {
|
||||
let cond_val = self.eval_operand_to_scalar(cond)?.to_bool()?;
|
||||
let cond_val = self.eval_operand_and_read_value(cond, None)?.to_scalar()?.to_bool()?;
|
||||
if expected == cond_val {
|
||||
self.goto_block(target);
|
||||
} else {
|
||||
use rustc::mir::interpret::EvalErrorKind::*;
|
||||
return match *msg {
|
||||
BoundsCheck { ref len, ref index } => {
|
||||
let len = self.eval_operand_to_scalar(len)
|
||||
.expect("can't eval len")
|
||||
let len = self.eval_operand_and_read_value(len, None)
|
||||
.expect("can't eval len").to_scalar()?
|
||||
.to_bits(self.memory().pointer_size())? as u64;
|
||||
let index = self.eval_operand_to_scalar(index)
|
||||
.expect("can't eval index")
|
||||
let index = self.eval_operand_and_read_value(index, None)
|
||||
.expect("can't eval index").to_scalar()?
|
||||
.to_bits(self.memory().pointer_size())? as u64;
|
||||
err!(BoundsCheck { len, index })
|
||||
}
|
||||
|
@ -259,36 +251,37 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
|||
fn eval_fn_call(
|
||||
&mut self,
|
||||
instance: ty::Instance<'tcx>,
|
||||
destination: Option<(Place, mir::BasicBlock)>,
|
||||
args: &[ValTy<'tcx>],
|
||||
destination: Option<(PlaceTy<'tcx>, mir::BasicBlock)>,
|
||||
args: &[OpTy<'tcx>],
|
||||
span: Span,
|
||||
sig: ty::FnSig<'tcx>,
|
||||
) -> EvalResult<'tcx> {
|
||||
trace!("eval_fn_call: {:#?}", instance);
|
||||
if let Some((place, _)) = destination {
|
||||
assert_eq!(place.layout.ty, sig.output());
|
||||
}
|
||||
match instance.def {
|
||||
ty::InstanceDef::Intrinsic(..) => {
|
||||
let (ret, target) = match destination {
|
||||
Some(dest) => dest,
|
||||
_ => return err!(Unreachable),
|
||||
};
|
||||
let ty = sig.output();
|
||||
let layout = self.layout_of(ty)?;
|
||||
M::call_intrinsic(self, instance, args, ret, layout, target)?;
|
||||
self.dump_local(ret);
|
||||
M::call_intrinsic(self, instance, args, ret, target)?;
|
||||
self.dump_place(*ret);
|
||||
Ok(())
|
||||
}
|
||||
// FIXME: figure out why we can't just go through the shim
|
||||
ty::InstanceDef::ClosureOnceShim { .. } => {
|
||||
if M::eval_fn_call(self, instance, destination, args, span, sig)? {
|
||||
if M::eval_fn_call(self, instance, destination, args, span)? {
|
||||
return Ok(());
|
||||
}
|
||||
let mut arg_locals = self.frame().mir.args_iter();
|
||||
match sig.abi {
|
||||
// closure as closure once
|
||||
Abi::RustCall => {
|
||||
for (arg_local, &valty) in arg_locals.zip(args) {
|
||||
for (arg_local, &op) in arg_locals.zip(args) {
|
||||
let dest = self.eval_place(&mir::Place::Local(arg_local))?;
|
||||
self.write_value(valty, dest)?;
|
||||
self.copy_op(op, dest)?;
|
||||
}
|
||||
}
|
||||
// non capture closure as fn ptr
|
||||
|
@ -296,17 +289,17 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
|||
// and need to pack arguments
|
||||
Abi::Rust => {
|
||||
trace!(
|
||||
"arg_locals: {:#?}",
|
||||
self.frame().mir.args_iter().collect::<Vec<_>>()
|
||||
"args: {:#?}",
|
||||
self.frame().mir.args_iter().zip(args.iter())
|
||||
.map(|(local, arg)| (local, **arg, arg.layout.ty)).collect::<Vec<_>>()
|
||||
);
|
||||
trace!("args: {:#?}", args);
|
||||
let local = arg_locals.nth(1).unwrap();
|
||||
for (i, &valty) in args.into_iter().enumerate() {
|
||||
for (i, &op) in args.into_iter().enumerate() {
|
||||
let dest = self.eval_place(&mir::Place::Local(local).field(
|
||||
mir::Field::new(i),
|
||||
valty.ty,
|
||||
op.layout.ty,
|
||||
))?;
|
||||
self.write_value(valty, dest)?;
|
||||
self.copy_op(op, dest)?;
|
||||
}
|
||||
}
|
||||
_ => bug!("bad ABI for ClosureOnceShim: {:?}", sig.abi),
|
||||
|
@ -318,7 +311,10 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
|||
ty::InstanceDef::CloneShim(..) |
|
||||
ty::InstanceDef::Item(_) => {
|
||||
// Push the stack frame, and potentially be entirely done if the call got hooked
|
||||
if M::eval_fn_call(self, instance, destination, args, span, sig)? {
|
||||
if M::eval_fn_call(self, instance, destination, args, span)? {
|
||||
// TODO: Can we make it return the frame to push, instead
|
||||
// of the hook doing half of the work and us doing the argument
|
||||
// initialization?
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
|
@ -326,10 +322,10 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
|||
let mut arg_locals = self.frame().mir.args_iter();
|
||||
trace!("ABI: {:?}", sig.abi);
|
||||
trace!(
|
||||
"arg_locals: {:#?}",
|
||||
self.frame().mir.args_iter().collect::<Vec<_>>()
|
||||
"args: {:#?}",
|
||||
self.frame().mir.args_iter().zip(args.iter())
|
||||
.map(|(local, arg)| (local, **arg, arg.layout.ty)).collect::<Vec<_>>()
|
||||
);
|
||||
trace!("args: {:#?}", args);
|
||||
match sig.abi {
|
||||
Abi::RustCall => {
|
||||
assert_eq!(args.len(), 2);
|
||||
|
@ -338,26 +334,21 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
|||
// write first argument
|
||||
let first_local = arg_locals.next().unwrap();
|
||||
let dest = self.eval_place(&mir::Place::Local(first_local))?;
|
||||
self.write_value(args[0], dest)?;
|
||||
self.copy_op(args[0], dest)?;
|
||||
}
|
||||
|
||||
// unpack and write all other args
|
||||
let layout = self.layout_of(args[1].ty)?;
|
||||
if let ty::TyTuple(_) = args[1].ty.sty {
|
||||
let layout = args[1].layout;
|
||||
if let ty::TyTuple(_) = layout.ty.sty {
|
||||
if layout.is_zst() {
|
||||
// Nothing to do, no need to unpack zsts
|
||||
return Ok(());
|
||||
}
|
||||
if self.frame().mir.args_iter().count() == layout.fields.count() + 1 {
|
||||
for (i, arg_local) in arg_locals.enumerate() {
|
||||
let field = mir::Field::new(i);
|
||||
let (value, layout) = self.read_field(args[1].value, None, field, layout)?;
|
||||
let arg = self.operand_field(args[1], i as u64)?;
|
||||
let dest = self.eval_place(&mir::Place::Local(arg_local))?;
|
||||
let valty = ValTy {
|
||||
value,
|
||||
ty: layout.ty,
|
||||
};
|
||||
self.write_value(valty, dest)?;
|
||||
self.copy_op(arg, dest)?;
|
||||
}
|
||||
} else {
|
||||
trace!("manual impl of rust-call ABI");
|
||||
|
@ -365,20 +356,19 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
|||
let dest = self.eval_place(
|
||||
&mir::Place::Local(arg_locals.next().unwrap()),
|
||||
)?;
|
||||
self.write_value(args[1], dest)?;
|
||||
self.copy_op(args[1], dest)?;
|
||||
}
|
||||
} else {
|
||||
bug!(
|
||||
"rust-call ABI tuple argument was {:#?}, {:#?}",
|
||||
args[1].ty,
|
||||
"rust-call ABI tuple argument was {:#?}",
|
||||
layout
|
||||
);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
for (arg_local, &valty) in arg_locals.zip(args) {
|
||||
for (arg_local, &op) in arg_locals.zip(args) {
|
||||
let dest = self.eval_place(&mir::Place::Local(arg_local))?;
|
||||
self.write_value(valty, dest)?;
|
||||
self.copy_op(op, dest)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -388,16 +378,22 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
|||
ty::InstanceDef::Virtual(_, idx) => {
|
||||
let ptr_size = self.memory.pointer_size();
|
||||
let ptr_align = self.tcx.data_layout.pointer_align;
|
||||
let (ptr, vtable) = self.into_ptr_vtable_pair(args[0].value)?;
|
||||
let (ptr, vtable) = self.read_value(args[0])?.to_scalar_dyn_trait()?;
|
||||
let fn_ptr = self.memory.read_ptr_sized(
|
||||
vtable.offset(ptr_size * (idx as u64 + 3), &self)?,
|
||||
ptr_align
|
||||
)?.unwrap_or_err()?.to_ptr()?;
|
||||
)?.to_ptr()?;
|
||||
let instance = self.memory.get_fn(fn_ptr)?;
|
||||
|
||||
// We have to patch the self argument, in particular get the layout
|
||||
// expected by the actual function. Cannot just use "field 0" due to
|
||||
// Box<self>.
|
||||
let mut args = args.to_vec();
|
||||
let ty = self.layout_of(args[0].ty)?.field(&self, 0)?.ty;
|
||||
args[0].ty = ty;
|
||||
args[0].value = Value::Scalar(ptr);
|
||||
let pointee = args[0].layout.ty.builtin_deref(true).unwrap().ty;
|
||||
let fake_fat_ptr_ty = self.tcx.mk_mut_ptr(pointee);
|
||||
args[0].layout = self.layout_of(fake_fat_ptr_ty)?.field(&self, 0)?;
|
||||
args[0].op = Operand::Immediate(Value::Scalar(ptr.into())); // strip vtable
|
||||
trace!("Patched self operand to {:#?}", args[0]);
|
||||
// recurse with concrete function
|
||||
self.eval_fn_call(instance, destination, &args, span, sig)
|
||||
}
|
||||
|
|
|
@ -36,15 +36,15 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
|||
|
||||
let drop = ::monomorphize::resolve_drop_in_place(*self.tcx, ty);
|
||||
let drop = self.memory.create_fn_alloc(drop);
|
||||
self.memory.write_ptr_sized_unsigned(vtable, ptr_align, Scalar::Ptr(drop).into())?;
|
||||
self.memory.write_ptr_sized(vtable, ptr_align, Scalar::Ptr(drop).into())?;
|
||||
|
||||
let size_ptr = vtable.offset(ptr_size, &self)?;
|
||||
self.memory.write_ptr_sized_unsigned(size_ptr, ptr_align, Scalar::Bits {
|
||||
self.memory.write_ptr_sized(size_ptr, ptr_align, Scalar::Bits {
|
||||
bits: size as u128,
|
||||
size: ptr_size.bytes() as u8,
|
||||
}.into())?;
|
||||
let align_ptr = vtable.offset(ptr_size * 2, &self)?;
|
||||
self.memory.write_ptr_sized_unsigned(align_ptr, ptr_align, Scalar::Bits {
|
||||
self.memory.write_ptr_sized(align_ptr, ptr_align, Scalar::Bits {
|
||||
bits: align as u128,
|
||||
size: ptr_size.bytes() as u8,
|
||||
}.into())?;
|
||||
|
@ -54,7 +54,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
|||
let instance = self.resolve(def_id, substs)?;
|
||||
let fn_ptr = self.memory.create_fn_alloc(instance);
|
||||
let method_ptr = vtable.offset(ptr_size * (3 + i as u64), &self)?;
|
||||
self.memory.write_ptr_sized_unsigned(method_ptr, ptr_align, Scalar::Ptr(fn_ptr).into())?;
|
||||
self.memory.write_ptr_sized(method_ptr, ptr_align, Scalar::Ptr(fn_ptr).into())?;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -72,7 +72,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
|||
) -> EvalResult<'tcx, ty::Instance<'tcx>> {
|
||||
// we don't care about the pointee type, we just want a pointer
|
||||
let pointer_align = self.tcx.data_layout.pointer_align;
|
||||
let drop_fn = self.memory.read_ptr_sized(vtable, pointer_align)?.unwrap_or_err()?.to_ptr()?;
|
||||
let drop_fn = self.memory.read_ptr_sized(vtable, pointer_align)?.to_ptr()?;
|
||||
self.memory.get_fn(drop_fn)
|
||||
}
|
||||
|
||||
|
@ -82,11 +82,11 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
|||
) -> EvalResult<'tcx, (Size, Align)> {
|
||||
let pointer_size = self.memory.pointer_size();
|
||||
let pointer_align = self.tcx.data_layout.pointer_align;
|
||||
let size = self.memory.read_ptr_sized(vtable.offset(pointer_size, self)?, pointer_align)?.unwrap_or_err()?.to_bits(pointer_size)? as u64;
|
||||
let size = self.memory.read_ptr_sized(vtable.offset(pointer_size, self)?, pointer_align)?.to_bits(pointer_size)? as u64;
|
||||
let align = self.memory.read_ptr_sized(
|
||||
vtable.offset(pointer_size * 2, self)?,
|
||||
pointer_align
|
||||
)?.unwrap_or_err()?.to_bits(pointer_size)? as u64;
|
||||
)?.to_bits(pointer_size)? as u64;
|
||||
Ok((Size::from_bytes(size), Align::from_bytes(align, align).unwrap()))
|
||||
}
|
||||
}
|
||||
|
|
348
src/librustc_mir/interpret/validity.rs
Normal file
348
src/librustc_mir/interpret/validity.rs
Normal file
|
@ -0,0 +1,348 @@
|
|||
use std::fmt::Write;
|
||||
|
||||
use syntax_pos::symbol::Symbol;
|
||||
use rustc::ty::layout::{self, Size, Primitive};
|
||||
use rustc::ty::{self, Ty};
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc::mir::interpret::{
|
||||
Scalar, AllocType, EvalResult, ScalarMaybeUndef, EvalErrorKind
|
||||
};
|
||||
|
||||
use super::{
|
||||
MPlaceTy, Machine, EvalContext
|
||||
};
|
||||
|
||||
macro_rules! validation_failure{
|
||||
($what:expr, $where:expr, $details:expr) => {{
|
||||
let where_ = path_format($where);
|
||||
let where_ = if where_.is_empty() {
|
||||
String::new()
|
||||
} else {
|
||||
format!(" at {}", where_)
|
||||
};
|
||||
err!(ValidationFailure(format!(
|
||||
"encountered {}{}, but expected {}",
|
||||
$what, where_, $details,
|
||||
)))
|
||||
}};
|
||||
($what:expr, $where:expr) => {{
|
||||
let where_ = path_format($where);
|
||||
let where_ = if where_.is_empty() {
|
||||
String::new()
|
||||
} else {
|
||||
format!(" at {}", where_)
|
||||
};
|
||||
err!(ValidationFailure(format!(
|
||||
"encountered {}{}",
|
||||
$what, where_,
|
||||
)))
|
||||
}};
|
||||
}
|
||||
|
||||
/// We want to show a nice path to the invalid field for diagnotsics,
|
||||
/// but avoid string operations in the happy case where no error happens.
|
||||
/// So we track a `Vec<PathElem>` where `PathElem` contains all the data we
|
||||
/// need to later print something for the user.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum PathElem {
|
||||
Field(Symbol),
|
||||
ClosureVar(Symbol),
|
||||
ArrayElem(usize),
|
||||
TupleElem(usize),
|
||||
Deref,
|
||||
Tag,
|
||||
}
|
||||
|
||||
// Adding a Deref and making a copy of the path to be put into the queue
|
||||
// always go together. This one does it with only new allocation.
|
||||
fn path_clone_and_deref(path: &Vec<PathElem>) -> Vec<PathElem> {
|
||||
let mut new_path = Vec::with_capacity(path.len()+1);
|
||||
new_path.clone_from(path);
|
||||
new_path.push(PathElem::Deref);
|
||||
new_path
|
||||
}
|
||||
|
||||
/// Format a path
|
||||
fn path_format(path: &Vec<PathElem>) -> String {
|
||||
use self::PathElem::*;
|
||||
|
||||
let mut out = String::new();
|
||||
for elem in path.iter() {
|
||||
match elem {
|
||||
Field(name) => write!(out, ".{}", name).unwrap(),
|
||||
ClosureVar(name) => write!(out, ".<closure-var({})>", name).unwrap(),
|
||||
TupleElem(idx) => write!(out, ".{}", idx).unwrap(),
|
||||
ArrayElem(idx) => write!(out, "[{}]", idx).unwrap(),
|
||||
Deref =>
|
||||
// This does not match Rust syntax, but it is more readable for long paths -- and
|
||||
// some of the other items here also are not Rust syntax. Actually we can't
|
||||
// even use the usual syntax because we are just showing the projections,
|
||||
// not the root.
|
||||
write!(out, ".<deref>").unwrap(),
|
||||
Tag => write!(out, ".<enum-tag>").unwrap(),
|
||||
}
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
||||
fn validate_scalar(
|
||||
&self,
|
||||
value: ScalarMaybeUndef,
|
||||
size: Size,
|
||||
scalar: &layout::Scalar,
|
||||
path: &Vec<PathElem>,
|
||||
ty: Ty,
|
||||
) -> EvalResult<'tcx> {
|
||||
trace!("validate scalar: {:#?}, {:#?}, {:#?}, {}", value, size, scalar, ty);
|
||||
let (lo, hi) = scalar.valid_range.clone().into_inner();
|
||||
|
||||
let value = match value {
|
||||
ScalarMaybeUndef::Scalar(scalar) => scalar,
|
||||
ScalarMaybeUndef::Undef => return validation_failure!("undefined bytes", path),
|
||||
};
|
||||
|
||||
let bits = match value {
|
||||
Scalar::Bits { bits, size: value_size } => {
|
||||
assert_eq!(value_size as u64, size.bytes());
|
||||
bits
|
||||
},
|
||||
Scalar::Ptr(_) => {
|
||||
let ptr_size = self.memory.pointer_size();
|
||||
let ptr_max = u128::max_value() >> (128 - ptr_size.bits());
|
||||
return if lo > hi {
|
||||
if lo - hi == 1 {
|
||||
// no gap, all values are ok
|
||||
Ok(())
|
||||
} else if hi < ptr_max || lo > 1 {
|
||||
let max = u128::max_value() >> (128 - size.bits());
|
||||
validation_failure!(
|
||||
"pointer",
|
||||
path,
|
||||
format!("something in the range {:?} or {:?}", 0..=lo, hi..=max)
|
||||
)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
} else if hi < ptr_max || lo > 1 {
|
||||
validation_failure!(
|
||||
"pointer",
|
||||
path,
|
||||
format!("something in the range {:?}", scalar.valid_range)
|
||||
)
|
||||
} else {
|
||||
Ok(())
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
// char gets a special treatment, because its number space is not contiguous so `TyLayout`
|
||||
// has no special checks for chars
|
||||
match ty.sty {
|
||||
ty::TyChar => {
|
||||
debug_assert_eq!(size.bytes(), 4);
|
||||
if ::std::char::from_u32(bits as u32).is_none() {
|
||||
return validation_failure!(
|
||||
"character",
|
||||
path,
|
||||
"a valid unicode codepoint"
|
||||
);
|
||||
}
|
||||
}
|
||||
_ => {},
|
||||
}
|
||||
|
||||
use std::ops::RangeInclusive;
|
||||
let in_range = |bound: RangeInclusive<u128>| bound.contains(&bits);
|
||||
if lo > hi {
|
||||
if in_range(0..=hi) || in_range(lo..=u128::max_value()) {
|
||||
Ok(())
|
||||
} else {
|
||||
validation_failure!(
|
||||
bits,
|
||||
path,
|
||||
format!("something in the range {:?} or {:?}", ..=hi, lo..)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
if in_range(scalar.valid_range.clone()) {
|
||||
Ok(())
|
||||
} else {
|
||||
validation_failure!(
|
||||
bits,
|
||||
path,
|
||||
format!("something in the range {:?}", scalar.valid_range)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This function checks the memory where `dest` points to. The place must be sized
|
||||
/// (i.e., dest.extra == PlaceExtra::None).
|
||||
/// It will error if the bits at the destination do not match the ones described by the layout.
|
||||
/// The `path` may be pushed to, but the part that is present when the function
|
||||
/// starts must not be changed!
|
||||
pub fn validate_mplace(
|
||||
&self,
|
||||
dest: MPlaceTy<'tcx>,
|
||||
path: &mut Vec<PathElem>,
|
||||
seen: &mut FxHashSet<(MPlaceTy<'tcx>)>,
|
||||
todo: &mut Vec<(MPlaceTy<'tcx>, Vec<PathElem>)>,
|
||||
) -> EvalResult<'tcx> {
|
||||
self.memory.dump_alloc(dest.to_ptr()?.alloc_id);
|
||||
trace!("validate_mplace: {:?}, {:#?}", *dest, dest.layout);
|
||||
|
||||
// Find the right variant. We have to handle this as a prelude, not via
|
||||
// proper recursion with the new inner layout, to be able to later nicely
|
||||
// print the field names of the enum field that is being accessed.
|
||||
let (variant, dest) = match dest.layout.variants {
|
||||
layout::Variants::NicheFilling { niche: ref tag, .. } |
|
||||
layout::Variants::Tagged { ref tag, .. } => {
|
||||
let size = tag.value.size(self);
|
||||
// we first read the tag value as scalar, to be able to validate it
|
||||
let tag_mplace = self.mplace_field(dest, 0)?;
|
||||
let tag_value = self.read_scalar(tag_mplace.into())?;
|
||||
path.push(PathElem::Tag);
|
||||
self.validate_scalar(
|
||||
tag_value, size, tag, &path, tag_mplace.layout.ty
|
||||
)?;
|
||||
path.pop(); // remove the element again
|
||||
// then we read it again to get the index, to continue
|
||||
let variant = self.read_discriminant_as_variant_index(dest.into())?;
|
||||
let inner_dest = self.mplace_downcast(dest, variant)?;
|
||||
// Put the variant projection onto the path, as a field
|
||||
path.push(PathElem::Field(dest.layout.ty.ty_adt_def().unwrap().variants[variant].name));
|
||||
trace!("variant layout: {:#?}", dest.layout);
|
||||
(variant, inner_dest)
|
||||
},
|
||||
layout::Variants::Single { index } => {
|
||||
(index, dest)
|
||||
}
|
||||
};
|
||||
|
||||
// Remember the length, in case we need to truncate
|
||||
let path_len = path.len();
|
||||
|
||||
// Validate all fields
|
||||
match dest.layout.fields {
|
||||
// primitives are unions with zero fields
|
||||
// We still check `layout.fields`, not `layout.abi`, because `layout.abi`
|
||||
// is `Scalar` for newtypes around scalars, but we want to descend through the
|
||||
// fields to get a proper `path`.
|
||||
layout::FieldPlacement::Union(0) => {
|
||||
match dest.layout.abi {
|
||||
// nothing to do, whatever the pointer points to, it is never going to be read
|
||||
layout::Abi::Uninhabited =>
|
||||
return validation_failure!("a value of an uninhabited type", path),
|
||||
// check that the scalar is a valid pointer or that its bit range matches the
|
||||
// expectation.
|
||||
layout::Abi::Scalar(ref scalar_layout) => {
|
||||
let size = scalar_layout.value.size(self);
|
||||
let value = self.read_value(dest.into())?;
|
||||
let scalar = value.to_scalar_or_undef();
|
||||
self.validate_scalar(scalar, size, scalar_layout, &path, dest.layout.ty)?;
|
||||
if scalar_layout.value == Primitive::Pointer {
|
||||
// ignore integer pointers, we can't reason about the final hardware
|
||||
if let Scalar::Ptr(ptr) = scalar.not_undef()? {
|
||||
let alloc_kind = self.tcx.alloc_map.lock().get(ptr.alloc_id);
|
||||
if let Some(AllocType::Static(did)) = alloc_kind {
|
||||
// statics from other crates are already checked.
|
||||
// extern statics should not be validated as they have no body.
|
||||
if !did.is_local() || self.tcx.is_foreign_item(did) {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
if value.layout.ty.builtin_deref(false).is_some() {
|
||||
trace!("Recursing below ptr {:#?}", value);
|
||||
let ptr_place = self.ref_to_mplace(value)?;
|
||||
// we have not encountered this pointer+layout combination before
|
||||
if seen.insert(ptr_place) {
|
||||
todo.push((ptr_place, path_clone_and_deref(path)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => bug!("bad abi for FieldPlacement::Union(0): {:#?}", dest.layout.abi),
|
||||
}
|
||||
}
|
||||
layout::FieldPlacement::Union(_) => {
|
||||
// We can't check unions, their bits are allowed to be anything.
|
||||
// The fields don't need to correspond to any bit pattern of the union's fields.
|
||||
// See https://github.com/rust-lang/rust/issues/32836#issuecomment-406875389
|
||||
},
|
||||
layout::FieldPlacement::Array { .. } => {
|
||||
for (i, field) in self.mplace_array_fields(dest)?.enumerate() {
|
||||
let field = field?;
|
||||
path.push(PathElem::ArrayElem(i));
|
||||
self.validate_mplace(field, path, seen, todo)?;
|
||||
path.truncate(path_len);
|
||||
}
|
||||
},
|
||||
layout::FieldPlacement::Arbitrary { ref offsets, .. } => {
|
||||
// Fat pointers need special treatment.
|
||||
if dest.layout.ty.builtin_deref(true).is_some() {
|
||||
// This is a fat pointer.
|
||||
let ptr = match self.ref_to_mplace(self.read_value(dest.into())?) {
|
||||
Ok(ptr) => ptr,
|
||||
Err(err) => match err.kind {
|
||||
EvalErrorKind::ReadPointerAsBytes =>
|
||||
return validation_failure!(
|
||||
"fat pointer length is not a valid integer", path
|
||||
),
|
||||
EvalErrorKind::ReadBytesAsPointer =>
|
||||
return validation_failure!(
|
||||
"fat pointer vtable is not a valid pointer", path
|
||||
),
|
||||
_ => return Err(err),
|
||||
}
|
||||
};
|
||||
let unpacked_ptr = self.unpack_unsized_mplace(ptr)?;
|
||||
// for safe ptrs, recursively check it
|
||||
if !dest.layout.ty.is_unsafe_ptr() {
|
||||
trace!("Recursing below fat ptr {:?} (unpacked: {:?})", ptr, unpacked_ptr);
|
||||
if seen.insert(unpacked_ptr) {
|
||||
todo.push((unpacked_ptr, path_clone_and_deref(path)));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Not a pointer, perform regular aggregate handling below
|
||||
for i in 0..offsets.len() {
|
||||
let field = self.mplace_field(dest, i as u64)?;
|
||||
path.push(self.aggregate_field_path_elem(dest.layout.ty, variant, i));
|
||||
self.validate_mplace(field, path, seen, todo)?;
|
||||
path.truncate(path_len);
|
||||
}
|
||||
// FIXME: For a TyStr, check that this is valid UTF-8.
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn aggregate_field_path_elem(&self, ty: Ty<'tcx>, variant: usize, field: usize) -> PathElem {
|
||||
match ty.sty {
|
||||
// generators and closures.
|
||||
ty::TyClosure(def_id, _) | ty::TyGenerator(def_id, _, _) => {
|
||||
let node_id = self.tcx.hir.as_local_node_id(def_id).unwrap();
|
||||
let freevar = self.tcx.with_freevars(node_id, |fv| fv[field]);
|
||||
PathElem::ClosureVar(self.tcx.hir.name(freevar.var_id()))
|
||||
}
|
||||
|
||||
// tuples
|
||||
ty::TyTuple(_) => PathElem::TupleElem(field),
|
||||
|
||||
// enums
|
||||
ty::TyAdt(def, ..) if def.is_enum() => {
|
||||
let variant = &def.variants[variant];
|
||||
PathElem::Field(variant.fields[field].ident.name)
|
||||
}
|
||||
|
||||
// other ADTs
|
||||
ty::TyAdt(def, _) => PathElem::Field(def.non_enum_variant().fields[field].ident.name),
|
||||
|
||||
// nothing else has an aggregate layout
|
||||
_ => bug!("aggregate_field_path_elem: got non-aggregate type {:?}", ty),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -37,6 +37,7 @@ Rust MIR: a lowered representation of Rust. Also: an experiment!
|
|||
#![feature(step_trait)]
|
||||
#![feature(slice_concat_ext)]
|
||||
#![feature(if_while_or_patterns)]
|
||||
#![feature(try_from)]
|
||||
|
||||
#![recursion_limit="256"]
|
||||
|
||||
|
@ -82,7 +83,7 @@ pub fn provide(providers: &mut Providers) {
|
|||
shim::provide(providers);
|
||||
transform::provide(providers);
|
||||
providers.const_eval = interpret::const_eval_provider;
|
||||
providers.const_value_to_allocation = interpret::const_value_to_allocation_provider;
|
||||
providers.const_to_allocation = interpret::const_to_allocation_provider;
|
||||
providers.check_match = hair::pattern::check_match;
|
||||
}
|
||||
|
||||
|
|
|
@ -17,16 +17,16 @@ use rustc::mir::{Constant, Location, Place, Mir, Operand, Rvalue, Local};
|
|||
use rustc::mir::{NullOp, StatementKind, Statement, BasicBlock, LocalKind};
|
||||
use rustc::mir::{TerminatorKind, ClearCrossCrate, SourceInfo, BinOp, ProjectionElem};
|
||||
use rustc::mir::visit::{Visitor, PlaceContext};
|
||||
use rustc::mir::interpret::{ConstEvalErr, EvalErrorKind, ScalarMaybeUndef};
|
||||
use rustc::mir::interpret::{
|
||||
ConstEvalErr, EvalErrorKind, ScalarMaybeUndef, Scalar, GlobalId, EvalResult
|
||||
};
|
||||
use rustc::ty::{TyCtxt, self, Instance};
|
||||
use rustc::mir::interpret::{Value, Scalar, GlobalId, EvalResult};
|
||||
use interpret::EvalContext;
|
||||
use interpret::CompileTimeEvaluator;
|
||||
use interpret::{eval_promoted, mk_borrowck_eval_cx, ValTy};
|
||||
use interpret::{EvalContext, CompileTimeEvaluator, eval_promoted, mk_borrowck_eval_cx};
|
||||
use interpret::{Value, OpTy, MemoryKind};
|
||||
use transform::{MirPass, MirSource};
|
||||
use syntax::source_map::{Span, DUMMY_SP};
|
||||
use rustc::ty::subst::Substs;
|
||||
use rustc_data_structures::indexed_vec::IndexVec;
|
||||
use rustc_data_structures::indexed_vec::{IndexVec, Idx};
|
||||
use rustc::ty::ParamEnv;
|
||||
use rustc::ty::layout::{
|
||||
LayoutOf, TyLayout, LayoutError,
|
||||
|
@ -65,7 +65,7 @@ impl MirPass for ConstProp {
|
|||
}
|
||||
}
|
||||
|
||||
type Const<'tcx> = (Value, TyLayout<'tcx>, Span);
|
||||
type Const<'tcx> = (OpTy<'tcx>, Span);
|
||||
|
||||
/// Finds optimization opportunities on the MIR.
|
||||
struct ConstPropagator<'b, 'a, 'tcx:'a+'b> {
|
||||
|
@ -257,10 +257,10 @@ impl<'b, 'a, 'tcx:'b> ConstPropagator<'b, 'a, 'tcx> {
|
|||
source_info: SourceInfo,
|
||||
) -> Option<Const<'tcx>> {
|
||||
self.ecx.tcx.span = source_info.span;
|
||||
match self.ecx.const_to_value(c.literal.val) {
|
||||
Ok(val) => {
|
||||
match self.ecx.const_value_to_op(c.literal.val) {
|
||||
Ok(op) => {
|
||||
let layout = self.tcx.layout_of(self.param_env.and(c.literal.ty)).ok()?;
|
||||
Some((val, layout, c.span))
|
||||
Some((OpTy { op, layout }, c.span))
|
||||
},
|
||||
Err(error) => {
|
||||
let (stacktrace, span) = self.ecx.generate_stacktrace(None);
|
||||
|
@ -284,12 +284,15 @@ impl<'b, 'a, 'tcx:'b> ConstPropagator<'b, 'a, 'tcx> {
|
|||
Place::Projection(ref proj) => match proj.elem {
|
||||
ProjectionElem::Field(field, _) => {
|
||||
trace!("field proj on {:?}", proj.base);
|
||||
let (base, layout, span) = self.eval_place(&proj.base, source_info)?;
|
||||
let valty = self.use_ecx(source_info, |this| {
|
||||
this.ecx.read_field(base, None, field, layout)
|
||||
let (base, span) = self.eval_place(&proj.base, source_info)?;
|
||||
let res = self.use_ecx(source_info, |this| {
|
||||
this.ecx.operand_field(base, field.index() as u64)
|
||||
})?;
|
||||
Some((valty.0, valty.1, span))
|
||||
Some((res, span))
|
||||
},
|
||||
// We could get more projections by using e.g. `operand_projection`,
|
||||
// but we do not even have the stack frame set up properly so
|
||||
// an `Index` projection would throw us off-track.
|
||||
_ => None,
|
||||
},
|
||||
Place::Promoted(ref promoted) => {
|
||||
|
@ -306,12 +309,11 @@ impl<'b, 'a, 'tcx:'b> ConstPropagator<'b, 'a, 'tcx> {
|
|||
};
|
||||
// cannot use `const_eval` here, because that would require having the MIR
|
||||
// for the current function available, but we're producing said MIR right now
|
||||
let (value, _, ty) = self.use_ecx(source_info, |this| {
|
||||
let res = self.use_ecx(source_info, |this| {
|
||||
eval_promoted(&mut this.ecx, cid, this.mir, this.param_env)
|
||||
})?;
|
||||
let val = (value, ty, source_info.span);
|
||||
trace!("evaluated promoted {:?} to {:?}", promoted, val);
|
||||
Some(val)
|
||||
trace!("evaluated promoted {:?} to {:?}", promoted, res);
|
||||
Some((res, source_info.span))
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
|
@ -343,17 +345,11 @@ impl<'b, 'a, 'tcx:'b> ConstPropagator<'b, 'a, 'tcx> {
|
|||
Rvalue::Discriminant(..) => None,
|
||||
|
||||
Rvalue::Cast(kind, ref operand, _) => {
|
||||
let (value, layout, span) = self.eval_operand(operand, source_info)?;
|
||||
let (op, span) = self.eval_operand(operand, source_info)?;
|
||||
self.use_ecx(source_info, |this| {
|
||||
let dest_ptr = this.ecx.alloc_ptr(place_layout)?;
|
||||
let place_align = place_layout.align;
|
||||
let dest = ::interpret::Place::from_ptr(dest_ptr, place_align);
|
||||
this.ecx.cast(ValTy { value, ty: layout.ty }, kind, place_layout.ty, dest)?;
|
||||
Ok((
|
||||
Value::ByRef(dest_ptr.into(), place_align),
|
||||
place_layout,
|
||||
span,
|
||||
))
|
||||
let dest = this.ecx.allocate(place_layout, MemoryKind::Stack)?;
|
||||
this.ecx.cast(op, kind, dest.into())?;
|
||||
Ok((dest.into(), span))
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -361,11 +357,13 @@ impl<'b, 'a, 'tcx:'b> ConstPropagator<'b, 'a, 'tcx> {
|
|||
Rvalue::Len(_) => None,
|
||||
Rvalue::NullaryOp(NullOp::SizeOf, ty) => {
|
||||
type_size_of(self.tcx, self.param_env, ty).and_then(|n| Some((
|
||||
Value::Scalar(Scalar::Bits {
|
||||
OpTy::from_scalar_value(
|
||||
Scalar::Bits {
|
||||
bits: n as u128,
|
||||
size: self.tcx.data_layout.pointer_size.bytes() as u8,
|
||||
}.into()),
|
||||
},
|
||||
self.tcx.layout_of(self.param_env.and(self.tcx.types.usize)).ok()?,
|
||||
),
|
||||
span,
|
||||
)))
|
||||
}
|
||||
|
@ -381,12 +379,12 @@ impl<'b, 'a, 'tcx:'b> ConstPropagator<'b, 'a, 'tcx> {
|
|||
return None;
|
||||
}
|
||||
|
||||
let val = self.eval_operand(arg, source_info)?;
|
||||
let prim = self.use_ecx(source_info, |this| {
|
||||
this.ecx.value_to_scalar(ValTy { value: val.0, ty: val.1.ty })
|
||||
let (arg, _) = self.eval_operand(arg, source_info)?;
|
||||
let val = self.use_ecx(source_info, |this| {
|
||||
let prim = this.ecx.read_scalar(arg)?.not_undef()?;
|
||||
this.ecx.unary_op(op, prim, arg.layout)
|
||||
})?;
|
||||
let val = self.use_ecx(source_info, |this| this.ecx.unary_op(op, prim, val.1))?;
|
||||
Some((Value::Scalar(val.into()), place_layout, span))
|
||||
Some((OpTy::from_scalar_value(val, place_layout), span))
|
||||
}
|
||||
Rvalue::CheckedBinaryOp(op, ref left, ref right) |
|
||||
Rvalue::BinaryOp(op, ref left, ref right) => {
|
||||
|
@ -404,7 +402,7 @@ impl<'b, 'a, 'tcx:'b> ConstPropagator<'b, 'a, 'tcx> {
|
|||
}
|
||||
|
||||
let r = self.use_ecx(source_info, |this| {
|
||||
this.ecx.value_to_scalar(ValTy { value: right.0, ty: right.1.ty })
|
||||
this.ecx.read_value(right.0)
|
||||
})?;
|
||||
if op == BinOp::Shr || op == BinOp::Shl {
|
||||
let left_ty = left.ty(self.mir, self.tcx);
|
||||
|
@ -414,8 +412,9 @@ impl<'b, 'a, 'tcx:'b> ConstPropagator<'b, 'a, 'tcx> {
|
|||
.unwrap()
|
||||
.size
|
||||
.bits();
|
||||
let right_size = right.1.size;
|
||||
if r.to_bits(right_size).ok().map_or(false, |b| b >= left_bits as u128) {
|
||||
let right_size = right.0.layout.size;
|
||||
let r_bits = r.to_scalar().and_then(|r| r.to_bits(right_size));
|
||||
if r_bits.ok().map_or(false, |b| b >= left_bits as u128) {
|
||||
let source_scope_local_data = match self.mir.source_scope_local_data {
|
||||
ClearCrossCrate::Set(ref data) => data,
|
||||
ClearCrossCrate::Clear => return None,
|
||||
|
@ -436,11 +435,11 @@ impl<'b, 'a, 'tcx:'b> ConstPropagator<'b, 'a, 'tcx> {
|
|||
}
|
||||
let left = self.eval_operand(left, source_info)?;
|
||||
let l = self.use_ecx(source_info, |this| {
|
||||
this.ecx.value_to_scalar(ValTy { value: left.0, ty: left.1.ty })
|
||||
this.ecx.read_value(left.0)
|
||||
})?;
|
||||
trace!("const evaluating {:?} for {:?} and {:?}", op, left, right);
|
||||
let (val, overflow) = self.use_ecx(source_info, |this| {
|
||||
this.ecx.binary_op(op, l, left.1.ty, r, right.1.ty)
|
||||
this.ecx.binary_op(op, l, r)
|
||||
})?;
|
||||
let val = if let Rvalue::CheckedBinaryOp(..) = *rvalue {
|
||||
Value::ScalarPair(
|
||||
|
@ -455,7 +454,11 @@ impl<'b, 'a, 'tcx:'b> ConstPropagator<'b, 'a, 'tcx> {
|
|||
}
|
||||
Value::Scalar(val.into())
|
||||
};
|
||||
Some((val, place_layout, span))
|
||||
let res = OpTy {
|
||||
op: ::interpret::Operand::Immediate(val),
|
||||
layout: place_layout,
|
||||
};
|
||||
Some((res, span))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -571,7 +574,8 @@ impl<'b, 'a, 'tcx> Visitor<'tcx> for ConstPropagator<'b, 'a, 'tcx> {
|
|||
if let TerminatorKind::Assert { expected, msg, cond, .. } = kind {
|
||||
if let Some(value) = self.eval_operand(cond, source_info) {
|
||||
trace!("assertion on {:?} should be {:?}", value, expected);
|
||||
if Value::Scalar(Scalar::from_bool(*expected).into()) != value.0 {
|
||||
let expected = Value::Scalar(Scalar::from_bool(*expected).into());
|
||||
if expected != value.0.to_immediate() {
|
||||
// poison all places this operand references so that further code
|
||||
// doesn't use the invalid value
|
||||
match cond {
|
||||
|
@ -607,7 +611,7 @@ impl<'b, 'a, 'tcx> Visitor<'tcx> for ConstPropagator<'b, 'a, 'tcx> {
|
|||
let len = self
|
||||
.eval_operand(len, source_info)
|
||||
.expect("len must be const");
|
||||
let len = match len.0 {
|
||||
let len = match len.0.to_immediate() {
|
||||
Value::Scalar(ScalarMaybeUndef::Scalar(Scalar::Bits {
|
||||
bits, ..
|
||||
})) => bits,
|
||||
|
@ -616,7 +620,7 @@ impl<'b, 'a, 'tcx> Visitor<'tcx> for ConstPropagator<'b, 'a, 'tcx> {
|
|||
let index = self
|
||||
.eval_operand(index, source_info)
|
||||
.expect("index must be const");
|
||||
let index = match index.0 {
|
||||
let index = match index.0.to_immediate() {
|
||||
Value::Scalar(ScalarMaybeUndef::Scalar(Scalar::Bits {
|
||||
bits, ..
|
||||
})) => bits,
|
||||
|
|
|
@ -1375,7 +1375,7 @@ fn maybe_check_static_with_link_section(tcx: TyCtxt, id: DefId, span: Span) {
|
|||
};
|
||||
let param_env = ty::ParamEnv::reveal_all();
|
||||
if let Ok(static_) = tcx.const_eval(param_env.and(cid)) {
|
||||
let alloc = tcx.const_value_to_allocation(static_);
|
||||
let alloc = tcx.const_to_allocation(static_);
|
||||
if alloc.relocations.len() != 0 {
|
||||
let msg = "statics with a custom `#[link_section]` must be a \
|
||||
simple list of bytes on the wasm target with no \
|
||||
|
|
|
@ -22,7 +22,7 @@ error: this constant cannot be used
|
|||
LL | const Z2: i32 = unsafe { *(42 as *const i32) }; //~ ERROR cannot be used
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^-------------------^^^
|
||||
| |
|
||||
| tried to access memory with alignment 2, but alignment 4 is required
|
||||
| a memory access tried to interpret some bytes as a pointer
|
||||
|
||||
error: this constant cannot be used
|
||||
--> $DIR/const_raw_ptr_ops.rs:27:1
|
||||
|
|
|
@ -5,7 +5,7 @@ LL | / static FOO: (&Foo, &Bar) = unsafe {( //~ undefined behavior
|
|||
LL | | Union { usize: &BAR }.foo,
|
||||
LL | | Union { usize: &BAR }.bar,
|
||||
LL | | )};
|
||||
| |___^ type validation failed: encountered 5 at (*.1).TAG, but expected something in the range 42..=99
|
||||
| |___^ type validation failed: encountered 5 at .1.<deref>.<enum-tag>, but expected something in the range 42..=99
|
||||
|
|
||||
= 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
|
||||
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#[repr(usize)]
|
||||
#[derive(Copy, Clone)]
|
||||
enum Enum {
|
||||
A = 0,
|
||||
}
|
||||
|
||||
union Foo {
|
||||
a: &'static u8,
|
||||
b: Enum,
|
||||
}
|
||||
|
||||
// A pointer is guaranteed non-null
|
||||
const BAD_ENUM: Enum = unsafe { Foo { a: &1 }.b};
|
||||
//~^ ERROR this constant likely exhibits undefined behavior
|
||||
|
||||
fn main() {
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
error[E0080]: this constant likely exhibits undefined behavior
|
||||
--> $DIR/ub-enum-ptr.rs:23:1
|
||||
|
|
||||
LL | const BAD_ENUM: Enum = unsafe { Foo { a: &1 }.b};
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer at .TAG, but expected something in the range 0..=0
|
||||
|
|
||||
= 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`.
|
49
src/test/ui/consts/const-eval/ub-enum.rs
Normal file
49
src/test/ui/consts/const-eval/ub-enum.rs
Normal file
|
@ -0,0 +1,49 @@
|
|||
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#[repr(usize)]
|
||||
#[derive(Copy, Clone)]
|
||||
enum Enum {
|
||||
A = 0,
|
||||
}
|
||||
union TransmuteEnum {
|
||||
a: &'static u8,
|
||||
b: Enum,
|
||||
}
|
||||
|
||||
// A pointer is guaranteed non-null
|
||||
const BAD_ENUM: Enum = unsafe { TransmuteEnum { a: &1 }.b };
|
||||
//~^ ERROR this constant likely exhibits undefined behavior
|
||||
|
||||
// Invalid enum discriminant
|
||||
#[repr(usize)]
|
||||
#[derive(Copy, Clone)]
|
||||
enum Enum2 {
|
||||
A = 2,
|
||||
}
|
||||
union TransmuteEnum2 {
|
||||
a: usize,
|
||||
b: Enum2,
|
||||
}
|
||||
const BAD_ENUM2 : Enum2 = unsafe { TransmuteEnum2 { a: 0 }.b };
|
||||
//~^ ERROR this constant likely exhibits undefined behavior
|
||||
|
||||
// Invalid enum field content (mostly to test printing of apths for enum tuple
|
||||
// variants and tuples).
|
||||
union TransmuteChar {
|
||||
a: u32,
|
||||
b: char,
|
||||
}
|
||||
// Need to create something which does not clash with enum layout optimizations.
|
||||
const BAD_ENUM_CHAR : Option<(char, char)> = Some(('x', unsafe { TransmuteChar { a: !0 }.b }));
|
||||
//~^ ERROR this constant likely exhibits undefined behavior
|
||||
|
||||
fn main() {
|
||||
}
|
27
src/test/ui/consts/const-eval/ub-enum.stderr
Normal file
27
src/test/ui/consts/const-eval/ub-enum.stderr
Normal file
|
@ -0,0 +1,27 @@
|
|||
error[E0080]: this constant likely exhibits undefined behavior
|
||||
--> $DIR/ub-enum.rs:22:1
|
||||
|
|
||||
LL | const BAD_ENUM: Enum = unsafe { TransmuteEnum { a: &1 }.b };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer at .<enum-tag>, but expected something in the range 0..=0
|
||||
|
|
||||
= 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/ub-enum.rs:35:1
|
||||
|
|
||||
LL | const BAD_ENUM2 : Enum2 = unsafe { TransmuteEnum2 { a: 0 }.b };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0 at .<enum-tag>, but expected something in the range 2..=2
|
||||
|
|
||||
= 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/ub-enum.rs:45:1
|
||||
|
|
||||
LL | const BAD_ENUM_CHAR : Option<(char, char)> = Some(('x', unsafe { TransmuteChar { a: !0 }.b }));
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered character at .Some.0.1, but expected a valid unicode codepoint
|
||||
|
|
||||
= 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 3 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0080`.
|
|
@ -13,6 +13,11 @@
|
|||
// normalize-stderr-test "allocation \d+" -> "allocation N"
|
||||
// normalize-stderr-test "size \d+" -> "size N"
|
||||
|
||||
union BoolTransmute {
|
||||
val: u8,
|
||||
bl: bool,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
struct SliceRepr {
|
||||
|
@ -32,6 +37,7 @@ union SliceTransmute {
|
|||
bad: BadSliceRepr,
|
||||
slice: &'static [u8],
|
||||
str: &'static str,
|
||||
my_str: &'static Str,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
|
@ -63,32 +69,48 @@ union DynTransmute {
|
|||
}
|
||||
|
||||
trait Trait {}
|
||||
impl Trait for bool {}
|
||||
|
||||
struct Str(str);
|
||||
|
||||
// OK
|
||||
const A: &str = unsafe { SliceTransmute { repr: SliceRepr { ptr: &42, len: 1 } }.str};
|
||||
// should lint
|
||||
// bad str
|
||||
const B: &str = unsafe { SliceTransmute { repr: SliceRepr { ptr: &42, len: 999 } }.str};
|
||||
// bad
|
||||
//~^ ERROR this constant likely exhibits undefined behavior
|
||||
// bad str
|
||||
const C: &str = unsafe { SliceTransmute { bad: BadSliceRepr { ptr: &42, len: &3 } }.str};
|
||||
//~^ ERROR this constant likely exhibits undefined behavior
|
||||
// bad str in Str
|
||||
const C2: &Str = unsafe { SliceTransmute { bad: BadSliceRepr { ptr: &42, len: &3 } }.my_str};
|
||||
//~^ ERROR this constant likely exhibits undefined behavior
|
||||
|
||||
// OK
|
||||
const A2: &[u8] = unsafe { SliceTransmute { repr: SliceRepr { ptr: &42, len: 1 } }.slice};
|
||||
// should lint
|
||||
// bad slice
|
||||
const B2: &[u8] = unsafe { SliceTransmute { repr: SliceRepr { ptr: &42, len: 999 } }.slice};
|
||||
// bad
|
||||
const C2: &[u8] = unsafe { SliceTransmute { bad: BadSliceRepr { ptr: &42, len: &3 } }.slice};
|
||||
//~^ ERROR this constant likely exhibits undefined behavior
|
||||
// bad slice
|
||||
const C3: &[u8] = unsafe { SliceTransmute { bad: BadSliceRepr { ptr: &42, len: &3 } }.slice};
|
||||
//~^ ERROR this constant likely exhibits undefined behavior
|
||||
|
||||
// bad
|
||||
// bad trait object
|
||||
const D: &Trait = unsafe { DynTransmute { repr: DynRepr { ptr: &92, vtable: &3 } }.rust};
|
||||
//~^ ERROR this constant likely exhibits undefined behavior
|
||||
// bad
|
||||
// bad trait object
|
||||
const E: &Trait = unsafe { DynTransmute { repr2: DynRepr2 { ptr: &92, vtable: &3 } }.rust};
|
||||
//~^ ERROR this constant likely exhibits undefined behavior
|
||||
// bad
|
||||
// bad trait object
|
||||
const F: &Trait = unsafe { DynTransmute { bad: BadDynRepr { ptr: &92, vtable: 3 } }.rust};
|
||||
//~^ ERROR this constant likely exhibits undefined behavior
|
||||
|
||||
// bad data *inside* the trait object
|
||||
const G: &Trait = &unsafe { BoolTransmute { val: 3 }.bl };
|
||||
//~^ ERROR this constant likely exhibits undefined behavior
|
||||
|
||||
// bad data *inside* the slice
|
||||
const H: &[bool] = &[unsafe { BoolTransmute { val: 3 }.bl }];
|
||||
//~^ ERROR this constant likely exhibits undefined behavior
|
||||
|
||||
fn main() {
|
||||
}
|
||||
|
|
|
@ -1,21 +1,45 @@
|
|||
error[E0080]: this constant likely exhibits undefined behavior
|
||||
--> $DIR/union-ub-fat-ptr.rs:72:1
|
||||
--> $DIR/union-ub-fat-ptr.rs:79:1
|
||||
|
|
||||
LL | const B: &str = unsafe { SliceTransmute { repr: SliceRepr { ptr: &42, len: 999 } }.str};
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access at offset N, outside bounds of allocation N which has size N
|
||||
|
|
||||
= 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-ub-fat-ptr.rs:82:1
|
||||
|
|
||||
LL | const C: &str = unsafe { SliceTransmute { bad: BadSliceRepr { ptr: &42, len: &3 } }.str};
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered length is not a valid integer
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered fat pointer length is not a valid integer
|
||||
|
|
||||
= 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-ub-fat-ptr.rs:80:1
|
||||
--> $DIR/union-ub-fat-ptr.rs:85:1
|
||||
|
|
||||
LL | const C2: &[u8] = unsafe { SliceTransmute { bad: BadSliceRepr { ptr: &42, len: &3 } }.slice};
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered length is not a valid integer
|
||||
LL | const C2: &Str = unsafe { SliceTransmute { bad: BadSliceRepr { ptr: &42, len: &3 } }.my_str};
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered fat pointer length is not a valid integer
|
||||
|
|
||||
= 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-ub-fat-ptr.rs:84:1
|
||||
--> $DIR/union-ub-fat-ptr.rs:91:1
|
||||
|
|
||||
LL | const B2: &[u8] = unsafe { SliceTransmute { repr: SliceRepr { ptr: &42, len: 999 } }.slice};
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access at offset N, outside bounds of allocation N which has size N
|
||||
|
|
||||
= 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-ub-fat-ptr.rs:94:1
|
||||
|
|
||||
LL | const C3: &[u8] = unsafe { SliceTransmute { bad: BadSliceRepr { ptr: &42, len: &3 } }.slice};
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered fat pointer length is not a valid integer
|
||||
|
|
||||
= 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-ub-fat-ptr.rs:98:1
|
||||
|
|
||||
LL | const D: &Trait = unsafe { DynTransmute { repr: DynRepr { ptr: &92, vtable: &3 } }.rust};
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ tried to access memory with alignment N, but alignment N is required
|
||||
|
@ -23,21 +47,37 @@ LL | const D: &Trait = unsafe { DynTransmute { repr: DynRepr { ptr: &92, vtable:
|
|||
= 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-ub-fat-ptr.rs:87:1
|
||||
--> $DIR/union-ub-fat-ptr.rs:101:1
|
||||
|
|
||||
LL | const E: &Trait = unsafe { DynTransmute { repr2: DynRepr2 { ptr: &92, vtable: &3 } }.rust};
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access at offset N, outside bounds of allocation N which has size N
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ a memory access tried to interpret some bytes as a pointer
|
||||
|
|
||||
= 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-ub-fat-ptr.rs:90:1
|
||||
--> $DIR/union-ub-fat-ptr.rs:104:1
|
||||
|
|
||||
LL | const F: &Trait = unsafe { DynTransmute { bad: BadDynRepr { ptr: &92, vtable: 3 } }.rust};
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered vtable address is not a pointer
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered fat pointer vtable is not a valid pointer
|
||||
|
|
||||
= 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 5 previous errors
|
||||
error[E0080]: this constant likely exhibits undefined behavior
|
||||
--> $DIR/union-ub-fat-ptr.rs:108:1
|
||||
|
|
||||
LL | const G: &Trait = &unsafe { BoolTransmute { val: 3 }.bl };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 3 at .<deref>, but expected something in the range 0..=1
|
||||
|
|
||||
= 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-ub-fat-ptr.rs:112:1
|
||||
|
|
||||
LL | const H: &[bool] = &[unsafe { BoolTransmute { val: 3 }.bl }];
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 3 at .<deref>[0], but expected something in the range 0..=1
|
||||
|
|
||||
= 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 10 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0080`.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue