mirror of
https://github.com/rust-lang/rust.git
synced 2025-05-09 00:17:44 +00:00
implement more casts
This commit is contained in:
parent
ad053d66fe
commit
0f177fdecf
@ -28,7 +28,7 @@ pub enum EvalError<'tcx> {
|
||||
ExecuteMemory,
|
||||
ArrayIndexOutOfBounds(Span, u64, u64),
|
||||
Math(Span, ConstMathErr),
|
||||
InvalidChar(u32),
|
||||
InvalidChar(u64),
|
||||
OutOfMemory {
|
||||
allocation_size: usize,
|
||||
memory_size: usize,
|
||||
|
96
src/interpreter/cast.rs
Normal file
96
src/interpreter/cast.rs
Normal 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))),
|
||||
}
|
||||
}
|
||||
}
|
@ -22,6 +22,7 @@ use std::collections::HashMap;
|
||||
|
||||
mod step;
|
||||
mod terminator;
|
||||
mod cast;
|
||||
|
||||
pub struct EvalContext<'a, 'tcx: 'a> {
|
||||
/// 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 static_ptr = self.memory.allocate(s.len(), 1)?;
|
||||
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_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)
|
||||
}
|
||||
ByteStr(ref bs) => {
|
||||
@ -244,6 +246,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
@ -558,12 +562,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||
|
||||
Ref(_, _, ref 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 {
|
||||
LvalueExtra::None => {},
|
||||
LvalueExtra::Length(len) => {
|
||||
let len_ptr = dest.offset(self.memory.pointer_size() as isize);
|
||||
self.memory.write_usize(len_ptr, len)?;
|
||||
self.memory.write_usize(extra, len)?;
|
||||
}
|
||||
LvalueExtra::DowncastVariant(..) =>
|
||||
bug!("attempted to take a reference to an enum downcast lvalue"),
|
||||
@ -583,14 +587,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||
Unsize => {
|
||||
let src = self.eval_operand(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 dest_pointee_ty = pointee_type(dest_ty).unwrap();
|
||||
|
||||
match (&src_pointee_ty.sty, &dest_pointee_ty.sty) {
|
||||
(&ty::TyArray(_, length), &ty::TySlice(_)) => {
|
||||
let len_ptr = dest.offset(self.memory.pointer_size() as isize);
|
||||
self.memory.write_usize(len_ptr, length as u64)?;
|
||||
self.memory.write_usize(extra, length as u64)?;
|
||||
}
|
||||
|
||||
_ => return Err(EvalError::Unimplemented(format!("can't handle cast: {:?}", rvalue))),
|
||||
@ -600,20 +604,28 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||
Misc => {
|
||||
let src = self.eval_operand(operand)?;
|
||||
let src_ty = self.operand_ty(operand);
|
||||
// FIXME(solson): Wrong for almost everything.
|
||||
warn!("misc cast from {:?} to {:?}", src_ty, dest_ty);
|
||||
let dest_size = self.type_size(dest_ty);
|
||||
let src_size = self.type_size(src_ty);
|
||||
let dest_align = self.type_align(dest_ty);
|
||||
|
||||
// Hack to support fat pointer -> thin pointer casts to keep tests for
|
||||
// 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)?;
|
||||
if self.type_is_immediate(src_ty) {
|
||||
// FIXME: dest_ty should already be monomorphized
|
||||
let dest_ty = self.monomorphize(dest_ty, self.substs());
|
||||
assert!(self.type_is_immediate(dest_ty));
|
||||
let src_val = self.read_primval(src, src_ty)?;
|
||||
let dest_val = self.cast_primval(src_val, dest_ty)?;
|
||||
self.memory.write_primval(dest, dest_val)?;
|
||||
} 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(())
|
||||
}
|
||||
|
||||
/// 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> {
|
||||
// Skip the constant 0 at the start meant for LLVM GEP.
|
||||
let mut path = discrfield.iter().skip(1).map(|&i| i as usize);
|
||||
@ -809,11 +850,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||
|
||||
Deref => {
|
||||
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 extra = match pointee_ty.sty {
|
||||
ty::TySlice(_) | ty::TyStr => {
|
||||
let len_ptr = base.ptr.offset(self.memory.pointer_size() as isize);
|
||||
let len = self.memory.read_usize(len_ptr)?;
|
||||
let (_, extra) = self.get_fat_ptr(base.ptr);
|
||||
let len = self.memory.read_usize(extra)?;
|
||||
LvalueExtra::Length(len)
|
||||
}
|
||||
ty::TyTrait(_) => unimplemented!(),
|
||||
@ -842,6 +884,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||
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> {
|
||||
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;
|
||||
match ::std::char::from_u32(c) {
|
||||
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),
|
||||
@ -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),
|
||||
};
|
||||
Ok(val)
|
||||
|
@ -47,7 +47,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||
let discr_val = self.memory.read_uint(discr_ptr, discr_size)?;
|
||||
if let ty::TyChar = discr_ty.sty {
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -455,8 +455,8 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
|
||||
PrimVal::IntegerPtr(n) => self.write_uint(ptr, n as u64, pointer_size),
|
||||
PrimVal::F32(f) => self.write_f32(ptr, f),
|
||||
PrimVal::F64(f) => self.write_f64(ptr, f),
|
||||
PrimVal::FnPtr(_p) |
|
||||
PrimVal::AbstractPtr(_p) => unimplemented!(),
|
||||
PrimVal::FnPtr(p) |
|
||||
PrimVal::AbstractPtr(p) => self.write_ptr(ptr, p),
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user