Skip to content

RPIT lifetime bound imprecision without use<..> bound #1175

@J3m3

Description

@J3m3

This issue is meant to document the known imprecision for potential future cleanup.

Description

We currently have a few functions returning RPIT (return-position impl Trait) abstract (or opaque) types with + 'a bound. While the intended lifetime relationship is that the abstract type lives at most as long as 'a, the signature actually implies the opposite: the abstract type outlives 'a.

To elaborate, one concrete example is XArray's iter implementation:

linux/rust/kernel/xarray.rs

Lines 104 to 118 in 0303584

fn iter(&self) -> impl Iterator<Item = NonNull<T::PointedTo>> + '_ {
let mut index = 0;
// SAFETY: `self.xa` is always valid by the type invariant.
iter::once(unsafe {
bindings::xa_find(self.xa.get(), &mut index, usize::MAX, bindings::XA_PRESENT)
})
.chain(iter::from_fn(move || {
// SAFETY: `self.xa` is always valid by the type invariant.
Some(unsafe {
bindings::xa_find_after(self.xa.get(), &mut index, usize::MAX, bindings::XA_PRESENT)
})
}))
.map_while(|ptr| NonNull::new(ptr.cast()))
}

Here, the return type uses + '_, which promises that the RPIT abstract type outlives the lifetime of &self. However, this is not semantically accurate since the concrete iterator type returned should not outlive the lifetime of &self to which it refers. The reason it works in this specific case is that the lifetime of the returned abstract type happens to be exactly equal to the lifetime of &self.

Such mismatches could be correctly expressed using use<..> bounds. For the XArray case, the signature could eventually become:

fn iter(&self) -> impl Iterator<Item = NonNull<T::PointedTo>> + use<'_, T> {
    // ...
}

Unfortunately, use<..> bounds are only available starting with Rust 1.82, while the current MSRV for Rust-for-Linux is 1.78.0. As a result, this fix cannot be adopted yet.

No immediate action is required, but these cases may be revisited once the MSRV is updated to Rust 1.82.

Remaining cases

  • impl WaitEntry {
    #[inline]
    fn insert_new(list: &ListHead) -> impl PinInit<Self> + '_ {
    pin_init!(Self {
    thread: thread::current(),
    wait_list <- ListHead::insert_prev(list),
    })
    }
    }
  • #[inline]
    pub fn insert_next(list: &ListHead) -> impl PinInit<Self, Infallible> + '_ {
    try_pin_init!(&this in Self {
    prev: list.next.prev().replace(unsafe { Link::new_unchecked(this)}),
    next: list.next.replace(unsafe { Link::new_unchecked(this)}),
    pin: PhantomPinned,
    }? Infallible)
    }
    #[inline]
    pub fn insert_prev(list: &ListHead) -> impl PinInit<Self, Infallible> + '_ {
    try_pin_init!(&this in Self {
    next: list.prev.next().replace(unsafe { Link::new_unchecked(this)}),
    prev: list.prev.replace(unsafe { Link::new_unchecked(this)}),
    pin: PhantomPinned,
    }? Infallible)
    }

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    • libRelated to the `rust/` library.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions