diff --git a/src/cast.rs b/src/cast.rs index aa24de944a7..cb0b1121709 100644 --- a/src/cast.rs +++ b/src/cast.rs @@ -19,9 +19,21 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { F32 => self.cast_float(val.to_f32()? as f64, dest_ty), F64 => self.cast_float(val.to_f64()?, dest_ty), - I8 | I16 | I32 | I64 | I128 => self.cast_signed_int(val.to_i128()?, dest_ty), + I8 | I16 | I32 | I64 | I128 => { + if val.is_ptr() { + self.cast_ptr(val, dest_ty) + } else { + self.cast_signed_int(val.to_i128()?, dest_ty) + } + }, - Bool | Char | U8 | U16 | U32 | U64 | U128 => self.cast_int(val.to_u128()?, dest_ty, false), + Bool | Char | U8 | U16 | U32 | U64 | U128 => { + if val.is_ptr() { + self.cast_ptr(val, dest_ty) + } else { + self.cast_int(val.to_u128()?, dest_ty, false) + } + }, FnPtr | Ptr => self.cast_ptr(val, dest_ty), } @@ -70,6 +82,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { TyChar if v as u8 as u128 == v => Ok(PrimVal::Bytes(v)), TyChar => Err(EvalError::InvalidChar(v)), + // No alignment check needed for raw pointers TyRawPtr(_) => Ok(PrimVal::Bytes(v % (1 << self.memory.pointer_size()))), _ => Err(EvalError::Unimplemented(format!("int to {:?} cast", ty))), @@ -94,8 +107,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { fn cast_ptr(&self, ptr: PrimVal, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> { use rustc::ty::TypeVariants::*; match ty.sty { - TyRef(..) | TyRawPtr(_) | TyFnPtr(_) | TyInt(_) | TyUint(_) => + // Casting to a reference or fn pointer is not permitted by rustc, no need to support it here. + TyRawPtr(_) | TyInt(IntTy::Is) | TyUint(UintTy::Us) => Ok(ptr), + TyInt(_) | TyUint(_) => Err(EvalError::ReadPointerAsBytes), _ => Err(EvalError::Unimplemented(format!("ptr to {:?} cast", ty))), } } diff --git a/src/error.rs b/src/error.rs index 0c23038c3c3..919232d9eef 100644 --- a/src/error.rs +++ b/src/error.rs @@ -21,7 +21,7 @@ pub enum EvalError<'tcx> { access: bool, allocation_size: u64, }, - NullPointerOutOfBounds, + InvalidNullPointerUsage, ReadPointerAsBytes, ReadBytesAsPointer, InvalidPointerMath, @@ -84,8 +84,8 @@ impl<'tcx> Error for EvalError<'tcx> { "invalid enum discriminant value read", EvalError::PointerOutOfBounds { .. } => "pointer offset outside bounds of allocation", - EvalError::NullPointerOutOfBounds => - "invalid NULL pointer offset", + EvalError::InvalidNullPointerUsage => + "invalid use of NULL pointer", EvalError::ReadPointerAsBytes => "a raw memory access tried to access part of a pointer value as raw bytes", EvalError::ReadBytesAsPointer => diff --git a/src/eval_context.rs b/src/eval_context.rs index 7052a411f7e..ece2b37209c 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -717,19 +717,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { (Value::ByVal(_), _) => bug!("expected fat ptr"), } } else { - // First, try casting - let dest_val = self.value_to_primval(src, src_ty).and_then( - |src_val| { self.cast_primval(src_val, src_ty, dest_ty) }) - // Alternatively, if the sizes are equal, try just reading at the target type - .or_else(|err| { - let size = self.type_size(src_ty)?; - if size.is_some() && size == self.type_size(dest_ty)? { - self.value_to_primval(src, dest_ty) - } else { - Err(err) - } - }); - self.write_value(Value::ByVal(dest_val?), dest, dest_ty)?; + let src_val = self.value_to_primval(src, src_ty)?; + let dest_val = self.cast_primval(src_val, src_ty, dest_ty)?; + self.write_value(Value::ByVal(dest_val), dest, dest_ty)?; } } @@ -908,7 +898,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // allocation. if ptr.is_null()? { // NULL pointers must only be offset by 0 - return if offset == 0 { Ok(ptr) } else { Err(EvalError::NullPointerOutOfBounds) }; + return if offset == 0 { Ok(ptr) } else { Err(EvalError::InvalidNullPointerUsage) }; } // FIXME: assuming here that type size is < i64::max_value() let pointee_size = self.type_size(pointee_ty)?.expect("cannot offset a pointer to an unsized type") as i64; @@ -919,7 +909,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.memory.check_bounds(ptr, false)?; } else if ptr.is_null()? { // We moved *to* a NULL pointer. That seems wrong, LLVM considers the NULL pointer its own small allocation. Reject this, for now. - return Err(EvalError::NullPointerOutOfBounds); + return Err(EvalError::InvalidNullPointerUsage); } Ok(ptr) } else { diff --git a/tests/compile-fail/ptr_int_cast.rs b/tests/compile-fail/ptr_int_cast.rs index b004a18ff46..396c71ebb03 100644 --- a/tests/compile-fail/ptr_int_cast.rs +++ b/tests/compile-fail/ptr_int_cast.rs @@ -1,7 +1,8 @@ fn main() { let x = &1; // Casting down to u8 and back up to a pointer loses too much precision; this must not work. - let x = x as *const i32 as u8; - let x = x as *const i32; //~ ERROR: a raw memory access tried to access part of a pointer value as raw bytes + let x = x as *const i32; + let x = x as u8; //~ ERROR: a raw memory access tried to access part of a pointer value as raw bytes + let x = x as *const i32; let _ = unsafe { *x }; }