From fe70ab6911f954f390b9ff04871e3c58fbba24e0 Mon Sep 17 00:00:00 2001 From: Joshua Liebow-Feeser Date: Tue, 25 Feb 2025 10:40:33 -0800 Subject: [PATCH] [pointer] Fix Ptr[Inner] variance (#2351) (#2393) 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 --- src/pointer/inner.rs | 11 +++---- src/pointer/ptr.rs | 4 +-- tests/ui-msrv/ptr-is-invariant-over-v.rs | 1 + tests/ui-msrv/ptr-is-invariant-over-v.stderr | 17 ++++++++++ tests/ui-nightly/ptr-is-invariant-over-v.rs | 20 ++++++++++++ .../ui-nightly/ptr-is-invariant-over-v.stderr | 31 +++++++++++++++++++ tests/ui-stable/ptr-is-invariant-over-v.rs | 1 + .../ui-stable/ptr-is-invariant-over-v.stderr | 31 +++++++++++++++++++ 8 files changed, 108 insertions(+), 8 deletions(-) create mode 120000 tests/ui-msrv/ptr-is-invariant-over-v.rs create mode 100644 tests/ui-msrv/ptr-is-invariant-over-v.stderr create mode 100644 tests/ui-nightly/ptr-is-invariant-over-v.rs create mode 100644 tests/ui-nightly/ptr-is-invariant-over-v.stderr create mode 120000 tests/ui-stable/ptr-is-invariant-over-v.rs create mode 100644 tests/ui-stable/ptr-is-invariant-over-v.stderr diff --git a/src/pointer/inner.rs b/src/pointer/inner.rs index 48d4e8f113..ac0938424f 100644 --- a/src/pointer/inner.rs +++ b/src/pointer/inner.rs @@ -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> @@ -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` is covariant over `T` [1]. - // - // [1]: https://doc.rust-lang.org/std/ptr/struct.NonNull.html ptr: NonNull, - // SAFETY: `&'a T` is covariant over `'a` [1]. + // SAFETY: `&'a UnsafeCell` 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>, } impl<'a, T: 'a + ?Sized> Copy for PtrInner<'a, T> {} diff --git a/src/pointer/ptr.rs b/src/pointer/ptr.rs index cf67bbb48b..14382ce12e 100644 --- a/src/pointer/ptr.rs +++ b/src/pointer/ptr.rs @@ -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> @@ -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, } diff --git a/tests/ui-msrv/ptr-is-invariant-over-v.rs b/tests/ui-msrv/ptr-is-invariant-over-v.rs new file mode 120000 index 0000000000..d29c80df7b --- /dev/null +++ b/tests/ui-msrv/ptr-is-invariant-over-v.rs @@ -0,0 +1 @@ +../ui-nightly/ptr-is-invariant-over-v.rs \ No newline at end of file diff --git a/tests/ui-msrv/ptr-is-invariant-over-v.stderr b/tests/ui-msrv/ptr-is-invariant-over-v.stderr new file mode 100644 index 0000000000..bd297c6d21 --- /dev/null +++ b/tests/ui-msrv/ptr-is-invariant-over-v.stderr @@ -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 diff --git a/tests/ui-nightly/ptr-is-invariant-over-v.rs b/tests/ui-nightly/ptr-is-invariant-over-v.rs new file mode 100644 index 0000000000..ebbd01fa6e --- /dev/null +++ b/tests/ui-nightly/ptr-is-invariant-over-v.rs @@ -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() {} diff --git a/tests/ui-nightly/ptr-is-invariant-over-v.stderr b/tests/ui-nightly/ptr-is-invariant-over-v.stderr new file mode 100644 index 0000000000..d12adcbbeb --- /dev/null +++ b/tests/ui-nightly/ptr-is-invariant-over-v.stderr @@ -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 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 for more information about variance diff --git a/tests/ui-stable/ptr-is-invariant-over-v.rs b/tests/ui-stable/ptr-is-invariant-over-v.rs new file mode 120000 index 0000000000..d29c80df7b --- /dev/null +++ b/tests/ui-stable/ptr-is-invariant-over-v.rs @@ -0,0 +1 @@ +../ui-nightly/ptr-is-invariant-over-v.rs \ No newline at end of file diff --git a/tests/ui-stable/ptr-is-invariant-over-v.stderr b/tests/ui-stable/ptr-is-invariant-over-v.stderr new file mode 100644 index 0000000000..c68334ffdf --- /dev/null +++ b/tests/ui-stable/ptr-is-invariant-over-v.stderr @@ -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 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 for more information about variance