Skip to content

Commit

Permalink
[pointer] Fix Ptr[Inner] variance (#2351) (#2393)
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 25, 2025
1 parent 8bbfec2 commit fe70ab6
Show file tree
Hide file tree
Showing 8 changed files with 108 additions and 8 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
4 changes: 2 additions & 2 deletions src/pointer/ptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ mod def {
/// - `ptr` conforms to the validity invariant of
/// [`I::Validity`](invariant::Validity).
///
/// `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, T, I>
Expand All @@ -54,7 +54,7 @@ mod def {
/// [`I::Alignment`](invariant::Alignment).
/// 2. `ptr` conforms to the validity invariant of
/// [`I::Validity`](invariant::Validity).
// 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, T>,
_invariants: PhantomData<I>,
}
Expand Down
1 change: 1 addition & 0 deletions tests/ui-msrv/ptr-is-invariant-over-v.rs
17 changes: 17 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,17 @@
error[E0623]: lifetime mismatch
--> tests/ui-msrv/ptr-is-invariant-over-v.rs:10:14
|
7 | big: Ptr<'small, &'big u32, (Exclusive, Aligned, Valid)>,
| --------------------------------------------------- these two types are declared with different lifetimes...
...
10 | _small = big;
| ^^^ ...but data from `big` flows into `big` here

error[E0623]: lifetime mismatch
--> tests/ui-msrv/ptr-is-invariant-over-v.rs:17:14
|
14 | big: Ptr<'small, &'big u32, (Shared, Aligned, Valid)>,
| ------------------------------------------------ these two types are declared with different lifetimes...
...
17 | _small = big;
| ^^^ ...but data from `big` flows into `big` here
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, &'big u32, (Exclusive, Aligned, Valid)>,
mut _small: Ptr<'small, &'small u32, (Exclusive, Aligned, Valid)>,
) {
_small = big;
}

fn _when_shared<'big: 'small, 'small>(
big: Ptr<'small, &'big u32, (Shared, Aligned, Valid)>,
mut _small: Ptr<'small, &'small u32, (Shared, Aligned, Valid)>,
) {
_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<'_, &u32, (invariant::Exclusive, Aligned, Valid)>`, which makes the generic argument `&u32` invariant
= note: the struct `Ptr<'a, T, I>` is invariant over the parameter `T`
= 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<'_, &u32, (Shared, Aligned, Valid)>`, which makes the generic argument `&u32` invariant
= note: the struct `Ptr<'a, T, I>` is invariant over the parameter `T`
= 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<'_, &u32, (invariant::Exclusive, Aligned, Valid)>`, which makes the generic argument `&u32` invariant
= note: the struct `Ptr<'a, T, I>` is invariant over the parameter `T`
= 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<'_, &u32, (Shared, Aligned, Valid)>`, which makes the generic argument `&u32` invariant
= note: the struct `Ptr<'a, T, I>` is invariant over the parameter `T`
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance

0 comments on commit fe70ab6

Please sign in to comment.