From ea6f6079ca6958c1b74f8255b55c2d76e9c121d8 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 20 Jun 2017 14:26:50 +0200 Subject: [PATCH] Use PrimVal instead of Pointer where applicable --- src/eval_context.rs | 30 +++++----- src/lvalue.rs | 11 ++-- src/memory.rs | 15 +++-- src/terminator/intrinsic.rs | 14 ++--- tests/run-pass/slice-of-zero-size-elements.rs | 58 +++++++++++++++++++ tests/run-pass/zero-sized-binary-heap-push.rs | 28 +++++++++ 6 files changed, 125 insertions(+), 31 deletions(-) create mode 100644 tests/run-pass/slice-of-zero-size-elements.rs create mode 100644 tests/run-pass/zero-sized-binary-heap-push.rs diff --git a/src/eval_context.rs b/src/eval_context.rs index 185f4e1709b..30a4be7b4b8 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -637,7 +637,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let value = self.eval_operand(operand)?; // FIXME(solson) - let dest = self.force_allocation(dest)?.to_ptr()?; + let dest = PrimVal::Ptr(self.force_allocation(dest)?.to_ptr()?); for i in 0..length { let elem_dest = dest.offset(i * elem_size, self.memory.layout)?; @@ -893,6 +893,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } // FIXME: assuming here that type size is < i64::max_value() let pointee_size = self.type_size(pointee_ty)?.expect("cannot offset a pointer to an unsized type") as i64; + if pointee_size == 0 { + // rustc relies on offsetting pointers to zsts to be a nop + return Ok(ptr); + } return if let Some(offset) = offset.checked_mul(pointee_size) { let ptr = ptr.signed_offset(offset, self.memory.layout)?; self.memory.check_bounds(ptr.to_ptr()?, false)?; @@ -943,7 +947,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.monomorphize(operand.ty(self.mir(), self.tcx), self.substs()) } - fn copy(&mut self, src: Pointer, dest: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx> { + fn copy(&mut self, src: PrimVal, dest: PrimVal, ty: Ty<'tcx>) -> EvalResult<'tcx> { 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)?; @@ -969,7 +973,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let substs = self.stack[frame].instance.substs; let ptr = self.alloc_ptr_with_substs(ty, substs)?; self.stack[frame].locals[local.index() - 1] = Some(Value::ByRef(ptr)); // it stays live - self.write_value_to_ptr(val, ptr, ty)?; + self.write_value_to_ptr(val, PrimVal::Ptr(ptr), ty)?; let lval = Lvalue::from_ptr(ptr); if let Some((field, field_ty)) = field { self.lvalue_field(lval, field, ty, field_ty)? @@ -987,7 +991,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { _ => { let ptr = self.alloc_ptr_with_substs(global_val.ty, cid.instance.substs)?; self.memory.mark_static(ptr.alloc_id); - self.write_value_to_ptr(global_val.value, ptr, global_val.ty)?; + self.write_value_to_ptr(global_val.value, PrimVal::Ptr(ptr), global_val.ty)?; // see comment on `initialized` field if global_val.initialized { self.memory.mark_static_initalized(ptr.alloc_id, global_val.mutable)?; @@ -1059,7 +1063,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Lvalue::Ptr { ptr, extra } => { assert_eq!(extra, LvalueExtra::None); - self.write_value_to_ptr(src_val, ptr.to_ptr()?, dest_ty) + self.write_value_to_ptr(src_val, ptr, dest_ty) } Lvalue::Local { frame, local, field } => { @@ -1090,7 +1094,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // // Thus, it would be an error to replace the `ByRef` with a `ByVal`, unless we // knew for certain that there were no outstanding pointers to this allocation. - self.write_value_to_ptr(src_val, dest_ptr, dest_ty)?; + self.write_value_to_ptr(src_val, PrimVal::Ptr(dest_ptr), dest_ty)?; } else if let Value::ByRef(src_ptr) = src_val { // If the value is not `ByRef`, then we know there are no pointers to it @@ -1108,7 +1112,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { write_dest(self, src_val)?; } else { let dest_ptr = self.alloc_ptr(dest_ty)?; - self.copy(src_ptr, dest_ptr, dest_ty)?; + self.copy(PrimVal::Ptr(src_ptr), PrimVal::Ptr(dest_ptr), dest_ty)?; write_dest(self, Value::ByRef(dest_ptr))?; } @@ -1123,16 +1127,16 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub(super) fn write_value_to_ptr( &mut self, value: Value, - dest: Pointer, + dest: PrimVal, dest_ty: Ty<'tcx>, ) -> EvalResult<'tcx> { match value { - Value::ByRef(ptr) => self.copy(ptr, dest, dest_ty), + Value::ByRef(ptr) => self.copy(PrimVal::Ptr(ptr), dest, dest_ty), Value::ByVal(primval) => { let size = self.type_size(dest_ty)?.expect("dest type must be sized"); self.memory.write_primval(dest, primval, size) } - Value::ByValPair(a, b) => self.write_pair_to_ptr(a, b, dest, dest_ty), + Value::ByValPair(a, b) => self.write_pair_to_ptr(a, b, dest.to_ptr()?, dest_ty), } } @@ -1153,8 +1157,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let field_1_ty = self.get_field_ty(ty, 1)?; let field_0_size = self.type_size(field_0_ty)?.expect("pair element type must be sized"); let field_1_size = self.type_size(field_1_ty)?.expect("pair element type must be sized"); - self.memory.write_primval(ptr.offset(field_0, self.memory.layout)?, a, field_0_size)?; - self.memory.write_primval(ptr.offset(field_1, self.memory.layout)?, b, field_1_size)?; + self.memory.write_primval(PrimVal::Ptr(ptr.offset(field_0, self.memory.layout)?), a, field_0_size)?; + self.memory.write_primval(PrimVal::Ptr(ptr.offset(field_1, self.memory.layout)?), b, field_1_size)?; Ok(()) } @@ -1457,7 +1461,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let src_f_ptr = src_ptr.offset(src_field_offset, self.memory.layout)?; let dst_f_ptr = dest.offset(dst_field_offset, self.memory.layout)?; if src_fty == dst_fty { - self.copy(src_f_ptr, dst_f_ptr, src_fty)?; + self.copy(PrimVal::Ptr(src_f_ptr), PrimVal::Ptr(dst_f_ptr), src_fty)?; } else { self.unsize_into(Value::ByRef(src_f_ptr), src_fty, Lvalue::from_ptr(dst_f_ptr), dst_fty)?; } diff --git a/src/lvalue.rs b/src/lvalue.rs index f344a4e3b2d..f409f373484 100644 --- a/src/lvalue.rs +++ b/src/lvalue.rs @@ -66,10 +66,11 @@ pub struct Global<'tcx> { impl<'tcx> Lvalue<'tcx> { /// Produces an Lvalue that will error if attempted to be read from pub fn undef() -> Self { - Lvalue::Ptr { - ptr: PrimVal::Undef, - extra: LvalueExtra::None, - } + Self::from_primval_ptr(PrimVal::Undef) + } + + fn from_primval_ptr(ptr: PrimVal) -> Self { + Lvalue::Ptr { ptr, extra: LvalueExtra::None } } pub fn zst() -> Self { @@ -77,7 +78,7 @@ impl<'tcx> Lvalue<'tcx> { } pub fn from_ptr(ptr: Pointer) -> Self { - Lvalue::Ptr { ptr: PrimVal::Ptr(ptr), extra: LvalueExtra::None } + Self::from_primval_ptr(PrimVal::Ptr(ptr)) } pub(super) fn to_ptr_and_extra(self) -> (PrimVal, LvalueExtra) { diff --git a/src/memory.rs b/src/memory.rs index ea1d8695d59..8aab55e0239 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -618,7 +618,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { return Ok(&mut []); } self.clear_relocations(ptr, size)?; - self.mark_definedness(ptr, size, true)?; + self.mark_definedness(PrimVal::Ptr(ptr), size, true)?; self.get_bytes_unchecked_mut(ptr, size, align) } } @@ -671,10 +671,12 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { Ok(()) } - pub fn copy(&mut self, src: Pointer, dest: Pointer, size: u64, align: u64) -> EvalResult<'tcx> { + pub fn copy(&mut self, src: PrimVal, dest: PrimVal, size: u64, align: u64) -> EvalResult<'tcx> { if size == 0 { return Ok(()); } + let src = src.to_ptr()?; + let dest = dest.to_ptr()?; self.check_relocation_edges(src, size)?; let src_bytes = self.get_bytes_unchecked(src, size, align)?.as_ptr(); @@ -755,14 +757,14 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { pub fn write_primval( &mut self, - dest: Pointer, + dest: PrimVal, val: PrimVal, size: u64, ) -> EvalResult<'tcx> { match val { PrimVal::Ptr(ptr) => { assert_eq!(size, self.pointer_size()); - self.write_ptr(dest, ptr) + self.write_ptr(dest.to_ptr()?, ptr) } PrimVal::Bytes(bytes) => { @@ -776,7 +778,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { 16 => !0, _ => bug!("unexpected PrimVal::Bytes size"), }; - self.write_uint(dest, bytes & mask, size) + self.write_uint(dest.to_ptr()?, bytes & mask, size) } PrimVal::Undef => self.mark_definedness(dest, size, false), @@ -962,13 +964,14 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { pub fn mark_definedness( &mut self, - ptr: Pointer, + ptr: PrimVal, size: u64, new_state: bool ) -> EvalResult<'tcx> { if size == 0 { return Ok(()) } + let ptr = ptr.to_ptr()?; let mut alloc = self.get_mut(ptr.alloc_id)?; alloc.undef_mask.set_range(ptr.offset, ptr.offset + size, new_state); Ok(()) diff --git a/src/terminator/intrinsic.rs b/src/terminator/intrinsic.rs index a69380f1b18..f6ef46ecbfb 100644 --- a/src/terminator/intrinsic.rs +++ b/src/terminator/intrinsic.rs @@ -69,7 +69,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "atomic_store_rel" | "volatile_store" => { let ty = substs.type_at(0); - let dest = arg_vals[0].read_ptr(&self.memory)?.to_ptr()?; + let dest = arg_vals[0].read_ptr(&self.memory)?; self.write_value_to_ptr(arg_vals[1], dest, ty)?; } @@ -145,8 +145,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let elem_size = self.type_size(elem_ty)?.expect("cannot copy unsized value"); if elem_size != 0 { let elem_align = self.type_align(elem_ty)?; - let src = arg_vals[0].read_ptr(&self.memory)?.to_ptr()?; - let dest = arg_vals[1].read_ptr(&self.memory)?.to_ptr()?; + let src = arg_vals[0].read_ptr(&self.memory)?; + let dest = arg_vals[1].read_ptr(&self.memory)?; let count = self.value_to_primval(arg_vals[2], usize)?.to_u64()?; self.memory.copy(src, dest, count * elem_size, elem_align)?; } @@ -284,7 +284,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "move_val_init" => { let ty = substs.type_at(0); - let ptr = arg_vals[0].read_ptr(&self.memory)?.to_ptr()?; + let ptr = arg_vals[0].read_ptr(&self.memory)?; self.write_value_to_ptr(arg_vals[1], ptr, ty)?; } @@ -392,7 +392,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { if dest_align < src_align { let ptr = self.force_allocation(dest)?.to_ptr()?; self.memory.mark_packed(ptr, size); - self.write_value_to_ptr(arg_vals[0], ptr, dest_ty)?; + self.write_value_to_ptr(arg_vals[0], PrimVal::Ptr(ptr), dest_ty)?; } else { self.write_value(arg_vals[0], dest, dest_ty)?; } @@ -403,7 +403,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let uninit = |this: &mut Self, val: Value| { match val { Value::ByRef(ptr) => { - this.memory.mark_definedness(ptr, size, false)?; + this.memory.mark_definedness(PrimVal::Ptr(ptr), size, false)?; Ok(Value::ByRef(ptr)) }, _ => Ok(Value::ByVal(PrimVal::Undef)), @@ -412,7 +412,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match dest { Lvalue::Local { frame, local, field } => self.modify_local(frame, local, field.map(|(i, _)| i), uninit)?, Lvalue::Ptr { ptr, extra: LvalueExtra::None } => - self.memory.mark_definedness(ptr.to_ptr()?, size, false)?, + self.memory.mark_definedness(ptr, size, false)?, Lvalue::Ptr { .. } => bug!("uninit intrinsic tried to write to fat ptr target"), Lvalue::Global(cid) => self.modify_global(cid, uninit)?, } diff --git a/tests/run-pass/slice-of-zero-size-elements.rs b/tests/run-pass/slice-of-zero-size-elements.rs new file mode 100644 index 00000000000..dbe8ec9adda --- /dev/null +++ b/tests/run-pass/slice-of-zero-size-elements.rs @@ -0,0 +1,58 @@ +// 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. + +// compile-flags: -C debug-assertions + +use std::slice; + +fn foo(v: &[T]) -> Option<&[T]> { + let mut it = v.iter(); + for _ in 0..5 { + let _ = it.next(); + } + Some(it.as_slice()) +} + +fn foo_mut(v: &mut [T]) -> Option<&mut [T]> { + let mut it = v.iter_mut(); + for _ in 0..5 { + let _ = it.next(); + } + Some(it.into_slice()) +} + +pub fn main() { + // In a slice of zero-size elements the pointer is meaningless. + // Ensure iteration still works even if the pointer is at the end of the address space. + let slice: &[()] = unsafe { slice::from_raw_parts(-5isize as *const (), 10) }; + assert_eq!(slice.len(), 10); + assert_eq!(slice.iter().count(), 10); + + // .nth() on the iterator should also behave correctly + let mut it = slice.iter(); + assert!(it.nth(5).is_some()); + assert_eq!(it.count(), 4); + + // Converting Iter to a slice should never have a null pointer + assert!(foo(slice).is_some()); + + // Test mutable iterators as well + let slice: &mut [()] = unsafe { slice::from_raw_parts_mut(-5isize as *mut (), 10) }; + assert_eq!(slice.len(), 10); + assert_eq!(slice.iter_mut().count(), 10); + + { + let mut it = slice.iter_mut(); + assert!(it.nth(5).is_some()); + assert_eq!(it.count(), 4); + } + + assert!(foo_mut(slice).is_some()) +} diff --git a/tests/run-pass/zero-sized-binary-heap-push.rs b/tests/run-pass/zero-sized-binary-heap-push.rs new file mode 100644 index 00000000000..63a0d65f017 --- /dev/null +++ b/tests/run-pass/zero-sized-binary-heap-push.rs @@ -0,0 +1,28 @@ +// Copyright 2013 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::collections::BinaryHeap; +use std::iter::Iterator; + +fn main() { + const N: usize = 8; + + for len in 0..N { + let mut tester = BinaryHeap::with_capacity(len); + assert_eq!(tester.len(), 0); + assert!(tester.capacity() >= len); + for _ in 0..len { + tester.push(()); + } + assert_eq!(tester.len(), len); + assert_eq!(tester.iter().count(), len); + tester.clear(); + } +}