From 25ddf056cacc921b5d82f2ce3b1e427ac767eb71 Mon Sep 17 00:00:00 2001 From: Joshua Liebow-Feeser Date: Sat, 12 Oct 2024 15:31:04 -0700 Subject: [PATCH] [pointer] Match variance of references When the aliasing mode is `Any`, `Ptr<'a, T>` is invariant in `'a` and `T`. When the aliasing mode is `Shared` or `Exclusive`, `Ptr` has the same variance as `&'a T` and `&'a mut T` respectively. Makes progress on #1839 --- src/pointer/invariant.rs | 19 +++++++++++++++++++ src/pointer/ptr.rs | 5 +++-- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/pointer/invariant.rs b/src/pointer/invariant.rs index abd019a391b..cec4bbfc0dd 100644 --- a/src/pointer/invariant.rs +++ b/src/pointer/invariant.rs @@ -31,6 +31,12 @@ pub trait Aliasing: Sealed { /// Is `Self` [`Exclusive`]? #[doc(hidden)] const IS_EXCLUSIVE: bool; + + /// A type which has the correct variance over `'a` and `T` for this + /// aliasing invariant. `Ptr` stores a `::Variance<'a, T>` to inherit this variance. + #[doc(hidden)] + type Variance<'a, T: 'a + ?Sized>; } /// The alignment invariant of a [`Ptr`][super::Ptr]. @@ -51,6 +57,17 @@ pub trait Reference: Aliasing + Sealed {} pub enum Any {} impl Aliasing for Any { const IS_EXCLUSIVE: bool = false; + + // SAFETY: Since we don't know what aliasing model this is, we have to be + // conservative. Invariance is strictly more restrictive than any other + // variance model, so this can never cause soundness issues. + // + // `fn() -> T` and `fn(T) -> ()` are covariant and contravariant in `T`, + // respectively. [1] Thus, `fn(T) -> T` is invariant in `T`. Thus, `fn(&'a + // T) -> &'a T` is invariant in `'a` and `T`. + // + // [1] https://doc.rust-lang.org/1.81.0/reference/subtyping.html#variance + type Variance<'a, T: 'a + ?Sized> = fn(&'a T) -> &'a T; } impl Alignment for Any {} impl Validity for Any {} @@ -66,6 +83,7 @@ impl Validity for Any {} pub enum Shared {} impl Aliasing for Shared { const IS_EXCLUSIVE: bool = false; + type Variance<'a, T: 'a + ?Sized> = &'a T; } impl Reference for Shared {} @@ -77,6 +95,7 @@ impl Reference for Shared {} pub enum Exclusive {} impl Aliasing for Exclusive { const IS_EXCLUSIVE: bool = true; + type Variance<'a, T: 'a + ?Sized> = &'a mut T; } impl Reference for Exclusive {} diff --git a/src/pointer/ptr.rs b/src/pointer/ptr.rs index 1352c6a48d6..f9d96b15663 100644 --- a/src/pointer/ptr.rs +++ b/src/pointer/ptr.rs @@ -56,6 +56,7 @@ mod def { /// [`I::Validity`](invariant::Validity). // SAFETY: `PtrInner<'a, T>` is covariant over `'a` and `T`. ptr: PtrInner<'a, T>, + _variance: PhantomData<::Variance<'a, T>>, _invariants: PhantomData, } @@ -93,7 +94,7 @@ mod def { let ptr = unsafe { PtrInner::new(ptr) }; // SAFETY: The caller has promised (in 6 - 8) to satisfy all safety // invariants of `Ptr`. - Self { ptr, _invariants: PhantomData } + Self { ptr, _variance: PhantomData, _invariants: PhantomData } } /// Constructs a new `Ptr` from a [`PtrInner`]. @@ -111,7 +112,7 @@ mod def { pub(super) const unsafe fn from_inner(ptr: PtrInner<'a, T>) -> Ptr<'a, T, I> { // SAFETY: The caller has promised to satisfy all safety invariants // of `Ptr`. - Self { ptr, _invariants: PhantomData } + Self { ptr, _variance: PhantomData, _invariants: PhantomData } } /// Converts this `Ptr` to a [`PtrInner`].