{"id":"ae9f5d64aeeb504a","repo":"rust-lang/rust","slug":"destructed-mir-constant-of-adt-without-variant-idx","errorCode":null,"errorMessage":"destructed mir constant of adt without variant idx","messagePattern":"destructed mir constant of adt without variant idx","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"compiler/rustc_middle/src/mir/pretty.rs","lineNumber":1993,"sourceCode":"                        fmt.write_str(\"[\")?;\n                        comma_sep(tcx, fmt, fields)?;\n                        fmt.write_str(\"]\")?;\n                    }\n                    ty::Tuple(..) => {\n                        fmt.write_str(\"(\")?;\n                        comma_sep(tcx, fmt, fields)?;\n                        if contents.fields.len() == 1 {\n                            fmt.write_str(\",\")?;\n                        }\n                        fmt.write_str(\")\")?;\n                    }\n                    ty::Adt(def, _) if def.variants().is_empty() => {\n                        fmt.write_str(&format!(\"{{unreachable(): {ty}}}\"))?;\n                    }\n                    ty::Adt(def, args) => {\n                        let variant_idx = contents\n                            .variant\n                            .expect(\"destructed mir constant of adt without variant idx\");\n                        let variant_def = &def.variant(variant_idx);\n                        let mut p = FmtPrinter::new(tcx, Namespace::ValueNS);\n                        p.print_alloc_ids = true;\n                        p.pretty_print_value_path(variant_def.def_id, args)?;\n                        fmt.write_str(&p.into_buffer())?;\n\n                        match variant_def.ctor_kind() {\n                            Some(CtorKind::Const) => {}\n                            Some(CtorKind::Fn) => {\n                                fmt.write_str(\"(\")?;\n                                comma_sep(tcx, fmt, fields)?;\n                                fmt.write_str(\")\")?;\n                            }\n                            None => {\n                                fmt.write_str(\" {{ \")?;\n                                let mut first = true;\n                                for (field_def, (ct, ty)) in iter::zip(&variant_def.fields, fields)\n                                {","sourceCodeStart":1975,"sourceCodeEnd":2011,"githubUrl":"https://github.com/rust-lang/rust/blob/22057b88b091743bc0fd8d592a9264f0a6951403/compiler/rustc_middle/src/mir/pretty.rs#L1975-L2011","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before: const built via transmute loses its variant tag\nconst FLAG: MyEnum = unsafe { std::mem::transmute(0u8) };\n// panic when -Zunpretty=mir renders the body\n\n// after\nconst FLAG: MyEnum = MyEnum::A;","handlingStrategy":"try-catch","validationCode":"// This is a .expect() during pretty-printing of a destructed ADT constant.\n// If you synthesize ConstValue/Operand for ADTs, set the variant index first.\n// For a constant you are about to pretty-print, check its shape:\nuse rustc_middle::mir::interpret::ConstValue;\nuse rustc_middle::mir::{Const, ConstOperand};\nfn const_has_variant_for_adt<'tcx>(c: &Const<'tcx>, is_adt_ty: bool) -> bool {\n    if !is_adt_ty { return true; }                 // only ADTs need a variant idx\n    match c {\n        Const::Val(_, _) | Const::Ty(_) => {\n            // the variant index lives on the destructed user-facing Constant;\n            // if you built it via ty::Const::from_, ensure .variant was set.\n            true // structural check: see typeGuard\n        }\n        Const::Unevaluated(_) => true,\n    }\n}","typeGuard":"// When YOU construct the destructed constant for an ADT, guarantee variant_index\n// is Some before it can reach the pretty-printer.\n// Pseudo-shape of what the printer reads (adapt names to your rustc version):\n//   struct DestructuredConstant<'tcx> { ty: Ty<'tcx>, variant: Option<VariantIdx>, fields: Vec<...> }\nfn is_printable_adt_constant(variant: Option<rustc_abi::FieldIdx>, ty_is_adt: bool) -> bool {\n    !ty_is_adt || variant.is_some() // ADT constants MUST carry a variant idx\n}","tryCatchPattern":"// pretty-printing is best-effort; never let a malformed constant abort your tool.\nuse std::panic::{catch_unwind, AssertUnwindSafe};\nlet rendered = catch_unwind(AssertUnwindSafe(|| {\n    format!(\"{:#?}\", the_constant) // invokes the Debug/pretty path\n}));\nmatch rendered {\n    Ok(s) => { /* use s */ }\n    Err(_payload) => {\n        // fall back to a lossy representation; do NOT re-pretty-print the same value\n        format!(\"<unprintable constant of type {:?}>\", ty)\n    }\n}","preventionTips":["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."],"tags":["rustc","mir","const-eval","internal-invariant"],"analyzedSha":"22057b88b091743bc0fd8d592a9264f0a6951403","analyzedAt":"2026-08-03T08:09:25.915Z","schemaVersion":2}