Simplify PrimValKind and remove a horrible hack.

This takes the `AllocId` out of PrimValKind, replacing it with a
`relocation` field on `PrimVal`, which is closer to an earlier design
for `PrimVal` I discussed with @eddyb.

This commit prepares the code for removing the `PrimValKind` from
`PrimVal` and making them more pure bitbags. The only code dealing with
`PrimValKind` will be code making decisions like "what kind of operation
do I need to do on these bits", like operators and casting. Transmutes
of `PrimVal`s will become true no-ops, not even adjusting a `kind`
field.

This commit also removes my horrible `value_to_primval` hack that made
an allocation for every `ByVal` passed in, so it could use `read_value`
to get a `PrimVal` with the right kind. Now I just compute the
`PrimValKind` from the `Ty` and re-tag the `PrimVal`.

The code got slightly messier in some areas here, but I think a _lot_ of
code will simplify in obvious ways once I remove the `kind` field from
`PrimVal`.

Gosh, if my commit messages aren't turning into essays these days.
This commit is contained in:
Scott Olson 2016-10-21 03:17:53 -06:00
parent 330be7766f
commit e7bcf35f8a
7 changed files with 214 additions and 150 deletions

@ -21,8 +21,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
Bool | Char | U8 | U16 | U32 | U64 => self.cast_int(val.bits, ty, false),
FnPtr(alloc) | Ptr(alloc) => {
let ptr = Pointer::new(alloc, val.bits as usize);
FnPtr | Ptr => {
let ptr = val.expect_ptr("FnPtr- or Ptr-tagged PrimVal had no relocation");
self.cast_ptr(ptr, ty)
}
}

@ -972,7 +972,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
}
Deref => {
use primval::PrimValKind::*;
use interpreter::value::Value::*;
let val = match self.eval_and_read_lvalue(&proj.base)? {
@ -981,22 +980,21 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
};
match val {
ByValPair(
PrimVal { kind: Ptr(ptr_alloc), bits: ptr_offset },
PrimVal { kind: Ptr(vtable_alloc), bits: vtable_offset },
) => {
let ptr = Pointer::new(ptr_alloc, ptr_offset as usize);
let vtable = Pointer::new(vtable_alloc, vtable_offset as usize);
ByValPair(ptr, vtable)
if ptr.try_as_ptr().is_some() && vtable.try_as_ptr().is_some()
=> {
let ptr = ptr.try_as_ptr().unwrap();
let vtable = vtable.try_as_ptr().unwrap();
(ptr, LvalueExtra::Vtable(vtable))
}
ByValPair(PrimVal { kind: Ptr(alloc), bits: offset }, n) => {
let ptr = Pointer::new(alloc, offset as usize);
ByValPair(ptr, n) if ptr.try_as_ptr().is_some() => {
let ptr = ptr.try_as_ptr().unwrap();
(ptr, LvalueExtra::Length(n.expect_uint("slice length")))
}
ByVal(PrimVal { kind: Ptr(alloc), bits: offset }) => {
let ptr = Pointer::new(alloc, offset as usize);
ByVal(ptr) if ptr.try_as_ptr().is_some() => {
let ptr = ptr.try_as_ptr().unwrap();
(ptr, LvalueExtra::None)
}
@ -1122,39 +1120,20 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
Value::ByValPair(..) => bug!("value_to_primval can't work with fat pointers"),
},
// FIXME(solson): This unnecessarily allocates to work around a new issue my `Value`
// locals refactoring introduced. There is code that calls this function and expects to
// get a PrimVal reflecting the specific type that it asked for, e.g. `PrimVal::Bool`
// when it was asking for `TyBool`. This used to always work because it would go
// through `read_value` which does the right thing.
//
// This is the comment and implementation from before my refactor:
//
// TODO(solson): Sanity-check the primval type against the input type.
// Value::ByVal(primval) => Ok(primval),
//
// Turns out sanity-checking isn't enough now, and we need conversion.
//
// Now that we can possibly be reading a `ByVal` straight out of the locals vec, if the
// user did something tricky like transmuting a `u8` to a `bool`, then we'll have a
// `PrimVal::U8` and need to convert to `PrimVal::Bool`.
//
// I want to avoid handling the full set of conversions between `PrimVal`s, so for now
// I will use this hack. I have a plan to change the representation of `PrimVal` to be
// more like a small piece of memory tagged with a `PrimValKind`, which should make the
// conversion easy and make the problem solveable using code already in `Memory`.
Value::ByVal(primval) => {
let ptr = self.alloc_ptr(ty)?;
self.memory.write_primval(ptr, primval)?;
let primval = self.value_to_primval(Value::ByRef(ptr), ty)?;
self.memory.deallocate(ptr)?;
Ok(primval)
let new_primval = self.transmute_primval(primval, ty)?;
self.ensure_valid_value(new_primval, ty)?;
Ok(new_primval)
}
Value::ByValPair(..) => bug!("value_to_primval can't work with fat pointers"),
}
}
fn transmute_primval(&self, val: PrimVal, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> {
Ok(PrimVal { kind: self.ty_to_primval_kind(ty)?, ..val })
}
fn write_primval(
&mut self,
dest: Lvalue,
@ -1252,6 +1231,77 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
Ok(())
}
fn ty_to_primval_kind(&self, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimValKind> {
use syntax::ast::FloatTy;
let kind = match ty.sty {
ty::TyBool => PrimValKind::Bool,
ty::TyChar => PrimValKind::Char,
ty::TyInt(int_ty) => {
use syntax::ast::IntTy::*;
let size = match int_ty {
I8 => 1,
I16 => 2,
I32 => 4,
I64 => 8,
Is => self.memory.pointer_size(),
};
PrimValKind::from_int_size(size)
}
ty::TyUint(uint_ty) => {
use syntax::ast::UintTy::*;
let size = match uint_ty {
U8 => 1,
U16 => 2,
U32 => 4,
U64 => 8,
Us => self.memory.pointer_size(),
};
PrimValKind::from_uint_size(size)
}
ty::TyFloat(FloatTy::F32) => PrimValKind::F32,
ty::TyFloat(FloatTy::F64) => PrimValKind::F64,
ty::TyFnPtr(_) => PrimValKind::FnPtr,
ty::TyBox(ty) |
ty::TyRef(_, ty::TypeAndMut { ty, .. }) |
ty::TyRawPtr(ty::TypeAndMut { ty, .. }) if self.type_is_sized(ty) => PrimValKind::Ptr,
ty::TyAdt(..) => {
use rustc::ty::layout::Layout::*;
if let CEnum { discr, signed, .. } = *self.type_layout(ty) {
let size = discr.size().bytes() as usize;
if signed {
PrimValKind::from_int_size(size)
} else {
PrimValKind::from_uint_size(size)
}
} else {
bug!("primitive read of non-clike enum: {:?}", ty);
}
},
_ => bug!("primitive read of non-primitive type: {:?}", ty),
};
Ok(kind)
}
fn ensure_valid_value(&self, val: PrimVal, ty: Ty<'tcx>) -> EvalResult<'tcx, ()> {
match ty.sty {
ty::TyBool if val.bits > 1 => Err(EvalError::InvalidBool),
ty::TyChar if ::std::char::from_u32(val.bits as u32).is_none()
=> Err(EvalError::InvalidChar(val.bits as u32 as u64)),
_ => Ok(()),
}
}
fn read_value(&mut self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, Value> {
use syntax::ast::FloatTy;

@ -19,10 +19,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
dest_ty: Ty<'tcx>,
dest_layout: &'tcx Layout,
) -> EvalResult<'tcx, ()> {
let args_ptrs: EvalResult<Vec<Value>> = args.iter()
let arg_vals: EvalResult<Vec<Value>> = args.iter()
.map(|arg| self.eval_operand(arg))
.collect();
let args_ptrs = args_ptrs?;
let arg_vals = arg_vals?;
let i32 = self.tcx.types.i32;
let isize = self.tcx.types.isize;
let usize = self.tcx.types.usize;
@ -42,8 +42,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
"arith_offset" => {
let ptr = args_ptrs[0].read_ptr(&self.memory)?;
let offset = self.value_to_primval(args_ptrs[1], isize)?
let ptr = arg_vals[0].read_ptr(&self.memory)?;
let offset = self.value_to_primval(arg_vals[1], isize)?
.expect_int("arith_offset second arg not isize");
let new_ptr = ptr.offset(offset as isize);
self.write_primval(dest, PrimVal::from_ptr(new_ptr))?;
@ -51,23 +51,22 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
"assume" => {
let bool = self.tcx.types.bool;
let cond = self.value_to_primval(args_ptrs[0], bool)?
.expect_bool("assume arg not bool");
let cond = self.value_to_primval(arg_vals[0], bool)?.try_as_bool()?;
if !cond { return Err(EvalError::AssumptionNotHeld); }
}
"atomic_load" |
"volatile_load" => {
let ty = substs.type_at(0);
let ptr = args_ptrs[0].read_ptr(&self.memory)?;
let ptr = arg_vals[0].read_ptr(&self.memory)?;
self.write_value(Value::ByRef(ptr), dest, ty)?;
}
"atomic_store" |
"volatile_store" => {
let ty = substs.type_at(0);
let dest = args_ptrs[0].read_ptr(&self.memory)?;
self.write_value_to_ptr(args_ptrs[1], dest, ty)?;
let dest = arg_vals[0].read_ptr(&self.memory)?;
self.write_value_to_ptr(arg_vals[1], dest, ty)?;
}
"breakpoint" => unimplemented!(), // halt miri
@ -78,9 +77,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
let elem_ty = substs.type_at(0);
let elem_size = self.type_size(elem_ty);
let elem_align = self.type_align(elem_ty);
let src = args_ptrs[0].read_ptr(&self.memory)?;
let dest = args_ptrs[1].read_ptr(&self.memory)?;
let count = self.value_to_primval(args_ptrs[2], usize)?
let src = arg_vals[0].read_ptr(&self.memory)?;
let dest = arg_vals[1].read_ptr(&self.memory)?;
let count = self.value_to_primval(arg_vals[2], usize)?
.expect_uint("arith_offset second arg not isize");
self.memory.copy(src, dest, count as usize * elem_size, elem_align)?;
}
@ -90,34 +89,34 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
"ctlz" |
"bswap" => {
let elem_ty = substs.type_at(0);
let num = self.value_to_primval(args_ptrs[0], elem_ty)?;
let num = self.value_to_primval(arg_vals[0], elem_ty)?;
let num = numeric_intrinsic(intrinsic_name, num);
self.write_primval(dest, num)?;
}
"discriminant_value" => {
let ty = substs.type_at(0);
let adt_ptr = args_ptrs[0].read_ptr(&self.memory)?;
let adt_ptr = arg_vals[0].read_ptr(&self.memory)?;
let discr_val = self.read_discriminant_value(adt_ptr, ty)?;
self.write_primval(dest, PrimVal::new(discr_val, PrimValKind::U64))?;
}
"fabsf32" => {
let f = self.value_to_primval(args_ptrs[2], f32)?
let f = self.value_to_primval(arg_vals[2], f32)?
.expect_f32("fabsf32 read non f32");
self.write_primval(dest, PrimVal::from_f32(f.abs()))?;
}
"fabsf64" => {
let f = self.value_to_primval(args_ptrs[2], f64)?
let f = self.value_to_primval(arg_vals[2], f64)?
.expect_f64("fabsf64 read non f64");
self.write_primval(dest, PrimVal::from_f64(f.abs()))?;
}
"fadd_fast" => {
let ty = substs.type_at(0);
let a = self.value_to_primval(args_ptrs[0], ty)?;
let b = self.value_to_primval(args_ptrs[0], ty)?;
let a = self.value_to_primval(arg_vals[0], ty)?;
let b = self.value_to_primval(arg_vals[0], ty)?;
let result = primval::binary_op(mir::BinOp::Add, a, b)?;
self.write_primval(dest, result.0)?;
}
@ -151,8 +150,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
"move_val_init" => {
let ty = substs.type_at(0);
let ptr = args_ptrs[0].read_ptr(&self.memory)?;
self.write_value_to_ptr(args_ptrs[1], ptr, ty)?;
let ptr = arg_vals[0].read_ptr(&self.memory)?;
self.write_value_to_ptr(arg_vals[1], ptr, ty)?;
}
"needs_drop" => {
@ -165,10 +164,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
"offset" => {
let pointee_ty = substs.type_at(0);
let pointee_size = self.type_size(pointee_ty) as isize;
let offset = self.value_to_primval(args_ptrs[1], isize)?
let offset = self.value_to_primval(arg_vals[1], isize)?
.expect_int("offset second arg not isize");
let ptr = args_ptrs[0].read_ptr(&self.memory)?;
let ptr = arg_vals[0].read_ptr(&self.memory)?;
let result_ptr = ptr.offset(offset as isize * pointee_size);
self.write_primval(dest, PrimVal::from_ptr(result_ptr))?;
}
@ -186,29 +185,29 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
}
"powif32" => {
let f = self.value_to_primval(args_ptrs[0], f32)?
let f = self.value_to_primval(arg_vals[0], f32)?
.expect_f32("powif32 first arg not f32");
let i = self.value_to_primval(args_ptrs[1], i32)?
let i = self.value_to_primval(arg_vals[1], i32)?
.expect_int("powif32 second arg not i32");
self.write_primval(dest, PrimVal::from_f32(f.powi(i as i32)))?;
}
"powif64" => {
let f = self.value_to_primval(args_ptrs[0], f64)?
let f = self.value_to_primval(arg_vals[0], f64)?
.expect_f64("powif64 first arg not f64");
let i = self.value_to_primval(args_ptrs[1], i32)?
let i = self.value_to_primval(arg_vals[1], i32)?
.expect_int("powif64 second arg not i32");
self.write_primval(dest, PrimVal::from_f64(f.powi(i as i32)))?;
}
"sqrtf32" => {
let f = self.value_to_primval(args_ptrs[0], f32)?
let f = self.value_to_primval(arg_vals[0], f32)?
.expect_f32("sqrtf32 first arg not f32");
self.write_primval(dest, PrimVal::from_f32(f.sqrt()))?;
}
"sqrtf64" => {
let f = self.value_to_primval(args_ptrs[0], f64)?
let f = self.value_to_primval(arg_vals[0], f64)?
.expect_f64("sqrtf64 first arg not f64");
self.write_primval(dest, PrimVal::from_f64(f.sqrt()))?;
}
@ -222,7 +221,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
"size_of_val" => {
let ty = substs.type_at(0);
let (size, _) = self.size_and_align_of_dst(ty, args_ptrs[0])?;
let (size, _) = self.size_and_align_of_dst(ty, arg_vals[0])?;
let size_val = self.usize_primval(size);
self.write_primval(dest, size_val)?;
}
@ -239,8 +238,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
}
"transmute" => {
let ty = substs.type_at(0);
self.write_value(args_ptrs[0], dest, ty)?;
let dest_ty = substs.type_at(1);
let val = match arg_vals[0] {
Value::ByVal(primval) =>
Value::ByVal(self.transmute_primval(primval, dest_ty)?),
v => v,
};
self.write_value(val, dest, dest_ty)?;
}
"uninit" => {

@ -35,8 +35,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
Goto { target } => self.goto_block(target),
If { ref cond, targets: (then_target, else_target) } => {
let cond_val = self.eval_operand_to_primval(cond)?
.expect_bool("TerminatorKind::If condition constant was not a bool");
let cond_val = self.eval_operand_to_primval(cond)?.try_as_bool()?;
self.goto_block(if cond_val { then_target } else { else_target });
}
@ -116,8 +115,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
}
Assert { ref cond, expected, ref msg, target, .. } => {
let cond_val = self.eval_operand_to_primval(cond)?
.expect_bool("TerminatorKind::Assert condition constant was not a bool");
let cond_val = self.eval_operand_to_primval(cond)?.try_as_bool()?;
if expected == cond_val {
self.goto_block(target);
} else {

@ -1,6 +1,6 @@
use error::EvalResult;
use memory::{Memory, Pointer};
use primval::{PrimVal, PrimValKind};
use primval::PrimVal;
/// A `Value` represents a single self-contained Rust value.
///
@ -23,10 +23,8 @@ impl<'a, 'tcx: 'a> Value {
match *self {
ByRef(ptr) => mem.read_ptr(ptr),
ByVal(PrimVal { kind: PrimValKind::Ptr(alloc), bits: offset }) |
ByVal(PrimVal { kind: PrimValKind::FnPtr(alloc), bits: offset }) => {
let ptr = Pointer::new(alloc, offset as usize);
Ok(ptr)
ByVal(ptr) if ptr.try_as_ptr().is_some() => {
Ok(ptr.try_as_ptr().unwrap())
}
ByValPair(..) => unimplemented!(),
@ -46,12 +44,11 @@ impl<'a, 'tcx: 'a> Value {
Ok((ptr, vtable))
}
ByValPair(
PrimVal { kind: PrimValKind::Ptr(ptr_alloc), bits: ptr_offset },
PrimVal { kind: PrimValKind::Ptr(vtable_alloc), bits: vtable_offset },
) => {
let ptr = Pointer::new(ptr_alloc, ptr_offset as usize);
let vtable = Pointer::new(vtable_alloc, vtable_offset as usize);
ByValPair(ptr, vtable)
if ptr.try_as_ptr().is_some() && vtable.try_as_ptr().is_some()
=> {
let ptr = ptr.try_as_ptr().unwrap();
let vtable = vtable.try_as_ptr().unwrap();
Ok((ptr, vtable))
}

@ -532,25 +532,21 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
Ok(())
}
pub fn write_primval(&mut self, ptr: Pointer, val: PrimVal) -> EvalResult<'tcx, ()> {
use primval::PrimValKind::*;
match val.kind {
FnPtr(alloc_id) | Ptr(alloc_id) => {
let p = Pointer::new(alloc_id, val.bits as usize);
return self.write_ptr(ptr, p);
}
_ => {}
pub fn write_primval(&mut self, dest: Pointer, val: PrimVal) -> EvalResult<'tcx, ()> {
if let Some(ptr) = val.try_as_ptr() {
return self.write_ptr(dest, ptr);
}
use primval::PrimValKind::*;
let (size, bits) = match val.kind {
I8 | U8 | Bool => (1, val.bits as u8 as u64),
I16 | U16 => (2, val.bits as u16 as u64),
I32 | U32 | F32 | Char => (4, val.bits as u32 as u64),
I64 | U64 | F64 => (8, val.bits),
FnPtr(_) | Ptr(_) => bug!("handled above"),
FnPtr | Ptr => bug!("handled above"),
};
self.write_uint(ptr, bits, size)
self.write_uint(dest, bits, size)
}
pub fn read_bool(&self, ptr: Pointer) -> EvalResult<'tcx, bool> {

@ -33,6 +33,15 @@ fn bits_to_bool(n: u64) -> bool {
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct PrimVal {
pub bits: u64,
/// This field is initialized when the `PrimVal` represents a pointer into an `Allocation`. An
/// `Allocation` in the `memory` module has a list of relocations, but a `PrimVal` is only
/// large enough to contain one, hence the `Option`.
pub relocation: Option<AllocId>,
// FIXME(solson): I think we can make this field unnecessary, or at least move it outside of
// this struct. We can either match over `Ty`s or generate simple `PrimVal`s from `Ty`s and
// match over those to decide which operations to perform on `PrimVal`s.
pub kind: PrimValKind,
}
@ -43,8 +52,8 @@ pub enum PrimValKind {
F32, F64,
Bool,
Char,
Ptr(AllocId),
FnPtr(AllocId),
Ptr,
FnPtr,
}
impl PrimValKind {
@ -55,19 +64,43 @@ impl PrimValKind {
_ => false,
}
}
pub fn from_uint_size(size: usize) -> Self {
match size {
1 => PrimValKind::U8,
2 => PrimValKind::U16,
4 => PrimValKind::U32,
8 => PrimValKind::U64,
_ => bug!("can't make uint with size {}", size),
}
}
pub fn from_int_size(size: usize) -> Self {
match size {
1 => PrimValKind::I8,
2 => PrimValKind::I16,
4 => PrimValKind::I32,
8 => PrimValKind::I64,
_ => bug!("can't make int with size {}", size),
}
}
}
impl PrimVal {
pub fn new(bits: u64, kind: PrimValKind) -> Self {
PrimVal { bits: bits, kind: kind }
PrimVal { bits: bits, relocation: None, kind: kind }
}
pub fn new_with_relocation(bits: u64, kind: PrimValKind, alloc_id: AllocId) -> Self {
PrimVal { bits: bits, relocation: Some(alloc_id), kind: kind }
}
pub fn from_ptr(ptr: Pointer) -> Self {
PrimVal::new(ptr.offset as u64, PrimValKind::Ptr(ptr.alloc_id))
PrimVal::new_with_relocation(ptr.offset as u64, PrimValKind::Ptr, ptr.alloc_id)
}
pub fn from_fn_ptr(ptr: Pointer) -> Self {
PrimVal::new(ptr.offset as u64, PrimValKind::FnPtr(ptr.alloc_id))
PrimVal::new_with_relocation(ptr.offset as u64, PrimValKind::FnPtr, ptr.alloc_id)
}
pub fn from_bool(b: bool) -> Self {
@ -87,64 +120,58 @@ impl PrimVal {
}
pub fn from_uint_with_size(n: u64, size: usize) -> Self {
let kind = match size {
1 => PrimValKind::U8,
2 => PrimValKind::U16,
4 => PrimValKind::U32,
8 => PrimValKind::U64,
_ => bug!("can't make uint ({}) with size {}", n, size),
};
PrimVal::new(n, kind)
PrimVal::new(n, PrimValKind::from_uint_size(size))
}
pub fn from_int_with_size(n: i64, size: usize) -> Self {
let kind = match size {
1 => PrimValKind::I8,
2 => PrimValKind::I16,
4 => PrimValKind::I32,
8 => PrimValKind::I64,
_ => bug!("can't make int ({}) with size {}", n, size),
};
PrimVal::new(n as u64, kind)
PrimVal::new(n as u64, PrimValKind::from_int_size(size))
}
pub fn to_f32(self) -> f32 {
assert!(self.relocation.is_none());
bits_to_f32(self.bits)
}
pub fn to_f64(self) -> f64 {
assert!(self.relocation.is_none());
bits_to_f64(self.bits)
}
pub fn try_as_ptr(self) -> Option<Pointer> {
self.relocation.map(|alloc_id| {
Pointer::new(alloc_id, self.bits as usize)
})
}
pub fn expect_uint(self, error_msg: &str) -> u64 {
if let Some(ptr) = self.try_as_ptr() {
return ptr.to_int().expect("non abstract ptr") as u64
}
use self::PrimValKind::*;
match self.kind {
U8 | U16 | U32 | U64 => self.bits,
Ptr(alloc_id) => {
let ptr = Pointer::new(alloc_id, self.bits as usize);
ptr.to_int().expect("non abstract ptr") as u64
}
_ => bug!("{}", error_msg),
}
}
pub fn expect_int(self, error_msg: &str) -> i64 {
if let Some(ptr) = self.try_as_ptr() {
return ptr.to_int().expect("non abstract ptr") as i64
}
use self::PrimValKind::*;
match self.kind {
I8 | I16 | I32 | I64 => self.bits as i64,
Ptr(alloc_id) => {
let ptr = Pointer::new(alloc_id, self.bits as usize);
ptr.to_int().expect("non abstract ptr") as i64
}
_ => bug!("{}", error_msg),
}
}
pub fn expect_bool(self, error_msg: &str) -> bool {
match (self.kind, self.bits) {
(PrimValKind::Bool, 0) => false,
(PrimValKind::Bool, 1) => true,
_ => bug!("{}", error_msg),
pub fn try_as_bool<'tcx>(self) -> EvalResult<'tcx, bool> {
match self.bits {
0 => Ok(false),
1 => Ok(true),
_ => Err(EvalError::InvalidBool),
}
}
@ -163,17 +190,12 @@ impl PrimVal {
}
pub fn expect_ptr(self, error_msg: &str) -> Pointer {
match self.kind {
PrimValKind::Ptr(alloc_id) => Pointer::new(alloc_id, self.bits as usize),
_ => bug!("{}", error_msg),
}
self.try_as_ptr().expect(error_msg)
}
/// FIXME(solson): Refactored into a duplicate of `expect_ptr`. Investigate removal.
pub fn expect_fn_ptr(self, error_msg: &str) -> Pointer {
match self.kind {
PrimValKind::FnPtr(alloc_id) => Pointer::new(alloc_id, self.bits as usize),
_ => bug!("{}", error_msg),
}
self.try_as_ptr().expect(error_msg)
}
}
@ -255,22 +277,19 @@ pub fn binary_op<'tcx>(
use rustc::mir::repr::BinOp::*;
use self::PrimValKind::*;
match (left.kind, right.kind) {
(FnPtr(_), Ptr(_)) |
(Ptr(_), FnPtr(_)) => return Ok((unrelated_ptr_ops(bin_op)?, false)),
(Ptr(l_alloc), Ptr(r_alloc)) if l_alloc != r_alloc
=> return Ok((unrelated_ptr_ops(bin_op)?, false)),
(FnPtr(l_alloc), FnPtr(r_alloc)) => {
match bin_op {
Eq => return Ok((PrimVal::from_bool(l_alloc == r_alloc), false)),
Ne => return Ok((PrimVal::from_bool(l_alloc != r_alloc), false)),
_ => {}
match (left.try_as_ptr(), right.try_as_ptr()) {
(Some(left_ptr), Some(right_ptr)) => {
if left_ptr.alloc_id != right_ptr.alloc_id {
return Ok((unrelated_ptr_ops(bin_op)?, false));
}
// If the pointers are into the same allocation, fall through to the more general match
// later, which will do comparisons on the `bits` fields, which are the pointer offsets
// in this case.
}
_ => {}
(None, None) => {}
_ => unimplemented!(),
}
let (l, r) = (left.bits, right.bits);