rust-lang/rust · critical

`homogeneous_aggregate` should not be called for scalable ve

Error message

`homogeneous_aggregate` should not be called for scalable vectors

What it means

`homogeneous_aggregate` walks an aggregate's leaf fields to detect a single uniform pass-by-register unit, an ABI optimization for things like `(f32, f32, f32)`. Scalable (VL) SIMD vectors (`BackendRepr::SimdScalableVector`) have a runtime-unknown element count, so the question 'are all leaves the same register' is meaningless for them. This `unreachable!` enforces that no calling-convention code path ever asks it.

Source

Thrown at compiler/rustc_abi/src/callconv.rs:89

            BackendRepr::Scalar(scalar) => {
                let kind = match scalar.primitive() {
                    Primitive::Int(..) | Primitive::Pointer(_) => RegKind::Integer,
                    Primitive::Float(_) => RegKind::Float,
                };
                Ok(HomogeneousAggregate::Homogeneous(Reg { kind, size: self.size }))
            }

            BackendRepr::SimdVector { element, count: _ } => {
                assert!(!self.is_zst());

                Ok(HomogeneousAggregate::Homogeneous(Reg {
                    kind: RegKind::Vector { hint_vector_elem: element.primitive() },
                    size: self.size,
                }))
            }

            BackendRepr::SimdScalableVector { .. } => {
                unreachable!("`homogeneous_aggregate` should not be called for scalable vectors")
            }

            BackendRepr::ScalarPair { .. } | BackendRepr::Memory { sized: true } => {
                // Helper for computing `homogeneous_aggregate`, allowing a custom
                // starting offset (used below for handling variants).
                let from_fields_at =
                    |layout: Self,
                     start: Size|
                     -> Result<(HomogeneousAggregate, Size), Heterogeneous> {
                        let is_union = match layout.fields {
                            FieldsShape::Primitive => {
                                unreachable!("aggregates can't have `FieldsShape::Primitive`")
                            }
                            FieldsShape::Array { count, .. } => {
                                assert_eq!(start, Size::ZERO);

                                let result = if count > 0 {
                                    layout.field(cx, 0).homogeneous_aggregate(cx)?

View on GitHub (pinned to 22057b88b0)

Solutions

  1. Guard the caller so scalable-vector arguments skip `homogeneous_aggregate` and are handled by the target's dedicated SVE/RVV PCS path.
  2. Check the `BackendRepr` before invoking: `if matches!(layout.backend_repr, BackendRepr::SimdScalableVector { .. }) { return dedicated handling }`.
  3. Audit the target-specific callconv to ensure scalable vectors never appear as fields of an aggregate passed to this function.
  4. Add a regression test with a scalable-vector type to lock in the early-return.

Example fix

// before
let homo = layout.homogeneous_aggregate(cx)?; // panics for scalable vectors

// after
if matches!(layout.backend_repr, BackendRepr::SimdScalableVector { .. }) {
    return Err(Heterogeneous);
}
let homo = layout.homogeneous_aggregate(cx)?;
Defensive patterns

Strategy: validation

Validate before calling

// homogeneous_aggregate unreachable!() on BackendRepr::SimdScalableVector.
// Check the layout's backend repr before recursing into it.
use rustc_abi::{BackendRepr, LayoutData};
fn homogeneous_eligible<F, V>(layout: &LayoutData<F, V>) -> bool {
    !matches!(layout.backend_repr, BackendRepr::SimdScalableVector { .. })
}
// caller:
// if homogeneous_eligible(&ty_layout) { ty_layout.homogeneous_aggregate(cx)? } else { return Err(Heterogeneous); }

Type guard

fn is_fixed_simd_or_scalar<F, V>(layout: &LayoutData<F, V>) -> bool {
    matches!(
        layout.backend_repr,
        BackendRepr::Scalar(_) | BackendRepr::ScalarPair { .. } | BackendRepr::Memory { .. }
    ) || matches!(layout.backend_repr, BackendRepr::SimdVector { .. })
}

Try / catch

let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
    layout.homogeneous_aggregate(cx)
}));
match result {
    Ok(Ok(ha)) => { /* use homogeneous aggregate */ }
    Ok(Err(_))  => { /* legitimately heterogeneous */ }
    Err(_)      => { /* scalable vector slipped through; treat as heterogeneous */ }
}

Prevention

When it happens

Trigger: Calling `TyAndLayout::homogeneous_aggregate(cx)` on a type whose `backend_repr` is `BackendRepr::SimdScalableVector { .. }` — e.g. a Rust scalable-vector type such as `#[repr(simd)]` scalable elements used as a function argument under an ABI that consults homogeneity (AArch64 SVE PCS).

Common situations: A calling-convention or codegen change that routes scalable-vector arguments through the generic homogeneous-aggregate path instead of the dedicated vector handling, or a new scalable-vector primitive leaking into a struct/aggregate field that the homogeneity walker then recurses into.

Related errors


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