rust-lang/rust · critical
assignment does not match variant
Error message
assignment does not match variant
What it means
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.
Source
Thrown at compiler/rustc_abi/src/layout/coroutine.rs:223
let outer_fields =
FieldsShape::Arbitrary { offsets: offsets_a, in_memory_order: in_memory_order_a };
(outer_fields, offsets_b, in_memory_order_b.invert_bijective_mapping())
}
_ => unreachable!(),
};
let mut size = prefix.size;
let mut align = prefix.align;
let variants = variant_fields
.iter_enumerated()
.map(|(index, variant_fields)| {
// Only include overlap-eligible fields when we compute our variant layout.
let variant_only_tys = variant_fields
.iter()
.filter(|local| match assignments[**local] {
Unassigned => unreachable!(),
Assigned(v) if v == index => true,
Assigned(_) => unreachable!("assignment does not match variant"),
Ineligible(_) => false,
})
.map(|local| local_layouts[*local]);
let mut variant = calc.univariant(
&variant_only_tys.collect::<IndexVec<_, _>>(),
&ReprOptions::default(),
StructKind::Prefixed(prefix_size, prefix_align.abi),
)?;
let FieldsShape::Arbitrary { offsets, in_memory_order } = variant.fields else {
unreachable!();
};
// Now, stitch the promoted and variant-only fields back together in
// the order they are mentioned by our CoroutineLayout.
// Because we only use some subset (that can differ between variants)
// of the promoted fields, we can't just pick those elements of theView on GitHub (pinned to 22057b88b0)
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.
Example fix
// before — a field assigned to variant 0 is filtered while stitching variant 1
if let Assigned(v) = assignments[local] {
// v != current variant index -> panic
}
// after — only iterate fields whose assignment matches the current variant
Assigned(v) if v == index => true,
Assigned(_) => false, Defensive patterns
Strategy: try-catch
Try / catch
// 'assignment does not match variant' is an internal invariant of the coroutine
// layout computation; there is no public precondition a caller can check. Isolate
// the layout call so a corrupted assignment table does not abort the driver.
use rustc_abi::LayoutCx;
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
cx.layout_of_coroutine_def(coroutine_def_id, args)
}));
match result {
Ok(Ok(layout)) => { /* use layout */ }
Ok(Err(e)) => { /* normal layout error */ }
Err(payload) => {
let msg = payload.downcast_ref::<String>().map(|s| s.as_str())
.or_else(|| payload.downcast_ref::<&'static str>().copied())
.unwrap_or("coroutine layout panic");
log::error!("coroutine layout invariant violated: {msg}");
// report the coroutine def-id for a bug report and skip it
}
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- non-`async`/`gen` closure body turned `async`/`gen` during l
- aggregates can't have `FieldsShape::Primitive`
- Expected multi-variant layout in `Layout::for_variant`
- a multi-variant layout should have `Arbitrary` fields
- use of `await` outside of an async context.
AI-assisted analysis of rust-lang/rust@22057b88b0 (2026-08-03).
Data as JSON: /data/errors/42e736de718cf79c.json.
Report an issue: GitHub.