Open
Description
This is a variant of #107503, but with a different underlying cause and hence not fixed by #122568. @tmiasko provided this magnificent example (comments by me, they may be wrong):
//! This used to fail in optimized builds but pass in unoptimized builds. The reason is that in
//! optimized builds, `f` gets marked as cross-crate-inlineable, so the functions it calls become
//! reachable, and therefore `g` becomes a collection root. But in unoptimized builds, `g` is no
//! root, and the call to `g` disappears in an early `SimplifyCfg` before "mentioned items" are
//! gathered, so we never reach `g`.
#![crate_type = "lib"]
struct Fail<T>(T);
impl<T> Fail<T> {
const C: () = panic!(); //~ERROR: evaluation of `Fail::<i32>::C` failed
}
pub fn f() {
loop {}; g()
}
#[inline(never)]
fn g() {
h::<i32>()
}
// Make sure we only use the faulty const in a generic function, or
// else it gets evaluated by some MIR pass.
fn h<T>() {
Fail::<T>::C;
}
The symptom here is the opposite of #107503: cargo build
succeeds but cargo build --release
fails. This is because more things become roots in optimized builds and therefore we evaluate more things.
I can think of two ways of fixing this:
- Making the set of collection roots opt-level-independent. This is what I tried in a303df0. It caused a ~2% slowdown on some benchmarks; it's hard to get the full picture as we only benchmarked it together with the rest of recursively evaluate the constants in everything that is 'mentioned' #122568. There may be ways to reduce this cost.
- Make sure
g
is considered "mentioned" inf
. This requires either collecting (some of) the mentioned items very early during MIR building, or makingSimplifyCfg
preserved unreachable blocks. This likely has lower perf impact, but it means we'd still miss const-eval failures reachable from dead private monomorphic functions.