diff --git a/.travis.yml b/.travis.yml index 598ea9852e56..e152db542270 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ language: rust rust: -- nightly-2016-04-11 +- nightly-2016-04-21 - nightly matrix: allow_failures: diff --git a/README.md b/README.md index 5a4e27431703..26d1278ceb84 100644 --- a/README.md +++ b/README.md @@ -15,26 +15,26 @@ I currently recommend that you install [multirust][multirust] and then use it to install the current rustc nightly version that works with Miri: ```sh -multirust update nightly-2016-04-11 +multirust update nightly-2016-04-21 ``` ## Build ```sh -multirust run nightly-2016-04-11 cargo build +multirust run nightly-2016-04-21 cargo build ``` ## Run a test ```sh -multirust run nightly-2016-04-11 cargo run -- \ - --sysroot $HOME/.multirust/toolchains/nightly-2016-04-11 \ +multirust run nightly-2016-04-21 cargo run -- \ + --sysroot $HOME/.multirust/toolchains/nightly-2016-04-21 \ test/filename.rs ``` If you are using [rustup][rustup] (the name of the multirust rewrite in Rust), the `sysroot` path will also include your build target (e.g. -`$HOME/.multirust/toolchains/nightly-2016-04-11-x86_64-apple-darwin`). You can +`$HOME/.multirust/toolchains/nightly-2016-04-21-x86_64-apple-darwin`). You can see the current toolchain's directory by running `rustup which cargo` (ignoring the trailing `/bin/cargo`). diff --git a/src/interpreter.rs b/src/interpreter.rs index 0b4aab71b615..5dc7d51ea7f8 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -1,4 +1,3 @@ -use arena::TypedArena; use rustc::infer; use rustc::middle::const_val; use rustc::hir::def_id::DefId; @@ -6,10 +5,10 @@ use rustc::mir::repr as mir; 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, TyCtxt}; use rustc::util::nodemap::DefIdMap; -use rustc_data_structures::fnv::FnvHashMap; use std::cell::RefCell; use std::ops::Deref; use std::rc::Rc; @@ -19,12 +18,12 @@ use syntax::codemap::{self, DUMMY_SP}; use error::{EvalError, EvalResult}; -use memory::{FieldRepr, Memory, Pointer, Repr}; +use memory::{Memory, Pointer}; use primval::{self, PrimVal}; const TRACE_EXECUTION: bool = false; -struct Interpreter<'a, 'tcx: 'a, 'arena> { +struct Interpreter<'a, 'tcx: 'a> { /// The results of the type checker, from rustc. tcx: &'a TyCtxt<'tcx>, @@ -34,12 +33,6 @@ struct Interpreter<'a, 'tcx: 'a, 'arena> { /// A local cache from DefIds to Mir for non-crate-local items. mir_cache: RefCell>>>, - /// An arena allocator for type representations. - repr_arena: &'arena TypedArena, - - /// A cache for in-memory representations of types. - repr_cache: RefCell, &'arena Repr>>, - /// The virtual memory system. memory: Memory, @@ -91,7 +84,8 @@ struct Lvalue { enum LvalueExtra { None, Length(u64), - // Vtable(memory::AllocId), + // TODO(tsion): Vtable(memory::AllocId), + DowncastVariant(usize), } #[derive(Clone)] @@ -112,16 +106,12 @@ enum TerminatorTarget { Return, } -impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { - fn new(tcx: &'a TyCtxt<'tcx>, mir_map: &'a MirMap<'tcx>, repr_arena: &'arena TypedArena) - -> Self - { +impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { + fn new(tcx: &'a TyCtxt<'tcx>, mir_map: &'a MirMap<'tcx>) -> Self { Interpreter { tcx: tcx, mir_map: mir_map, mir_cache: RefCell::new(DefIdMap()), - repr_arena: repr_arena, - repr_cache: RefCell::new(FnvHashMap()), memory: Memory::new(), stack: Vec::new(), substs_stack: Vec::new(), @@ -242,7 +232,10 @@ fn eval_terminator(&mut self, terminator: &mir::Terminator<'tcx>) SwitchInt { ref discr, ref values, ref targets, .. } => { let discr_ptr = try!(self.eval_lvalue(discr)).to_ptr(); - let discr_size = self.lvalue_repr(discr).size(); + let discr_size = self + .type_layout(self.lvalue_ty(discr)) + .size(&self.tcx.data_layout) + .bytes() as usize; let discr_val = try!(self.memory.read_uint(discr_ptr, discr_size)); // Branch to the `otherwise` case by default, if no match is found. @@ -262,19 +255,35 @@ fn eval_terminator(&mut self, terminator: &mir::Terminator<'tcx>) Switch { ref discr, ref targets, adt_def } => { let adt_ptr = try!(self.eval_lvalue(discr)).to_ptr(); - let adt_repr = self.lvalue_repr(discr); - let discr_size = match *adt_repr { - Repr::Aggregate { discr_size, .. } => discr_size, - _ => panic!("attmpted to switch on non-aggregate type"), - }; - let discr_val = try!(self.memory.read_uint(adt_ptr, discr_size)); + let adt_layout = self.type_layout(self.lvalue_ty(discr)); - let matching = adt_def.variants.iter() - .position(|v| discr_val == v.disr_val.to_u64_unchecked()); + match *adt_layout { + Layout::General { discr, .. } | Layout::CEnum { discr, .. } => { + let discr_size = discr.size().bytes(); + let discr_val = try!(self.memory.read_uint(adt_ptr, discr_size as usize)); - match matching { - Some(i) => TerminatorTarget::Block(targets[i]), - None => return Err(EvalError::InvalidDiscriminant), + let matching = adt_def.variants.iter() + .position(|v| discr_val == v.disr_val.to_u64_unchecked()); + + match matching { + Some(i) => TerminatorTarget::Block(targets[i]), + None => return Err(EvalError::InvalidDiscriminant), + } + } + + Layout::RawNullablePointer { nndiscr, .. } => { + let is_null = match self.memory.read_usize(adt_ptr) { + Ok(0) => true, + Ok(_) | Err(EvalError::ReadPointerAsBytes) => false, + Err(e) => return Err(e), + }; + + assert!(nndiscr == 0 || nndiscr == 1); + let target = if is_null { 1 - nndiscr } else { nndiscr }; + TerminatorTarget::Block(targets[target as usize]) + } + + _ => panic!("attempted to switch on non-aggregate type"), } } @@ -328,13 +337,15 @@ fn eval_terminator(&mut self, terminator: &mir::Terminator<'tcx>) let last_arg = args.last().unwrap(); let last = try!(self.eval_operand(last_arg)); let last_ty = self.operand_ty(last_arg); - let last_repr = self.type_repr(last_ty); - match (&last_ty.sty, last_repr) { + let last_layout = self.type_layout(last_ty); + match (&last_ty.sty, last_layout) { (&ty::TyTuple(ref fields), - &Repr::Aggregate { discr_size: 0, ref variants, .. }) => { - assert_eq!(variants.len(), 1); - for (repr, ty) in variants[0].iter().zip(fields) { - let src = last.offset(repr.offset as isize); + &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)); } } @@ -578,28 +589,17 @@ fn call_c_abi( Ok(TerminatorTarget::Call) } - fn assign_to_aggregate( + fn assign_fields>( &mut self, dest: Pointer, - dest_repr: &Repr, - variant: usize, - discr: Option, + offsets: I, operands: &[mir::Operand<'tcx>], ) -> EvalResult<()> { - match *dest_repr { - Repr::Aggregate { discr_size, ref variants, .. } => { - if discr_size > 0 { - try!(self.memory.write_uint(dest, discr.unwrap(), discr_size)); - } - let after_discr = dest.offset(discr_size as isize); - for (field, operand) in variants[variant].iter().zip(operands) { - let src = try!(self.eval_operand(operand)); - let src_ty = self.operand_ty(operand); - let field_dest = after_discr.offset(field.offset as isize); - try!(self.move_(src, field_dest, src_ty)); - } - } - _ => panic!("expected Repr::Aggregate target"), + for (offset, operand) in offsets.into_iter().zip(operands) { + let src = try!(self.eval_operand(operand)); + let src_ty = self.operand_ty(operand); + let field_dest = dest.offset(offset as isize); + try!(self.move_(src, field_dest, src_ty)); } Ok(()) } @@ -609,7 +609,7 @@ fn eval_assignment(&mut self, lvalue: &mir::Lvalue<'tcx>, rvalue: &mir::Rvalue<' { let dest = try!(self.eval_lvalue(lvalue)).to_ptr(); let dest_ty = self.lvalue_ty(lvalue); - let dest_repr = self.lvalue_repr(lvalue); + let dest_layout = self.type_layout(dest_ty); use rustc::mir::repr::Rvalue::*; match *rvalue { @@ -639,39 +639,85 @@ fn eval_assignment(&mut self, lvalue: &mir::Lvalue<'tcx>, rvalue: &mir::Rvalue<' } Aggregate(ref kind, ref operands) => { - use rustc::mir::repr::AggregateKind::*; - match *kind { - Tuple | Closure(..) => - try!(self.assign_to_aggregate(dest, &dest_repr, 0, None, operands)), - - Adt(adt_def, variant, _) => { - let discr = Some(adt_def.variants[variant].disr_val.to_u64_unchecked()); - try!(self.assign_to_aggregate(dest, &dest_repr, variant, discr, operands)); + use rustc::ty::layout::Layout::*; + match *dest_layout { + Univariant { ref variant, .. } => { + let offsets = iter::once(0) + .chain(variant.offset_after_field.iter().map(|s| s.bytes())); + try!(self.assign_fields(dest, offsets, operands)); } - Vec => if let Repr::Array { elem_size, length } = *dest_repr { - assert_eq!(length, operands.len()); - for (i, operand) in operands.iter().enumerate() { - let src = try!(self.eval_operand(operand)); - let src_ty = self.operand_ty(operand); - let elem_dest = dest.offset((i * elem_size) as isize); - try!(self.move_(src, elem_dest, src_ty)); + Array { .. } => { + let elem_size = match dest_ty.sty { + ty::TyArray(elem_ty, _) => self.type_size(elem_ty) as u64, + _ => panic!("tried to assign {:?} to non-array type {:?}", + kind, dest_ty), + }; + let offsets = (0..).map(|i| i * elem_size); + try!(self.assign_fields(dest, offsets, operands)); + } + + General { discr, ref variants, .. } => { + if let mir::AggregateKind::Adt(adt_def, variant, _) = *kind { + let discr_val = adt_def.variants[variant].disr_val.to_u64_unchecked(); + let discr_size = discr.size().bytes() as usize; + try!(self.memory.write_uint(dest, discr_val, discr_size)); + + let offsets = variants[variant].offset_after_field.iter() + .map(|s| s.bytes()); + try!(self.assign_fields(dest, offsets, operands)); + } else { + panic!("tried to assign {:?} to Layout::General", kind); } - } else { - panic!("expected Repr::Array target"); - }, + } + + RawNullablePointer { nndiscr, .. } => { + if let mir::AggregateKind::Adt(_, variant, _) = *kind { + if nndiscr == variant as u64 { + assert_eq!(operands.len(), 1); + let operand = &operands[0]; + let src = try!(self.eval_operand(operand)); + let src_ty = self.operand_ty(operand); + try!(self.move_(src, dest, src_ty)); + } else { + assert_eq!(operands.len(), 0); + try!(self.memory.write_isize(dest, 0)); + } + } else { + panic!("tried to assign {:?} to Layout::RawNullablePointer", kind); + } + } + + CEnum { discr, signed, min, max } => { + assert_eq!(operands.len(), 0); + if let mir::AggregateKind::Adt(adt_def, variant, _) = *kind { + if signed { + unimplemented!() + } else { + let val = adt_def.variants[variant].disr_val.to_u64().unwrap(); + let size = discr.size().bytes() as usize; + try!(self.memory.write_uint(dest, val, size)); + } + } else { + panic!("tried to assign {:?} to Layout::CEnum", kind); + } + } + + _ => panic!("can't handle destination layout {:?} when assigning {:?}", + dest_layout, kind), } } Repeat(ref operand, _) => { - if let Repr::Array { elem_size, length } = *dest_repr { - let src = try!(self.eval_operand(operand)); - for i in 0..length { - let elem_dest = dest.offset((i * elem_size) as isize); - try!(self.memory.copy(src, elem_dest, elem_size)); - } - } else { - panic!("expected Repr::Array target"); + let (elem_size, length) = match dest_ty.sty { + ty::TyArray(elem_ty, n) => (self.type_size(elem_ty), n), + _ => panic!("tried to assign array-repeat to non-array type {:?}", dest_ty), + }; + + let src = try!(self.eval_operand(operand)); + for i in 0..length { + let elem_dest = dest.offset((i * elem_size) as isize); + try!(self.memory.copy(src, elem_dest, elem_size)); } } @@ -699,6 +745,8 @@ fn eval_assignment(&mut self, lvalue: &mir::Lvalue<'tcx>, rvalue: &mir::Rvalue<' let len_ptr = dest.offset(self.memory.pointer_size as isize); try!(self.memory.write_usize(len_ptr, len)); } + LvalueExtra::DowncastVariant(..) => + panic!("attempted to take a reference to an enum downcast lvalue"), } } @@ -731,7 +779,7 @@ fn eval_assignment(&mut self, lvalue: &mir::Lvalue<'tcx>, rvalue: &mir::Rvalue<' Misc => { // FIXME(tsion): Wrong for almost everything. - let size = dest_repr.size(); + let size = dest_layout.size(&self.tcx.data_layout).bytes() as usize; try!(self.memory.copy(src, dest, size)); } @@ -747,42 +795,20 @@ fn eval_assignment(&mut self, lvalue: &mir::Lvalue<'tcx>, rvalue: &mir::Rvalue<' } fn eval_operand(&mut self, op: &mir::Operand<'tcx>) -> EvalResult { - self.eval_operand_and_repr(op).map(|(p, _)| p) - } - - fn eval_operand_and_repr(&mut self, op: &mir::Operand<'tcx>) - -> EvalResult<(Pointer, &'arena Repr)> - { use rustc::mir::repr::Operand::*; match *op { Consume(ref lvalue) => - Ok((try!(self.eval_lvalue(lvalue)).to_ptr(), self.lvalue_repr(lvalue))), - Constant(mir::Constant { ref literal, ty, .. }) => { + Ok(try!(self.eval_lvalue(lvalue)).to_ptr()), + Constant(mir::Constant { ref literal, .. }) => { use rustc::mir::repr::Literal::*; match *literal { - Value { ref value } => Ok(( - try!(self.const_to_ptr(value)), - self.type_repr(ty), - )), + Value { ref value } => Ok(try!(self.const_to_ptr(value))), Item { .. } => unimplemented!(), } } } } - // TODO(tsion): Replace this inefficient hack with a wrapper like LvalueTy (e.g. LvalueRepr). - fn lvalue_repr(&self, lvalue: &mir::Lvalue<'tcx>) -> &'arena Repr { - use rustc::mir::tcx::LvalueTy; - match self.mir().lvalue_ty(self.tcx, lvalue) { - LvalueTy::Ty { ty } => self.type_repr(ty), - LvalueTy::Downcast { adt_def, substs, variant_index } => { - let field_tys = adt_def.variants[variant_index].fields.iter() - .map(|f| f.ty(self.tcx, substs)); - self.repr_arena.alloc(self.make_aggregate_repr(iter::once(field_tys))) - } - } - } - fn eval_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>) -> EvalResult { use rustc::mir::repr::Lvalue::*; let ptr = match *lvalue { @@ -795,30 +821,50 @@ fn eval_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>) -> EvalResult { Static(_def_id) => unimplemented!(), Projection(ref proj) => { - let base_ptr = try!(self.eval_lvalue(&proj.base)).to_ptr(); - let base_repr = self.lvalue_repr(&proj.base); + let base = try!(self.eval_lvalue(&proj.base)); let base_ty = self.lvalue_ty(&proj.base); + let base_layout = self.type_layout(base_ty); + use rustc::mir::repr::ProjectionElem::*; match proj.elem { - Field(field, _) => match *base_repr { - Repr::Aggregate { discr_size: 0, ref variants, .. } => { - let fields = &variants[0]; - base_ptr.offset(fields[field.index()].offset as isize) - } - _ => panic!("field access on non-product type: {:?}", base_repr), + Field(field, _) => { + let variant = match *base_layout { + Layout::Univariant { ref variant, .. } => variant, + Layout::General { ref variants, .. } => { + if let LvalueExtra::DowncastVariant(variant_idx) = base.extra { + &variants[variant_idx] + } else { + panic!("field access on enum had no variant index"); + } + } + Layout::RawNullablePointer { .. } => { + assert_eq!(field.index(), 0); + return Ok(base); + } + _ => panic!("field access on non-product type: {:?}", base_layout), + }; + + let offset = variant.field_offset(field.index()).bytes(); + base.ptr.offset(offset as isize) }, - Downcast(..) => match *base_repr { - Repr::Aggregate { discr_size, .. } => base_ptr.offset(discr_size as isize), - _ => panic!("variant downcast on non-aggregate type: {:?}", base_repr), + Downcast(_, variant) => match *base_layout { + Layout::General { discr, .. } => { + return Ok(Lvalue { + ptr: base.ptr.offset(discr.size().bytes() as isize), + extra: LvalueExtra::DowncastVariant(variant), + }); + } + Layout::RawNullablePointer { .. } => return Ok(base), + _ => panic!("variant downcast on non-aggregate type: {:?}", base_layout), }, Deref => { let pointee_ty = pointee_type(base_ty).expect("Deref of non-pointer"); - let ptr = try!(self.memory.read_ptr(base_ptr)); + let ptr = try!(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_ptr = base.ptr.offset(self.memory.pointer_size as isize); let len = try!(self.memory.read_usize(len_ptr)); LvalueExtra::Length(len) } @@ -836,7 +882,7 @@ fn eval_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>) -> EvalResult { }; let n_ptr = try!(self.eval_operand(operand)); let n = try!(self.memory.read_usize(n_ptr)); - base_ptr.offset(n as isize * elem_size as isize) + base.ptr.offset(n as isize * elem_size as isize) } ConstantIndex { .. } => unimplemented!(), @@ -921,99 +967,17 @@ fn type_is_sized(&self, ty: ty::Ty<'tcx>) -> bool { } fn type_size(&self, ty: ty::Ty<'tcx>) -> usize { - self.type_repr(ty).size() + self.type_layout(ty).size(&self.tcx.data_layout).bytes() as usize } - fn type_repr(&self, ty: ty::Ty<'tcx>) -> &'arena Repr { + fn type_layout(&self, ty: ty::Ty<'tcx>) -> &'tcx Layout { + // TODO(tsion): Is this inefficient? Needs investigation. let ty = self.monomorphize(ty); - if let Some(repr) = self.repr_cache.borrow().get(ty) { - return repr; - } + let infcx = infer::normalizing_infer_ctxt(self.tcx, &self.tcx.tables, ProjectionMode::Any); - use syntax::ast::{IntTy, UintTy}; - let repr = match ty.sty { - ty::TyBool => Repr::Primitive { size: 1 }, - - ty::TyInt(IntTy::I8) | ty::TyUint(UintTy::U8) => Repr::Primitive { size: 1 }, - ty::TyInt(IntTy::I16) | ty::TyUint(UintTy::U16) => Repr::Primitive { size: 2 }, - ty::TyInt(IntTy::I32) | ty::TyUint(UintTy::U32) => Repr::Primitive { size: 4 }, - ty::TyInt(IntTy::I64) | ty::TyUint(UintTy::U64) => Repr::Primitive { size: 8 }, - - ty::TyInt(IntTy::Is) | ty::TyUint(UintTy::Us) => - Repr::Primitive { size: self.memory.pointer_size }, - - ty::TyTuple(ref fields) => - self.make_aggregate_repr(iter::once(fields.iter().cloned())), - - ty::TyEnum(adt_def, substs) | ty::TyStruct(adt_def, substs) => { - let variants = adt_def.variants.iter().map(|v| { - v.fields.iter().map(|f| f.ty(self.tcx, substs)) - }); - self.make_aggregate_repr(variants) - } - - ty::TyArray(elem_ty, length) => Repr::Array { - elem_size: self.type_size(elem_ty), - length: length, - }, - - ty::TyRef(_, ty::TypeAndMut { ty, .. }) | - ty::TyRawPtr(ty::TypeAndMut { ty, .. }) | - ty::TyBox(ty) => { - if self.type_is_sized(ty) { - Repr::Primitive { size: self.memory.pointer_size } - } else { - Repr::Primitive { size: self.memory.pointer_size * 2 } - } - } - - ty::TyFnPtr(..) => Repr::Primitive { size: self.memory.pointer_size }, - - ty::TyClosure(_, ref closure_substs) => - self.make_aggregate_repr(iter::once(closure_substs.upvar_tys.iter().cloned())), - - ref t => panic!("can't convert type to repr: {:?}", t), - }; - - let repr_ref = self.repr_arena.alloc(repr); - self.repr_cache.borrow_mut().insert(ty, repr_ref); - repr_ref - } - - fn make_aggregate_repr(&self, variant_fields: V) -> Repr - where V: IntoIterator, V::Item: IntoIterator> - { - let mut variants = Vec::new(); - let mut max_variant_size = 0; - - for field_tys in variant_fields { - let mut fields = Vec::new(); - let mut size = 0; - - for ty in field_tys { - let field_size = self.type_size(ty); - let offest = size; - size += field_size; - fields.push(FieldRepr { offset: offest, size: field_size }); - } - - if size > max_variant_size { max_variant_size = size; } - variants.push(fields); - } - - let discr_size = match variants.len() as u64 { - n if n <= 1 => 0, - n if n <= 1 << 8 => 1, - n if n <= 1 << 16 => 2, - n if n <= 1 << 32 => 4, - _ => 8, - }; - Repr::Aggregate { - discr_size: discr_size, - size: max_variant_size + discr_size, - variants: variants, - } + // TODO(tsion): Report this error properly. + ty.layout(&infcx).unwrap() } pub fn read_primval(&mut self, ptr: Pointer, ty: ty::Ty<'tcx>) -> EvalResult { @@ -1235,8 +1199,7 @@ pub fn interpret_start_points<'tcx>(tcx: &TyCtxt<'tcx>, mir_map: &MirMap<'tcx>) println!("Interpreting: {}", item.name); - let repr_arena = TypedArena::new(); - let mut miri = Interpreter::new(tcx, mir_map, &repr_arena); + let mut miri = Interpreter::new(tcx, mir_map); let return_ptr = match mir.return_ty { ty::FnConverging(ty) => { let size = miri.type_size(ty); @@ -1257,3 +1220,35 @@ pub fn interpret_start_points<'tcx>(tcx: &TyCtxt<'tcx>, mir_map: &MirMap<'tcx>) } } } + +// TODO(tsion): Upstream these methods into rustc::ty::layout. + +trait IntegerExt { + fn size(self) -> Size; +} + +impl IntegerExt for layout::Integer { + fn size(self) -> Size { + use rustc::ty::layout::Integer::*; + match self { + I1 | I8 => Size::from_bits(8), + I16 => Size::from_bits(16), + I32 => Size::from_bits(32), + I64 => Size::from_bits(64), + } + } +} + +trait StructExt { + fn field_offset(&self, index: usize) -> Size; +} + +impl StructExt for layout::Struct { + fn field_offset(&self, index: usize) -> Size { + if index == 0 { + Size::from_bytes(0) + } else { + self.offset_after_field[index - 1] + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 8ddafb499111..0bf7dfb87d14 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,9 +8,7 @@ )] // From rustc. -extern crate arena; #[macro_use] extern crate rustc; -extern crate rustc_data_structures; extern crate rustc_mir; extern crate syntax; diff --git a/src/memory.rs b/src/memory.rs index 285c3554b647..45f8b392c006 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -6,54 +6,6 @@ use error::{EvalError, EvalResult}; use primval::PrimVal; -//////////////////////////////////////////////////////////////////////////////// -// Value representations -//////////////////////////////////////////////////////////////////////////////// - -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum Repr { - /// Representation for a non-aggregate type such as a boolean, integer, character or pointer. - Primitive { - size: usize - }, - - /// The representation for aggregate types including structs, enums, and tuples. - Aggregate { - /// The size of the discriminant (an integer). Should be between 0 and 8. Always 0 for - /// structs and tuples. - discr_size: usize, - - /// The size of the entire aggregate, including the discriminant. - size: usize, - - /// The representations of the contents of each variant. - variants: Vec>, - }, - - Array { - elem_size: usize, - - /// Number of elements. - length: usize, - }, -} - -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub struct FieldRepr { - pub offset: usize, - pub size: usize, -} - -impl Repr { - pub fn size(&self) -> usize { - match *self { - Repr::Primitive { size } | - Repr::Aggregate { size, .. } => size, - Repr::Array { elem_size, length } => elem_size * length, - } - } -} - //////////////////////////////////////////////////////////////////////////////// // Allocations and pointers //////////////////////////////////////////////////////////////////////////////// @@ -451,7 +403,7 @@ fn copy_relocations(&mut self, src: Pointer, dest: Pointer, size: usize) -> Eval // Undefined bytes //////////////////////////////////////////////////////////////////////////////// - // FIXME(tsino): This is a very naive, slow version. + // FIXME(tsion): This is a very naive, slow version. fn copy_undef_mask(&mut self, src: Pointer, dest: Pointer, size: usize) -> EvalResult<()> { // The bits have to be saved locally before writing to dest in case src and dest overlap. let mut v = Vec::with_capacity(size); diff --git a/src/primval.rs b/src/primval.rs index c9117d033fdd..ad96fbe7d419 100644 --- a/src/primval.rs +++ b/src/primval.rs @@ -14,10 +14,11 @@ pub enum PrimVal { } pub fn binary_op(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> EvalResult { + use rustc::mir::repr::BinOp::*; + use self::PrimVal::*; + macro_rules! int_binops { ($v:ident, $l:ident, $r:ident) => ({ - use rustc::mir::repr::BinOp::*; - use self::PrimVal::*; match bin_op { Add => $v($l + $r), Sub => $v($l - $r), @@ -52,7 +53,6 @@ fn unrelated_ptr_ops(bin_op: mir::BinOp) -> EvalResult { } } - use self::PrimVal::*; let val = match (left, right) { (I8(l), I8(r)) => int_binops!(I8, l, r), (I16(l), I16(r)) => int_binops!(I16, l, r), @@ -63,6 +63,22 @@ fn unrelated_ptr_ops(bin_op: mir::BinOp) -> EvalResult { (U32(l), U32(r)) => int_binops!(U32, l, r), (U64(l), U64(r)) => int_binops!(U64, l, r), + (Bool(l), Bool(r)) => { + Bool(match bin_op { + Eq => l == r, + Ne => l != r, + Lt => l < r, + Le => l <= r, + Gt => l > r, + Ge => l >= r, + BitOr => l | r, + BitXor => l ^ r, + BitAnd => l & r, + Add | Sub | Mul | Div | Rem | Shl | Shr => + panic!("invalid binary operation on booleans: {:?}", bin_op), + }) + } + (IntegerPtr(l), IntegerPtr(r)) => int_binops!(IntegerPtr, l, r), (AbstractPtr(_), IntegerPtr(_)) | (IntegerPtr(_), AbstractPtr(_)) => @@ -76,7 +92,6 @@ fn unrelated_ptr_ops(bin_op: mir::BinOp) -> EvalResult { let l = l_ptr.offset; let r = r_ptr.offset; - use rustc::mir::repr::BinOp::*; match bin_op { Eq => Bool(l == r), Ne => Bool(l != r), diff --git a/tests/compile-fail/bugs/slice_index.rs b/tests/compile-fail/bugs/slice_index.rs deleted file mode 100644 index 52a76247ca46..000000000000 --- a/tests/compile-fail/bugs/slice_index.rs +++ /dev/null @@ -1,12 +0,0 @@ -#![feature(custom_attribute)] -#![allow(dead_code, unused_attributes)] - -// error-pattern:assertion failed - -#[miri_run] -fn slice() -> u8 { - let arr: &[_] = &[101, 102, 103, 104, 105, 106]; - arr[5] -} - -fn main() {} diff --git a/tests/compile-fail/bugs/struct_wrapped_nullable_pointer.rs b/tests/compile-fail/bugs/struct_wrapped_nullable_pointer.rs new file mode 100644 index 000000000000..880ca42d4555 --- /dev/null +++ b/tests/compile-fail/bugs/struct_wrapped_nullable_pointer.rs @@ -0,0 +1,23 @@ +#![feature(custom_attribute, box_syntax)] +#![allow(dead_code, unused_attributes)] + +// error-pattern:can't handle destination layout StructWrappedNullablePointer + +use std::cell::RefCell; +use std::rc::Rc; + +struct Loop(Rc>>); + +#[miri_run] +fn rc_reference_cycle() -> Loop { + let a = Rc::new(RefCell::new(None)); + let b = a.clone(); + *a.borrow_mut() = Some(Loop(b)); + Loop(a) +} + +#[miri_run] +fn main() { + let x = rc_reference_cycle().0; + assert!(x.borrow().is_some()); +} diff --git a/tests/run-pass/arrays.rs b/tests/run-pass/arrays.rs index 36b7217f5806..1fbf24b46684 100644 --- a/tests/run-pass/arrays.rs +++ b/tests/run-pass/arrays.rs @@ -38,11 +38,18 @@ fn index() -> i32 { [42; 8] } +#[miri_run] +fn slice_index() -> u8 { + let arr: &[_] = &[101, 102, 103, 104, 105, 106]; + arr[5] +} + #[miri_run] fn main() { //assert_eq!(empty_array(), []); assert_eq!(index_unsafe(), 20); assert_eq!(index(), 20); + assert_eq!(slice_index(), 106); /* assert_eq!(big_array(), [5, 4, 3, 2, 1]); assert_eq!(array_array(), [[5, 4], [3, 2], [1, 0]]); diff --git a/tests/compile-fail/bugs/option_box_transmute_ptr.rs b/tests/run-pass/option_box_transmute_ptr.rs similarity index 54% rename from tests/compile-fail/bugs/option_box_transmute_ptr.rs rename to tests/run-pass/option_box_transmute_ptr.rs index 84161daf88dd..55beb11edd44 100644 --- a/tests/compile-fail/bugs/option_box_transmute_ptr.rs +++ b/tests/run-pass/option_box_transmute_ptr.rs @@ -1,11 +1,13 @@ #![feature(custom_attribute)] #![allow(dead_code, unused_attributes)] +// This tests that the size of Option> is the same as *const i32. + #[miri_run] fn option_box_deref() -> i32 { let val = Some(Box::new(42)); unsafe { - let ptr: *const i32 = std::mem::transmute(val); //~ ERROR: pointer offset outside bounds of allocation + let ptr: *const i32 = std::mem::transmute::>, *const i32>(val); *ptr } } diff --git a/tests/run-pass/specialization.rs b/tests/run-pass/specialization.rs index b82038f40030..4b5f510ad440 100644 --- a/tests/run-pass/specialization.rs +++ b/tests/run-pass/specialization.rs @@ -18,6 +18,7 @@ fn specialization() -> (bool, bool) { (i32::is_unit(), <()>::is_unit()) } +#[miri_run] fn main() { assert_eq!(specialization(), (false, true)); } diff --git a/tests/run-pass/std.rs b/tests/run-pass/std.rs index 1d4dc8befef8..b5bb7c7dbdb9 100644 --- a/tests/run-pass/std.rs +++ b/tests/run-pass/std.rs @@ -1,7 +1,7 @@ #![feature(custom_attribute, box_syntax)] #![allow(dead_code, unused_attributes)] -use std::cell::{Cell, RefCell}; +use std::cell::Cell; use std::rc::Rc; use std::sync::Arc; @@ -29,16 +29,6 @@ fn arc() -> Arc { a } -struct Loop(Rc>>); - -#[miri_run] -fn rc_reference_cycle() -> Loop { - let a = Rc::new(RefCell::new(None)); - let b = a.clone(); - *a.borrow_mut() = Some(Loop(b)); - Loop(a) -} - #[miri_run] fn true_assert() { assert_eq!(1, 1); @@ -46,8 +36,6 @@ fn true_assert() { #[miri_run] fn main() { - //let x = rc_reference_cycle().0; - //assert!(x.borrow().is_some()); assert_eq!(*arc(), 42); assert_eq!(rc_cell().get(), 84); } diff --git a/tests/run-pass/sums.rs b/tests/run-pass/sums.rs index b8635b4dcd64..d6eddc69fc9a 100644 --- a/tests/run-pass/sums.rs +++ b/tests/run-pass/sums.rs @@ -2,24 +2,24 @@ #![allow(dead_code, unused_attributes)] #[derive(Debug, PartialEq)] -enum Unit { Unit } +enum Unit { Unit(()) } // Force non-C-enum representation. #[miri_run] fn return_unit() -> Unit { - Unit::Unit + Unit::Unit(()) } #[derive(Debug, PartialEq)] -enum MyBool { False, True } +enum MyBool { False(()), True(()) } // Force non-C-enum representation. #[miri_run] fn return_true() -> MyBool { - MyBool::True + MyBool::True(()) } #[miri_run] fn return_false() -> MyBool { - MyBool::False + MyBool::False(()) } #[miri_run] diff --git a/tex/report/miri-report.tex b/tex/report/miri-report.tex index 6d2686c138f7..9ef9652eed1d 100644 --- a/tex/report/miri-report.tex +++ b/tex/report/miri-report.tex @@ -536,12 +536,6 @@ Miri supports unsafe operations on \rust{Vec} like \rust{v.set_len(10)} or \emph{does} invoke undefined behaviour, Miri will abort with an appropriate error message (see \autoref{fig:vec-error}). -% You can even do unsafe things with \rust{Vec} like \rust{v.set_len(10)} or -% \rust{v.get_unchecked(2)}, but if you do these things carefully in a way that doesn't cause any -% undefined behaviour (just like when you write unsafe code for regular Rust), then Miri can handle it -% all. But if you do slip up, Miri will error out with an appropriate message (see -% \autoref{fig:vec-error}). - \begin{figure}[t] \begin{minted}[autogobble]{rust} fn out_of_bounds() -> u8 {