rust-lang/rust · critical

Expected multi-variant layout in `Layout::for_variant`

Error message

Expected multi-variant layout in `Layout::for_variant`

What it means

`Layout::for_variant(parent, index)` extracts one variant's layout out of a multi-variant enum. It only knows how to read variant data from `Variants::Multiple { variants, .. }`; a `Variants::Single` or `Variants::Empty` parent has no variant table to index. Asking 'give me variant N' of a layout that doesn't have multiple variants is a misuse.

Source

Thrown at compiler/rustc_abi/src/layout/simple.rs:156

            },
            backend_repr: BackendRepr::Memory { sized: true },
            largest_niche: None,
            uninhabited: true,
            align: AbiAlign::new(dl.i8_align),
            size: Size::ZERO,
            max_repr_align: None,
            unadjusted_abi_align: dl.i8_align,
            // Variant layouts never flow back into actual layout computations,
            // so dummy values are fine here.
            randomization_seed: Hash64::ZERO,
        }
    }

    /// Returns a layout for an inhabited variant.
    pub fn for_variant(parent: &Self, index: VariantIdx) -> Self {
        let layout = match &parent.variants {
            Variants::Multiple { variants, .. } => &variants[index],
            _ => panic!("Expected multi-variant layout in `Layout::for_variant`"),
        };

        Self {
            fields: FieldsShape::Arbitrary {
                offsets: layout.field_offsets.clone(),
                in_memory_order: layout.fields_in_memory_order.clone(),
            },
            variants: Variants::Single { index },
            backend_repr: layout.backend_repr,
            largest_niche: layout.largest_niche,
            uninhabited: layout.uninhabited,
            size: layout.size,
            align: parent.align,
            max_repr_align: parent.max_repr_align,
            unadjusted_abi_align: parent.unadjusted_abi_align,
            // Variant layouts never flow back into actual layout computations,
            // so dummy values are fine here.
            randomization_seed: Hash64::ZERO,

View on GitHub (pinned to 22057b88b0)

Solutions

  1. Before calling `for_variant`, check `matches!(parent.variants, Variants::Multiple { .. })`; for `Single { index }` the variant is the layout itself, for `Empty` there are no variants.
  2. Use the existing `TyAndLayout` helpers that already gate on `variants`, rather than reaching into `Layout::for_variant` directly.
  3. If you maintain a layout cache, key it by `(type, Option<VariantIdx>)` so single-variant types never ask for a variant.
  4. Add a debug-assertion upstream that catches single-variant parents before they reach this point.

Example fix

// before
let v = Layout::for_variant(&parent, idx); // panics if parent is Single/Empty

// after
let v = match &parent.variants {
    Variants::Multiple { variants, .. } => variants[idx].clone(),
    Variants::Single { .. } => parent.clone(),
    Variants::Empty => parent.clone(),
};
Defensive patterns

Strategy: type-guard

Validate before calling

// Layout::for_variant panics unless the parent layout is Variants::Multiple.
use rustc_abi::{Layout, LayoutData, Variants};
fn can_project_variant<F, V>(layout: &LayoutData<F, V>) -> bool {
    matches!(layout.variants, Variants::Multiple { .. })
}
// caller:
// if can_project_variant(&parent) { Layout::for_variant(&parent, idx) } else { /* parent is single-variant; idx must be 0 or the layout itself */ }

Type guard

fn is_multi_variant_layout<F, V>(layout: &LayoutData<F, V>) -> bool {
    matches!(layout.variants, Variants::Multiple { .. })
}

Try / catch

let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
    Layout::for_variant(&parent, variant_idx)
}));
match result {
    Ok(v) => { /* projected variant layout */ }
    Err(_) => {
        // parent is Variants::Single; the only legal variant index is its own index.
        // Fall back to returning the parent layout unchanged.
    }
}

Prevention

When it happens

Trigger: Calling `Layout::for_variant(&parent_layout, index)` when `parent_layout.variants` is `Variants::Single { .. }` or `Variants::Empty` — i.e. the type is not a multi-variant enum but the caller assumed it was.

Common situations: A codegen/MIR path that treats every type as a potential enum and calls `for_variant` unconditionally, a refactor that changed a type's layout from multi-variant to single-variant without updating the caller, or a query that caches a single-variant layout being fed into a variant-aware consumer.

Related errors


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