Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Potentially false positive of "lifetime may not live long enough" after introduction of Sized bound on associated type #137184

Open
yannham opened this issue Feb 17, 2025 · 4 comments
Labels
A-lifetimes Area: Lifetimes / regions A-trait-system Area: Trait system C-bug Category: This is a bug. T-lang Relevant to the language team, which will review and decide on the PR/issue.

Comments

@yannham
Copy link

yannham commented Feb 17, 2025

I tried this code:

use std::marker::PhantomData;

/// Some data. In real life, imagine that its content is arena-allocated with lifetime `'a`.
struct Data<'a> {
    _marker: PhantomData<&'a ()>,
}

struct DataAlloc;

/// Data with borrowed content that can be copied from one location to another.
trait CopyTo {
    /// This is always `Self`, be we need associated types to make Rust understand that `Self` is
    /// actually parametric over some lifetime.
    type Data<'a>;

    /// Copy data from the current allocator to `dest`.
    fn copy_to<'from, 'to>(data: Self::Data<'from>, dest: &'to DataAlloc) -> Self::Data<'to>;
}

impl CopyTo for Data<'_> {
    type Data<'a> = Data<'a>;

    fn copy_to<'from, 'to>(data: Data<'from>, _dest: &'to DataAlloc) -> Data<'to> {
        Data {
            _marker: PhantomData,
        }
    }
}

impl DataAlloc {
    pub fn copy<'from, 'to, T: CopyTo>(&'to self, data: T::Data<'from>) -> T::Data<'to>
    where
        T: CopyTo<Data<'to>: Sized>,
    {
        T::copy_to(data, self)
    }
}

I expected to see this happen: this code should compile. In fact, if I remove the where T: CopyTo<Data<'to>: Sized> clause, which isn't used at all, this code compiles.

Instead, this happened: I get a lifetime may not live long enough error, with the note "Proving this value is Sized requires to to must outlive 'from" as well as the dual message:

error: lifetime may not live long enough
  --> src/lib.rs:35:20
   |
31 |     pub fn copy<'from, 'to, T: CopyTo>(&'to self, data: T::Data<'from>) -> T::Data<'to>
   |                 -----  --- lifetime `'to` defined here
   |                 |
   |                 lifetime `'from` defined here
...
35 |         T::copy_to(data, self)
   |                    ^^^^ proving this value is `Sized` requires that `'to` must outlive `'from`
   |
   = help: consider adding the following bound: `'to: 'from`

error: lifetime may not live long enough
  --> src/lib.rs:35:20
   |
31 |     pub fn copy<'from, 'to, T: CopyTo>(&'to self, data: T::Data<'from>) -> T::Data<'to>
   |                 -----  --- lifetime `'to` defined here
   |                 |
   |                 lifetime `'from` defined here
...
35 |         T::copy_to(data, self)
   |                    ^^^^ proving this value is `Sized` requires that `'from` must outlive `'to`
   |
   = help: consider adding the following bound: `'from: 'to`

Context

This is a simplified version of a real use-case where I have arena-allocated data and I want a trait that implements moving them from one arena to another with a different lifetime. Since Rust doesn't support general higher-kinded types, I use GATs as a poor man's HKT, which requires more annotations but it works ok. However, when I try to implement a variant of copy where I need additional bounds on the associated type, I started to get strange lifetime errors. What's surprising here is that the trait bound is unrelated to the body of the function and never used. It's surprising that adding a Sized bound adds any lifetime constraints at all, unless I'm missing something obvious.

Potential duplicate of #108345, but I wanted to make sure it's not a different error first.

Meta

rustc --version --verbose:

rustc 1.84.1 (e71f9a9a9 2025-01-27)
binary: rustc
commit-hash: e71f9a9a98b0faf423844bf0ba7438f29dc27d58
commit-date: 2025-01-27
host: x86_64-unknown-linux-gnu
release: 1.84.1
LLVM version: 19.1.5
@yannham yannham added the C-bug Category: This is a bug. label Feb 17, 2025
@rustbot rustbot added the needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. label Feb 17, 2025
@theemathas
Copy link
Contributor

theemathas commented Feb 18, 2025

Minimized:

pub trait CopyTo {
    type Data<'a>;
}

pub fn copy<'to, T: CopyTo + 'static>(data: T::Data<'static>)
where
    // deleting this line makes the code compile
    T: CopyTo<Data<'to>: Sized>,
{
    let _data = data;
}

Error output:

error: lifetime may not live long enough
  --> src/lib.rs:10:17
   |
5  | pub fn copy<'to, T: CopyTo + 'static>(data: T::Data<'static>)
   |             --- lifetime `'to` defined here
...
10 |     let _data = data;
   |                 ^^^^ proving this value is `Sized` requires that `'to` must outlive `'static`

@theemathas
Copy link
Contributor

Seems similar to #21974

@theemathas
Copy link
Contributor

As a workaround, you can change T: CopyTo<Data<'to>: Sized>, to T: for<'a> CopyTo<Data<'a>: Sized>, if it works for your use case.

@yannham
Copy link
Author

yannham commented Feb 18, 2025

As a workaround, you can change T: CopyTo<Data<'to>: Sized>, to T: for<'a> CopyTo<Data<'a>: Sized>, if it works for your use case.

Oh, I have quantified lifetimes like this for functions before, but I didn't realize it works for arbitrary trait constraints. It did the trick, thanks!

@lolbinarycat lolbinarycat added A-lifetimes Area: Lifetimes / regions A-trait-system Area: Trait system T-lang Relevant to the language team, which will review and decide on the PR/issue. labels Feb 20, 2025
@Noratrieb Noratrieb removed the needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. label Mar 1, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-lifetimes Area: Lifetimes / regions A-trait-system Area: Trait system C-bug Category: This is a bug. T-lang Relevant to the language team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

5 participants