Skip to content

Trait object coercion has problems with contravariant structs and lifetime bounds #36667

Open
@amluto

Description

@amluto

I would expect this code to compile:

#![allow(dead_code)]
#![allow(unused_variables)]

struct Covariant<'a> { y: &'a i32 }
struct Contravariant<'a> { y: fn(&'a i32) }

trait HasLifetime<'a> : 'a {}
impl<'a, T> HasLifetime<'a> for T where T: 'a {}

fn check_lifetime_bound<'a, T: 'a>(x: &T) {}

fn check_contra_rightway<'a>(x: Contravariant<'a>) {
    let xref = &x;
    let xstatic: &Contravariant<'static> = &x;
    check_lifetime_bound::<'a>(&x);
    check_lifetime_bound::<'a>(xref);
    check_lifetime_bound::<'a>(xstatic);
    check_lifetime_bound::<'static>(&x);  // <-- this works, but...
    check_lifetime_bound::<'static>(xref);
    check_lifetime_bound::<'static>(xstatic);

    let a: &HasLifetime<'static> = &x;  // <-- this doesn't (BUG!)

    let a: &HasLifetime<'static> = xstatic;  // <-- nonetheless, this does work

    // NB: Substituting Any for HasLifetime<'static> produces identical
    // results.
}

fn check_co_wrongway<'a>(x: Covariant<'a>) {
    let xref = &x;
    // let xstatic: &Covariant<'static> = &x;  <-- correctly fails
    check_lifetime_bound::<'a>(&x);
    check_lifetime_bound::<'a>(xref);
    // check_lifetime_bound::<'static>(&x);  <-- correctly fails
    // check_lifetime_bound::<'static>(xref); <-- correctly fails

    let a: &HasLifetime<'a> = &x;
}

fn check_co_rightway<'a>(x: Covariant<'static>, _: &'a i32) {
    let xref = &x;
    let xa: &Covariant<'a> = &x;
    check_lifetime_bound::<'a>(&x);
    check_lifetime_bound::<'a>(xref);
    check_lifetime_bound::<'a>(xa);
    check_lifetime_bound::<'static>(&x);
    check_lifetime_bound::<'static>(xref);
    // check_lifetime_bound::<'static>(xa);  <-- this correctly fails
    let a: &HasLifetime<'a> = &x;
    let a: &HasLifetime<'a> = xref;
    let a: &HasLifetime<'a> = xa;

    // let bad: &HasLifetime<'static> = xa;  <-- correctly fails
}

fn main() {}

Using rustc 1.11.0 (9b21dcd6a 2016-08-15), I get:

ref_life.rs:22:36: 22:38 error: the type `Contravariant<'a>` does not fulfill the required lifetime [E0477]
ref_life.rs:22     let a: &HasLifetime<'static> = &x;  // <-- this doesn't (BUG!)
                                                  ^~
ref_life.rs:22:36: 22:38 note: type must outlive the static lifetime
ref_life.rs:22:36: 22:38 error: cannot infer an appropriate lifetime for lifetime parameter `'a` due to conflicting requirements [E0495]
ref_life.rs:22     let a: &HasLifetime<'static> = &x;  // <-- this doesn't (BUG!)
                                                  ^~
ref_life.rs:12:52: 28:2 note: first, the lifetime cannot outlive the lifetime 'a as defined on the block at 12:51...
ref_life.rs:12 fn check_contra_rightway<'a>(x: Contravariant<'a>) {
                                                                  ^
ref_life.rs:22:36: 22:38 note: ...so that the type `Contravariant<'a>` will meet its required lifetime bounds
ref_life.rs:22     let a: &HasLifetime<'static> = &x;  // <-- this doesn't (BUG!)
                                                  ^~
ref_life.rs:22:36: 22:38 note: but, the lifetime must be valid for the static lifetime...
ref_life.rs:22:36: 22:38 note: ...so that trait type parameters matches those specified on the impl (expected HasLifetime<'static>, found HasLifetime<'_>)
ref_life.rs:22     let a: &HasLifetime<'static> = &x;  // <-- this doesn't (BUG!)
                                                  ^~
error: aborting due to 2 previous errors

It seems that the rustc can match a covariant struct against a shorter lifetime bound when coercing to a trait object but that it can't match a contravariant struct against a longer timetime bound when coercing to a trait object, depite the fact that coercing directly works fine.

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-coercionsArea: implicit and explicit `expr as Type` coercionsA-dyn-traitArea: trait objects, vtable layoutA-lifetimesArea: Lifetimes / regionsA-varianceArea: Variance (https://doc.rust-lang.org/nomicon/subtyping.html)C-enhancementCategory: An issue proposing an enhancement or a PR with one.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.T-typesRelevant to the types 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