Skip to content

Panic: refine_local_defs calls optimized_mir on non-fn-like defs before the is_fn_like guard, so any program with a const/static item (or an array-repeat [v; N]) aborts before verification #198

Description

@coord-e

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions