From 921f5af1fe14dad904df3b5f6da47860b234a813 Mon Sep 17 00:00:00 2001 From: Oliver 'ker' Schneider Date: Thu, 10 Nov 2016 19:20:11 +0100 Subject: [PATCH 01/19] ensure that integers cast to pointers will never point at a valid alloc, not even the zst alloc --- src/error.rs | 2 +- src/interpreter/terminator/mod.rs | 2 +- src/memory.rs | 6 +++--- tests/run-pass/assume_bug.rs | 3 +++ 4 files changed, 8 insertions(+), 5 deletions(-) create mode 100644 tests/run-pass/assume_bug.rs diff --git a/src/error.rs b/src/error.rs index 86f22d33a2..52662218ff 100644 --- a/src/error.rs +++ b/src/error.rs @@ -61,7 +61,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 as a function pointer", EvalError::InvalidBool => "invalid boolean value read", EvalError::InvalidDiscriminant => diff --git a/src/interpreter/terminator/mod.rs b/src/interpreter/terminator/mod.rs index f99a23a22a..1ddcee3472 100644 --- a/src/interpreter/terminator/mod.rs +++ b/src/interpreter/terminator/mod.rs @@ -639,7 +639,7 @@ 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() { + if drop_fn != Pointer::from_int(0) { 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]; diff --git a/src/memory.rs b/src/memory.rs index 73770ee403..a83ed98a37 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -73,7 +73,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 { @@ -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,7 +302,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), } } 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(); +} From b2d476eb387b739e37f28435613596e027241fc5 Mon Sep 17 00:00:00 2001 From: Oliver 'ker' Schneider Date: Fri, 11 Nov 2016 13:07:41 +0100 Subject: [PATCH 02/19] `type_size` now returns `None` for unsized types --- src/interpreter/mod.rs | 29 ++++++++++++++---------- src/interpreter/terminator/intrinsics.rs | 16 ++++++++----- src/interpreter/terminator/mod.rs | 6 ++--- src/interpreter/vtable.rs | 2 +- 4 files changed, 31 insertions(+), 22 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index b15f8b2d2d..9c5dd30e37 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) } @@ -290,7 +290,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 +298,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 +485,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); @@ -534,7 +539,7 @@ 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)?; @@ -576,7 +581,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) @@ -991,7 +996,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 +1012,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 +1031,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 +1051,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(()) @@ -1512,7 +1517,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { 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..280cabc078 100644 --- a/src/interpreter/terminator/intrinsics.rs +++ b/src/interpreter/terminator/intrinsics.rs @@ -96,7 +96,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)?; @@ -230,7 +230,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 +281,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 +364,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,7 +439,7 @@ 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 elem_size = self.type_size(elem_ty).expect("slice element must be sized") as u64; let len = value.expect_slice_len(&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 1ddcee3472..0097e4d2c2 100644 --- a/src/interpreter/terminator/mod.rs +++ b/src/interpreter/terminator/mod.rs @@ -193,7 +193,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); @@ -655,7 +655,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 +668,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/vtable.rs b/src/interpreter/vtable.rs index 17f2c6b702..7320872724 100644 --- a/src/interpreter/vtable.rs +++ b/src/interpreter/vtable.rs @@ -78,7 +78,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(); From 1c40fb0da1dcdb8fd7445589c66b5a755a36ab3b Mon Sep 17 00:00:00 2001 From: Oliver 'ker' Schneider Date: Fri, 11 Nov 2016 13:08:14 +0100 Subject: [PATCH 03/19] report the bad integer size instead of just the fact that it is bad --- src/memory.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/memory.rs b/src/memory.rs index a83ed98a37..412eb62e9a 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -570,7 +570,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), } } From d42a7d021d564edc950678c39da593266f6411ba Mon Sep 17 00:00:00 2001 From: Oliver 'ker' Schneider Date: Fri, 11 Nov 2016 13:10:47 +0100 Subject: [PATCH 04/19] fix null optimizations for smaller than pointer enums fixes #76 --- src/interpreter/mod.rs | 20 +++++++++----------- src/interpreter/terminator/mod.rs | 15 +++++++++------ tests/run-pass/small_enum_size_bug.rs | 14 ++++++++++++++ 3 files changed, 32 insertions(+), 17 deletions(-) create mode 100644 tests/run-pass/small_enum_size_bug.rs diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 9c5dd30e37..bd99d9f86f 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -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)?; @@ -523,7 +519,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.write_value(value, dest, value_ty)?; } else { assert_eq!(operands.len(), 0); - let zero = self.isize_primval(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 { @@ -541,13 +538,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let operand_ty = self.operand_ty(operand); 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).unwrap_or(self.memory.pointer_size()); + try!(self.memory.write_int(dest, 0, dest_size)); } } else { bug!("tried to assign {:?} to Layout::RawNullablePointer", kind); @@ -694,7 +692,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); @@ -709,10 +707,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. @@ -722,7 +720,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>> { diff --git a/src/interpreter/terminator/mod.rs b/src/interpreter/terminator/mod.rs index 0097e4d2c2..afbcf3d0e5 100644 --- a/src/interpreter/terminator/mod.rs +++ b/src/interpreter/terminator/mod.rs @@ -263,14 +263,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).unwrap_or(self.memory.pointer_size()); + self.read_nonnull_discriminant_value(nonnull, nndiscr, discr_size)? } // The discriminant_value intrinsic returns 0 for non-sum types. @@ -281,8 +284,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), 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!(); + } +} From 75f56eb144be4c87893c83fa5ad3b0ce4b84e737 Mon Sep 17 00:00:00 2001 From: Oliver 'ker' Schneider Date: Sun, 13 Nov 2016 19:26:20 +0100 Subject: [PATCH 05/19] fix field indexing into fat pointers --- src/interpreter/mod.rs | 15 ++++++++++++--- src/interpreter/terminator/mod.rs | 2 +- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index bd99d9f86f..08713b7211 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -544,7 +544,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let dest = self.force_allocation(dest)?.to_ptr(); let dest = dest.offset(offset.bytes() as isize); - let dest_size = self.type_size(ty).unwrap_or(self.memory.pointer_size()); + let dest_size = self.type_size(ty).expect("bad StructWrappedNullablePointer discrfield"); try!(self.memory.write_int(dest, 0, dest_size)); } } else { @@ -734,8 +734,17 @@ 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) + if self.type_is_sized(ty) { + assert_eq!(field_index, 0); + Ok(self.tcx.mk_imm_ptr(self.tcx.types.never)) + } else { + match (field_index, &ty.sty) { + (1, &ty::TySlice(_)) => Ok(self.tcx.types.usize), + (1, &ty::TyTrait(_)) | + (0, _) => Ok(self.tcx.mk_imm_ptr(self.tcx.types.never)), + _ => bug!("invalid fat pointee type"), + } + } } _ => Err(EvalError::Unimplemented(format!("can't handle type: {:?}, {:?}", ty, ty.sty))), } diff --git a/src/interpreter/terminator/mod.rs b/src/interpreter/terminator/mod.rs index afbcf3d0e5..655a59902e 100644 --- a/src/interpreter/terminator/mod.rs +++ b/src/interpreter/terminator/mod.rs @@ -272,7 +272,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let (offset, ty) = self.nonnull_offset_and_ty(adt_ty, nndiscr, discrfield)?; let nonnull = adt_ptr.offset(offset.bytes() as isize); // only the pointer part of a fat pointer is used for this space optimization - let discr_size = self.type_size(ty).unwrap_or(self.memory.pointer_size()); + let discr_size = self.type_size(ty).expect("bad StructWrappedNullablePointer discrfield"); self.read_nonnull_discriminant_value(nonnull, nndiscr, discr_size)? } From f71c31c0e866f87e7aaafc315b042c077ff302b5 Mon Sep 17 00:00:00 2001 From: Oliver 'ker' Schneider Date: Sun, 13 Nov 2016 21:30:03 +0100 Subject: [PATCH 06/19] cannot index into non-fat-pointers --- src/interpreter/mod.rs | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 08713b7211..0e96656a21 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -734,16 +734,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::TyRef(_, ty::TypeAndMut { ty, .. }) | ty::TyRawPtr(ty::TypeAndMut { ty, .. }) | ty::TyBox(ty) => { - if self.type_is_sized(ty) { - assert_eq!(field_index, 0); - Ok(self.tcx.mk_imm_ptr(self.tcx.types.never)) - } else { - match (field_index, &ty.sty) { - (1, &ty::TySlice(_)) => Ok(self.tcx.types.usize), - (1, &ty::TyTrait(_)) | - (0, _) => Ok(self.tcx.mk_imm_ptr(self.tcx.types.never)), - _ => bug!("invalid fat pointee type"), - } + assert!(!self.type_is_sized(ty)); + match (field_index, &self.tcx.struct_tail(ty).sty) { + (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"), } } _ => Err(EvalError::Unimplemented(format!("can't handle type: {:?}, {:?}", ty, ty.sty))), From 2c34d6558c4d5c8f0283566cba8110a50de427a1 Mon Sep 17 00:00:00 2001 From: Oliver 'ker' Schneider Date: Sun, 13 Nov 2016 21:56:57 +0100 Subject: [PATCH 07/19] also address TyStr in the null pointer optimization --- src/interpreter/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 0e96656a21..3a4d875921 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -734,12 +734,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::TyRef(_, ty::TypeAndMut { ty, .. }) | ty::TyRawPtr(ty::TypeAndMut { ty, .. }) | ty::TyBox(ty) => { - assert!(!self.type_is_sized(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"), + _ => bug!("invalid fat pointee type: {}", ty), } } _ => Err(EvalError::Unimplemented(format!("can't handle type: {:?}, {:?}", ty, ty.sty))), From 511fa40d23fc77c7867c773135eb0a2fe15a607f Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 15 Nov 2016 14:11:00 +0100 Subject: [PATCH 08/19] add test for int -> fn ptr cast --- src/error.rs | 2 +- tests/compile-fail/cast_box_int_to_fn_ptr.rs | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 tests/compile-fail/cast_box_int_to_fn_ptr.rs diff --git a/src/error.rs b/src/error.rs index 52662218ff..8001d95941 100644 --- a/src/error.rs +++ b/src/error.rs @@ -61,7 +61,7 @@ impl<'tcx> Error for EvalError<'tcx> { EvalError::DanglingPointerDeref => "dangling pointer was dereferenced", EvalError::InvalidFunctionPointer => - "tried to use an integer 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 => 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 +} From 4a39c228df089e32cebea1b1aec8cd9694089574 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 15 Nov 2016 14:11:44 +0100 Subject: [PATCH 09/19] minor fixes the FIXME was wrong here, there's no need for any special offsetting --- src/interpreter/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 3a4d875921..a67142e733 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -1406,7 +1406,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)?), @@ -1513,7 +1513,7 @@ 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(); From 14ff6411f0018c58de6a323dc8fb8bc46fc44356 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 15 Nov 2016 14:12:49 +0100 Subject: [PATCH 10/19] make sure ByVal pointers act just like ByRef to a pointer --- src/interpreter/cast.rs | 5 +-- src/interpreter/mod.rs | 40 ++++++++----------- src/interpreter/terminator/intrinsics.rs | 14 ++++--- src/interpreter/terminator/mod.rs | 14 +++---- src/interpreter/value.rs | 31 +++++++-------- src/memory.rs | 4 +- src/primval.rs | 50 ++++++++++-------------- tests/compile-fail/cast_int_to_fn_ptr.rs | 7 ++++ 8 files changed, 77 insertions(+), 88 deletions(-) create mode 100644 tests/compile-fail/cast_int_to_fn_ptr.rs 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 a67142e733..791466f690 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -963,33 +963,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), } } diff --git a/src/interpreter/terminator/intrinsics.rs b/src/interpreter/terminator/intrinsics.rs index 280cabc078..bc25857466 100644 --- a/src/interpreter/terminator/intrinsics.rs +++ b/src/interpreter/terminator/intrinsics.rs @@ -124,15 +124,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), }, }, }; @@ -440,7 +442,7 @@ 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).expect("slice element must be sized") as u64; - let len = value.expect_slice_len(&self.memory)?; + 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 655a59902e..fbd9e76a07 100644 --- a/src/interpreter/terminator/mod.rs +++ b/src/interpreter/terminator/mod.rs @@ -85,8 +85,7 @@ 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 fn_ptr = self.eval_operand_to_primval(func)?.to_ptr(); 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)); @@ -542,14 +541,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 { 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/memory.rs b/src/memory.rs index 412eb62e9a..348437f50b 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -533,8 +533,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::*; 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_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 +} From 13f22f8344f2ac149ecb5d6d817f208e99926ba1 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 15 Nov 2016 14:33:07 +0100 Subject: [PATCH 11/19] print traces only when not running on the rust run-pass test suite (since tracing is slow) --- tests/compiletest.rs | 2 ++ 1 file changed, 2 insertions(+) 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; From f77a0ab10bbe2dac1ad7733c0cd63729ceb0ed15 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 15 Nov 2016 15:18:49 +0100 Subject: [PATCH 12/19] fix writing int->ptr transmuted primvals to memory --- src/memory.rs | 3 +- tests/run-pass/binops.rs | 96 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 tests/run-pass/binops.rs diff --git a/src/memory.rs b/src/memory.rs index 348437f50b..8bb39d5167 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -543,7 +543,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) diff --git a/tests/run-pass/binops.rs b/tests/run-pass/binops.rs new file mode 100644 index 0000000000..9466f115b1 --- /dev/null +++ b/tests/run-pass/binops.rs @@ -0,0 +1,96 @@ +// 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); + + unsafe { + println!("q = {:x}, r = {:x}", + (::std::mem::transmute::<*const P, usize>(&q)), + (::std::mem::transmute::<*const P, usize>(&r))); + } + 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(); +} From e2091ff93481c5e1c41da26ea202c911c1140a0c Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 15 Nov 2016 15:19:38 +0100 Subject: [PATCH 13/19] add more atomic intrinsics --- src/interpreter/terminator/intrinsics.rs | 48 ++++++++++++++++++++++++ tests/run-pass/sendable-class.rs | 34 +++++++++++++++++ 2 files changed, 82 insertions(+) create mode 100644 tests/run-pass/sendable-class.rs diff --git a/src/interpreter/terminator/intrinsics.rs b/src/interpreter/terminator/intrinsics.rs index bc25857466..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)?; 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; +} From 1549c2d51e128825f6ecd92ae1cd88dd4a0ae02b Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 15 Nov 2016 15:20:10 +0100 Subject: [PATCH 14/19] erase all lifetimes from function types before creating pointers to them --- src/interpreter/mod.rs | 3 +++ src/interpreter/vtable.rs | 5 +++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 791466f690..a431a7111a 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -654,6 +654,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ReifyFnPointer => match self.operand_ty(operand).sty { ty::TyFnDef(def_id, substs, fn_ty) => { + let fn_ty = self.tcx.erase_regions(&fn_ty); let fn_ptr = self.memory.create_fn_ptr(def_id, substs, fn_ty); self.write_value(Value::ByVal(PrimVal::from_fn_ptr(fn_ptr)), dest, dest_ty)?; }, @@ -665,6 +666,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { 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 unsafe_fn_ty = self.tcx.erase_regions(&unsafe_fn_ty); let fn_ptr = self.memory.create_fn_ptr(def_id, substs, unsafe_fn_ty); self.write_value(Value::ByVal(PrimVal::from_fn_ptr(fn_ptr)), dest, dest_ty)?; }, @@ -1390,6 +1392,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) => { + let fn_ty = self.tcx.erase_regions(&fn_ty); PrimVal::from_fn_ptr(self.memory.create_fn_ptr(def_id, substs, fn_ty)) }, ty::TyFnPtr(_) => self.memory.read_ptr(ptr).map(PrimVal::from_fn_ptr)?, diff --git a/src/interpreter/vtable.rs b/src/interpreter/vtable.rs index 7320872724..9892da0bdb 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(mth.method.def_id, mth.substs, fn_ty) })) .collect::>() .into_iter() @@ -90,7 +91,7 @@ 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); From 4748587a7743f297ff7550b51dc7dfe4ef7c67ee Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 15 Nov 2016 15:23:19 +0100 Subject: [PATCH 15/19] fix creation of simd types --- src/interpreter/mod.rs | 7 ++++ .../simd-intrinsic-generic-elements.rs | 42 +++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 tests/run-pass/simd-intrinsic-generic-elements.rs diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index a431a7111a..3c4900c054 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -570,6 +570,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))), } } 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); + +} From 5ee75c0805be98f89ac4edd6101a0960add75108 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 15 Nov 2016 15:24:22 +0100 Subject: [PATCH 16/19] don't print in the binop tests --- tests/run-pass/binops.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tests/run-pass/binops.rs b/tests/run-pass/binops.rs index 9466f115b1..a03b96fa49 100644 --- a/tests/run-pass/binops.rs +++ b/tests/run-pass/binops.rs @@ -76,11 +76,6 @@ fn test_class() { let q = p(1, 2); let mut r = p(1, 2); - unsafe { - println!("q = {:x}, r = {:x}", - (::std::mem::transmute::<*const P, usize>(&q)), - (::std::mem::transmute::<*const P, usize>(&r))); - } assert_eq!(q, r); r.y = 17; assert!((r.y != q.y)); From 1c5c6cd078a12beba06c5eb70bd9f05ca27836b1 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 15 Nov 2016 16:15:17 +0100 Subject: [PATCH 17/19] allow zsts in the zero case of a nullable pointer optimized enum --- src/interpreter/mod.rs | 6 ++++- .../enum-nullable-const-null-with-fields.rs | 22 +++++++++++++++++++ tests/run-pass/unique-send.rs | 20 +++++++++++++++++ 3 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 tests/run-pass/enum-nullable-const-null-with-fields.rs create mode 100644 tests/run-pass/unique-send.rs diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 3c4900c054..6697dcd60e 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -518,7 +518,11 @@ 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); + 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)?; 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/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); +} From 64155ffd102ba739008a903afd82bb7aeb4c3607 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 15 Nov 2016 16:32:21 +0100 Subject: [PATCH 18/19] implement fn item -> trait object conversion --- src/interpreter/vtable.rs | 11 +++++++---- tests/run-pass/issue-30530.rs | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 4 deletions(-) create mode 100644 tests/run-pass/issue-30530.rs diff --git a/src/interpreter/vtable.rs b/src/interpreter/vtable.rs index 9892da0bdb..201bedc1a8 100644 --- a/src/interpreter/vtable.rs +++ b/src/interpreter/vtable.rs @@ -58,11 +58,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } 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(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 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, + } + } +} From fd68670c0a65dc6b972b336d449ca60547a01355 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 15 Nov 2016 17:19:37 +0100 Subject: [PATCH 19/19] merge closures and function and implement some closure vtable cases --- src/error.rs | 9 +-- src/interpreter/mod.rs | 9 ++- src/interpreter/terminator/mod.rs | 16 +++-- src/interpreter/vtable.rs | 15 ++--- src/memory.rs | 76 +++++++++--------------- tests/compile-fail/cast_fn_ptr.rs | 2 +- tests/run-pass/last-use-in-cap-clause.rs | 25 ++++++++ 7 files changed, 75 insertions(+), 77 deletions(-) create mode 100644 tests/run-pass/last-use-in-cap-clause.rs diff --git a/src/error.rs b/src/error.rs index 8001d95941..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, @@ -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/mod.rs b/src/interpreter/mod.rs index 6697dcd60e..ee503f97d7 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -666,7 +666,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ReifyFnPointer => match self.operand_ty(operand).sty { ty::TyFnDef(def_id, substs, fn_ty) => { let fn_ty = self.tcx.erase_regions(&fn_ty); - let fn_ptr = self.memory.create_fn_ptr(def_id, substs, 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), @@ -676,9 +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 (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(def_id, substs, 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), @@ -1403,8 +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) => { - let fn_ty = self.tcx.erase_regions(&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) | diff --git a/src/interpreter/terminator/mod.rs b/src/interpreter/terminator/mod.rs index fbd9e76a07..ccf7671eec 100644 --- a/src/interpreter/terminator/mod.rs +++ b/src/interpreter/terminator/mod.rs @@ -86,9 +86,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match func_ty.sty { ty::TyFnPtr(bare_fn_ty) => { let fn_ptr = self.eval_operand_to_primval(func)?.to_ptr(); - 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 (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)? @@ -500,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) @@ -643,9 +642,8 @@ 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 != Pointer::from_int(0) { - 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]; + 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 { diff --git a/src/interpreter/vtable.rs b/src/interpreter/vtable.rs index 201bedc1a8..6651194be7 100644 --- a/src/interpreter/vtable.rs +++ b/src/interpreter/vtable.rs @@ -36,7 +36,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { .into_iter() .map(|opt_mth| opt_mth.map(|mth| { let fn_ty = self.tcx.erase_regions(&mth.method.fty); - self.memory.create_fn_ptr(mth.method.def_id, mth.substs, fn_ty) + self.memory.create_fn_ptr(self.tcx, mth.method.def_id, mth.substs, fn_ty) })) .collect::>() .into_iter() @@ -47,14 +47,7 @@ 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 { @@ -62,7 +55,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { nested: _ }) => { match fn_ty.sty { ty::TyFnDef(did, substs, bare_fn_ty) => { - vec![Some(self.memory.create_fn_ptr(did, substs, bare_fn_ty))].into_iter() + vec![Some(self.memory.create_fn_ptr(self.tcx, did, substs, bare_fn_ty))].into_iter() }, _ => bug!("bad VtableFnPointer fn_ty: {:?}", fn_ty), } @@ -97,7 +90,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { 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 8bb39d5167..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; @@ -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(), }) } @@ -308,33 +308,15 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } } - 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), 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/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); +}