rust-lang/rust · critical
destructed mir constant of adt without variant idx
Error message
destructed mir constant of adt without variant idx
What it means
When pretty-printing a MIR constant whose type is a non-empty enum/struct ADT, rustc_middle destructures the constant via `tcx.try_destructure_mir_constant_for_user_output` and then `.expect`s the returned `contents.variant` is `Some`. The panic indicates the destructure succeeded (returned fields) but did not carry a variant index — an internal invariant violation in the const-eval / ty layer for ADT constants.
Source
Thrown at compiler/rustc_middle/src/mir/pretty.rs:1993
fmt.write_str("[")?;
comma_sep(tcx, fmt, fields)?;
fmt.write_str("]")?;
}
ty::Tuple(..) => {
fmt.write_str("(")?;
comma_sep(tcx, fmt, fields)?;
if contents.fields.len() == 1 {
fmt.write_str(",")?;
}
fmt.write_str(")")?;
}
ty::Adt(def, _) if def.variants().is_empty() => {
fmt.write_str(&format!("{{unreachable(): {ty}}}"))?;
}
ty::Adt(def, args) => {
let variant_idx = contents
.variant
.expect("destructed mir constant of adt without variant idx");
let variant_def = &def.variant(variant_idx);
let mut p = FmtPrinter::new(tcx, Namespace::ValueNS);
p.print_alloc_ids = true;
p.pretty_print_value_path(variant_def.def_id, args)?;
fmt.write_str(&p.into_buffer())?;
match variant_def.ctor_kind() {
Some(CtorKind::Const) => {}
Some(CtorKind::Fn) => {
fmt.write_str("(")?;
comma_sep(tcx, fmt, fields)?;
fmt.write_str(")")?;
}
None => {
fmt.write_str(" {{ ")?;
let mut first = true;
for (field_def, (ct, ty)) in iter::zip(&variant_def.fields, fields)
{View on GitHub (pinned to 22057b88b0)
Solutions
- Reproduce with `-Zmir-opt-level=0 --edition=2021` and `cargo clean` to rule out incr-comp corruption; if it disappears, clear `target/incremental`.
- Bisect MIR passes with `-Zdump-mir=all` to find which pass produced the offending ADT const, then report the pass + reduced repro to rustc.
- Avoid the triggering construct in your crate: replace the unusual const (transmuted enum, intrinsic-built ADT) with a normally-constructed value.
- On nightlies, try a few adjacent rustc dates to confirm whether it's a regression and attach the working/failing commit to the bug report.
Example fix
// before: const built via transmute loses its variant tag
const FLAG: MyEnum = unsafe { std::mem::transmute(0u8) };
// panic when -Zunpretty=mir renders the body
// after
const FLAG: MyEnum = MyEnum::A; Defensive patterns
Strategy: try-catch
Validate before calling
// This is a .expect() during pretty-printing of a destructed ADT constant.
// If you synthesize ConstValue/Operand for ADTs, set the variant index first.
// For a constant you are about to pretty-print, check its shape:
use rustc_middle::mir::interpret::ConstValue;
use rustc_middle::mir::{Const, ConstOperand};
fn const_has_variant_for_adt<'tcx>(c: &Const<'tcx>, is_adt_ty: bool) -> bool {
if !is_adt_ty { return true; } // only ADTs need a variant idx
match c {
Const::Val(_, _) | Const::Ty(_) => {
// the variant index lives on the destructed user-facing Constant;
// if you built it via ty::Const::from_, ensure .variant was set.
true // structural check: see typeGuard
}
Const::Unevaluated(_) => true,
}
} Type guard
// When YOU construct the destructed constant for an ADT, guarantee variant_index
// is Some before it can reach the pretty-printer.
// Pseudo-shape of what the printer reads (adapt names to your rustc version):
// struct DestructuredConstant<'tcx> { ty: Ty<'tcx>, variant: Option<VariantIdx>, fields: Vec<...> }
fn is_printable_adt_constant(variant: Option<rustc_abi::FieldIdx>, ty_is_adt: bool) -> bool {
!ty_is_adt || variant.is_some() // ADT constants MUST carry a variant idx
} Try / catch
// pretty-printing is best-effort; never let a malformed constant abort your tool.
use std::panic::{catch_unwind, AssertUnwindSafe};
let rendered = catch_unwind(AssertUnwindSafe(|| {
format!("{:#?}", the_constant) // invokes the Debug/pretty path
}));
match rendered {
Ok(s) => { /* use s */ }
Err(_payload) => {
// fall back to a lossy representation; do NOT re-pretty-print the same value
format!("<unprintable constant of type {:?}>", ty)
}
} Prevention
- When you build a destructured/evaluated constant for an enum or struct ADT, always populate the variant index in the same call that sets the type.
- Don't reuse a Constant intended for a non-ADT type as the value for an ADT type — the variant idx won't be set.
- Treat MIR pretty-printing and `-Z mir-dump` as non-fatal: run them under catch_unwind so a single bad constant can't take down your driver.
- If you cross crate boundaries with synthesized constants, round-trip them through the normal const-eval path rather than hand-assembling the destructured form.
When it happens
Trigger: Triggered during `write_mir_pretty` / constant rendering of a MIR body that contains an ADT-typed `Const` whose `ConstValue` is shape-compatible for destructure but the const system failed to attach a `VariantIdx`. Reproducible by feeding rustc a hand-constructed or post-pass MIR constant where the variant discriminant is missing.
Common situations: Internal rustc bug surfaced by an unusual const (e.g. an enum constructed via intrinsic/transmute, a const that survived an optimization that dropped the variant tag); nightly-only mir-opt or const-eval change; custom const-fold pass that emits ADT consts without a variant; corrupted incr-comp cache feeding a malformed const.
Related errors
- range should be nonempty
- there must be provenance somewhere here
- an interpreter error got improperly discarded; use `discard_
- statics should not have generic parameters
- got a pointer where a ScalarInt was expected
AI-assisted analysis of rust-lang/rust@22057b88b0 (2026-08-03).
Data as JSON: /data/errors/ae9f5d64aeeb504a.json.
Report an issue: GitHub.