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

Lifetime checker only checks the signature of a type and not its content #35852

Closed
tomaka opened this issue Aug 20, 2016 · 9 comments
Closed

Comments

@tomaka
Copy link
Contributor

tomaka commented Aug 20, 2016

trait Foo {
    type AssociatedType;

    fn build_associated_type(&self) -> Self::AssociatedType;

    fn through_carrier(self) -> Carrier<Self> where Self: Sized {
        let at = self.build_associated_type();
        Carrier { inner: at }
    }
}


struct FooImpl<'a> { inside: &'a u32 }

impl<'a> Foo for FooImpl<'a> {
    type AssociatedType = u32;
    fn build_associated_type(&self) -> u32 { *self.inside }
}


struct Carrier<T: Foo> { inner: T::AssociatedType }



fn must_be_static<T: 'static>(_: T) {}


fn main() {
    let data = 3;
    let foo_impl = FooImpl { inside: &data };
    let carrier = foo_impl.through_carrier();
    must_be_static(carrier);
}

This fails to compile at the call to must_be_static because: data does not live long enough, reference must be valid for the static lifetime.

When we call must_be_static(carrier), the type of carrier is Carrier<FooImpl<'a>>. Rustc presumably sees that there's a 'a and fails the compilation, since the parameter of must_be_static must be static.

However the inside of Carrier<FooImpl<'a>> is actually just a u32, which is static. Even though the type signature contains a lifetime, the actual content of the struct does not. Therefore this code should in theory compile.

@tomaka
Copy link
Contributor Author

tomaka commented Aug 20, 2016

Note that when HKLs/HKTs are implemented, this could eventually cause a soundness problem if left unfixed, as we will be able to add non-static associated type on static structs.

@eddyb
Copy link
Member

eddyb commented Aug 20, 2016

cc @nikomatsakis Would lazy normalization result in <FooImpl<'a> as Foo>::AssociatedType: 'static properly ignoring 'a by resolving to u32: 'static, or is this an orthogonal feature/fix?

@arielb1
Copy link
Contributor

arielb1 commented Aug 20, 2016

carrier is of type Carrier<FooImpl<'a>>, where 'a is the lifetime of the borrow, just as through you had

fn example<'a>(data: &'a u32) {
    let foo_impl = FooImpl { inside: &data };
    let carrier: Carrier<FooImpl<'a>> = foo_impl.through_carrier();
    must_be_static(carrier);
}

The struct can't outlive its type parameter. I don't see the bug here.

@eddyb
Copy link
Member

eddyb commented Aug 20, 2016

@arielb1 Is only variance of parameters taken into account for Struct<T>: 'a elaboration?

@arielb1
Copy link
Contributor

arielb1 commented Aug 20, 2016

According to RFC 1214, a struct like Carrier<FooImpl<'a>> outlives 'static when all of its components outlive 'static, so when FooImpl<'a>: 'static and 'a: 'static.

@arielb1 arielb1 closed this as completed Aug 20, 2016
@eddyb
Copy link
Member

eddyb commented Aug 20, 2016

@arielb1 And "components" refers to "type and lifetime parameters, according to their variance"?
As opposed to "field types", I mean.

@arielb1
Copy link
Contributor

arielb1 commented Aug 20, 2016

The rule is

OutlivesNominalType:
  ∀i. R ⊢ Pi: 'a
  --------------------------------------------------
  R ⊢ Id<P0..Pn>: 'a

It requires all type parameters and all lifetime parameters to outlive 'static.

Also, the type parameter of Carrier is invariant, because struct Carrier projects out of it as an associated type (and variance is per-type-parameter).

@nikomatsakis
Copy link
Contributor

As @arielb1 notes, this is very much by design (it is kind of crucial to having a usable outlives relation for projection types).

@nikomatsakis
Copy link
Contributor

That said, I do find some cases of errors unfortunate. It may be that we can evolve more here, though I'm not quire sure how (in some ways, I worry we are not always strict enough, as well, as it makes me uncomfortable to have lifetimes in types that can ever be "out of scope", and I think that may be possible in some particular scenarios).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants