Skip to content

lifetime diagnostic reports lifetimes backwards with invariance or contravariance #108636

Open
@QuineDot

Description

@QuineDot

Code

trait Slice {
    type S<'a>: Copy
    where
        Self: 'a;
}

struct Foo<'a, L: Slice + 'a> {
    foo: L::S<'a>,
}

impl<'a, L: Slice + 'a> Foo<'a, L> {
    fn foo(&self) -> L::S<'_> {
        self.foo
    }
}

Current output

error: lifetime may not live long enough
  --> src/lib.rs:13:9
   |
11 | impl<'a, L: Slice + 'a> Foo<'a, L> {
   |      -- lifetime `'a` defined here
12 |     fn foo(&self) -> L::S<'_> {
   |            - let's call the lifetime of this reference `'1`
13 |         self.foo
   |         ^^^^^^^^ associated function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'1`

Desired output

error: GAT parameters are invariant
  --> src/lib.rs:13:9
   |
11 | impl<'a, L: Slice + 'a> Foo<'a, L> {
   |      -- lifetime `'a` defined here
12 |     fn foo(&self) -> L::S<'_> {
   |            - let's call the lifetime of this reference `'1`
13 |         self.foo
   |         ^^^^^^^^ associated function was supposed to return data with lifetime `'1` but it is returning data with lifetime `'a`

error: could not compile `playground` due to previous error

Rationale and extra context

The current error seems to assume a typical covariant-situation error where one is trying to return a long-lived value from behind a short-lived reference, and says that:

lifetime may not live long enough

associated function was supposed to return data with lifetime 'a but it is returning data with lifetime '1

But actually the opposite is true: the function was supposed to return data with lifetime '1 but is returning data with lifetime 'a because the GAT in question is invariant in 'a. You could say that '1 doesn't live long enough, but in my opinion it would be better to say that 'a is invariant (and thus doesn't "live short enough").

Witnessed in this URLO topic by nnotaka.

Other cases

// This one gives better diagnostics w.r.t. variance but
// still gets the "supposed to return" lifetimes backwards 
struct Foo2<'a> {
    foo: *mut &'a str,
}

impl<'a> Foo2<'a> {
    fn foo(&self) -> *mut &str {
        self.foo
    }
}

// This contravariant case suggests a bound which is
// effectively invariant, but still gets the lifetimes
// backwards
struct Foo3<'a> {
    foo: &'a dyn Fn(&'a str),
}

impl<'a> Foo3<'a> {
    fn foo<'b>(&'b self) -> &'b dyn Fn(&'b str) {
        self.foo
    }
}

Anything else?

  • Main playground
  • *mut playground
    error: lifetime may not live long enough
     --> src/lib.rs:7:9
      |
    5 | impl<'a> Foo2<'a> {
      |      -- lifetime `'a` defined here
    6 |     fn foo(&self) -> *mut &str {
      |            - let's call the lifetime of this reference `'1`
    7 |         self.foo
      |         ^^^^^^^^ associated function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'1`
      |
      = note: requirement occurs because of a mutable pointer to `&str`
      = note: mutable pointers are invariant over their type parameter
      = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
  • dyn Fn playground
    error: lifetime may not live long enough
     --> src/lib.rs:7:9
      |
    5 | impl<'a> Foo3<'a> {
      |      -- lifetime `'a` defined here
    6 |     fn foo<'b>(&'b self) -> &'b dyn Fn(&'b str) {
      |            -- lifetime `'b` defined here
    7 |         self.foo
      |         ^^^^^^^^ associated function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'b`
      |
      = help: consider adding the following bound: `'b: 'a`
    
  • Covariant case where the "return data" lifetimes are correct
    error: lifetime may not live long enough
     --> src/lib.rs:7:9
      |
    5 | impl<'a> Foo4<'a> {
      |      -- lifetime `'a` defined here
    6 |     fn foo(&self) -> &'a str {
      |            - let's call the lifetime of this reference `'1`
    7 |         self.foo
      |         ^^^^^^^^ associated function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'1`
    

@rustbot label +A-lifetimes +A-variance

Metadata

Metadata

Assignees

Labels

A-NLLArea: Non-lexical lifetimes (NLL)A-borrow-checkerArea: The borrow checkerA-diagnosticsArea: Messages for errors, warnings, and lintsA-lifetimesArea: Lifetimes / regionsA-varianceArea: Variance (https://doc.rust-lang.org/nomicon/subtyping.html)D-incorrectDiagnostics: A diagnostic that is giving misleading or incorrect information.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions