From 2dbd30fa516c7fd91f9d13c0aeaff88c7e4e6021 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 20 Jun 2016 12:29:45 +0200 Subject: [PATCH 1/4] implement char handling --- src/error.rs | 5 +++++ src/interpreter/mod.rs | 13 ++++++++++++- src/memory.rs | 1 + src/primval.rs | 10 ++++++++++ tests/compile-fail/option_eq.rs | 5 +++++ tests/run-pass/char.rs | 9 +++++++++ 6 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 tests/compile-fail/option_eq.rs create mode 100644 tests/run-pass/char.rs diff --git a/src/error.rs b/src/error.rs index b19f63231a..9725e9aafc 100644 --- a/src/error.rs +++ b/src/error.rs @@ -28,6 +28,7 @@ pub enum EvalError<'tcx> { ExecuteMemory, ArrayIndexOutOfBounds(Span, u64, u64), Math(Span, ConstMathErr), + InvalidChar(u32), } pub type EvalResult<'tcx, T> = Result>; @@ -66,6 +67,8 @@ impl<'tcx> Error for EvalError<'tcx> { "array index out of bounds", EvalError::Math(..) => "mathematical operation failed", + EvalError::InvalidChar(..) => + "tried to interpret an invalid 32-bit value as a char", } } @@ -85,6 +88,8 @@ impl<'tcx> fmt::Display for EvalError<'tcx> { write!(f, "array index {} out of bounds {} at {:?}", index, len, span), EvalError::Math(span, ref err) => write!(f, "mathematical operation at {:?} failed with {:?}", span, err), + EvalError::InvalidChar(c) => + write!(f, "invalid utf8 character: {}", c), _ => write!(f, "{}", self.description()), } } diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 0a8c82c5df..6b6779b59c 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -208,7 +208,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.memory.write_bool(ptr, b)?; Ok(ptr) } - Char(_c) => unimplemented!(), + Char(c) => { + let ptr = self.memory.allocate(4); + self.memory.write_uint(ptr, c as u32 as u64, 4)?; + Ok(ptr) + }, Struct(_node_id) => unimplemented!(), Tuple(_node_id) => unimplemented!(), Function(_def_id) => unimplemented!(), @@ -1371,6 +1375,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { use syntax::ast::{IntTy, UintTy}; let val = match (self.memory.pointer_size, &ty.sty) { (_, &ty::TyBool) => PrimVal::Bool(self.memory.read_bool(ptr)?), + (_, &ty::TyChar) => { + 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)), + } + } (_, &ty::TyInt(IntTy::I8)) => PrimVal::I8(self.memory.read_int(ptr, 1)? as i8), (2, &ty::TyInt(IntTy::Is)) | (_, &ty::TyInt(IntTy::I16)) => PrimVal::I16(self.memory.read_int(ptr, 2)? as i16), diff --git a/src/memory.rs b/src/memory.rs index be0cd0ef4f..17a42e6fcc 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -369,6 +369,7 @@ impl<'tcx> Memory<'tcx> { PrimVal::U16(n) => self.write_uint(ptr, n as u64, 2), PrimVal::U32(n) => self.write_uint(ptr, n as u64, 4), PrimVal::U64(n) => self.write_uint(ptr, n as u64, 8), + PrimVal::Char(c) => self.write_uint(ptr, c as u32 as u64, 4), PrimVal::IntegerPtr(n) => self.write_uint(ptr, n as u64, pointer_size), PrimVal::FnPtr(_p) | PrimVal::AbstractPtr(_p) => unimplemented!(), diff --git a/src/primval.rs b/src/primval.rs index 6b17a63b65..f3aedfc197 100644 --- a/src/primval.rs +++ b/src/primval.rs @@ -12,6 +12,7 @@ pub enum PrimVal { AbstractPtr(Pointer), FnPtr(Pointer), IntegerPtr(u64), + Char(char), } /// returns the result of the operation and whether the operation overflowed @@ -127,6 +128,15 @@ pub fn binary_op<'tcx>(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> Eva (U16(l), U16(r)) => int_binops!(U16, l, r), (U32(l), U32(r)) => int_binops!(U32, l, r), (U64(l), U64(r)) => int_binops!(U64, l, r), + (Char(l), Char(r)) => match bin_op { + Eq => Bool(l == r), + Ne => Bool(l != r), + Lt => Bool(l < r), + Le => Bool(l <= r), + Gt => Bool(l > r), + Ge => Bool(l >= r), + _ => panic!("invalid char op: {:?}", bin_op), + }, (Bool(l), Bool(r)) => { Bool(match bin_op { diff --git a/tests/compile-fail/option_eq.rs b/tests/compile-fail/option_eq.rs new file mode 100644 index 0000000000..8315ae7af8 --- /dev/null +++ b/tests/compile-fail/option_eq.rs @@ -0,0 +1,5 @@ +//error-pattern: can't handle cast: tmp0 as isize (Misc) +// no-ignore-x86 ignore-x86_64 +fn main() { + assert_eq!(std::char::from_u32('x' as u32), Some('x')); +} diff --git a/tests/run-pass/char.rs b/tests/run-pass/char.rs new file mode 100644 index 0000000000..505c09b0ad --- /dev/null +++ b/tests/run-pass/char.rs @@ -0,0 +1,9 @@ +fn main() { + let c = 'x'; + assert_eq!(c, 'x'); + assert!('a' < 'z'); + assert!('1' < '9'); + assert_eq!(std::char::from_u32('x' as u32).unwrap(), 'x'); + // FIXME: + // assert_eq!(std::char::from_u32('x' as u32), Some('x')); +} From 422e5edd28bba8599bf3e816f67d2746528c1af8 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 21 Jun 2016 09:43:27 +0200 Subject: [PATCH 2/4] error message improvements --- src/error.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/error.rs b/src/error.rs index 9725e9aafc..da5d8cf787 100644 --- a/src/error.rs +++ b/src/error.rs @@ -85,11 +85,11 @@ impl<'tcx> fmt::Display for EvalError<'tcx> { EvalError::FunctionPointerTyMismatch(expected, got) => write!(f, "tried to call a function of type {:?} through a function pointer of type {:?}", expected, got), EvalError::ArrayIndexOutOfBounds(span, len, index) => - write!(f, "array index {} out of bounds {} at {:?}", index, len, span), + write!(f, "index out of bounds: the len is {} but the index is {} at {:?}", len, index, span), EvalError::Math(span, ref err) => - write!(f, "mathematical operation at {:?} failed with {:?}", span, err), + write!(f, "{:?} at {:?}", err, span), EvalError::InvalidChar(c) => - write!(f, "invalid utf8 character: {}", c), + write!(f, "tried to interpret an invalid 32-bit value as a char: {}", c), _ => write!(f, "{}", self.description()), } } From 7a9272c8e153b9264fffb3a19a113638e89b7857 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 21 Jun 2016 09:43:45 +0200 Subject: [PATCH 3/4] no need to cast chars as u32 before casting to u64 --- src/interpreter/mod.rs | 2 +- src/memory.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 6b6779b59c..18f97f0269 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -210,7 +210,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } Char(c) => { let ptr = self.memory.allocate(4); - self.memory.write_uint(ptr, c as u32 as u64, 4)?; + self.memory.write_uint(ptr, c as u64, 4)?; Ok(ptr) }, Struct(_node_id) => unimplemented!(), diff --git a/src/memory.rs b/src/memory.rs index 17a42e6fcc..a16fbc57a6 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -369,7 +369,7 @@ impl<'tcx> Memory<'tcx> { PrimVal::U16(n) => self.write_uint(ptr, n as u64, 2), PrimVal::U32(n) => self.write_uint(ptr, n as u64, 4), PrimVal::U64(n) => self.write_uint(ptr, n as u64, 8), - PrimVal::Char(c) => self.write_uint(ptr, c as u32 as u64, 4), + PrimVal::Char(c) => self.write_uint(ptr, c as u64, 4), PrimVal::IntegerPtr(n) => self.write_uint(ptr, n as u64, pointer_size), PrimVal::FnPtr(_p) | PrimVal::AbstractPtr(_p) => unimplemented!(), From b10fc7a99ff127b68260d134ea2c70a3aa5021c1 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 21 Jun 2016 09:44:01 +0200 Subject: [PATCH 4/4] make sure miri never switches over an invalid char value --- src/interpreter/mod.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 18f97f0269..41828d7a23 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -406,11 +406,17 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { SwitchInt { ref discr, ref values, ref targets, .. } => { let discr_ptr = self.eval_lvalue(discr)?.to_ptr(); + let discr_ty = self.lvalue_ty(discr); let discr_size = self - .type_layout(self.lvalue_ty(discr)) + .type_layout(discr_ty) .size(&self.tcx.data_layout) .bytes() as usize; 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)); + } + } // Branch to the `otherwise` case by default, if no match is found. let mut target_block = targets[targets.len() - 1];