BoundaryML/baml · critical
virtual local without definition
Error message
virtual local {local} without definition What it means
Panic while inlining a virtual (def-use optimized) local at its use site: the analysis recorded the local as having a use, but def_use[&local].def is None, so there is no rvalue to inline. A virtual local exists precisely to have its single definition inlined; a use without a definition breaks that invariant.
Solutions
- Ensure every virtual local has its definition rvalue recorded in def_use before emission of its uses.
- Re-materialize locals as normal stack locals when their definition cannot be inlined.
- Verify the pass that promotes locals to virtual ones checks for a present definition.
- Report the triggering BAML source as a compiler bug.
Defensive patterns
Strategy: validation
Validate before calling
// only treat locals as virtual when a definition exists
fn can_be_virtual(local: &Local, def_use: &DefUse) -> bool {
def_use.uses(local) == 1 && def_use.def(local).is_some()
} Try / catch
// compiler panic — guard the compile entry point
match baml_compile(source) {
Err(e) => log_internal_bug(e),
Ok(b) => use_bytecode(b),
} Prevention
- Promote a local to virtual only if its definition rvalue is recorded
- Fall back to a stack local when inlining is impossible
- Test the def-use optimization against edge-case control flow
- Keep optimization passes and emitter invariants covered by tests
When it happens
Trigger: Emitting a use of a virtual local whose def-use entry has no recorded definition rvalue — e.g. the definition was dropped by a pass but the use remained, or the local was misclassified as virtual.
Common situations: Compiler development around the def-use/virtual-local optimization; users see a compiler crash rather than a source-level error.
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
- array access should be either map or array.
- baml.panics.Unreachable
- class field count fits u32
- class offset fits u32
- enum offset fits u32
AI-assisted analysis of BoundaryML/baml@bd85ce9dee (2026-09-12).
Data as JSON: /api/errors/cdcba348e9ba6314.
Report an issue: GitHub.
Appendix: source
Thrown at baml_language/crates/baml_compiler2_emit/src/emit.rs:3371
fn pull_constant(&mut self, constant: &Constant<'ctx>) -> Result<(), Self::Error> {
self.emit_constant(constant);
Ok(())
}
fn pull_local(&mut self, local: Local) -> Result<LocalPullAction<'ctx>, Self::Error> {
let classification = self.analysis.classifications[&local];
let action = match classification {
LocalClassification::Virtual => {
// Attribute inlined virtual loads to their defining statement.
self.set_debug_span(self.def_span_for_local(local), false);
// Inline the definition rvalue at use site.
let rvalue = self.analysis.def_use[&local]
.def
.as_ref()
.map(|def| def.rvalue.clone())
.unwrap_or_else(|| panic!("virtual local {local} without definition"));
// MakeClosure must be handled specially: its captures need to load
// cell pointers (LoadVar) not cell values (LoadDeref). We intercept
// here so that `emit_rvalue_pull` (which sets loading_for_closure_capture)
// is called rather than the generic `walk_rvalue_pull` inlining path.
// MakeBoundMethod / MakeVirtualBoundMethod / VirtualFieldAccess must
// also be handled specially: none is handled by `walk_rvalue_pull`
// (which panics on them), so route through `emit_rvalue_pull`.
// BinaryOp must be routed through `emit_rvalue_pull` so that the
// type-aware specialization in `try_specialize_binary_op` can fire
// (e.g. emitting `CmpBigintOp` instead of the generic `CmpOp`).
// Class aggregates may use emitter-only spread helpers, so they
// also need to flow through `emit_rvalue_pull` when inlined.
if matches!(
rvalue,
Rvalue::MakeClosure { .. }
| Rvalue::MakeBoundMethod { .. }
| Rvalue::MakeVirtualBoundMethod { .. }
| Rvalue::MakeVirtualFunction { .. }View on GitHub (pinned to bd85ce9dee)