diff --git a/src/libcore/ptr.rs b/src/libcore/ptr/mod.rs similarity index 87% rename from src/libcore/ptr.rs rename to src/libcore/ptr/mod.rs index 4bb4d3ee46660..80ac67d8eb57c 100644 --- a/src/libcore/ptr.rs +++ b/src/libcore/ptr/mod.rs @@ -1,5 +1,3 @@ -// ignore-tidy-filelength - //! Manually manage memory through raw pointers. //! //! *[See also the pointer primitive types](../../std/primitive.pointer.html).* @@ -65,14 +63,10 @@ #![stable(feature = "rust1", since = "1.0.0")] -use crate::convert::From; use crate::intrinsics; -use crate::ops::{CoerceUnsized, DispatchFromDyn}; use crate::fmt; use crate::hash; -use crate::marker::{PhantomData, Unsize}; use crate::mem::{self, MaybeUninit}; - use crate::cmp::Ordering::{self, Less, Equal, Greater}; #[stable(feature = "rust1", since = "1.0.0")] @@ -84,6 +78,14 @@ pub use crate::intrinsics::copy; #[stable(feature = "rust1", since = "1.0.0")] pub use crate::intrinsics::write_bytes; +mod non_null; +#[stable(feature = "nonnull", since = "1.25.0")] +pub use non_null::NonNull; + +mod unique; +#[unstable(feature = "ptr_internals", issue = "0")] +pub use unique::Unique; + /// Executes the destructor (if any) of the pointed-to value. /// /// This is semantically equivalent to calling [`ptr::read`] and discarding @@ -2742,384 +2744,3 @@ impl PartialOrd for *mut T { #[inline] fn ge(&self, other: &*mut T) -> bool { *self >= *other } } - -/// A wrapper around a raw non-null `*mut T` that indicates that the possessor -/// of this wrapper owns the referent. Useful for building abstractions like -/// `Box`, `Vec`, `String`, and `HashMap`. -/// -/// Unlike `*mut T`, `Unique` behaves "as if" it were an instance of `T`. -/// It implements `Send`/`Sync` if `T` is `Send`/`Sync`. It also implies -/// the kind of strong aliasing guarantees an instance of `T` can expect: -/// the referent of the pointer should not be modified without a unique path to -/// its owning Unique. -/// -/// If you're uncertain of whether it's correct to use `Unique` for your purposes, -/// consider using `NonNull`, which has weaker semantics. -/// -/// Unlike `*mut T`, the pointer must always be non-null, even if the pointer -/// is never dereferenced. This is so that enums may use this forbidden value -/// as a discriminant -- `Option>` has the same size as `Unique`. -/// However the pointer may still dangle if it isn't dereferenced. -/// -/// Unlike `*mut T`, `Unique` is covariant over `T`. This should always be correct -/// for any type which upholds Unique's aliasing requirements. -#[unstable(feature = "ptr_internals", issue = "0", - reason = "use NonNull instead and consider PhantomData \ - (if you also use #[may_dangle]), Send, and/or Sync")] -#[doc(hidden)] -#[repr(transparent)] -#[rustc_layout_scalar_valid_range_start(1)] -pub struct Unique { - pointer: *const T, - // NOTE: this marker has no consequences for variance, but is necessary - // for dropck to understand that we logically own a `T`. - // - // For details, see: - // https://github.com/rust-lang/rfcs/blob/master/text/0769-sound-generic-drop.md#phantom-data - _marker: PhantomData, -} - -#[unstable(feature = "ptr_internals", issue = "0")] -impl fmt::Debug for Unique { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Pointer::fmt(&self.as_ptr(), f) - } -} - -/// `Unique` pointers are `Send` if `T` is `Send` because the data they -/// reference is unaliased. Note that this aliasing invariant is -/// unenforced by the type system; the abstraction using the -/// `Unique` must enforce it. -#[unstable(feature = "ptr_internals", issue = "0")] -unsafe impl Send for Unique { } - -/// `Unique` pointers are `Sync` if `T` is `Sync` because the data they -/// reference is unaliased. Note that this aliasing invariant is -/// unenforced by the type system; the abstraction using the -/// `Unique` must enforce it. -#[unstable(feature = "ptr_internals", issue = "0")] -unsafe impl Sync for Unique { } - -#[unstable(feature = "ptr_internals", issue = "0")] -impl Unique { - /// Creates a new `Unique` that is dangling, but well-aligned. - /// - /// This is useful for initializing types which lazily allocate, like - /// `Vec::new` does. - /// - /// Note that the pointer value may potentially represent a valid pointer to - /// a `T`, which means this must not be used as a "not yet initialized" - /// sentinel value. Types that lazily allocate must track initialization by - /// some other means. - // FIXME: rename to dangling() to match NonNull? - pub const fn empty() -> Self { - unsafe { - Unique::new_unchecked(mem::align_of::() as *mut T) - } - } -} - -#[unstable(feature = "ptr_internals", issue = "0")] -impl Unique { - /// Creates a new `Unique`. - /// - /// # Safety - /// - /// `ptr` must be non-null. - pub const unsafe fn new_unchecked(ptr: *mut T) -> Self { - Unique { pointer: ptr as _, _marker: PhantomData } - } - - /// Creates a new `Unique` if `ptr` is non-null. - pub fn new(ptr: *mut T) -> Option { - if !ptr.is_null() { - Some(unsafe { Unique { pointer: ptr as _, _marker: PhantomData } }) - } else { - None - } - } - - /// Acquires the underlying `*mut` pointer. - pub const fn as_ptr(self) -> *mut T { - self.pointer as *mut T - } - - /// Dereferences the content. - /// - /// The resulting lifetime is bound to self so this behaves "as if" - /// it were actually an instance of T that is getting borrowed. If a longer - /// (unbound) lifetime is needed, use `&*my_ptr.as_ptr()`. - pub unsafe fn as_ref(&self) -> &T { - &*self.as_ptr() - } - - /// Mutably dereferences the content. - /// - /// The resulting lifetime is bound to self so this behaves "as if" - /// it were actually an instance of T that is getting borrowed. If a longer - /// (unbound) lifetime is needed, use `&mut *my_ptr.as_ptr()`. - pub unsafe fn as_mut(&mut self) -> &mut T { - &mut *self.as_ptr() - } -} - -#[unstable(feature = "ptr_internals", issue = "0")] -impl Clone for Unique { - fn clone(&self) -> Self { - *self - } -} - -#[unstable(feature = "ptr_internals", issue = "0")] -impl Copy for Unique { } - -#[unstable(feature = "ptr_internals", issue = "0")] -impl CoerceUnsized> for Unique where T: Unsize { } - -#[unstable(feature = "ptr_internals", issue = "0")] -impl DispatchFromDyn> for Unique where T: Unsize { } - -#[unstable(feature = "ptr_internals", issue = "0")] -impl fmt::Pointer for Unique { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Pointer::fmt(&self.as_ptr(), f) - } -} - -#[unstable(feature = "ptr_internals", issue = "0")] -impl From<&mut T> for Unique { - fn from(reference: &mut T) -> Self { - unsafe { Unique { pointer: reference as *mut T, _marker: PhantomData } } - } -} - -#[unstable(feature = "ptr_internals", issue = "0")] -impl From<&T> for Unique { - fn from(reference: &T) -> Self { - unsafe { Unique { pointer: reference as *const T, _marker: PhantomData } } - } -} - -#[unstable(feature = "ptr_internals", issue = "0")] -impl<'a, T: ?Sized> From> for Unique { - fn from(p: NonNull) -> Self { - unsafe { Unique { pointer: p.pointer, _marker: PhantomData } } - } -} - -/// `*mut T` but non-zero and covariant. -/// -/// This is often the correct thing to use when building data structures using -/// raw pointers, but is ultimately more dangerous to use because of its additional -/// properties. If you're not sure if you should use `NonNull`, just use `*mut T`! -/// -/// Unlike `*mut T`, the pointer must always be non-null, even if the pointer -/// is never dereferenced. This is so that enums may use this forbidden value -/// as a discriminant -- `Option>` has the same size as `*mut T`. -/// However the pointer may still dangle if it isn't dereferenced. -/// -/// Unlike `*mut T`, `NonNull` is covariant over `T`. If this is incorrect -/// for your use case, you should include some [`PhantomData`] in your type to -/// provide invariance, such as `PhantomData>` or `PhantomData<&'a mut T>`. -/// Usually this won't be necessary; covariance is correct for most safe abstractions, -/// such as `Box`, `Rc`, `Arc`, `Vec`, and `LinkedList`. This is the case because they -/// provide a public API that follows the normal shared XOR mutable rules of Rust. -/// -/// Notice that `NonNull` has a `From` instance for `&T`. However, this does -/// not change the fact that mutating through a (pointer derived from a) shared -/// reference is undefined behavior unless the mutation happens inside an -/// [`UnsafeCell`]. The same goes for creating a mutable reference from a shared -/// reference. When using this `From` instance without an `UnsafeCell`, -/// it is your responsibility to ensure that `as_mut` is never called, and `as_ptr` -/// is never used for mutation. -/// -/// [`PhantomData`]: ../marker/struct.PhantomData.html -/// [`UnsafeCell`]: ../cell/struct.UnsafeCell.html -#[stable(feature = "nonnull", since = "1.25.0")] -#[repr(transparent)] -#[rustc_layout_scalar_valid_range_start(1)] -#[cfg_attr(not(stage0), rustc_nonnull_optimization_guaranteed)] -pub struct NonNull { - pointer: *const T, -} - -/// `NonNull` pointers are not `Send` because the data they reference may be aliased. -// N.B., this impl is unnecessary, but should provide better error messages. -#[stable(feature = "nonnull", since = "1.25.0")] -impl !Send for NonNull { } - -/// `NonNull` pointers are not `Sync` because the data they reference may be aliased. -// N.B., this impl is unnecessary, but should provide better error messages. -#[stable(feature = "nonnull", since = "1.25.0")] -impl !Sync for NonNull { } - -impl NonNull { - /// Creates a new `NonNull` that is dangling, but well-aligned. - /// - /// This is useful for initializing types which lazily allocate, like - /// `Vec::new` does. - /// - /// Note that the pointer value may potentially represent a valid pointer to - /// a `T`, which means this must not be used as a "not yet initialized" - /// sentinel value. Types that lazily allocate must track initialization by - /// some other means. - #[stable(feature = "nonnull", since = "1.25.0")] - #[inline] - pub const fn dangling() -> Self { - unsafe { - let ptr = mem::align_of::() as *mut T; - NonNull::new_unchecked(ptr) - } - } -} - -impl NonNull { - /// Creates a new `NonNull`. - /// - /// # Safety - /// - /// `ptr` must be non-null. - #[stable(feature = "nonnull", since = "1.25.0")] - #[inline] - pub const unsafe fn new_unchecked(ptr: *mut T) -> Self { - NonNull { pointer: ptr as _ } - } - - /// Creates a new `NonNull` if `ptr` is non-null. - #[stable(feature = "nonnull", since = "1.25.0")] - #[inline] - pub fn new(ptr: *mut T) -> Option { - if !ptr.is_null() { - Some(unsafe { Self::new_unchecked(ptr) }) - } else { - None - } - } - - /// Acquires the underlying `*mut` pointer. - #[stable(feature = "nonnull", since = "1.25.0")] - #[inline] - pub const fn as_ptr(self) -> *mut T { - self.pointer as *mut T - } - - /// Dereferences the content. - /// - /// The resulting lifetime is bound to self so this behaves "as if" - /// it were actually an instance of T that is getting borrowed. If a longer - /// (unbound) lifetime is needed, use `&*my_ptr.as_ptr()`. - #[stable(feature = "nonnull", since = "1.25.0")] - #[inline] - pub unsafe fn as_ref(&self) -> &T { - &*self.as_ptr() - } - - /// Mutably dereferences the content. - /// - /// The resulting lifetime is bound to self so this behaves "as if" - /// it were actually an instance of T that is getting borrowed. If a longer - /// (unbound) lifetime is needed, use `&mut *my_ptr.as_ptr()`. - #[stable(feature = "nonnull", since = "1.25.0")] - #[inline] - pub unsafe fn as_mut(&mut self) -> &mut T { - &mut *self.as_ptr() - } - - /// Cast to a pointer of another type - #[stable(feature = "nonnull_cast", since = "1.27.0")] - #[inline] - pub const fn cast(self) -> NonNull { - unsafe { - NonNull::new_unchecked(self.as_ptr() as *mut U) - } - } -} - -#[stable(feature = "nonnull", since = "1.25.0")] -impl Clone for NonNull { - fn clone(&self) -> Self { - *self - } -} - -#[stable(feature = "nonnull", since = "1.25.0")] -impl Copy for NonNull { } - -#[unstable(feature = "coerce_unsized", issue = "27732")] -impl CoerceUnsized> for NonNull where T: Unsize { } - -#[unstable(feature = "dispatch_from_dyn", issue = "0")] -impl DispatchFromDyn> for NonNull where T: Unsize { } - -#[stable(feature = "nonnull", since = "1.25.0")] -impl fmt::Debug for NonNull { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Pointer::fmt(&self.as_ptr(), f) - } -} - -#[stable(feature = "nonnull", since = "1.25.0")] -impl fmt::Pointer for NonNull { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Pointer::fmt(&self.as_ptr(), f) - } -} - -#[stable(feature = "nonnull", since = "1.25.0")] -impl Eq for NonNull {} - -#[stable(feature = "nonnull", since = "1.25.0")] -impl PartialEq for NonNull { - #[inline] - fn eq(&self, other: &Self) -> bool { - self.as_ptr() == other.as_ptr() - } -} - -#[stable(feature = "nonnull", since = "1.25.0")] -impl Ord for NonNull { - #[inline] - fn cmp(&self, other: &Self) -> Ordering { - self.as_ptr().cmp(&other.as_ptr()) - } -} - -#[stable(feature = "nonnull", since = "1.25.0")] -impl PartialOrd for NonNull { - #[inline] - fn partial_cmp(&self, other: &Self) -> Option { - self.as_ptr().partial_cmp(&other.as_ptr()) - } -} - -#[stable(feature = "nonnull", since = "1.25.0")] -impl hash::Hash for NonNull { - #[inline] - fn hash(&self, state: &mut H) { - self.as_ptr().hash(state) - } -} - -#[unstable(feature = "ptr_internals", issue = "0")] -impl From> for NonNull { - #[inline] - fn from(unique: Unique) -> Self { - unsafe { NonNull { pointer: unique.pointer } } - } -} - -#[stable(feature = "nonnull", since = "1.25.0")] -impl From<&mut T> for NonNull { - #[inline] - fn from(reference: &mut T) -> Self { - unsafe { NonNull { pointer: reference as *mut T } } - } -} - -#[stable(feature = "nonnull", since = "1.25.0")] -impl From<&T> for NonNull { - #[inline] - fn from(reference: &T) -> Self { - unsafe { NonNull { pointer: reference as *const T } } - } -} diff --git a/src/libcore/ptr/non_null.rs b/src/libcore/ptr/non_null.rs new file mode 100644 index 0000000000000..0a6985e334cd0 --- /dev/null +++ b/src/libcore/ptr/non_null.rs @@ -0,0 +1,226 @@ +use crate::convert::From; +use crate::ops::{CoerceUnsized, DispatchFromDyn}; +use crate::fmt; +use crate::hash; +use crate::marker::Unsize; +use crate::mem; +use crate::ptr::Unique; +use crate::cmp::Ordering; + +/// `*mut T` but non-zero and covariant. +/// +/// This is often the correct thing to use when building data structures using +/// raw pointers, but is ultimately more dangerous to use because of its additional +/// properties. If you're not sure if you should use `NonNull`, just use `*mut T`! +/// +/// Unlike `*mut T`, the pointer must always be non-null, even if the pointer +/// is never dereferenced. This is so that enums may use this forbidden value +/// as a discriminant -- `Option>` has the same size as `*mut T`. +/// However the pointer may still dangle if it isn't dereferenced. +/// +/// Unlike `*mut T`, `NonNull` is covariant over `T`. If this is incorrect +/// for your use case, you should include some [`PhantomData`] in your type to +/// provide invariance, such as `PhantomData>` or `PhantomData<&'a mut T>`. +/// Usually this won't be necessary; covariance is correct for most safe abstractions, +/// such as `Box`, `Rc`, `Arc`, `Vec`, and `LinkedList`. This is the case because they +/// provide a public API that follows the normal shared XOR mutable rules of Rust. +/// +/// Notice that `NonNull` has a `From` instance for `&T`. However, this does +/// not change the fact that mutating through a (pointer derived from a) shared +/// reference is undefined behavior unless the mutation happens inside an +/// [`UnsafeCell`]. The same goes for creating a mutable reference from a shared +/// reference. When using this `From` instance without an `UnsafeCell`, +/// it is your responsibility to ensure that `as_mut` is never called, and `as_ptr` +/// is never used for mutation. +/// +/// [`PhantomData`]: ../marker/struct.PhantomData.html +/// [`UnsafeCell`]: ../cell/struct.UnsafeCell.html +#[stable(feature = "nonnull", since = "1.25.0")] +#[repr(transparent)] +#[rustc_layout_scalar_valid_range_start(1)] +#[cfg_attr(not(stage0), rustc_nonnull_optimization_guaranteed)] +pub struct NonNull { + pointer: *const T, +} + +/// `NonNull` pointers are not `Send` because the data they reference may be aliased. +// N.B., this impl is unnecessary, but should provide better error messages. +#[stable(feature = "nonnull", since = "1.25.0")] +impl !Send for NonNull { } + +/// `NonNull` pointers are not `Sync` because the data they reference may be aliased. +// N.B., this impl is unnecessary, but should provide better error messages. +#[stable(feature = "nonnull", since = "1.25.0")] +impl !Sync for NonNull { } + +impl NonNull { + /// Creates a new `NonNull` that is dangling, but well-aligned. + /// + /// This is useful for initializing types which lazily allocate, like + /// `Vec::new` does. + /// + /// Note that the pointer value may potentially represent a valid pointer to + /// a `T`, which means this must not be used as a "not yet initialized" + /// sentinel value. Types that lazily allocate must track initialization by + /// some other means. + #[stable(feature = "nonnull", since = "1.25.0")] + #[inline] + pub const fn dangling() -> Self { + unsafe { + let ptr = mem::align_of::() as *mut T; + NonNull::new_unchecked(ptr) + } + } +} + +impl NonNull { + /// Creates a new `NonNull`. + /// + /// # Safety + /// + /// `ptr` must be non-null. + #[stable(feature = "nonnull", since = "1.25.0")] + #[inline] + pub const unsafe fn new_unchecked(ptr: *mut T) -> Self { + NonNull { pointer: ptr as _ } + } + + /// Creates a new `NonNull` if `ptr` is non-null. + #[stable(feature = "nonnull", since = "1.25.0")] + #[inline] + pub fn new(ptr: *mut T) -> Option { + if !ptr.is_null() { + Some(unsafe { Self::new_unchecked(ptr) }) + } else { + None + } + } + + /// Acquires the underlying `*mut` pointer. + #[stable(feature = "nonnull", since = "1.25.0")] + #[inline] + pub const fn as_ptr(self) -> *mut T { + self.pointer as *mut T + } + + /// Dereferences the content. + /// + /// The resulting lifetime is bound to self so this behaves "as if" + /// it were actually an instance of T that is getting borrowed. If a longer + /// (unbound) lifetime is needed, use `&*my_ptr.as_ptr()`. + #[stable(feature = "nonnull", since = "1.25.0")] + #[inline] + pub unsafe fn as_ref(&self) -> &T { + &*self.as_ptr() + } + + /// Mutably dereferences the content. + /// + /// The resulting lifetime is bound to self so this behaves "as if" + /// it were actually an instance of T that is getting borrowed. If a longer + /// (unbound) lifetime is needed, use `&mut *my_ptr.as_ptr()`. + #[stable(feature = "nonnull", since = "1.25.0")] + #[inline] + pub unsafe fn as_mut(&mut self) -> &mut T { + &mut *self.as_ptr() + } + + /// Cast to a pointer of another type + #[stable(feature = "nonnull_cast", since = "1.27.0")] + #[inline] + pub const fn cast(self) -> NonNull { + unsafe { + NonNull::new_unchecked(self.as_ptr() as *mut U) + } + } +} + +#[stable(feature = "nonnull", since = "1.25.0")] +impl Clone for NonNull { + #[inline] + fn clone(&self) -> Self { + *self + } +} + +#[stable(feature = "nonnull", since = "1.25.0")] +impl Copy for NonNull { } + +#[unstable(feature = "coerce_unsized", issue = "27732")] +impl CoerceUnsized> for NonNull where T: Unsize { } + +#[unstable(feature = "dispatch_from_dyn", issue = "0")] +impl DispatchFromDyn> for NonNull where T: Unsize { } + +#[stable(feature = "nonnull", since = "1.25.0")] +impl fmt::Debug for NonNull { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Pointer::fmt(&self.as_ptr(), f) + } +} + +#[stable(feature = "nonnull", since = "1.25.0")] +impl fmt::Pointer for NonNull { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Pointer::fmt(&self.as_ptr(), f) + } +} + +#[stable(feature = "nonnull", since = "1.25.0")] +impl Eq for NonNull {} + +#[stable(feature = "nonnull", since = "1.25.0")] +impl PartialEq for NonNull { + #[inline] + fn eq(&self, other: &Self) -> bool { + self.as_ptr() == other.as_ptr() + } +} + +#[stable(feature = "nonnull", since = "1.25.0")] +impl Ord for NonNull { + #[inline] + fn cmp(&self, other: &Self) -> Ordering { + self.as_ptr().cmp(&other.as_ptr()) + } +} + +#[stable(feature = "nonnull", since = "1.25.0")] +impl PartialOrd for NonNull { + #[inline] + fn partial_cmp(&self, other: &Self) -> Option { + self.as_ptr().partial_cmp(&other.as_ptr()) + } +} + +#[stable(feature = "nonnull", since = "1.25.0")] +impl hash::Hash for NonNull { + #[inline] + fn hash(&self, state: &mut H) { + self.as_ptr().hash(state) + } +} + +#[unstable(feature = "ptr_internals", issue = "0")] +impl From> for NonNull { + #[inline] + fn from(unique: Unique) -> Self { + unsafe { NonNull::new_unchecked(unique.as_ptr()) } + } +} + +#[stable(feature = "nonnull", since = "1.25.0")] +impl From<&mut T> for NonNull { + #[inline] + fn from(reference: &mut T) -> Self { + unsafe { NonNull { pointer: reference as *mut T } } + } +} + +#[stable(feature = "nonnull", since = "1.25.0")] +impl From<&T> for NonNull { + #[inline] + fn from(reference: &T) -> Self { + unsafe { NonNull { pointer: reference as *const T } } + } +} diff --git a/src/libcore/ptr/unique.rs b/src/libcore/ptr/unique.rs new file mode 100644 index 0000000000000..5911518919e6d --- /dev/null +++ b/src/libcore/ptr/unique.rs @@ -0,0 +1,180 @@ +use crate::convert::From; +use crate::ops::{CoerceUnsized, DispatchFromDyn}; +use crate::fmt; +use crate::marker::{PhantomData, Unsize}; +use crate::mem; +use crate::ptr::NonNull; + +/// A wrapper around a raw non-null `*mut T` that indicates that the possessor +/// of this wrapper owns the referent. Useful for building abstractions like +/// `Box`, `Vec`, `String`, and `HashMap`. +/// +/// Unlike `*mut T`, `Unique` behaves "as if" it were an instance of `T`. +/// It implements `Send`/`Sync` if `T` is `Send`/`Sync`. It also implies +/// the kind of strong aliasing guarantees an instance of `T` can expect: +/// the referent of the pointer should not be modified without a unique path to +/// its owning Unique. +/// +/// If you're uncertain of whether it's correct to use `Unique` for your purposes, +/// consider using `NonNull`, which has weaker semantics. +/// +/// Unlike `*mut T`, the pointer must always be non-null, even if the pointer +/// is never dereferenced. This is so that enums may use this forbidden value +/// as a discriminant -- `Option>` has the same size as `Unique`. +/// However the pointer may still dangle if it isn't dereferenced. +/// +/// Unlike `*mut T`, `Unique` is covariant over `T`. This should always be correct +/// for any type which upholds Unique's aliasing requirements. +#[unstable(feature = "ptr_internals", issue = "0", + reason = "use NonNull instead and consider PhantomData \ + (if you also use #[may_dangle]), Send, and/or Sync")] +#[doc(hidden)] +#[repr(transparent)] +#[rustc_layout_scalar_valid_range_start(1)] +pub struct Unique { + pointer: *const T, + // NOTE: this marker has no consequences for variance, but is necessary + // for dropck to understand that we logically own a `T`. + // + // For details, see: + // https://github.com/rust-lang/rfcs/blob/master/text/0769-sound-generic-drop.md#phantom-data + _marker: PhantomData, +} + +/// `Unique` pointers are `Send` if `T` is `Send` because the data they +/// reference is unaliased. Note that this aliasing invariant is +/// unenforced by the type system; the abstraction using the +/// `Unique` must enforce it. +#[unstable(feature = "ptr_internals", issue = "0")] +unsafe impl Send for Unique { } + +/// `Unique` pointers are `Sync` if `T` is `Sync` because the data they +/// reference is unaliased. Note that this aliasing invariant is +/// unenforced by the type system; the abstraction using the +/// `Unique` must enforce it. +#[unstable(feature = "ptr_internals", issue = "0")] +unsafe impl Sync for Unique { } + +#[unstable(feature = "ptr_internals", issue = "0")] +impl Unique { + /// Creates a new `Unique` that is dangling, but well-aligned. + /// + /// This is useful for initializing types which lazily allocate, like + /// `Vec::new` does. + /// + /// Note that the pointer value may potentially represent a valid pointer to + /// a `T`, which means this must not be used as a "not yet initialized" + /// sentinel value. Types that lazily allocate must track initialization by + /// some other means. + // FIXME: rename to dangling() to match NonNull? + #[inline] + pub const fn empty() -> Self { + unsafe { + Unique::new_unchecked(mem::align_of::() as *mut T) + } + } +} + +#[unstable(feature = "ptr_internals", issue = "0")] +impl Unique { + /// Creates a new `Unique`. + /// + /// # Safety + /// + /// `ptr` must be non-null. + #[inline] + pub const unsafe fn new_unchecked(ptr: *mut T) -> Self { + Unique { pointer: ptr as _, _marker: PhantomData } + } + + /// Creates a new `Unique` if `ptr` is non-null. + #[inline] + pub fn new(ptr: *mut T) -> Option { + if !ptr.is_null() { + Some(unsafe { Unique { pointer: ptr as _, _marker: PhantomData } }) + } else { + None + } + } + + /// Acquires the underlying `*mut` pointer. + #[inline] + pub const fn as_ptr(self) -> *mut T { + self.pointer as *mut T + } + + /// Dereferences the content. + /// + /// The resulting lifetime is bound to self so this behaves "as if" + /// it were actually an instance of T that is getting borrowed. If a longer + /// (unbound) lifetime is needed, use `&*my_ptr.as_ptr()`. + #[inline] + pub unsafe fn as_ref(&self) -> &T { + &*self.as_ptr() + } + + /// Mutably dereferences the content. + /// + /// The resulting lifetime is bound to self so this behaves "as if" + /// it were actually an instance of T that is getting borrowed. If a longer + /// (unbound) lifetime is needed, use `&mut *my_ptr.as_ptr()`. + #[inline] + pub unsafe fn as_mut(&mut self) -> &mut T { + &mut *self.as_ptr() + } +} + +#[unstable(feature = "ptr_internals", issue = "0")] +impl Clone for Unique { + #[inline] + fn clone(&self) -> Self { + *self + } +} + +#[unstable(feature = "ptr_internals", issue = "0")] +impl Copy for Unique { } + +#[unstable(feature = "ptr_internals", issue = "0")] +impl CoerceUnsized> for Unique where T: Unsize { } + +#[unstable(feature = "ptr_internals", issue = "0")] +impl DispatchFromDyn> for Unique where T: Unsize { } + +#[unstable(feature = "ptr_internals", issue = "0")] +impl fmt::Debug for Unique { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Pointer::fmt(&self.as_ptr(), f) + } +} + +#[unstable(feature = "ptr_internals", issue = "0")] +impl fmt::Pointer for Unique { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Pointer::fmt(&self.as_ptr(), f) + } +} + +#[unstable(feature = "ptr_internals", issue = "0")] +impl From<&mut T> for Unique { + #[inline] + fn from(reference: &mut T) -> Self { + unsafe { Unique { pointer: reference as *mut T, _marker: PhantomData } } + } +} + +#[unstable(feature = "ptr_internals", issue = "0")] +impl From<&T> for Unique { + #[inline] + fn from(reference: &T) -> Self { + unsafe { Unique { pointer: reference as *const T, _marker: PhantomData } } + } +} + +#[unstable(feature = "ptr_internals", issue = "0")] +impl<'a, T: ?Sized> From> for Unique { + #[inline] + fn from(p: NonNull) -> Self { + unsafe { Unique::new_unchecked(p.as_ptr()) } + } +} diff --git a/src/librustc/mir/interpret/allocation.rs b/src/librustc/mir/interpret/allocation.rs index 0e2da4c577205..9549a5af5af5c 100644 --- a/src/librustc/mir/interpret/allocation.rs +++ b/src/librustc/mir/interpret/allocation.rs @@ -2,7 +2,6 @@ use super::{ Pointer, EvalResult, AllocId, ScalarMaybeUndef, write_target_uint, read_target_uint, Scalar, - truncate, }; use crate::ty::layout::{Size, Align}; @@ -407,18 +406,9 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { ScalarMaybeUndef::Undef => return self.mark_definedness(ptr, type_size, false), }; - let bytes = match val { - Scalar::Ptr(val) => { - assert_eq!(type_size, cx.data_layout().pointer_size); - val.offset.bytes() as u128 - } - - Scalar::Bits { bits, size } => { - assert_eq!(size as u64, type_size.bytes()); - debug_assert_eq!(truncate(bits, Size::from_bytes(size.into())), bits, - "Unexpected value of size {} when writing to memory", size); - bits - }, + let bytes = match val.to_bits_or_ptr(type_size, cx) { + Err(val) => val.offset.bytes() as u128, + Ok(data) => data, }; let endian = cx.data_layout().endian; diff --git a/src/librustc/mir/interpret/mod.rs b/src/librustc/mir/interpret/mod.rs index 595ea8bd34687..07d22cf991c6b 100644 --- a/src/librustc/mir/interpret/mod.rs +++ b/src/librustc/mir/interpret/mod.rs @@ -349,6 +349,7 @@ impl<'tcx> AllocMap<'tcx> { /// illegal and will likely ICE. /// This function exists to allow const eval to detect the difference between evaluation- /// local dangling pointers and allocations in constants/statics. + #[inline] pub fn get(&self, id: AllocId) -> Option> { self.id_to_kind.get(&id).cloned() } @@ -397,6 +398,7 @@ impl<'tcx> AllocMap<'tcx> { // Methods to access integers in the target endianness //////////////////////////////////////////////////////////////////////////////// +#[inline] pub fn write_target_uint( endianness: layout::Endian, mut target: &mut [u8], @@ -409,6 +411,7 @@ pub fn write_target_uint( } } +#[inline] pub fn read_target_uint(endianness: layout::Endian, mut source: &[u8]) -> Result { match endianness { layout::Endian::Little => source.read_uint128::(source.len()), @@ -420,8 +423,15 @@ pub fn read_target_uint(endianness: layout::Endian, mut source: &[u8]) -> Result // Methods to facilitate working with signed integers stored in a u128 //////////////////////////////////////////////////////////////////////////////// +/// Truncate `value` to `size` bits and then sign-extend it to 128 bits +/// (i.e., if it is negative, fill with 1's on the left). +#[inline] pub fn sign_extend(value: u128, size: Size) -> u128 { let size = size.bits(); + if size == 0 { + // Truncated until nothing is left. + return 0; + } // sign extend let shift = 128 - size; // shift the unsigned value to the left @@ -429,8 +439,14 @@ pub fn sign_extend(value: u128, size: Size) -> u128 { (((value << shift) as i128) >> shift) as u128 } +/// Truncate `value` to `size` bits. +#[inline] pub fn truncate(value: u128, size: Size) -> u128 { let size = size.bits(); + if size == 0 { + // Truncated until nothing is left. + return 0; + } let shift = 128 - size; // truncate (shift left to drop out leftover values, shift right to fill with zeroes) (value << shift) >> shift diff --git a/src/librustc/mir/interpret/pointer.rs b/src/librustc/mir/interpret/pointer.rs index 9422abc4e6f71..9e71399d4fdf7 100644 --- a/src/librustc/mir/interpret/pointer.rs +++ b/src/librustc/mir/interpret/pointer.rs @@ -20,30 +20,20 @@ pub trait PointerArithmetic: layout::HasDataLayout { self.data_layout().pointer_size } - //// Trunace the given value to the pointer size; also return whether there was an overflow + /// Helper function: truncate given value-"overflowed flag" pair to pointer size and + /// update "overflowed flag" if there was an overflow. + /// This should be called by all the other methods before returning! #[inline] - fn truncate_to_ptr(&self, val: u128) -> (u64, bool) { + fn truncate_to_ptr(&self, (val, over): (u64, bool)) -> (u64, bool) { + let val = val as u128; let max_ptr_plus_1 = 1u128 << self.pointer_size().bits(); - ((val % max_ptr_plus_1) as u64, val >= max_ptr_plus_1) - } - - #[inline] - fn offset<'tcx>(&self, val: u64, i: u64) -> EvalResult<'tcx, u64> { - let (res, over) = self.overflowing_offset(val, i); - if over { err!(Overflow(mir::BinOp::Add)) } else { Ok(res) } + ((val % max_ptr_plus_1) as u64, over || val >= max_ptr_plus_1) } #[inline] fn overflowing_offset(&self, val: u64, i: u64) -> (u64, bool) { - let (res, over1) = val.overflowing_add(i); - let (res, over2) = self.truncate_to_ptr(u128::from(res)); - (res, over1 || over2) - } - - #[inline] - fn signed_offset<'tcx>(&self, val: u64, i: i64) -> EvalResult<'tcx, u64> { - let (res, over) = self.overflowing_signed_offset(val, i128::from(i)); - if over { err!(Overflow(mir::BinOp::Add)) } else { Ok(res) } + let res = val.overflowing_add(i); + self.truncate_to_ptr(res) } // Overflow checking only works properly on the range from -u64 to +u64. @@ -51,14 +41,27 @@ pub trait PointerArithmetic: layout::HasDataLayout { fn overflowing_signed_offset(&self, val: u64, i: i128) -> (u64, bool) { // FIXME: is it possible to over/underflow here? if i < 0 { - // trickery to ensure that i64::min_value() works fine - // this formula only works for true negative values, it panics for zero! + // Trickery to ensure that i64::min_value() works fine: compute n = -i. + // This formula only works for true negative values, it overflows for zero! let n = u64::max_value() - (i as u64) + 1; - val.overflowing_sub(n) + let res = val.overflowing_sub(n); + self.truncate_to_ptr(res) } else { self.overflowing_offset(val, i as u64) } } + + #[inline] + fn offset<'tcx>(&self, val: u64, i: u64) -> EvalResult<'tcx, u64> { + let (res, over) = self.overflowing_offset(val, i); + if over { err!(Overflow(mir::BinOp::Add)) } else { Ok(res) } + } + + #[inline] + fn signed_offset<'tcx>(&self, val: u64, i: i64) -> EvalResult<'tcx, u64> { + let (res, over) = self.overflowing_signed_offset(val, i128::from(i)); + if over { err!(Overflow(mir::BinOp::Add)) } else { Ok(res) } + } } impl PointerArithmetic for T {} diff --git a/src/librustc/mir/interpret/value.rs b/src/librustc/mir/interpret/value.rs index 72545f23f8e2b..21792b847db77 100644 --- a/src/librustc/mir/interpret/value.rs +++ b/src/librustc/mir/interpret/value.rs @@ -87,11 +87,11 @@ impl<'tcx> ConstValue<'tcx> { RustcEncodable, RustcDecodable, Hash, HashStable)] pub enum Scalar { /// The raw bytes of a simple value. - Bits { - /// The first `size` bytes are the value. + Raw { + /// The first `size` bytes of `data` are the value. /// Do not try to read less or more bytes than that. The remaining bytes must be 0. + data: u128, size: u8, - bits: u128, }, /// A pointer into an `Allocation`. An `Allocation` in the `memory` module has a list of @@ -108,16 +108,14 @@ impl fmt::Debug for Scalar { match self { Scalar::Ptr(ptr) => write!(f, "{:?}", ptr), - &Scalar::Bits { bits, size } => { + &Scalar::Raw { data, size } => { + Scalar::check_data(data, size); if size == 0 { - assert_eq!(bits, 0, "ZST value must be 0"); write!(f, "") } else { - assert_eq!(truncate(bits, Size::from_bytes(size as u64)), bits, - "Scalar value {:#x} exceeds size of {} bytes", bits, size); // Format as hex number wide enough to fit any value of the given `size`. - // So bits=20, size=1 will be "0x14", but with size=4 it'll be "0x00000014". - write!(f, "0x{:>0width$x}", bits, width=(size*2) as usize) + // So data=20, size=1 will be "0x14", but with size=4 it'll be "0x00000014". + write!(f, "0x{:>0width$x}", data, width=(size*2) as usize) } } } @@ -128,17 +126,23 @@ impl fmt::Display for Scalar { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Scalar::Ptr(_) => write!(f, "a pointer"), - Scalar::Bits { bits, .. } => write!(f, "{}", bits), + Scalar::Raw { data, .. } => write!(f, "{}", data), } } } impl<'tcx> Scalar<()> { + #[inline(always)] + fn check_data(data: u128, size: u8) { + debug_assert_eq!(truncate(data, Size::from_bytes(size as u64)), data, + "Scalar value {:#x} exceeds size of {} bytes", data, size); + } + #[inline] pub fn with_tag(self, new_tag: Tag) -> Scalar { match self { Scalar::Ptr(ptr) => Scalar::Ptr(ptr.with_tag(new_tag)), - Scalar::Bits { bits, size } => Scalar::Bits { bits, size }, + Scalar::Raw { data, size } => Scalar::Raw { data, size }, } } @@ -155,31 +159,31 @@ impl<'tcx, Tag> Scalar { pub fn erase_tag(self) -> Scalar { match self { Scalar::Ptr(ptr) => Scalar::Ptr(ptr.erase_tag()), - Scalar::Bits { bits, size } => Scalar::Bits { bits, size }, + Scalar::Raw { data, size } => Scalar::Raw { data, size }, } } #[inline] pub fn ptr_null(cx: &impl HasDataLayout) -> Self { - Scalar::Bits { - bits: 0, + Scalar::Raw { + data: 0, size: cx.data_layout().pointer_size.bytes() as u8, } } #[inline] pub fn zst() -> Self { - Scalar::Bits { bits: 0, size: 0 } + Scalar::Raw { data: 0, size: 0 } } #[inline] pub fn ptr_offset(self, i: Size, cx: &impl HasDataLayout) -> EvalResult<'tcx, Self> { let dl = cx.data_layout(); match self { - Scalar::Bits { bits, size } => { + Scalar::Raw { data, size } => { assert_eq!(size as u64, dl.pointer_size.bytes()); - Ok(Scalar::Bits { - bits: dl.offset(bits as u64, i.bytes())? as u128, + Ok(Scalar::Raw { + data: dl.offset(data as u64, i.bytes())? as u128, size, }) } @@ -191,10 +195,10 @@ impl<'tcx, Tag> Scalar { pub fn ptr_wrapping_offset(self, i: Size, cx: &impl HasDataLayout) -> Self { let dl = cx.data_layout(); match self { - Scalar::Bits { bits, size } => { + Scalar::Raw { data, size } => { assert_eq!(size as u64, dl.pointer_size.bytes()); - Scalar::Bits { - bits: dl.overflowing_offset(bits as u64, i.bytes()).0 as u128, + Scalar::Raw { + data: dl.overflowing_offset(data as u64, i.bytes()).0 as u128, size, } } @@ -206,10 +210,10 @@ impl<'tcx, Tag> Scalar { pub fn ptr_signed_offset(self, i: i64, cx: &impl HasDataLayout) -> EvalResult<'tcx, Self> { let dl = cx.data_layout(); match self { - Scalar::Bits { bits, size } => { + Scalar::Raw { data, size } => { assert_eq!(size as u64, dl.pointer_size().bytes()); - Ok(Scalar::Bits { - bits: dl.signed_offset(bits as u64, i)? as u128, + Ok(Scalar::Raw { + data: dl.signed_offset(data as u64, i)? as u128, size, }) } @@ -221,10 +225,10 @@ impl<'tcx, Tag> Scalar { pub fn ptr_wrapping_signed_offset(self, i: i64, cx: &impl HasDataLayout) -> Self { let dl = cx.data_layout(); match self { - Scalar::Bits { bits, size } => { + Scalar::Raw { data, size } => { assert_eq!(size as u64, dl.pointer_size.bytes()); - Scalar::Bits { - bits: dl.overflowing_signed_offset(bits as u64, i128::from(i)).0 as u128, + Scalar::Raw { + data: dl.overflowing_signed_offset(data as u64, i128::from(i)).0 as u128, size, } } @@ -232,14 +236,14 @@ impl<'tcx, Tag> Scalar { } } - /// Returns this pointers offset from the allocation base, or from NULL (for + /// Returns this pointer's offset from the allocation base, or from NULL (for /// integer pointers). #[inline] pub fn get_ptr_offset(self, cx: &impl HasDataLayout) -> Size { match self { - Scalar::Bits { bits, size } => { + Scalar::Raw { data, size } => { assert_eq!(size as u64, cx.pointer_size().bytes()); - Size::from_bytes(bits as u64) + Size::from_bytes(data as u64) } Scalar::Ptr(ptr) => ptr.offset, } @@ -248,9 +252,9 @@ impl<'tcx, Tag> Scalar { #[inline] pub fn is_null_ptr(self, cx: &impl HasDataLayout) -> bool { match self { - Scalar::Bits { bits, size } => { + Scalar::Raw { data, size } => { assert_eq!(size as u64, cx.data_layout().pointer_size.bytes()); - bits == 0 + data == 0 }, Scalar::Ptr(_) => false, } @@ -258,20 +262,22 @@ impl<'tcx, Tag> Scalar { #[inline] pub fn from_bool(b: bool) -> Self { - Scalar::Bits { bits: b as u128, size: 1 } + Scalar::Raw { data: b as u128, size: 1 } } #[inline] pub fn from_char(c: char) -> Self { - Scalar::Bits { bits: c as u128, size: 4 } + Scalar::Raw { data: c as u128, size: 4 } } #[inline] pub fn from_uint(i: impl Into, size: Size) -> Self { let i = i.into(); - debug_assert_eq!(truncate(i, size), i, - "Unsigned value {} does not fit in {} bits", i, size.bits()); - Scalar::Bits { bits: i, size: size.bytes() as u8 } + assert_eq!( + truncate(i, size), i, + "Unsigned value {:#x} does not fit in {} bits", i, size.bits() + ); + Scalar::Raw { data: i, size: size.bytes() as u8 } } #[inline] @@ -279,28 +285,51 @@ impl<'tcx, Tag> Scalar { let i = i.into(); // `into` performed sign extension, we have to truncate let truncated = truncate(i as u128, size); - debug_assert_eq!(sign_extend(truncated, size) as i128, i, - "Signed value {} does not fit in {} bits", i, size.bits()); - Scalar::Bits { bits: truncated, size: size.bytes() as u8 } + assert_eq!( + sign_extend(truncated, size) as i128, i, + "Signed value {:#x} does not fit in {} bits", i, size.bits() + ); + Scalar::Raw { data: truncated, size: size.bytes() as u8 } } #[inline] pub fn from_f32(f: f32) -> Self { - Scalar::Bits { bits: f.to_bits() as u128, size: 4 } + Scalar::Raw { data: f.to_bits() as u128, size: 4 } } #[inline] pub fn from_f64(f: f64) -> Self { - Scalar::Bits { bits: f.to_bits() as u128, size: 8 } + Scalar::Raw { data: f.to_bits() as u128, size: 8 } + } + + #[inline] + pub fn to_bits_or_ptr( + self, + target_size: Size, + cx: &impl HasDataLayout, + ) -> Result> { + match self { + Scalar::Raw { data, size } => { + assert_eq!(target_size.bytes(), size as u64); + assert_ne!(size, 0, "you should never look at the bits of a ZST"); + Scalar::check_data(data, size); + Ok(data) + } + Scalar::Ptr(ptr) => { + assert_eq!(target_size, cx.data_layout().pointer_size); + Err(ptr) + } + } } #[inline] pub fn to_bits(self, target_size: Size) -> EvalResult<'tcx, u128> { match self { - Scalar::Bits { bits, size } => { + Scalar::Raw { data, size } => { assert_eq!(target_size.bytes(), size as u64); - assert_ne!(size, 0, "to_bits cannot be used with zsts"); - Ok(bits) + assert_ne!(size, 0, "you should never look at the bits of a ZST"); + Scalar::check_data(data, size); + Ok(data) } Scalar::Ptr(_) => err!(ReadPointerAsBytes), } @@ -309,8 +338,8 @@ impl<'tcx, Tag> Scalar { #[inline] pub fn to_ptr(self) -> EvalResult<'tcx, Pointer> { match self { - Scalar::Bits { bits: 0, .. } => err!(InvalidNullPointerUsage), - Scalar::Bits { .. } => err!(ReadBytesAsPointer), + Scalar::Raw { data: 0, .. } => err!(InvalidNullPointerUsage), + Scalar::Raw { .. } => err!(ReadBytesAsPointer), Scalar::Ptr(p) => Ok(p), } } @@ -318,7 +347,7 @@ impl<'tcx, Tag> Scalar { #[inline] pub fn is_bits(self) -> bool { match self { - Scalar::Bits { .. } => true, + Scalar::Raw { .. } => true, _ => false, } } @@ -333,8 +362,8 @@ impl<'tcx, Tag> Scalar { pub fn to_bool(self) -> EvalResult<'tcx, bool> { match self { - Scalar::Bits { bits: 0, size: 1 } => Ok(false), - Scalar::Bits { bits: 1, size: 1 } => Ok(true), + Scalar::Raw { data: 0, size: 1 } => Ok(false), + Scalar::Raw { data: 1, size: 1 } => Ok(true), _ => err!(InvalidBool), } } @@ -350,27 +379,23 @@ impl<'tcx, Tag> Scalar { pub fn to_u8(self) -> EvalResult<'static, u8> { let sz = Size::from_bits(8); let b = self.to_bits(sz)?; - assert_eq!(b as u8 as u128, b); Ok(b as u8) } pub fn to_u32(self) -> EvalResult<'static, u32> { let sz = Size::from_bits(32); let b = self.to_bits(sz)?; - assert_eq!(b as u32 as u128, b); Ok(b as u32) } pub fn to_u64(self) -> EvalResult<'static, u64> { let sz = Size::from_bits(64); let b = self.to_bits(sz)?; - assert_eq!(b as u64 as u128, b); Ok(b as u64) } pub fn to_usize(self, cx: &impl HasDataLayout) -> EvalResult<'static, u64> { let b = self.to_bits(cx.data_layout().pointer_size)?; - assert_eq!(b as u64 as u128, b); Ok(b as u64) } @@ -378,7 +403,6 @@ impl<'tcx, Tag> Scalar { let sz = Size::from_bits(8); let b = self.to_bits(sz)?; let b = sign_extend(b, sz) as i128; - assert_eq!(b as i8 as i128, b); Ok(b as i8) } @@ -386,7 +410,6 @@ impl<'tcx, Tag> Scalar { let sz = Size::from_bits(32); let b = self.to_bits(sz)?; let b = sign_extend(b, sz) as i128; - assert_eq!(b as i32 as i128, b); Ok(b as i32) } @@ -394,14 +417,13 @@ impl<'tcx, Tag> Scalar { let sz = Size::from_bits(64); let b = self.to_bits(sz)?; let b = sign_extend(b, sz) as i128; - assert_eq!(b as i64 as i128, b); Ok(b as i64) } pub fn to_isize(self, cx: &impl HasDataLayout) -> EvalResult<'static, i64> { - let b = self.to_bits(cx.data_layout().pointer_size)?; - let b = sign_extend(b, cx.data_layout().pointer_size) as i128; - assert_eq!(b as i64 as i128, b); + let sz = cx.data_layout().pointer_size; + let b = self.to_bits(sz)?; + let b = sign_extend(b, sz) as i128; Ok(b as i64) } diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index 84aff8101a09d..646f81398f46e 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -1669,10 +1669,7 @@ impl<'tcx> TerminatorKind<'tcx> { .map(|&u| { tcx.mk_const(ty::Const { val: ConstValue::Scalar( - Scalar::Bits { - bits: u, - size: size.bytes() as u8, - }.into(), + Scalar::from_uint(u, size).into(), ), ty: switch_ty, }).to_string().into() @@ -2040,7 +2037,7 @@ impl<'tcx> Place<'tcx> { /// a single deref of a local. // // FIXME: can we safely swap the semantics of `fn base_local` below in here instead? - pub fn local(&self) -> Option { + pub fn local_or_deref_local(&self) -> Option { match self { Place::Base(PlaceBase::Local(local)) | Place::Projection(box Projection { diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index 44b6e03655773..f4ee39d69883c 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -117,16 +117,16 @@ impl LinkerPluginLto { } #[derive(Clone, PartialEq, Hash)] -pub enum PgoGenerate { +pub enum SwitchWithOptPath { Enabled(Option), Disabled, } -impl PgoGenerate { +impl SwitchWithOptPath { pub fn enabled(&self) -> bool { match *self { - PgoGenerate::Enabled(_) => true, - PgoGenerate::Disabled => false, + SwitchWithOptPath::Enabled(_) => true, + SwitchWithOptPath::Disabled => false, } } } @@ -834,7 +834,7 @@ macro_rules! options { pub const parse_linker_plugin_lto: Option<&str> = Some("either a boolean (`yes`, `no`, `on`, `off`, etc), \ or the path to the linker plugin"); - pub const parse_pgo_generate: Option<&str> = + pub const parse_switch_with_opt_path: Option<&str> = Some("an optional path to the profiling data output directory"); pub const parse_merge_functions: Option<&str> = Some("one of: `disabled`, `trampolines`, or `aliases`"); @@ -842,7 +842,7 @@ macro_rules! options { #[allow(dead_code)] mod $mod_set { - use super::{$struct_name, Passes, Sanitizer, LtoCli, LinkerPluginLto, PgoGenerate}; + use super::{$struct_name, Passes, Sanitizer, LtoCli, LinkerPluginLto, SwitchWithOptPath}; use rustc_target::spec::{LinkerFlavor, MergeFunctions, PanicStrategy, RelroLevel}; use std::path::PathBuf; use std::str::FromStr; @@ -1097,10 +1097,10 @@ macro_rules! options { true } - fn parse_pgo_generate(slot: &mut PgoGenerate, v: Option<&str>) -> bool { + fn parse_switch_with_opt_path(slot: &mut SwitchWithOptPath, v: Option<&str>) -> bool { *slot = match v { - None => PgoGenerate::Enabled(None), - Some(path) => PgoGenerate::Enabled(Some(PathBuf::from(path))), + None => SwitchWithOptPath::Enabled(None), + Some(path) => SwitchWithOptPath::Enabled(Some(PathBuf::from(path))), }; true } @@ -1379,7 +1379,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "extra arguments to prepend to the linker invocation (space separated)"), profile: bool = (false, parse_bool, [TRACKED], "insert profiling code"), - pgo_gen: PgoGenerate = (PgoGenerate::Disabled, parse_pgo_generate, [TRACKED], + pgo_gen: SwitchWithOptPath = (SwitchWithOptPath::Disabled, + parse_switch_with_opt_path, [TRACKED], "Generate PGO profile data, to a given file, or to the default location if it's empty."), pgo_use: Option = (None, parse_opt_pathbuf, [TRACKED], "Use PGO profile data from the given profile file."), @@ -1447,7 +1448,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "don't interleave execution of lints; allows benchmarking individual lints"), crate_attr: Vec = (Vec::new(), parse_string_push, [TRACKED], "inject the given attribute in the crate"), - self_profile: bool = (false, parse_bool, [UNTRACKED], + self_profile: SwitchWithOptPath = (SwitchWithOptPath::Disabled, + parse_switch_with_opt_path, [UNTRACKED], "run the self profiler and output the raw event data"), self_profile_events: Option> = (None, parse_opt_comma_list, [UNTRACKED], "specifies which kinds of events get recorded by the self profiler"), @@ -2558,7 +2560,7 @@ mod dep_tracking { use std::path::PathBuf; use std::collections::hash_map::DefaultHasher; use super::{CrateType, DebugInfo, ErrorOutputType, OptLevel, OutputTypes, - Passes, Sanitizer, LtoCli, LinkerPluginLto, PgoGenerate}; + Passes, Sanitizer, LtoCli, LinkerPluginLto, SwitchWithOptPath}; use syntax::feature_gate::UnstableFeatures; use rustc_target::spec::{MergeFunctions, PanicStrategy, RelroLevel, TargetTriple}; use syntax::edition::Edition; @@ -2626,7 +2628,7 @@ mod dep_tracking { impl_dep_tracking_hash_via_hash!(TargetTriple); impl_dep_tracking_hash_via_hash!(Edition); impl_dep_tracking_hash_via_hash!(LinkerPluginLto); - impl_dep_tracking_hash_via_hash!(PgoGenerate); + impl_dep_tracking_hash_via_hash!(SwitchWithOptPath); impl_dep_tracking_hash_for_sortable_vec_of!(String); impl_dep_tracking_hash_for_sortable_vec_of!(PathBuf); @@ -2694,7 +2696,7 @@ mod tests { build_session_options_and_crate_config, to_crate_config }; - use crate::session::config::{LtoCli, LinkerPluginLto, PgoGenerate, ExternEntry}; + use crate::session::config::{LtoCli, LinkerPluginLto, SwitchWithOptPath, ExternEntry}; use crate::session::build_session; use crate::session::search_paths::SearchPath; use std::collections::{BTreeMap, BTreeSet}; @@ -3207,7 +3209,7 @@ mod tests { assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); opts = reference.clone(); - opts.debugging_opts.pgo_gen = PgoGenerate::Enabled(None); + opts.debugging_opts.pgo_gen = SwitchWithOptPath::Enabled(None); assert_ne!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); opts = reference.clone(); diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index 3d8092f6e0070..974a5bb70e653 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -9,7 +9,7 @@ use crate::lint; use crate::lint::builtin::BuiltinLintDiagnostics; use crate::middle::allocator::AllocatorKind; use crate::middle::dependency_format; -use crate::session::config::OutputType; +use crate::session::config::{OutputType, SwitchWithOptPath}; use crate::session::search_paths::{PathKind, SearchPath}; use crate::util::nodemap::{FxHashMap, FxHashSet}; use crate::util::common::{duration_to_secs_str, ErrorReported}; @@ -1137,8 +1137,18 @@ fn build_session_( driver_lint_caps: FxHashMap, ) -> Session { let self_profiler = - if sopts.debugging_opts.self_profile { - let profiler = SelfProfiler::new(&sopts.debugging_opts.self_profile_events); + if let SwitchWithOptPath::Enabled(ref d) = sopts.debugging_opts.self_profile { + let directory = if let Some(ref directory) = d { + directory + } else { + std::path::Path::new(".") + }; + + let profiler = SelfProfiler::new( + directory, + sopts.crate_name.as_ref().map(|s| &s[..]), + &sopts.debugging_opts.self_profile_events + ); match profiler { Ok(profiler) => { crate::ty::query::QueryName::register_with_profiler(&profiler); diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index a56fe7d7003a1..92de3e28d1903 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -1001,7 +1001,7 @@ impl<'tcx> CommonConsts<'tcx> { CommonConsts { err: mk_const(ty::Const { - val: ConstValue::Scalar(Scalar::Bits { bits: 0, size: 0 }), + val: ConstValue::Scalar(Scalar::zst()), ty: types.err, }), } diff --git a/src/librustc/ty/print/pretty.rs b/src/librustc/ty/print/pretty.rs index 4b4dd5d88d93a..a246d9652f2f0 100644 --- a/src/librustc/ty/print/pretty.rs +++ b/src/librustc/ty/print/pretty.rs @@ -845,22 +845,22 @@ pub trait PrettyPrinter<'gcx: 'tcx, 'tcx>: p!(write("{}", name)); return Ok(self); } - if let ConstValue::Scalar(Scalar::Bits { bits, .. }) = ct.val { + if let ConstValue::Scalar(Scalar::Raw { data, .. }) = ct.val { match ct.ty.sty { ty::Bool => { - p!(write("{}", if bits == 0 { "false" } else { "true" })); + p!(write("{}", if data == 0 { "false" } else { "true" })); return Ok(self); }, ty::Float(ast::FloatTy::F32) => { - p!(write("{}f32", Single::from_bits(bits))); + p!(write("{}f32", Single::from_bits(data))); return Ok(self); }, ty::Float(ast::FloatTy::F64) => { - p!(write("{}f64", Double::from_bits(bits))); + p!(write("{}f64", Double::from_bits(data))); return Ok(self); }, ty::Uint(ui) => { - p!(write("{}{}", bits, ui)); + p!(write("{}{}", data, ui)); return Ok(self); }, ty::Int(i) =>{ @@ -868,11 +868,11 @@ pub trait PrettyPrinter<'gcx: 'tcx, 'tcx>: let size = self.tcx().layout_of(ty::ParamEnv::empty().and(ty)) .unwrap() .size; - p!(write("{}{}", sign_extend(bits, size) as i128, i)); + p!(write("{}{}", sign_extend(data, size) as i128, i)); return Ok(self); }, ty::Char => { - p!(write("{:?}", ::std::char::from_u32(bits as u32).unwrap())); + p!(write("{:?}", ::std::char::from_u32(data as u32).unwrap())); return Ok(self); } _ => {}, diff --git a/src/librustc/ty/relate.rs b/src/librustc/ty/relate.rs index 2049341327495..75d227d8067c3 100644 --- a/src/librustc/ty/relate.rs +++ b/src/librustc/ty/relate.rs @@ -613,7 +613,7 @@ where (ConstValue::Placeholder(p1), ConstValue::Placeholder(p2)) if p1 == p2 => { Ok(a) } - (ConstValue::Scalar(Scalar::Bits { .. }), _) if a == b => { + (ConstValue::Scalar(Scalar::Raw { .. }), _) if a == b => { Ok(a) } (ConstValue::ByRef(..), _) => { diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index 4711429502f3f..0a673dd380b52 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -3,7 +3,7 @@ use crate::hir; use crate::hir::def_id::DefId; use crate::infer::canonical::Canonical; -use crate::mir::interpret::{ConstValue, truncate}; +use crate::mir::interpret::ConstValue; use crate::middle::region; use polonius_engine::Atom; use rustc_data_structures::indexed_vec::Idx; @@ -2232,14 +2232,12 @@ impl<'tcx> Const<'tcx> { let size = tcx.layout_of(ty).unwrap_or_else(|e| { panic!("could not compute layout for {:?}: {:?}", ty, e) }).size; - let truncated = truncate(bits, size); - assert_eq!(truncated, bits, "from_bits called with untruncated value"); - Self::from_scalar(tcx, Scalar::Bits { bits, size: size.bytes() as u8 }, ty.value) + Self::from_scalar(tcx, Scalar::from_uint(bits, size), ty.value) } #[inline] pub fn zero_sized(tcx: TyCtxt<'_, '_, 'tcx>, ty: Ty<'tcx>) -> &'tcx Self { - Self::from_scalar(tcx, Scalar::Bits { bits: 0, size: 0 }, ty) + Self::from_scalar(tcx, Scalar::zst(), ty) } #[inline] diff --git a/src/librustc/util/profiling.rs b/src/librustc/util/profiling.rs index 585970e64df8d..8624856a4f55c 100644 --- a/src/librustc/util/profiling.rs +++ b/src/librustc/util/profiling.rs @@ -1,6 +1,8 @@ use std::borrow::Cow; use std::error::Error; +use std::fs; use std::mem::{self, Discriminant}; +use std::path::Path; use std::process; use std::thread::ThreadId; use std::u32; @@ -71,10 +73,17 @@ pub struct SelfProfiler { } impl SelfProfiler { - pub fn new(event_filters: &Option>) -> Result> { - let filename = format!("pid-{}.rustc_profile", process::id()); - let path = std::path::Path::new(&filename); - let profiler = Profiler::new(path)?; + pub fn new( + output_directory: &Path, + crate_name: Option<&str>, + event_filters: &Option> + ) -> Result> { + fs::create_dir_all(output_directory)?; + + let crate_name = crate_name.unwrap_or("unknown-crate"); + let filename = format!("{}-{}.rustc_profile", crate_name, process::id()); + let path = output_directory.join(&filename); + let profiler = Profiler::new(&path)?; let query_event_kind = profiler.alloc_string("Query"); let generic_activity_event_kind = profiler.alloc_string("GenericActivity"); diff --git a/src/librustc_codegen_llvm/back/write.rs b/src/librustc_codegen_llvm/back/write.rs index 1eee9ab8c0b67..d8a9f681639a0 100644 --- a/src/librustc_codegen_llvm/back/write.rs +++ b/src/librustc_codegen_llvm/back/write.rs @@ -13,7 +13,7 @@ use crate::LlvmCodegenBackend; use rustc::hir::def_id::LOCAL_CRATE; use rustc_codegen_ssa::back::write::{CodegenContext, ModuleConfig, run_assembler}; use rustc_codegen_ssa::traits::*; -use rustc::session::config::{self, OutputType, Passes, Lto, PgoGenerate}; +use rustc::session::config::{self, OutputType, Passes, Lto, SwitchWithOptPath}; use rustc::session::Session; use rustc::ty::TyCtxt; use rustc_codegen_ssa::{RLIB_BYTECODE_EXTENSION, ModuleCodegen, CompiledModule}; @@ -707,7 +707,7 @@ pub unsafe fn with_llvm_pmb(llmod: &llvm::Module, let inline_threshold = config.inline_threshold; let pgo_gen_path = match config.pgo_gen { - PgoGenerate::Enabled(ref opt_dir_path) => { + SwitchWithOptPath::Enabled(ref opt_dir_path) => { let path = if let Some(dir_path) = opt_dir_path { dir_path.join("default_%m.profraw") } else { @@ -716,7 +716,7 @@ pub unsafe fn with_llvm_pmb(llmod: &llvm::Module, Some(CString::new(format!("{}", path.display())).unwrap()) } - PgoGenerate::Disabled => { + SwitchWithOptPath::Disabled => { None } }; diff --git a/src/librustc_codegen_llvm/common.rs b/src/librustc_codegen_llvm/common.rs index b9fd9629e6ff1..c713362440d97 100644 --- a/src/librustc_codegen_llvm/common.rs +++ b/src/librustc_codegen_llvm/common.rs @@ -294,13 +294,13 @@ impl ConstMethods<'tcx> for CodegenCx<'ll, 'tcx> { ) -> &'ll Value { let bitsize = if layout.is_bool() { 1 } else { layout.value.size(self).bits() }; match cv { - Scalar::Bits { size: 0, .. } => { + Scalar::Raw { size: 0, .. } => { assert_eq!(0, layout.value.size(self).bytes()); self.const_undef(self.type_ix(0)) }, - Scalar::Bits { bits, size } => { + Scalar::Raw { data, size } => { assert_eq!(size as u64, layout.value.size(self).bytes()); - let llval = self.const_uint_big(self.type_ix(bitsize), bits); + let llval = self.const_uint_big(self.type_ix(bitsize), data); if layout.value == layout::Pointer { unsafe { llvm::LLVMConstIntToPtr(llval, llty) } } else { diff --git a/src/librustc_codegen_ssa/back/write.rs b/src/librustc_codegen_ssa/back/write.rs index 74c41969268e9..5abff2d8ec350 100644 --- a/src/librustc_codegen_ssa/back/write.rs +++ b/src/librustc_codegen_ssa/back/write.rs @@ -13,7 +13,7 @@ use rustc::dep_graph::{WorkProduct, WorkProductId, WorkProductFileKind}; use rustc::dep_graph::cgu_reuse_tracker::CguReuseTracker; use rustc::middle::cstore::EncodedMetadata; use rustc::session::config::{self, OutputFilenames, OutputType, Passes, Lto, - Sanitizer, PgoGenerate}; + Sanitizer, SwitchWithOptPath}; use rustc::session::Session; use rustc::util::nodemap::FxHashMap; use rustc::hir::def_id::{CrateNum, LOCAL_CRATE}; @@ -56,7 +56,7 @@ pub struct ModuleConfig { /// Some(level) to optimize binary size, or None to not affect program size. pub opt_size: Option, - pub pgo_gen: PgoGenerate, + pub pgo_gen: SwitchWithOptPath, pub pgo_use: Option, // Flags indicating which outputs to produce. @@ -94,7 +94,7 @@ impl ModuleConfig { opt_level: None, opt_size: None, - pgo_gen: PgoGenerate::Disabled, + pgo_gen: SwitchWithOptPath::Disabled, pgo_use: None, emit_no_opt_bc: false, diff --git a/src/librustc_codegen_utils/symbol_names.rs b/src/librustc_codegen_utils/symbol_names.rs index 864071629078a..bf81b7f0da525 100644 --- a/src/librustc_codegen_utils/symbol_names.rs +++ b/src/librustc_codegen_utils/symbol_names.rs @@ -443,7 +443,7 @@ impl Printer<'tcx, 'tcx> for SymbolPrinter<'_, 'tcx> { ct: &'tcx ty::Const<'tcx>, ) -> Result { // only print integers - if let ConstValue::Scalar(Scalar::Bits { .. }) = ct.val { + if let ConstValue::Scalar(Scalar::Raw { .. }) = ct.val { if ct.ty.is_integral() { return self.pretty_print_const(ct); } diff --git a/src/librustc_mir/borrow_check/conflict_errors.rs b/src/librustc_mir/borrow_check/conflict_errors.rs index 8022d1f0c7315..f464e58e36b15 100644 --- a/src/librustc_mir/borrow_check/conflict_errors.rs +++ b/src/librustc_mir/borrow_check/conflict_errors.rs @@ -1616,7 +1616,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { ); // Find the local from the operand. - let assigned_from_local = match assigned_from.local() { + let assigned_from_local = match assigned_from.local_or_deref_local() { Some(local) => local, None => continue, }; @@ -1672,7 +1672,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { ); // Find the local from the rvalue. - let assigned_from_local = match assigned_from.local() { + let assigned_from_local = match assigned_from.local_or_deref_local() { Some(local) => local, None => continue, }; @@ -1735,7 +1735,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { assigned_from, ); - if let Some(assigned_from_local) = assigned_from.local() { + if let Some(assigned_from_local) = assigned_from.local_or_deref_local() { debug!( "annotate_argument_and_return_for_borrow: assigned_from_local={:?}", assigned_from_local, diff --git a/src/librustc_mir/borrow_check/error_reporting.rs b/src/librustc_mir/borrow_check/error_reporting.rs index ec0359794e7be..5a22c81a5d057 100644 --- a/src/librustc_mir/borrow_check/error_reporting.rs +++ b/src/librustc_mir/borrow_check/error_reporting.rs @@ -37,15 +37,15 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { diag: &mut DiagnosticBuilder<'_>, ) { debug!("add_moved_or_invoked_closure_note: location={:?} place={:?}", location, place); - let mut target = place.local(); + let mut target = place.local_or_deref_local(); for stmt in &self.mir[location.block].statements[location.statement_index..] { debug!("add_moved_or_invoked_closure_note: stmt={:?} target={:?}", stmt, target); if let StatementKind::Assign(into, box Rvalue::Use(from)) = &stmt.kind { debug!("add_fnonce_closure_note: into={:?} from={:?}", into, from); match from { Operand::Copy(ref place) | - Operand::Move(ref place) if target == place.local() => - target = into.local(), + Operand::Move(ref place) if target == place.local_or_deref_local() => + target = into.local_or_deref_local(), _ => {}, } } @@ -69,8 +69,8 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { if self.infcx.tcx.parent(id) == self.infcx.tcx.lang_items().fn_once_trait() { let closure = match args.first() { Some(Operand::Copy(ref place)) | - Some(Operand::Move(ref place)) if target == place.local() => - place.local().unwrap(), + Some(Operand::Move(ref place)) if target == place.local_or_deref_local() => + place.local_or_deref_local().unwrap(), _ => return, }; diff --git a/src/librustc_mir/const_eval.rs b/src/librustc_mir/const_eval.rs index c43bc41ba685e..7b3d0bceb6896 100644 --- a/src/librustc_mir/const_eval.rs +++ b/src/librustc_mir/const_eval.rs @@ -115,7 +115,7 @@ fn op_to_const<'tcx>( ecx.tcx.alloc_map.lock().unwrap_memory(ptr.alloc_id), ptr.offset.bytes(), ), - Scalar::Bits { .. } => ( + Scalar::Raw { .. } => ( ecx.tcx.intern_const_alloc(Allocation::from_byte_aligned_bytes(b"", ())), 0, ), diff --git a/src/librustc_mir/dataflow/impls/storage_liveness.rs b/src/librustc_mir/dataflow/impls/storage_liveness.rs index 3bf11c57379c2..5dde9812d605c 100644 --- a/src/librustc_mir/dataflow/impls/storage_liveness.rs +++ b/src/librustc_mir/dataflow/impls/storage_liveness.rs @@ -46,8 +46,10 @@ impl<'a, 'tcx> BitDenotation<'tcx> for MaybeStorageLive<'a, 'tcx> { sets: &mut BlockSets<'_, Local>, loc: Location) { match &self.mir[loc.block].terminator().kind { - TerminatorKind::Drop { location, .. } => if let Some(l) = location.local() { - sets.kill(l); + TerminatorKind::Drop { location, .. } => { + if let Some(l) = location.local_or_deref_local() { + sets.kill(l); + } } _ => (), } diff --git a/src/librustc_mir/hair/constant.rs b/src/librustc_mir/hair/constant.rs index d2c86d36238e1..69df36348a69e 100644 --- a/src/librustc_mir/hair/constant.rs +++ b/src/librustc_mir/hair/constant.rs @@ -1,5 +1,5 @@ use syntax::ast; -use rustc::ty::{self, Ty, TyCtxt, ParamEnv}; +use rustc::ty::{self, Ty, TyCtxt, ParamEnv, layout::Size}; use syntax_pos::symbol::Symbol; use rustc::mir::interpret::{ConstValue, Scalar}; @@ -23,10 +23,7 @@ crate fn lit_to_const<'a, 'gcx, 'tcx>( trace!("trunc {} with size {} and shift {}", n, width.bits(), 128 - width.bits()); let result = truncate(n, width); trace!("trunc result: {}", result); - Ok(ConstValue::Scalar(Scalar::Bits { - bits: result, - size: width.bytes() as u8, - })) + Ok(ConstValue::Scalar(Scalar::from_uint(result, width))) }; use rustc::mir::interpret::*; @@ -50,10 +47,7 @@ crate fn lit_to_const<'a, 'gcx, 'tcx>( let id = tcx.allocate_bytes(data); ConstValue::Scalar(Scalar::Ptr(id.into())) }, - LitKind::Byte(n) => ConstValue::Scalar(Scalar::Bits { - bits: n as u128, - size: 1, - }), + LitKind::Byte(n) => ConstValue::Scalar(Scalar::from_uint(n, Size::from_bytes(1))), LitKind::Int(n, _) if neg => { let n = n as i128; let n = n.overflowing_neg().0; @@ -84,7 +78,7 @@ fn parse_float<'tcx>( let num = num.as_str(); use rustc_apfloat::ieee::{Single, Double}; use rustc_apfloat::Float; - let (bits, size) = match fty { + let (data, size) = match fty { ast::FloatTy::F32 => { num.parse::().map_err(|_| ())?; let mut f = num.parse::().unwrap_or_else(|e| { @@ -107,5 +101,5 @@ fn parse_float<'tcx>( } }; - Ok(ConstValue::Scalar(Scalar::Bits { bits, size })) + Ok(ConstValue::Scalar(Scalar::from_uint(data, Size::from_bytes(size)))) } diff --git a/src/librustc_mir/interpret/cast.rs b/src/librustc_mir/interpret/cast.rs index 2512525b4bb7e..76b11ac2fe646 100644 --- a/src/librustc_mir/interpret/cast.rs +++ b/src/librustc_mir/interpret/cast.rs @@ -6,7 +6,7 @@ use syntax::symbol::sym; use rustc_apfloat::ieee::{Single, Double}; use rustc::mir::interpret::{ - Scalar, EvalResult, Pointer, PointerArithmetic, InterpError, truncate + Scalar, EvalResult, Pointer, PointerArithmetic, InterpError, }; use rustc::mir::CastKind; use rustc_apfloat::Float; @@ -135,29 +135,13 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tcx, M> use rustc::ty::TyKind::*; trace!("Casting {:?}: {:?} to {:?}", val, src_layout.ty, dest_layout.ty); - match val { - Scalar::Ptr(ptr) => self.cast_from_ptr(ptr, dest_layout.ty), - Scalar::Bits { bits, size } => { - debug_assert_eq!(size as u64, src_layout.size.bytes()); - debug_assert_eq!(truncate(bits, Size::from_bytes(size.into())), bits, - "Unexpected value of size {} before casting", size); - - let res = match src_layout.ty.sty { - Float(fty) => self.cast_from_float(bits, fty, dest_layout.ty)?, - _ => self.cast_from_int(bits, src_layout, dest_layout)?, - }; - - // Sanity check - match res { - Scalar::Ptr(_) => bug!("Fabricated a ptr value from an int...?"), - Scalar::Bits { bits, size } => { - debug_assert_eq!(size as u64, dest_layout.size.bytes()); - debug_assert_eq!(truncate(bits, Size::from_bytes(size.into())), bits, - "Unexpected value of size {} after casting", size); - } + match val.to_bits_or_ptr(src_layout.size, self) { + Err(ptr) => self.cast_from_ptr(ptr, dest_layout.ty), + Ok(data) => { + match src_layout.ty.sty { + Float(fty) => self.cast_from_float(data, fty, dest_layout.ty), + _ => self.cast_from_int(data, src_layout, dest_layout), } - // Done - Ok(res) } } } @@ -177,7 +161,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tcx, M> trace!("cast_from_int: {}, {}, {}", v, src_layout.ty, dest_layout.ty); use rustc::ty::TyKind::*; match dest_layout.ty.sty { - Int(_) | Uint(_) => { + Int(_) | Uint(_) | RawPtr(_) => { let v = self.truncate(v, dest_layout); Ok(Scalar::from_uint(v, dest_layout.size)) } @@ -205,15 +189,6 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tcx, M> Ok(Scalar::from_uint(v, Size::from_bytes(4))) }, - // No alignment check needed for raw pointers. - // But we have to truncate to target ptr size. - RawPtr(_) => { - Ok(Scalar::from_uint( - self.truncate_to_ptr(v).0, - self.pointer_size(), - )) - }, - // Casts to bool are not permitted by rustc, no need to handle them here. _ => err!(Unimplemented(format!("int to {:?} cast", dest_layout.ty))), } diff --git a/src/librustc_mir/interpret/memory.rs b/src/librustc_mir/interpret/memory.rs index 0cd258825acb8..65a3b04c8b152 100644 --- a/src/librustc_mir/interpret/memory.rs +++ b/src/librustc_mir/interpret/memory.rs @@ -12,7 +12,6 @@ use std::borrow::Cow; use rustc::ty::{self, Instance, ParamEnv, query::TyCtxtAt}; use rustc::ty::layout::{Align, TargetDataLayout, Size, HasDataLayout}; -pub use rustc::mir::interpret::{truncate, write_target_uint, read_target_uint}; use rustc_data_structures::fx::{FxHashSet, FxHashMap}; use syntax::ast::Mutability; @@ -248,23 +247,21 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { required_align: Align ) -> EvalResult<'tcx> { // Check non-NULL/Undef, extract offset - let (offset, alloc_align) = match ptr { - Scalar::Ptr(ptr) => { + let (offset, alloc_align) = match ptr.to_bits_or_ptr(self.pointer_size(), self) { + Err(ptr) => { // check this is not NULL -- which we can ensure only if this is in-bounds // of some (potentially dead) allocation. let align = self.check_bounds_ptr(ptr, InboundsCheck::MaybeDead, CheckInAllocMsg::NullPointerTest)?; (ptr.offset.bytes(), align) } - Scalar::Bits { bits, size } => { - assert_eq!(size as u64, self.pointer_size().bytes()); - assert!(bits < (1u128 << self.pointer_size().bits())); + Ok(data) => { // check this is not NULL - if bits == 0 { + if data == 0 { return err!(InvalidNullPointerUsage); } // the "base address" is 0 and hence always aligned - (bits as u64, required_align) + (data as u64, required_align) } }; // Check alignment diff --git a/src/librustc_mir/interpret/operand.rs b/src/librustc_mir/interpret/operand.rs index c03b35c40c6db..289379f34a9a3 100644 --- a/src/librustc_mir/interpret/operand.rs +++ b/src/librustc_mir/interpret/operand.rs @@ -641,19 +641,20 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tcx, M> } => { let variants_start = niche_variants.start().as_u32() as u128; let variants_end = niche_variants.end().as_u32() as u128; - match raw_discr { - ScalarMaybeUndef::Scalar(Scalar::Ptr(ptr)) => { + let raw_discr = raw_discr.not_undef() + .map_err(|_| InterpError::InvalidDiscriminant(ScalarMaybeUndef::Undef))?; + match raw_discr.to_bits_or_ptr(discr_val.layout.size, self) { + Err(ptr) => { // The niche must be just 0 (which an inbounds pointer value never is) let ptr_valid = niche_start == 0 && variants_start == variants_end && self.memory.check_bounds_ptr(ptr, InboundsCheck::MaybeDead, CheckInAllocMsg::NullPointerTest).is_ok(); if !ptr_valid { - return err!(InvalidDiscriminant(raw_discr.erase_tag())); + return err!(InvalidDiscriminant(raw_discr.erase_tag().into())); } (dataful_variant.as_u32() as u128, dataful_variant) }, - ScalarMaybeUndef::Scalar(Scalar::Bits { bits: raw_discr, size }) => { - assert_eq!(size as u64, discr_val.layout.size.bytes()); + Ok(raw_discr) => { let adjusted_discr = raw_discr.wrapping_sub(niche_start) .wrapping_add(variants_start); if variants_start <= adjusted_discr && adjusted_discr <= variants_end { @@ -668,8 +669,6 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tcx, M> (dataful_variant.as_u32() as u128, dataful_variant) } }, - ScalarMaybeUndef::Undef => - return err!(InvalidDiscriminant(ScalarMaybeUndef::Undef)), } } }) diff --git a/src/librustc_mir/interpret/place.rs b/src/librustc_mir/interpret/place.rs index 57d5ab71ecafd..0ae4e90b7c24d 100644 --- a/src/librustc_mir/interpret/place.rs +++ b/src/librustc_mir/interpret/place.rs @@ -686,7 +686,7 @@ where Immediate::Scalar(ScalarMaybeUndef::Scalar(Scalar::Ptr(_))) => assert_eq!(self.pointer_size(), dest.layout.size, "Size mismatch when writing pointer"), - Immediate::Scalar(ScalarMaybeUndef::Scalar(Scalar::Bits { size, .. })) => + Immediate::Scalar(ScalarMaybeUndef::Scalar(Scalar::Raw { size, .. })) => assert_eq!(Size::from_bytes(size.into()), dest.layout.size, "Size mismatch when writing bits"), Immediate::Scalar(ScalarMaybeUndef::Undef) => {}, // undef can have any size diff --git a/src/librustc_mir/interpret/snapshot.rs b/src/librustc_mir/interpret/snapshot.rs index 83bd3666b3d3b..c0bc7ce6b39ad 100644 --- a/src/librustc_mir/interpret/snapshot.rs +++ b/src/librustc_mir/interpret/snapshot.rs @@ -186,9 +186,9 @@ impl<'a, Ctx> Snapshot<'a, Ctx> for Scalar fn snapshot(&self, ctx: &'a Ctx) -> Self::Item { match self { Scalar::Ptr(p) => Scalar::Ptr(p.snapshot(ctx)), - Scalar::Bits{ size, bits } => Scalar::Bits { + Scalar::Raw{ size, data } => Scalar::Raw { + data: *data, size: *size, - bits: *bits, }, } } diff --git a/src/librustc_mir/interpret/validity.rs b/src/librustc_mir/interpret/validity.rs index f9401d9763506..072c6f4fd90e8 100644 --- a/src/librustc_mir/interpret/validity.rs +++ b/src/librustc_mir/interpret/validity.rs @@ -480,8 +480,8 @@ impl<'rt, 'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> wrapping_range_format(&layout.valid_range, max_hi), ) ); - let bits = match value { - Scalar::Ptr(ptr) => { + let bits = match value.to_bits_or_ptr(op.layout.size, self.ecx) { + Err(ptr) => { if lo == 1 && hi == max_hi { // only NULL is not allowed. // We can call `check_align` to check non-NULL-ness, but have to also look @@ -509,10 +509,8 @@ impl<'rt, 'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> ); } } - Scalar::Bits { bits, size } => { - assert_eq!(size as u64, op.layout.size.bytes()); - bits - } + Ok(data) => + data }; // Now compare. This is slightly subtle because this is a special "wrap-around" range. if wrapping_range_contains(&layout.valid_range, bits) { diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index 11022be097cfd..c46b439f4d34e 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/src/librustc_mir/transform/const_prop.rs @@ -382,10 +382,7 @@ impl<'a, 'mir, 'tcx> ConstPropagator<'a, 'mir, 'tcx> { type_size_of(self.tcx, self.param_env, ty).and_then(|n| Some( ImmTy { imm: Immediate::Scalar( - Scalar::Bits { - bits: n as u128, - size: self.tcx.data_layout.pointer_size.bytes() as u8, - }.into() + Scalar::from_uint(n, self.tcx.data_layout.pointer_size).into() ), layout: self.tcx.layout_of(self.param_env.and(self.tcx.types.usize)).ok()?, }.into() @@ -713,18 +710,18 @@ impl<'b, 'a, 'tcx> MutVisitor<'tcx> for ConstPropagator<'b, 'a, 'tcx> { .eval_operand(len, source_info) .expect("len must be const"); let len = match self.ecx.read_scalar(len) { - Ok(ScalarMaybeUndef::Scalar(Scalar::Bits { - bits, .. - })) => bits, + Ok(ScalarMaybeUndef::Scalar(Scalar::Raw { + data, .. + })) => data, other => bug!("const len not primitive: {:?}", other), }; let index = self .eval_operand(index, source_info) .expect("index must be const"); let index = match self.ecx.read_scalar(index) { - Ok(ScalarMaybeUndef::Scalar(Scalar::Bits { - bits, .. - })) => bits, + Ok(ScalarMaybeUndef::Scalar(Scalar::Raw { + data, .. + })) => data, other => bug!("const index not primitive: {:?}", other), }; format!(