BoundaryML/baml · error

generic arity fits u32

Error message

generic arity fits u32

What it means

When building an impl frame, the compiler converts the impl's declared generic parameter count to `u32` with `.expect("generic arity fits u32")` to generate `TyTemplate::TypeArgRef` slots. The panic means an impl block declares more than `u32::MAX` generic parameters — practically only reachable via an arity-computation bug, since real declarations cannot have 4 billion generics.

Source

Thrown at baml_language/crates/baml_compiler2_emit/src/lib.rs:1050

                _ => unreachable!("split_interface matched an interface"),
            };
            complete_interface_assoc(
                &mut interface_assoc,
                &iface_tn,
                &iface_arg_tys,
                &for_ty,
                &impl_params,
                resolved,
            );
            // The constraint set was lowered (and fail-closed gated) inside
            // `impl_rule_target`, so the bake, the decompose attribution,
            // and the rule's `ImplCoherenceKey` all carry the identical
            // canonicalized bounds.
            // A block's own method is compiled against the owner frame — the
            // impl's declared generics, which for an in-class block ARE the
            // class's.
            let impl_frame: Vec<bex_vm_types::TyTemplate> = (0..u32::try_from(impl_params.len())
                .expect("generic arity fits u32"))
                .map(bex_vm_types::TyTemplate::TypeArgRef)
                .collect();
            let Some(interface_head) = interface_indices
                .get(&iface_tn)
                .copied()
                .map(ObjectIndex::from_raw)
            else {
                continue;
            };
            let mut methods = indexmap::IndexMap::new();
            for &m in &block.methods {
                let method_name = function_data(db, m).name.clone();
                // An unindexed body has a `$compiler_intrinsic` /
                // `$await_any` body Pass 4 never pools — drop just that
                // method (losing a dispatch, never adding a wrong one). The
                // stdlib only uses those bodies on free functions, so this is
                // unreachable today; the debug_assert pins the convention.
                // TODO: reject intrinsic/await-any bodies inside impl blocks

View on GitHub (pinned to bd85ce9dee)

Solutions

  1. Audit how `impl_params` is collected for duplication or runaway expansion
  2. Deduplicate the generic parameter list before frame construction
  3. If genuinely large arities are expected, widen the field or cap with a proper diagnostic

Example fix

// before
u32::try_from(impl_params.len()).expect("generic arity fits u32")
// after
u32::try_from(impl_params.len())
    .map_err(|_| EmitError::TooManyGenerics(impl_params.len()))?
Defensive patterns

Strategy: validation

Validate before calling

if impl_params.len() > u32::MAX as usize {
    return Err(CompilerBug::GenericArityOverflow(impl_params.len()));
}

Type guard

fn fits_u32(n: usize) -> Option<u32> {
    u32::try_from(n).ok()
}

Try / catch

std::panic::catch_unwind(|| build_packages(db))
    .unwrap_or_else(|_| report_compiler_bug("generic arity overflow"));

Prevention

When it happens

Trigger: `impl_params.len()` overflowing u32 during `build_packages` frame construction — caused by duplicated or recursively expanded parameter lists rather than a genuine huge arity.

Common situations: Lowering bugs that duplicate generic parameters when merging class and impl generics; pathological generated code; infinite recursion in parameter collection that inflated the list.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


AI-assisted analysis of BoundaryML/baml@bd85ce9dee (2026-09-12). Data as JSON: /api/errors/a688fdb296e21b39. Report an issue: GitHub.