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

E0493 emitted without drop when destructuring #86897

Open
CAD97 opened this issue Jul 5, 2021 · 3 comments
Open

E0493 emitted without drop when destructuring #86897

CAD97 opened this issue Jul 5, 2021 · 3 comments
Labels
A-const-fn Area: const fn foo(..) {..}. Pure functions which can be applied at compile time. A-destructors Area: Destructors (`Drop`, …) A-diagnostics Area: Messages for errors, warnings, and lints T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@CAD97
Copy link
Contributor

CAD97 commented Jul 5, 2021

Given the following code:

pub struct Wrap(String);

impl Wrap {
    pub const fn into_inner(self) -> String {
        self.0
    }
}

The current output is:

error[E0493]: destructors cannot be evaluated at compile-time
 --> src/lib.rs:4:29
  |
4 |     pub const fn into_inner(self) -> String {
  |                             ^^^^ constant functions cannot evaluate destructors
5 |         self.0
6 |     }
  |     - value is dropped here

This error is clearly wrong: self is not dropped, as it's been moved out of. In fact, no destructors are run, so this should be able to compile. If it shouldn't compile, the error should be changed to not be incorrect.

This seems to be an issue in the drop suppression when moving out field-wise; using e.g. mem::forget works to suppress the destructor and allow the function to compile. I was unable to trick the compiler to accept the function with ManuallyDrop, as extracting the wrapped value requires manifesting a wrapper again, which triggers E0493 "destructors cannot be evaluated at compile-time".

@rustbot label: +A-const-fn +A-destructors

@CAD97 CAD97 added A-diagnostics Area: Messages for errors, warnings, and lints T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Jul 5, 2021
@rustbot rustbot added A-const-fn Area: const fn foo(..) {..}. Pure functions which can be applied at compile time. A-destructors Area: Destructors (`Drop`, …) labels Jul 5, 2021
@ghost
Copy link

ghost commented Jul 6, 2021

I believe this is fixed by #![feature(const_precise_live_drops)].

@trentj
Copy link

trentj commented Jan 2, 2022

Does not seem to be fixed in all cases. While investigating a question raised on Discord:

const fn concat<T>(this: [T; 4], that: [T; 4]) -> [T; 8] {
    let [a, b, c, d] = this;
    let [e, f, g, h] = that;
    [a, b, c, d, e, f, g, h]
}

gives the bogus error for this and that despite const_precise_live_drops (playground).

Curiously, adding std::mem::forget(this); triggers use of partially moved value: `this` which suggests that this is considered only partially moved from for some reason. Perhaps this is a different problem; this is the nearest issue I could find.

@CAD97
Copy link
Contributor Author

CAD97 commented Jul 30, 2023

The OP example with just one field can now be straightforwardly worked around with transmute. The array case can currently be worked around with just #![feature(const_refs_to_cell)] and an ugly pile of repetitious unsafe: [playground]

pub const fn concat<T>(this: [T; 4], that: [T; 4]) -> [T; 8] {
    let this = ManuallyDrop::new(this);
    let that = ManuallyDrop::new(that);
    unsafe {
        let a = addr_of!(this).cast::<T>().add(0).read();
        let b = addr_of!(this).cast::<T>().add(1).read();
        let c = addr_of!(this).cast::<T>().add(2).read();
        let d = addr_of!(this).cast::<T>().add(3).read();
        let e = addr_of!(that).cast::<T>().add(0).read();
        let f = addr_of!(that).cast::<T>().add(1).read();
        let g = addr_of!(that).cast::<T>().add(2).read();
        let h = addr_of!(that).cast::<T>().add(3).read();
        [a, b, c, d, e, f, g, h]
    }
}

I believe that the proper fix should be along the lines of that a binding pattern like [a, b, c, d] gets identified as being complete — that it creates a binding covering every place in the scrutinee — and using the fact that the pattern is complete (and in the by-value mode) to mark the scrutinee place as fully moved from instead of only partially moved from.

Unfortunately, it gets more interesting when Copy typed subplaces are involved, since then the scrutinee is not fully moved from (Copy places are not invalidated when moved from). That's where const_precise_live_drops becomes necessary.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-const-fn Area: const fn foo(..) {..}. Pure functions which can be applied at compile time. A-destructors Area: Destructors (`Drop`, …) A-diagnostics Area: Messages for errors, warnings, and lints T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

3 participants