BoundaryML/baml · error

split_interface matched an interface

Error message

split_interface matched an interface

What it means

In `build_packages`, after `split_interface` matches an impl block against an interface, the code destructures the resulting `iface_ty` and asserts with `unreachable!` that it is a `ty::Ty::Interface`. The panic means `split_interface` reported a match but the reconstructed type is not an Interface variant — an internal desynchronization between the matching and construction steps.

Source

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

                mut interface_assoc,
                for_ty,
                for_ty_pattern,
                impl_params,
                impl_bounds,
                generic_param_bounds,
            }) = impl_rule_target(db, *file, impl_loc, resolved)
            else {
                continue;
            };
            interface_assoc.extend(lower_assoc(
                store,
                &block.associated_type_bindings,
                &impl_params,
                &impl_bounds,
            ));
            let iface_arg_tys = match &iface_ty {
                ty::Ty::Interface(_, args, _, _) => args.clone(),
                _ => 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"))

View on GitHub (pinned to bd85ce9dee)

Solutions

  1. Inspect `split_interface` to ensure the returned type is preserved as `Ty::Interface` through alias/canonicalization
  2. Check whether a recent change makes interface heads collapse to aliases before this match
  3. Update the match to handle the new variant or fix the earlier construction step
  4. Reproduce with the failing impl block's source to identify the alias/instantiation path
Defensive patterns

Strategy: type-guard

Validate before calling

if !matches!(iface_ty, ty::Ty::Interface(_, _, _, _)) {
    return Err(CompilerBug::SplitInterfaceNonInterface(iface_tn.clone()));
}

Type guard

fn is_interface_ty(t: &ty::Ty) -> bool {
    matches!(t, ty::Ty::Interface(_, _, _, _))
}

Try / catch

std::panic::catch_unwind(|| build_packages(db))
    .unwrap_or_else(|_| report_compiler_bug("split_interface produced non-interface type"));

Prevention

When it happens

Trigger: An impl block whose interface type was rebuilt into a different `Ty` variant (alias collapse, generic instantiation producing a non-Interface head) despite `split_interface` succeeding.

Common situations: Type-alias resolution changes that collapse interface types; refactors of `split_interface` that changed what it matches; canonicalization bugs with generic interfaces.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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