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
- 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.
- Validate `LayoutData` invariants in your `TyAbiInterface` impl before returning: multi-variant ⇒ Arbitrary fields.
- Dump the layout with `-Zprint-layout` to confirm the offending type, then check the repr/options that produced it.
- 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
- Multi-variant layouts are always Arbitrary by construction; if you build or transform LayoutData yourself, never leave a Variants::Multiple paired with Union/Array/Primitive fields.
- Treat variant_dependent_padding_ranges as callable only on enum/coroutine layouts you obtained from the layout calculator unchanged — do not call it on layouts you have patched.
- If you must inspect padding on a layout you cannot fully trust, short-circuit to Vec::new() when the fields shape is not Arbitrary.
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
- Expected multi-variant layout in `Layout::for_variant`
- aggregates can't have `FieldsShape::Primitive`
- assignment does not match variant
- layout decided on a larger discriminant type ({min_ity:?}) t
- encountered a non-arbitrary layout during enum layout
AI-assisted analysis of rust-lang/rust@22057b88b0 (2026-08-03).
Data as JSON: /data/errors/7b207dbc7576f2aa.json.
Report an issue: GitHub.