implement more casts

This commit is contained in:
Oliver Schneider 2016-09-07 18:34:59 +02:00
parent ad053d66fe
commit 0f177fdecf
No known key found for this signature in database
GPG Key ID: 56D6EEA0FC67AC46
5 changed files with 190 additions and 27 deletions

View File

@ -28,7 +28,7 @@ pub enum EvalError<'tcx> {
ExecuteMemory, ExecuteMemory,
ArrayIndexOutOfBounds(Span, u64, u64), ArrayIndexOutOfBounds(Span, u64, u64),
Math(Span, ConstMathErr), Math(Span, ConstMathErr),
InvalidChar(u32), InvalidChar(u64),
OutOfMemory { OutOfMemory {
allocation_size: usize, allocation_size: usize,
memory_size: usize, memory_size: usize,

96
src/interpreter/cast.rs Normal file
View File

@ -0,0 +1,96 @@
use super::{
EvalContext,
};
use error::{EvalResult, EvalError};
use rustc::ty;
use primval::PrimVal;
use memory::Pointer;
use rustc::ty::Ty;
use syntax::ast;
impl<'a, 'tcx> EvalContext<'a, 'tcx> {
pub(super) fn cast_primval(&self, val: PrimVal, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> {
use primval::PrimVal::*;
match val {
Bool(b) => self.cast_const_int(b as u64, ty, false),
F32(f) => self.cast_const_float(f as f64, ty),
F64(f) => self.cast_const_float(f, ty),
I8(i) => self.cast_signed_int(i as i64, ty),
I16(i) => self.cast_signed_int(i as i64, ty),
I32(i) => self.cast_signed_int(i as i64, ty),
I64(i) => self.cast_signed_int(i, ty),
U8(u) => self.cast_const_int(u as u64, ty, false),
U16(u) => self.cast_const_int(u as u64, ty, false),
U32(u) => self.cast_const_int(u as u64, ty, false),
Char(c) => self.cast_const_int(c as u64, ty, false),
U64(u) |
IntegerPtr(u) => self.cast_const_int(u, ty, false),
FnPtr(ptr) |
AbstractPtr(ptr) => self.cast_ptr(ptr, ty),
}
}
fn cast_ptr(&self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> {
use primval::PrimVal::*;
match ty.sty {
ty::TyRef(..) |
ty::TyRawPtr(_) => Ok(AbstractPtr(ptr)),
ty::TyFnPtr(_) => Ok(FnPtr(ptr)),
_ => Err(EvalError::Unimplemented(format!("ptr to {:?} cast", ty))),
}
}
fn cast_signed_int(&self, val: i64, ty: ty::Ty<'tcx>) -> EvalResult<'tcx, PrimVal> {
self.cast_const_int(val as u64, ty, val < 0)
}
fn cast_const_int(&self, v: u64, ty: ty::Ty<'tcx>, negative: bool) -> EvalResult<'tcx, PrimVal> {
use primval::PrimVal::*;
match ty.sty {
ty::TyBool if v == 0 => Ok(Bool(false)),
ty::TyBool if v == 1 => Ok(Bool(true)),
ty::TyBool => Err(EvalError::InvalidBool),
ty::TyInt(ast::IntTy::I8) => Ok(I8(v as i64 as i8)),
ty::TyInt(ast::IntTy::I16) => Ok(I16(v as i64 as i16)),
ty::TyInt(ast::IntTy::I32) => Ok(I32(v as i64 as i32)),
ty::TyInt(ast::IntTy::I64) => Ok(I64(v as i64)),
ty::TyInt(ast::IntTy::Is) => {
let int_ty = self.tcx.sess.target.int_type;
let ty = self.tcx.mk_mach_int(int_ty);
self.cast_const_int(v, ty, negative)
},
ty::TyUint(ast::UintTy::U8) => Ok(U8(v as u8)),
ty::TyUint(ast::UintTy::U16) => Ok(U16(v as u16)),
ty::TyUint(ast::UintTy::U32) => Ok(U32(v as u32)),
ty::TyUint(ast::UintTy::U64) => Ok(U64(v)),
ty::TyUint(ast::UintTy::Us) => {
let uint_ty = self.tcx.sess.target.uint_type;
let ty = self.tcx.mk_mach_uint(uint_ty);
self.cast_const_int(v, ty, negative)
},
ty::TyFloat(ast::FloatTy::F64) if negative => Ok(F64(v as i64 as f64)),
ty::TyFloat(ast::FloatTy::F64) => Ok(F64(v as f64)),
ty::TyFloat(ast::FloatTy::F32) if negative => Ok(F32(v as i64 as f32)),
ty::TyFloat(ast::FloatTy::F32) => Ok(F32(v as f32)),
ty::TyRawPtr(_) => Ok(IntegerPtr(v)),
ty::TyChar if v as u8 as u64 == v => Ok(Char(v as u8 as char)),
ty::TyChar => Err(EvalError::InvalidChar(v)),
_ => Err(EvalError::Unimplemented(format!("int to {:?} cast", ty))),
}
}
fn cast_const_float(&self, val: f64, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> {
use primval::PrimVal::*;
match ty.sty {
// casting negative floats to unsigned integers yields zero
ty::TyUint(_) if val < 0.0 => self.cast_const_int(0, ty, false),
ty::TyInt(_) if val < 0.0 => self.cast_const_int(val as i64 as u64, ty, true),
ty::TyInt(_) | ty::TyUint(_) => self.cast_const_int(val as u64, ty, false),
ty::TyFloat(ast::FloatTy::F64) => Ok(F64(val)),
ty::TyFloat(ast::FloatTy::F32) => Ok(F32(val as f32)),
_ => Err(EvalError::Unimplemented(format!("float to {:?} cast", ty))),
}
}
}

View File

@ -22,6 +22,7 @@ use std::collections::HashMap;
mod step; mod step;
mod terminator; mod terminator;
mod cast;
pub struct EvalContext<'a, 'tcx: 'a> { pub struct EvalContext<'a, 'tcx: 'a> {
/// The results of the type checker, from rustc. /// The results of the type checker, from rustc.
@ -211,9 +212,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
let psize = self.memory.pointer_size(); let psize = self.memory.pointer_size();
let static_ptr = self.memory.allocate(s.len(), 1)?; let static_ptr = self.memory.allocate(s.len(), 1)?;
let ptr = self.memory.allocate(psize * 2, psize)?; let ptr = self.memory.allocate(psize * 2, psize)?;
let (ptr, extra) = self.get_fat_ptr(ptr);
self.memory.write_bytes(static_ptr, s.as_bytes())?; self.memory.write_bytes(static_ptr, s.as_bytes())?;
self.memory.write_ptr(ptr, static_ptr)?; self.memory.write_ptr(ptr, static_ptr)?;
self.memory.write_usize(ptr.offset(psize as isize), s.len() as u64)?; self.memory.write_usize(extra, s.len() as u64)?;
Ok(ptr) Ok(ptr)
} }
ByteStr(ref bs) => { ByteStr(ref bs) => {
@ -244,6 +246,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
} }
fn type_is_sized(&self, ty: Ty<'tcx>) -> bool { fn type_is_sized(&self, ty: Ty<'tcx>) -> bool {
// generics are weird, don't run this function on a generic
debug_assert_eq!(self.monomorphize(ty, self.substs()), ty);
ty.is_sized(self.tcx, &self.tcx.empty_parameter_environment(), DUMMY_SP) ty.is_sized(self.tcx, &self.tcx.empty_parameter_environment(), DUMMY_SP)
} }
@ -558,12 +562,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
Ref(_, _, ref lvalue) => { Ref(_, _, ref lvalue) => {
let lv = self.eval_lvalue(lvalue)?; let lv = self.eval_lvalue(lvalue)?;
self.memory.write_ptr(dest, lv.ptr)?; let (ptr, extra) = self.get_fat_ptr(dest);
self.memory.write_ptr(ptr, lv.ptr)?;
match lv.extra { match lv.extra {
LvalueExtra::None => {}, LvalueExtra::None => {},
LvalueExtra::Length(len) => { LvalueExtra::Length(len) => {
let len_ptr = dest.offset(self.memory.pointer_size() as isize); self.memory.write_usize(extra, len)?;
self.memory.write_usize(len_ptr, len)?;
} }
LvalueExtra::DowncastVariant(..) => LvalueExtra::DowncastVariant(..) =>
bug!("attempted to take a reference to an enum downcast lvalue"), bug!("attempted to take a reference to an enum downcast lvalue"),
@ -583,14 +587,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
Unsize => { Unsize => {
let src = self.eval_operand(operand)?; let src = self.eval_operand(operand)?;
let src_ty = self.operand_ty(operand); let src_ty = self.operand_ty(operand);
self.move_(src, dest, src_ty)?; let (ptr, extra) = self.get_fat_ptr(dest);
self.move_(src, ptr, src_ty)?;
let src_pointee_ty = pointee_type(src_ty).unwrap(); let src_pointee_ty = pointee_type(src_ty).unwrap();
let dest_pointee_ty = pointee_type(dest_ty).unwrap(); let dest_pointee_ty = pointee_type(dest_ty).unwrap();
match (&src_pointee_ty.sty, &dest_pointee_ty.sty) { match (&src_pointee_ty.sty, &dest_pointee_ty.sty) {
(&ty::TyArray(_, length), &ty::TySlice(_)) => { (&ty::TyArray(_, length), &ty::TySlice(_)) => {
let len_ptr = dest.offset(self.memory.pointer_size() as isize); self.memory.write_usize(extra, length as u64)?;
self.memory.write_usize(len_ptr, length as u64)?;
} }
_ => return Err(EvalError::Unimplemented(format!("can't handle cast: {:?}", rvalue))), _ => return Err(EvalError::Unimplemented(format!("can't handle cast: {:?}", rvalue))),
@ -600,20 +604,28 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
Misc => { Misc => {
let src = self.eval_operand(operand)?; let src = self.eval_operand(operand)?;
let src_ty = self.operand_ty(operand); let src_ty = self.operand_ty(operand);
// FIXME(solson): Wrong for almost everything. if self.type_is_immediate(src_ty) {
warn!("misc cast from {:?} to {:?}", src_ty, dest_ty); // FIXME: dest_ty should already be monomorphized
let dest_size = self.type_size(dest_ty); let dest_ty = self.monomorphize(dest_ty, self.substs());
let src_size = self.type_size(src_ty); assert!(self.type_is_immediate(dest_ty));
let dest_align = self.type_align(dest_ty); let src_val = self.read_primval(src, src_ty)?;
let dest_val = self.cast_primval(src_val, dest_ty)?;
// Hack to support fat pointer -> thin pointer casts to keep tests for self.memory.write_primval(dest, dest_val)?;
// other things passing for now.
let is_fat_ptr_cast = pointee_type(src_ty).map_or(false, |ty| !self.type_is_sized(ty));
if dest_size == src_size || is_fat_ptr_cast {
self.memory.copy(src, dest, dest_size, dest_align)?;
} else { } else {
return Err(EvalError::Unimplemented(format!("can't handle cast: {:?}", rvalue))); // Casts from a fat-ptr.
assert!(self.type_is_fat_ptr(src_ty));
let (data_ptr, _meta_ptr) = self.get_fat_ptr(src);
let ptr_size = self.memory.pointer_size();
let dest_ty = self.monomorphize(dest_ty, self.substs());
if self.type_is_fat_ptr(dest_ty) {
// FIXME: add assertion that the extra part of the src_ty and
// dest_ty is of the same type
self.memory.copy(data_ptr, dest, ptr_size * 2, ptr_size)?;
} else { // cast to thin-ptr
// Cast of fat-ptr to thin-ptr is an extraction of data-ptr and
// pointer-cast of that pointer to desired pointer type.
self.memory.copy(data_ptr, dest, ptr_size, ptr_size)?;
}
} }
} }
@ -644,6 +656,35 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
Ok(()) Ok(())
} }
/// equivalent to rustc_trans::common::type_is_immediate
fn type_is_immediate(&self, ty: Ty<'tcx>) -> bool {
let simple = ty.is_scalar() ||
ty.is_unique() || ty.is_region_ptr() ||
ty.is_simd();
if simple && !self.type_is_fat_ptr(ty) {
return true;
}
if !self.type_is_sized(ty) {
return false;
}
match ty.sty {
ty::TyStruct(..) | ty::TyEnum(..) | ty::TyTuple(..) | ty::TyArray(_, _) |
ty::TyClosure(..) => {
self.type_size(ty) < self.memory.pointer_size()
}
_ => self.type_size(ty) == 0
}
}
fn type_is_fat_ptr(&self, ty: Ty<'tcx>) -> bool {
match ty.sty {
ty::TyRawPtr(ty::TypeAndMut{ty, ..}) |
ty::TyRef(_, ty::TypeAndMut{ty, ..}) |
ty::TyBox(ty) => !self.type_is_sized(ty),
_ => false,
}
}
fn nonnull_offset(&self, ty: Ty<'tcx>, nndiscr: u64, discrfield: &[u32]) -> EvalResult<'tcx, Size> { fn nonnull_offset(&self, ty: Ty<'tcx>, nndiscr: u64, discrfield: &[u32]) -> EvalResult<'tcx, Size> {
// Skip the constant 0 at the start meant for LLVM GEP. // Skip the constant 0 at the start meant for LLVM GEP.
let mut path = discrfield.iter().skip(1).map(|&i| i as usize); let mut path = discrfield.iter().skip(1).map(|&i| i as usize);
@ -809,11 +850,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
Deref => { Deref => {
let pointee_ty = pointee_type(base_ty).expect("Deref of non-pointer"); let pointee_ty = pointee_type(base_ty).expect("Deref of non-pointer");
self.memory.dump(base.ptr.alloc_id);
let ptr = self.memory.read_ptr(base.ptr)?; let ptr = self.memory.read_ptr(base.ptr)?;
let extra = match pointee_ty.sty { let extra = match pointee_ty.sty {
ty::TySlice(_) | ty::TyStr => { ty::TySlice(_) | ty::TyStr => {
let len_ptr = base.ptr.offset(self.memory.pointer_size() as isize); let (_, extra) = self.get_fat_ptr(base.ptr);
let len = self.memory.read_usize(len_ptr)?; let len = self.memory.read_usize(extra)?;
LvalueExtra::Length(len) LvalueExtra::Length(len)
} }
ty::TyTrait(_) => unimplemented!(), ty::TyTrait(_) => unimplemented!(),
@ -842,6 +884,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
Ok(Lvalue { ptr: ptr, extra: LvalueExtra::None }) Ok(Lvalue { ptr: ptr, extra: LvalueExtra::None })
} }
fn get_fat_ptr(&self, ptr: Pointer) -> (Pointer, Pointer) {
assert_eq!(layout::FAT_PTR_ADDR, 0);
assert_eq!(layout::FAT_PTR_EXTRA, 1);
(ptr, ptr.offset(self.memory.pointer_size() as isize))
}
fn lvalue_ty(&self, lvalue: &mir::Lvalue<'tcx>) -> Ty<'tcx> { fn lvalue_ty(&self, lvalue: &mir::Lvalue<'tcx>) -> Ty<'tcx> {
self.monomorphize(lvalue.ty(&self.mir(), self.tcx).to_ty(self.tcx), self.substs()) self.monomorphize(lvalue.ty(&self.mir(), self.tcx).to_ty(self.tcx), self.substs())
} }
@ -865,7 +913,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
let c = self.memory.read_uint(ptr, 4)? as u32; let c = self.memory.read_uint(ptr, 4)? as u32;
match ::std::char::from_u32(c) { match ::std::char::from_u32(c) {
Some(ch) => PrimVal::Char(ch), Some(ch) => PrimVal::Char(ch),
None => return Err(EvalError::InvalidChar(c)), None => return Err(EvalError::InvalidChar(c as u64)),
} }
} }
(_, &ty::TyInt(IntTy::I8)) => PrimVal::I8(self.memory.read_int(ptr, 1)? as i8), (_, &ty::TyInt(IntTy::I8)) => PrimVal::I8(self.memory.read_int(ptr, 1)? as i8),
@ -905,6 +953,25 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
} }
} }
(_, &ty::TyEnum(..)) => {
use rustc::ty::layout::Layout::*;
if let CEnum { discr, signed, .. } = *self.type_layout(ty) {
match (discr.size().bytes(), signed) {
(1, true) => PrimVal::I8(self.memory.read_int(ptr, 1)? as i8),
(2, true) => PrimVal::I16(self.memory.read_int(ptr, 2)? as i16),
(4, true) => PrimVal::I32(self.memory.read_int(ptr, 4)? as i32),
(8, true) => PrimVal::I64(self.memory.read_int(ptr, 8)? as i64),
(1, false) => PrimVal::U8(self.memory.read_uint(ptr, 1)? as u8),
(2, false) => PrimVal::U16(self.memory.read_uint(ptr, 2)? as u16),
(4, false) => PrimVal::U32(self.memory.read_uint(ptr, 4)? as u32),
(8, false) => PrimVal::U64(self.memory.read_uint(ptr, 8)? as u64),
(size, _) => bug!("CEnum discr size {}", size),
}
} else {
bug!("primitive read of non-clike enum: {:?}", ty);
}
},
_ => bug!("primitive read of non-primitive type: {:?}", ty), _ => bug!("primitive read of non-primitive type: {:?}", ty),
}; };
Ok(val) Ok(val)

View File

@ -47,7 +47,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
let discr_val = self.memory.read_uint(discr_ptr, discr_size)?; let discr_val = self.memory.read_uint(discr_ptr, discr_size)?;
if let ty::TyChar = discr_ty.sty { if let ty::TyChar = discr_ty.sty {
if ::std::char::from_u32(discr_val as u32).is_none() { if ::std::char::from_u32(discr_val as u32).is_none() {
return Err(EvalError::InvalidChar(discr_val as u32)); return Err(EvalError::InvalidChar(discr_val as u64));
} }
} }

View File

@ -455,8 +455,8 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
PrimVal::IntegerPtr(n) => self.write_uint(ptr, n as u64, pointer_size), PrimVal::IntegerPtr(n) => self.write_uint(ptr, n as u64, pointer_size),
PrimVal::F32(f) => self.write_f32(ptr, f), PrimVal::F32(f) => self.write_f32(ptr, f),
PrimVal::F64(f) => self.write_f64(ptr, f), PrimVal::F64(f) => self.write_f64(ptr, f),
PrimVal::FnPtr(_p) | PrimVal::FnPtr(p) |
PrimVal::AbstractPtr(_p) => unimplemented!(), PrimVal::AbstractPtr(p) => self.write_ptr(ptr, p),
} }
} }