From 9780729104a257958a734e86501bee45a10c627e Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 9 Jun 2016 10:45:06 +0200 Subject: [PATCH 1/7] we already have the constant's type, no need to recompute from the def_id --- src/interpreter/stepper.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interpreter/stepper.rs b/src/interpreter/stepper.rs index 3e4816834a82d..1da8c903b79ea 100644 --- a/src/interpreter/stepper.rs +++ b/src/interpreter/stepper.rs @@ -129,7 +129,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'tcx> { mir::Literal::Value { .. } => {} mir::Literal::Item { def_id, substs } => { if let ty::TyFnDef(..) = constant.ty.sty { - // No need to do anything here, even if function pointers are implemented, + // No need to do anything here, // because the type is the actual function, not the signature of the function. // Thus we can simply create a zero sized allocation in `evaluate_operand` } else { From 67211218f0e3451cdc3fa979c0989ee47a0db048 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 8 Jun 2016 13:31:24 +0200 Subject: [PATCH 2/7] display the full path to the function if no MIR is found --- src/interpreter/mod.rs | 3 ++- src/lib.rs | 1 + tests/run-fail/inception.rs | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 6831ab212fb35..bbc2d2b5c492d 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -282,6 +282,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } fn load_mir(&self, def_id: DefId) -> CachedMir<'a, 'tcx> { + use rustc_trans::back::symbol_names::def_id_to_string; match self.tcx.map.as_local_node_id(def_id) { Some(node_id) => CachedMir::Ref(self.mir_map.map.get(&node_id).unwrap()), None => { @@ -292,7 +293,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let cs = &self.tcx.sess.cstore; let mir = cs.maybe_get_item_mir(self.tcx, def_id).unwrap_or_else(|| { - panic!("no mir for {:?}", def_id); + panic!("no mir for `{}`", def_id_to_string(self.tcx, def_id)); }); let cached = Rc::new(mir); mir_cache.insert(def_id, cached.clone()); diff --git a/src/lib.rs b/src/lib.rs index c3369878f3554..90979bd777ce8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,6 +12,7 @@ // From rustc. #[macro_use] extern crate rustc; extern crate rustc_mir; +extern crate rustc_trans; extern crate syntax; #[macro_use] extern crate log; extern crate log_settings; diff --git a/tests/run-fail/inception.rs b/tests/run-fail/inception.rs index f0fb4113f1f76..a98f71502f9f3 100644 --- a/tests/run-fail/inception.rs +++ b/tests/run-fail/inception.rs @@ -1,4 +1,4 @@ -// error-pattern:no mir for DefId +// error-pattern:no mir for `env_logger/ use std::env; use std::process::{Command, Output}; From 384623daa7ca04352fc57cb2f31790961ac27e21 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 8 Jun 2016 13:43:34 +0200 Subject: [PATCH 3/7] function pointers --- src/error.rs | 3 + src/interpreter/mod.rs | 192 ++++++++++++++++------------ src/memory.rs | 41 +++++- tests/compile-fail/unimplemented.rs | 2 +- tests/run-pass/function_pointers.rs | 17 +++ tests/run-pass/zst.rs | 17 +++ 6 files changed, 184 insertions(+), 88 deletions(-) create mode 100644 tests/run-pass/function_pointers.rs create mode 100644 tests/run-pass/zst.rs diff --git a/src/error.rs b/src/error.rs index 7d252658ba2d6..a4ae8cbdda25b 100644 --- a/src/error.rs +++ b/src/error.rs @@ -6,6 +6,7 @@ use memory::Pointer; #[derive(Clone, Debug)] pub enum EvalError { DanglingPointerDeref, + InvalidFunctionPointer, InvalidBool, InvalidDiscriminant, PointerOutOfBounds { @@ -28,6 +29,8 @@ impl Error for EvalError { match *self { EvalError::DanglingPointerDeref => "dangling pointer was dereferenced", + EvalError::InvalidFunctionPointer => + "tried to use a pointer as a function pointer", EvalError::InvalidBool => "invalid boolean value read", EvalError::InvalidDiscriminant => diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index bbc2d2b5c492d..943cf33a97a3a 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -6,7 +6,7 @@ use rustc::traits::{self, ProjectionMode}; use rustc::ty::fold::TypeFoldable; use rustc::ty::layout::{self, Layout, Size}; use rustc::ty::subst::{self, Subst, Substs}; -use rustc::ty::{self, Ty, TyCtxt}; +use rustc::ty::{self, Ty, TyCtxt, BareFnTy}; use rustc::util::nodemap::DefIdMap; use std::cell::RefCell; use std::ops::Deref; @@ -14,7 +14,7 @@ use std::rc::Rc; use std::{iter, mem}; use syntax::ast; use syntax::attr; -use syntax::codemap::{self, DUMMY_SP}; +use syntax::codemap::{self, DUMMY_SP, Span}; use error::{EvalError, EvalResult}; use memory::{Memory, Pointer}; @@ -39,7 +39,7 @@ pub struct EvalContext<'a, 'tcx: 'a> { mir_cache: RefCell>>>, /// The virtual memory system. - memory: Memory, + memory: Memory<'tcx>, /// Precomputed statics, constants and promoteds statics: HashMap, Pointer>, @@ -421,81 +421,15 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let func_ty = self.operand_ty(func); match func_ty.sty { + ty::TyFnPtr(bare_fn_ty) => { + let ptr = self.eval_operand(func)?; + assert_eq!(ptr.offset, 0); + let fn_ptr = self.memory.read_ptr(ptr)?; + let (def_id, substs) = self.memory.get_fn(fn_ptr.alloc_id)?; + self.eval_fn_call(def_id, substs, bare_fn_ty, return_ptr, args, terminator.span)? + }, ty::TyFnDef(def_id, substs, fn_ty) => { - use syntax::abi::Abi; - match fn_ty.abi { - Abi::RustIntrinsic => { - let name = self.tcx.item_name(def_id).as_str(); - match fn_ty.sig.0.output { - ty::FnConverging(ty) => { - let size = self.type_size(ty, self.substs()); - let ret = return_ptr.unwrap(); - self.call_intrinsic(&name, substs, args, ret, size)? - } - ty::FnDiverging => unimplemented!(), - } - } - - Abi::C => { - match fn_ty.sig.0.output { - ty::FnConverging(ty) => { - let size = self.type_size(ty, self.substs()); - self.call_c_abi(def_id, args, return_ptr.unwrap(), size)? - } - ty::FnDiverging => unimplemented!(), - } - } - - Abi::Rust | Abi::RustCall => { - // TODO(solson): Adjust the first argument when calling a Fn or - // FnMut closure via FnOnce::call_once. - - // Only trait methods can have a Self parameter. - let (resolved_def_id, resolved_substs) = if substs.self_ty().is_some() { - self.trait_method(def_id, substs) - } else { - (def_id, substs) - }; - - let mut arg_srcs = Vec::new(); - for arg in args { - let src = self.eval_operand(arg)?; - let src_ty = self.operand_ty(arg); - arg_srcs.push((src, src_ty)); - } - - if fn_ty.abi == Abi::RustCall && !args.is_empty() { - arg_srcs.pop(); - let last_arg = args.last().unwrap(); - let last = self.eval_operand(last_arg)?; - let last_ty = self.operand_ty(last_arg); - let last_layout = self.type_layout(last_ty, self.substs()); - match (&last_ty.sty, last_layout) { - (&ty::TyTuple(fields), - &Layout::Univariant { ref variant, .. }) => { - let offsets = iter::once(0) - .chain(variant.offset_after_field.iter() - .map(|s| s.bytes())); - for (offset, ty) in offsets.zip(fields) { - let src = last.offset(offset as isize); - arg_srcs.push((src, ty)); - } - } - ty => panic!("expected tuple as last argument in function with 'rust-call' ABI, got {:?}", ty), - } - } - - let mir = self.load_mir(resolved_def_id); - self.push_stack_frame(def_id, terminator.span, mir, resolved_substs, return_ptr); - - for (i, (src, src_ty)) in arg_srcs.into_iter().enumerate() { - let dest = self.frame().locals[i]; - self.move_(src, dest, src_ty)?; - } - } - - abi => return Err(EvalError::Unimplemented(format!("can't handle function with {:?} ABI", abi))), - } + self.eval_fn_call(def_id, substs, fn_ty, return_ptr, args, terminator.span)? } _ => return Err(EvalError::Unimplemented(format!("can't handle callee of type {:?}", func_ty))), @@ -515,6 +449,93 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(()) } + pub fn eval_fn_call( + &mut self, + def_id: DefId, + substs: &'tcx Substs<'tcx>, + fn_ty: &'tcx BareFnTy, + return_ptr: Option, + args: &[mir::Operand<'tcx>], + span: Span, + ) -> EvalResult<()> { + use syntax::abi::Abi; + match fn_ty.abi { + Abi::RustIntrinsic => { + let name = self.tcx.item_name(def_id).as_str(); + match fn_ty.sig.0.output { + ty::FnConverging(ty) => { + let size = self.type_size(ty, self.substs()); + let ret = return_ptr.unwrap(); + self.call_intrinsic(&name, substs, args, ret, size) + } + ty::FnDiverging => unimplemented!(), + } + } + + Abi::C => { + match fn_ty.sig.0.output { + ty::FnConverging(ty) => { + let size = self.type_size(ty, self.substs()); + self.call_c_abi(def_id, args, return_ptr.unwrap(), size) + } + ty::FnDiverging => unimplemented!(), + } + } + + Abi::Rust | Abi::RustCall => { + // TODO(solson): Adjust the first argument when calling a Fn or + // FnMut closure via FnOnce::call_once. + + // Only trait methods can have a Self parameter. + let (resolved_def_id, resolved_substs) = if substs.self_ty().is_some() { + self.trait_method(def_id, substs) + } else { + (def_id, substs) + }; + + let mut arg_srcs = Vec::new(); + for arg in args { + let src = self.eval_operand(arg)?; + let src_ty = self.operand_ty(arg); + arg_srcs.push((src, src_ty)); + } + + if fn_ty.abi == Abi::RustCall && !args.is_empty() { + arg_srcs.pop(); + let last_arg = args.last().unwrap(); + let last = self.eval_operand(last_arg)?; + let last_ty = self.operand_ty(last_arg); + let last_layout = self.type_layout(last_ty, self.substs()); + match (&last_ty.sty, last_layout) { + (&ty::TyTuple(fields), + &Layout::Univariant { ref variant, .. }) => { + let offsets = iter::once(0) + .chain(variant.offset_after_field.iter() + .map(|s| s.bytes())); + for (offset, ty) in offsets.zip(fields) { + let src = last.offset(offset as isize); + arg_srcs.push((src, ty)); + } + } + ty => panic!("expected tuple as last argument in function with 'rust-call' ABI, got {:?}", ty), + } + } + + let mir = self.load_mir(resolved_def_id); + self.push_stack_frame(def_id, span, mir, resolved_substs, return_ptr); + + for (i, (src, src_ty)) in arg_srcs.into_iter().enumerate() { + let dest = self.frame().locals[i]; + self.move_(src, dest, src_ty)?; + } + + Ok(()) + } + + abi => Err(EvalError::Unimplemented(format!("can't handle function with {:?} ABI", abi))), + } + } + fn drop(&mut self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<()> { if !self.type_needs_drop(ty) { debug!("no need to drop {:?}", ty); @@ -989,12 +1010,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } Cast(kind, ref operand, dest_ty) => { - let src = self.eval_operand(operand)?; - let src_ty = self.operand_ty(operand); - use rustc::mir::repr::CastKind::*; match kind { Unsize => { + let src = self.eval_operand(operand)?; + let src_ty = self.operand_ty(operand); self.move_(src, dest, src_ty)?; let src_pointee_ty = pointee_type(src_ty).unwrap(); let dest_pointee_ty = pointee_type(dest_ty).unwrap(); @@ -1010,11 +1030,20 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } Misc => { + let src = self.eval_operand(operand)?; // FIXME(solson): Wrong for almost everything. let size = dest_layout.size(&self.tcx.data_layout).bytes() as usize; self.memory.copy(src, dest, size)?; } + ReifyFnPointer => match self.operand_ty(operand).sty { + ty::TyFnDef(def_id, substs, _) => { + let fn_ptr = self.memory.create_fn_ptr(def_id, substs); + self.memory.write_ptr(dest, fn_ptr)?; + }, + ref other => panic!("reify fn pointer on {:?}", other), + }, + _ => return Err(EvalError::Unimplemented(format!("can't handle cast: {:?}", rvalue))), } } @@ -1103,7 +1132,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Value { ref value } => Ok(self.const_to_ptr(value)?), Item { def_id, substs } => { if let ty::TyFnDef(..) = ty.sty { - Err(EvalError::Unimplemented("unimplemented: mentions of function items".to_string())) + // function items are zero sized + Ok(self.memory.allocate(0)) } else { let cid = ConstantId { def_id: def_id, diff --git a/src/memory.rs b/src/memory.rs index ab527a47c0bf5..d9999821e1b03 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -3,6 +3,9 @@ use std::collections::Bound::{Included, Excluded}; use std::collections::{btree_map, BTreeMap, HashMap, HashSet, VecDeque}; use std::{fmt, iter, mem, ptr}; +use rustc::hir::def_id::DefId; +use rustc::ty::subst::Substs; + use error::{EvalError, EvalResult}; use primval::PrimVal; @@ -42,22 +45,37 @@ impl Pointer { // Top-level interpreter memory //////////////////////////////////////////////////////////////////////////////// -pub struct Memory { +pub struct Memory<'tcx> { alloc_map: HashMap, + functions: HashMap)>, next_id: AllocId, pub pointer_size: usize, } -impl Memory { +impl<'tcx> Memory<'tcx> { // FIXME: pass tcx.data_layout (This would also allow it to use primitive type alignments to diagnose unaligned memory accesses.) pub fn new(pointer_size: usize) -> Self { Memory { alloc_map: HashMap::new(), + functions: HashMap::new(), next_id: AllocId(0), pointer_size: pointer_size, } } + // FIXME: never create two pointers to the same def_id + substs combination + // maybe re-use the statics cache of the gecx? + pub fn create_fn_ptr(&mut self, def_id: DefId, substs: &'tcx Substs<'tcx>) -> Pointer { + let id = self.next_id; + debug!("creating fn ptr: {}", id); + self.next_id.0 += 1; + self.functions.insert(id, (def_id, substs)); + Pointer { + alloc_id: id, + offset: 0, + } + } + pub fn allocate(&mut self, size: usize) -> Pointer { let alloc = Allocation { bytes: vec![0; size], @@ -125,6 +143,11 @@ impl Memory { self.alloc_map.get_mut(&id).ok_or(EvalError::DanglingPointerDeref) } + pub fn get_fn(&self, id: AllocId) -> EvalResult<(DefId, &'tcx Substs<'tcx>)> { + debug!("reading fn ptr: {}", id); + self.functions.get(&id).map(|&did| did).ok_or(EvalError::InvalidFunctionPointer) + } + /// Print an allocation and all allocations it points to, recursively. pub fn dump(&self, id: AllocId) { let mut allocs_seen = HashSet::new(); @@ -137,12 +160,18 @@ impl Memory { print!("{}", prefix); let mut relocations = vec![]; - let alloc = match self.alloc_map.get(&id) { - Some(a) => a, - None => { + let alloc = match (self.alloc_map.get(&id), self.functions.get(&id)) { + (Some(a), None) => a, + (None, Some(_)) => { + // FIXME: print function name + println!("function pointer"); + continue; + }, + (None, None) => { println!("(deallocated)"); continue; - } + }, + (Some(_), Some(_)) => unreachable!(), }; for i in 0..alloc.bytes.len() { diff --git a/tests/compile-fail/unimplemented.rs b/tests/compile-fail/unimplemented.rs index 754d3d9ee7e6d..7752650ade887 100644 --- a/tests/compile-fail/unimplemented.rs +++ b/tests/compile-fail/unimplemented.rs @@ -1,7 +1,7 @@ #![feature(custom_attribute)] #![allow(dead_code, unused_attributes)] -//error-pattern:unimplemented: mentions of function items +//error-pattern:begin_panic_fmt #[miri_run] diff --git a/tests/run-pass/function_pointers.rs b/tests/run-pass/function_pointers.rs new file mode 100644 index 0000000000000..55a6f9fbeac46 --- /dev/null +++ b/tests/run-pass/function_pointers.rs @@ -0,0 +1,17 @@ +#![feature(custom_attribute)] +#![allow(dead_code, unused_attributes)] + +fn f() -> i32 { + 42 +} + +fn return_fn_ptr() -> fn() -> i32 { + f +} + +#[miri_run] +fn call_fn_ptr() -> i32 { + return_fn_ptr()() +} + +fn main() {} diff --git a/tests/run-pass/zst.rs b/tests/run-pass/zst.rs new file mode 100644 index 0000000000000..d4f1d4023ba7f --- /dev/null +++ b/tests/run-pass/zst.rs @@ -0,0 +1,17 @@ +#![feature(custom_attribute)] +#![allow(dead_code, unused_attributes)] + +struct A; + +#[miri_run] +fn zst_ret() -> A { + A +} + +#[miri_run] +fn use_zst() -> A { + let a = A; + a +} + +fn main() {} From 3aa585e421a1c60626b339079b1aa420c1e3ab1d Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 13 Jun 2016 11:24:01 +0200 Subject: [PATCH 4/7] Merge remote-tracking branch 'origin/master' into function_pointers2 --- src/interpreter/mod.rs | 88 +++++++++++++++++++++++++++--------------- src/memory.rs | 24 ++++++------ tests/compiletest.rs | 26 +++++++++---- tests/run-pass/heap.rs | 20 ++++++++++ tests/run-pass/sums.rs | 3 +- 5 files changed, 109 insertions(+), 52 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 251ebb768e0d6..1b0b416e0e12c 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -143,7 +143,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub fn alloc_ret_ptr(&mut self, output_ty: ty::FnOutput<'tcx>, substs: &'tcx Substs<'tcx>) -> Option { match output_ty { ty::FnConverging(ty) => { - let size = self.type_size(ty, substs); + let size = self.type_size_with_substs(ty, substs); Some(self.memory.allocate(size)) } ty::FnDiverging => None, @@ -308,11 +308,19 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.tcx.normalize_associated_type(&substituted) } - fn type_size(&self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx>) -> usize { - self.type_layout(ty, substs).size(&self.tcx.data_layout).bytes() as usize + fn type_size(&self, ty: Ty<'tcx>) -> usize { + self.type_size_with_substs(ty, self.substs()) } - fn type_layout(&self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx>) -> &'tcx Layout { + fn type_size_with_substs(&self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx>) -> usize { + self.type_layout_with_substs(ty, substs).size(&self.tcx.data_layout).bytes() as usize + } + + fn type_layout(&self, ty: Ty<'tcx>) -> &'tcx Layout { + self.type_layout_with_substs(ty, self.substs()) + } + + fn type_layout_with_substs(&self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx>) -> &'tcx Layout { // TODO(solson): Is this inefficient? Needs investigation. let ty = self.monomorphize(ty, substs); @@ -335,7 +343,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ::log_settings::settings().indentation += 1; let locals: Vec = arg_tys.chain(var_tys).chain(temp_tys).map(|ty| { - let size = self.type_size(ty, substs); + let size = self.type_size_with_substs(ty, substs); self.memory.allocate(size) }).collect(); @@ -378,7 +386,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { SwitchInt { ref discr, ref values, ref targets, .. } => { let discr_ptr = self.eval_lvalue(discr)?.to_ptr(); let discr_size = self - .type_layout(self.lvalue_ty(discr), self.substs()) + .type_layout(self.lvalue_ty(discr)) .size(&self.tcx.data_layout) .bytes() as usize; let discr_val = self.memory.read_uint(discr_ptr, discr_size)?; @@ -479,7 +487,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let name = self.tcx.item_name(def_id).as_str(); match fn_ty.sig.0.output { ty::FnConverging(ty) => { - let size = self.type_size(ty, self.substs()); + let size = self.type_size(ty); let ret = return_ptr.unwrap(); self.call_intrinsic(&name, substs, args, ret, size) } @@ -490,7 +498,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Abi::C => { match fn_ty.sig.0.output { ty::FnConverging(ty) => { - let size = self.type_size(ty, self.substs()); + let size = self.type_size(ty); self.call_c_abi(def_id, args, return_ptr.unwrap(), size) } ty::FnDiverging => unimplemented!(), @@ -520,7 +528,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let last_arg = args.last().unwrap(); let last = self.eval_operand(last_arg)?; let last_ty = self.operand_ty(last_arg); - let last_layout = self.type_layout(last_ty, self.substs()); + let last_layout = self.type_layout(last_ty); match (&last_ty.sty, last_layout) { (&ty::TyTuple(fields), &Layout::Univariant { ref variant, .. }) => { @@ -587,7 +595,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // Filling drop. // FIXME(solson): Trait objects (with no static size) probably get filled, too. - let size = self.type_size(ty, self.substs()); + let size = self.type_size(ty); self.memory.drop_fill(ptr, size)?; Ok(()) @@ -595,7 +603,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { fn read_discriminant_value(&self, adt_ptr: Pointer, adt_ty: Ty<'tcx>) -> EvalResult { use rustc::ty::layout::Layout::*; - let adt_layout = self.type_layout(adt_ty, self.substs()); + let adt_layout = self.type_layout(adt_ty); let discr_val = match *adt_layout { General { discr, .. } | CEnum { discr, .. } => { @@ -648,7 +656,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // FIXME(solson): Handle different integer types correctly. "add_with_overflow" => { let ty = *substs.types.get(subst::FnSpace, 0); - let size = self.type_size(ty, self.substs()); + let size = self.type_size(ty); let left = self.memory.read_int(args[0], size)?; let right = self.memory.read_int(args[1], size)?; let (n, overflowed) = unsafe { @@ -662,7 +670,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "copy_nonoverlapping" => { let elem_ty = *substs.types.get(subst::FnSpace, 0); - let elem_size = self.type_size(elem_ty, self.substs()); + let elem_size = self.type_size(elem_ty); let src = self.memory.read_ptr(args[0])?; let dest = self.memory.read_ptr(args[1])?; let count = self.memory.read_isize(args[2])?; @@ -678,7 +686,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "forget" => { let arg_ty = *substs.types.get(subst::FnSpace, 0); - let arg_size = self.type_size(arg_ty, self.substs()); + let arg_size = self.type_size(arg_ty); self.memory.drop_fill(args[0], arg_size)?; } @@ -697,7 +705,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // FIXME(solson): Handle different integer types correctly. "mul_with_overflow" => { let ty = *substs.types.get(subst::FnSpace, 0); - let size = self.type_size(ty, self.substs()); + let size = self.type_size(ty); let left = self.memory.read_int(args[0], size)?; let right = self.memory.read_int(args[1], size)?; let (n, overflowed) = unsafe { @@ -709,7 +717,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "offset" => { let pointee_ty = *substs.types.get(subst::FnSpace, 0); - let pointee_size = self.type_size(pointee_ty, self.substs()) as isize; + let pointee_size = self.type_size(pointee_ty) as isize; let ptr_arg = args[0]; let offset = self.memory.read_isize(args[1])?; @@ -730,7 +738,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // FIXME(solson): Handle different integer types correctly. Use primvals? "overflowing_sub" => { let ty = *substs.types.get(subst::FnSpace, 0); - let size = self.type_size(ty, self.substs()); + let size = self.type_size(ty); let left = self.memory.read_int(args[0], size)?; let right = self.memory.read_int(args[1], size)?; let n = left.wrapping_sub(right); @@ -739,20 +747,20 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "size_of" => { let ty = *substs.types.get(subst::FnSpace, 0); - let size = self.type_size(ty, self.substs()) as u64; + let size = self.type_size(ty) as u64; self.memory.write_uint(dest, size, dest_size)?; } "size_of_val" => { let ty = *substs.types.get(subst::FnSpace, 0); if self.type_is_sized(ty) { - let size = self.type_size(ty, self.substs()) as u64; + let size = self.type_size(ty) as u64; self.memory.write_uint(dest, size, dest_size)?; } else { match ty.sty { ty::TySlice(_) | ty::TyStr => { let elem_ty = ty.sequence_element_type(self.tcx); - let elem_size = self.type_size(elem_ty, self.substs()) as u64; + let elem_size = self.type_size(elem_ty) as u64; let ptr_size = self.memory.pointer_size as isize; let n = self.memory.read_usize(args[0].offset(ptr_size))?; self.memory.write_uint(dest, n * elem_size, dest_size)?; @@ -831,7 +839,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.memory.write_int(dest, result, dest_size)?; } - _ => return Err(EvalError::Unimplemented(format!("can't call C ABI function: {}", link_name))), + _ => { + return Err(EvalError::Unimplemented(format!("can't call C ABI function: {}", link_name))); + } } // Since we pushed no stack frame, the main loop will act @@ -860,7 +870,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { { let dest = self.eval_lvalue(lvalue)?.to_ptr(); let dest_ty = self.lvalue_ty(lvalue); - let dest_layout = self.type_layout(dest_ty, self.substs()); + let dest_layout = self.type_layout(dest_ty); use rustc::mir::repr::Rvalue::*; match *rvalue { @@ -897,7 +907,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // FIXME(solson): Find the result type size properly. Perhaps refactor out // Projection calculations so we can do the equivalent of `dest.1` here. - let s = self.type_size(left_ty, self.substs()); + let s = self.type_size(left_ty); self.memory.write_bool(dest.offset(s as isize), false)?; } @@ -919,7 +929,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Array { .. } => { let elem_size = match dest_ty.sty { - ty::TyArray(elem_ty, _) => self.type_size(elem_ty, self.substs()) as u64, + ty::TyArray(elem_ty, _) => self.type_size(elem_ty) as u64, _ => panic!("tried to assign {:?} to non-array type {:?}", kind, dest_ty), }; @@ -997,7 +1007,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Repeat(ref operand, _) => { let (elem_size, length) = match dest_ty.sty { - ty::TyArray(elem_ty, n) => (self.type_size(elem_ty, self.substs()), n), + ty::TyArray(elem_ty, n) => (self.type_size(elem_ty), n), _ => panic!("tried to assign array-repeat to non-array type {:?}", dest_ty), }; @@ -1038,7 +1048,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } Box(ty) => { - let size = self.type_size(ty, self.substs()); + let size = self.type_size(ty); let ptr = self.memory.allocate(size); self.memory.write_ptr(dest, ptr)?; } @@ -1065,9 +1075,23 @@ 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. - let size = dest_layout.size(&self.tcx.data_layout).bytes() as usize; - self.memory.copy(src, dest, size)?; + 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); + + // 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(|ty| { + !self.type_is_sized(ty) + }).unwrap_or(false); + + if dest_size == src_size || is_fat_ptr_cast { + self.memory.copy(src, dest, dest_size)?; + } else { + return Err(EvalError::Unimplemented(format!("can't handle cast: {:?}", rvalue))); + } } ReifyFnPointer => match self.operand_ty(operand).sty { @@ -1139,7 +1163,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } fn get_field_offset(&self, ty: Ty<'tcx>, field_index: usize) -> EvalResult { - let layout = self.type_layout(ty, self.substs()); + let layout = self.type_layout(ty); use rustc::ty::layout::Layout::*; match *layout { @@ -1211,7 +1235,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Projection(ref proj) => { let base = self.eval_lvalue(&proj.base)?; let base_ty = self.lvalue_ty(&proj.base); - let base_layout = self.type_layout(base_ty, self.substs()); + let base_layout = self.type_layout(base_ty); use rustc::mir::repr::ProjectionElem::*; match proj.elem { @@ -1272,7 +1296,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Index(ref operand) => { let elem_size = match base_ty.sty { ty::TyArray(elem_ty, _) | - ty::TySlice(elem_ty) => self.type_size(elem_ty, self.substs()), + ty::TySlice(elem_ty) => self.type_size(elem_ty), _ => panic!("indexing expected an array or slice, got {:?}", base_ty), }; let n_ptr = self.eval_operand(operand)?; @@ -1298,7 +1322,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } fn move_(&mut self, src: Pointer, dest: Pointer, ty: Ty<'tcx>) -> EvalResult<()> { - let size = self.type_size(ty, self.substs()); + let size = self.type_size(ty); self.memory.copy(src, dest, size)?; if self.type_needs_drop(ty) { self.memory.drop_fill(src, size)?; diff --git a/src/memory.rs b/src/memory.rs index d9999821e1b03..ad6c60ef517c6 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -99,17 +99,18 @@ impl<'tcx> Memory<'tcx> { return Err(EvalError::Unimplemented(format!("bad pointer offset: {}", ptr.offset))); } - let alloc = self.get_mut(ptr.alloc_id)?; - let size = alloc.bytes.len(); + let size = self.get_mut(ptr.alloc_id)?.bytes.len(); + if new_size > size { let amount = new_size - size; + let alloc = self.get_mut(ptr.alloc_id)?; alloc.bytes.extend(iter::repeat(0).take(amount)); alloc.undef_mask.grow(amount, false); } else if size > new_size { - return Err(EvalError::Unimplemented(format!("unimplemented allocation relocation (from {} to {})", size, new_size))); - // alloc.bytes.truncate(new_size); - // alloc.undef_mask.len = new_size; - // TODO: potentially remove relocations + self.clear_relocations(ptr.offset(new_size as isize), size - new_size)?; + let alloc = self.get_mut(ptr.alloc_id)?; + alloc.bytes.truncate(new_size); + alloc.undef_mask.truncate(new_size); } Ok(()) @@ -386,7 +387,7 @@ impl<'tcx> Memory<'tcx> { -> EvalResult> { let start = ptr.offset.saturating_sub(self.pointer_size - 1); - let end = start + size; + let end = ptr.offset + size; Ok(self.get(ptr.alloc_id)?.relocations.range(Included(&start), Excluded(&end))) } @@ -535,11 +536,12 @@ impl UndefMask { self.len += amount; self.set_range_inbounds(start, start + amount, new_state); } -} -// fn uniform_block(state: bool) -> Block { -// if state { !0 } else { 0 } -// } + fn truncate(&mut self, length: usize) { + self.len = length; + self.blocks.truncate(self.len / BLOCK_SIZE + 1); + } +} fn bit_index(bits: usize) -> (usize, usize) { (bits / BLOCK_SIZE, bits % BLOCK_SIZE) diff --git a/tests/compiletest.rs b/tests/compiletest.rs index 2fc1e615cff19..76b5b5f6e9638 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -3,22 +3,32 @@ extern crate compiletest_rs as compiletest; use std::path::PathBuf; fn run_mode(mode: &'static str) { + // Disable rustc's new error fomatting. It breaks these tests. + std::env::remove_var("RUST_NEW_ERROR_FORMAT"); + + // Taken from https://github.com/Manishearth/rust-clippy/pull/911. + let home = option_env!("RUSTUP_HOME").or(option_env!("MULTIRUST_HOME")); + let toolchain = option_env!("RUSTUP_TOOLCHAIN").or(option_env!("MULTIRUST_TOOLCHAIN")); + let sysroot = match (home, toolchain) { + (Some(home), Some(toolchain)) => format!("{}/toolchains/{}", home, toolchain), + _ => option_env!("RUST_SYSROOT") + .expect("need to specify RUST_SYSROOT env var or use rustup or multirust") + .to_owned(), + }; + let sysroot_flag = format!("--sysroot {}", sysroot); + // FIXME: read directories in sysroot/lib/rustlib and generate the test targets from that let targets = &["x86_64-unknown-linux-gnu", "i686-unknown-linux-gnu"]; for &target in targets { let mut config = compiletest::default_config(); + config.host_rustcflags = Some(sysroot_flag.clone()); + config.mode = mode.parse().expect("Invalid mode"); + config.run_lib_path = format!("{}/lib/rustlib/{}/lib", sysroot, target); config.rustc_path = "target/debug/miri".into(); - let path = std::env::var("RUST_SYSROOT").expect("env variable `RUST_SYSROOT` not set"); - config.run_lib_path = format!("{}/lib/rustlib/{}/lib", path, target); - let path = format!("--sysroot {}", path); - config.target_rustcflags = Some(path.clone()); - config.host_rustcflags = Some(path); - let cfg_mode = mode.parse().ok().expect("Invalid mode"); - - config.mode = cfg_mode; config.src_base = PathBuf::from(format!("tests/{}", mode)); config.target = target.to_owned(); + config.target_rustcflags = Some(sysroot_flag.clone()); compiletest::run_tests(&config); } } diff --git a/tests/run-pass/heap.rs b/tests/run-pass/heap.rs index f5aad1601c77d..a7175969efac5 100644 --- a/tests/run-pass/heap.rs +++ b/tests/run-pass/heap.rs @@ -11,6 +11,26 @@ fn make_box_syntax() -> Box<(i16, i16)> { box (1, 2) } +#[miri_run] +fn allocate_reallocate() { + let mut s = String::new(); + + // 6 byte heap alloc (__rust_allocate) + s.push_str("foobar"); + assert_eq!(s.len(), 6); + assert_eq!(s.capacity(), 6); + + // heap size doubled to 12 (__rust_reallocate) + s.push_str("baz"); + assert_eq!(s.len(), 9); + assert_eq!(s.capacity(), 12); + + // heap size reduced to 9 (__rust_reallocate) + s.shrink_to_fit(); + assert_eq!(s.len(), 9); + assert_eq!(s.capacity(), 9); +} + #[miri_run] fn main() { assert_eq!(*make_box(), (1, 2)); diff --git a/tests/run-pass/sums.rs b/tests/run-pass/sums.rs index 9b7ddb43b7c78..120c196abe975 100644 --- a/tests/run-pass/sums.rs +++ b/tests/run-pass/sums.rs @@ -55,7 +55,8 @@ fn two_nones() -> (Option, Option) { (None, None) } -#[miri_run] +// FIXME(solson): Casts inside PartialEq fails on 32-bit. +#[cfg_attr(target_pointer_width = "64", miri_run)] fn main() { assert_eq!(two_nones(), (None, None)); assert_eq!(match_opt_some(), 13); From 4d090fa6932853b0035745a51fbd4bd88f2ee9b1 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 13 Jun 2016 11:39:15 +0200 Subject: [PATCH 5/7] report better errors when using a fn ptr as memory and vice versa --- src/error.rs | 6 ++++++ src/memory.rs | 27 ++++++++++++++++++++++++--- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/src/error.rs b/src/error.rs index a4ae8cbdda25b..49fd8564be639 100644 --- a/src/error.rs +++ b/src/error.rs @@ -20,6 +20,8 @@ pub enum EvalError { ReadUndefBytes, InvalidBoolOp(mir::BinOp), Unimplemented(String), + DerefFunctionPointer, + ExecuteMemory, } pub type EvalResult = Result; @@ -48,6 +50,10 @@ impl Error for EvalError { EvalError::InvalidBoolOp(_) => "invalid boolean operation", EvalError::Unimplemented(ref msg) => msg, + EvalError::DerefFunctionPointer => + "tried to dereference a function pointer", + EvalError::ExecuteMemory => + "tried to treat a memory pointer as a function pointer", } } diff --git a/src/memory.rs b/src/memory.rs index ad6c60ef517c6..2bc5ff184de08 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -46,7 +46,10 @@ impl Pointer { //////////////////////////////////////////////////////////////////////////////// pub struct Memory<'tcx> { + /// Actual memory allocations (arbitrary bytes, may contain pointers into other allocations) alloc_map: HashMap, + /// Function "allocations". They exist solely so pointers have something to point to, and + /// we can figure out what they point to. functions: HashMap)>, next_id: AllocId, pub pointer_size: usize, @@ -137,16 +140,34 @@ impl<'tcx> Memory<'tcx> { //////////////////////////////////////////////////////////////////////////////// pub fn get(&self, id: AllocId) -> EvalResult<&Allocation> { - self.alloc_map.get(&id).ok_or(EvalError::DanglingPointerDeref) + match self.alloc_map.get(&id) { + Some(alloc) => Ok(alloc), + None => match self.functions.get(&id) { + Some(_) => Err(EvalError::DerefFunctionPointer), + None => Err(EvalError::DanglingPointerDeref), + } + } } pub fn get_mut(&mut self, id: AllocId) -> EvalResult<&mut Allocation> { - self.alloc_map.get_mut(&id).ok_or(EvalError::DanglingPointerDeref) + match self.alloc_map.get_mut(&id) { + Some(alloc) => Ok(alloc), + None => match self.functions.get(&id) { + Some(_) => Err(EvalError::DerefFunctionPointer), + None => Err(EvalError::DanglingPointerDeref), + } + } } pub fn get_fn(&self, id: AllocId) -> EvalResult<(DefId, &'tcx Substs<'tcx>)> { debug!("reading fn ptr: {}", id); - self.functions.get(&id).map(|&did| did).ok_or(EvalError::InvalidFunctionPointer) + match self.functions.get(&id) { + Some(&fn_id) => Ok(fn_id), + None => match self.alloc_map.get(&id) { + Some(_) => Err(EvalError::ExecuteMemory), + None => Err(EvalError::InvalidFunctionPointer), + } + } } /// Print an allocation and all allocations it points to, recursively. From 9565d48203e0c6f6b5c2fcdb575fa46f81deac78 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 13 Jun 2016 12:29:01 +0200 Subject: [PATCH 6/7] add tests for fn pointers --- tests/compile-fail/deref_fn_ptr.rs | 13 +++++++++++++ tests/compile-fail/execute_memory.rs | 14 ++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 tests/compile-fail/deref_fn_ptr.rs create mode 100644 tests/compile-fail/execute_memory.rs diff --git a/tests/compile-fail/deref_fn_ptr.rs b/tests/compile-fail/deref_fn_ptr.rs new file mode 100644 index 0000000000000..52c7c2b8f9d55 --- /dev/null +++ b/tests/compile-fail/deref_fn_ptr.rs @@ -0,0 +1,13 @@ +#![feature(custom_attribute)] +#![allow(dead_code, unused_attributes)] + +fn f() {} + +#[miri_run] +fn deref_fn_ptr() -> i32 { + unsafe { + *std::mem::transmute::(f) //~ ERROR: tried to dereference a function pointer + } +} + +fn main() {} diff --git a/tests/compile-fail/execute_memory.rs b/tests/compile-fail/execute_memory.rs new file mode 100644 index 0000000000000..4e06fd8db8ded --- /dev/null +++ b/tests/compile-fail/execute_memory.rs @@ -0,0 +1,14 @@ +#![feature(custom_attribute, box_syntax)] +#![allow(dead_code, unused_attributes)] + +#[miri_run] +fn deref_fn_ptr() { + //FIXME: this span is wrong + let x = box 42; //~ ERROR: tried to treat a memory pointer as a function pointer + unsafe { + let f = std::mem::transmute::, fn()>(x); + f() + } +} + +fn main() {} From fe9b4550061b7593a5324ed2cbcc8e86bf6d986b Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 13 Jun 2016 15:32:08 +0200 Subject: [PATCH 7/7] comment nit --- src/memory.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/memory.rs b/src/memory.rs index 2bc5ff184de08..35ee99eab9702 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -67,7 +67,7 @@ impl<'tcx> Memory<'tcx> { } // FIXME: never create two pointers to the same def_id + substs combination - // maybe re-use the statics cache of the gecx? + // maybe re-use the statics cache of the EvalContext? pub fn create_fn_ptr(&mut self, def_id: DefId, substs: &'tcx Substs<'tcx>) -> Pointer { let id = self.next_id; debug!("creating fn ptr: {}", id);