Summary
crate_::Analyzer::refine_local_defs (src/analyze/crate_.rs:62) iterates over every key in mir_keys(()) and immediately builds a local_def::Analyzer for it:
for local_def_id in self.tcx.mir_keys(()) {
let analyzer = self.ctx.local_def_analyzer(*local_def_id); // :68
// ... is_annotated_as_* checks ...
if !self.tcx.def_kind(*local_def_id).is_fn_like() { // :96 <-- guard is HERE
keys.swap_remove(local_def_id);
}
}
local_def_analyzer (src/analyze.rs:652) calls local_def::Analyzer::new, whose first action is
let body = tcx.optimized_mir(local_def_id.to_def_id()).clone(); // src/analyze/local_def.rs:1115
mir_keys(()) includes non-fn-like defs — const items, static items, and the anonymous consts introduced by array-repeat expressions [v; N]. rustc forbids calling optimized_mir on those (they must go through mir_for_ctfe), so the query aborts the compiler:
thread 'rustc' panicked at compiler/rustc_mir_transform/src/lib.rs:763:24:
do not use `optimized_mir` for constants: Const { inline: false }
The is_fn_like guard that is supposed to skip these defs runs at :96, i.e. after local_def_analyzer has already forced optimized_mir at :68. As a result Thrust aborts on any crate that merely contains a const/static item — before it emits a single CHC — regardless of whether the item is used.
Reproduction
Each of the following aborts (RUST_BACKTRACE trimmed):
// const_unused.rs — the const is never referenced
const K: i64 = 42;
fn main() {}
// const_used.rs
const K: i64 = 42;
fn main() { let x = K; assert!(x == 42); }
// static_item.rs
static S: i64 = 7;
fn main() { let x = S; assert!(x == 7); }
// array_repeat.rs — the repeat length is an anonymous const
fn main() { let a = [0i64; 3]; let s: &[i64] = &a; let _ = s[0]; }
$ cargo run --quiet -- -Adead_code -C debug-assertions=false const_unused.rs
thread 'rustc' panicked at compiler/rustc_mir_transform/src/lib.rs:763:24:
do not use `optimized_mir` for constants: Const { inline: false }
static_item.rs reports ... for constants: Static(Not); the array-repeat case reports Const { inline: false } for the anonymous length const.
Backtrace pinpointing the call chain:
6: thrust::analyze::local_def::Analyzer::new at src/analyze/local_def.rs:1115 (optimized_mir)
7: thrust::analyze::Analyzer::local_def_analyzer at src/analyze.rs:652
8: thrust::analyze::crate_::Analyzer::refine_local_defs at src/analyze/crate_.rs:68
9: thrust::analyze::crate_::Analyzer::run at src/analyze/crate_.rs:288
Root cause
refine_local_defs constructs the per-def analyzer (which eagerly fetches optimized_mir) before filtering non-fn-like defs. The sibling function analyze_local_defs (src/analyze/crate_.rs:160) already does the correct thing — it applies the same guard first and continues:
fn analyze_local_defs(&mut self) {
for local_def_id in self.tcx.mir_keys(()) {
if !self.tcx.def_kind(*local_def_id).is_fn_like() { // guard BEFORE any use
continue;
};
...
So the two loops disagree only on where the is_fn_like check sits relative to touching the def, and refine_local_defs has it in the wrong place.
Why this is a defect, not a "feature not supported" gap
Thrust already supports programs whose values come from constants: const_value_ty / const_bytes_ty (src/analyze/basic_block.rs:342, :380) decode integer/bool/tuple/struct constants, and the is_fn_like filter exists precisely so that const/static item definitions are skipped as analysis targets while the functions that use them are analyzed. The intended behavior for const K: i64 = 42; fn main() { assert!(K == 42); } is therefore to verify, not to crash. The crash is an ordering bug in that existing support (the skip-guard runs one step too late), not an unimplemented feature — which is why const/static/array-repeat are ubiquitous, non-exotic constructs that abort the whole run.
Expected behavior
Non-fn-like defs (const, static, anonymous consts) should be skipped in refine_local_defs before local_def_analyzer is invoked, mirroring analyze_local_defs. Concretely, hoist the is_fn_like check above the local_def_analyzer(*local_def_id) call (and continue/skip), or make Analyzer::new route const/static defs through mir_for_ctfe instead of optimized_mir. With that, programs containing const/static items and array-repeat expressions can be analyzed instead of aborting.
Relation to existing issues
Environment
- thrust @
6953863
- rustc
nightly-2025-09-08 (per rust-toolchain.toml)
- Z3 4.13.0
- Confirmed by running
thrust-rustc (not inspection-only): each program above aborts before any verification result is produced.
Summary
crate_::Analyzer::refine_local_defs(src/analyze/crate_.rs:62) iterates over every key inmir_keys(())and immediately builds alocal_def::Analyzerfor it:local_def_analyzer(src/analyze.rs:652) callslocal_def::Analyzer::new, whose first action ismir_keys(())includes non-fn-like defs —constitems,staticitems, and the anonymous consts introduced by array-repeat expressions[v; N].rustcforbids callingoptimized_miron those (they must go throughmir_for_ctfe), so the query aborts the compiler:The
is_fn_likeguard that is supposed to skip these defs runs at:96, i.e. afterlocal_def_analyzerhas already forcedoptimized_mirat:68. As a result Thrust aborts on any crate that merely contains aconst/staticitem — before it emits a single CHC — regardless of whether the item is used.Reproduction
Each of the following aborts (
RUST_BACKTRACEtrimmed):static_item.rsreports... for constants: Static(Not); the array-repeat case reportsConst { inline: false }for the anonymous length const.Backtrace pinpointing the call chain:
Root cause
refine_local_defsconstructs the per-def analyzer (which eagerly fetchesoptimized_mir) before filtering non-fn-like defs. The sibling functionanalyze_local_defs(src/analyze/crate_.rs:160) already does the correct thing — it applies the same guard first andcontinues:So the two loops disagree only on where the
is_fn_likecheck sits relative to touching the def, andrefine_local_defshas it in the wrong place.Why this is a defect, not a "feature not supported" gap
Thrust already supports programs whose values come from constants:
const_value_ty/const_bytes_ty(src/analyze/basic_block.rs:342,:380) decode integer/bool/tuple/struct constants, and theis_fn_likefilter exists precisely so thatconst/staticitem definitions are skipped as analysis targets while the functions that use them are analyzed. The intended behavior forconst K: i64 = 42; fn main() { assert!(K == 42); }is therefore to verify, not to crash. The crash is an ordering bug in that existing support (the skip-guard runs one step too late), not an unimplemented feature — which is whyconst/static/array-repeat are ubiquitous, non-exotic constructs that abort the whole run.Expected behavior
Non-fn-like defs (
const,static, anonymous consts) should be skipped inrefine_local_defsbeforelocal_def_analyzeris invoked, mirroringanalyze_local_defs. Concretely, hoist theis_fn_likecheck above thelocal_def_analyzer(*local_def_id)call (andcontinue/skip), or makeAnalyzer::newroute const/static defs throughmir_for_ctfeinstead ofoptimized_mir. With that, programs containingconst/staticitems and array-repeat expressions can be analyzed instead of aborting.Relation to existing issues
const_value_tywhen analyzing integer constants larger thani64::MAX#116 is a panic inconst_value_tywhen decoding an integer constant value larger thani64::MAX. This report is about a different site and cause: the crate-level iteration inrefine_local_defscallingoptimized_miron a const/static item definition, which fires for ordinary small values (42,7) and even for an unusedconst.solver.rsunwrap panics) and Stack overflow (non-termination) indropping_formula_for_termwhen a recursive ADT's self-pointer is nested inside a tuple/struct field #178 (stack overflow indropping_formula_for_term) are unrelated crash sites.42,7,0, and an unusedconst; noMAX/MIN/casts are involved.Environment
6953863nightly-2025-09-08(perrust-toolchain.toml)thrust-rustc(not inspection-only): each program above aborts before any verification result is produced.