Skip to content

Commit

Permalink
Implement phantom variance markers
Browse files Browse the repository at this point in the history
  • Loading branch information
jhpratt committed Jan 22, 2025
1 parent b605c65 commit c558d94
Show file tree
Hide file tree
Showing 2 changed files with 241 additions and 0 deletions.
7 changes: 7 additions & 0 deletions library/core/src/marker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@
#![stable(feature = "rust1", since = "1.0.0")]

mod variance;

#[unstable(feature = "phantom_variance_markers", issue = "135806")]
pub use self::variance::{
PhantomContravariant, PhantomContravariantLifetime, PhantomCovariant, PhantomCovariantLifetime,
PhantomInvariant, PhantomInvariantLifetime, Variance, variance,
};
use crate::cell::UnsafeCell;
use crate::cmp;
use crate::fmt::Debug;
Expand Down
234 changes: 234 additions & 0 deletions library/core/src/marker/variance.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
#![unstable(feature = "phantom_variance_markers", issue = "135806")]

use super::PhantomData;
use crate::any::type_name;
use crate::cmp::Ordering;
use crate::fmt;
use crate::hash::{Hash, Hasher};

macro_rules! first_token {
($first:tt $($rest:tt)*) => {
$first
};
}

macro_rules! phantom_type {
($(
$(#[$attr:meta])*
pub struct $name:ident <$t:ident> ($($inner:tt)*);
)*) => {$(
$(#[$attr])*
pub struct $name<$t>($($inner)*) where T: ?Sized;

impl<T> $name<T>
where T: ?Sized
{
/// Constructs a new instance of the variance marker.
pub const fn new() -> Self {
Self(PhantomData)
}
}

impl<T> self::sealed::Sealed for $name<T> where T: ?Sized {
const VALUE: Self = Self::new();
}
impl<T> Variance for $name<T> where T: ?Sized {}

impl<T> Default for $name<T>
where T: ?Sized
{
fn default() -> Self {
Self(PhantomData)
}
}

impl<T> fmt::Debug for $name<T>
where T: ?Sized
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}<{}>", stringify!($name), type_name::<T>())
}
}

impl<T> Clone for $name<T>
where T: ?Sized
{
fn clone(&self) -> Self {
*self
}
}

impl<T> Copy for $name<T> where T: ?Sized {}

impl<T> PartialEq for $name<T>
where T: ?Sized
{
fn eq(&self, _: &Self) -> bool {
true
}
}

impl<T> Eq for $name<T> where T: ?Sized {}

impl<T> PartialOrd for $name<T>
where T: ?Sized
{
fn partial_cmp(&self, _: &Self) -> Option<Ordering> {
Some(Ordering::Equal)
}
}

impl<T> Ord for $name<T>
where T: ?Sized
{
fn cmp(&self, _: &Self) -> Ordering {
Ordering::Equal
}
}

impl<T> Hash for $name<T>
where T: ?Sized
{
fn hash<H: Hasher>(&self, _: &mut H) {}
}
)*};
}

macro_rules! phantom_lifetime {
($(
$(#[$attr:meta])*
pub struct $name:ident <$lt:lifetime> ($($inner:tt)*);
)*) => {$(
$(#[$attr])*
#[derive(Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct $name<$lt>($($inner)*);

impl $name<'_> {
/// Constructs a new instance of the variance marker.
pub const fn new() -> Self {
Self(first_token!($($inner)*)(PhantomData))
}
}

impl self::sealed::Sealed for $name<'_> {
const VALUE: Self = Self::new();
}
impl Variance for $name<'_> {}

impl fmt::Debug for $name<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", stringify!($name))
}
}
)*};
}

phantom_lifetime! {
/// Zero-sized type used to mark a lifetime as covariant.
///
/// Covariant lifetimes must live at least as long as declared. See [the reference][1] for more
/// information.
///
/// [1]: https://doc.rust-lang.org/stable/reference/subtyping.html#variance
///
/// ## Layout
///
/// For all `'a`, the following are guaranteed:
/// * `size_of::<PhantomCovariantLifetime<'a>>() == 0`
/// * `align_of::<PhantomCovariantLifetime<'a>>() == 1`
pub struct PhantomCovariantLifetime<'a>(PhantomCovariant<&'a ()>);
/// Zero-sized type used to mark a lifetime as contravariant.
///
/// Contravariant lifetimes must live at most as long as declared. See [the reference][1] for
/// more information.
///
/// [1]: https://doc.rust-lang.org/stable/reference/subtyping.html#variance
///
/// ## Layout
///
/// For all `'a`, the following are guaranteed:
/// * `size_of::<PhantomContravariantLifetime<'a>>() == 0`
/// * `align_of::<PhantomContravariantLifetime<'a>>() == 1`
pub struct PhantomContravariantLifetime<'a>(PhantomContravariant<&'a ()>);
/// Zero-sized type used to mark a lifetime as invariant.
///
/// Invariant lifetimes must be live for the exact length declared, neither shorter nor longer.
/// See [the reference][1] for more information.
///
/// [1]: https://doc.rust-lang.org/stable/reference/subtyping.html#variance
///
/// ## Layout
///
/// For all `'a`, the following are guaranteed:
/// * `size_of::<PhantomInvariantLifetime<'a>>() == 0`
/// * `align_of::<PhantomInvariantLifetime<'a>>() == 1`
pub struct PhantomInvariantLifetime<'a>(PhantomInvariant<&'a ()>);
}

phantom_type! {
/// Zero-sized type used to mark a type parameter as covariant.
///
/// Types passed as arguments to functions are covariant. See [the reference][1] for more
/// information.
///
/// [1]: https://doc.rust-lang.org/stable/reference/subtyping.html#variance
///
/// ## Layout
///
/// For all `T`, the following are guaranteed:
/// * `size_of::<PhantomCovariant<T>>() == 0`
/// * `align_of::<PhantomCovariant<T>>() == 1`
pub struct PhantomCovariant<T>(PhantomData<T>);
/// Zero-sized type used to mark a type parameter as contravariant.
///
/// Types returned from functions are contravariant. See [the reference][1] for more
/// information.
///
/// [1]: https://doc.rust-lang.org/stable/reference/subtyping.html#variance
///
/// ## Layout
///
/// For all `T`, the following are guaranteed:
/// * `size_of::<PhantomContravariant<T>>() == 0`
/// * `align_of::<PhantomContravariant<T>>() == 1`
pub struct PhantomContravariant<T>(PhantomData<fn(T)>);
/// Zero-sized type used to mark a type parameter as invariant.
///
/// Types used as both arguments and return values. See [the reference][1] for more information.
///
/// [1]: https://doc.rust-lang.org/stable/reference/subtyping.html#variance
///
/// ## Layout
///
/// For all `T`, the following are guaranteed:
/// * `size_of::<PhantomInvariant<T>>() == 0`
/// * `align_of::<PhantomInvariant<T>>() == 1`
pub struct PhantomInvariant<T>(PhantomData<fn(T) -> T>);
}

mod sealed {
pub trait Sealed {
const VALUE: Self;
}
}

/// A marker trait for phantom variance types.
pub trait Variance: sealed::Sealed + Default {}

/// Construct a variance marker; equivalent to [`Default::default`].
///
/// This type can be any of the following. You generally should not need to explicitly name the
/// type, however.
///
/// - [`PhantomCovariant`]
/// - [`PhantomContravariant`]
/// - [`PhantomInvariant`]
/// - [`PhantomCovariantLifetime`]
/// - [`PhantomContravariantLifetime`]
/// - [`PhantomInvariantLifetime`]
pub const fn variance<T>() -> T
where
T: Variance,
{
T::VALUE
}

0 comments on commit c558d94

Please sign in to comment.