diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index c7c37d30dd546..4c8c1b2c86e19 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -224,6 +224,7 @@ language_item_table! { IndexMut, sym::index_mut, index_mut_trait, Target::Trait, GenericRequirement::Exact(1); UnsafeCell, sym::unsafe_cell, unsafe_cell_type, Target::Struct, GenericRequirement::None; + CovariantUnsafeCell, sym::covariant_unsafe_cell, covariant_unsafe_cell_type, Target::Struct, GenericRequirement::Exact(1); UnsafePinned, sym::unsafe_pinned, unsafe_pinned_type, Target::Struct, GenericRequirement::None; VaArgSafe, sym::va_arg_safe, va_arg_safe, Target::Trait, GenericRequirement::None; diff --git a/compiler/rustc_hir_analysis/src/variance/terms.rs b/compiler/rustc_hir_analysis/src/variance/terms.rs index 6faeab4217e37..d22eb01467ebe 100644 --- a/compiler/rustc_hir_analysis/src/variance/terms.rs +++ b/compiler/rustc_hir_analysis/src/variance/terms.rs @@ -111,6 +111,7 @@ fn lang_items(tcx: TyCtxt<'_>) -> Vec<(LocalDefId, Vec)> { let all = [ (lang_items.phantom_data(), vec![ty::Covariant]), (lang_items.unsafe_cell_type(), vec![ty::Invariant]), + (lang_items.covariant_unsafe_cell_type(), vec![ty::Covariant]), ]; all.into_iter() // iterating over (Option, Variance) diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 3b44015b4f452..27c6bc0f4b6d7 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -737,6 +737,7 @@ symbols! { cosf64, cosf128, count, + covariant_unsafe_cell, coverage, coverage_attribute, cr, diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index 1b8ec2a91478a..7051ab22581f6 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -259,9 +259,12 @@ use crate::pin::PinCoerceUnsized; use crate::ptr::{self, NonNull}; use crate::range; +mod covariant_unsafe_cell; mod lazy; mod once; +#[unstable(feature = "covariant_unsafe_cell", issue = "159735")] +pub use covariant_unsafe_cell::CovariantUnsafeCell; #[stable(feature = "lazy_cell", since = "1.80.0")] pub use lazy::LazyCell; #[stable(feature = "once_cell", since = "1.70.0")] diff --git a/library/core/src/cell/covariant_unsafe_cell.rs b/library/core/src/cell/covariant_unsafe_cell.rs new file mode 100644 index 0000000000000..3876cc404cd2e --- /dev/null +++ b/library/core/src/cell/covariant_unsafe_cell.rs @@ -0,0 +1,183 @@ +use crate::cell::UnsafeCell; +use crate::fmt; +use crate::ops::CoerceUnsized; +use crate::ptr::{self, NonNull}; + +/// **Co**variant version of [`UnsafeCell`]. +#[unstable(feature = "covariant_unsafe_cell", issue = "159735")] +#[repr(transparent)] +#[rustc_pub_transparent] +// Implementation note: +// +// We could make `CovariantUnsafeCell` be the canonical lang item and make `UnsafeCell` a wrapper +// over it, with `PhantomData<*mut T>`. That would however be a huge compiler change, without clear +// benefit. +// +// As such, `CovariantUnsafeCell` is wrapping `UnsafeCell` instead. It is a lang-item only to +// hardcode its variance to be **co**variant in `T`, even though it is wrapping `UnsafeCell` which +// is **in**variant in `T`. +#[lang = "covariant_unsafe_cell"] +pub struct CovariantUnsafeCell(UnsafeCell); + +#[unstable(feature = "covariant_unsafe_cell", issue = "159735")] +impl !Sync for CovariantUnsafeCell {} + +impl CovariantUnsafeCell { + /// Constructs a new instance of `CovariantUnsafeCell` which will wrap the specified value. + /// + /// All access to the inner value through `&CovariantUnsafeCell` requires `unsafe` code. + /// + /// # Examples + /// + /// ``` + /// #![feature(covariant_unsafe_cell)] + /// use std::cell::CovariantUnsafeCell; + /// + /// let uc = CovariantUnsafeCell::new(5); + /// ``` + #[unstable(feature = "covariant_unsafe_cell", issue = "159735")] + #[rustc_const_unstable(feature = "covariant_unsafe_cell", issue = "159735")] + #[inline(always)] + pub const fn new(value: T) -> CovariantUnsafeCell { + CovariantUnsafeCell(UnsafeCell::new(value)) + } + + /// Unwraps the value, consuming the cell. + /// + /// # Examples + /// + /// ``` + /// #![feature(covariant_unsafe_cell)] + /// use std::cell::CovariantUnsafeCell; + /// + /// let uc = CovariantUnsafeCell::new(5); + /// + /// let five = uc.into_inner(); + /// ``` + #[inline(always)] + #[unstable(feature = "covariant_unsafe_cell", issue = "159735")] + #[rustc_const_unstable(feature = "covariant_unsafe_cell", issue = "159735")] + pub const fn into_inner(self) -> T { + self.0.into_inner() + } +} + +impl CovariantUnsafeCell { + /// Gets a mutable non-null pointer to the wrapped value. + /// + /// This can be cast to a pointer of any kind. When creating (shared or mutable) references, you + /// must uphold the aliasing rules; see [the `UnsafeCell` type-level docs] for more discussion + /// and caveats. + /// + /// [the `UnsafeCell` type-level docs]: super::UnsafeCell#aliasing-rules + /// + /// # Examples + /// + /// ``` + /// #![feature(covariant_unsafe_cell)] + /// use std::cell::CovariantUnsafeCell; + /// use std::ptr::NonNull; + /// + /// let uc = CovariantUnsafeCell::new(5); + /// + /// let ptr: NonNull = uc.get(); + /// ``` + #[inline(always)] + #[rustc_as_ptr] + #[rustc_should_not_be_called_on_const_items] + #[unstable(feature = "covariant_unsafe_cell", issue = "159735")] + #[rustc_const_unstable(feature = "covariant_unsafe_cell", issue = "159735")] + pub const fn get(&self) -> NonNull { + // We can just cast the pointer from `CovariantUnsafeCell` to `T` because of + // #[repr(transparent)]. + // + // Note that this is also known to be allowed for user code as per + // `#[rustc_pub_transparent]`. + // SAFETY: the pointer is not null, as it comes from a reference + unsafe { NonNull::new_unchecked(ptr::from_ref(self).cast_mut() as *mut T) } + } + + /// Returns a mutable reference to the underlying data. + /// + /// This call borrows the `CovariantUnsafeCell` mutably (at compile-time) which guarantees that + /// we possess the only reference. + /// + /// # Examples + /// + /// ``` + /// #![feature(covariant_unsafe_cell)] + /// use std::cell::CovariantUnsafeCell; + /// + /// let mut c = CovariantUnsafeCell::new(5); + /// *c.get_mut() += 1; + /// + /// assert_eq!(*c.get_mut(), 6); + /// ``` + #[inline(always)] + #[unstable(feature = "covariant_unsafe_cell", issue = "159735")] + #[rustc_const_unstable(feature = "covariant_unsafe_cell", issue = "159735")] + pub const fn get_mut(&mut self) -> &mut T { + self.0.get_mut() + } + + /// Gets a mutable pointer to the wrapped value. + /// The difference from [`get`] is that this function accepts a raw pointer, + /// which is useful to avoid the creation of temporary references. + /// + /// This can be cast to a pointer of any kind. When creating (shared or mutable) references, you + /// must uphold the aliasing rules; see [the `UnsafeCell` type-level docs] for more discussion + /// and caveats. + /// + /// [`get`]: CovariantUnsafeCell::get() + /// + /// # Examples + /// + /// Gradual initialization of an `CovariantUnsafeCell` requires `raw_get`, as + /// calling `get` would require creating a reference to uninitialized data: + /// + /// ``` + /// #![feature(covariant_unsafe_cell)] + /// use std::cell::CovariantUnsafeCell; + /// use std::mem::MaybeUninit; + /// + /// let m = MaybeUninit::>::uninit(); + /// unsafe { CovariantUnsafeCell::raw_get(m.as_ptr()).write(5); } + /// // avoid below which references to uninitialized data + /// // unsafe { CovariantUnsafeCell::get(&*m.as_ptr()).write(5); } + /// let uc = unsafe { m.assume_init() }; + /// + /// assert_eq!(uc.into_inner(), 5); + /// ``` + #[inline(always)] + #[unstable(feature = "covariant_unsafe_cell", issue = "159735")] + #[rustc_const_unstable(feature = "covariant_unsafe_cell", issue = "159735")] + pub const fn raw_get(this: *const Self) -> *mut T { + // We can just cast the pointer from `UnsafeCell` to `T` because of + // #[repr(transparent)]. + // + // Note that this is also known to be allowed for user code as per + // `#[rustc_pub_transparent]`. + this as *const T as *mut T + } +} + +#[unstable(feature = "coerce_unsized", issue = "18598")] +impl, U> CoerceUnsized> for CovariantUnsafeCell {} + +#[unstable(feature = "covariant_unsafe_cell", issue = "159735")] +impl fmt::Debug for CovariantUnsafeCell { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("CovariantUnsafeCell").finish_non_exhaustive() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + fn _covarience<'short, 'long: 'short>( + x: CovariantUnsafeCell<&'long ()>, + ) -> CovariantUnsafeCell<&'short ()> { + x + } +}