Skip to content

Commit

Permalink
Auto merge of #46094 - dtolnay:is_null, r=alexcrichton
Browse files Browse the repository at this point in the history
Remove `T: Sized` on `ptr::is_null()`

Originally from #44932 -- this is purely a revert of the last commit of that PR, which was removing some changes from the previous commits in the PR. So a revert of a revert means this is code written by @cuviper!

@mikeyhew makes a compelling case in rust-lang/rfcs#433 (comment) for why this is the right way to implement `is_null` for trait objects. And the behavior for slices makes sense to me as well.

```diff
  impl<T: ?Sized> *const T {
-     pub fn is_null(self) -> bool where T: Sized;
+     pub fn is_null(self) -> bool;
  }

  impl<T: ?Sized> *mut T {
-     pub fn is_null(self) -> bool where T: Sized;
+     pub fn is_null(self) -> bool;
  }
  • Loading branch information
bors committed Nov 28, 2017
2 parents 71340ca + e0f58c6 commit 73bca2b
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 15 deletions.
3 changes: 1 addition & 2 deletions src/libcore/nonzero.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,7 @@ macro_rules! impl_zeroable_for_pointer_types {
unsafe impl<T: ?Sized> Zeroable for $Ptr {
#[inline]
fn is_zero(&self) -> bool {
// Cast because `is_null` is only available on thin pointers
(*self as *mut u8).is_null()
(*self).is_null()
}
}
)+
Expand Down
34 changes: 21 additions & 13 deletions src/libcore/ptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,11 @@ pub unsafe fn write_volatile<T>(dst: *mut T, src: T) {
impl<T: ?Sized> *const T {
/// Returns `true` if the pointer is null.
///
/// Note that unsized types have many possible null pointers, as only the
/// raw data pointer is considered, not their length, vtable, etc.
/// Therefore, two pointers that are null may still not compare equal to
/// each other.
///
/// # Examples
///
/// Basic usage:
Expand All @@ -485,8 +490,10 @@ impl<T: ?Sized> *const T {
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
pub fn is_null(self) -> bool where T: Sized {
self == null()
pub fn is_null(self) -> bool {
// Compare via a cast to a thin pointer, so fat pointers are only
// considering their "data" part for null-ness.
(self as *const u8) == null()
}

/// Returns `None` if the pointer is null, or else returns a reference to
Expand Down Expand Up @@ -518,9 +525,7 @@ impl<T: ?Sized> *const T {
#[stable(feature = "ptr_as_ref", since = "1.9.0")]
#[inline]
pub unsafe fn as_ref<'a>(self) -> Option<&'a T> {
// Check for null via a cast to a thin pointer, so fat pointers are only
// considering their "data" part for null-ness.
if (self as *const u8).is_null() {
if self.is_null() {
None
} else {
Some(&*self)
Expand Down Expand Up @@ -1107,6 +1112,11 @@ impl<T: ?Sized> *const T {
impl<T: ?Sized> *mut T {
/// Returns `true` if the pointer is null.
///
/// Note that unsized types have many possible null pointers, as only the
/// raw data pointer is considered, not their length, vtable, etc.
/// Therefore, two pointers that are null may still not compare equal to
/// each other.
///
/// # Examples
///
/// Basic usage:
Expand All @@ -1118,8 +1128,10 @@ impl<T: ?Sized> *mut T {
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
pub fn is_null(self) -> bool where T: Sized {
self == null_mut()
pub fn is_null(self) -> bool {
// Compare via a cast to a thin pointer, so fat pointers are only
// considering their "data" part for null-ness.
(self as *mut u8) == null_mut()
}

/// Returns `None` if the pointer is null, or else returns a reference to
Expand Down Expand Up @@ -1151,9 +1163,7 @@ impl<T: ?Sized> *mut T {
#[stable(feature = "ptr_as_ref", since = "1.9.0")]
#[inline]
pub unsafe fn as_ref<'a>(self) -> Option<&'a T> {
// Check for null via a cast to a thin pointer, so fat pointers are only
// considering their "data" part for null-ness.
if (self as *const u8).is_null() {
if self.is_null() {
None
} else {
Some(&*self)
Expand Down Expand Up @@ -1277,9 +1287,7 @@ impl<T: ?Sized> *mut T {
#[stable(feature = "ptr_as_ref", since = "1.9.0")]
#[inline]
pub unsafe fn as_mut<'a>(self) -> Option<&'a mut T> {
// Check for null via a cast to a thin pointer, so fat pointers are only
// considering their "data" part for null-ness.
if (self as *mut u8).is_null() {
if self.is_null() {
None
} else {
Some(&mut *self)
Expand Down
33 changes: 33 additions & 0 deletions src/libcore/tests/ptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,39 @@ fn test_is_null() {

let mq = unsafe { mp.offset(1) };
assert!(!mq.is_null());

// Pointers to unsized types -- slices
let s: &mut [u8] = &mut [1, 2, 3];
let cs: *const [u8] = s;
assert!(!cs.is_null());

let ms: *mut [u8] = s;
assert!(!ms.is_null());

let cz: *const [u8] = &[];
assert!(!cz.is_null());

let mz: *mut [u8] = &mut [];
assert!(!mz.is_null());

let ncs: *const [u8] = null::<[u8; 3]>();
assert!(ncs.is_null());

let nms: *mut [u8] = null_mut::<[u8; 3]>();
assert!(nms.is_null());

// Pointers to unsized types -- trait objects
let ci: *const ToString = &3;
assert!(!ci.is_null());

let mi: *mut ToString = &mut 3;
assert!(!mi.is_null());

let nci: *const ToString = null::<isize>();
assert!(nci.is_null());

let nmi: *mut ToString = null_mut::<isize>();
assert!(nmi.is_null());
}

#[test]
Expand Down

0 comments on commit 73bca2b

Please sign in to comment.