You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Can't implement a const Option::unwrap because of "destructors cannot be evaluated at compile-time" error. #66753
#![feature(const_fn)]#![feature(const_if_match)]#![feature(const_panic)]constfnunwrap<T>(opt:Option<T>)->T{match opt {Some(x)=>x,None=>panic!("Trying to unwrap a None"),}}
Because the function triggers this error:
error[E0493]: destructors cannot be evaluated at compile-time
--> src/lib.rs:5:20
|
5 | const fn unwrap<T>(opt:Option<T>)->T{
| ^^^ constant functions cannot evaluate destructors
error: aborting due to previous error
Reacted by Felipe S. S. Schneider
Reacted by Felipe S. S. Schneider
Reacted by Dylan MacKenzie, Michael Pfaff and Felipe S. S. Schneider
Reacted by Felipe S. S. Schneider
Reacted by Felipe S. S. Schneider, Andrii Savytskyi and orzogc
This was discussed here in the initial PR for #![feature(const_if_match)]. Basically, we don't clear qualifs from a local (opt in this case) if we move out of the only field of that local. I still think we want to special-case unit structs/enum variants because it will cover commonly used types like Option and Result. Alternatively, we could maybe suppress Drop terminators entirely when a local is moved out of completely, or if this is already implemented, run const checks after this pass occurs.
Alternatively, we could maybe suppress Drop terminators entirely when a local is moved out of completely, or if this is already implemented, run const checks after this pass occurs.
So... we have three situations afaict:
struct without explicit Drop impl, a !Copy field is moved out of (this is the (Vec<T>,) case)
!Copy field of enum variant is moved out of (this is the Some case)
So... if even drop elaboration can't figure this out, I'm not sure const qualif should be finding an independent solution.
Several solutions come to mind:
Introduce a Forget terminator in the spirit of
mem::drop == Drop terminator
hint::unreachable == Unreachable terminator
process::abort == Abort terminator
mem::forget == Forget terminator
Add an "early drop elab" pass that detects when you match on an enum and move out in one variant and then automatically treats the other variants as moved out
seems dangerous to me, if we generate similar code out of other reasons, the optimization would trigger. Also affects borrowck
Change mir building to not generate drops in the None-like cases
Undesirable because we don't want mir building to be complex
the None case. Even after drop elaboriation, we still execute Drop::drop for the enum in the None case.
I think there's something missing from drop elaboration's (and other analyses'?) dataflow usage:
Each edge of a SwitchInt(Discriminant(x)) can assume all other variants of x are uninitialized.
So the None arm would know Some.0 is uninitialized, and the Some arm moves out ofSome.0, so the net effect would be that there's no fields to drop.
The annoying thing is that we also have to be able to check this in const-checking.
Hang on... promotion has to run before borrow-checking because it modifies the MIR.
But const-checking doesn't. Could we run const-checking after borrow-checking?
I'm planning to investigate eddyb's idea early next week (they gave me some instructions to follow over Zulip). This will require a small change to the dataflow analysis API which I'll probably do on top of #65672 just because I'm more familiar with it. Hopefully I can get a proof-of-concept up and running.
After #68528 and #68943, the optimized MIR for unwrap no longer has any drop terminators in non-cleanup blocks. In order to make unwrap a const fn, we need to move const-checking (or at least the part of const-checking that looks for live drops) after drop elaboration.
Reacted by memoryruins, Aaron Hill, rodrimati1992, oli, tesuji and Crystal Durham
#![feature(const_forget)]#![feature(const_fn)]#![feature(const_if_match)]#![feature(const_panic)]constfnunwrap<T>(opt:Option<T>)->T{match opt {Some(x)=>x,None=>{
std::mem::forget(opt);panic!("Trying to unwrap a None")},}}
More precise drop elaboration is now behind #![feature(const_precise_live_drops)], so Option::unwrap could be made unstably const fn with some #[cfg(bootstrap)] magic. We should probably just wait for the next bootstrap bump, however.
Activity
ecstatic-morse commentedon Nov 25, 2019
This was discussed here in the initial PR for
#![feature(const_if_match)]. Basically, we don't clear qualifs from a local (optin this case) if we move out of the only field of that local. I still think we want to special-case unit structs/enum variants because it will cover commonly used types likeOptionandResult. Alternatively, we could maybe suppressDropterminators entirely when a local is moved out of completely, or if this is already implemented, run const checks after this pass occurs.cc @rust-lang/wg-const-eval
Centril commentedon Nov 25, 2019
This might need to consider
#[non_exhaustive]as well similar to how the borrow checker needs to with #66722. cc @matthewjasperoli-obk commentedon Dec 20, 2019
So... we have three situations afaict:
structwithout explicitDropimpl, a!Copyfield is moved out of (this is the(Vec<T>,)case)!Copyfield ofenumvariant is moved out of (this is theSomecase)Nonecase. Even after drop elaboriation, we still executeDrop::dropfor the enum in theNonecase. Well, actually, even after our entire optimization pipeline, we hit bb16 in https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=152c663c007e0c9b67e733bf3d145588 which drops the value.So... if even drop elaboration can't figure this out, I'm not sure const qualif should be finding an independent solution.
Several solutions come to mind:
Forgetterminator in the spirit ofmem::drop==Dropterminatorhint::unreachable==Unreachableterminatorprocess::abort==Abortterminatormem::forget==ForgetterminatorNone-like casescc @eddyb
eddyb commentedon Jan 16, 2020
I think there's something missing from drop elaboration's (and other analyses'?) dataflow usage:
Each edge of a
SwitchInt(Discriminant(x))can assume all other variants ofxare uninitialized.So the
Nonearm would knowSome.0is uninitialized, and theSomearm moves out ofSome.0, so the net effect would be that there's no fields to drop.The annoying thing is that we also have to be able to check this in const-checking.
Hang on... promotion has to run before borrow-checking because it modifies the MIR.
But const-checking doesn't. Could we run const-checking after borrow-checking?
ecstatic-morse commentedon Jan 17, 2020
I'm planning to investigate eddyb's idea early next week (they gave me some instructions to follow over Zulip). This will require a small change to the dataflow analysis API which I'll probably do on top of #65672 just because I'm more familiar with it. Hopefully I can get a proof-of-concept up and running.
Auto merge of #68528 - ecstatic-morse:maybe-init-variants, r=oli-obk
ecstatic-morse commentedon Mar 2, 2020
After #68528 and #68943, the optimized MIR for
unwrapno longer has any drop terminators in non-cleanup blocks. In order to makeunwrapaconst fn, we need to move const-checking (or at least the part of const-checking that looks for live drops) after drop elaboration.jyn514 commentedon May 22, 2020
Silly question but - why does this need compiler support? Can't you
mem::forgetthe option?rodrimati1992 commentedon May 22, 2020
@jyn514
If you mean this:
https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=69e7f334990002dd979b81e38df3812e
It has no effect on the error message:
Neither does using
ManuallyDrop::newinstead.Rollup merge of rust-lang#71824 - ecstatic-morse:const-check-post-dro…
Rollup merge of rust-lang#71824 - ecstatic-morse:const-check-post-dro…
Rollup merge of rust-lang#71824 - ecstatic-morse:const-check-post-dro…
Rollup merge of rust-lang#71824 - ecstatic-morse:const-check-post-dro…
ecstatic-morse commentedon Jun 15, 2020
More precise drop elaboration is now behind
#![feature(const_precise_live_drops)], soOption::unwrapcould be made unstablyconst fnwith some#[cfg(bootstrap)]magic. We should probably just wait for the next bootstrap bump, however.Rollup merge of rust-lang#72437 - ecstatic-morse:stabilize-const-if-m…
Rollup merge of rust-lang#72437 - ecstatic-morse:stabilize-const-if-m…