Skip to content

Misleading error with Drop and lifetime parameters #17858

Closed

Description

@bnoordhuis
struct S<'a>(&'a T);

struct T;

impl<'a> Drop for S<'a> {
    fn drop(&mut self) {}
}

fn main() {
    let _ = S(&T);
}
$ rustc -v
rustc 0.12.0-nightly (b5ba2f551 2014-10-06 20:27:14 +0000)

$ rustc dtor.rs 
dtor.rs:5:1: 7:2 error: cannot implement a destructor on a structure with type parameters [E0141]
dtor.rs:5 impl<'a> Drop for S<'a> {
dtor.rs:6     fn drop(&mut self) {}
dtor.rs:7 }
dtor.rs:5:1: 7:2 note: use "#[unsafe_destructor]" on the implementation to force the compiler to allow this
dtor.rs:5 impl<'a> Drop for S<'a> {
dtor.rs:6     fn drop(&mut self) {}
dtor.rs:7 }
error: aborting due to previous error

It's not clear to me why this program gets rejected.

Activity

  1. alexcrichton commented on Oct 7, 2014

    @alexcrichton
    Member

    I think the error message may just be a little misleading, if you opt into unsafe_destructor it should compile though

    #![feature(unsafe_destructor)]
    
    struct S<'a>(&'a T);
    
    struct T;
    
    #[unsafe_destructor]
    impl<'a> Drop for S<'a> {
        fn drop(&mut self) {}
    }
    
    fn main() {
        let _ = S(&T);
    }
  2. bnoordhuis commented on Oct 7, 2014

    @bnoordhuis
    ContributorAuthor

    It does but I don't quite understand why the compiler rejects the program. Does the presence of the lifetime make it unsound?

  3. alexcrichton commented on Oct 7, 2014

    @alexcrichton
    Member

    Right now the compiler's handling of lifetimes and destructors aren't quite sound in all cases. For example, in your example, it should be required that the destructor of S run before the destructor to T (otherwise S may see uninitialized memory).

    Whereas, in this example (which compiles today), the destructor for T runs first:

    #![feature(unsafe_destructor)]
    
    struct S<'a>(&'a T);
    
    struct T;
    
    impl<'a> S<'a> {
        fn foo(&self) {}
    }
    
    #[unsafe_destructor]
    impl<'a> Drop for S<'a> {
        fn drop(&mut self) {
            println!("dropping S");
        }
    }
    
    impl Drop for T {
        fn drop(&mut self) {
            println!("dropping T");
        }
    }
    
    fn main() {
        {
            println!("before!");
            let t = T;
            S(&t).foo()
        };
        println!("after!");
    
    }
    $ rustc -v
    rustc 0.12.0-nightly (b5ba2f551 2014-10-06 20:27:14 +0000)
    $ rustc foo.rs 
    $ ./foo 
    before!
    dropping T
    dropping S
    after!
    
  4. alexcrichton commented on Oct 7, 2014

    @alexcrichton
    Member

    And for a real world use case, you can see my comment which uses a standard RefCell to generate unsafe reads and writes.

  5. bnoordhuis commented on Oct 8, 2014

    @bnoordhuis
    ContributorAuthor

    Thanks Alex, that makes perfect sense. Should I close the issue?

  6. changed the title [-]impl Drop disallowed with E0141 for types with lifetimes[/-] [+]Misleading error with Drop and lifetime parameters[/+] on Oct 8, 2014
  7. alexcrichton commented on Oct 8, 2014

    @alexcrichton
    Member

    I think the error message could still use some improvement (mention lifetime parameters, for example), so I'm ok leaving this open to track that.

  8. added
    A-diagnosticsArea: Messages for errors, warnings, and lints
    on Oct 8, 2014
  9. Byron commented on Feb 9, 2015

    @Byron
    Member

    There is something I don't understand about the example though: We explicitly specify that the lifetime of the &T must match the one of the S instance which contains it, and as Rust assures that, the destructor should be safe. After all, we specified that the reference to T needs to live at least as long as S. It seems the actual problem is that we didn't actually express that instance of T shall outlive instance of S - as far as Rust is concerned, it only knows T must live as long as S, which might make the destructor a special case, as technically, an instance of S is about to decease.

    After all, it appears there might be a problem with either expressiveness of lifetimes, or with the way destructors are handled, which is exactly what you said :) : "Right now the compiler's handling of lifetimes and destructors aren't quite sound in all cases.". Good I arrived at the same conclusion.

    Another concern of mine is also stated in #11406/#8861: there may be an inflationary use of #![feature(unsafe_destructor)] of people who don't really understand the implications (which includes me, usually). Therefore I hope this will be fixed until 1.0 is released to prevent unsafe_destructors to pop up everywhere.

  10. pnkfelix commented on Feb 9, 2015

    @pnkfelix
    Contributor

    @Byron this is exactly the topic of rust-lang/rfcs#769 , which has a prototype implementation (see #21972) essentially ready to land (and should land for 1.0), and that should remove the unsoundness discussed here.

  11. nham commented on May 12, 2015

    @nham
    Contributor

    I suspect this issue can be closed. Error E0141 is no longer in the code base, and the example code now produces this error:

    E0141.rs:10:16: 10:17 error: borrowed value does not live long enough
    E0141.rs:10     let _ = S(&T);
                               ^
    E0141.rs:10:5: 10:19 note: reference must be valid for the destruction scope surrounding statement at 10:4...
    E0141.rs:10     let _ = S(&T);
                    ^~~~~~~~~~~~~~
    E0141.rs:10:5: 10:19 note: ...but borrowed value is only valid for the statement at 10:4
    E0141.rs:10     let _ = S(&T);
                    ^~~~~~~~~~~~~~
    E0141.rs:10:5: 10:19 help: consider using a `let` binding to increase its lifetime
    E0141.rs:10     let _ = S(&T);
                    ^~~~~~~~~~~~~~
    error: aborting due to previous error
    

    (rustc 1.1.0-nightly (dc630d01e 2015-05-09) (built 2015-05-10))

  12. self-assigned this
    on May 12, 2015
  13. steveklabnik commented on Nov 7, 2015

    @steveklabnik
    Contributor

    Agree with @nham

  14. added a commit that references this issue on Sep 25, 2024
    51cc5a7
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

A-diagnosticsArea: Messages for errors, warnings, and lints

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions