diff --git a/src/liballoc/arc.rs b/src/liballoc/arc.rs index 1d2157b7e2fa3..9610e31a7260d 100644 --- a/src/liballoc/arc.rs +++ b/src/liballoc/arc.rs @@ -24,8 +24,7 @@ use core::mem; use core::ops::{Drop, Deref}; use core::option::Option; use core::option::Option::{Some, None}; -use core::ptr::RawPtr; -use core::ptr; +use core::ptr::{mod, NonZero, RawPtr}; use heap::deallocate; /// An atomically reference counted wrapper for shared state. @@ -59,7 +58,7 @@ use heap::deallocate; pub struct Arc { // FIXME #12808: strange name to try to avoid interfering with // field accesses of the contained type via Deref - _ptr: *mut ArcInner, + _ptr: NonZero<*mut ArcInner>, } /// A weak pointer to an `Arc`. @@ -71,7 +70,7 @@ pub struct Arc { pub struct Weak { // FIXME #12808: strange name to try to avoid interfering with // field accesses of the contained type via Deref - _ptr: *mut ArcInner, + _ptr: NonZero<*mut ArcInner>, } struct ArcInner { @@ -92,7 +91,7 @@ impl Arc { weak: atomic::AtomicUint::new(1), data: data, }; - Arc { _ptr: unsafe { mem::transmute(x) } } + Arc { _ptr: unsafe { NonZero::new(mem::transmute(x)) } } } /// Downgrades a strong pointer to a weak pointer. @@ -116,7 +115,7 @@ impl Arc { // `ArcInner` structure itself is `Sync` because the inner data is // `Sync` as well, so we're ok loaning out an immutable pointer to // these contents. - unsafe { &*self._ptr } + unsafe { &**self._ptr } } } @@ -184,7 +183,7 @@ impl Arc { // reference count is guaranteed to be 1 at this point, and we required // the Arc itself to be `mut`, so we're returning the only possible // reference to the inner data. - let inner = unsafe { &mut *self._ptr }; + let inner = unsafe { &mut **self._ptr }; &mut inner.data } } @@ -193,10 +192,11 @@ impl Arc { #[experimental = "waiting on stability of Drop"] impl Drop for Arc { fn drop(&mut self) { + let ptr = *self._ptr; // This structure has #[unsafe_no_drop_flag], so this drop glue may run // more than once (but it is guaranteed to be zeroed after the first if // it's run more than once) - if self._ptr.is_null() { return } + if ptr.is_null() { return } // Because `fetch_sub` is already atomic, we do not need to synchronize // with other threads unless we are going to delete the object. This @@ -228,7 +228,7 @@ impl Drop for Arc { if self.inner().weak.fetch_sub(1, atomic::Release) == 1 { atomic::fence(atomic::Acquire); - unsafe { deallocate(self._ptr as *mut u8, size_of::>(), + unsafe { deallocate(ptr as *mut u8, size_of::>(), min_align_of::>()) } } } @@ -256,7 +256,7 @@ impl Weak { #[inline] fn inner(&self) -> &ArcInner { // See comments above for why this is "safe" - unsafe { &*self._ptr } + unsafe { &**self._ptr } } } @@ -274,15 +274,17 @@ impl Clone for Weak { #[experimental = "Weak pointers may not belong in this module."] impl Drop for Weak { fn drop(&mut self) { + let ptr = *self._ptr; + // see comments above for why this check is here - if self._ptr.is_null() { return } + if ptr.is_null() { return } // If we find out that we were the last weak pointer, then its time to // deallocate the data entirely. See the discussion in Arc::drop() about // the memory orderings if self.inner().weak.fetch_sub(1, atomic::Release) == 1 { atomic::fence(atomic::Acquire); - unsafe { deallocate(self._ptr as *mut u8, size_of::>(), + unsafe { deallocate(ptr as *mut u8, size_of::>(), min_align_of::>()) } } } diff --git a/src/liballoc/rc.rs b/src/liballoc/rc.rs index 53891583edb20..7584ea1c46af4 100644 --- a/src/liballoc/rc.rs +++ b/src/liballoc/rc.rs @@ -151,8 +151,7 @@ use core::mem::{transmute, min_align_of, size_of, forget}; use core::ops::{Deref, Drop}; use core::option::Option; use core::option::Option::{Some, None}; -use core::ptr; -use core::ptr::RawPtr; +use core::ptr::{mod, NonZero, RawPtr}; use core::result::Result; use core::result::Result::{Ok, Err}; @@ -172,7 +171,7 @@ struct RcBox { pub struct Rc { // FIXME #12808: strange names to try to avoid interfering with // field accesses of the contained type via Deref - _ptr: *mut RcBox, + _ptr: NonZero<*mut RcBox>, _nosend: marker::NoSend, _noshare: marker::NoSync } @@ -196,11 +195,11 @@ impl Rc { // destructor never frees the allocation while the // strong destructor is running, even if the weak // pointer is stored inside the strong one. - _ptr: transmute(box RcBox { + _ptr: NonZero::new(transmute(box RcBox { value: value, strong: Cell::new(1), weak: Cell::new(1) - }), + })), _nosend: marker::NoSend, _noshare: marker::NoSync } @@ -281,7 +280,7 @@ pub fn try_unwrap(rc: Rc) -> Result> { let val = ptr::read(&*rc); // copy the contained object // destruct the box and skip our Drop // we can ignore the refcounts because we know we're unique - deallocate(rc._ptr as *mut u8, size_of::>(), + deallocate(*rc._ptr as *mut u8, size_of::>(), min_align_of::>()); forget(rc); Ok(val) @@ -311,7 +310,7 @@ pub fn try_unwrap(rc: Rc) -> Result> { #[experimental] pub fn get_mut<'a, T>(rc: &'a mut Rc) -> Option<&'a mut T> { if is_unique(rc) { - let inner = unsafe { &mut *rc._ptr }; + let inner = unsafe { &mut **rc._ptr }; Some(&mut inner.value) } else { None @@ -344,7 +343,7 @@ impl Rc { // reference count is guaranteed to be 1 at this point, and we required // the `Rc` itself to be `mut`, so we're returning the only possible // reference to the inner value. - let inner = unsafe { &mut *self._ptr }; + let inner = unsafe { &mut **self._ptr }; &mut inner.value } } @@ -386,7 +385,8 @@ impl Drop for Rc { /// ``` fn drop(&mut self) { unsafe { - if !self._ptr.is_null() { + let ptr = *self._ptr; + if !ptr.is_null() { self.dec_strong(); if self.strong() == 0 { ptr::read(&**self); // destroy the contained object @@ -396,7 +396,7 @@ impl Drop for Rc { self.dec_weak(); if self.weak() == 0 { - deallocate(self._ptr as *mut u8, size_of::>(), + deallocate(ptr as *mut u8, size_of::>(), min_align_of::>()) } } @@ -604,7 +604,7 @@ impl fmt::Show for Rc { pub struct Weak { // FIXME #12808: strange names to try to avoid interfering with // field accesses of the contained type via Deref - _ptr: *mut RcBox, + _ptr: NonZero<*mut RcBox>, _nosend: marker::NoSend, _noshare: marker::NoSync } @@ -668,12 +668,13 @@ impl Drop for Weak { /// ``` fn drop(&mut self) { unsafe { - if !self._ptr.is_null() { + let ptr = *self._ptr; + if !ptr.is_null() { self.dec_weak(); // the weak count starts at 1, and will only go to // zero if all the strong pointers have disappeared. if self.weak() == 0 { - deallocate(self._ptr as *mut u8, size_of::>(), + deallocate(ptr as *mut u8, size_of::>(), min_align_of::>()) } } @@ -728,12 +729,12 @@ trait RcBoxPtr { impl RcBoxPtr for Rc { #[inline(always)] - fn inner(&self) -> &RcBox { unsafe { &(*self._ptr) } } + fn inner(&self) -> &RcBox { unsafe { &(**self._ptr) } } } impl RcBoxPtr for Weak { #[inline(always)] - fn inner(&self) -> &RcBox { unsafe { &(*self._ptr) } } + fn inner(&self) -> &RcBox { unsafe { &(**self._ptr) } } } #[cfg(test)] diff --git a/src/libcollections/vec.rs b/src/libcollections/vec.rs index c77ef86697ba4..a7b6316ef3350 100644 --- a/src/libcollections/vec.rs +++ b/src/libcollections/vec.rs @@ -26,6 +26,7 @@ use core::mem; use core::num::{Int, UnsignedInt}; use core::ops; use core::ptr; +use core::ptr::NonZero; use core::raw::Slice as RawSlice; use core::uint; @@ -103,7 +104,7 @@ use slice::{CloneSliceAllocPrelude}; #[unsafe_no_drop_flag] #[stable] pub struct Vec { - ptr: *mut T, + ptr: NonZero<*mut T>, len: uint, cap: uint, } @@ -146,7 +147,7 @@ impl Vec { // non-null value which is fine since we never call deallocate on the ptr // if cap is 0. The reason for this is because the pointer of a slice // being NULL would break the null pointer optimization for enums. - Vec { ptr: EMPTY as *mut T, len: 0, cap: 0 } + Vec { ptr: unsafe { NonZero::new(EMPTY as *mut T) }, len: 0, cap: 0 } } /// Constructs a new, empty `Vec` with the specified capacity. @@ -180,7 +181,7 @@ impl Vec { #[stable] pub fn with_capacity(capacity: uint) -> Vec { if mem::size_of::() == 0 { - Vec { ptr: EMPTY as *mut T, len: 0, cap: uint::MAX } + Vec { ptr: unsafe { NonZero::new(EMPTY as *mut T) }, len: 0, cap: uint::MAX } } else if capacity == 0 { Vec::new() } else { @@ -188,7 +189,7 @@ impl Vec { .expect("capacity overflow"); let ptr = unsafe { allocate(size, mem::min_align_of::()) }; if ptr.is_null() { ::alloc::oom() } - Vec { ptr: ptr as *mut T, len: 0, cap: capacity } + Vec { ptr: unsafe { NonZero::new(ptr as *mut T) }, len: 0, cap: capacity } } } @@ -261,7 +262,7 @@ impl Vec { #[unstable = "needs finalization"] pub unsafe fn from_raw_parts(ptr: *mut T, length: uint, capacity: uint) -> Vec { - Vec { ptr: ptr, len: length, cap: capacity } + Vec { ptr: NonZero::new(ptr), len: length, cap: capacity } } /// Creates a vector by copying the elements from a raw pointer. @@ -746,7 +747,7 @@ impl Vec { if self.len == 0 { if self.cap != 0 { unsafe { - dealloc(self.ptr, self.cap) + dealloc(*self.ptr, self.cap) } self.cap = 0; } @@ -754,11 +755,12 @@ impl Vec { unsafe { // Overflow check is unnecessary as the vector is already at // least this large. - self.ptr = reallocate(self.ptr as *mut u8, - self.cap * mem::size_of::(), - self.len * mem::size_of::(), - mem::min_align_of::()) as *mut T; - if self.ptr.is_null() { ::alloc::oom() } + let ptr = reallocate(*self.ptr as *mut u8, + self.cap * mem::size_of::(), + self.len * mem::size_of::(), + mem::min_align_of::()) as *mut T; + if ptr.is_null() { ::alloc::oom() } + self.ptr = NonZero::new(ptr); } self.cap = self.len; } @@ -818,7 +820,7 @@ impl Vec { pub fn as_mut_slice<'a>(&'a mut self) -> &'a mut [T] { unsafe { mem::transmute(RawSlice { - data: self.ptr as *const T, + data: *self.ptr as *const T, len: self.len, }) } @@ -841,9 +843,9 @@ impl Vec { #[unstable = "matches collection reform specification, waiting for dust to settle"] pub fn into_iter(self) -> MoveItems { unsafe { - let ptr = self.ptr; + let ptr = *self.ptr; let cap = self.cap; - let begin = self.ptr as *const T; + let begin = ptr as *const T; let end = if mem::size_of::() == 0 { (ptr as uint + self.len()) as *const T } else { @@ -1060,14 +1062,15 @@ impl Vec { let size = max(old_size, 2 * mem::size_of::()) * 2; if old_size > size { panic!("capacity overflow") } unsafe { - self.ptr = alloc_or_realloc(self.ptr, old_size, size); - if self.ptr.is_null() { ::alloc::oom() } + let ptr = alloc_or_realloc(*self.ptr, old_size, size); + if ptr.is_null() { ::alloc::oom() } + self.ptr = NonZero::new(ptr); } self.cap = max(self.cap, 2) * 2; } unsafe { - let end = (self.ptr as *const T).offset(self.len as int) as *mut T; + let end = *self.ptr.offset(self.len as int); ptr::write(&mut *end, value); self.len += 1; } @@ -1147,8 +1150,9 @@ impl Vec { let size = capacity.checked_mul(mem::size_of::()) .expect("capacity overflow"); unsafe { - self.ptr = alloc_or_realloc(self.ptr, self.cap * mem::size_of::(), size); - if self.ptr.is_null() { ::alloc::oom() } + let ptr = alloc_or_realloc(*self.ptr, self.cap * mem::size_of::(), size); + if ptr.is_null() { ::alloc::oom() } + self.ptr = NonZero::new(ptr); } self.cap = capacity; } @@ -1269,7 +1273,7 @@ impl AsSlice for Vec { fn as_slice<'a>(&'a self) -> &'a [T] { unsafe { mem::transmute(RawSlice { - data: self.ptr as *const T, + data: *self.ptr as *const T, len: self.len }) } @@ -1296,7 +1300,7 @@ impl Drop for Vec { for x in self.iter() { ptr::read(x); } - dealloc(self.ptr, self.cap) + dealloc(*self.ptr, self.cap) } } } @@ -1332,7 +1336,7 @@ impl MoveItems { for _x in self { } let MoveItems { allocation, cap, ptr: _ptr, end: _end } = self; mem::forget(self); - Vec { ptr: allocation, cap: cap, len: 0 } + Vec { ptr: NonZero::new(allocation), cap: cap, len: 0 } } } diff --git a/src/libcore/ptr.rs b/src/libcore/ptr.rs index 3f6ac49786d2e..0d1d7ba6ea315 100644 --- a/src/libcore/ptr.rs +++ b/src/libcore/ptr.rs @@ -92,6 +92,7 @@ use clone::Clone; use intrinsics; use option::Option; use option::Option::{Some, None}; +use ops::Deref; use cmp::{PartialEq, Eq, PartialOrd, Equiv}; use cmp::Ordering; @@ -101,6 +102,31 @@ pub use intrinsics::copy_memory; pub use intrinsics::copy_nonoverlapping_memory; pub use intrinsics::set_memory; + +/// A wrapper type for raw pointers and integers that will never be +/// NULL or 0 that might allow certain optimizations. +#[lang="non_zero"] +#[deriving(Clone, PartialEq, Eq, PartialOrd)] +#[experimental] +pub struct NonZero(T); + +impl NonZero { + /// Create an instance of NonZero with the provided value. + /// You must indeed ensure that the value is actually "non-zero". + #[inline(always)] + pub unsafe fn new(inner: T) -> NonZero { + NonZero(inner) + } +} + +impl Deref for NonZero { + #[inline] + fn deref<'a>(&'a self) -> &'a T { + let NonZero(ref inner) = *self; + inner + } +} + /// Create a null pointer. /// /// # Example @@ -264,6 +290,32 @@ impl RawPtr for *const T { } } +impl RawPtr for NonZero<*const T> { + #[inline] + fn null() -> NonZero<*const T> { NonZero(null()) } + + #[inline] + fn is_null(&self) -> bool { false } + + #[inline] + fn to_uint(&self) -> uint { + let NonZero(p) = *self; + p as uint + } + + #[inline] + unsafe fn offset(self, count: int) -> NonZero<*const T> { + let NonZero(p) = self; + NonZero(intrinsics::offset(p, count)) + } + + #[inline] + unsafe fn as_ref<'a>(&self) -> Option<&'a T> { + let NonZero(p) = *self; + Some(&*p) + } +} + impl RawPtr for *mut T { #[inline] fn null() -> *mut T { null_mut() } @@ -289,6 +341,32 @@ impl RawPtr for *mut T { } } +impl RawPtr for NonZero<*mut T> { + #[inline] + fn null() -> NonZero<*mut T> { NonZero(null_mut()) } + + #[inline] + fn is_null(&self) -> bool { false } + + #[inline] + fn to_uint(&self) -> uint { + let NonZero(p) = *self; + p as uint + } + + #[inline] + unsafe fn offset(self, count: int) -> NonZero<*mut T> { + let NonZero(p) = self; + NonZero(intrinsics::offset(p as *const T, count) as *mut T) + } + + #[inline] + unsafe fn as_ref<'a>(&self) -> Option<&'a T> { + let NonZero(p) = *self; + Some(&*p) + } +} + impl RawMutPtr for *mut T { #[inline] unsafe fn as_mut<'a>(&self) -> Option<&'a mut T> { @@ -300,6 +378,14 @@ impl RawMutPtr for *mut T { } } +impl RawMutPtr for NonZero<*mut T> { + #[inline] + unsafe fn as_mut<'a>(&self) -> Option<&'a mut T> { + let NonZero(p) = *self; + Some(&mut *p) + } +} + // Equality for pointers impl PartialEq for *const T { #[inline] diff --git a/src/librustc/middle/lang_items.rs b/src/librustc/middle/lang_items.rs index da1c0bd649a16..4fe0a60dcdec1 100644 --- a/src/librustc/middle/lang_items.rs +++ b/src/librustc/middle/lang_items.rs @@ -308,6 +308,8 @@ lets_do_this! { NoSyncItem, "no_sync_bound", no_sync_bound; ManagedItem, "managed_bound", managed_bound; + NonZeroItem, "non_zero", non_zero; + IteratorItem, "iterator", iterator; StackExhaustedLangItem, "stack_exhausted", stack_exhausted; diff --git a/src/librustc_trans/trans/adt.rs b/src/librustc_trans/trans/adt.rs index 2f0f373325a01..1f861496c8ca1 100644 --- a/src/librustc_trans/trans/adt.rs +++ b/src/librustc_trans/trans/adt.rs @@ -43,14 +43,13 @@ #![allow(unsigned_negation)] -pub use self::PointerField::*; pub use self::Repr::*; use std::num::Int; use std::rc::Rc; use llvm::{ValueRef, True, IntEQ, IntNE}; -use back::abi; +use back::abi::FAT_PTR_ADDR; use middle::subst; use middle::subst::Subst; use trans::_match; @@ -71,7 +70,6 @@ use util::ppaux::ty_to_string; type Hint = attr::ReprAttr; - /// Representations. #[deriving(Eq, PartialEq, Show)] pub enum Repr<'tcx> { @@ -101,7 +99,7 @@ pub enum Repr<'tcx> { nullfields: Vec> }, /// Two cases distinguished by a nullable pointer: the case with discriminant - /// `nndiscr` is represented by the struct `nonnull`, where the `ptrfield`th + /// `nndiscr` is represented by the struct `nonnull`, where the `discrfield`th /// field is known to be nonnull due to its type; if that field is null, then /// it represents the other case, which is inhabited by at most one value /// (and all other fields are undefined/unused). @@ -112,7 +110,7 @@ pub enum Repr<'tcx> { StructWrappedNullablePointer { nonnull: Struct<'tcx>, nndiscr: Disr, - ptrfield: PointerField, + discrfield: DiscrField, nullfields: Vec>, } } @@ -230,18 +228,20 @@ fn represent_type_uncached<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, let st = mk_struct(cx, cases[discr].tys.as_slice(), false, t); match cases[discr].find_ptr(cx) { - Some(ThinPointer(_)) if st.fields.len() == 1 => { + Some(ref pf) if pf.len() == 1 && st.fields.len() == 1 => { return RawNullablePointer { nndiscr: discr as Disr, nnty: st.fields[0], nullfields: cases[1 - discr].tys.clone() }; } - Some(ptrfield) => { + Some(pf) => { + let mut discrfield = vec![0]; + discrfield.extend(pf.into_iter()); return StructWrappedNullablePointer { nndiscr: discr as Disr, nonnull: st, - ptrfield: ptrfield, + discrfield: discrfield, nullfields: cases[1 - discr].tys.clone() }; } @@ -280,48 +280,110 @@ struct Case<'tcx> { tys: Vec> } +/// This represents the (GEP) indices to follow to get to the discriminant field +pub type DiscrField = Vec; -#[deriving(Eq, PartialEq, Show)] -pub enum PointerField { - ThinPointer(uint), - FatPointer(uint) -} +fn find_discr_field_candidate<'tcx>(tcx: &ty::ctxt<'tcx>, ty: Ty<'tcx>) -> Option { + match ty.sty { + // &T/&mut T/Box could either be a thin or fat pointer depending on T + ty::ty_rptr(_, ty::mt { ty, .. }) | ty::ty_uniq(ty) => match ty.sty { + // &[T] and &str are a pointer and length pair + ty::ty_vec(_, None) | ty::ty_str => Some(vec![FAT_PTR_ADDR]), -impl<'tcx> Case<'tcx> { - fn is_zerolen<'a>(&self, cx: &CrateContext<'a, 'tcx>, scapegoat: Ty<'tcx>) -> bool { - mk_struct(cx, self.tys.as_slice(), false, scapegoat).size == 0 - } + // &Trait is a pair of pointers: the actual object and a vtable + ty::ty_trait(..) => Some(vec![FAT_PTR_ADDR]), - fn find_ptr<'a>(&self, cx: &CrateContext<'a, 'tcx>) -> Option { - for (i, &ty) in self.tys.iter().enumerate() { - match ty.sty { - // &T/&mut T/Box could either be a thin or fat pointer depending on T - ty::ty_rptr(_, ty::mt { ty, .. }) | ty::ty_uniq(ty) => match ty.sty { - // &[T] and &str are a pointer and length pair - ty::ty_vec(_, None) | ty::ty_str => return Some(FatPointer(i)), + ty::ty_struct(..) if !ty::type_is_sized(tcx, ty) => Some(vec![FAT_PTR_ADDR]), + + // Any other &T is just a pointer + _ => Some(vec![]) + }, - // &Trait is a pair of pointers: the actual object and a vtable - ty::ty_trait(..) => return Some(FatPointer(i)), + // Functions are just pointers + ty::ty_bare_fn(..) => Some(vec![]), + + // Closures are a pair of pointers: the code and environment + ty::ty_closure(..) => Some(vec![FAT_PTR_ADDR]), + + // Is this the NonZero lang item wrapping a pointer or integer type? + ty::ty_struct(did, ref substs) if Some(did) == tcx.lang_items.non_zero() => { + let nonzero_fields = ty::lookup_struct_fields(tcx, did); + assert_eq!(nonzero_fields.len(), 1); + let nonzero_field = ty::lookup_field_type(tcx, did, nonzero_fields[0].id, substs); + match nonzero_field.sty { + ty::ty_ptr(..) | ty::ty_int(..) | + ty::ty_uint(..) => Some(vec![0]), + _ => None + } + }, - ty::ty_struct(..) if !ty::type_is_sized(cx.tcx(), ty) => { - return Some(FatPointer(i)) + // Perhaps one of the fields of this struct is non-zero + // let's recurse and find out + ty::ty_struct(def_id, ref substs) => { + let fields = ty::lookup_struct_fields(tcx, def_id); + for (j, field) in fields.iter().enumerate() { + let field_ty = ty::lookup_field_type(tcx, def_id, field.id, substs); + match find_discr_field_candidate(tcx, field_ty) { + Some(v) => { + let mut discrfield = vec![j]; + discrfield.extend(v.into_iter()); + return Some(discrfield); } + None => continue + } + } + None + }, - // Any other &T is just a pointer - _ => return Some(ThinPointer(i)) - }, + // Can we use one of the fields in this tuple? + ty::ty_tup(ref tys) => { + for (j, &ty) in tys.iter().enumerate() { + match find_discr_field_candidate(tcx, ty) { + Some(v) => { + let mut discrfield = vec![j]; + discrfield.extend(v.into_iter()); + return Some(discrfield); + } + None => continue + } + } + None + }, - // Functions are just pointers - ty::ty_bare_fn(..) => return Some(ThinPointer(i)), + // Is this a fixed-size array of something non-zero + // with at least one element? + ty::ty_vec(ety, Some(d)) if d > 0 => { + match find_discr_field_candidate(tcx, ety) { + Some(v) => { + let mut discrfield = vec![0]; + discrfield.extend(v.into_iter()); + return Some(discrfield); + } + None => None + } + }, - // Closures are a pair of pointers: the code and environment - ty::ty_closure(..) => return Some(FatPointer(i)), + // Anything else is not a pointer + _ => None + } +} + +impl<'tcx> Case<'tcx> { + fn is_zerolen<'a>(&self, cx: &CrateContext<'a, 'tcx>, scapegoat: Ty<'tcx>) -> bool { + mk_struct(cx, self.tys.as_slice(), false, scapegoat).size == 0 + } - // Anything else is not a pointer - _ => continue + fn find_ptr<'a>(&self, cx: &CrateContext<'a, 'tcx>) -> Option { + for (i, &ty) in self.tys.iter().enumerate() { + match find_discr_field_candidate(cx.tcx(), ty) { + Some(v) => { + let mut discrfield = vec![i]; + discrfield.extend(v.into_iter()); + return Some(discrfield); + } + None => continue } } - None } } @@ -653,8 +715,8 @@ pub fn trans_get_discr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, r: &Repr<'tcx>, val = ICmp(bcx, cmp, Load(bcx, scrutinee), C_null(llptrty)); signed = false; } - StructWrappedNullablePointer { nndiscr, ptrfield, .. } => { - val = struct_wrapped_nullable_bitdiscr(bcx, nndiscr, ptrfield, scrutinee); + StructWrappedNullablePointer { nndiscr, ref discrfield, .. } => { + val = struct_wrapped_nullable_bitdiscr(bcx, nndiscr, discrfield, scrutinee); signed = false; } } @@ -664,12 +726,9 @@ pub fn trans_get_discr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, r: &Repr<'tcx>, } } -fn struct_wrapped_nullable_bitdiscr(bcx: Block, nndiscr: Disr, ptrfield: PointerField, +fn struct_wrapped_nullable_bitdiscr(bcx: Block, nndiscr: Disr, discrfield: &DiscrField, scrutinee: ValueRef) -> ValueRef { - let llptrptr = match ptrfield { - ThinPointer(field) => GEPi(bcx, scrutinee, &[0, field]), - FatPointer(field) => GEPi(bcx, scrutinee, &[0, field, abi::FAT_PTR_ADDR]) - }; + let llptrptr = GEPi(bcx, scrutinee, discrfield[]); let llptr = Load(bcx, llptrptr); let cmp = if nndiscr == 0 { IntEQ } else { IntNE }; ICmp(bcx, cmp, llptr, C_null(val_ty(llptr))) @@ -755,17 +814,10 @@ pub fn trans_set_discr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, r: &Repr<'tcx>, Store(bcx, C_null(llptrty), val) } } - StructWrappedNullablePointer { ref nonnull, nndiscr, ptrfield, .. } => { + StructWrappedNullablePointer { nndiscr, ref discrfield, .. } => { if discr != nndiscr { - let (llptrptr, llptrty) = match ptrfield { - ThinPointer(field) => - (GEPi(bcx, val, &[0, field]), - type_of::type_of(bcx.ccx(), nonnull.fields[field])), - FatPointer(field) => { - let v = GEPi(bcx, val, &[0, field, abi::FAT_PTR_ADDR]); - (v, val_ty(v).element_type()) - } - }; + let llptrptr = GEPi(bcx, val, discrfield[]); + let llptrty = val_ty(llptrptr).element_type(); Store(bcx, C_null(llptrty), llptrptr) } } @@ -982,7 +1034,7 @@ pub fn trans_const<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, r: &Repr<'tcx>, discr false) } else { let vals = nonnull.fields.iter().map(|&ty| { - // Always use null even if it's not the `ptrfield`th + // Always use null even if it's not the `discrfield`th // field; see #8506. C_null(type_of::sizing_type_of(ccx, ty)) }).collect::>(); @@ -1062,9 +1114,8 @@ fn padding(ccx: &CrateContext, size: u64) -> ValueRef { #[inline] fn roundup(x: u64, a: u32) -> u64 { let a = a as u64; ((x + (a - 1)) / a) * a } -/// Get the discriminant of a constant value. (Not currently used.) -pub fn const_get_discrim(ccx: &CrateContext, r: &Repr, val: ValueRef) - -> Disr { +/// Get the discriminant of a constant value. +pub fn const_get_discrim(ccx: &CrateContext, r: &Repr, val: ValueRef) -> Disr { match *r { CEnum(ity, _, _) => { match ity { @@ -1079,25 +1130,8 @@ pub fn const_get_discrim(ccx: &CrateContext, r: &Repr, val: ValueRef) } } Univariant(..) => 0, - RawNullablePointer { nndiscr, .. } => { - if is_null(val) { - /* subtraction as uint is ok because nndiscr is either 0 or 1 */ - (1 - nndiscr) as Disr - } else { - nndiscr - } - } - StructWrappedNullablePointer { nndiscr, ptrfield, .. } => { - let (idx, sub_idx) = match ptrfield { - ThinPointer(field) => (field, None), - FatPointer(field) => (field, Some(abi::FAT_PTR_ADDR)) - }; - if is_null(const_struct_field(ccx, val, idx, sub_idx)) { - /* subtraction as uint is ok because nndiscr is either 0 or 1 */ - (1 - nndiscr) as Disr - } else { - nndiscr - } + RawNullablePointer { .. } | StructWrappedNullablePointer { .. } => { + ccx.sess().bug("const discrim access of non c-like enum") } } } @@ -1111,29 +1145,25 @@ pub fn const_get_field(ccx: &CrateContext, r: &Repr, val: ValueRef, _discr: Disr, ix: uint) -> ValueRef { match *r { CEnum(..) => ccx.sess().bug("element access in C-like enum const"), - Univariant(..) => const_struct_field(ccx, val, ix, None), - General(..) => const_struct_field(ccx, val, ix + 1, None), + Univariant(..) => const_struct_field(ccx, val, ix), + General(..) => const_struct_field(ccx, val, ix + 1), RawNullablePointer { .. } => { assert_eq!(ix, 0); val - } - StructWrappedNullablePointer{ .. } => const_struct_field(ccx, val, ix, None) + }, + StructWrappedNullablePointer{ .. } => const_struct_field(ccx, val, ix) } } /// Extract field of struct-like const, skipping our alignment padding. -fn const_struct_field(ccx: &CrateContext, val: ValueRef, ix: uint, sub_idx: Option) - -> ValueRef { +fn const_struct_field(ccx: &CrateContext, val: ValueRef, ix: uint) -> ValueRef { // Get the ix-th non-undef element of the struct. let mut real_ix = 0; // actual position in the struct let mut ix = ix; // logical index relative to real_ix let mut field; loop { loop { - field = match sub_idx { - Some(si) => const_get_elt(ccx, val, &[real_ix, si as u32]), - None => const_get_elt(ccx, val, &[real_ix]) - }; + field = const_get_elt(ccx, val, &[real_ix]); if !is_undef(field) { break; } diff --git a/src/librustc_trans/trans/debuginfo.rs b/src/librustc_trans/trans/debuginfo.rs index 6c75086fec60b..ca3e42cca8d7d 100644 --- a/src/librustc_trans/trans/debuginfo.rs +++ b/src/librustc_trans/trans/debuginfo.rs @@ -2245,14 +2245,14 @@ impl<'tcx> EnumMemberDescriptionFactory<'tcx> { }, adt::StructWrappedNullablePointer { nonnull: ref struct_def, nndiscr, - ptrfield, ..} => { + ref discrfield, ..} => { // Create a description of the non-null variant let (variant_type_metadata, variant_llvm_type, member_description_factory) = describe_enum_variant(cx, self.enum_type, struct_def, &*(*self.variants)[nndiscr as uint], - OptimizedDiscriminant(ptrfield), + OptimizedDiscriminant, self.containing_scope, self.span); @@ -2268,10 +2268,9 @@ impl<'tcx> EnumMemberDescriptionFactory<'tcx> { // member's name. let null_variant_index = (1 - nndiscr) as uint; let null_variant_name = token::get_name((*self.variants)[null_variant_index].name); - let discrfield = match ptrfield { - adt::ThinPointer(field) => format!("{}", field), - adt::FatPointer(field) => format!("{}", field) - }; + let discrfield = discrfield.iter() + .map(|x| x.to_string()) + .collect::>().connect("$"); let union_member_name = format!("RUST$ENCODED$ENUM${}${}", discrfield, null_variant_name); @@ -2319,7 +2318,7 @@ impl<'tcx> VariantMemberDescriptionFactory<'tcx> { enum EnumDiscriminantInfo { RegularDiscriminant(DIType), - OptimizedDiscriminant(adt::PointerField), + OptimizedDiscriminant, NoDiscriminant } diff --git a/src/test/run-pass/enum-null-pointer-opt.rs b/src/test/run-pass/enum-null-pointer-opt.rs index 3e22c83318393..101e9010ba135 100644 --- a/src/test/run-pass/enum-null-pointer-opt.rs +++ b/src/test/run-pass/enum-null-pointer-opt.rs @@ -10,6 +10,9 @@ use std::mem::size_of; +use std::ptr::NonZero; +use std::rc::Rc; +use std::sync::Arc; trait Trait {} @@ -35,9 +38,33 @@ fn main() { // Pointers - Box assert_eq!(size_of::>(), size_of::>>()); - // The optimization can't apply to raw pointers assert!(size_of::>() != size_of::<*const int>()); assert!(Some(0 as *const int).is_some()); // Can't collapse None to null + struct Foo { + _a: Box + } + struct Bar(Box); + + // Should apply through structs + assert_eq!(size_of::(), size_of::>()); + assert_eq!(size_of::(), size_of::>()); + // and tuples + assert_eq!(size_of::<(u8, Box)>(), size_of::)>>()); + // and fixed-size arrays + assert_eq!(size_of::<[Box, ..1]>(), size_of::, ..1]>>()); + + // Should apply to NonZero + assert_eq!(size_of::>(), size_of::>>()); + assert_eq!(size_of::>(), size_of::>>()); + + // Should apply to types that use NonZero internally + assert_eq!(size_of::>(), size_of::>>()); + assert_eq!(size_of::>(), size_of::>>()); + assert_eq!(size_of::>(), size_of::>>()); + + // Should apply to types that have NonZero transitively + assert_eq!(size_of::(), size_of::>()); + }