rust-lang/rust · critical

a multi-variant layout should have `Arbitrary` fields

Error message

a multi-variant layout should have `Arbitrary` fields

What it means

`variant_dependent_padding_ranges` computes which bytes are padding for a specific enum variant. Multi-variant layouts always store per-variant field offsets in a `FieldsShape::Arbitrary` (offsets + in_memory_order); other shapes (Primitive/Array/Union) cannot express per-variant fields, so reaching this code with a non-Arbitrary multi-variant layout means the layout is internally inconsistent.

Source

Thrown at compiler/rustc_abi/src/layout/ty.rs:364

        variant_index: VariantIdx,
    ) -> Vec<Range<Size>>
    where
        Ty: TyAbiInterface<'a, C> + Copy,
    {
        let Variants::Multiple { .. } = self.variants else {
            return Vec::new();
        };

        // Bytes that are data in some variant.
        let mut any = RangeSet::new();
        self.add_data_ranges(cx, Size::ZERO, &mut any);

        // Bytes that are data in this variant.
        let mut this = RangeSet::new();

        // The variants do not contain e.g. the discriminant or coroutine upvars.
        let FieldsShape::Arbitrary { offsets, in_memory_order: _ } = &self.fields else {
            unreachable!("a multi-variant layout should have `Arbitrary` fields")
        };

        // So add them explicitly.
        for (field, &offset) in offsets.iter_enumerated() {
            let field = self.field(cx, field.as_usize());
            field.add_data_ranges(cx, offset, &mut this);
        }

        self.for_variant(cx, variant_index).add_data_ranges(cx, Size::ZERO, &mut this);

        // Padding specific to this variant: data in some variant, but not in this one.
        any.difference(&this).0.iter().map(|&(offset, size)| offset..offset + size).collect()
    }

    /// Extend `out` with all ranges of bytes that *may* carry relevant data for values of this type.
    /// For enums and unions there are offsets that are initialized for some
    /// variants but not for others; those offset *will* get added to `out`.
    fn add_data_ranges<C>(self, cx: &C, base_offset: Size, out: &mut RangeSet<Size>)

View on GitHub (pinned to 22057b88b0)

Solutions

  1. Find the layout builder for enums (the `Variants::Multiple` arm of the layout calculator) and confirm it always emits `FieldsShape::Arbitrary` with per-variant offsets.
  2. Validate `LayoutData` invariants in your `TyAbiInterface` impl before returning: multi-variant ⇒ Arbitrary fields.
  3. Dump the layout with `-Zprint-layout` to confirm the offending type, then check the repr/options that produced it.
  4. File an ICE with the enum definition if it reproduces on stock rustc.

Example fix

// before — multi-variant enum built with a non-Arbitrary field shape
LayoutData {
    variants: Variants::Multiple { .. },
    fields: FieldsShape::Primitive, // inconsistent; later panics
    ..
}

// after
LayoutData {
    variants: Variants::Multiple { tag, variants, tag_encoding },
    fields: FieldsShape::Arbitrary { offsets, in_memory_order },
    ..
}
Defensive patterns

Strategy: validation

Validate before calling

// variant_dependent_padding_ranges asserts a multi-variant layout must have
// FieldsShape::Arbitrary. Validate both conditions up front.
use rustc_abi::{FieldsShape, LayoutData, Variants};
fn safe_padding_query<F, V>(layout: &LayoutData<F, V>) -> bool {
    matches!(layout.variants, Variants::Multiple { .. })
        && matches!(layout.fields, FieldsShape::Arbitrary { .. })
}
// caller:
// if safe_padding_query(&layout) { layout.variant_dependent_padding_ranges(cx, idx) } else { Vec::new() }

Type guard

fn has_arbitrary_field_shape<F, V>(layout: &LayoutData<F, V>) -> bool {
    matches!(layout.fields, FieldsShape::Arbitrary { .. })
}

Try / catch

let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
    layout.variant_dependent_padding_ranges(cx, variant_idx)
}));
match result {
    Ok(ranges) => { /* use ranges */ }
    Err(_) => {
        // Multi-variant layout had non-Arbitrary fields (shouldn't happen for
        // well-formed enum layouts). Return empty padding as a safe default.
    }
}

Prevention

When it happens

Trigger: Calling `variant_dependent_padding_ranges(cx, variant_index)` on a `TyAndLayout` whose `variants` is `Variants::Multiple` but whose `fields` is not `FieldsShape::Arbitrary` — a state that the layout builder is supposed to prevent.

Common situations: A layout-computation change that built a multi-variant enum with the wrong field shape, a custom `TyAbiInterface` impl returning an inconsistent `LayoutData`, or corruption of a cached layout. Real enums never hit this; only a builder bug does.

Related errors


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