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
- Inspect `split_interface` to ensure the returned type is preserved as `Ty::Interface` through alias/canonicalization
- Check whether a recent change makes interface heads collapse to aliases before this match
- Update the match to handle the new variant or fix the earlier construction step
- 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
- Make split_interface return the exact matched Ty::Interface instead of reconstructing it
- Test impl blocks whose interface is reached through a type alias
- Keep canonicalization from collapsing interface heads
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
- interface `{iface_wire}` declares a non-runtime type: {e:?}
- interface `{iface_wire}` declares a non-runtime constraint:
- interface frame starts with Self
- sys_op callee must resolve to a statically-known global func
- expected jump instruction at index {instruction_idx}
AI-assisted analysis of BoundaryML/baml@bd85ce9dee (2026-09-12).
Data as JSON: /api/errors/05123df062f6f7e5.
Report an issue: GitHub.