{"id":"42e736de718cf79c","repo":"rust-lang/rust","slug":"assignment-does-not-match-variant","errorCode":null,"errorMessage":"assignment does not match variant","messagePattern":"assignment does not match variant","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"compiler/rustc_abi/src/layout/coroutine.rs","lineNumber":223,"sourceCode":"            let outer_fields =\n                FieldsShape::Arbitrary { offsets: offsets_a, in_memory_order: in_memory_order_a };\n            (outer_fields, offsets_b, in_memory_order_b.invert_bijective_mapping())\n        }\n        _ => unreachable!(),\n    };\n\n    let mut size = prefix.size;\n    let mut align = prefix.align;\n    let variants = variant_fields\n        .iter_enumerated()\n        .map(|(index, variant_fields)| {\n            // Only include overlap-eligible fields when we compute our variant layout.\n            let variant_only_tys = variant_fields\n                .iter()\n                .filter(|local| match assignments[**local] {\n                    Unassigned => unreachable!(),\n                    Assigned(v) if v == index => true,\n                    Assigned(_) => unreachable!(\"assignment does not match variant\"),\n                    Ineligible(_) => false,\n                })\n                .map(|local| local_layouts[*local]);\n\n            let mut variant = calc.univariant(\n                &variant_only_tys.collect::<IndexVec<_, _>>(),\n                &ReprOptions::default(),\n                StructKind::Prefixed(prefix_size, prefix_align.abi),\n            )?;\n\n            let FieldsShape::Arbitrary { offsets, in_memory_order } = variant.fields else {\n                unreachable!();\n            };\n\n            // Now, stitch the promoted and variant-only fields back together in\n            // the order they are mentioned by our CoroutineLayout.\n            // Because we only use some subset (that can differ between variants)\n            // of the promoted fields, we can't just pick those elements of the","sourceCodeStart":205,"sourceCodeEnd":241,"githubUrl":"https://github.com/rust-lang/rust/blob/22057b88b091743bc0fd8d592a9264f0a6951403/compiler/rustc_abi/src/layout/coroutine.rs#L205-L241","documentation":"When computing a coroutine's memory layout, fields are classified as either promoted (shared across variants) or belonging to a specific variant, recorded in an `assignments` map keyed by field. While iterating each variant's fields, the code matches on `assignments[local]`: `Assigned(v)` is expected to either be `Unassigned` (promoted, unreachable here) or equal to the current variant index. A field assigned to a *different* variant violates the assignment bookkeeping and triggers this panic.","triggerScenarios":"Calling the coroutine layout calculator (`LayoutCalculator::coroutine_layout` / its variant-stitching loop) where `variant_fields`/`assignments` were built inconsistently — a field's `Assigned(variant)` value disagrees with the `VariantIdx` of the variant currently being stitched.","commonSituations":"A bug in the upvar/storage-liveness analysis that populates `assignments` for coroutines/async generators, a refactor of coroutine state variants that desynchronized `assignments` from `variant_fields`, or a hand-constructed `CoroutineLayout` in a test/backend that mismatches field assignments.","solutions":["Reproduce with `-Ztreat-err-as-bug` and capture the coroutine type; inspect the `assignments` IndexVec vs `variant_fields` for the offending local.","Audit the code that fills `assignments` (storage-liveness / liveness analysis pass) to confirm each local is assigned to exactly the variants that use it.","Check that `variant_fields[variant]` and `assignments[local]` agree for every (variant, local) pair before the stitching loop.","If you construct `CoroutineLayout` directly in a backend, regenerate it from the analysis pass instead of hand-writing it."],"exampleFix":"// before — a field assigned to variant 0 is filtered while stitching variant 1\nif let Assigned(v) = assignments[local] {\n    // v != current variant index -> panic\n}\n\n// after — only iterate fields whose assignment matches the current variant\nAssigned(v) if v == index => true,\nAssigned(_) => false,","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"// 'assignment does not match variant' is an internal invariant of the coroutine\n// layout computation; there is no public precondition a caller can check. Isolate\n// the layout call so a corrupted assignment table does not abort the driver.\nuse rustc_abi::LayoutCx;\nlet result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {\n    cx.layout_of_coroutine_def(coroutine_def_id, args)\n}));\nmatch result {\n    Ok(Ok(layout))   => { /* use layout */ }\n    Ok(Err(e))       => { /* normal layout error */ }\n    Err(payload)     => {\n        let msg = payload.downcast_ref::<String>().map(|s| s.as_str())\n            .or_else(|| payload.downcast_ref::<&'static str>().copied())\n            .unwrap_or(\"coroutine layout panic\");\n        log::error!(\"coroutine layout invariant violated: {msg}\");\n        // report the coroutine def-id for a bug report and skip it\n    }\n}","preventionTips":["This panic signals an internal compiler bug in coroutine (async generator) layout assignment; it is not a user input error, so the correct response is to isolate the failing compilation unit and report the def-id.","When driving rustc programmatically over many crates, wrap each item's layout computation in catch_unwind so one corrupt coroutine layout does not take down the whole run.","Capture the coroutine's type arguments and upvar set at the point of failure; that is the minimization input a compiler developer will need to reproduce the invariant violation."],"tags":["rustc","coroutine","async","layout","ice"],"analyzedSha":"22057b88b091743bc0fd8d592a9264f0a6951403","analyzedAt":"2026-08-03T08:09:25.915Z","schemaVersion":2}