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
Const-eval errors in dead functions are optimization-dependent #107503
#![feature(inline_const)]pubfncause_late_error<T>(){const{panic!()};}fnmain(){let a = 1;let b = 2;if a + b == 5{cause_late_error::<()>();}}
I expected to see this happen:
error[E0080]: evaluation of `cause_late_error::<()>::{constant#0}` failed
--> src/main.rs:7:9
|
7 | panic!()
| ^^^^^^^^ the evaluated program panicked at 'explicit panic', src/main.rs:7:9
|
= note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info)
note: the above error was encountered while instantiating `fn cause_late_error::<()>`
--> src/main.rs:13:9
|
13 | cause_late_error::<()>();
| ^^^^^^^^^^^^^^^^^^^^^^^^
Instead, this happened: it compiled without any warnings.
changed the title [-]Whether panicking in inline_const causes an error a depends on constant propagation[/-][+]Whether panicking in inline_const causes an error a depends on optimization[/+]on Jan 31, 2023
changed the title [-]Whether panicking in inline_const causes an error a depends on optimization[/-][+]Whether panicking in inline_const causes an error depends on optimization[/+]on Jan 31, 2023
I believe so, yes; any post-mono error should work. It's just significantly easier to show with panicking inline const than any other post-mono error, and much more accessible.
I believe this just results from cause_late_error::<()> being determined dead by MIR analysis and thus not monomorphized. Monomorphizing all mentioned names (enough to flush middleend post-mono errors) should fundamentally resolve the issue of constant evaluation errors disappearing because of MIR-level dead code elimination.
@RalfJung Is it possible to produce a similarly horrifying example as #107503 (comment) that compiles on stable today, and is optimization-dependent and DCA dependent?
pubfncause_late_error<T>(){structS;implS{constC:() = panic!();}S::C;}fnmain(){let a = 1;let b = 2;if a + b == 5{// changing 5 to 3 makes compilation failcause_late_error::<()>();}}
Sorry if this sounds rude, but could we just issue a crater run and push a breaking change at this point? Panic in consteval is not something stabilized for too long. We can recognize this1 as a design mistake. Whether the code depends on something other than #[cfg(debug_assertions)] is a bit too far for me...
This is the same underlying issue as #106617, so we probably should close one of these in favor of the other (or open a new issue to track the collected problems). We don't monomorphize dead code, which means
since optimizations affect which code is considered dead, we do opt-dependent monomorphization
since some errors are only raised during monomorphization, we have missing errors in dead code and opt-dependent errors
All of this affects stable Rust with associated consts.
Regarding whether this should block the stabilization of inline const, we tentatively felt that it should not, but we wanted to discuss that with more of the team before resolving the #104087 (comment) on #104087.
Ah, reading that concern now gives me flashbacks. ;)
That concern makes a difference between "Optimization-dependent errors" and "Errors in unused code". It turns out those cannot be disentangled, as basically all notions of "used" that exist in rustc depend on optimization levels. (This was not the case back when the comment was written, but is true ever since #[inline] is automatically inferred and that inference depends on optimization levels.)
The only way I can imagine making cross_crate_inlinable not optimization-dependent is by analyzing unoptimized MIR and doing the analysis at all optimization levels. If we do that, I am confident that we will regress both our compile time and our code generation quality (we could of course delete it altogether to avoid regressing compile time). But such a change does nothing to fix the behavior of #[inline], which is the root (heh) cause here. I do not think that library authors would not be any more troubled by not getting errors due to cross_crate_inlinable than they would be by realizing that their inline-const assert that should have prevented shipping a bug did not do its job because they followed a clippy lint to put #[inline] on all their public items.
On the other hand, the fact that this bug has been shipping on nightly for quite some time and nobody has complained perhaps indicates that the impact is not too widespread.
I am very happy to see anyone trying to untangle the reachability/MIR collection code.
To be clear, the only blessed pattern libs may use is that if a const is directly used inside some function, then whenever that function executes with a given choice of its generic parameters, the const succeeded evaluating. So we do not guarantee that the code in the OP will fail to build!
The point of this issue is that we don't want optimization-level-dependent behavior. But which functions we walk and evaluate consts for is still not a stable guarantee -- except that any function that actually gets executed has been walked and had its consts evaluated.
Hm, I wonder if inlining can also cause trouble here... if a call like f::<i32>() gets inlined, previously generic consts inside that body become monomorphic, and so if they ever get evaluated by later MIR passes then maybe that is evaluation that would not have happened otherwise (if this is all in a function that never gets collected)?
I'm beginning to wonder if MIR passes need a way to eval consts such that even if it fails, no error is emitted.
(After some experimentation with inlining) I think we aren't even computing optimized_mir for functions that do not participate in collection... in that case we're good. :)
Activity
CAD97 commentedon Jan 31, 2023
@rustbot modify labels +T-lang
joshtriplett commentedon Jan 31, 2023
let _: c_void = screaming.into();Ahem. Thank you for this most illuminating and horrifying example.
oskgo commentedon Jan 31, 2023
On a related note, there's also this following code that compiles in release mode but fails to compile in debug mode: [playground]
Maybe it makes more sense to track
inline_const's interactions with optimizations as a whole?[-]Whether panicking in inline_const causes an error a depends on constant propagation[/-][+]Whether panicking in inline_const causes an error a depends on optimization[/+][-]Whether panicking in inline_const causes an error a depends on optimization[/-][+]Whether panicking in inline_const causes an error depends on optimization[/+]RalfJung commentedon Jan 31, 2023
To be clear, this has nothing to do with inline_const, right? Associated consts show the same behavior?
CAD97 commentedon Jan 31, 2023
I believe so, yes; any post-mono error should work. It's just significantly easier to show with panicking inline const than any other post-mono error, and much more accessible.
I believe this just results from
cause_late_error::<()>being determined dead by MIR analysis and thus not monomorphized. Monomorphizing all mentioned names (enough to flush middleend post-mono errors) should fundamentally resolve the issue of constant evaluation errors disappearing because of MIR-level dead code elimination.joshtriplett commentedon Jan 31, 2023
@RalfJung Is it possible to produce a similarly horrifying example as #107503 (comment) that compiles on stable today, and is optimization-dependent and DCA dependent?
RalfJung commentedon Jan 31, 2023
@joshtriplett yes. It's not even that hard.
RalfJung commentedon Jan 31, 2023
And of course that also works for @oskgo's example:
Compiles in release mode but not in debug mode.
zirconium-n commentedon Feb 1, 2023
Sorry if this sounds rude, but could we just issue a crater run and push a breaking change at this point? Panic in consteval is not something stabilized for too long. We can recognize this1 as a design mistake. Whether the code depends on something other than
#[cfg(debug_assertions)]is a bit too far for me...Footnotes
Unused associated const doesn't not evaluate. ↩
RalfJung commentedon May 17, 2023
This is the same underlying issue as #106617, so we probably should close one of these in favor of the other (or open a new issue to track the collected problems). We don't monomorphize dead code, which means
All of this affects stable Rust with associated consts.
54 remaining items
RalfJung commentedon Mar 20, 2024
Ah, reading that concern now gives me flashbacks. ;)
That concern makes a difference between "Optimization-dependent errors" and "Errors in unused code". It turns out those cannot be disentangled, as basically all notions of "used" that exist in rustc depend on optimization levels. (This was not the case back when the comment was written, but is true ever since
#[inline]is automatically inferred and that inference depends on optimization levels.)saethlin commentedon Mar 20, 2024
The only way I can imagine making
cross_crate_inlinablenot optimization-dependent is by analyzing unoptimized MIR and doing the analysis at all optimization levels. If we do that, I am confident that we will regress both our compile time and our code generation quality (we could of course delete it altogether to avoid regressing compile time). But such a change does nothing to fix the behavior of#[inline], which is the root (heh) cause here. I do not think that library authors would not be any more troubled by not getting errors due tocross_crate_inlinablethan they would be by realizing that their inline-const assert that should have prevented shipping a bug did not do its job because they followed a clippy lint to put#[inline]on all their public items.On the other hand, the fact that this bug has been shipping on nightly for quite some time and nobody has complained perhaps indicates that the impact is not too widespread.
I am very happy to see anyone trying to untangle the reachability/MIR collection code.
RalfJung commentedon Mar 20, 2024
That was three "not" in a row, you lost me here. ;)
I gave up the hope of trying to simplify it, but maybe I can at least document it a bit better. ;)
saethlin commentedon Mar 21, 2024
I think library authors will be equally confused by
cross_crate_inlinableimplications as they will be by plain old#[inline]possibly hiding errors.(yeah wow that sentence is a mess)
RalfJung commentedon Mar 21, 2024
I have opened #122814 for the collection root issue @tmiasko discovered.
workingjubilee commentedon Mar 21, 2024
All uses in libraries I have seen are one of:
and I have certainly seen a few particularly... dank... usages of these kinds of "const assertions".
RalfJung commentedon Mar 21, 2024
To be clear, the only blessed pattern libs may use is that if a const is directly used inside some function, then whenever that function executes with a given choice of its generic parameters, the const succeeded evaluating. So we do not guarantee that the code in the OP will fail to build!
The point of this issue is that we don't want optimization-level-dependent behavior. But which functions we walk and evaluate consts for is still not a stable guarantee -- except that any function that actually gets executed has been walked and had its consts evaluated.
workingjubilee commentedon Mar 21, 2024
yeah, that seems fine? weird but fine.
workingjubilee commentedon Mar 21, 2024
the OP's code fails to build in both debug and release now. huh. weird.
RalfJung commentedon Mar 21, 2024
RalfJung commentedon Mar 21, 2024
Hm, I wonder if inlining can also cause trouble here... if a call like
f::<i32>()gets inlined, previously generic consts inside that body become monomorphic, and so if they ever get evaluated by later MIR passes then maybe that is evaluation that would not have happened otherwise (if this is all in a function that never gets collected)?I'm beginning to wonder if MIR passes need a way to eval consts such that even if it fails, no error is emitted.
Auto merge of rust-lang#122568 - RalfJung:mentioned-items, r=oli-obk
RalfJung commentedon Mar 21, 2024
(After some experimentation with inlining) I think we aren't even computing
optimized_mirfor functions that do not participate in collection... in that case we're good. :)Auto merge of #122568 - RalfJung:mentioned-items, r=oli-obk