handle ptr-int casts explicitly in cast.rs
This commit is contained in:
parent
7b7f690274
commit
f0c8df2291
21
src/cast.rs
21
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))),
|
||||
}
|
||||
}
|
||||
|
@ -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 =>
|
||||
|
@ -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 {
|
||||
|
@ -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 };
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user