Skip to content

Commit

Permalink
[pointer] Fix Ptr[Inner] variance (#2351)
Browse files Browse the repository at this point in the history
Previously, `Ptr<'a, T>` and `PtrInner<'a, T>` documented themselves to
be covariant in both `'a` and `T`. This was true for `PtrInner`, but not
for `Ptr`, which used GATs, which are invariant. This is also not the
desired variance: for `Exclusive` aliasing, the desired variance matches
that of `&mut` references - namely, covariant in `'a` but invariant in
`T`.

This commit fixes this by making `Ptr<'a, T>` and `PtrInner<'a, T>`
unconditionally covariant in `'a` and invariant in `T`.

gherrit-pr-id: I29f8429d9d7b14026313f030f8dc1e895a98ad56
  • Loading branch information
joshlf authored Feb 18, 2025
1 parent 9322a2c commit 79ec7c4
Show file tree
Hide file tree
Showing 9 changed files with 124 additions and 19 deletions.
11 changes: 5 additions & 6 deletions src/pointer/inner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ mod _def {
use super::*;
/// The inner pointer stored inside a [`Ptr`][crate::Ptr].
///
/// `Ptr<'a, T>` is [covariant] in `'a` and `T`.
/// `PtrInner<'a, T>` is [covariant] in `'a` and invariant in `T`.
///
/// [covariant]: https://doc.rust-lang.org/reference/subtyping.html
pub(crate) struct PtrInner<'a, T>
Expand All @@ -42,14 +42,13 @@ mod _def {
/// address space.
/// 5. If `ptr`'s referent is not zero sized,`A` is guaranteed to live
/// for at least `'a`.
// SAFETY: `NonNull<T>` is covariant over `T` [1].
//
// [1]: https://doc.rust-lang.org/std/ptr/struct.NonNull.html
ptr: NonNull<T>,
// SAFETY: `&'a T` is covariant over `'a` [1].
// SAFETY: `&'a UnsafeCell<T>` is covariant in `'a` and invariant in `T`
// [1]. We use this construction rather than the equivalent `&mut T`,
// because our MSRV of 1.65 prohibits `&mut` types in const contexts.
//
// [1] https://doc.rust-lang.org/1.81.0/reference/subtyping.html#variance
_marker: PhantomData<&'a T>,
_marker: PhantomData<&'a core::cell::UnsafeCell<T>>,
}

impl<'a, T: 'a + ?Sized> Copy for PtrInner<'a, T> {}
Expand Down
8 changes: 0 additions & 8 deletions src/pointer/invariant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,6 @@ 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 `<I::Aliasing as
/// Aliasing>::Variance<'a, T>` to inherit this variance.
#[doc(hidden)]
type Variance<'a, T: 'a + ?Sized>;
}

/// The alignment invariant of a [`Ptr`][super::Ptr].
Expand Down Expand Up @@ -132,7 +126,6 @@ impl<T: ?Sized> Validity for Uninit<T> {
pub enum Shared {}
impl Aliasing for Shared {
const IS_EXCLUSIVE: bool = false;
type Variance<'a, T: 'a + ?Sized> = &'a T;
}
impl Reference for Shared {}

Expand All @@ -144,7 +137,6 @@ 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 {}

Expand Down
9 changes: 4 additions & 5 deletions src/pointer/ptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ mod def {
/// - `ptr` conforms to the alignment invariant of
/// [`I::Alignment`](invariant::Alignment).
///
/// `Ptr<'a, T>` is [covariant] in `'a` and `T`.
/// `Ptr<'a, T>` is [covariant] in `'a` and invariant in `T`.
///
/// [covariant]: https://doc.rust-lang.org/reference/subtyping.html
pub struct Ptr<'a, V, I>
Expand All @@ -54,9 +54,8 @@ mod def {
/// [`I::Aliasing`](invariant::Aliasing).
/// 2. `ptr` conforms to the alignment invariant of
/// [`I::Alignment`](invariant::Alignment).
// SAFETY: `PtrInner<'a, T>` is covariant over `'a` and `T`.
// SAFETY: `PtrInner<'a, T>` is covariant in `'a` and invariant in `T`.
ptr: PtrInner<'a, V::Inner>,
_variance: PhantomData<<I::Aliasing as Aliasing>::Variance<'a, V::Inner>>,
_invariants: PhantomData<I>,
}

Expand Down Expand Up @@ -94,7 +93,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, _variance: PhantomData, _invariants: PhantomData }
Self { ptr, _invariants: PhantomData }
}

/// Constructs a new `Ptr` from a [`PtrInner`].
Expand All @@ -112,7 +111,7 @@ mod def {
pub(super) const unsafe fn from_inner(ptr: PtrInner<'a, V::Inner>) -> Ptr<'a, V, I> {
// SAFETY: The caller has promised to satisfy all safety invariants
// of `Ptr`.
Self { ptr, _variance: PhantomData, _invariants: PhantomData }
Self { ptr, _invariants: PhantomData }
}

/// Converts this `Ptr<T>` to a [`PtrInner<T>`].
Expand Down
1 change: 1 addition & 0 deletions tests/ui-msrv/ptr-is-invariant-over-v.rs
31 changes: 31 additions & 0 deletions tests/ui-msrv/ptr-is-invariant-over-v.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
error: lifetime may not live long enough
--> tests/ui-msrv/ptr-is-invariant-over-v.rs:10:5
|
6 | fn _when_exclusive<'big: 'small, 'small>(
| ---- ------ lifetime `'small` defined here
| |
| lifetime `'big` defined here
...
10 | _small = big;
| ^^^^^^^^^^^^ assignment requires that `'small` must outlive `'big`
|
= help: consider adding the following bound: `'small: 'big`
= note: requirement occurs because of the type `Ptr<'_, Valid<&u32>, (invariant::Exclusive, Aligned)>`, which makes the generic argument `Valid<&u32>` invariant
= note: the struct `Ptr<'a, V, I>` is invariant over the parameter `V`
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance

error: lifetime may not live long enough
--> tests/ui-msrv/ptr-is-invariant-over-v.rs:17:5
|
13 | fn _when_shared<'big: 'small, 'small>(
| ---- ------ lifetime `'small` defined here
| |
| lifetime `'big` defined here
...
17 | _small = big;
| ^^^^^^^^^^^^ assignment requires that `'small` must outlive `'big`
|
= help: consider adding the following bound: `'small: 'big`
= note: requirement occurs because of the type `Ptr<'_, Valid<&u32>, (Shared, Aligned)>`, which makes the generic argument `Valid<&u32>` invariant
= note: the struct `Ptr<'a, V, I>` is invariant over the parameter `V`
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
20 changes: 20 additions & 0 deletions tests/ui-nightly/ptr-is-invariant-over-v.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
use zerocopy::pointer::{
invariant::{Aligned, Exclusive, Shared, Valid},
Ptr,
};

fn _when_exclusive<'big: 'small, 'small>(
big: Ptr<'small, Valid<&'big u32>, (Exclusive, Aligned)>,
mut _small: Ptr<'small, Valid<&'small u32>, (Exclusive, Aligned)>,
) {
_small = big;
}

fn _when_shared<'big: 'small, 'small>(
big: Ptr<'small, Valid<&'big u32>, (Shared, Aligned)>,
mut _small: Ptr<'small, Valid<&'small u32>, (Shared, Aligned)>,
) {
_small = big;
}

fn main() {}
31 changes: 31 additions & 0 deletions tests/ui-nightly/ptr-is-invariant-over-v.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
error: lifetime may not live long enough
--> tests/ui-nightly/ptr-is-invariant-over-v.rs:10:5
|
6 | fn _when_exclusive<'big: 'small, 'small>(
| ---- ------ lifetime `'small` defined here
| |
| lifetime `'big` defined here
...
10 | _small = big;
| ^^^^^^^^^^^^ assignment requires that `'small` must outlive `'big`
|
= help: consider adding the following bound: `'small: 'big`
= note: requirement occurs because of the type `Ptr<'_, Valid<&u32>, (invariant::Exclusive, Aligned)>`, which makes the generic argument `Valid<&u32>` invariant
= note: the struct `Ptr<'a, V, I>` is invariant over the parameter `V`
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance

error: lifetime may not live long enough
--> tests/ui-nightly/ptr-is-invariant-over-v.rs:17:5
|
13 | fn _when_shared<'big: 'small, 'small>(
| ---- ------ lifetime `'small` defined here
| |
| lifetime `'big` defined here
...
17 | _small = big;
| ^^^^^^^^^^^^ assignment requires that `'small` must outlive `'big`
|
= help: consider adding the following bound: `'small: 'big`
= note: requirement occurs because of the type `Ptr<'_, Valid<&u32>, (Shared, Aligned)>`, which makes the generic argument `Valid<&u32>` invariant
= note: the struct `Ptr<'a, V, I>` is invariant over the parameter `V`
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
1 change: 1 addition & 0 deletions tests/ui-stable/ptr-is-invariant-over-v.rs
31 changes: 31 additions & 0 deletions tests/ui-stable/ptr-is-invariant-over-v.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
error: lifetime may not live long enough
--> tests/ui-stable/ptr-is-invariant-over-v.rs:10:5
|
6 | fn _when_exclusive<'big: 'small, 'small>(
| ---- ------ lifetime `'small` defined here
| |
| lifetime `'big` defined here
...
10 | _small = big;
| ^^^^^^^^^^^^ assignment requires that `'small` must outlive `'big`
|
= help: consider adding the following bound: `'small: 'big`
= note: requirement occurs because of the type `Ptr<'_, Valid<&u32>, (invariant::Exclusive, Aligned)>`, which makes the generic argument `Valid<&u32>` invariant
= note: the struct `Ptr<'a, V, I>` is invariant over the parameter `V`
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance

error: lifetime may not live long enough
--> tests/ui-stable/ptr-is-invariant-over-v.rs:17:5
|
13 | fn _when_shared<'big: 'small, 'small>(
| ---- ------ lifetime `'small` defined here
| |
| lifetime `'big` defined here
...
17 | _small = big;
| ^^^^^^^^^^^^ assignment requires that `'small` must outlive `'big`
|
= help: consider adding the following bound: `'small: 'big`
= note: requirement occurs because of the type `Ptr<'_, Valid<&u32>, (Shared, Aligned)>`, which makes the generic argument `Valid<&u32>` invariant
= note: the struct `Ptr<'a, V, I>` is invariant over the parameter `V`
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance

0 comments on commit 79ec7c4

Please sign in to comment.