Skip to content

Commit 248bfde

Browse files
authored
Merge pull request #50 from oli-obk/casts
implement more casts
2 parents f1dc961 + 168d9e7 commit 248bfde

File tree

5 files changed

+168
-28
lines changed

5 files changed

+168
-28
lines changed

src/error.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ pub enum EvalError<'tcx> {
2828
ExecuteMemory,
2929
ArrayIndexOutOfBounds(Span, u64, u64),
3030
Math(Span, ConstMathErr),
31-
InvalidChar(u32),
31+
InvalidChar(u64),
3232
OutOfMemory {
3333
allocation_size: usize,
3434
memory_size: usize,

src/interpreter/cast.rs

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
2+
use super::{
3+
EvalContext,
4+
};
5+
use error::{EvalResult, EvalError};
6+
use rustc::ty;
7+
use primval::PrimVal;
8+
use memory::Pointer;
9+
10+
use rustc::ty::Ty;
11+
use syntax::ast;
12+
13+
impl<'a, 'tcx> EvalContext<'a, 'tcx> {
14+
pub(super) fn cast_primval(&self, val: PrimVal, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> {
15+
use primval::PrimVal::*;
16+
match val {
17+
Bool(b) => self.cast_const_int(b as u64, ty, false),
18+
F32(f) => self.cast_const_float(f as f64, ty),
19+
F64(f) => self.cast_const_float(f, ty),
20+
I8(i) => self.cast_signed_int(i as i64, ty),
21+
I16(i) => self.cast_signed_int(i as i64, ty),
22+
I32(i) => self.cast_signed_int(i as i64, ty),
23+
I64(i) => self.cast_signed_int(i, ty),
24+
U8(u) => self.cast_const_int(u as u64, ty, false),
25+
U16(u) => self.cast_const_int(u as u64, ty, false),
26+
U32(u) => self.cast_const_int(u as u64, ty, false),
27+
Char(c) => self.cast_const_int(c as u64, ty, false),
28+
U64(u) |
29+
IntegerPtr(u) => self.cast_const_int(u, ty, false),
30+
FnPtr(ptr) |
31+
AbstractPtr(ptr) => self.cast_ptr(ptr, ty),
32+
}
33+
}
34+
35+
fn cast_ptr(&self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> {
36+
use primval::PrimVal::*;
37+
match ty.sty {
38+
ty::TyRef(..) |
39+
ty::TyRawPtr(_) => Ok(AbstractPtr(ptr)),
40+
ty::TyFnPtr(_) => Ok(FnPtr(ptr)),
41+
_ => Err(EvalError::Unimplemented(format!("ptr to {:?} cast", ty))),
42+
}
43+
}
44+
45+
fn cast_signed_int(&self, val: i64, ty: ty::Ty<'tcx>) -> EvalResult<'tcx, PrimVal> {
46+
self.cast_const_int(val as u64, ty, val < 0)
47+
}
48+
49+
fn cast_const_int(&self, v: u64, ty: ty::Ty<'tcx>, negative: bool) -> EvalResult<'tcx, PrimVal> {
50+
use primval::PrimVal::*;
51+
match ty.sty {
52+
ty::TyBool if v == 0 => Ok(Bool(false)),
53+
ty::TyBool if v == 1 => Ok(Bool(true)),
54+
ty::TyBool => Err(EvalError::InvalidBool),
55+
ty::TyInt(ast::IntTy::I8) => Ok(I8(v as i64 as i8)),
56+
ty::TyInt(ast::IntTy::I16) => Ok(I16(v as i64 as i16)),
57+
ty::TyInt(ast::IntTy::I32) => Ok(I32(v as i64 as i32)),
58+
ty::TyInt(ast::IntTy::I64) => Ok(I64(v as i64)),
59+
ty::TyInt(ast::IntTy::Is) => {
60+
let int_ty = self.tcx.sess.target.int_type;
61+
let ty = self.tcx.mk_mach_int(int_ty);
62+
self.cast_const_int(v, ty, negative)
63+
},
64+
ty::TyUint(ast::UintTy::U8) => Ok(U8(v as u8)),
65+
ty::TyUint(ast::UintTy::U16) => Ok(U16(v as u16)),
66+
ty::TyUint(ast::UintTy::U32) => Ok(U32(v as u32)),
67+
ty::TyUint(ast::UintTy::U64) => Ok(U64(v)),
68+
ty::TyUint(ast::UintTy::Us) => {
69+
let uint_ty = self.tcx.sess.target.uint_type;
70+
let ty = self.tcx.mk_mach_uint(uint_ty);
71+
self.cast_const_int(v, ty, negative)
72+
},
73+
ty::TyFloat(ast::FloatTy::F64) if negative => Ok(F64(v as i64 as f64)),
74+
ty::TyFloat(ast::FloatTy::F64) => Ok(F64(v as f64)),
75+
ty::TyFloat(ast::FloatTy::F32) if negative => Ok(F32(v as i64 as f32)),
76+
ty::TyFloat(ast::FloatTy::F32) => Ok(F32(v as f32)),
77+
ty::TyRawPtr(_) => Ok(IntegerPtr(v)),
78+
ty::TyChar if v as u8 as u64 == v => Ok(Char(v as u8 as char)),
79+
ty::TyChar => Err(EvalError::InvalidChar(v)),
80+
_ => Err(EvalError::Unimplemented(format!("int to {:?} cast", ty))),
81+
}
82+
}
83+
84+
fn cast_const_float(&self, val: f64, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> {
85+
use primval::PrimVal::*;
86+
match ty.sty {
87+
// casting negative floats to unsigned integers yields zero
88+
ty::TyUint(_) if val < 0.0 => self.cast_const_int(0, ty, false),
89+
ty::TyInt(_) if val < 0.0 => self.cast_const_int(val as i64 as u64, ty, true),
90+
ty::TyInt(_) | ty::TyUint(_) => self.cast_const_int(val as u64, ty, false),
91+
ty::TyFloat(ast::FloatTy::F64) => Ok(F64(val)),
92+
ty::TyFloat(ast::FloatTy::F32) => Ok(F32(val as f32)),
93+
_ => Err(EvalError::Unimplemented(format!("float to {:?} cast", ty))),
94+
}
95+
}
96+
}

src/interpreter/mod.rs

Lines changed: 68 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use rustc::mir::repr as mir;
55
use rustc::traits::Reveal;
66
use rustc::ty::layout::{self, Layout, Size};
77
use rustc::ty::subst::{self, Subst, Substs};
8-
use rustc::ty::{self, Ty, TyCtxt};
8+
use rustc::ty::{self, Ty, TyCtxt, TypeFoldable};
99
use rustc::util::nodemap::DefIdMap;
1010
use rustc_data_structures::indexed_vec::Idx;
1111
use std::cell::RefCell;
@@ -22,6 +22,7 @@ use std::collections::HashMap;
2222

2323
mod step;
2424
mod terminator;
25+
mod cast;
2526

2627
pub struct EvalContext<'a, 'tcx: 'a> {
2728
/// The results of the type checker, from rustc.
@@ -211,9 +212,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
211212
let psize = self.memory.pointer_size();
212213
let static_ptr = self.memory.allocate(s.len(), 1)?;
213214
let ptr = self.memory.allocate(psize * 2, psize)?;
215+
let (ptr, extra) = self.get_fat_ptr(ptr);
214216
self.memory.write_bytes(static_ptr, s.as_bytes())?;
215217
self.memory.write_ptr(ptr, static_ptr)?;
216-
self.memory.write_usize(ptr.offset(psize as isize), s.len() as u64)?;
218+
self.memory.write_usize(extra, s.len() as u64)?;
217219
Ok(ptr)
218220
}
219221
ByteStr(ref bs) => {
@@ -244,6 +246,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
244246
}
245247

246248
fn type_is_sized(&self, ty: Ty<'tcx>) -> bool {
249+
// generics are weird, don't run this function on a generic
250+
assert!(!ty.needs_subst());
247251
ty.is_sized(self.tcx, &self.tcx.empty_parameter_environment(), DUMMY_SP)
248252
}
249253

@@ -558,12 +562,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
558562

559563
Ref(_, _, ref lvalue) => {
560564
let lv = self.eval_lvalue(lvalue)?;
561-
self.memory.write_ptr(dest, lv.ptr)?;
565+
let (ptr, extra) = self.get_fat_ptr(dest);
566+
self.memory.write_ptr(ptr, lv.ptr)?;
562567
match lv.extra {
563568
LvalueExtra::None => {},
564569
LvalueExtra::Length(len) => {
565-
let len_ptr = dest.offset(self.memory.pointer_size() as isize);
566-
self.memory.write_usize(len_ptr, len)?;
570+
self.memory.write_usize(extra, len)?;
567571
}
568572
LvalueExtra::DowncastVariant(..) =>
569573
bug!("attempted to take a reference to an enum downcast lvalue"),
@@ -583,14 +587,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
583587
Unsize => {
584588
let src = self.eval_operand(operand)?;
585589
let src_ty = self.operand_ty(operand);
586-
self.move_(src, dest, src_ty)?;
590+
let (ptr, extra) = self.get_fat_ptr(dest);
591+
self.move_(src, ptr, src_ty)?;
587592
let src_pointee_ty = pointee_type(src_ty).unwrap();
588593
let dest_pointee_ty = pointee_type(dest_ty).unwrap();
589594

590595
match (&src_pointee_ty.sty, &dest_pointee_ty.sty) {
591596
(&ty::TyArray(_, length), &ty::TySlice(_)) => {
592-
let len_ptr = dest.offset(self.memory.pointer_size() as isize);
593-
self.memory.write_usize(len_ptr, length as u64)?;
597+
self.memory.write_usize(extra, length as u64)?;
594598
}
595599

596600
_ => return Err(EvalError::Unimplemented(format!("can't handle cast: {:?}", rvalue))),
@@ -600,20 +604,25 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
600604
Misc => {
601605
let src = self.eval_operand(operand)?;
602606
let src_ty = self.operand_ty(operand);
603-
// FIXME(solson): Wrong for almost everything.
604-
warn!("misc cast from {:?} to {:?}", src_ty, dest_ty);
605-
let dest_size = self.type_size(dest_ty);
606-
let src_size = self.type_size(src_ty);
607-
let dest_align = self.type_align(dest_ty);
608-
609-
// Hack to support fat pointer -> thin pointer casts to keep tests for
610-
// other things passing for now.
611-
let is_fat_ptr_cast = pointee_type(src_ty).map_or(false, |ty| !self.type_is_sized(ty));
612-
613-
if dest_size == src_size || is_fat_ptr_cast {
614-
self.memory.copy(src, dest, dest_size, dest_align)?;
607+
if self.type_is_fat_ptr(src_ty) {
608+
let (data_ptr, _meta_ptr) = self.get_fat_ptr(src);
609+
let ptr_size = self.memory.pointer_size();
610+
let dest_ty = self.monomorphize(dest_ty, self.substs());
611+
if self.type_is_fat_ptr(dest_ty) {
612+
// FIXME: add assertion that the extra part of the src_ty and
613+
// dest_ty is of the same type
614+
self.memory.copy(data_ptr, dest, ptr_size * 2, ptr_size)?;
615+
} else { // cast to thin-ptr
616+
// Cast of fat-ptr to thin-ptr is an extraction of data-ptr and
617+
// pointer-cast of that pointer to desired pointer type.
618+
self.memory.copy(data_ptr, dest, ptr_size, ptr_size)?;
619+
}
615620
} else {
616-
return Err(EvalError::Unimplemented(format!("can't handle cast: {:?}", rvalue)));
621+
// FIXME: dest_ty should already be monomorphized
622+
let dest_ty = self.monomorphize(dest_ty, self.substs());
623+
let src_val = self.read_primval(src, src_ty)?;
624+
let dest_val = self.cast_primval(src_val, dest_ty)?;
625+
self.memory.write_primval(dest, dest_val)?;
617626
}
618627
}
619628

@@ -644,6 +653,15 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
644653
Ok(())
645654
}
646655

656+
fn type_is_fat_ptr(&self, ty: Ty<'tcx>) -> bool {
657+
match ty.sty {
658+
ty::TyRawPtr(ty::TypeAndMut{ty, ..}) |
659+
ty::TyRef(_, ty::TypeAndMut{ty, ..}) |
660+
ty::TyBox(ty) => !self.type_is_sized(ty),
661+
_ => false,
662+
}
663+
}
664+
647665
fn nonnull_offset(&self, ty: Ty<'tcx>, nndiscr: u64, discrfield: &[u32]) -> EvalResult<'tcx, Size> {
648666
// Skip the constant 0 at the start meant for LLVM GEP.
649667
let mut path = discrfield.iter().skip(1).map(|&i| i as usize);
@@ -809,11 +827,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
809827

810828
Deref => {
811829
let pointee_ty = pointee_type(base_ty).expect("Deref of non-pointer");
830+
self.memory.dump(base.ptr.alloc_id);
812831
let ptr = self.memory.read_ptr(base.ptr)?;
813832
let extra = match pointee_ty.sty {
814833
ty::TySlice(_) | ty::TyStr => {
815-
let len_ptr = base.ptr.offset(self.memory.pointer_size() as isize);
816-
let len = self.memory.read_usize(len_ptr)?;
834+
let (_, extra) = self.get_fat_ptr(base.ptr);
835+
let len = self.memory.read_usize(extra)?;
817836
LvalueExtra::Length(len)
818837
}
819838
ty::TyTrait(_) => unimplemented!(),
@@ -842,6 +861,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
842861
Ok(Lvalue { ptr: ptr, extra: LvalueExtra::None })
843862
}
844863

864+
fn get_fat_ptr(&self, ptr: Pointer) -> (Pointer, Pointer) {
865+
assert_eq!(layout::FAT_PTR_ADDR, 0);
866+
assert_eq!(layout::FAT_PTR_EXTRA, 1);
867+
(ptr, ptr.offset(self.memory.pointer_size() as isize))
868+
}
869+
845870
fn lvalue_ty(&self, lvalue: &mir::Lvalue<'tcx>) -> Ty<'tcx> {
846871
self.monomorphize(lvalue.ty(&self.mir(), self.tcx).to_ty(self.tcx), self.substs())
847872
}
@@ -865,7 +890,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
865890
let c = self.memory.read_uint(ptr, 4)? as u32;
866891
match ::std::char::from_u32(c) {
867892
Some(ch) => PrimVal::Char(ch),
868-
None => return Err(EvalError::InvalidChar(c)),
893+
None => return Err(EvalError::InvalidChar(c as u64)),
869894
}
870895
}
871896
(_, &ty::TyInt(IntTy::I8)) => PrimVal::I8(self.memory.read_int(ptr, 1)? as i8),
@@ -905,6 +930,25 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
905930
}
906931
}
907932

933+
(_, &ty::TyEnum(..)) => {
934+
use rustc::ty::layout::Layout::*;
935+
if let CEnum { discr, signed, .. } = *self.type_layout(ty) {
936+
match (discr.size().bytes(), signed) {
937+
(1, true) => PrimVal::I8(self.memory.read_int(ptr, 1)? as i8),
938+
(2, true) => PrimVal::I16(self.memory.read_int(ptr, 2)? as i16),
939+
(4, true) => PrimVal::I32(self.memory.read_int(ptr, 4)? as i32),
940+
(8, true) => PrimVal::I64(self.memory.read_int(ptr, 8)? as i64),
941+
(1, false) => PrimVal::U8(self.memory.read_uint(ptr, 1)? as u8),
942+
(2, false) => PrimVal::U16(self.memory.read_uint(ptr, 2)? as u16),
943+
(4, false) => PrimVal::U32(self.memory.read_uint(ptr, 4)? as u32),
944+
(8, false) => PrimVal::U64(self.memory.read_uint(ptr, 8)? as u64),
945+
(size, _) => bug!("CEnum discr size {}", size),
946+
}
947+
} else {
948+
bug!("primitive read of non-clike enum: {:?}", ty);
949+
}
950+
},
951+
908952
_ => bug!("primitive read of non-primitive type: {:?}", ty),
909953
};
910954
Ok(val)

src/interpreter/terminator.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
4747
let discr_val = self.memory.read_uint(discr_ptr, discr_size)?;
4848
if let ty::TyChar = discr_ty.sty {
4949
if ::std::char::from_u32(discr_val as u32).is_none() {
50-
return Err(EvalError::InvalidChar(discr_val as u32));
50+
return Err(EvalError::InvalidChar(discr_val as u64));
5151
}
5252
}
5353

src/memory.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -455,8 +455,8 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
455455
PrimVal::IntegerPtr(n) => self.write_uint(ptr, n as u64, pointer_size),
456456
PrimVal::F32(f) => self.write_f32(ptr, f),
457457
PrimVal::F64(f) => self.write_f64(ptr, f),
458-
PrimVal::FnPtr(_p) |
459-
PrimVal::AbstractPtr(_p) => unimplemented!(),
458+
PrimVal::FnPtr(p) |
459+
PrimVal::AbstractPtr(p) => self.write_ptr(ptr, p),
460460
}
461461
}
462462

0 commit comments

Comments
 (0)