From 21d085a9edebfebbbc2e60c6eccdaa772d1180d4 Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Wed, 25 Oct 2017 18:51:27 +0200 Subject: [PATCH 1/3] core: Add functions core::ptr::dangling/_mut() -> *T Add functions to create a dangling aligned non-null raw pointer. The use case is to fix all the places we use "1 as *mut T" is used, in particular in the slice iterators. The aligned pointer has a value like 0x1, 0x4, 0x8 etc. depending on the alignment of the pointed-to type. It is useful when a non-null pointer is required like in references, slices, boxes, and other places. NOTE: This changes the observable behaviour of slice iterators. Previously, it would always yield 0x1 pointers (as references) for ZST, including types like `[SomeType; 0]`. They now use an aligned non-null pointer. Expose std::ptr::dangling/_mut as new unstable functions with intent on later stabilization, because if this low level trick is needed in collections, the rest of the Rust ecosystem will need them as well. --- src/liballoc/lib.rs | 1 + src/liballoc/vec.rs | 4 ++-- src/libcore/lib.rs | 1 + src/libcore/ptr.rs | 24 ++++++++++++++++++++---- src/libcore/slice/mod.rs | 12 ++++++------ src/librustc/lib.rs | 1 + src/librustc/ty/mod.rs | 3 ++- 7 files changed, 33 insertions(+), 13 deletions(-) diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index 3cc3ea467966b..a1b6e0e4af5d0 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -109,6 +109,7 @@ #![feature(pattern)] #![feature(placement_in_syntax)] #![feature(placement_new_protocol)] +#![feature(ptr_dangling)] #![feature(rustc_attrs)] #![feature(shared)] #![feature(slice_get_slice)] diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs index 5aca199cf40c0..3d6da0acd5cf1 100644 --- a/src/liballoc/vec.rs +++ b/src/liballoc/vec.rs @@ -2344,7 +2344,7 @@ impl Iterator for IntoIter { // Use a non-null pointer value // (self.ptr might be null because of wrapping) - Some(ptr::read(1 as *mut T)) + Some(ptr::read(ptr::dangling())) } else { let old = self.ptr; self.ptr = self.ptr.offset(1); @@ -2384,7 +2384,7 @@ impl DoubleEndedIterator for IntoIter { // Use a non-null pointer value // (self.end might be null because of wrapping) - Some(ptr::read(1 as *mut T)) + Some(ptr::read(ptr::dangling())) } else { self.end = self.end.offset(-1); diff --git a/src/libcore/lib.rs b/src/libcore/lib.rs index 631b9f98589f6..8283c6a94b4da 100644 --- a/src/libcore/lib.rs +++ b/src/libcore/lib.rs @@ -91,6 +91,7 @@ #![feature(unboxed_closures)] #![feature(untagged_unions)] #![feature(unwind_attributes)] +#![feature(const_align_of)] #![feature(const_min_value)] #![feature(const_max_value)] #![feature(const_atomic_bool_new)] diff --git a/src/libcore/ptr.rs b/src/libcore/ptr.rs index 126558e3025d5..0a183817e0fb3 100644 --- a/src/libcore/ptr.rs +++ b/src/libcore/ptr.rs @@ -2083,6 +2083,24 @@ impl PartialEq for *mut T { #[stable(feature = "rust1", since = "1.0.0")] impl Eq for *mut T {} +#[unstable(feature = "ptr_dangling", issue = "45557")] +#[rustc_const_unstable(feature = "const_ptr_dangling")] +/// Returns a const raw pointer that is dangling, but well-aligned and not null. +/// +/// This is used where a non-null pointer is required. +pub const fn dangling() -> *const T { + mem::align_of::() as *const T +} + +#[unstable(feature = "ptr_dangling", issue = "45557")] +#[rustc_const_unstable(feature = "const_ptr_dangling_mut")] +/// Returns a mut raw pointer that is dangling, but well-aligned and not null. +/// +/// This is used where a non-null pointer is required. +pub const fn dangling_mut() -> *mut T { + mem::align_of::() as *mut T +} + /// Compare raw pointers for equality. /// /// This is the same as using the `==` operator, but less generic: @@ -2325,8 +2343,7 @@ impl Unique { /// `Vec::new` does. pub fn empty() -> Self { unsafe { - let ptr = mem::align_of::() as *mut T; - Unique::new_unchecked(ptr) + Unique::new_unchecked(dangling_mut()) } } } @@ -2460,8 +2477,7 @@ impl Shared { /// `Vec::new` does. pub fn empty() -> Self { unsafe { - let ptr = mem::align_of::() as *mut T; - Shared::new_unchecked(ptr) + Shared::new_unchecked(dangling_mut()) } } } diff --git a/src/libcore/slice/mod.rs b/src/libcore/slice/mod.rs index 49c51f4f04fdc..cf6feae47ac18 100644 --- a/src/libcore/slice/mod.rs +++ b/src/libcore/slice/mod.rs @@ -244,7 +244,7 @@ macro_rules! make_ref { let ptr = $ptr; if size_from_ptr(ptr) == 0 { // Use a non-null pointer value - &*(1 as *mut _) + &*ptr::dangling() } else { &*ptr } @@ -257,7 +257,7 @@ macro_rules! make_ref_mut { let ptr = $ptr; if size_from_ptr(ptr) == 0 { // Use a non-null pointer value - &mut *(1 as *mut _) + &mut *(ptr::dangling_mut()) } else { &mut *ptr } @@ -279,7 +279,7 @@ impl SliceExt for [T] { fn iter(&self) -> Iter { unsafe { let p = if mem::size_of::() == 0 { - 1 as *const _ + ptr::dangling() } else { let p = self.as_ptr(); assume(!p.is_null()); @@ -443,7 +443,7 @@ impl SliceExt for [T] { fn iter_mut(&mut self) -> IterMut { unsafe { let p = if mem::size_of::() == 0 { - 1 as *mut _ + ptr::dangling_mut() } else { let p = self.as_mut_ptr(); assume(!p.is_null()); @@ -1259,7 +1259,7 @@ macro_rules! make_slice { let diff = ($end as usize).wrapping_sub(start as usize); if size_from_ptr(start) == 0 { // use a non-null pointer value - unsafe { from_raw_parts(1 as *const _, diff) } + unsafe { from_raw_parts(ptr::dangling(), diff) } } else { let len = diff / size_from_ptr(start); unsafe { from_raw_parts(start, len) } @@ -1273,7 +1273,7 @@ macro_rules! make_mut_slice { let diff = ($end as usize).wrapping_sub(start as usize); if size_from_ptr(start) == 0 { // use a non-null pointer value - unsafe { from_raw_parts_mut(1 as *mut _, diff) } + unsafe { from_raw_parts_mut(ptr::dangling_mut(), diff) } } else { let len = diff / size_from_ptr(start); unsafe { from_raw_parts_mut(start, len) } diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index b59f7480476b8..a6bc80cd9fd68 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -55,6 +55,7 @@ #![feature(match_default_bindings)] #![feature(never_type)] #![feature(nonzero)] +#![feature(ptr_dangling)] #![feature(quote)] #![feature(refcell_replace_swap)] #![feature(rustc_diagnostic_macros)] diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 48ec92a255b4c..18e82a4b1d514 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -46,6 +46,7 @@ use std::rc::Rc; use std::slice; use std::vec::IntoIter; use std::mem; +use std::ptr; use syntax::ast::{self, DUMMY_NODE_ID, Name, Ident, NodeId}; use syntax::attr; use syntax::ext::hygiene::{Mark, SyntaxContext}; @@ -572,7 +573,7 @@ impl<'tcx> serialize::UseSpecializedDecodable for &'tcx Slice> {} impl Slice { pub fn empty<'a>() -> &'a Slice { unsafe { - mem::transmute(slice::from_raw_parts(0x1 as *const T, 0)) + mem::transmute(slice::from_raw_parts(ptr::dangling::(), 0)) } } } From 65a9cb67f0ced682b9ff48f1391d103e688ac389 Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Wed, 25 Oct 2017 18:56:20 +0200 Subject: [PATCH 2/3] std: use ptr::dangling_mut() in thread locals and related code --- src/libstd/io/lazy.rs | 4 ++-- src/libstd/lib.rs | 1 + src/libstd/sys_common/thread_local.rs | 8 ++++---- src/libstd/thread/local.rs | 4 ++-- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/libstd/io/lazy.rs b/src/libstd/io/lazy.rs index 9cef4e3cdf1ca..30a4670794b4d 100644 --- a/src/libstd/io/lazy.rs +++ b/src/libstd/io/lazy.rs @@ -37,7 +37,7 @@ impl Lazy { let ptr = self.ptr.get(); let ret = if ptr.is_null() { Some(self.init()) - } else if ptr as usize == 1 { + } else if ptr == ptr::dangling_mut() { None } else { Some((*ptr).clone()) @@ -55,7 +55,7 @@ impl Lazy { let registered = sys_common::at_exit(move || { self.lock.lock(); let ptr = self.ptr.get(); - self.ptr.set(1 as *mut _); + self.ptr.set(ptr::dangling_mut()); self.lock.unlock(); drop(Box::from_raw(ptr)) }); diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index ccc89ccdcf4c3..06b71d505e353 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -301,6 +301,7 @@ #![feature(placement_in_syntax)] #![feature(placement_new_protocol)] #![feature(prelude_import)] +#![feature(ptr_dangling)] #![feature(rand)] #![feature(raw)] #![feature(repr_align)] diff --git a/src/libstd/sys_common/thread_local.rs b/src/libstd/sys_common/thread_local.rs index 87ffd304e1a33..c06386269229a 100644 --- a/src/libstd/sys_common/thread_local.rs +++ b/src/libstd/sys_common/thread_local.rs @@ -36,7 +36,7 @@ //! ```ignore (cannot-doctest-private-modules) //! let key = Key::new(None); //! assert!(key.get().is_null()); -//! key.set(1 as *mut u8); +//! key.set(std::ptr::dangling_mut::()); //! assert!(!key.get().is_null()); //! //! drop(key); // deallocate this TLS slot. @@ -50,7 +50,7 @@ //! //! unsafe { //! assert!(KEY.get().is_null()); -//! KEY.set(1 as *mut u8); +//! KEY.set(std::ptr::dangling_mut::()); //! } //! ``` @@ -81,7 +81,7 @@ use sys_common::mutex::Mutex; /// /// unsafe { /// assert!(KEY.get().is_null()); -/// KEY.set(1 as *mut u8); +/// KEY.set(std::ptr::dangling_mut::()); /// } /// ``` pub struct StaticKey { @@ -110,7 +110,7 @@ pub struct StaticKey { /// /// let key = Key::new(None); /// assert!(key.get().is_null()); -/// key.set(1 as *mut u8); +/// key.set(std::ptr::dangling_mut::()); /// assert!(!key.get().is_null()); /// /// drop(key); // deallocate this TLS slot. diff --git a/src/libstd/thread/local.rs b/src/libstd/thread/local.rs index fcbca38a98f0b..8d9e164ec6efa 100644 --- a/src/libstd/thread/local.rs +++ b/src/libstd/thread/local.rs @@ -492,7 +492,7 @@ pub mod os { pub unsafe fn get(&'static self) -> Option<&'static UnsafeCell>> { let ptr = self.os.get() as *mut Value; if !ptr.is_null() { - if ptr as usize == 1 { + if ptr == ptr::dangling_mut() { return None } return Some(&(*ptr).value); @@ -520,7 +520,7 @@ pub mod os { // before we return from the destructor ourselves. let ptr = Box::from_raw(ptr as *mut Value); let key = ptr.key; - key.os.set(1 as *mut u8); + key.os.set(ptr::dangling_mut()); drop(ptr); key.os.set(ptr::null_mut()); } From 481db9f8327973a59d4111ed6193577794349bfb Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Wed, 25 Oct 2017 19:22:22 +0200 Subject: [PATCH 3/3] Add test for aligned references from slice iterators --- .../run-pass/zero-sized-by-ref-iterator.rs | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 src/test/run-pass/zero-sized-by-ref-iterator.rs diff --git a/src/test/run-pass/zero-sized-by-ref-iterator.rs b/src/test/run-pass/zero-sized-by-ref-iterator.rs new file mode 100644 index 0000000000000..eef897a3f6640 --- /dev/null +++ b/src/test/run-pass/zero-sized-by-ref-iterator.rs @@ -0,0 +1,41 @@ +// Copyright 2017 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_align, attr_literals, core_intrinsics)] + +use std::mem::align_of; +use std::intrinsics::type_name; + +fn has_aligned_refs<'a, I, T: 'a>(iterable: I) +where + I: Iterator +{ + for elt in iterable { + unsafe { + assert_eq!((elt as *const T as usize) % align_of::(), 0, + "Assertion failed for type {}", type_name::()); + } + } +} + +fn main() { + #[derive(Copy, Clone)] + struct Zst; + + #[derive(Copy, Clone)] + #[repr(align(64))] + struct Aligned; + + has_aligned_refs([Zst; 8].iter()); + has_aligned_refs([[0f64; 0]; 8].iter()); + has_aligned_refs([Aligned; 8].iter()); + has_aligned_refs([Zst; 8].iter_mut().map(|t| &*t)); + has_aligned_refs([[0f64; 0]; 8].iter_mut().map(|t| &*t)); + has_aligned_refs([Aligned; 8].iter_mut().map(|t| &*t)); +}