diff --git a/src/interpreter/cast.rs b/src/interpreter/cast.rs index e59376c32135756de9b11ec1ad7f43220434be4d..217a10a4c7bccb60d8fd53d49d3cceafddc49d26 100644 --- a/src/interpreter/cast.rs +++ b/src/interpreter/cast.rs @@ -21,10 +21,7 @@ pub(super) fn cast_primval(&self, val: PrimVal, ty: Ty<'tcx>) -> EvalResult<'tcx Bool | Char | U8 | U16 | U32 | U64 => self.cast_int(val.bits, ty, false), - FnPtr | Ptr => { - let ptr = val.expect_ptr("FnPtr- or Ptr-tagged PrimVal had no relocation"); - self.cast_ptr(ptr, ty) - } + FnPtr | Ptr => self.cast_ptr(val.to_ptr(), ty), } } diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index a67142e7335d2b54625d9297c320205b10e80d36..791466f6908bfe402c2d7474eeb37b095ce466c6 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -963,33 +963,27 @@ fn eval_lvalue_projection( } Deref => { - use interpreter::value::Value::*; + let val = self.eval_and_read_lvalue(&proj.base)?; - let val = match self.eval_and_read_lvalue(&proj.base)? { - ByRef(ptr) => self.read_value(ptr, base_ty)?, - v => v, + let pointee_type = match base_ty.sty { + ty::TyRawPtr(ty::TypeAndMut{ty, ..}) | + ty::TyRef(_, ty::TypeAndMut{ty, ..}) | + ty::TyBox(ty) => ty, + _ => bug!("can only deref pointer types"), }; - match val { - 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(ptr, n) if ptr.try_as_ptr().is_some() => { - let ptr = ptr.try_as_ptr().unwrap(); - (ptr, LvalueExtra::Length(n.expect_uint("slice length"))) - } + trace!("deref to {} on {:?}", pointee_type, val); - ByVal(ptr) if ptr.try_as_ptr().is_some() => { - let ptr = ptr.try_as_ptr().unwrap(); - (ptr, LvalueExtra::None) - } - - _ => bug!("can't deref non pointer types"), + match self.tcx.struct_tail(pointee_type).sty { + ty::TyTrait(_) => { + let (ptr, vtable) = val.expect_ptr_vtable_pair(&self.memory)?; + (ptr, LvalueExtra::Vtable(vtable)) + }, + ty::TyStr | ty::TySlice(_) => { + let (ptr, len) = val.expect_slice(&self.memory)?; + (ptr, LvalueExtra::Length(len)) + }, + _ => (val.read_ptr(&self.memory)?, LvalueExtra::None), } } diff --git a/src/interpreter/terminator/intrinsics.rs b/src/interpreter/terminator/intrinsics.rs index 280cabc07859365e07cf17da0722b0d2dc8266f0..bc258574665d1e870615f7b750a09efc490f787e 100644 --- a/src/interpreter/terminator/intrinsics.rs +++ b/src/interpreter/terminator/intrinsics.rs @@ -124,15 +124,17 @@ pub(super) fn call_intrinsic( "drop_in_place" => { let ty = substs.type_at(0); + trace!("drop in place on {}", ty); let ptr_ty = self.tcx.mk_mut_ptr(ty); let lvalue = match self.follow_by_ref_value(arg_vals[0], ptr_ty)? { Value::ByRef(_) => bug!("follow_by_ref_value returned ByRef"), - Value::ByVal(ptr) => Lvalue::from_ptr(ptr.expect_ptr("drop_in_place first arg not a pointer")), + Value::ByVal(value) => Lvalue::from_ptr(value.to_ptr()), Value::ByValPair(ptr, extra) => Lvalue::Ptr { - ptr: ptr.expect_ptr("drop_in_place first arg not a pointer"), - extra: match extra.try_as_ptr() { - Some(vtable) => LvalueExtra::Vtable(vtable), - None => LvalueExtra::Length(extra.expect_uint("either pointer or not, but not neither")), + ptr: ptr.to_ptr(), + extra: match self.tcx.struct_tail(ty).sty { + ty::TyTrait(_) => LvalueExtra::Vtable(extra.to_ptr()), + ty::TyStr | ty::TySlice(_) => LvalueExtra::Length(extra.try_as_uint()?), + _ => bug!("invalid fat pointer type: {}", ptr_ty), }, }, }; @@ -440,7 +442,7 @@ fn size_and_align_of_dst( ty::TySlice(_) | ty::TyStr => { let elem_ty = ty.sequence_element_type(self.tcx); let elem_size = self.type_size(elem_ty).expect("slice element must be sized") as u64; - let len = value.expect_slice_len(&self.memory)?; + let (_, len) = value.expect_slice(&self.memory)?; let align = self.type_align(elem_ty); Ok((len * elem_size, align as u64)) } diff --git a/src/interpreter/terminator/mod.rs b/src/interpreter/terminator/mod.rs index 655a59902e9ee3ddd4f50e50365bf9e2f820436e..fbd9e76a07f2418cdbe02eb24c51830c655cf4ae 100644 --- a/src/interpreter/terminator/mod.rs +++ b/src/interpreter/terminator/mod.rs @@ -85,8 +85,7 @@ pub(super) fn eval_terminator( let func_ty = self.operand_ty(func); match func_ty.sty { ty::TyFnPtr(bare_fn_ty) => { - let fn_ptr = self.eval_operand_to_primval(func)? - .expect_fn_ptr("TyFnPtr callee did not evaluate to FnPtr"); + let fn_ptr = self.eval_operand_to_primval(func)?.to_ptr(); let (def_id, substs, fn_ty) = self.memory.get_fn(fn_ptr.alloc_id)?; if fn_ty != bare_fn_ty { return Err(EvalError::FunctionPointerTyMismatch(fn_ty, bare_fn_ty)); @@ -542,14 +541,15 @@ fn drop( Value::ByRef(_) => bug!("follow_by_ref_value can't result in ByRef"), Value::ByVal(ptr) => { assert!(self.type_is_sized(contents_ty)); - let contents_ptr = ptr.expect_ptr("value of Box type must be a pointer"); + let contents_ptr = ptr.to_ptr(); self.drop(Lvalue::from_ptr(contents_ptr), contents_ty, drop)?; }, Value::ByValPair(prim_ptr, extra) => { - let ptr = prim_ptr.expect_ptr("value of Box type must be a pointer"); - let extra = match extra.try_as_ptr() { - Some(vtable) => LvalueExtra::Vtable(vtable), - None => LvalueExtra::Length(extra.expect_uint("slice length")), + let ptr = prim_ptr.to_ptr(); + let extra = match self.tcx.struct_tail(contents_ty).sty { + ty::TyTrait(_) => LvalueExtra::Vtable(extra.to_ptr()), + ty::TyStr | ty::TySlice(_) => LvalueExtra::Length(extra.try_as_uint()?), + _ => bug!("invalid fat pointer type: {}", ty), }; self.drop( Lvalue::Ptr { diff --git a/src/interpreter/value.rs b/src/interpreter/value.rs index d9285a41e41ab545d38105fce596da952d01ab98..fa89d02ad77d12dc151cff4248a99c5813d307bc 100644 --- a/src/interpreter/value.rs +++ b/src/interpreter/value.rs @@ -22,10 +22,7 @@ pub(super) fn read_ptr(&self, mem: &Memory<'a, 'tcx>) -> EvalResult<'tcx, Pointe use self::Value::*; match *self { ByRef(ptr) => mem.read_ptr(ptr), - - ByVal(ptr) | ByValPair(ptr, _) => { - Ok(ptr.try_as_ptr().expect("unimplemented: `read_ptr` on non-ptr primval")) - } + ByVal(ptr) | ByValPair(ptr, _) => Ok(ptr.to_ptr()), } } @@ -35,29 +32,29 @@ pub(super) fn expect_ptr_vtable_pair( ) -> EvalResult<'tcx, (Pointer, Pointer)> { use self::Value::*; match *self { - ByRef(ptr) => { - let ptr = mem.read_ptr(ptr)?; - let vtable = mem.read_ptr(ptr.offset(mem.pointer_size() as isize))?; + ByRef(ref_ptr) => { + let ptr = mem.read_ptr(ref_ptr)?; + let vtable = mem.read_ptr(ref_ptr.offset(mem.pointer_size() as isize))?; Ok((ptr, vtable)) } - 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)) - } + ByValPair(ptr, vtable) => Ok((ptr.to_ptr(), vtable.to_ptr())), _ => bug!("expected ptr and vtable, got {:?}", self), } } - pub(super) fn expect_slice_len(&self, mem: &Memory<'a, 'tcx>) -> EvalResult<'tcx, u64> { + pub(super) fn expect_slice(&self, mem: &Memory<'a, 'tcx>) -> EvalResult<'tcx, (Pointer, u64)> { use self::Value::*; match *self { - ByRef(ptr) => mem.read_usize(ptr.offset(mem.pointer_size() as isize)), - ByValPair(_, val) if val.kind.is_int() => Ok(val.bits), + ByRef(ref_ptr) => { + let ptr = mem.read_ptr(ref_ptr)?; + let len = mem.read_usize(ref_ptr.offset(mem.pointer_size() as isize))?; + Ok((ptr, len)) + }, + ByValPair(ptr, val) => { + Ok((ptr.to_ptr(), val.try_as_uint()?)) + }, _ => unimplemented!(), } } diff --git a/src/memory.rs b/src/memory.rs index 412eb62e9aefe92aa253c755b0030f64edd7a93f..348437f50b6460e232b246afa17804225d4f60d2 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -533,8 +533,8 @@ pub fn write_ptr(&mut self, dest: Pointer, ptr: Pointer) -> EvalResult<'tcx, ()> } 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); + if let Some(alloc_id) = val.relocation { + return self.write_ptr(dest, Pointer::new(alloc_id, val.bits as usize)); } use primval::PrimValKind::*; diff --git a/src/primval.rs b/src/primval.rs index 2d8b50076234f46bc142d9e8747f68c8e5ee17aa..15cc88ca4cf9ec53d3fb93fafb9b8462842349a2 100644 --- a/src/primval.rs +++ b/src/primval.rs @@ -137,15 +137,19 @@ pub fn to_f64(self) -> f64 { bits_to_f64(self.bits) } - pub fn try_as_ptr(self) -> Option { + pub fn to_ptr(self) -> Pointer { self.relocation.map(|alloc_id| { Pointer::new(alloc_id, self.bits as usize) - }) + }).unwrap_or_else(|| Pointer::from_int(self.bits as usize)) + } + + pub fn try_as_uint<'tcx>(self) -> EvalResult<'tcx, u64> { + self.to_ptr().to_int().map(|val| val as u64) } 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 + if let Ok(int) = self.try_as_uint() { + return int; } use self::PrimValKind::*; @@ -156,8 +160,8 @@ pub fn expect_uint(self, error_msg: &str) -> u64 { } 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 + if let Ok(int) = self.try_as_uint() { + return int as i64; } use self::PrimValKind::*; @@ -188,15 +192,6 @@ pub fn expect_f64(self, error_msg: &str) -> f64 { _ => bug!("{}", error_msg), } } - - pub fn expect_ptr(self, error_msg: &str) -> Pointer { - 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 { - self.try_as_ptr().expect(error_msg) - } } //////////////////////////////////////////////////////////////////////////////// @@ -277,19 +272,13 @@ pub fn binary_op<'tcx>( use rustc::mir::BinOp::*; use self::PrimValKind::*; - 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) => {} - _ => return Err(EvalError::ReadPointerAsBytes), + // 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. + let left_ptr = left.to_ptr(); + let right_ptr = right.to_ptr(); + if left_ptr.alloc_id != right_ptr.alloc_id { + return Ok((unrelated_ptr_ops(bin_op, left_ptr, right_ptr)?, false)); } let (l, r) = (left.bits, right.bits); @@ -376,12 +365,15 @@ pub fn binary_op<'tcx>( Ok((val, false)) } -fn unrelated_ptr_ops<'tcx>(bin_op: mir::BinOp) -> EvalResult<'tcx, PrimVal> { +fn unrelated_ptr_ops<'tcx>(bin_op: mir::BinOp, left: Pointer, right: Pointer) -> EvalResult<'tcx, PrimVal> { use rustc::mir::BinOp::*; match bin_op { Eq => Ok(PrimVal::from_bool(false)), Ne => Ok(PrimVal::from_bool(true)), Lt | Le | Gt | Ge => Err(EvalError::InvalidPointerMath), + _ if left.to_int().is_ok() ^ right.to_int().is_ok() => { + Err(EvalError::ReadPointerAsBytes) + }, _ => bug!(), } } diff --git a/tests/compile-fail/cast_int_to_fn_ptr.rs b/tests/compile-fail/cast_int_to_fn_ptr.rs new file mode 100644 index 0000000000000000000000000000000000000000..dc39f7dda1b638dac4136c1ba8fa2e82ff4f53ca --- /dev/null +++ b/tests/compile-fail/cast_int_to_fn_ptr.rs @@ -0,0 +1,7 @@ +fn main() { + let g = unsafe { + std::mem::transmute::(42) + }; + + g(42) //~ ERROR tried to use an integer pointer or a dangling pointer as a function pointer +}