diff --git a/src/interpreter/cast.rs b/src/interpreter/cast.rs index 427b8aed0f1..70b39fc882e 100644 --- a/src/interpreter/cast.rs +++ b/src/interpreter/cast.rs @@ -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) } } diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 463b9bf0063..1e37d3ec753 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -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; diff --git a/src/interpreter/terminator/intrinsics.rs b/src/interpreter/terminator/intrinsics.rs index c3327dd76f9..bb88e3864ee 100644 --- a/src/interpreter/terminator/intrinsics.rs +++ b/src/interpreter/terminator/intrinsics.rs @@ -19,10 +19,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { dest_ty: Ty<'tcx>, dest_layout: &'tcx Layout, ) -> EvalResult<'tcx, ()> { - let args_ptrs: EvalResult> = args.iter() + let arg_vals: EvalResult> = 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" => { diff --git a/src/interpreter/terminator/mod.rs b/src/interpreter/terminator/mod.rs index be6147f8b74..1a7f985d20a 100644 --- a/src/interpreter/terminator/mod.rs +++ b/src/interpreter/terminator/mod.rs @@ -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 { diff --git a/src/interpreter/value.rs b/src/interpreter/value.rs index 09b0f8345d8..22698f97818 100644 --- a/src/interpreter/value.rs +++ b/src/interpreter/value.rs @@ -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)) } diff --git a/src/memory.rs b/src/memory.rs index 6979d7ef339..73770ee403f 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -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> { diff --git a/src/primval.rs b/src/primval.rs index 5008aa24a2a..597bee6b402 100644 --- a/src/primval.rs +++ b/src/primval.rs @@ -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, + + // 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 { + 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);