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 the

View on GitHub (pinned to 22057b88b0)

Solutions

  1. Reproduce with `-Ztreat-err-as-bug` and capture the coroutine type; inspect the `assignments` IndexVec vs `variant_fields` for the offending local.
  2. Audit the code that fills `assignments` (storage-liveness / liveness analysis pass) to confirm each local is assigned to exactly the variants that use it.
  3. Check that `variant_fields[variant]` and `assignments[local]` agree for every (variant, local) pair before the stitching loop.
  4. 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

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


AI-assisted analysis of rust-lang/rust@22057b88b0 (2026-08-03). Data as JSON: /data/errors/42e736de718cf79c.json. Report an issue: GitHub.