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 variables in type bounds not properly enforced #12857

Closed
dwrensha opened this issue Mar 13, 2014 · 11 comments
Closed

lifetime variables in type bounds not properly enforced #12857

dwrensha opened this issue Mar 13, 2014 · 11 comments
Labels
A-lifetimes Area: Lifetimes / regions

Comments

@dwrensha
Copy link
Contributor

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
@dwrensha
Copy link
Contributor Author

cc @pnkfelix

@eddyb
Copy link
Member

eddyb commented Mar 13, 2014

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.

@dwrensha
Copy link
Contributor Author

@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]}

@dwrensha
Copy link
Contributor Author

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.

@dwrensha
Copy link
Contributor Author

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

@nikomatsakis
Copy link
Contributor

This does look like a bug.

@nikomatsakis
Copy link
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.

@pnkfelix
Copy link
Member

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

@pnkfelix
Copy link
Member

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])
% 

@dwrensha
Copy link
Contributor Author

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);
}

@pcwalton
Copy link
Contributor

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

fasterthanlime pushed a commit to fasterthanlime/rust that referenced this issue Jul 26, 2022
fasterthanlime pushed a commit to fasterthanlime/rust that referenced this issue Jul 26, 2022
…, r=DorianListens

fix: Autocomplete for struct fields includes receiver

fixes rust-lang#12857
fasterthanlime pushed a commit to fasterthanlime/rust that referenced this issue Jul 26, 2022
flip1995 pushed a commit to flip1995/rust that referenced this issue Jun 13, 2024
fix: let non_canonical_impls skip proc marco

Fixed rust-lang#12788

Although the issue only mentions `NON_CANONICAL_CLONE_IMPL`, this fix will also affect `NON_CANONICAL_PARTIAL_ORD_IMPL` because I saw
> Because of these unforeseeable or unstable behaviors, macro expansion should often not be regarded as a part of the stable API.

on Clippy Documentation and these two lints are similar, so I think it might be good, not sure if it's right or not.

---

changelog: `NON_CANONICAL_CLONE_IMPL`, `NON_CANONICAL_PARTIAL_ORD_IMPL` will skip proc marco now
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-lifetimes Area: Lifetimes / regions
Projects
None yet
Development

No branches or pull requests

5 participants