diff --git a/src/error.rs b/src/error.rs index 86f22d33a2..481815996a 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,14 +1,15 @@ use std::error::Error; use std::fmt; use rustc::mir; -use rustc::ty::{BareFnTy, Ty}; +use rustc::ty::{BareFnTy, Ty, FnSig}; +use syntax::abi::Abi; use memory::Pointer; use rustc_const_math::ConstMathErr; use syntax::codemap::Span; #[derive(Clone, Debug)] pub enum EvalError<'tcx> { - FunctionPointerTyMismatch(&'tcx BareFnTy<'tcx>, &'tcx BareFnTy<'tcx>), + FunctionPointerTyMismatch(Abi, &'tcx FnSig<'tcx>, &'tcx BareFnTy<'tcx>), NoMirFor(String), DanglingPointerDeref, InvalidMemoryAccess, @@ -61,7 +62,7 @@ impl<'tcx> Error for EvalError<'tcx> { EvalError::DanglingPointerDeref => "dangling pointer was dereferenced", EvalError::InvalidFunctionPointer => - "tried to use a pointer as a function pointer", + "tried to use an integer pointer or a dangling pointer as a function pointer", EvalError::InvalidBool => "invalid boolean value read", EvalError::InvalidDiscriminant => @@ -123,8 +124,8 @@ impl<'tcx> fmt::Display for EvalError<'tcx> { ptr.offset, ptr.offset + size, ptr.alloc_id, allocation_size) }, EvalError::NoMirFor(ref func) => write!(f, "no mir for `{}`", func), - EvalError::FunctionPointerTyMismatch(expected, got) => - write!(f, "tried to call a function of type {:?} through a function pointer of type {:?}", expected, got), + EvalError::FunctionPointerTyMismatch(abi, sig, got) => + write!(f, "tried to call a function with abi {:?} and sig {:?} through a function pointer of type {:?}", abi, sig, got), EvalError::ArrayIndexOutOfBounds(span, len, index) => write!(f, "index out of bounds: the len is {} but the index is {} at {:?}", len, index, span), EvalError::Math(span, ref err) => diff --git a/src/interpreter/cast.rs b/src/interpreter/cast.rs index e59376c321..217a10a4c7 100644 --- a/src/interpreter/cast.rs +++ b/src/interpreter/cast.rs @@ -21,10 +21,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Bool | Char | U8 | U16 | U32 | U64 => self.cast_int(val.bits, ty, false), - FnPtr | Ptr => { - let ptr = val.expect_ptr("FnPtr- or Ptr-tagged PrimVal had no relocation"); - self.cast_ptr(ptr, ty) - } + FnPtr | Ptr => self.cast_ptr(val.to_ptr(), ty), } } diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index b15f8b2d2d..ee503f97d7 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -182,7 +182,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty: Ty<'tcx>, substs: &'tcx Substs<'tcx> ) -> EvalResult<'tcx, Pointer> { - let size = self.type_size_with_substs(ty, substs); + let size = self.type_size_with_substs(ty, substs).expect("cannot alloc memory for unsized type"); let align = self.type_align_with_substs(ty, substs); self.memory.allocate(size, align) } @@ -203,10 +203,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { PrimVal::from_uint_with_size(n, self.memory.pointer_size()) } - fn isize_primval(&self, n: i64) -> PrimVal { - PrimVal::from_int_with_size(n, self.memory.pointer_size()) - } - fn str_to_value(&mut self, s: &str) -> EvalResult<'tcx, Value> { // FIXME: cache these allocs let ptr = self.memory.allocate(s.len(), 1)?; @@ -290,7 +286,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.tcx.normalize_associated_type(&substituted) } - fn type_size(&self, ty: Ty<'tcx>) -> usize { + fn type_size(&self, ty: Ty<'tcx>) -> Option { self.type_size_with_substs(ty, self.substs()) } @@ -298,8 +294,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.type_align_with_substs(ty, self.substs()) } - 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_size_with_substs(&self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx>) -> Option { + let layout = self.type_layout_with_substs(ty, substs); + if layout.is_unsized() { + None + } else { + Some(layout.size(&self.tcx.data_layout).bytes() as usize) + } } fn type_align_with_substs(&self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx>) -> usize { @@ -480,7 +481,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Array { .. } => { let elem_size = match dest_ty.sty { - ty::TyArray(elem_ty, _) => self.type_size(elem_ty) as u64, + ty::TyArray(elem_ty, _) => self.type_size(elem_ty).expect("array elements are sized") as u64, _ => bug!("tried to assign {:?} to non-array type {:?}", kind, dest_ty), }; let offsets = (0..).map(|i| i * elem_size); @@ -517,8 +518,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let value_ty = self.operand_ty(operand); self.write_value(value, dest, value_ty)?; } else { - assert_eq!(operands.len(), 0); - let zero = self.isize_primval(0); + if let Some(operand) = operands.get(0) { + assert_eq!(operands.len(), 1); + let operand_ty = self.operand_ty(operand); + assert_eq!(self.type_size(operand_ty), Some(0)); + } + let value_size = self.type_size(dest_ty).expect("pointer types are sized"); + let zero = PrimVal::from_int_with_size(0, value_size); self.write_primval(dest, zero)?; } } else { @@ -534,15 +540,16 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } else { for operand in operands { let operand_ty = self.operand_ty(operand); - assert_eq!(self.type_size(operand_ty), 0); + assert_eq!(self.type_size(operand_ty), Some(0)); } - let offset = self.nonnull_offset(dest_ty, nndiscr, discrfield)?; + let (offset, ty) = self.nonnull_offset_and_ty(dest_ty, nndiscr, discrfield)?; // FIXME(solson) let dest = self.force_allocation(dest)?.to_ptr(); let dest = dest.offset(offset.bytes() as isize); - try!(self.memory.write_isize(dest, 0)); + let dest_size = self.type_size(ty).expect("bad StructWrappedNullablePointer discrfield"); + try!(self.memory.write_int(dest, 0, dest_size)); } } else { bug!("tried to assign {:?} to Layout::RawNullablePointer", kind); @@ -567,6 +574,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } + Vector { element, count } => { + let elem_size = element.size(&self.tcx.data_layout).bytes(); + debug_assert_eq!(count, operands.len() as u64); + let offsets = (0..).map(|i| i * elem_size); + self.assign_fields(dest, offsets, operands)?; + } + _ => return Err(EvalError::Unimplemented(format!("can't handle destination layout {:?} when assigning {:?}", dest_layout, kind))), } } @@ -576,7 +590,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::TyArray(elem_ty, n) => (elem_ty, n), _ => bug!("tried to assign array-repeat to non-array type {:?}", dest_ty), }; - let elem_size = self.type_size(elem_ty); + let elem_size = self.type_size(elem_ty).expect("repeat element type must be sized"); let value = self.eval_operand(operand)?; // FIXME(solson) @@ -651,7 +665,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ReifyFnPointer => match self.operand_ty(operand).sty { ty::TyFnDef(def_id, substs, fn_ty) => { - let fn_ptr = self.memory.create_fn_ptr(def_id, substs, fn_ty); + let fn_ty = self.tcx.erase_regions(&fn_ty); + let fn_ptr = self.memory.create_fn_ptr(self.tcx,def_id, substs, fn_ty); self.write_value(Value::ByVal(PrimVal::from_fn_ptr(fn_ptr)), dest, dest_ty)?; }, ref other => bug!("reify fn pointer on {:?}", other), @@ -661,8 +676,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::TyFnPtr(unsafe_fn_ty) => { let src = self.eval_operand(operand)?; let ptr = src.read_ptr(&self.memory)?; - let (def_id, substs, _) = self.memory.get_fn(ptr.alloc_id)?; - let fn_ptr = self.memory.create_fn_ptr(def_id, substs, unsafe_fn_ty); + let (def_id, substs, _, _) = self.memory.get_fn(ptr.alloc_id)?; + let unsafe_fn_ty = self.tcx.erase_regions(&unsafe_fn_ty); + let fn_ptr = self.memory.create_fn_ptr(self.tcx, def_id, substs, unsafe_fn_ty); self.write_value(Value::ByVal(PrimVal::from_fn_ptr(fn_ptr)), dest, dest_ty)?; }, ref other => bug!("fn to unsafe fn cast on {:?}", other), @@ -689,7 +705,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - fn nonnull_offset(&self, ty: Ty<'tcx>, nndiscr: u64, discrfield: &[u32]) -> EvalResult<'tcx, Size> { + fn nonnull_offset_and_ty(&self, ty: Ty<'tcx>, nndiscr: u64, discrfield: &[u32]) -> EvalResult<'tcx, (Size, Ty<'tcx>)> { // Skip the constant 0 at the start meant for LLVM GEP. let mut path = discrfield.iter().skip(1).map(|&i| i as usize); @@ -704,10 +720,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { _ => bug!("non-enum for StructWrappedNullablePointer: {}", ty), }; - self.field_path_offset(inner_ty, path) + self.field_path_offset_and_ty(inner_ty, path) } - fn field_path_offset>(&self, mut ty: Ty<'tcx>, path: I) -> EvalResult<'tcx, Size> { + fn field_path_offset_and_ty>(&self, mut ty: Ty<'tcx>, path: I) -> EvalResult<'tcx, (Size, Ty<'tcx>)> { let mut offset = Size::from_bytes(0); // Skip the initial 0 intended for LLVM GEP. @@ -717,7 +733,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { offset = offset.checked_add(field_offset, &self.tcx.data_layout).unwrap(); } - Ok(offset) + Ok((offset, ty)) } fn get_field_ty(&self, ty: Ty<'tcx>, field_index: usize) -> EvalResult<'tcx, Ty<'tcx>> { @@ -731,8 +747,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::TyRef(_, ty::TypeAndMut { ty, .. }) | ty::TyRawPtr(ty::TypeAndMut { ty, .. }) | ty::TyBox(ty) => { - assert_eq!(field_index, 0); - Ok(ty) + match (field_index, &self.tcx.struct_tail(ty).sty) { + (1, &ty::TyStr) | + (1, &ty::TySlice(_)) => Ok(self.tcx.types.usize), + (1, &ty::TyTrait(_)) | + (0, _) => Ok(self.tcx.mk_imm_ptr(self.tcx.types.u8)), + _ => bug!("invalid fat pointee type: {}", ty), + } } _ => Err(EvalError::Unimplemented(format!("can't handle type: {:?}, {:?}", ty, ty.sty))), } @@ -955,33 +976,27 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } Deref => { - use interpreter::value::Value::*; + let val = self.eval_and_read_lvalue(&proj.base)?; - let val = match self.eval_and_read_lvalue(&proj.base)? { - ByRef(ptr) => self.read_value(ptr, base_ty)?, - v => v, + let pointee_type = match base_ty.sty { + ty::TyRawPtr(ty::TypeAndMut{ty, ..}) | + ty::TyRef(_, ty::TypeAndMut{ty, ..}) | + ty::TyBox(ty) => ty, + _ => bug!("can only deref pointer types"), }; - match val { - ByValPair(ptr, vtable) - if ptr.try_as_ptr().is_some() && vtable.try_as_ptr().is_some() - => { - let ptr = ptr.try_as_ptr().unwrap(); - let vtable = vtable.try_as_ptr().unwrap(); - (ptr, LvalueExtra::Vtable(vtable)) - } - - ByValPair(ptr, n) if ptr.try_as_ptr().is_some() => { - let ptr = ptr.try_as_ptr().unwrap(); - (ptr, LvalueExtra::Length(n.expect_uint("slice length"))) - } + trace!("deref to {} on {:?}", pointee_type, val); - ByVal(ptr) if ptr.try_as_ptr().is_some() => { - let ptr = ptr.try_as_ptr().unwrap(); - (ptr, LvalueExtra::None) - } - - _ => bug!("can't deref non pointer types"), + match self.tcx.struct_tail(pointee_type).sty { + ty::TyTrait(_) => { + let (ptr, vtable) = val.expect_ptr_vtable_pair(&self.memory)?; + (ptr, LvalueExtra::Vtable(vtable)) + }, + ty::TyStr | ty::TySlice(_) => { + let (ptr, len) = val.expect_slice(&self.memory)?; + (ptr, LvalueExtra::Length(len)) + }, + _ => (val.read_ptr(&self.memory)?, LvalueExtra::None), } } @@ -991,7 +1006,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let (base_ptr, _) = base.to_ptr_and_extra(); let (elem_ty, len) = base.elem_ty_and_len(base_ty); - let elem_size = self.type_size(elem_ty); + let elem_size = self.type_size(elem_ty).expect("slice element must be sized"); let n_ptr = self.eval_operand(operand)?; let usize = self.tcx.types.usize; let n = self.value_to_primval(n_ptr, usize)? @@ -1007,7 +1022,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let (base_ptr, _) = base.to_ptr_and_extra(); let (elem_ty, n) = base.elem_ty_and_len(base_ty); - let elem_size = self.type_size(elem_ty); + let elem_size = self.type_size(elem_ty).expect("sequence element must be sized"); assert!(n >= min_length as u64); let index = if from_end { @@ -1026,7 +1041,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let (base_ptr, _) = base.to_ptr_and_extra(); let (elem_ty, n) = base.elem_ty_and_len(base_ty); - let elem_size = self.type_size(elem_ty); + let elem_size = self.type_size(elem_ty).expect("slice element must be sized"); assert!((from as u64) <= n - (to as u64)); let ptr = base_ptr.offset(from as isize * elem_size as isize); let extra = LvalueExtra::Length(n - to as u64 - from as u64); @@ -1046,7 +1061,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } fn copy(&mut self, src: Pointer, dest: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, ()> { - let size = self.type_size(ty); + let size = self.type_size(ty).expect("cannot copy from an unsized type"); let align = self.type_align(ty); self.memory.copy(src, dest, size, align)?; Ok(()) @@ -1388,7 +1403,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::TyFloat(FloatTy::F64) => PrimVal::from_f64(self.memory.read_f64(ptr)?), ty::TyFnDef(def_id, substs, fn_ty) => { - PrimVal::from_fn_ptr(self.memory.create_fn_ptr(def_id, substs, fn_ty)) + PrimVal::from_fn_ptr(self.memory.create_fn_ptr(self.tcx, def_id, substs, fn_ty)) }, ty::TyFnPtr(_) => self.memory.read_ptr(ptr).map(PrimVal::from_fn_ptr)?, ty::TyBox(ty) | @@ -1398,7 +1413,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { if self.type_is_sized(ty) { PrimVal::from_ptr(p) } else { - // FIXME: extract the offset to the tail field for `Box<(i64, i32, [u8])>` + trace!("reading fat pointer extra of type {}", ty); let extra = ptr.offset(self.memory.pointer_size() as isize); let extra = match self.tcx.struct_tail(ty).sty { ty::TyTrait(..) => PrimVal::from_ptr(self.memory.read_ptr(extra)?), @@ -1505,14 +1520,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { //let dst = adt::MaybeSizedValue::sized(dst); let src_ptr = match src { Value::ByRef(ptr) => ptr, - _ => panic!("expected pointer, got {:?}", src), + _ => bug!("expected pointer, got {:?}", src), }; let iter = src_fields.zip(dst_fields).enumerate(); for (i, (src_f, dst_f)) in iter { let src_fty = monomorphize_field_ty(self.tcx, src_f, substs_a); let dst_fty = monomorphize_field_ty(self.tcx, dst_f, substs_b); - if self.type_size(dst_fty) == 0 { + if self.type_size(dst_fty) == Some(0) { continue; } let src_field_offset = self.get_field_offset(src_ty, i)?.bytes() as isize; diff --git a/src/interpreter/terminator/intrinsics.rs b/src/interpreter/terminator/intrinsics.rs index 9161f6e35b..5cea0bc2d0 100644 --- a/src/interpreter/terminator/intrinsics.rs +++ b/src/interpreter/terminator/intrinsics.rs @@ -57,6 +57,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } "atomic_load" | + "atomic_load_acq" | "volatile_load" => { let ty = substs.type_at(0); let ptr = arg_vals[0].read_ptr(&self.memory)?; @@ -74,6 +75,53 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // we are inherently singlethreaded and singlecored, this is a nop } + "atomic_xchg" => { + let ty = substs.type_at(0); + let ptr = arg_vals[0].read_ptr(&self.memory)?; + let change = self.value_to_primval(arg_vals[1], ty)?; + let old = self.read_value(ptr, ty)?; + let old = match old { + Value::ByVal(val) => val, + Value::ByRef(_) => bug!("just read the value, can't be byref"), + Value::ByValPair(..) => bug!("atomic_xchg doesn't work with nonprimitives"), + }; + self.write_primval(dest, old)?; + self.write_primval(Lvalue::from_ptr(ptr), change)?; + } + + "atomic_cxchg" => { + let ty = substs.type_at(0); + let ptr = arg_vals[0].read_ptr(&self.memory)?; + let expect_old = self.value_to_primval(arg_vals[1], ty)?; + let change = self.value_to_primval(arg_vals[2], ty)?; + let old = self.read_value(ptr, ty)?; + let old = match old { + Value::ByVal(val) => val, + Value::ByRef(_) => bug!("just read the value, can't be byref"), + Value::ByValPair(..) => bug!("atomic_cxchg doesn't work with nonprimitives"), + }; + let (val, _) = primval::binary_op(mir::BinOp::Eq, old, expect_old)?; + let dest = self.force_allocation(dest)?.to_ptr(); + self.write_pair_to_ptr(old, val, dest, dest_ty)?; + self.write_primval(Lvalue::from_ptr(ptr), change)?; + } + + "atomic_xadd_relaxed" => { + let ty = substs.type_at(0); + let ptr = arg_vals[0].read_ptr(&self.memory)?; + let change = self.value_to_primval(arg_vals[1], ty)?; + let old = self.read_value(ptr, ty)?; + let old = match old { + Value::ByVal(val) => val, + Value::ByRef(_) => bug!("just read the value, can't be byref"), + Value::ByValPair(..) => bug!("atomic_xadd_relaxed doesn't work with nonprimitives"), + }; + self.write_primval(dest, old)?; + // FIXME: what do atomics do on overflow? + let (val, _) = primval::binary_op(mir::BinOp::Add, old, change)?; + self.write_primval(Lvalue::from_ptr(ptr), val)?; + }, + "atomic_xsub_rel" => { let ty = substs.type_at(0); let ptr = arg_vals[0].read_ptr(&self.memory)?; @@ -96,7 +144,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "copy_nonoverlapping" => { // FIXME: check whether overlapping occurs let elem_ty = substs.type_at(0); - let elem_size = self.type_size(elem_ty); + let elem_size = self.type_size(elem_ty).expect("cannot copy unsized value"); let elem_align = self.type_align(elem_ty); let src = arg_vals[0].read_ptr(&self.memory)?; let dest = arg_vals[1].read_ptr(&self.memory)?; @@ -124,15 +172,17 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "drop_in_place" => { let ty = substs.type_at(0); + trace!("drop in place on {}", ty); let ptr_ty = self.tcx.mk_mut_ptr(ty); let lvalue = match self.follow_by_ref_value(arg_vals[0], ptr_ty)? { Value::ByRef(_) => bug!("follow_by_ref_value returned ByRef"), - Value::ByVal(ptr) => Lvalue::from_ptr(ptr.expect_ptr("drop_in_place first arg not a pointer")), + Value::ByVal(value) => Lvalue::from_ptr(value.to_ptr()), Value::ByValPair(ptr, extra) => Lvalue::Ptr { - ptr: ptr.expect_ptr("drop_in_place first arg not a pointer"), - extra: match extra.try_as_ptr() { - Some(vtable) => LvalueExtra::Vtable(vtable), - None => LvalueExtra::Length(extra.expect_uint("either pointer or not, but not neither")), + ptr: ptr.to_ptr(), + extra: match self.tcx.struct_tail(ty).sty { + ty::TyTrait(_) => LvalueExtra::Vtable(extra.to_ptr()), + ty::TyStr | ty::TySlice(_) => LvalueExtra::Length(extra.try_as_uint()?), + _ => bug!("invalid fat pointer type: {}", ptr_ty), }, }, }; @@ -230,7 +280,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "offset" => { let pointee_ty = substs.type_at(0); - let pointee_size = self.type_size(pointee_ty) as isize; + let pointee_size = self.type_size(pointee_ty).expect("cannot offset a pointer to an unsized type") as isize; let offset = self.value_to_primval(arg_vals[1], isize)? .expect_int("offset second arg not isize"); @@ -281,7 +331,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "size_of" => { let ty = substs.type_at(0); - let size = self.type_size(ty) as u64; + // FIXME: change the `box_free` lang item to take `T: ?Sized` and have it use the + // `size_of_val` intrinsic, then change this back to + // .expect("size_of intrinsic called on unsized value") + // see https://github.com/rust-lang/rust/pull/37708 + let size = self.type_size(ty).unwrap_or(!0) as u64; let size_val = self.usize_primval(size); self.write_primval(dest, size_val)?; } @@ -360,8 +414,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { value: Value, ) -> EvalResult<'tcx, (u64, u64)> { let pointer_size = self.memory.pointer_size(); - if self.type_is_sized(ty) { - Ok((self.type_size(ty) as u64, self.type_align(ty) as u64)) + if let Some(size) = self.type_size(ty) { + Ok((size as u64, self.type_align(ty) as u64)) } else { match ty.sty { ty::TyAdt(def, substs) => { @@ -435,8 +489,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::TySlice(_) | ty::TyStr => { let elem_ty = ty.sequence_element_type(self.tcx); - let elem_size = self.type_size(elem_ty) as u64; - let len = value.expect_slice_len(&self.memory)?; + let elem_size = self.type_size(elem_ty).expect("slice element must be sized") as u64; + let (_, len) = value.expect_slice(&self.memory)?; let align = self.type_align(elem_ty); Ok((len * elem_size, align as u64)) } diff --git a/src/interpreter/terminator/mod.rs b/src/interpreter/terminator/mod.rs index f99a23a22a..ccf7671eec 100644 --- a/src/interpreter/terminator/mod.rs +++ b/src/interpreter/terminator/mod.rs @@ -85,11 +85,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let func_ty = self.operand_ty(func); match func_ty.sty { ty::TyFnPtr(bare_fn_ty) => { - let fn_ptr = self.eval_operand_to_primval(func)? - .expect_fn_ptr("TyFnPtr callee did not evaluate to FnPtr"); - let (def_id, substs, fn_ty) = self.memory.get_fn(fn_ptr.alloc_id)?; - if fn_ty != bare_fn_ty { - return Err(EvalError::FunctionPointerTyMismatch(fn_ty, bare_fn_ty)); + let fn_ptr = self.eval_operand_to_primval(func)?.to_ptr(); + let (def_id, substs, abi, sig) = self.memory.get_fn(fn_ptr.alloc_id)?; + if abi != bare_fn_ty.abi || sig != bare_fn_ty.sig.skip_binder() { + return Err(EvalError::FunctionPointerTyMismatch(abi, sig, bare_fn_ty)); } self.eval_fn_call(def_id, substs, bare_fn_ty, destination, args, terminator.source_info.span)? @@ -193,7 +192,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Abi::C => { let ty = fn_ty.sig.0.output; - let size = self.type_size(ty); + let size = self.type_size(ty).expect("function return type cannot be unsized"); let (ret, target) = destination.unwrap(); self.call_c_abi(def_id, arg_operands, ret, size)?; self.goto_block(target); @@ -263,14 +262,17 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.memory.read_int(adt_ptr, discr_size as usize)? as u64 } - RawNullablePointer { nndiscr, .. } => { - self.read_nonnull_discriminant_value(adt_ptr, nndiscr)? + RawNullablePointer { nndiscr, value } => { + let discr_size = value.size(&self.tcx.data_layout).bytes() as usize; + self.read_nonnull_discriminant_value(adt_ptr, nndiscr, discr_size)? } StructWrappedNullablePointer { nndiscr, ref discrfield, .. } => { - let offset = self.nonnull_offset(adt_ty, nndiscr, discrfield)?; + let (offset, ty) = self.nonnull_offset_and_ty(adt_ty, nndiscr, discrfield)?; let nonnull = adt_ptr.offset(offset.bytes() as isize); - self.read_nonnull_discriminant_value(nonnull, nndiscr)? + // only the pointer part of a fat pointer is used for this space optimization + let discr_size = self.type_size(ty).expect("bad StructWrappedNullablePointer discrfield"); + self.read_nonnull_discriminant_value(nonnull, nndiscr, discr_size)? } // The discriminant_value intrinsic returns 0 for non-sum types. @@ -281,8 +283,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(discr_val) } - fn read_nonnull_discriminant_value(&self, ptr: Pointer, nndiscr: u64) -> EvalResult<'tcx, u64> { - let not_null = match self.memory.read_usize(ptr) { + fn read_nonnull_discriminant_value(&self, ptr: Pointer, nndiscr: u64, discr_size: usize) -> EvalResult<'tcx, u64> { + let not_null = match self.memory.read_uint(ptr, discr_size) { Ok(0) => false, Ok(_) | Err(EvalError::ReadPointerAsBytes) => true, Err(e) => return Err(e), @@ -498,9 +500,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let idx = idx + 3; let offset = idx * self.memory.pointer_size(); let fn_ptr = self.memory.read_ptr(vtable.offset(offset as isize))?; - let (def_id, substs, ty) = self.memory.get_fn(fn_ptr.alloc_id)?; - // FIXME: skip_binder is wrong for HKL - *first_ty = ty.sig.skip_binder().inputs[0]; + let (def_id, substs, _abi, sig) = self.memory.get_fn(fn_ptr.alloc_id)?; + *first_ty = sig.inputs[0]; Ok((def_id, substs)) } else { Err(EvalError::VtableForArgumentlessMethod) @@ -539,14 +540,15 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Value::ByRef(_) => bug!("follow_by_ref_value can't result in ByRef"), Value::ByVal(ptr) => { assert!(self.type_is_sized(contents_ty)); - let contents_ptr = ptr.expect_ptr("value of Box type must be a pointer"); + let contents_ptr = ptr.to_ptr(); self.drop(Lvalue::from_ptr(contents_ptr), contents_ty, drop)?; }, Value::ByValPair(prim_ptr, extra) => { - let ptr = prim_ptr.expect_ptr("value of Box type must be a pointer"); - let extra = match extra.try_as_ptr() { - Some(vtable) => LvalueExtra::Vtable(vtable), - None => LvalueExtra::Length(extra.expect_uint("slice length")), + let ptr = prim_ptr.to_ptr(); + let extra = match self.tcx.struct_tail(contents_ty).sty { + ty::TyTrait(_) => LvalueExtra::Vtable(extra.to_ptr()), + ty::TyStr | ty::TySlice(_) => LvalueExtra::Length(extra.try_as_uint()?), + _ => bug!("invalid fat pointer type: {}", ty), }; self.drop( Lvalue::Ptr { @@ -639,10 +641,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }; let drop_fn = self.memory.read_ptr(vtable)?; // some values don't need to call a drop impl, so the value is null - if !drop_fn.points_to_zst() { - let (def_id, substs, ty) = self.memory.get_fn(drop_fn.alloc_id)?; - let fn_sig = self.tcx.erase_late_bound_regions_and_normalize(&ty.sig); - let real_ty = fn_sig.inputs[0]; + if drop_fn != Pointer::from_int(0) { + let (def_id, substs, _abi, sig) = self.memory.get_fn(drop_fn.alloc_id)?; + let real_ty = sig.inputs[0]; self.drop(Lvalue::from_ptr(ptr), real_ty, drop)?; drop.push((def_id, Value::ByVal(PrimVal::from_ptr(ptr)), substs)); } else { @@ -655,7 +656,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Lvalue::Ptr { ptr, extra: LvalueExtra::Length(len) } => (ptr, len as isize), _ => bug!("expected an lvalue with a length"), }; - let size = self.type_size(elem_ty) as isize; + let size = self.type_size(elem_ty).expect("slice element must be sized") as isize; // FIXME: this creates a lot of stack frames if the element type has // a drop impl for i in 0..len { @@ -668,7 +669,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Lvalue::Ptr { ptr, extra } => (ptr, extra), _ => bug!("expected an lvalue with optional extra data"), }; - let size = self.type_size(elem_ty) as isize; + let size = self.type_size(elem_ty).expect("array element cannot be unsized") as isize; // FIXME: this creates a lot of stack frames if the element type has // a drop impl for i in 0..len { diff --git a/src/interpreter/value.rs b/src/interpreter/value.rs index d9285a41e4..fa89d02ad7 100644 --- a/src/interpreter/value.rs +++ b/src/interpreter/value.rs @@ -22,10 +22,7 @@ impl<'a, 'tcx: 'a> Value { use self::Value::*; match *self { ByRef(ptr) => mem.read_ptr(ptr), - - ByVal(ptr) | ByValPair(ptr, _) => { - Ok(ptr.try_as_ptr().expect("unimplemented: `read_ptr` on non-ptr primval")) - } + ByVal(ptr) | ByValPair(ptr, _) => Ok(ptr.to_ptr()), } } @@ -35,29 +32,29 @@ impl<'a, 'tcx: 'a> Value { ) -> EvalResult<'tcx, (Pointer, Pointer)> { use self::Value::*; match *self { - ByRef(ptr) => { - let ptr = mem.read_ptr(ptr)?; - let vtable = mem.read_ptr(ptr.offset(mem.pointer_size() as isize))?; + ByRef(ref_ptr) => { + let ptr = mem.read_ptr(ref_ptr)?; + let vtable = mem.read_ptr(ref_ptr.offset(mem.pointer_size() as isize))?; Ok((ptr, vtable)) } - ByValPair(ptr, vtable) - if ptr.try_as_ptr().is_some() && vtable.try_as_ptr().is_some() - => { - let ptr = ptr.try_as_ptr().unwrap(); - let vtable = vtable.try_as_ptr().unwrap(); - Ok((ptr, vtable)) - } + ByValPair(ptr, vtable) => Ok((ptr.to_ptr(), vtable.to_ptr())), _ => bug!("expected ptr and vtable, got {:?}", self), } } - pub(super) fn expect_slice_len(&self, mem: &Memory<'a, 'tcx>) -> EvalResult<'tcx, u64> { + pub(super) fn expect_slice(&self, mem: &Memory<'a, 'tcx>) -> EvalResult<'tcx, (Pointer, u64)> { use self::Value::*; match *self { - ByRef(ptr) => mem.read_usize(ptr.offset(mem.pointer_size() as isize)), - ByValPair(_, val) if val.kind.is_int() => Ok(val.bits), + ByRef(ref_ptr) => { + let ptr = mem.read_ptr(ref_ptr)?; + let len = mem.read_usize(ref_ptr.offset(mem.pointer_size() as isize))?; + Ok((ptr, len)) + }, + ByValPair(ptr, val) => { + Ok((ptr.to_ptr(), val.try_as_uint()?)) + }, _ => unimplemented!(), } } diff --git a/src/interpreter/vtable.rs b/src/interpreter/vtable.rs index 17f2c6b702..6651194be7 100644 --- a/src/interpreter/vtable.rs +++ b/src/interpreter/vtable.rs @@ -35,7 +35,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.get_vtable_methods(id, substs) .into_iter() .map(|opt_mth| opt_mth.map(|mth| { - self.memory.create_fn_ptr(mth.method.def_id, mth.substs, mth.method.fty) + let fn_ty = self.tcx.erase_regions(&mth.method.fty); + self.memory.create_fn_ptr(self.tcx, mth.method.def_id, mth.substs, fn_ty) })) .collect::>() .into_iter() @@ -46,22 +47,18 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { substs, nested: _ }) => { let closure_type = self.tcx.closure_type(closure_def_id, substs); - let fn_ty = ty::BareFnTy { - unsafety: closure_type.unsafety, - abi: closure_type.abi, - sig: closure_type.sig, - }; - let _fn_ty = self.tcx.mk_bare_fn(fn_ty); - unimplemented!() - //vec![Some(self.memory.create_fn_ptr(closure_def_id, substs.func_substs, fn_ty))].into_iter() + vec![Some(self.memory.create_closure_ptr(self.tcx, closure_def_id, substs, closure_type))].into_iter() } traits::VtableFnPointer( traits::VtableFnPointerData { - fn_ty: _bare_fn_ty, + fn_ty, nested: _ }) => { - let _trait_closure_kind = tcx.lang_items.fn_trait_kind(trait_ref.def_id()).unwrap(); - //vec![trans_fn_pointer_shim(ccx, trait_closure_kind, bare_fn_ty)].into_iter() - unimplemented!() + match fn_ty.sty { + ty::TyFnDef(did, substs, bare_fn_ty) => { + vec![Some(self.memory.create_fn_ptr(self.tcx, did, substs, bare_fn_ty))].into_iter() + }, + _ => bug!("bad VtableFnPointer fn_ty: {:?}", fn_ty), + } } traits::VtableObject(ref data) => { // this would imply that the Self type being erased is @@ -78,7 +75,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } }).collect(); - let size = self.type_size(trait_ref.self_ty()); + let size = self.type_size(trait_ref.self_ty()).expect("can't create a vtable for an unsized type"); let align = self.type_align(trait_ref.self_ty()); let ptr_size = self.memory.pointer_size(); @@ -90,10 +87,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { if let Some(drop_def_id) = adt_def.destructor() { let ty_scheme = self.tcx.lookup_item_type(drop_def_id); let fn_ty = match ty_scheme.ty.sty { - ty::TyFnDef(_, _, fn_ty) => fn_ty, + ty::TyFnDef(_, _, fn_ty) => self.tcx.erase_regions(&fn_ty), _ => bug!("drop method is not a TyFnDef"), }; - let fn_ptr = self.memory.create_fn_ptr(drop_def_id, substs, fn_ty); + let fn_ptr = self.memory.create_fn_ptr(self.tcx, drop_def_id, substs, fn_ty); self.memory.write_ptr(vtable, fn_ptr)?; } } diff --git a/src/memory.rs b/src/memory.rs index 73770ee403..fce0ff4c97 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -4,10 +4,12 @@ use std::collections::{btree_map, BTreeMap, HashMap, HashSet, VecDeque}; use std::{fmt, iter, ptr}; use rustc::hir::def_id::DefId; -use rustc::ty::{BareFnTy, ClosureTy, ClosureSubsts}; +use rustc::ty::{self, BareFnTy, ClosureTy, ClosureSubsts, TyCtxt}; use rustc::ty::subst::Substs; use rustc::ty::layout::{self, TargetDataLayout}; +use syntax::abi::Abi; + use error::{EvalError, EvalResult}; use primval::PrimVal; @@ -73,7 +75,7 @@ impl Pointer { // FIXME(solson): Integer pointers should use u64, not usize. Target pointers can be larger // than host usize. pub fn from_int(i: usize) -> Self { - Pointer::new(ZST_ALLOC_ID, i) + Pointer::new(NEVER_ALLOC_ID, i) } pub fn zst_ptr() -> Self { @@ -88,19 +90,9 @@ impl Pointer { #[derive(Debug, Clone, Hash, Eq, PartialEq)] struct FunctionDefinition<'tcx> { pub def_id: DefId, - pub kind: FunctionKind<'tcx>, -} - -#[derive(Debug, Clone, Hash, Eq, PartialEq)] -enum FunctionKind<'tcx> { - Closure { - substs: ClosureSubsts<'tcx>, - ty: ClosureTy<'tcx>, - }, - Function { - substs: &'tcx Substs<'tcx>, - ty: &'tcx BareFnTy<'tcx>, - } + pub substs: &'tcx Substs<'tcx>, + pub abi: Abi, + pub sig: &'tcx ty::FnSig<'tcx>, } //////////////////////////////////////////////////////////////////////////////// @@ -143,23 +135,31 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { self.alloc_map.iter() } - pub fn create_closure_ptr(&mut self, def_id: DefId, substs: ClosureSubsts<'tcx>, fn_ty: ClosureTy<'tcx>) -> Pointer { + pub fn create_closure_ptr(&mut self, tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId, substs: ClosureSubsts<'tcx>, fn_ty: ClosureTy<'tcx>) -> Pointer { + // FIXME: this is a hack + let fn_ty = tcx.mk_bare_fn(ty::BareFnTy { + unsafety: fn_ty.unsafety, + abi: fn_ty.abi, + sig: fn_ty.sig, + }); self.create_fn_alloc(FunctionDefinition { def_id: def_id, - kind: FunctionKind::Closure { - substs: substs, - ty: fn_ty, - } + substs: substs.func_substs, + abi: fn_ty.abi, + // FIXME: why doesn't this compile? + //sig: tcx.erase_late_bound_regions(&fn_ty.sig), + sig: fn_ty.sig.skip_binder(), }) } - pub fn create_fn_ptr(&mut self, def_id: DefId, substs: &'tcx Substs<'tcx>, fn_ty: &'tcx BareFnTy<'tcx>) -> Pointer { + pub fn create_fn_ptr(&mut self, _tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId, substs: &'tcx Substs<'tcx>, fn_ty: &'tcx BareFnTy<'tcx>) -> Pointer { self.create_fn_alloc(FunctionDefinition { def_id: def_id, - kind: FunctionKind::Function { - substs: substs, - ty: fn_ty, - } + substs: substs, + abi: fn_ty.abi, + // FIXME: why doesn't this compile? + //sig: tcx.erase_late_bound_regions(&fn_ty.sig), + sig: fn_ty.sig.skip_binder(), }) } @@ -290,7 +290,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { Some(alloc) => Ok(alloc), None => match self.functions.get(&id) { Some(_) => Err(EvalError::DerefFunctionPointer), - None if id == ZST_ALLOC_ID => Err(EvalError::InvalidMemoryAccess), + None if id == NEVER_ALLOC_ID || id == ZST_ALLOC_ID => Err(EvalError::InvalidMemoryAccess), None => Err(EvalError::DanglingPointerDeref), } } @@ -302,39 +302,21 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { Some(alloc) => Ok(alloc), None => match self.functions.get(&id) { Some(_) => Err(EvalError::DerefFunctionPointer), - None if id == ZST_ALLOC_ID => Err(EvalError::InvalidMemoryAccess), + None if id == NEVER_ALLOC_ID || id == ZST_ALLOC_ID => Err(EvalError::InvalidMemoryAccess), None => Err(EvalError::DanglingPointerDeref), } } } - pub fn get_closure(&self, id: AllocId) -> EvalResult<'tcx, (DefId, ClosureSubsts<'tcx>, ClosureTy<'tcx>)> { - debug!("reading closure fn ptr: {}", id); - match self.functions.get(&id) { - Some(&FunctionDefinition { - def_id, - kind: FunctionKind::Closure { ref substs, ref ty } - }) => Ok((def_id, *substs, ty.clone())), - Some(&FunctionDefinition { - kind: FunctionKind::Function { .. }, .. - }) => Err(EvalError::CalledClosureAsFunction), - None => match self.alloc_map.get(&id) { - Some(_) => Err(EvalError::ExecuteMemory), - None => Err(EvalError::InvalidFunctionPointer), - } - } - } - - pub fn get_fn(&self, id: AllocId) -> EvalResult<'tcx, (DefId, &'tcx Substs<'tcx>, &'tcx BareFnTy<'tcx>)> { + pub fn get_fn(&self, id: AllocId) -> EvalResult<'tcx, (DefId, &'tcx Substs<'tcx>, Abi, &'tcx ty::FnSig<'tcx>)> { debug!("reading fn ptr: {}", id); match self.functions.get(&id) { Some(&FunctionDefinition { def_id, - kind: FunctionKind::Function { substs, ty } - }) => Ok((def_id, substs, ty)), - Some(&FunctionDefinition { - kind: FunctionKind::Closure { .. }, .. - }) => Err(EvalError::CalledClosureAsFunction), + substs, + abi, + sig, + }) => Ok((def_id, substs, abi, sig)), None => match self.alloc_map.get(&id) { Some(_) => Err(EvalError::ExecuteMemory), None => Err(EvalError::InvalidFunctionPointer), @@ -533,8 +515,8 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } pub fn write_primval(&mut self, dest: Pointer, val: PrimVal) -> EvalResult<'tcx, ()> { - if let Some(ptr) = val.try_as_ptr() { - return self.write_ptr(dest, ptr); + if let Some(alloc_id) = val.relocation { + return self.write_ptr(dest, Pointer::new(alloc_id, val.bits as usize)); } use primval::PrimValKind::*; @@ -543,7 +525,8 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { I16 | U16 => (2, val.bits as u16 as u64), I32 | U32 | F32 | Char => (4, val.bits as u32 as u64), I64 | U64 | F64 => (8, val.bits), - FnPtr | Ptr => bug!("handled above"), + // int -> ptr transmutes are handled here + FnPtr | Ptr => return self.write_usize(dest, val.bits), }; self.write_uint(dest, bits, size) @@ -570,7 +553,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { 2 => Ok(self.layout.i16_align.abi() as usize), 4 => Ok(self.layout.i32_align.abi() as usize), 8 => Ok(self.layout.i64_align.abi() as usize), - _ => bug!("bad integer size"), + _ => bug!("bad integer size: {}", size), } } diff --git a/src/primval.rs b/src/primval.rs index 2d8b500762..15cc88ca4c 100644 --- a/src/primval.rs +++ b/src/primval.rs @@ -137,15 +137,19 @@ impl PrimVal { bits_to_f64(self.bits) } - pub fn try_as_ptr(self) -> Option { + pub fn to_ptr(self) -> Pointer { self.relocation.map(|alloc_id| { Pointer::new(alloc_id, self.bits as usize) - }) + }).unwrap_or_else(|| Pointer::from_int(self.bits as usize)) + } + + pub fn try_as_uint<'tcx>(self) -> EvalResult<'tcx, u64> { + self.to_ptr().to_int().map(|val| val as u64) } pub fn expect_uint(self, error_msg: &str) -> u64 { - if let Some(ptr) = self.try_as_ptr() { - return ptr.to_int().expect("non abstract ptr") as u64 + if let Ok(int) = self.try_as_uint() { + return int; } use self::PrimValKind::*; @@ -156,8 +160,8 @@ impl PrimVal { } pub fn expect_int(self, error_msg: &str) -> i64 { - if let Some(ptr) = self.try_as_ptr() { - return ptr.to_int().expect("non abstract ptr") as i64 + if let Ok(int) = self.try_as_uint() { + return int as i64; } use self::PrimValKind::*; @@ -188,15 +192,6 @@ impl PrimVal { _ => bug!("{}", error_msg), } } - - pub fn expect_ptr(self, error_msg: &str) -> Pointer { - self.try_as_ptr().expect(error_msg) - } - - /// FIXME(solson): Refactored into a duplicate of `expect_ptr`. Investigate removal. - pub fn expect_fn_ptr(self, error_msg: &str) -> Pointer { - self.try_as_ptr().expect(error_msg) - } } //////////////////////////////////////////////////////////////////////////////// @@ -277,19 +272,13 @@ pub fn binary_op<'tcx>( use rustc::mir::BinOp::*; use self::PrimValKind::*; - match (left.try_as_ptr(), right.try_as_ptr()) { - (Some(left_ptr), Some(right_ptr)) => { - if left_ptr.alloc_id != right_ptr.alloc_id { - return Ok((unrelated_ptr_ops(bin_op)?, false)); - } - - // If the pointers are into the same allocation, fall through to the more general match - // later, which will do comparisons on the `bits` fields, which are the pointer offsets - // in this case. - } - - (None, None) => {} - _ => return Err(EvalError::ReadPointerAsBytes), + // If the pointers are into the same allocation, fall through to the more general match + // later, which will do comparisons on the `bits` fields, which are the pointer offsets + // in this case. + let left_ptr = left.to_ptr(); + let right_ptr = right.to_ptr(); + if left_ptr.alloc_id != right_ptr.alloc_id { + return Ok((unrelated_ptr_ops(bin_op, left_ptr, right_ptr)?, false)); } let (l, r) = (left.bits, right.bits); @@ -376,12 +365,15 @@ pub fn binary_op<'tcx>( Ok((val, false)) } -fn unrelated_ptr_ops<'tcx>(bin_op: mir::BinOp) -> EvalResult<'tcx, PrimVal> { +fn unrelated_ptr_ops<'tcx>(bin_op: mir::BinOp, left: Pointer, right: Pointer) -> EvalResult<'tcx, PrimVal> { use rustc::mir::BinOp::*; match bin_op { Eq => Ok(PrimVal::from_bool(false)), Ne => Ok(PrimVal::from_bool(true)), Lt | Le | Gt | Ge => Err(EvalError::InvalidPointerMath), + _ if left.to_int().is_ok() ^ right.to_int().is_ok() => { + Err(EvalError::ReadPointerAsBytes) + }, _ => bug!(), } } diff --git a/tests/compile-fail/cast_box_int_to_fn_ptr.rs b/tests/compile-fail/cast_box_int_to_fn_ptr.rs new file mode 100644 index 0000000000..030bed6a35 --- /dev/null +++ b/tests/compile-fail/cast_box_int_to_fn_ptr.rs @@ -0,0 +1,8 @@ +fn main() { + let b = Box::new(42); + let g = unsafe { + std::mem::transmute::<&usize, &fn(i32)>(&b) + }; + + (*g)(42) //~ ERROR tried to use an integer pointer or a dangling pointer as a function pointer +} diff --git a/tests/compile-fail/cast_fn_ptr.rs b/tests/compile-fail/cast_fn_ptr.rs index e9b2536a70..c8070913f1 100644 --- a/tests/compile-fail/cast_fn_ptr.rs +++ b/tests/compile-fail/cast_fn_ptr.rs @@ -5,5 +5,5 @@ fn main() { std::mem::transmute::(f) }; - g(42) //~ ERROR tried to call a function of type + g(42) //~ ERROR tried to call a function with abi Rust and sig } diff --git a/tests/compile-fail/cast_int_to_fn_ptr.rs b/tests/compile-fail/cast_int_to_fn_ptr.rs new file mode 100644 index 0000000000..dc39f7dda1 --- /dev/null +++ b/tests/compile-fail/cast_int_to_fn_ptr.rs @@ -0,0 +1,7 @@ +fn main() { + let g = unsafe { + std::mem::transmute::(42) + }; + + g(42) //~ ERROR tried to use an integer pointer or a dangling pointer as a function pointer +} diff --git a/tests/compiletest.rs b/tests/compiletest.rs index e8f43bbbe0..025ec660d2 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -60,6 +60,8 @@ fn compile_test() { let files: Box> = if let Ok(path) = std::env::var("MIRI_RUSTC_TEST") { Box::new(files.chain(std::fs::read_dir(path).unwrap())) } else { + // print traces only when not running on the rust run-pass test suite (since tracing is slow) + std::env::set_var("MIRI_LOG", "trace"); Box::new(files) }; let mut mir_not_found = 0; diff --git a/tests/run-pass/assume_bug.rs b/tests/run-pass/assume_bug.rs new file mode 100644 index 0000000000..e14f875c02 --- /dev/null +++ b/tests/run-pass/assume_bug.rs @@ -0,0 +1,3 @@ +fn main() { + vec![()].into_iter(); +} diff --git a/tests/run-pass/binops.rs b/tests/run-pass/binops.rs new file mode 100644 index 0000000000..a03b96fa49 --- /dev/null +++ b/tests/run-pass/binops.rs @@ -0,0 +1,91 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Binop corner cases + +fn test_nil() { + assert_eq!((), ()); + assert!((!(() != ()))); + assert!((!(() < ()))); + assert!((() <= ())); + assert!((!(() > ()))); + assert!((() >= ())); +} + +fn test_bool() { + assert!((!(true < false))); + assert!((!(true <= false))); + assert!((true > false)); + assert!((true >= false)); + + assert!((false < true)); + assert!((false <= true)); + assert!((!(false > true))); + assert!((!(false >= true))); + + // Bools support bitwise binops + assert_eq!(false & false, false); + assert_eq!(true & false, false); + assert_eq!(true & true, true); + assert_eq!(false | false, false); + assert_eq!(true | false, true); + assert_eq!(true | true, true); + assert_eq!(false ^ false, false); + assert_eq!(true ^ false, true); + assert_eq!(true ^ true, false); +} + +fn test_ptr() { + unsafe { + let p1: *const u8 = ::std::mem::transmute(0_usize); + let p2: *const u8 = ::std::mem::transmute(0_usize); + let p3: *const u8 = ::std::mem::transmute(1_usize); + + assert_eq!(p1, p2); + assert!(p1 != p3); + assert!(p1 < p3); + assert!(p1 <= p3); + assert!(p3 > p1); + assert!(p3 >= p3); + assert!(p1 <= p2); + assert!(p1 >= p2); + } +} + +#[derive(PartialEq, Debug)] +struct P { + x: isize, + y: isize, +} + +fn p(x: isize, y: isize) -> P { + P { + x: x, + y: y + } +} + +fn test_class() { + let q = p(1, 2); + let mut r = p(1, 2); + + assert_eq!(q, r); + r.y = 17; + assert!((r.y != q.y)); + assert_eq!(r.y, 17); + assert!((q != r)); +} + +pub fn main() { + test_nil(); + test_bool(); + test_ptr(); + test_class(); +} diff --git a/tests/run-pass/enum-nullable-const-null-with-fields.rs b/tests/run-pass/enum-nullable-const-null-with-fields.rs new file mode 100644 index 0000000000..1342c4e104 --- /dev/null +++ b/tests/run-pass/enum-nullable-const-null-with-fields.rs @@ -0,0 +1,22 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + + +use std::result::Result; +use std::result::Result::Ok; + +static C: Result<(), Box> = Ok(()); + +// This is because of yet another bad assertion (ICE) about the null side of a nullable enum. +// So we won't actually compile if the bug is present, but we check the value in main anyway. + +pub fn main() { + assert!(C.is_ok()); +} diff --git a/tests/run-pass/issue-30530.rs b/tests/run-pass/issue-30530.rs new file mode 100644 index 0000000000..d5139c908b --- /dev/null +++ b/tests/run-pass/issue-30530.rs @@ -0,0 +1,35 @@ +// Copyright 2012-2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Regression test for Issue #30530: alloca's created for storing +// intermediate scratch values during brace-less match arms need to be +// initialized with their drop-flag set to "dropped" (or else we end +// up running the destructors on garbage data at the end of the +// function). + +pub enum Handler { + Default, + #[allow(dead_code)] + Custom(*mut Box), +} + +fn main() { + take(Handler::Default, Box::new(main)); +} + +#[inline(never)] +pub fn take(h: Handler, f: Box) -> Box { + unsafe { + match h { + Handler::Custom(ptr) => *Box::from_raw(ptr), + Handler::Default => f, + } + } +} diff --git a/tests/run-pass/last-use-in-cap-clause.rs b/tests/run-pass/last-use-in-cap-clause.rs new file mode 100644 index 0000000000..de2d815ca5 --- /dev/null +++ b/tests/run-pass/last-use-in-cap-clause.rs @@ -0,0 +1,25 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Make sure #1399 stays fixed + +#[allow(dead_code)] +struct A { a: Box } + +fn foo() -> Box isize + 'static> { + let k: Box<_> = Box::new(22); + let _u = A {a: k.clone()}; + let result = || 22; + Box::new(result) +} + +pub fn main() { + assert_eq!(foo()(), 22); +} diff --git a/tests/run-pass/sendable-class.rs b/tests/run-pass/sendable-class.rs new file mode 100644 index 0000000000..b3e07d00f0 --- /dev/null +++ b/tests/run-pass/sendable-class.rs @@ -0,0 +1,34 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that a class with only sendable fields can be sent + +// pretty-expanded FIXME #23616 + +use std::sync::mpsc::channel; + +#[allow(dead_code)] +struct Foo { + i: isize, + j: char, +} + +fn foo(i:isize, j: char) -> Foo { + Foo { + i: i, + j: j + } +} + +pub fn main() { + let (tx, rx) = channel(); + let _ = tx.send(foo(42, 'c')); + let _ = rx; +} diff --git a/tests/run-pass/simd-intrinsic-generic-elements.rs b/tests/run-pass/simd-intrinsic-generic-elements.rs new file mode 100644 index 0000000000..36567f4c03 --- /dev/null +++ b/tests/run-pass/simd-intrinsic-generic-elements.rs @@ -0,0 +1,42 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(repr_simd, platform_intrinsics)] + +#[repr(simd)] +#[derive(Copy, Clone, Debug, PartialEq)] +#[allow(non_camel_case_types)] +struct i32x2(i32, i32); +#[repr(simd)] +#[derive(Copy, Clone, Debug, PartialEq)] +#[allow(non_camel_case_types)] +struct i32x3(i32, i32, i32); +#[repr(simd)] +#[derive(Copy, Clone, Debug, PartialEq)] +#[allow(non_camel_case_types)] +struct i32x4(i32, i32, i32, i32); +#[repr(simd)] +#[derive(Copy, Clone, Debug, PartialEq)] +#[allow(non_camel_case_types)] +struct i32x8(i32, i32, i32, i32, + i32, i32, i32, i32); + +fn main() { + let _x2 = i32x2(20, 21); + let _x3 = i32x3(30, 31, 32); + let _x4 = i32x4(40, 41, 42, 43); + let _x8 = i32x8(80, 81, 82, 83, 84, 85, 86, 87); + + let _y2 = i32x2(120, 121); + let _y3 = i32x3(130, 131, 132); + let _y4 = i32x4(140, 141, 142, 143); + let _y8 = i32x8(180, 181, 182, 183, 184, 185, 186, 187); + +} diff --git a/tests/run-pass/small_enum_size_bug.rs b/tests/run-pass/small_enum_size_bug.rs new file mode 100644 index 0000000000..7576a97e36 --- /dev/null +++ b/tests/run-pass/small_enum_size_bug.rs @@ -0,0 +1,14 @@ +#![allow(dead_code)] + +enum E { + A = 1, + B = 2, + C = 3, +} + +fn main() { + let enone = None::; + if let Some(..) = enone { + panic!(); + } +} diff --git a/tests/run-pass/unique-send.rs b/tests/run-pass/unique-send.rs new file mode 100644 index 0000000000..7644da08e4 --- /dev/null +++ b/tests/run-pass/unique-send.rs @@ -0,0 +1,20 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(box_syntax)] + +use std::sync::mpsc::channel; + +pub fn main() { + let (tx, rx) = channel::>(); + tx.send(box 100).unwrap(); + let v = rx.recv().unwrap(); + assert_eq!(v, box 100); +}