Skip to content

lifetime variables in type bounds not properly enforced #12857

Closed

Description

@dwrensha

Please see the MessageReader::get_root function below and the call to it in main(). It seems that 'a is not properly constrained.

This is related to #5121 and #12807.

// lifetimes.rs


// Like a slice.
pub struct StructReader<'a> {
    buf : *u8,
    len : uint,
    marker : std::kinds::marker::ContravariantLifetime<'a>,
}

// Something that can be constructed from a StructReader<'a>,
// and will have the same lifetime 'a.
pub trait FromStructReader<'a> {
    fn new(struct_reader : StructReader<'a>) -> Self;
}

pub struct MessageReader {
    buf : ~[u8],
}

impl MessageReader {

    // borrow `self` with lifetime `'a`, and construct a `T` that lives no longer than `'a`.
    pub fn get_root<'a, T : FromStructReader<'a>>(&'a self) -> T {
        FromStructReader::<'a>::new(
            StructReader {buf : self.buf.as_ptr(),
                          len : self.buf.len(),
                          marker : std::kinds::marker::ContravariantLifetime::<'a>,
            })
    }
}

pub struct FooReader<'a> {
    reader : StructReader<'a>
}

impl <'a> FromStructReader<'a> for FooReader<'a> {
    fn new(struct_reader : StructReader<'a>) -> FooReader<'a> {
        FooReader { reader : struct_reader}
    }
}

impl <'a> FooReader<'a> {
    pub fn get(&self, idx : uint) -> u8 {
        assert!(idx < self.reader.len);
        unsafe {
            *self.reader.buf.offset(idx as int)
        }
    }

}

pub fn main () {
    let bar = {
        let message = MessageReader {buf : ~[1,2,3,4,5]};
        let foo : FooReader = message.get_root();
        foo
    }; // this shouldn't typecheck, because `foo` cannot outlive `message`

    let _x : ~[u8] = ~[9,9,9,9,9,9,9,9,9,9,9,9];

    println!("bar.get(0) = {}", bar.get(0));
}

I expect this program not to typecheck, because it allows a FooReader to live longer than the StructReader it was constructed from.

However, we get:

$ rustc lifetimes.rs 
$ ./lifetimes 
bar.get(0) = 9

Activity

  1. dwrensha commented on Mar 13, 2014

    @dwrensha
    ContributorAuthor
  2. eddyb commented on Mar 13, 2014

    @eddyb
    Contributor

    I can't see how you're enforcing the lifetime of anything there. You just have things generic over a lifetime without a mention of the lifetime in the return type (in the trait definition, that is).

    You should also check ContravariantLifetime<'a> in generic contexts and replacing it with a non-marker that's contravariant (like &'a T).

    IMO that trait should be HKT over a lifetime for this to work at all.

  3. dwrensha commented on Mar 13, 2014

    @dwrensha
    ContributorAuthor

    @eddyb I see the same problem if I use a slice instead of a marker type:

    // lifetimes.rs
    
    pub trait FromSlice<'a> {
        fn new(struct_reader : &'a [u8]) -> Self;
    }
    
    pub struct MessageReader {
        buf : ~[u8],
    }
    
    impl MessageReader {
    
        pub fn get_root<'a, T : FromSlice<'a>>(&'a self) -> T {
            FromSlice::<'a>::new(self.buf.as_slice())
        }
    
    }
    
    pub struct FooReader<'a> {
        reader : &'a [u8]
    }
    
    impl <'a> FromSlice<'a> for FooReader<'a> {
        fn new(reader : &'a [u8]) -> FooReader<'a> {
            FooReader { reader : reader}
        }
    }
    
    pub fn main () {
        let bar = {
            let message = MessageReader {buf : ~[1,2,3,4,5]};
            let foo : FooReader = message.get_root();
            foo
        }; // this shouldn't typecheck, because `foo` cannot outlive `message`
    
        let _x : ~[u8] = ~[9,9,9,9,9,9,9,9,9,9,9,9];
    
        println!("bar = {:?}", bar);
    }
    
    $ rustc lifetimes.rs 
    $ ./lifetimes
    bar = FooReader<>{reader: &[9u8, 9u8, 9u8, 9u8, 9u8]}
    
  4. dwrensha commented on Mar 13, 2014

    @dwrensha
    ContributorAuthor

    I don't think this example should require higher kinded types. The trait in question is:

    pub trait FromSlice<'a> {
        fn new(struct_reader : &'a [u8]) -> Self;
    }
    

    Here, Self refers to the type that implements FromSlice<'a>. In particular, that type can refer to 'a, as FooReader<'a> does.

  5. dwrensha commented on Mar 13, 2014

    @dwrensha
    ContributorAuthor

    Maybe @nikomatsakis can say if I'm mistaken on this point?

  6. nikomatsakis commented on Mar 18, 2014

    @nikomatsakis
    Contributor

    This does look like a bug.

  7. nikomatsakis commented on Mar 18, 2014

    @nikomatsakis
    Contributor

    Sorry it took me 5 days to read into it. What I expect to happen is that the region rules require that the lifetime of bar be equal to the enclosing block, which should lead to an error somewhere since foo cannot have such a long lifetime. Might be related to #5121, might not.

  8. pnkfelix commented on Mar 23, 2014

    @pnkfelix
    Contributor

    I'm looking at this problem now. In particular the second version that avoids using any native pointers.

  9. pnkfelix commented on Mar 23, 2014

    @pnkfelix
    Contributor

    A new datapoint: the use of a trait method seems to be necessary to expose the problem here:

    pub trait FromSlice<'a> { fn new(s : &'a [u8]) -> Self; }
    
    struct Hide<'a> { slice : &'a [u8] }
    impl<'a> Hide<'a> { fn new(s: &'a [u8]) -> Hide<'a> { Hide { slice: s } } }
    impl<'a> FromSlice<'a> for Hide<'a> {
        fn new(s: &'a [u8]) -> Hide<'a> { Hide::new(s) }
    }
    
    #[cfg(hide_works)]
    pub fn main () {
        let bar : Hide = {
            let foo = ~[1, 2, 3, 4, 5];
            FromSlice::new(foo.as_slice())
        }; // somehow sidesteps typecheck
    
        let _x : ~[u8] = ~[9,9,9,9,9,9,9,9,9,9,9,9];
    
        println!("bar.slice = {}, should reject (was [1, 2, 3, 4, 5])", bar.slice);
    }
    
    #[cfg(hide_fails)]
    pub fn main () {
        let bar : Hide = {
            let foo = ~[1, 2, 3, 4, 5];
            Hide::new(foo.as_slice())
        }; // does not typecheck: `foo` does not live long enough
    
        let _x : ~[u8] = ~[9,9,9,9,9,9,9,9,9,9,9,9];
    
        println!("bar.slice = {}, should reject (was [1, 2, 3, 4, 5])", bar.slice);
    }
    % rustc --cfg hide_fails iss12857-c.rs
    iss12857-c.rs:25:19: 25:22 error: `foo` does not live long enough
    iss12857-c.rs:25         Hide::new(foo.as_slice())
                                       ^~~
    iss12857-c.rs:22:16: 31:2 note: reference must be valid for the block at 22:15...
    iss12857-c.rs:22 pub fn main () {
    iss12857-c.rs:23     let bar : Hide = {
    iss12857-c.rs:24         let foo = ~[1, 2, 3, 4, 5];
    iss12857-c.rs:25         Hide::new(foo.as_slice())
    iss12857-c.rs:26     }; // does not typecheck: `foo` does not live long enough
    iss12857-c.rs:27 
                     ...
    iss12857-c.rs:23:22: 26:6 note: ...but borrowed value is only valid for the block at 23:21
    iss12857-c.rs:23     let bar : Hide = {
    iss12857-c.rs:24         let foo = ~[1, 2, 3, 4, 5];
    iss12857-c.rs:25         Hide::new(foo.as_slice())
    iss12857-c.rs:26     }; // does not typecheck: `foo` does not live long enough
    error: aborting due to previous error
    % rustc --cfg hide_works iss12857-c.rs
    % ./iss12857-c
    bar.slice = [9, 9, 9, 9, 9], should reject (was [1, 2, 3, 4, 5])
    % 
    
  10. dwrensha commented on Jun 14, 2014

    @dwrensha
    ContributorAuthor

    Here is an updated test case.

    // lifetimes.rs
    
    extern crate debug;
    
    pub trait FromSlice<'a> {
        fn new(struct_reader : &'a [u8]) -> Self;
    }
    
    pub struct MessageReader {
        buf : Vec<u8>,
    }
    
    impl MessageReader {
        pub fn get_root<'a, T : FromSlice<'a>>(&'a self) -> T {
            FromSlice::<'a>::new(self.buf.as_slice())
        }
    }
    
    pub struct FooReader<'a> {
        reader : &'a [u8]
    }
    
    impl <'a> FromSlice<'a> for FooReader<'a> {
        fn new(reader : &'a [u8]) -> FooReader<'a> {
            FooReader { reader : reader}
        }
    }
    
    pub fn main () {
        let bar = {
            let message = MessageReader {buf : vec!(1,2,3,4,5)};
            let foo : FooReader = message.get_root();
            foo
        }; // this shouldn't typecheck, because `foo` cannot outlive `message`
    
        // prints "bar = FooReader<'_>{reader: &[96u8, 16u8, 129u8, 7u8, 1u8]}"
        println!("bar = {:?}", bar);
    }
    
  11. pcwalton commented on Jul 26, 2014

    @pcwalton
    Contributor

    In HEAD I get error: message does not live long enough. Closing. Please reopen if this is still an issue.

  12. added 3 commits that reference this issue on Jul 26, 2022
    77acb5c
    7e2b983
    75bbbd5
  13. added a commit that references this issue on Jun 13, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-lifetimesArea: Lifetimes / regions

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions