diff --git a/baml_language/crates/baml_compiler2_ast/src/ast.rs b/baml_language/crates/baml_compiler2_ast/src/ast.rs index c2559a08f7..b5bbaff993 100644 --- a/baml_language/crates/baml_compiler2_ast/src/ast.rs +++ b/baml_language/crates/baml_compiler2_ast/src/ast.rs @@ -640,7 +640,6 @@ pub enum BinaryOp { BitXor, Shl, Shr, - Instanceof, /// Null coalescing: `a ?? b` — returns `a` if non-null, else `b`. NullCoalesce, } @@ -666,7 +665,6 @@ impl std::fmt::Display for BinaryOp { BinaryOp::BitXor => "^", BinaryOp::Shl => "<<", BinaryOp::Shr => ">>", - BinaryOp::Instanceof => "instanceof", BinaryOp::NullCoalesce => "??", }; write!(f, "{s}") diff --git a/baml_language/crates/baml_compiler2_ast/src/lower_expr_body.rs b/baml_language/crates/baml_compiler2_ast/src/lower_expr_body.rs index 8de1418f23..b519a8d1ef 100644 --- a/baml_language/crates/baml_compiler2_ast/src/lower_expr_body.rs +++ b/baml_language/crates/baml_compiler2_ast/src/lower_expr_body.rs @@ -610,7 +610,11 @@ impl LoweringContext { SyntaxKind::CARET => op = Some(BinaryOp::BitXor), SyntaxKind::LESS_LESS => op = Some(BinaryOp::Shl), SyntaxKind::GREATER_GREATER => op = Some(BinaryOp::Shr), - SyntaxKind::KW_INSTANCEOF => op = Some(BinaryOp::Instanceof), + SyntaxKind::KW_INSTANCEOF => { + self.diags + .push(LoweringDiagnostic::InstanceofRemoved { span }); + return self.alloc_expr(Expr::Missing, node.text_range()); + } SyntaxKind::QUESTION_QUESTION => op = Some(BinaryOp::NullCoalesce), SyntaxKind::QUESTION if op.is_none() => { // Two consecutive QUESTION tokens = null coalescing (??) diff --git a/baml_language/crates/baml_compiler2_ast/src/lowering_diagnostic.rs b/baml_language/crates/baml_compiler2_ast/src/lowering_diagnostic.rs index a465aab2ef..d1da27ba93 100644 --- a/baml_language/crates/baml_compiler2_ast/src/lowering_diagnostic.rs +++ b/baml_language/crates/baml_compiler2_ast/src/lowering_diagnostic.rs @@ -81,6 +81,9 @@ pub enum LoweringDiagnostic { /// A byte string literal contains an invalid escape sequence. InvalidByteStringEscape { message: String, span: TextRange }, + /// The `instanceof` operator was used; it has been removed. Use `match` instead. + InstanceofRemoved { span: TextRange }, + /// `void` was used outside of a function return type position. VoidInNonReturnPosition { context: String, span: TextRange }, } @@ -186,6 +189,12 @@ impl LoweringDiagnostic { *span, "invalid escape", ), + LoweringDiagnostic::InstanceofRemoved { span } => ( + DiagnosticId::InstanceofRemoved, + "`instanceof` is no longer supported. Use a `match` expression for type checking instead.".to_string(), + *span, + "use `match` instead", + ), LoweringDiagnostic::VoidInNonReturnPosition { context, span } => ( DiagnosticId::VoidInNonReturnPosition, format!("`void` can only be used as a function return type, not as {context}"), diff --git a/baml_language/crates/baml_compiler2_emit/src/analysis.rs b/baml_language/crates/baml_compiler2_emit/src/analysis.rs index 36a438bb87..6282aa7884 100644 --- a/baml_language/crates/baml_compiler2_emit/src/analysis.rs +++ b/baml_language/crates/baml_compiler2_emit/src/analysis.rs @@ -849,6 +849,24 @@ fn collect_uses_in_terminator( Terminator::Throw { value } | Terminator::ThrowIfPanic { value, .. } => { collect_uses_in_operand(value, block, StatementRef::Terminator, def_use); } + Terminator::ShortCircuit { + operand, + destination, + .. + } => { + collect_uses_in_operand(operand, block, StatementRef::Terminator, def_use); + // Record the def for the destination + if let Place::Local(local) = destination { + if let Some(du) = def_use.get_mut(local) { + du.def = Some(DefLocation { + block, + statement_ref: StatementRef::Terminator, + rvalue: Rvalue::Use(Operand::Constant(Constant::Null)), + }); + du.all_defs.push((block, StatementRef::Terminator)); + } + } + } } } @@ -930,6 +948,12 @@ fn classify_locals( // Stack-carry candidate validated in a later stack simulation pass. stack_carry_candidates.insert(local, stack_carry::StackCarryKind::PhiLike); LocalClassification::Real + } else if is_short_circuit_phi(local, du, body) { + // ShortCircuit destination: the JumpIfFalse keeps lhs on TOS on the + // short-circuit path, and the rhs block leaves its result on TOS. + // Treated like PhiLike — value stays on the stack, no store/load. + stack_carry_candidates.insert(local, stack_carry::StackCarryKind::PhiLike); + LocalClassification::Real } else if is_return_phi(local, body, def_use, redirect_targets) { // Stack-carry candidate validated in a later stack simulation pass. stack_carry_candidates.insert(local, stack_carry::StackCarryKind::ReturnPhi); @@ -1039,6 +1063,25 @@ fn is_phi_like( true } +/// Check if a local is the destination of a `ShortCircuit` terminator and has +/// exactly one use in the join block. The `JumpIfFalse` peek instruction keeps +/// the LHS on TOS for the short-circuit path, while the rhs block leaves its +/// result on TOS. At the join, the value is on TOS from whichever path ran. +fn is_short_circuit_phi(local: Local, du: &LocalDefUse, body: &MirFunctionBody) -> bool { + if du.uses.len() != 1 { + return false; + } + + // One of the defs must be a ShortCircuit terminator targeting this local. + du.all_defs.iter().any(|&(block_id, ref stmt_ref)| { + *stmt_ref == StatementRef::Terminator + && matches!( + &body.block(block_id).terminator, + Some(Terminator::ShortCircuit { destination: Place::Local(l), .. }) if *l == local + ) + }) +} + /// Check if a MIR statement is stack-neutral (doesn't push or pop from the eval stack). /// /// Stack-neutral statements can safely execute while a value meant for return sits on diff --git a/baml_language/crates/baml_compiler2_emit/src/emit.rs b/baml_language/crates/baml_compiler2_emit/src/emit.rs index 7f3bd14d91..931bf21c4d 100644 --- a/baml_language/crates/baml_compiler2_emit/src/emit.rs +++ b/baml_language/crates/baml_compiler2_emit/src/emit.rs @@ -20,7 +20,7 @@ use bex_vm_types::{ Instruction, Object, ObjectIndex, ObjectPool, UnaryOp as VmUnaryOp, bytecode::{ BlockNotification, BlockNotificationType, DebugLocalScope, InstructionMeta, JumpTableData, - LineTableEntry, OperandMeta, + LineTableEntry, MatchHashEntry, MatchHashTable, OperandMeta, }, }; @@ -35,6 +35,9 @@ enum SwitchStrategy { JumpTable { min: i64, max: i64 }, /// Use binary search tree (O(log n) comparisons) for sparse integers. BinarySearch, + /// Use perfect hash + dense jump table (O(1) dispatch) for sparse ≥4-arm switches. + /// Replaces `BinarySearch` when a compile-time perfect hash is found. + PerfectHash(PerfectHashResult), /// Use linear if-else chain (O(n) comparisons). IfElseChain, } @@ -86,9 +89,20 @@ fn analyze_switch(arms: &[(i64, BlockId)]) -> SwitchStrategy { { SwitchStrategy::JumpTable { min, max } } - // Use binary search for sparse but large switch + // Use binary search for sparse but large switch, with perfect hash + // preferred when a compile-time hash is found (O(1) vs O(log K)). + // + // Perfect hashing is always attempted here. It's only reached when + // density is too low for JumpTable, so it won't interfere with dense + // enum discriminant switches. See `find_perfect_hash` for algorithm + // details and references. else if arms.len() >= BINARY_SEARCH_MIN_ARMS { - SwitchStrategy::BinarySearch + let keys: Vec<(i64, usize)> = arms.iter().enumerate().map(|(i, (v, _))| (*v, i)).collect(); + if let Some(result) = find_perfect_hash(&keys) { + SwitchStrategy::PerfectHash(result) + } else { + SwitchStrategy::BinarySearch + } } // Default to if-else chain for small switches else { @@ -96,6 +110,102 @@ fn analyze_switch(arms: &[(i64, BlockId)]) -> SwitchStrategy { } } +/// Result of a successful perfect hash search. +#[derive(Debug)] +struct PerfectHashResult { + multiply: u64, + shift: u8, + mask: u8, + /// Verification + dispatch entries indexed by hash slot. + entries: Vec, +} + +/// Find a minimal perfect hash for a small set of integer keys. +/// +/// Searches for constants `(M, S, mask)` such that +/// `h(x) = ((x as u64).wrapping_mul(M) >> S) & mask` +/// maps all keys to distinct slots in `[0, table_size)`. +/// +/// The search tries increasing table sizes (`next_power_of_two(K)`, then 2x) +/// and brute-forces M values with all 64 shift values. For K ≤ 20 this +/// completes in microseconds. +/// +/// Returns `None` if no perfect hash is found (practically impossible for +/// K ≤ 20, but handled for safety). +/// +/// # Algorithm +/// +/// This implements the multiply-shift hash family described in: +/// - Neumann & Göbbert, "Improving Switch Statement Performance with Hashing +/// Optimized at Compile Time" +/// - Dietz 1992, "Coding Multiway Branches Using Customized Hash Functions" +/// +/// The approach has been proposed for production compilers: LLVM issue #96971, +/// Roslyn #66604, Go #34381. +#[allow( + clippy::cast_possible_truncation, + clippy::cast_sign_loss, + clippy::cast_lossless +)] +fn find_perfect_hash(keys: &[(i64, usize)]) -> Option { + let k = keys.len(); + // Cap at 128: `MatchHashEntry::dense_index` is a `u8` (max 255), and + // the birthday paradox makes collision-free hashing increasingly unlikely + // past ~128 keys in a 256-slot table. Beyond this threshold the brute-force + // search would waste ~20-40ms of compile time before giving up. Falls back + // to binary search (O(log K)) which is fine at this scale. + if k == 0 || k > 128 { + return None; + } + + // Try table sizes: tight (next power of 2), then 2x for easier search. + for table_size in [k.next_power_of_two(), (k.next_power_of_two() * 2).min(256)] { + let mask = (table_size - 1) as u8; + let mut slots = vec![false; table_size]; + + for m in 1u64..10_000 { + for s in 0u8..64 { + // Test if (m, s) produces distinct hashes for all keys. + slots.fill(false); + let mut ok = true; + for &(key, _) in keys { + let h = ((key as u64).wrapping_mul(m) >> s) & mask as u64; + let h = h as usize; + if h >= table_size || slots[h] { + ok = false; + break; + } + slots[h] = true; + } + if ok { + // Build the verification + dispatch table. + let mut entries = vec![ + MatchHashEntry { + expected_tag: i64::MIN, // sentinel for empty slots + dense_index: 0, + }; + table_size + ]; + for (dense_idx, &(key, _arm_idx)) in keys.iter().enumerate() { + let h = ((key as u64).wrapping_mul(m) >> s) & mask as u64; + entries[h as usize] = MatchHashEntry { + expected_tag: key, + dense_index: dense_idx as u8, + }; + } + return Some(PerfectHashResult { + multiply: m, + shift: s, + mask, + entries, + }); + } + } + } + } + None +} + use crate::{ MirCodegenContext, analysis::{AnalysisResult, LocalClassification, StatementRef}, @@ -1081,6 +1191,15 @@ impl<'ctx, 'obj> StackifyCodegen<'ctx, 'obj> { &name_map, ); } + SwitchStrategy::PerfectHash(hash_result) => { + self.emit_switch_perfect_hash( + discriminant, + arms, + *otherwise, + hash_result, + &name_map, + ); + } SwitchStrategy::BinarySearch => { self.emit_switch_binary_search( discriminant, @@ -1203,6 +1322,40 @@ impl<'ctx, 'obj> StackifyCodegen<'ctx, 'obj> { self.emit(Instruction::ThrowIfPanic); self.emit_jump_unless_fallthrough(*otherwise); } + + Terminator::ShortCircuit { + operand, + is_and, + destination: _, + eval_rhs, + join, + } => { + // Legacy-style short-circuit using JumpIfFalse (peek, no pop). + // The destination local is PhiLike — value stays on TOS, no store/load. + self.emit_operand_pull(operand); + + if *is_and { + // &&: false → short-circuit (value stays on TOS), jump to join. + // true → pop, evaluate rhs. + let sc_jump = self.emit(Instruction::JumpIfFalse(0)); + let resolved_join = self.resolve_pending_target(*join); + self.pending_jumps.push((sc_jump, resolved_join)); + self.emit(Instruction::Pop(1)); + self.emit_jump_unless_fallthrough(*eval_rhs); + } else { + // ||: false → pop, evaluate rhs. + // true → value stays on TOS, jump to join. + let false_jump = self.emit(Instruction::JumpIfFalse(0)); + let resolved_join = self.resolve_pending_target(*join); + let true_jump = self.emit(Instruction::Jump(0)); + self.pending_jumps.push((true_jump, resolved_join)); + // False landing: patch JumpIfFalse to here, pop, fall to eval_rhs. + let false_pc = self.bytecode.instructions.len(); + self.patch_jump_to(false_jump, false_pc); + self.emit(Instruction::Pop(1)); + self.emit_jump_unless_fallthrough(*eval_rhs); + } + } } } @@ -1245,6 +1398,9 @@ impl<'ctx, 'obj> StackifyCodegen<'ctx, 'obj> { Instruction::PopJumpIfFalse(_) => { self.bytecode.instructions[instruction_idx] = Instruction::PopJumpIfFalse(offset); } + Instruction::JumpIfFalse(_) => { + self.bytecode.instructions[instruction_idx] = Instruction::JumpIfFalse(offset); + } _ => panic!("expected jump instruction at index {instruction_idx}"), } } @@ -1355,24 +1511,37 @@ impl<'ctx, 'obj> StackifyCodegen<'ctx, 'obj> { exhaustive: bool, name_map: &std::collections::HashMap, ) { - self.emit_operand_pull(discriminant); + // Single exhaustive arm: no comparison needed, skip the discriminant entirely. + if exhaustive && arms.len() == 1 { + self.emit_jump_unless_fallthrough(arms[0].1); + return; + } + // Each arm re-loads the discriminant from the operand instead of + // keeping it on the stack with copy/pop. This makes each arm + // self-contained and avoids stack cleanup instructions. let num_arms = arms.len(); for (i, (value, target)) in arms.iter().enumerate() { let is_last = i == num_arms - 1; - // For exhaustive switches, skip the last arm's comparison + // For exhaustive switches, skip the last arm's comparison. if exhaustive && is_last { - self.emit(Instruction::Pop(1)); // Pop discriminant self.emit_jump_unless_fallthrough(*target); return; } let label = Self::switch_label(*value, name_map); - self.emit_compare_and_branch(*value, *target, label); + self.emit_operand_pull(discriminant); + let idx = self.add_constant(ConstValue::Int(*value)); + let inst = self.emit(Instruction::LoadConst(idx)); + self.set_operand(inst, OperandMeta::Const(label)); + self.emit(Instruction::CmpOp(CmpOp::Eq)); + let jump_idx = self.emit(Instruction::PopJumpIfFalse(0)); + self.emit_jump_unless_fallthrough(*target); + let skip_to = self.current_pc(); + self.patch_jump_to(jump_idx, skip_to); } - self.emit(Instruction::Pop(1)); self.emit_jump_unless_fallthrough(otherwise); } @@ -1508,6 +1677,101 @@ impl<'ctx, 'obj> StackifyCodegen<'ctx, 'obj> { } } + /// Emit switch using perfect hash + dense jump table (O(1) dispatch). + /// + /// The `MatchHash` instruction remaps the sparse type tag to a dense + /// `[0, K-1]` index via a compile-time minimal perfect hash. A subsequent + /// `JumpTable` dispatches on the dense index. `MatchHash` pushes `-1` for + /// unknown tags, which falls to the jump table's default arm. + /// + /// This replaces O(log K) `BinarySearch` (which emits ~4K instructions for + /// K=8) with just 3 instructions: `type_tag` + `match_hash` + `jump_table`. + /// + /// The perfect hash uses the multiply-shift family: + /// `h(tag) = ((tag as u64).wrapping_mul(M) >> S) & mask` + /// + /// References: + /// - Neumann & Göbbert, "Improving Switch Statement Performance with + /// Hashing Optimized at Compile Time" + /// - Dietz 1992, "Coding Multiway Branches Using Customized Hash Functions" + /// - Proposed for LLVM (#96971), Roslyn (#66604), Go (#34381) + #[allow(clippy::cast_possible_wrap)] + fn emit_switch_perfect_hash( + &mut self, + discriminant: &Operand, + arms: &[(i64, BlockId)], + otherwise: BlockId, + hash_result: PerfectHashResult, + name_map: &std::collections::HashMap, + ) { + // 1. Push discriminant (type tag) onto stack — consumed by DenseTag. + self.emit_operand_pull(discriminant); + + // 2. Store the MatchHashTable in bytecode and emit DenseTag instruction. + let key_names: Vec = arms + .iter() + .map(|(v, _)| { + name_map + .get(v) + .map(ToString::to_string) + .unwrap_or_else(|| v.to_string()) + }) + .collect(); + let table = MatchHashTable { + multiply: hash_result.multiply, + shift: hash_result.shift, + mask: hash_result.mask, + entries: hash_result.entries, + key_names, + }; + let hash_table_idx = self.bytecode.match_hash_tables.len(); + self.bytecode.match_hash_tables.push(table); + self.emit(Instruction::DenseTag(hash_table_idx)); + + // 3. Emit a dense JumpTable over [0, K-1]. + // The DenseTag output is dense by construction, so this is always + // a compact table with no holes. We emit the JumpTable directly + // (not via emit_switch_jump_table) because the dense index is + // already on the stack from DenseTag — we must not re-push the + // original discriminant. + let k = arms.len(); + let dense_min = 0i64; + let dense_max = (k - 1) as i64; + + let jt_table_idx = self.pending_jump_tables.len(); + let mut jt = JumpTableData::new(dense_min, dense_max); + + // Set symbolic names from the original arm names. + for (dense_idx, (orig_value, _)) in arms.iter().enumerate() { + if let Some(&name) = name_map.get(orig_value) { + jt.set_name(dense_idx as i64, name.to_string()); + } + } + + // Build dense arm mapping: dense_index → original BlockId. + let resolved_arms: Vec<(i64, PendingJumpTarget)> = arms + .iter() + .enumerate() + .map(|(dense_idx, (_, target))| { + (dense_idx as i64, self.resolve_pending_target(*target)) + }) + .collect(); + let resolved_otherwise = self.resolve_pending_target(otherwise); + + let jump_table_pc = self.emit(Instruction::JumpTable { + table_idx: jt_table_idx, + default: 0, // Will be patched later. + }); + + self.pending_jump_tables.push(PendingJumpTable { + table_idx: jt_table_idx, + jump_table_pc, + arms: resolved_arms, + otherwise: resolved_otherwise, + table: jt, + }); + } + // ======================================================================== // Switch helpers // ======================================================================== @@ -1523,6 +1787,10 @@ impl<'ctx, 'obj> StackifyCodegen<'ctx, 'obj> { /// Emit: copy TOS, compare with `value` for equality; if equal, pop /// discriminant and jump to `target`. On mismatch, fall through. + /// + /// Used by binary search where the discriminant stays on the stack across + /// the tree traversal. The if-else chain uses a different strategy + /// (re-loading the discriminant per arm) to avoid copy/pop overhead. fn emit_compare_and_branch(&mut self, value: i64, target: BlockId, label: String) { self.emit(Instruction::Copy(0)); let idx = self.add_constant(ConstValue::Int(value)); @@ -1841,15 +2109,12 @@ impl PullSink for StackifyCodegen<'_, '_> { } fn is_type(&mut self, ty: &Ty) -> Result<(), Self::Error> { - // Emit instanceof check for nominal class checks. if let Ty::Class(tn, _) | Ty::TypeAlias(tn, _) = ty { let class_name_str = tn.display_name.as_str(); if let Some(&class_obj_idx) = self.class_object_indices.get(class_name_str) { - let class_const = - self.add_constant(ConstValue::Object(ObjectIndex::from_raw(class_obj_idx))); - let inst = self.emit(Instruction::LoadConst(class_const)); + let c = self.add_constant(ConstValue::Object(ObjectIndex::from_raw(class_obj_idx))); + let inst = self.emit(Instruction::IsType(c)); self.set_operand(inst, OperandMeta::Const(class_name_str.to_string())); - self.emit(Instruction::CmpOp(CmpOp::InstanceOf)); } else { self.emit(Instruction::Pop(1)); let idx = self.add_constant(ConstValue::Bool(false)); @@ -1859,7 +2124,6 @@ impl PullSink for StackifyCodegen<'_, '_> { return Ok(()); } - // Primitive and builtin runtime kinds use type tags. let type_tag = match ty { Ty::Int { .. } => Some(baml_type::typetag::INT), Ty::String { .. } => Some(baml_type::typetag::STRING), @@ -1881,11 +2145,9 @@ impl PullSink for StackifyCodegen<'_, '_> { }; if let Some(tag) = type_tag { - self.emit(Instruction::TypeTag); - let idx = self.add_constant(ConstValue::Int(tag)); - let inst = self.emit(Instruction::LoadConst(idx)); - self.set_operand(inst, OperandMeta::Const(tag.to_string())); - self.emit(Instruction::CmpOp(CmpOp::Eq)); + let c = self.add_constant(ConstValue::Int(tag)); + let inst = self.emit(Instruction::IsType(c)); + self.set_operand(inst, OperandMeta::Const(ty.to_string())); } else { self.emit(Instruction::Pop(1)); let idx = self.add_constant(ConstValue::Bool(false)); diff --git a/baml_language/crates/baml_compiler2_emit/src/stack_carry.rs b/baml_language/crates/baml_compiler2_emit/src/stack_carry.rs index 4f07b6d06c..d67bfe9e82 100644 --- a/baml_language/crates/baml_compiler2_emit/src/stack_carry.rs +++ b/baml_language/crates/baml_compiler2_emit/src/stack_carry.rs @@ -544,6 +544,24 @@ fn simulate_terminator_stack( // or continues (consuming it). Either way the stack is clean after. sim.pop_n(1) } + Terminator::ShortCircuit { operand, .. } => { + // ShortCircuit peeks the operand (stays on TOS), then conditionally + // keeps or pops+evaluates-rhs. If the carried local is the operand, + // the peek consumes its stack-carry lifecycle — the short-circuit + // mechanism takes ownership of the value on TOS. If the carried local + // was consumed by an earlier statement, the operand pull just adds to + // the stack which is fine at block-end. + let mut sink = StackCarryPullSink { + sim, + carried_local, + classifications, + def_use, + }; + if pull_semantics::walk_operand_pull(&mut sink, operand).is_err() { + return false; + } + true + } } } diff --git a/baml_language/crates/baml_compiler2_mir/src/builder.rs b/baml_language/crates/baml_compiler2_mir/src/builder.rs index 7b38be8616..c4685af135 100644 --- a/baml_language/crates/baml_compiler2_mir/src/builder.rs +++ b/baml_language/crates/baml_compiler2_mir/src/builder.rs @@ -259,6 +259,24 @@ impl MirBuilder { }); } + /// Emit a short-circuit `&&` / `||` terminator. + pub(crate) fn short_circuit( + &mut self, + operand: Operand, + is_and: bool, + destination: Place, + eval_rhs: BlockId, + join: BlockId, + ) { + self.set_terminator(Terminator::ShortCircuit { + operand, + is_and, + destination, + eval_rhs, + join, + }); + } + /// Emit a multi-way switch. /// /// If `exhaustive` is true, the switch covers all possible discriminant values, diff --git a/baml_language/crates/baml_compiler2_mir/src/ir.rs b/baml_language/crates/baml_compiler2_mir/src/ir.rs index 85a01bf0f8..57c22fcf2d 100644 --- a/baml_language/crates/baml_compiler2_mir/src/ir.rs +++ b/baml_language/crates/baml_compiler2_mir/src/ir.rs @@ -375,6 +375,24 @@ pub enum Terminator { /// Used before wildcard catch arms to prevent them from swallowing /// panics the programmer didn't explicitly name. ThrowIfPanic { value: Operand, otherwise: BlockId }, + + /// Short-circuit `&&` / `||`. + /// + /// Evaluates `operand` and peeks at the result (without popping): + /// - `&&` (`is_and = true`): if false, jump to `join` (value stays on stack); + /// if true, pop and fall through to `eval_rhs`. + /// - `||` (`is_and = false`): if true, jump to `join` (value stays on stack); + /// if false, pop and fall through to `eval_rhs`. + /// + /// The `eval_rhs` block must assign to `destination` and then goto `join`. + /// At `join`, `destination` is on TOS from whichever path executed. + ShortCircuit { + operand: Operand, + is_and: bool, + destination: Place, + eval_rhs: BlockId, + join: BlockId, + }, } impl Terminator { @@ -413,6 +431,7 @@ impl Terminator { } Terminator::Throw { .. } => vec![], Terminator::ThrowIfPanic { otherwise, .. } => vec![*otherwise], + Terminator::ShortCircuit { eval_rhs, join, .. } => vec![*eval_rhs, *join], } } } @@ -546,7 +565,7 @@ pub enum Rvalue { /// Extract runtime type tag from any value: `type_tag(_1)` /// - /// Used for jump table dispatch on union types (instanceof patterns). + /// Used for jump table dispatch on union types (type patterns in match). /// Type tags are global constants: /// - Primitives: `int=0`, `string=1`, `bool=2`, `null=3`, `float=4` /// - Classes: assigned unique IDs starting at 100 diff --git a/baml_language/crates/baml_compiler2_mir/src/lower.rs b/baml_language/crates/baml_compiler2_mir/src/lower.rs index 8a553eb158..4a40c8c792 100644 --- a/baml_language/crates/baml_compiler2_mir/src/lower.rs +++ b/baml_language/crates/baml_compiler2_mir/src/lower.rs @@ -16,9 +16,9 @@ use crate::{ /// Classifies what kind of switch a match/catch expression lowers to. /// -/// `Integer` is for integer literal patterns; `EnumDiscriminant` is for -/// enum variant patterns; `TypeTag` is for type-annotated patterns dispatched -/// via `Rvalue::TypeTag` (e.g. `i: int => ...`, `s: string => ...`). +/// `Integer` and `EnumDiscriminant` are currently implemented. +/// `TypeTag` dispatches class-type and primitive-type match arms via runtime +/// type tags, using `Rvalue::TypeTag` for the switch operand. enum SwitchKind { Integer, EnumDiscriminant(Name), @@ -223,7 +223,9 @@ pub fn convert_tir2_ty(ty: &Tir2Ty, resolved: &ResolvedAliases) -> Ty { // ─── def_to_item_ref helper ────────────────────────────────────────────────── -use baml_compiler2_hir::{contributions::Definition, file_package::file_package}; +use baml_compiler2_hir::{ + compiler2_all_files, contributions::Definition, file_package::file_package, +}; // Use the PPIR item tree (which includes synthetic *$stream items) rather than // the bare HIR item tree. TIR resolves methods using PPIR `LocalItemId`s, so // MIR must use the same tree to avoid index mismatches. @@ -378,12 +380,8 @@ struct LoweringContext<'db> { // enum's package at each match site. class_fields: IndexMap>, enum_variants: IndexMap>, - // Pre-computed type tags for class types. - // Built during construction for potential use by switch optimization, but currently - // unused because the tag assignment order here differs from the emit crate's order, - // making cross-comparison incorrect. Class catch arms use `instanceof` (sequential - // dispatch) instead. Kept for future work if tag ordering is unified. - #[allow(dead_code)] + /// Pre-computed type tags for class types, used by `SwitchKind::TypeTag` + /// for union-type switch optimization (ported from MIR 1). class_type_tags: IndexMap, // Pre-computed type alias data for inline expansion in convert_tir2_ty @@ -425,15 +423,15 @@ struct LoweringContext<'db> { } impl<'db> LoweringContext<'db> { - /// Populate `class_fields`, `class_type_tags`, and `enum_variants` from a single - /// package's items. + /// Populate `class_fields` and `enum_variants` from a single package's items. + /// + /// Note: `class_type_tags` is built separately via `build_class_type_tags` to ensure + /// the same file-iteration order as the emitter (`generate_project_bytecode`). fn populate_from_package( db: &'db dyn crate::Db, pkg_items: &baml_compiler2_hir::package::PackageItems<'db>, pkg_name: &Name, class_fields: &mut IndexMap>, - class_type_tags: &mut IndexMap, - class_type_tag_counter: &mut i64, enum_variants: &mut IndexMap>, ) { for (ns_names, ns) in &pkg_items.namespaces { @@ -458,9 +456,6 @@ impl<'db> LoweringContext<'db> { for (idx, field) in class_data.fields.iter().enumerate() { fields.insert(field.name.to_string(), idx); } - let type_tag = baml_type::typetag::CLASS_BASE + *class_type_tag_counter; - *class_type_tag_counter += 1; - class_type_tags.insert(tn.clone(), type_tag); class_fields.insert(tn, fields); } Definition::Enum(enum_loc) => { @@ -480,6 +475,40 @@ impl<'db> LoweringContext<'db> { } } + /// Build `class_type_tags` by iterating `compiler2_all_files` in the same order as the + /// emitter (`generate_project_bytecode` in `baml_compiler2_emit`). This guarantees that + /// the integer type tags stored in Switch arms exactly match the `class.type_tag` values + /// assigned to runtime Class objects. + fn build_class_type_tags(db: &'db dyn crate::Db) -> IndexMap { + let all_files = compiler2_all_files(db); + let mut class_type_tags: IndexMap = IndexMap::new(); + let mut class_type_tag_counter = 0i64; + + for file in &all_files { + let item_tree = file_item_tree(db, *file); + let pkg_info = file_package(db, *file); + + // Build module_path: [package] ++ namespace_path + let mut module_path: Vec = vec![pkg_info.package.clone()]; + module_path.extend(pkg_info.namespace_path.iter().cloned()); + + for class_data in item_tree.classes.values() { + let tn = TypeName { + name: class_data.name.clone(), + module_path: module_path.clone(), + display_name: class_data.name.clone(), + }; + let type_tag = baml_type::typetag::CLASS_BASE + class_type_tag_counter; + class_type_tag_counter += 1; + // Use entry to avoid overwriting if the same class appears via multiple paths + // (e.g., both FQ and short names). First encounter wins — consistent with emit.rs. + class_type_tags.entry(tn).or_insert(type_tag); + } + } + + class_type_tags + } + fn new( db: &'db dyn crate::Db, func_loc: FunctionLoc<'db>, @@ -576,13 +605,11 @@ impl<'db> LoweringContext<'db> { ); } - // --- Build class_fields / enum_variants / class_type_tags from PackageItems --- + // --- Build class_fields / enum_variants from PackageItems --- let pkg_info = file_package(db, file); let pkg_id = PackageId::new(db, pkg_info.package.clone()); let mut class_fields: IndexMap> = IndexMap::new(); - let mut class_type_tags: IndexMap = IndexMap::new(); - let mut class_type_tag_counter = 0i64; let mut enum_variants: IndexMap> = IndexMap::new(); // Include classes from dependency packages first (e.g., "baml" builtins). @@ -595,8 +622,6 @@ impl<'db> LoweringContext<'db> { dep_items, &dep_name, &mut class_fields, - &mut class_type_tags, - &mut class_type_tag_counter, &mut enum_variants, ); } @@ -608,11 +633,13 @@ impl<'db> LoweringContext<'db> { pkg_items, &pkg_info.package, &mut class_fields, - &mut class_type_tags, - &mut class_type_tag_counter, &mut enum_variants, ); + // Build class_type_tags using the same file-iteration order as the emitter, + // so that switch arms get the same integer tags as runtime class.type_tag fields. + let class_type_tags = Self::build_class_type_tags(db); + let resolved_aliases = ResolvedAliases::for_package(db, pkg_id); // --- Determine arity from function signature --- @@ -752,13 +779,11 @@ impl<'db> LoweringContext<'db> { ); } - // --- Build class_fields / enum_variants / class_type_tags from PackageItems --- + // --- Build class_fields / enum_variants from PackageItems --- let pkg_info = file_package(db, file); let pkg_id = PackageId::new(db, pkg_info.package.clone()); let mut class_fields: IndexMap> = IndexMap::new(); - let mut class_type_tags: IndexMap = IndexMap::new(); - let mut class_type_tag_counter = 0i64; let mut enum_variants: IndexMap> = IndexMap::new(); // Include classes from dependency packages first. @@ -770,8 +795,6 @@ impl<'db> LoweringContext<'db> { dep_items, &dep_name, &mut class_fields, - &mut class_type_tags, - &mut class_type_tag_counter, &mut enum_variants, ); } @@ -783,11 +806,13 @@ impl<'db> LoweringContext<'db> { pkg_items, &pkg_info.package, &mut class_fields, - &mut class_type_tags, - &mut class_type_tag_counter, &mut enum_variants, ); + // Build class_type_tags using the same file-iteration order as the emitter, + // so that switch arms get the same integer tags as runtime class.type_tag fields. + let class_type_tags = Self::build_class_type_tags(db); + let resolved_aliases = ResolvedAliases::for_package(db, pkg_id); LoweringContext { @@ -1705,8 +1730,6 @@ impl LoweringContext<'_> { AstBinaryOp::Shr => Some(BinOp::Shr), // Short-circuit operators handled separately AstBinaryOp::And | AstBinaryOp::Or => None, - // Instanceof is not a simple binary op at MIR level - AstBinaryOp::Instanceof => None, // Null coalescing desugars to control flow, not a binary op AstBinaryOp::NullCoalesce => None, } @@ -1727,20 +1750,6 @@ impl LoweringContext<'_> { AstBinaryOp::Or => { return self.lower_short_circuit(expr_id, lhs, rhs, dest, false); } - AstBinaryOp::Instanceof => { - // Lower as IsType check - let lhs_op = self.lower_to_operand(lhs); - // Get the type from the expression type map (the rhs is a type name) - let check_ty = self.expr_ty(expr_id); - self.builder.assign( - dest, - Rvalue::IsType { - operand: lhs_op, - ty: check_ty, - }, - ); - return; - } AstBinaryOp::NullCoalesce => { return self.lower_null_coalesce(expr_id, lhs, rhs, dest); } @@ -1783,17 +1792,15 @@ impl LoweringContext<'_> { is_and: bool, ) { let lhs_op = self.lower_to_operand(lhs); - self.builder - .assign(dest.clone(), Rvalue::Use(lhs_op.clone())); let bb_rhs = self.builder.create_block(); let bb_join = self.builder.create_block(); - if is_and { - self.builder.branch(lhs_op, bb_rhs, bb_join); - } else { - self.builder.branch(lhs_op, bb_join, bb_rhs); - } + // ShortCircuit terminator: JumpIfFalse (peek) keeps lhs on TOS + // when short-circuiting. The rhs block evaluates and leaves its + // result on TOS. At join, dest is on TOS (PhiLike). + self.builder + .short_circuit(lhs_op, is_and, dest.clone(), bb_rhs, bb_join); self.builder.set_current_block(bb_rhs); self.lower_expr(rhs, dest); @@ -3397,7 +3404,7 @@ impl LoweringContext<'_> { return; } - self.lower_match_chain(scrutinee_local, &arms, dest, bb_join); + self.lower_match_chain(scrutinee_local, &arms, dest, bb_join, is_exhaustive); self.builder.set_current_block(bb_join); } @@ -3521,7 +3528,8 @@ impl LoweringContext<'_> { int_arms.push((disc, i)); } } - AstPattern::TypedBinding { .. } => { + AstPattern::Binding(_) | AstPattern::TypedBinding { .. } => { + // Type-pattern sub-arm: look up type tag via TIR. match &switch_kind { None => switch_kind = Some(SwitchKind::TypeTag), Some(SwitchKind::TypeTag) => {} @@ -3538,30 +3546,6 @@ impl LoweringContext<'_> { None => return false, } } - AstPattern::Binding(name) if name.as_str() != "_" => { - if self - .pat_types - .contains_key(&(self.current_scope, *sub_pat_id)) - { - match &switch_kind { - None => switch_kind = Some(SwitchKind::TypeTag), - Some(SwitchKind::TypeTag) => {} - Some(_) => return false, - } - match self.classify_pattern_type_tag(*sub_pat_id) { - Some(tags) => { - for tag in tags { - if seen_values.insert(tag) { - int_arms.push((tag, i)); - } - } - } - None => return false, - } - } else { - return false; - } - } _ => return false, } } @@ -3617,27 +3601,36 @@ impl LoweringContext<'_> { } otherwise_idx = Some(i); } - _ => return false, // Non-eligible pattern + AstPattern::Null | AstPattern::Literal(_) => return false, } } - // Need at least one int arm to justify a switch + // Need at least one int arm to justify a switch. if int_arms.is_empty() { return false; } - // TypeTag switches produce copy/pop overhead in the stackified bytecode - // because the emitter may fall back to if-else chains for sparse or small - // switches. The old IsType + Branch chain avoids this overhead, so we only - // use SwitchInt for enum discriminants and integer literals where jump_table - // reliably fires. - if matches!(switch_kind, Some(SwitchKind::TypeTag)) { + // TypeTag switches only pay off at 4+ arms (JumpTable). For fewer arms + // the sequential `is_type` chain is more compact because the if-else + // chain adds copy/pop stack management overhead per arm. + if matches!(switch_kind, Some(SwitchKind::TypeTag)) && int_arms.len() < 4 { return false; } - // Exhaustiveness is determined by TIR's type checker, not re-derived here. - // `(exhaustive)` means: no wildcard arm AND TIR confirmed all cases covered. - let is_switch_exhaustive = otherwise_idx.is_none() && is_exhaustive; + // Exhaustiveness: for **match** TypeTag switches without a wildcard arm, + // all typed arms together cover the union — the otherwise block is dead. + // TIR's `required_match_cases` returns None for class types, so class + // unions are never marked exhaustive by TIR even when all arms are + // covered. For match + TypeTag, if there's no wildcard, treat as + // exhaustive so the last arm skips its comparison and the otherwise + // block becomes Unreachable. + // + // For **catch** expressions, we never mark the switch as exhaustive + // even when all declared thrown types are covered, because panics can + // always occur at runtime and must be rethrown via the otherwise block. + let is_match = matches!(&otherwise, SwitchOtherwise::Match { .. }); + let is_switch_exhaustive = otherwise_idx.is_none() + && (is_exhaustive || (is_match && matches!(switch_kind, Some(SwitchKind::TypeTag)))); // Save the entry block — this is where the switch terminator goes let bb_entry = self.builder.current_block(); @@ -3656,12 +3649,14 @@ impl LoweringContext<'_> { Operand::Copy(Place::Local(disc)) } Some(SwitchKind::TypeTag) => { - let tag = self.builder.temp(Ty::Int { + let tag_local = self.builder.temp(Ty::Int { attr: TyAttr::default(), }); - self.builder - .assign(Place::local(tag), Rvalue::TypeTag(Place::local(scrutinee))); - Operand::Copy(Place::Local(tag)) + self.builder.assign( + Place::local(tag_local), + Rvalue::TypeTag(Place::local(scrutinee)), + ); + Operand::Copy(Place::Local(tag_local)) } _ => Operand::Copy(Place::Local(scrutinee)), }; @@ -3723,10 +3718,24 @@ impl LoweringContext<'_> { vec![] } } - Some(SwitchKind::TypeTag) => int_arms - .iter() - .map(|(v, _)| (*v, format_type_tag_name(*v))) - .collect(), + Some(SwitchKind::TypeTag) => { + // Reverse map: tag value → human-readable type name. + let reverse_class: std::collections::HashMap = self + .class_type_tags + .iter() + .map(|(tn, tag)| (*tag, tn.name.as_str())) + .collect(); + int_arms + .iter() + .map(|(v, _)| { + let name = reverse_class + .get(v) + .map(ToString::to_string) + .unwrap_or_else(|| format_type_tag_name(*v)); + (*v, name) + }) + .collect() + } _ => int_arms.iter().map(|(v, _)| (*v, v.to_string())).collect(), }; @@ -3751,19 +3760,31 @@ impl LoweringContext<'_> { self.builder.goto(join); } } else { - // No wildcard - match &otherwise { - SwitchOtherwise::Match { - is_exhaustive: true, - } => { - self.builder.unreachable(); - } - SwitchOtherwise::Catch { error_local, .. } => { - self.builder - .throw(Operand::Copy(Place::Local(*error_local))); + // No wildcard — decide what the otherwise block does. + // Use `is_switch_exhaustive` (which may be inferred for TypeTag) + // rather than the caller's original `is_exhaustive`, so the + // otherwise block stays consistent with the switch terminator flag. + if is_switch_exhaustive { + match &otherwise { + SwitchOtherwise::Match { .. } => { + self.builder.unreachable(); + } + SwitchOtherwise::Catch { error_local, .. } => { + // Even if exhaustive, catch otherwise should rethrow + // (the error might not match any arm at runtime). + self.builder + .throw(Operand::Copy(Place::Local(*error_local))); + } } - SwitchOtherwise::Match { .. } => { - self.builder.goto(join); + } else { + match &otherwise { + SwitchOtherwise::Catch { error_local, .. } => { + self.builder + .throw(Operand::Copy(Place::Local(*error_local))); + } + SwitchOtherwise::Match { .. } => { + self.builder.goto(join); + } } } } @@ -3805,6 +3826,7 @@ impl LoweringContext<'_> { arms: &[baml_compiler2_ast::MatchArm], dest: Place, join: BlockId, + exhaustive: bool, ) { if arms.is_empty() { // No more arms to test. Either a preceding wildcard/binding arm @@ -3818,6 +3840,16 @@ impl LoweringContext<'_> { let arm = &arms[0]; let rest = &arms[1..]; + // Exhaustive last arm: skip the pattern test — it must match. + if exhaustive && rest.is_empty() && arm.guard.is_none() { + self.bind_pattern(scrutinee, arm.pattern); + self.lower_expr(arm.body, dest); + if !self.builder.is_current_terminated() { + self.builder.goto(join); + } + return; + } + let bb_body = self.builder.create_block(); let bb_next = self.builder.create_block(); @@ -3837,7 +3869,7 @@ impl LoweringContext<'_> { } self.builder.set_current_block(bb_next); - self.lower_match_chain(scrutinee, rest, dest, join); + self.lower_match_chain(scrutinee, rest, dest, join, exhaustive); } /// Emit an `IsType` check that handles union types by expanding them @@ -3879,6 +3911,21 @@ impl LoweringContext<'_> { } } + /// Look up the integer type tag for a type. Returns `Some(tag)` for + /// primitives (INT=0, STRING=1, etc.) and classes (`CLASS_BASE` + index), + /// or `None` for types that don't have a tag (unions, generics, etc.). + fn type_tag_for_ty(&self, ty: &Ty) -> Option { + match ty { + Ty::Int { .. } => Some(baml_type::typetag::INT), + Ty::String { .. } => Some(baml_type::typetag::STRING), + Ty::Bool { .. } => Some(baml_type::typetag::BOOL), + Ty::Null { .. } => Some(baml_type::typetag::NULL), + Ty::Float { .. } => Some(baml_type::typetag::FLOAT), + Ty::Class(tn, _) => self.class_type_tags.get(tn).copied(), + _ => None, + } + } + fn lower_pattern_test( &mut self, scrutinee: Local, @@ -4033,12 +4080,13 @@ impl LoweringContext<'_> { impl LoweringContext<'_> { /// Classify a pattern into type tag value(s) for switch dispatch. + /// Classify a pattern as type-tag-eligible and return its tag(s). + /// /// Shared by match and catch lowering. /// /// Returns `Some(tags)` for `TypedBinding` and Binding-with-TIR-type patterns - /// that resolve to primitive types. Returns `None` for literals, wildcards, - /// enum variants, and class types (class tag ordering differs between MIR - /// and emit, so class dispatch stays sequential). + /// that resolve to primitive or class types. Returns `None` for literals, + /// wildcards, enum variants, and types without tag mappings. fn classify_pattern_type_tag(&self, pat_id: AstPatId) -> Option> { let pat = &self.body.patterns[pat_id]; match pat { @@ -4047,16 +4095,16 @@ impl LoweringContext<'_> { None } AstPattern::Binding(_) => { - // Named binding resolved by TIR to a class type + // Named binding resolved by TIR to a type let tir_ty = self.pat_types.get(&(self.current_scope, pat_id))?; - let resolved = convert_tir2_ty(tir_ty, &self.resolved_aliases); - Self::ty_to_type_tags(&resolved) + let resolved = self.resolved_aliases.convert(tir_ty); + self.ty_to_type_tags(&resolved) } AstPattern::TypedBinding { .. } => { // Type-annotated binding — always resolved by TIR let tir_ty = self.pat_types.get(&(self.current_scope, pat_id))?; - let resolved = convert_tir2_ty(tir_ty, &self.resolved_aliases); - Self::ty_to_type_tags(&resolved) + let resolved = self.resolved_aliases.convert(tir_ty); + self.ty_to_type_tags(&resolved) } // Literal, Null, EnumVariant, Union — not type-tag eligible _ => None, @@ -4066,32 +4114,20 @@ impl LoweringContext<'_> { /// Convert a `Ty` to the list of type tag integers it corresponds to. /// Returns `None` if the type has no simple tag representation. /// - /// Only primitive types with fixed, globally-stable type tags are eligible. - /// Class types are NOT eligible here: the `class_type_tags` map in - /// `LoweringContext` assigns tags in a different order than the emit crate, - /// so class tags from this map would not match the runtime objects. Class - /// catch arms correctly use the `instanceof` path (sequential dispatch fallback). - fn ty_to_type_tags(ty: &Ty) -> Option> { + /// Supports primitives (globally-stable tags) and class types (looked up + /// from `class_type_tags`). Union types are flattened — all members must + /// be tag-eligible. + fn ty_to_type_tags(&self, ty: &Ty) -> Option> { match ty { - Ty::Int { .. } => Some(vec![baml_type::typetag::INT]), - Ty::String { .. } => Some(vec![baml_type::typetag::STRING]), - Ty::Bool { .. } => Some(vec![baml_type::typetag::BOOL]), - Ty::Null { .. } => Some(vec![baml_type::typetag::NULL]), - Ty::Float { .. } => Some(vec![baml_type::typetag::FLOAT]), - Ty::List(_, _) => Some(vec![baml_type::typetag::LIST]), - Ty::Map { .. } => Some(vec![baml_type::typetag::MAP]), Ty::Union(members, _) => { - // All members must be primitive-tag-able let mut tags = Vec::new(); for m in members { - let member_tags = Self::ty_to_type_tags(m)?; + let member_tags = self.ty_to_type_tags(m)?; tags.extend(member_tags); } Some(tags) } - // Class, Enum, EnumVariant, Literal, Optional, TypeAlias, Opaque, etc. - // — not eligible for type-tag switch dispatch. - _ => None, + _ => self.type_tag_for_ty(ty).map(|tag| vec![tag]), } } } @@ -4154,8 +4190,8 @@ impl LoweringContext<'_> { self.locals.insert(name, error_local); } - // Flatten all arms from all clauses, pre-creating body blocks. - let mut arms = Vec::new(); + // Flatten all arms from all clauses (blocks created lazily below). + let mut arms: Vec<(baml_compiler2_ast::CatchArm, bool)> = Vec::new(); for clause in clauses { for &arm_id in &clause.arms { let arm = self.body.catch_arms[arm_id].clone(); @@ -4163,11 +4199,11 @@ impl LoweringContext<'_> { self.body.patterns[arm.pattern], AstPattern::Binding(ref name) if name.as_str() == "_" ); - arms.push((arm, self.builder.create_block(), is_wildcard)); + arms.push((arm, is_wildcard)); } } - let has_wildcard = arms.iter().any(|(_, _, is_wc)| *is_wc); + let has_wildcard = arms.iter().any(|(_, is_wc)| *is_wc); let is_catch_all_panics = clauses .iter() .any(|clause| matches!(clause.kind, CatchClauseKind::CatchAllPanics)); @@ -4204,10 +4240,8 @@ impl LoweringContext<'_> { // Switch on Rvalue::TypeTag instead of a sequential is_type chain. let switch_arms: Vec<(AstPatId, AstExprId, Option)> = arms .iter() - .map(|(arm, _, _)| (arm.pattern, arm.body, None)) + .map(|(arm, _)| (arm.pattern, arm.body, None)) .collect(); - let pre_blocks: Vec> = - arms.iter().map(|(_, block, _)| Some(*block)).collect(); self.builder.set_current_block(bb_handler); if self.try_lower_as_switch( error_local, @@ -4218,14 +4252,21 @@ impl LoweringContext<'_> { error_local, needs_throw_if_panic, }, - Some(&pre_blocks), + None, ) { self.builder.set_current_block(bb_join); return; } - self.builder.set_current_block(bb_handler); - for &(ref arm, body_block, is_wildcard) in &arms { + // Fallback: sequential pattern-test chain. + // Create body blocks now (not created earlier so the switch path + // doesn't leave orphaned unterminated blocks). + let arms_with_blocks: Vec<_> = arms + .iter() + .map(|(arm, is_wc)| (arm.clone(), self.builder.create_block(), *is_wc)) + .collect(); + + for &(ref arm, body_block, is_wildcard) in &arms_with_blocks { if is_wildcard && needs_throw_if_panic { let bb_wildcard = self.builder.create_block(); self.builder @@ -4244,7 +4285,7 @@ impl LoweringContext<'_> { } // Lower each arm body. - for &(ref arm, body_block, _) in &arms { + for &(ref arm, body_block, _) in &arms_with_blocks { self.builder.set_current_block(body_block); self.bind_pattern(error_local, arm.pattern); self.lower_expr(arm.body, dest.clone()); diff --git a/baml_language/crates/baml_compiler2_mir/src/optimize.rs b/baml_language/crates/baml_compiler2_mir/src/optimize.rs index 2b2614fe73..a46e3288c9 100644 --- a/baml_language/crates/baml_compiler2_mir/src/optimize.rs +++ b/baml_language/crates/baml_compiler2_mir/src/optimize.rs @@ -156,6 +156,10 @@ fn rewrite_block_ids_in_terminator(term: &mut Terminator, map: &[Option } Terminator::Throw { .. } => {} Terminator::ThrowIfPanic { otherwise, .. } => remap(otherwise), + Terminator::ShortCircuit { eval_rhs, join, .. } => { + remap(eval_rhs); + remap(join); + } } } @@ -224,6 +228,10 @@ fn rewrite_block_ids_in_terminator_with_map( } Terminator::Throw { .. } => {} Terminator::ThrowIfPanic { otherwise, .. } => remap(otherwise), + Terminator::ShortCircuit { eval_rhs, join, .. } => { + remap(eval_rhs); + remap(join); + } } } @@ -414,6 +422,14 @@ fn collect_place_index_locals(body: &MirFunctionBody) -> HashSet { scan_operand(value, &mut set); } Terminator::Await { destination, .. } => scan_place(destination, &mut set), + Terminator::ShortCircuit { + operand, + destination, + .. + } => { + scan_operand(operand, &mut set); + scan_place(destination, &mut set); + } Terminator::Goto { .. } | Terminator::Return | Terminator::Unreachable => {} } } @@ -445,6 +461,7 @@ fn count_local_defs(body: &MirFunctionBody) -> Vec { Some(Terminator::Call { destination, .. }) => Some(destination), Some(Terminator::DispatchFuture { future, .. }) => Some(future), Some(Terminator::Await { destination, .. }) => Some(destination), + Some(Terminator::ShortCircuit { destination, .. }) => Some(destination), _ => None, } { defs[dest.base_local().0] += 1; @@ -622,6 +639,14 @@ fn count_in_terminator(term: &Terminator, uses: &mut [usize]) { Terminator::Throw { value } | Terminator::ThrowIfPanic { value, .. } => { count_in_operand(value, uses); } + Terminator::ShortCircuit { + operand, + destination, + .. + } => { + count_in_operand(operand, uses); + count_dest_place(destination, uses); + } Terminator::Goto { .. } | Terminator::Return | Terminator::Unreachable => {} } } @@ -851,6 +876,9 @@ fn apply_subst_to_terminator(term: &mut Terminator, subst: &HashMap { apply_subst_to_operand(value, subst); } + Terminator::ShortCircuit { operand, .. } => { + apply_subst_to_operand(operand, subst); + } Terminator::Goto { .. } | Terminator::Return | Terminator::Unreachable @@ -875,6 +903,8 @@ fn eliminate_dead_locals(body: &mut MirFunctionBody, arity: usize) { Terminator::Call { destination, .. } => Some(destination.base_local()), Terminator::Await { destination, .. } => Some(destination.base_local()), Terminator::DispatchFuture { future, .. } => Some(future.base_local()), + // ShortCircuit is side-effect-free (pure control flow), so its + // destination can be dead-eliminated like any other local. _ => None, }; if let Some(l) = dest_local { @@ -922,6 +952,22 @@ fn eliminate_dead_locals(body: &mut MirFunctionBody, arity: usize) { }); } + // Replace ShortCircuit terminators whose destination is dead with Goto + // to the join block. The now-unreachable eval_rhs block will be cleaned + // up by eliminate_dead_blocks. + for block in &mut body.blocks { + if let Some(Terminator::ShortCircuit { + destination: Place::Local(l), + join, + .. + }) = &block.terminator + { + if old_to_new[l.0].is_none() { + block.terminator = Some(Terminator::Goto { target: *join }); + } + } + } + // Rewrite all Local references for block in &mut body.blocks { for stmt in &mut block.statements { @@ -1065,6 +1111,14 @@ fn rewrite_locals_in_terminator(term: &mut Terminator, map: &[Option]) { Terminator::Throw { value } | Terminator::ThrowIfPanic { value, .. } => { remap_operand(value, map); } + Terminator::ShortCircuit { + operand, + destination, + .. + } => { + remap_operand(operand, map); + remap_place(destination, map); + } Terminator::Goto { .. } | Terminator::Return | Terminator::Unreachable => {} } } @@ -1251,6 +1305,14 @@ fn verify_mir(body: &MirFunctionBody, name: &crate::ItemRef) { Terminator::Throw { value } | Terminator::ThrowIfPanic { value, .. } => { check_operand(value, &blk); } + Terminator::ShortCircuit { + operand, + destination, + .. + } => { + check_operand(operand, &blk); + check_place(destination, &blk); + } Terminator::Goto { .. } | Terminator::Return | Terminator::Unreachable => {} } } diff --git a/baml_language/crates/baml_compiler2_mir/src/pretty.rs b/baml_language/crates/baml_compiler2_mir/src/pretty.rs index ea8c15b81a..4a96b40faa 100644 --- a/baml_language/crates/baml_compiler2_mir/src/pretty.rs +++ b/baml_language/crates/baml_compiler2_mir/src/pretty.rs @@ -294,6 +294,18 @@ fn write_terminator(f: &mut impl Write, term: &Terminator) -> fmt::Result { write_operand(f, value)?; write!(f, " -> {otherwise};") } + Terminator::ShortCircuit { + operand, + is_and, + destination, + eval_rhs, + join, + } => { + let op = if *is_and { "&&" } else { "||" }; + write!(f, "{destination} = short_circuit({op}) ")?; + write_operand(f, operand)?; + write!(f, " -> [eval: {eval_rhs}, join: {join}];") + } } } diff --git a/baml_language/crates/baml_compiler2_tir/src/builder.rs b/baml_language/crates/baml_compiler2_tir/src/builder.rs index d0fcca629a..760e962c50 100644 --- a/baml_language/crates/baml_compiler2_tir/src/builder.rs +++ b/baml_language/crates/baml_compiler2_tir/src/builder.rs @@ -2238,7 +2238,7 @@ impl<'db> TypeInferenceBuilder<'db> { at_expr: ExprId, ) -> Ty { if self.pattern_has_multiple_variants(pattern_id, body, at_expr) { - // Multi-variant union binding — require instanceof narrowing + // Multi-variant union binding — require narrowing (e.g. match) // before field access. Use Error so diagnostics show the actual // type rather than the stdlib `unknown`. Ty::Error { @@ -4478,8 +4478,7 @@ impl<'db> TypeInferenceBuilder<'db> { | BinaryOp::Lt | BinaryOp::Le | BinaryOp::Gt - | BinaryOp::Ge - | BinaryOp::Instanceof => Ty::Primitive(PrimitiveType::Bool, TyAttr::default()), + | BinaryOp::Ge => Ty::Primitive(PrimitiveType::Bool, TyAttr::default()), // Logical → bool BinaryOp::And | BinaryOp::Or => Ty::Primitive(PrimitiveType::Bool, TyAttr::default()), diff --git a/baml_language/crates/baml_compiler2_visualization/src/control_flow/from_ast.rs b/baml_language/crates/baml_compiler2_visualization/src/control_flow/from_ast.rs index fb87efff32..a696a8aeda 100644 --- a/baml_language/crates/baml_compiler2_visualization/src/control_flow/from_ast.rs +++ b/baml_language/crates/baml_compiler2_visualization/src/control_flow/from_ast.rs @@ -702,7 +702,6 @@ fn render_expr_compact_ast(body: &ast::ExprBody, id: ast::ExprId) -> String { ast::BinaryOp::BitXor => "^", ast::BinaryOp::Shl => "<<", ast::BinaryOp::Shr => ">>", - ast::BinaryOp::Instanceof => "instanceof", ast::BinaryOp::NullCoalesce => "??", }; format!( diff --git a/baml_language/crates/baml_lsp2_actions_tests/test_files/syntax/expr/early_return_narrowing.baml b/baml_language/crates/baml_lsp2_actions_tests/test_files/syntax/expr/early_return_narrowing.baml index 5a1090c3d3..609e9683c7 100644 --- a/baml_language/crates/baml_lsp2_actions_tests/test_files/syntax/expr/early_return_narrowing.baml +++ b/baml_language/crates/baml_lsp2_actions_tests/test_files/syntax/expr/early_return_narrowing.baml @@ -91,6 +91,24 @@ function test_diverging_if_with_plain_else(x: int?) -> int { //---- //- diagnostics +// Error: `instanceof` is no longer supported. Use a `match` expression for type checking instead. +// ╭─[ expr_early_return_narrowing.baml:31:11 ] +// │ +// 31 │ if (x instanceof NarrowBar) { +// │ ─────┬──── +// │ ╰────── use `match` instead +// │ +// │ Note: Error code: E0098 +// ────╯ +// Error: `instanceof` is no longer supported. Use a `match` expression for type checking instead. +// ╭─[ expr_early_return_narrowing.baml:62:11 ] +// │ +// 62 │ if (x instanceof NarrowBar) { +// │ ─────┬──── +// │ ╰────── use `match` instead +// │ +// │ Note: Error code: E0098 +// ────╯ // Error: type `user.NarrowBar` has no member `field` // ╭─[ expr_early_return_narrowing.baml:35:14 ] // │ diff --git a/baml_language/crates/baml_lsp2_actions_tests/test_files/syntax/expr/instanceof_narrowing.baml b/baml_language/crates/baml_lsp2_actions_tests/test_files/syntax/expr/instanceof_narrowing.baml index f6070a9138..9c2b9a81b1 100644 --- a/baml_language/crates/baml_lsp2_actions_tests/test_files/syntax/expr/instanceof_narrowing.baml +++ b/baml_language/crates/baml_lsp2_actions_tests/test_files/syntax/expr/instanceof_narrowing.baml @@ -43,6 +43,42 @@ function test_multiple_classes(x: Foo | Bar | Baz) -> string { //---- //- diagnostics +// Error: `instanceof` is no longer supported. Use a `match` expression for type checking instead. +// ╭─[ expr_instanceof_narrowing.baml:11:11 ] +// │ +// 11 │ if (x instanceof Foo) { +// │ ─────┬──── +// │ ╰────── use `match` instead +// │ +// │ Note: Error code: E0098 +// ────╯ +// Error: `instanceof` is no longer supported. Use a `match` expression for type checking instead. +// ╭─[ expr_instanceof_narrowing.baml:22:11 ] +// │ +// 22 │ if (x instanceof Foo) { +// │ ─────┬──── +// │ ╰────── use `match` instead +// │ +// │ Note: Error code: E0098 +// ────╯ +// Error: `instanceof` is no longer supported. Use a `match` expression for type checking instead. +// ╭─[ expr_instanceof_narrowing.baml:35:11 ] +// │ +// 35 │ if (x instanceof Foo) { +// │ ─────┬──── +// │ ╰────── use `match` instead +// │ +// │ Note: Error code: E0098 +// ────╯ +// Error: `instanceof` is no longer supported. Use a `match` expression for type checking instead. +// ╭─[ expr_instanceof_narrowing.baml:37:18 ] +// │ +// 37 │ } else if (x instanceof Baz) { +// │ ─────┬──── +// │ ╰────── use `match` instead +// │ +// │ Note: Error code: E0098 +// ────╯ // Error: type `user.Bar` has no member `field` // ╭─[ expr_instanceof_narrowing.baml:13:18 ] // │ diff --git a/baml_language/crates/baml_tests/projects/instanceof_removed/instanceof_removed.baml b/baml_language/crates/baml_tests/projects/instanceof_removed/instanceof_removed.baml new file mode 100644 index 0000000000..14a817a214 --- /dev/null +++ b/baml_language/crates/baml_tests/projects/instanceof_removed/instanceof_removed.baml @@ -0,0 +1,21 @@ +// The `instanceof` operator has been removed in favor of `match`. +// Using it should produce error E0098 with a migration hint. + +class Cat { + name string +} + +class Dog { + breed string +} + +function check_instanceof(x: Cat | Dog) -> bool { + x instanceof Cat +} + +function check_instanceof_in_if(x: Cat | Dog) -> string { + if (x instanceof Dog) { + return "dog"; + } + "cat" +} diff --git a/baml_language/crates/baml_tests/snapshots/__baml_std__/baml_tests____baml_std____04_5_mir.snap b/baml_language/crates/baml_tests/snapshots/__baml_std__/baml_tests____baml_std____04_5_mir.snap index b653bfddda..00fb3da33e 100644 --- a/baml_language/crates/baml_tests/snapshots/__baml_std__/baml_tests____baml_std____04_5_mir.snap +++ b/baml_language/crates/baml_tests/snapshots/__baml_std__/baml_tests____baml_std____04_5_mir.snap @@ -138,8 +138,7 @@ fn baml.http.Response.ok(self: baml.http.Response) -> bool { bb0: { _3 = copy _1.0; _2 = copy _3 >= const 200_i64; - _0 = copy _2; - branch copy _2 -> [bb1, bb2]; + _0 = short_circuit(&&) copy _2 -> [eval: bb1, join: bb2]; } bb1: { diff --git a/baml_language/crates/baml_tests/snapshots/__baml_std__/baml_tests____baml_std____06_codegen.snap b/baml_language/crates/baml_tests/snapshots/__baml_std__/baml_tests____baml_std____06_codegen.snap index 35b308d27c..193d04868e 100644 --- a/baml_language/crates/baml_tests/snapshots/__baml_std__/baml_tests____baml_std____06_codegen.snap +++ b/baml_language/crates/baml_tests/snapshots/__baml_std__/baml_tests____baml_std____06_codegen.snap @@ -203,19 +203,14 @@ function baml.http.Response.ok(self: null) -> bool { load_field .status_code load_const 200 cmp_op >= - store_var _2 - load_var _2 - store_var _0 - load_var _2 - pop_jump_if_false L0 + jump_if_false L0 + pop 1 load_var self load_field .status_code load_const 300 cmp_op < - store_var _0 L0: - load_var _0 return } @@ -241,23 +236,21 @@ function baml.llm.Client.build_attempt_with_state(self: null, planner_state: voi load_var self load_field .client_type discriminant - copy 0 load_const ClientType.Primitive cmp_op == pop_jump_if_false L0 - pop 1 jump L11 L0: - copy 0 + load_var self + load_field .client_type + discriminant load_const ClientType.Fallback cmp_op == pop_jump_if_false L1 - pop 1 jump L4 L1: - pop 1 load_var self load_field .sub_clients call baml.Array.length @@ -385,8 +378,7 @@ function baml.llm.Client.build_plan_with_state(self: null, planner_state: void) load_field .retry store_var _3 load_var _3 - load_const baml.llm.RetryPolicy - cmp_op instanceof + is_type baml.llm.RetryPolicy pop_jump_if_false L0 jump L1 @@ -510,8 +502,7 @@ function baml.llm.Client.execute(self: null, context: void, inherited_delay_ms: load_field .retry store_var _4 load_var _4 - load_const baml.llm.RetryPolicy - cmp_op instanceof + is_type baml.llm.RetryPolicy pop_jump_if_false L0 jump L1 @@ -618,23 +609,21 @@ function baml.llm.Client.execute_once(self: null, context: void, active_delay_ms load_var self load_field .client_type discriminant - copy 0 load_const ClientType.Primitive cmp_op == pop_jump_if_false L0 - pop 1 jump L7 L0: - copy 0 + load_var self + load_field .client_type + discriminant load_const ClientType.Fallback cmp_op == pop_jump_if_false L1 - pop 1 jump L2 L1: - pop 1 load_var self load_field .counter store_var _66 diff --git a/baml_language/crates/baml_tests/snapshots/__testing_std__/baml_tests____testing_std____04_5_mir.snap b/baml_language/crates/baml_tests/snapshots/__testing_std__/baml_tests____testing_std____04_5_mir.snap index 694ec4c165..913329c962 100644 --- a/baml_language/crates/baml_tests/snapshots/__testing_std__/baml_tests____testing_std____04_5_mir.snap +++ b/baml_language/crates/baml_tests/snapshots/__testing_std__/baml_tests____testing_std____04_5_mir.snap @@ -395,8 +395,7 @@ fn testing.TestCollector.register_test(self: testing.TestCollector, name: string _21 = copy _22.0; _23 = copy _5; _20 = copy _21 == copy _23; - _19 = copy _20; - branch copy _20 -> [bb14, bb13]; + _19 = short_circuit(||) copy _20 -> [eval: bb13, join: bb14]; } bb13: { @@ -543,8 +542,7 @@ fn testing.TestCollector.register_test_set(self: testing.TestCollector, name: st _21 = copy _22.0; _23 = copy _5; _20 = copy _21 == copy _23; - _19 = copy _20; - branch copy _20 -> [bb14, bb13]; + _19 = short_circuit(||) copy _20 -> [eval: bb13, join: bb14]; } bb13: { diff --git a/baml_language/crates/baml_tests/snapshots/__testing_std__/baml_tests____testing_std____06_codegen.snap b/baml_language/crates/baml_tests/snapshots/__testing_std__/baml_tests____testing_std____06_codegen.snap index 3e0d8c7ee8..5ab7f4949d 100644 --- a/baml_language/crates/baml_tests/snapshots/__testing_std__/baml_tests____testing_std____06_codegen.snap +++ b/baml_language/crates/baml_tests/snapshots/__testing_std__/baml_tests____testing_std____06_codegen.snap @@ -158,13 +158,11 @@ function testing.TestCollector.register_test(self: null, name: string, body: () load_field .name load_var full_name cmp_op == - store_var _20 - load_var _20 - load_var _20 - pop_jump_if_false L9 + jump_if_false L9 jump L10 L9: + pop 1 load_var t load_field .name load_var hash_prefix @@ -279,13 +277,11 @@ function testing.TestCollector.register_test_set(self: null, name: string, colle load_field .name load_var full_name cmp_op == - store_var _20 - load_var _20 - load_var _20 - pop_jump_if_false L9 + jump_if_false L9 jump L10 L9: + pop 1 load_var ts load_field .name load_var hash_prefix @@ -593,8 +589,7 @@ function testing.TestRegistry.serialize(self: null) -> (testing.SerializedTest | call baml.Map.get store_var expanded load_var expanded - load_const testing.TestRegistry - cmp_op instanceof + is_type testing.TestRegistry pop_jump_if_false L5 jump L6 @@ -676,9 +671,7 @@ function testing.run_test(body: () -> null, runner: (() -> testing.TestReport) - L0: load_var runner - type_tag - load_const 8 - cmp_op == + is_type (() -> testing.TestReport) -> () -> testing.TestReport pop_jump_if_false L2 load_var base_run load_var runner @@ -705,9 +698,7 @@ function testing.run_testset(run_children: () -> testing.TestSetReport, runner: L0: load_var runner - type_tag - load_const 8 - cmp_op == + is_type (() -> testing.TestSetReport) -> () -> testing.TestSetReport pop_jump_if_false L2 load_var run_children load_var runner diff --git a/baml_language/crates/baml_tests/snapshots/catch_all_keyword/baml_tests__catch_all_keyword__06_codegen.snap b/baml_language/crates/baml_tests/snapshots/catch_all_keyword/baml_tests__catch_all_keyword__06_codegen.snap index dd58564d67..8d568e115d 100644 --- a/baml_language/crates/baml_tests/snapshots/catch_all_keyword/baml_tests__catch_all_keyword__06_codegen.snap +++ b/baml_language/crates/baml_tests/snapshots/catch_all_keyword/baml_tests__catch_all_keyword__06_codegen.snap @@ -6,17 +6,13 @@ function user.CatchAllTyped(x: int) -> string { call user.MayFailIntOrString jump L4 load_var e - type_tag - load_const 1 - cmp_op == + is_type string pop_jump_if_false L0 jump L3 L0: load_var e - type_tag - load_const 0 - cmp_op == + is_type int pop_jump_if_false L1 jump L2 @@ -52,9 +48,7 @@ function user.CatchThenCatchAll(x: int) -> string { call user.MayFailIntOrString jump L2 load_var e - type_tag - load_const 1 - cmp_op == + is_type string pop_jump_if_false L0 jump L1 @@ -73,23 +67,19 @@ function user.CatchThenCatchAll(x: int) -> string { function user.MayFailIntOrString(x: int) -> string { load_var x - copy 0 load_const 0 cmp_op == pop_jump_if_false L0 - pop 1 jump L3 L0: - copy 0 + load_var x load_const 1 cmp_op == pop_jump_if_false L1 - pop 1 jump L2 L1: - pop 1 load_const "ok" return @@ -107,9 +97,7 @@ function user.PartialCatch(x: int) -> string { call user.MayFailIntOrString jump L2 load_var e - type_tag - load_const 1 - cmp_op == + is_type string pop_jump_if_false L0 jump L1 diff --git a/baml_language/crates/baml_tests/snapshots/catch_return_type_mismatch/baml_tests__catch_return_type_mismatch__06_codegen.snap b/baml_language/crates/baml_tests/snapshots/catch_return_type_mismatch/baml_tests__catch_return_type_mismatch__06_codegen.snap index a6a48ac287..2dc9460ee5 100644 --- a/baml_language/crates/baml_tests/snapshots/catch_return_type_mismatch/baml_tests__catch_return_type_mismatch__06_codegen.snap +++ b/baml_language/crates/baml_tests/snapshots/catch_return_type_mismatch/baml_tests__catch_return_type_mismatch__06_codegen.snap @@ -11,15 +11,12 @@ function user.caller() -> bool { jump L2 load_var e discriminant - copy 0 load_const Errors.AuthenticationError cmp_op == pop_jump_if_false L0 - pop 1 jump L1 L0: - pop 1 load_var e throw diff --git a/baml_language/crates/baml_tests/snapshots/catch_throw/baml_tests__catch_throw__06_codegen.snap b/baml_language/crates/baml_tests/snapshots/catch_throw/baml_tests__catch_throw__06_codegen.snap index d7e1f6b7d5..b18a87ec21 100644 --- a/baml_language/crates/baml_tests/snapshots/catch_throw/baml_tests__catch_throw__06_codegen.snap +++ b/baml_language/crates/baml_tests/snapshots/catch_throw/baml_tests__catch_throw__06_codegen.snap @@ -3,15 +3,12 @@ source: crates/baml_tests/src/generated_tests.rs --- function user.AlsoMayFail(x: int) -> string { load_var x - copy 0 load_const 0 cmp_op == pop_jump_if_false L0 - pop 1 jump L1 L0: - pop 1 load_const "also ok" return @@ -38,17 +35,13 @@ function user.CatchWithFallback(x: int) -> string { call user.MayFailIntOrString jump L4 load_var e - type_tag - load_const 1 - cmp_op == + is_type string pop_jump_if_false L0 jump L3 L0: load_var e - type_tag - load_const 0 - cmp_op == + is_type int pop_jump_if_false L1 jump L2 @@ -72,9 +65,7 @@ function user.CatchWithTypedArm(x: int) -> string { call user.MayFail jump L2 load_var e - type_tag - load_const 1 - cmp_op == + is_type string pop_jump_if_false L0 jump L1 @@ -94,17 +85,13 @@ function user.ChainedCatchClauses(x: int) -> string { call user.MayFailIntOrString jump L4 load_var e - type_tag - load_const 1 - cmp_op == + is_type string pop_jump_if_false L0 jump L3 L0: load_var e - type_tag - load_const 0 - cmp_op == + is_type int pop_jump_if_false L1 jump L2 @@ -125,15 +112,12 @@ function user.ChainedCatchClauses(x: int) -> string { function user.MayFail(x: int) -> string { load_var x - copy 0 load_const 0 cmp_op == pop_jump_if_false L0 - pop 1 jump L1 L0: - pop 1 load_const "ok" return @@ -144,23 +128,19 @@ function user.MayFail(x: int) -> string { function user.MayFailIntOrString(x: int) -> string { load_var x - copy 0 load_const 0 cmp_op == pop_jump_if_false L0 - pop 1 jump L3 L0: - copy 0 + load_var x load_const 1 cmp_op == pop_jump_if_false L1 - pop 1 jump L2 L1: - pop 1 load_const "ok" return @@ -178,17 +158,13 @@ function user.MultipleCatchArms(x: int) -> string { call user.MayFailIntOrString jump L4 load_var e - type_tag - load_const 1 - cmp_op == + is_type string pop_jump_if_false L0 jump L3 L0: load_var e - type_tag - load_const 0 - cmp_op == + is_type int pop_jump_if_false L1 jump L2 @@ -234,9 +210,7 @@ function user.SingleCatchTyped(x: int) -> string { call user.MayFail jump L2 load_var e - type_tag - load_const 1 - cmp_op == + is_type string pop_jump_if_false L0 jump L1 diff --git a/baml_language/crates/baml_tests/snapshots/catch_throw_regressions/baml_tests__catch_throw_regressions__06_codegen.snap b/baml_language/crates/baml_tests/snapshots/catch_throw_regressions/baml_tests__catch_throw_regressions__06_codegen.snap index 4b9498f88b..0ec154b5bd 100644 --- a/baml_language/crates/baml_tests/snapshots/catch_throw_regressions/baml_tests__catch_throw_regressions__06_codegen.snap +++ b/baml_language/crates/baml_tests/snapshots/catch_throw_regressions/baml_tests__catch_throw_regressions__06_codegen.snap @@ -3,15 +3,12 @@ source: crates/baml_tests/src/generated_tests.rs --- function user.AlwaysThrowsStatus(n: int) -> Status { load_var n - copy 0 load_const 0 cmp_op == pop_jump_if_false L0 - pop 1 jump L1 L0: - pop 1 load_const 404 throw @@ -56,17 +53,13 @@ function user.CatchAlwaysThrows(n: int) -> string { call user.WrapperAlwaysThrows jump L4 load_var e - type_tag - load_const 1 - cmp_op == + is_type string pop_jump_if_false L0 jump L3 L0: load_var e - type_tag - load_const 0 - cmp_op == + is_type int pop_jump_if_false L1 jump L2 @@ -90,9 +83,7 @@ function user.CatchRecursiveThrow(n: int) -> string { call user.RecB jump L2 load_var e - type_tag - load_const 1 - cmp_op == + is_type string pop_jump_if_false L0 jump L1 @@ -109,15 +100,12 @@ function user.CatchRecursiveThrow(n: int) -> string { function user.RecA(n: int) -> string { load_var n - copy 0 load_const 0 cmp_op == pop_jump_if_false L0 - pop 1 jump L1 L0: - pop 1 load_var n call user.RecB return @@ -135,31 +123,26 @@ function user.RecB(n: int) -> string { function user.ThrowsAllStatusVariants(n: int) -> Status { load_var n - copy 0 load_const 1 cmp_op == pop_jump_if_false L0 - pop 1 jump L5 L0: - copy 0 + load_var n load_const 2 cmp_op == pop_jump_if_false L1 - pop 1 jump L4 L1: - copy 0 + load_var n load_const 3 cmp_op == pop_jump_if_false L2 - pop 1 jump L3 L2: - pop 1 load_const user.Status.SomeOtherError alloc_variant user.Status throw diff --git a/baml_language/crates/baml_tests/snapshots/control_flow/baml_tests__control_flow__06_codegen.snap b/baml_language/crates/baml_tests/snapshots/control_flow/baml_tests__control_flow__06_codegen.snap index 0688bf30b6..6fac6a6ed6 100644 --- a/baml_language/crates/baml_tests/snapshots/control_flow/baml_tests__control_flow__06_codegen.snap +++ b/baml_language/crates/baml_tests/snapshots/control_flow/baml_tests__control_flow__06_codegen.snap @@ -123,23 +123,19 @@ function user.LlmFunction$render_prompt(input: string) -> baml.llm.PromptAst { function user.MatchExpression(x: int) -> string { load_var x - copy 0 load_const 1 cmp_op == pop_jump_if_false L0 - pop 1 jump L3 L0: - copy 0 + load_var x load_const 2 cmp_op == pop_jump_if_false L1 - pop 1 jump L2 L1: - pop 1 notify_block Default load_const "other" jump L4 diff --git a/baml_language/crates/baml_tests/snapshots/error_cases/baml_tests__error_cases__06_codegen.snap b/baml_language/crates/baml_tests/snapshots/error_cases/baml_tests__error_cases__06_codegen.snap index de8a72713d..7b1e991b05 100644 --- a/baml_language/crates/baml_tests/snapshots/error_cases/baml_tests__error_cases__06_codegen.snap +++ b/baml_language/crates/baml_tests/snapshots/error_cases/baml_tests__error_cases__06_codegen.snap @@ -3,9 +3,7 @@ source: crates/baml_tests/src/generated_tests.rs --- function user.Broken.BadUnionPattern(x: int | bool) -> string { load_var x - type_tag - load_const 0 - cmp_op == + is_type int pop_jump_if_false L0 jump L3 @@ -18,9 +16,7 @@ function user.Broken.BadUnionPattern(x: int | bool) -> string { L1: load_var x - type_tag - load_const 2 - cmp_op == + is_type bool pop_jump_if_false L2 L2: @@ -51,9 +47,7 @@ function user.Broken.MixedLiteralAndTyped(x: int) -> string { L0: load_var x - type_tag - load_const 0 - cmp_op == + is_type int pop_jump_if_false L1 L1: diff --git a/baml_language/crates/baml_tests/snapshots/format_checks/baml_tests__format_checks__04_5_mir.snap b/baml_language/crates/baml_tests/snapshots/format_checks/baml_tests__format_checks__04_5_mir.snap index 2e960a9f62..bf9e0f36ef 100644 --- a/baml_language/crates/baml_tests/snapshots/format_checks/baml_tests__format_checks__04_5_mir.snap +++ b/baml_language/crates/baml_tests/snapshots/format_checks/baml_tests__format_checks__04_5_mir.snap @@ -208,14 +208,10 @@ fn user.BinaryExprs(a: int, b: int, c: int, d: int, s: string, flag: bool) -> in let _201: bool bb0: { - branch copy _6 -> [bb1, bb1]; + goto -> bb1; } bb1: { - branch copy _6 -> [bb2, bb2]; - } - - bb2: { _7 = copy _1; _7 = copy _7 + copy _2; _7 = copy _7 - copy _2; @@ -236,60 +232,45 @@ fn user.BinaryExprs(a: int, b: int, c: int, d: int, s: string, flag: bool) -> in _15 = copy _1 * copy _2; _14 = copy _15 / copy _3; _16 = copy _1 > const 0_i64; - branch copy _16 -> [bb3, bb3]; + goto -> bb2; } - bb3: { + bb2: { _17 = copy _1 > const 0_i64; - branch copy _17 -> [bb4, bb4]; + goto -> bb3; } - bb4: { + bb3: { _19 = copy _1 > const 0_i64; - _18 = copy _19; - branch copy _19 -> [bb5, bb6]; + _18 = short_circuit(&&) copy _19 -> [eval: bb4, join: bb5]; } - bb5: { + bb4: { _18 = copy _2 > const 0_i64; - goto -> bb6; - } - - bb6: { - branch copy _18 -> [bb7, bb7]; + goto -> bb5; } - bb7: { + bb5: { _21 = copy _1 > const 0_i64; - _20 = copy _21; - branch copy _21 -> [bb9, bb8]; + _20 = short_circuit(||) copy _21 -> [eval: bb6, join: bb7]; } - bb8: { + bb6: { _20 = copy _2 > const 0_i64; - goto -> bb9; - } - - bb9: { - branch copy _20 -> [bb10, bb10]; + goto -> bb7; } - bb10: { + bb7: { _23 = copy _1 > const 0_i64; - _22 = copy _23; - branch copy _23 -> [bb11, bb12]; + _22 = short_circuit(&&) copy _23 -> [eval: bb8, join: bb9]; } - bb11: { + bb8: { _22 = copy _2 > const 0_i64; - goto -> bb12; - } - - bb12: { - branch copy _22 -> [bb13, bb13]; + goto -> bb9; } - bb13: { + bb9: { _24 = copy _2 * copy _3; _25 = copy _1 + copy _2; _26 = copy _1 * copy _2; @@ -355,100 +336,87 @@ fn user.BinaryExprs(a: int, b: int, c: int, d: int, s: string, flag: bool) -> in _65 = copy _66 + copy _4; _64 = copy _65 + copy _1; _97 = copy _1 > const 0_i64; - _96 = copy _97; - branch copy _97 -> [bb14, bb15]; + _96 = short_circuit(&&) copy _97 -> [eval: bb10, join: bb11]; } - bb14: { + bb10: { _96 = copy _2 > const 0_i64; + goto -> bb11; + } + + bb11: { + _95 = short_circuit(&&) copy _96 -> [eval: bb12, join: bb13]; + } + + bb12: { + _95 = copy _3 > const 0_i64; + goto -> bb13; + } + + bb13: { + _94 = short_circuit(&&) copy _95 -> [eval: bb14, join: bb15]; + } + + bb14: { + _94 = copy _4 > const 0_i64; goto -> bb15; } bb15: { - _95 = copy _96; - branch copy _96 -> [bb16, bb17]; + _93 = short_circuit(&&) copy _94 -> [eval: bb16, join: bb17]; } bb16: { - _95 = copy _3 > const 0_i64; + _93 = copy _1 != copy _2; goto -> bb17; } bb17: { - _94 = copy _95; - branch copy _95 -> [bb18, bb19]; + _92 = short_circuit(&&) copy _93 -> [eval: bb18, join: bb19]; } bb18: { - _94 = copy _4 > const 0_i64; + _92 = copy _2 != copy _3; goto -> bb19; } bb19: { - _93 = copy _94; - branch copy _94 -> [bb20, bb21]; + _91 = short_circuit(&&) copy _92 -> [eval: bb20, join: bb21]; } bb20: { - _93 = copy _1 != copy _2; + _91 = copy _3 != copy _4; goto -> bb21; } bb21: { - _92 = copy _93; - branch copy _93 -> [bb22, bb23]; + _90 = short_circuit(&&) copy _91 -> [eval: bb22, join: bb23]; } bb22: { - _92 = copy _2 != copy _3; + _90 = copy _1 < const 8002_i64; goto -> bb23; } bb23: { - _91 = copy _92; - branch copy _92 -> [bb24, bb25]; + _89 = short_circuit(&&) copy _90 -> [eval: bb24, join: bb25]; } bb24: { - _91 = copy _3 != copy _4; + _89 = copy _2 < const 1000_i64; goto -> bb25; } bb25: { - _90 = copy _91; - branch copy _91 -> [bb26, bb27]; + _88 = short_circuit(&&) copy _89 -> [eval: bb26, join: bb27]; } bb26: { - _90 = copy _1 < const 8002_i64; + _88 = copy _3 < const 1000_i64; goto -> bb27; } bb27: { - _89 = copy _90; - branch copy _90 -> [bb28, bb29]; - } - - bb28: { - _89 = copy _2 < const 1000_i64; - goto -> bb29; - } - - bb29: { - _88 = copy _89; - branch copy _89 -> [bb30, bb31]; - } - - bb30: { - _88 = copy _3 < const 1000_i64; - goto -> bb31; - } - - bb31: { - branch copy _88 -> [bb32, bb32]; - } - - bb32: { _116 = copy _1 * copy _2; _117 = copy _3 * copy _4; _115 = copy _116 + copy _117; @@ -491,55 +459,42 @@ fn user.BinaryExprs(a: int, b: int, c: int, d: int, s: string, flag: bool) -> in _141 = copy _142 + copy _3; _140 = copy _141 + copy _4; _139 = copy _140 > const 0_i64; - _138 = copy _139; - branch copy _139 -> [bb33, bb34]; + _138 = short_circuit(&&) copy _139 -> [eval: bb28, join: bb29]; } - bb33: { + bb28: { _145 = copy _1 * copy _2; _144 = copy _145 * copy _3; _143 = copy _144 * copy _4; _138 = copy _143 < const 10000_i64; - goto -> bb34; + goto -> bb29; } - bb34: { - _137 = copy _138; - branch copy _138 -> [bb35, bb36]; + bb29: { + _137 = short_circuit(&&) copy _138 -> [eval: bb30, join: bb31]; } - bb35: { + bb30: { _148 = copy _1 - copy _2; _147 = copy _148 - copy _3; _146 = copy _147 - copy _4; _137 = copy _146 != const 0_i64; - goto -> bb36; + goto -> bb31; } - bb36: { - _136 = copy _137; - branch copy _137 -> [bb37, bb38]; + bb31: { + _136 = short_circuit(&&) copy _137 -> [eval: bb32, join: bb33]; } - bb37: { + bb32: { _150 = copy _1 + copy _2; _151 = copy _3 + copy _4; _149 = copy _150 * copy _151; _136 = copy _149 >= const 100_i64; - goto -> bb38; - } - - bb38: { - branch copy _136 -> [bb40, bb39]; - } - - bb39: { - _153 = copy _1 * const 1_i64; - _152 = copy _153 < const 2_i64; - branch copy _152 -> [bb40, bb40]; + goto -> bb33; } - bb40: { + bb33: { _164 = copy _1 * copy _2; _165 = copy _3 * copy _4; _163 = copy _164 + copy _165; @@ -582,71 +537,62 @@ fn user.BinaryExprs(a: int, b: int, c: int, d: int, s: string, flag: bool) -> in _185 = copy _186 + copy _2; _184 = copy _185 + copy _3; _199 = copy _1 > const 0_i64; - _198 = copy _199; - branch copy _199 -> [bb41, bb42]; + _198 = short_circuit(&&) copy _199 -> [eval: bb34, join: bb35]; } - bb41: { + bb34: { _198 = copy _2 > const 0_i64; - goto -> bb42; + goto -> bb35; } - bb42: { - _197 = copy _198; - branch copy _198 -> [bb43, bb44]; + bb35: { + _197 = short_circuit(&&) copy _198 -> [eval: bb36, join: bb37]; } - bb43: { + bb36: { _197 = copy _3 > const 0_i64; - goto -> bb44; + goto -> bb37; } - bb44: { - _196 = copy _197; - branch copy _197 -> [bb45, bb46]; + bb37: { + _196 = short_circuit(&&) copy _197 -> [eval: bb38, join: bb39]; } - bb45: { + bb38: { _196 = copy _4 > const 0_i64; - goto -> bb46; + goto -> bb39; } - bb46: { - _195 = copy _196; - branch copy _196 -> [bb47, bb48]; + bb39: { + _195 = short_circuit(&&) copy _196 -> [eval: bb40, join: bb41]; } - bb47: { + bb40: { _195 = copy _1 != copy _2; - goto -> bb48; + goto -> bb41; } - bb48: { - _194 = copy _195; - branch copy _195 -> [bb49, bb50]; + bb41: { + _194 = short_circuit(&&) copy _195 -> [eval: bb42, join: bb43]; } - bb49: { + bb42: { _194 = copy _2 != copy _3; - goto -> bb50; - } - - bb50: { - branch copy _194 -> [bb51, bb51]; + goto -> bb43; } - bb51: { + bb43: { _200 = copy _1 * copy _2; _201 = copy _1 == const 2_i64; - branch copy _201 -> [bb52, bb52]; + goto -> bb44; } - bb52: { + bb44: { _0 = copy _1 + copy _2; - goto -> bb53; + goto -> bb45; } - bb53: { + bb45: { return; } } @@ -2941,8 +2887,7 @@ fn user.AssertStmts(x: int, y: int) -> int { bb0: { _10 = copy _1 > const 0_i64; - _9 = copy _10; - branch copy _10 -> [bb1, bb2]; + _9 = short_circuit(&&) copy _10 -> [eval: bb1, join: bb2]; } bb1: { @@ -2951,8 +2896,7 @@ fn user.AssertStmts(x: int, y: int) -> int { } bb2: { - _8 = copy _9; - branch copy _9 -> [bb3, bb4]; + _8 = short_circuit(&&) copy _9 -> [eval: bb3, join: bb4]; } bb3: { @@ -2961,8 +2905,7 @@ fn user.AssertStmts(x: int, y: int) -> int { } bb4: { - _7 = copy _8; - branch copy _8 -> [bb5, bb6]; + _7 = short_circuit(&&) copy _8 -> [eval: bb5, join: bb6]; } bb5: { @@ -2971,8 +2914,7 @@ fn user.AssertStmts(x: int, y: int) -> int { } bb6: { - _6 = copy _7; - branch copy _7 -> [bb7, bb8]; + _6 = short_circuit(&&) copy _7 -> [eval: bb7, join: bb8]; } bb7: { @@ -2981,8 +2923,7 @@ fn user.AssertStmts(x: int, y: int) -> int { } bb8: { - _5 = copy _6; - branch copy _6 -> [bb9, bb10]; + _5 = short_circuit(&&) copy _6 -> [eval: bb9, join: bb10]; } bb9: { @@ -2992,8 +2933,7 @@ fn user.AssertStmts(x: int, y: int) -> int { } bb10: { - _4 = copy _5; - branch copy _5 -> [bb11, bb12]; + _4 = short_circuit(&&) copy _5 -> [eval: bb11, join: bb12]; } bb11: { @@ -3003,8 +2943,7 @@ fn user.AssertStmts(x: int, y: int) -> int { } bb12: { - _3 = copy _4; - branch copy _4 -> [bb13, bb14]; + _3 = short_circuit(&&) copy _4 -> [eval: bb13, join: bb14]; } bb13: { @@ -3014,306 +2953,244 @@ fn user.AssertStmts(x: int, y: int) -> int { } bb14: { - branch copy _3 -> [bb15, bb16]; + _25 = copy _1 > const 0_i64; + _24 = short_circuit(&&) copy _25 -> [eval: bb15, join: bb16]; } bb15: { - _14 = copy _1 % const 7_i64; + _24 = copy _1 < const 100_i64; goto -> bb16; } bb16: { - _25 = copy _1 > const 0_i64; - _24 = copy _25; - branch copy _25 -> [bb17, bb18]; + _23 = short_circuit(&&) copy _24 -> [eval: bb17, join: bb18]; } bb17: { - _24 = copy _1 < const 100_i64; + _23 = copy _2 > const 0_i64; goto -> bb18; } bb18: { - _23 = copy _24; - branch copy _24 -> [bb19, bb20]; + _22 = short_circuit(&&) copy _23 -> [eval: bb19, join: bb20]; } bb19: { - _23 = copy _2 > const 0_i64; + _22 = copy _2 < const 100_i64; goto -> bb20; } bb20: { - _22 = copy _23; - branch copy _23 -> [bb21, bb22]; + _21 = short_circuit(&&) copy _22 -> [eval: bb21, join: bb22]; } bb21: { - _22 = copy _2 < const 100_i64; + _21 = copy _1 != copy _2; goto -> bb22; } bb22: { - _21 = copy _22; - branch copy _22 -> [bb23, bb24]; + _20 = short_circuit(&&) copy _21 -> [eval: bb23, join: bb24]; } bb23: { - _21 = copy _1 != copy _2; + _26 = copy _1 + copy _2; + _20 = copy _26 < const 150_i64; goto -> bb24; } bb24: { - _20 = copy _21; - branch copy _21 -> [bb25, bb26]; + _19 = short_circuit(&&) copy _20 -> [eval: bb25, join: bb26]; } bb25: { - _26 = copy _1 + copy _2; - _20 = copy _26 < const 150_i64; + _27 = copy _1 * copy _2; + _19 = copy _27 > const 10_i64; goto -> bb26; } bb26: { - _19 = copy _20; - branch copy _20 -> [bb27, bb28]; + _18 = short_circuit(&&) copy _19 -> [eval: bb27, join: bb28]; } bb27: { - _27 = copy _1 * copy _2; - _19 = copy _27 > const 10_i64; + _28 = copy _1 - copy _2; + _18 = copy _28 != const 42_i64; goto -> bb28; } bb28: { - _18 = copy _19; - branch copy _19 -> [bb29, bb30]; + _17 = short_circuit(&&) copy _18 -> [eval: bb29, join: bb30]; } bb29: { - _28 = copy _1 - copy _2; - _18 = copy _28 != const 42_i64; + _29 = copy _1 % const 7_i64; + _17 = copy _29 == const 0_i64; goto -> bb30; } bb30: { - _17 = copy _18; - branch copy _18 -> [bb31, bb32]; + _16 = short_circuit(&&) copy _17 -> [eval: bb31, join: bb32]; } bb31: { - _29 = copy _1 % const 7_i64; - _17 = copy _29 == const 0_i64; + _30 = copy _2 % const 3_i64; + _16 = copy _30 == const 0_i64; goto -> bb32; } bb32: { - _16 = copy _17; - branch copy _17 -> [bb33, bb34]; + _15 = short_circuit(&&) copy _16 -> [eval: bb33, join: bb34]; } bb33: { - _30 = copy _2 % const 3_i64; - _16 = copy _30 == const 0_i64; + _32 = copy _1 + copy _2; + _33 = copy _1 * copy _2; + _31 = copy _32 + copy _33; + _15 = copy _31 > const 100_i64; goto -> bb34; } bb34: { - _15 = copy _16; - branch copy _16 -> [bb35, bb36]; + _44 = copy _1 > const 0_i64; + _43 = short_circuit(&&) copy _44 -> [eval: bb35, join: bb36]; } bb35: { - _32 = copy _1 + copy _2; - _33 = copy _1 * copy _2; - _31 = copy _32 + copy _33; - _15 = copy _31 > const 100_i64; + _43 = copy _1 < const 100_i64; goto -> bb36; } bb36: { - branch copy _15 -> [bb37, bb38]; + _42 = short_circuit(&&) copy _43 -> [eval: bb37, join: bb38]; } bb37: { - _35 = copy _1 * copy _1; - _36 = copy _2 * copy _2; - _34 = copy _35 + copy _36; + _42 = copy _2 > const 0_i64; goto -> bb38; } bb38: { - _44 = copy _1 > const 0_i64; - _43 = copy _44; - branch copy _44 -> [bb39, bb40]; + _41 = short_circuit(&&) copy _42 -> [eval: bb39, join: bb40]; } bb39: { - _43 = copy _1 < const 100_i64; + _41 = copy _2 < const 100_i64; goto -> bb40; } bb40: { - _42 = copy _43; - branch copy _43 -> [bb41, bb42]; + _40 = short_circuit(&&) copy _41 -> [eval: bb41, join: bb42]; } bb41: { - _42 = copy _2 > const 0_i64; + _40 = copy _1 != copy _2; goto -> bb42; } bb42: { - _41 = copy _42; - branch copy _42 -> [bb43, bb44]; + _39 = short_circuit(&&) copy _40 -> [eval: bb43, join: bb44]; } bb43: { - _41 = copy _2 < const 100_i64; + _45 = copy _1 + copy _2; + _39 = copy _45 < const 150_i64; goto -> bb44; } bb44: { - _40 = copy _41; - branch copy _41 -> [bb45, bb46]; + _38 = short_circuit(&&) copy _39 -> [eval: bb45, join: bb46]; } bb45: { - _40 = copy _1 != copy _2; + _46 = copy _1 * copy _2; + _38 = copy _46 > const 10_i64; goto -> bb46; } bb46: { - _39 = copy _40; - branch copy _40 -> [bb47, bb48]; + _37 = short_circuit(&&) copy _38 -> [eval: bb47, join: bb48]; } bb47: { - _45 = copy _1 + copy _2; - _39 = copy _45 < const 150_i64; + _47 = copy _1 - copy _2; + _37 = copy _47 != const 42_i64; goto -> bb48; } bb48: { - _38 = copy _39; - branch copy _39 -> [bb49, bb50]; + _56 = copy _1 > const 0_i64; + _55 = short_circuit(&&) copy _56 -> [eval: bb49, join: bb50]; } bb49: { - _46 = copy _1 * copy _2; - _38 = copy _46 > const 10_i64; + _55 = copy _1 < const 100_i64; goto -> bb50; } bb50: { - _37 = copy _38; - branch copy _38 -> [bb51, bb52]; + _54 = short_circuit(&&) copy _55 -> [eval: bb51, join: bb52]; } bb51: { - _47 = copy _1 - copy _2; - _37 = copy _47 != const 42_i64; + _54 = copy _2 > const 0_i64; goto -> bb52; } bb52: { - branch copy _37 -> [bb53, bb54]; + _53 = short_circuit(&&) copy _54 -> [eval: bb53, join: bb54]; } bb53: { - _48 = copy _1 % const 7_i64; + _53 = copy _2 < const 100_i64; goto -> bb54; } bb54: { - _56 = copy _1 > const 0_i64; - _55 = copy _56; - branch copy _56 -> [bb55, bb56]; + _52 = short_circuit(&&) copy _53 -> [eval: bb55, join: bb56]; } bb55: { - _55 = copy _1 < const 100_i64; + _52 = copy _1 != copy _2; goto -> bb56; } bb56: { - _54 = copy _55; - branch copy _55 -> [bb57, bb58]; + _51 = short_circuit(&&) copy _52 -> [eval: bb57, join: bb58]; } bb57: { - _54 = copy _2 > const 0_i64; + _57 = copy _1 + copy _2; + _51 = copy _57 < const 150_i64; goto -> bb58; } bb58: { - _53 = copy _54; - branch copy _54 -> [bb59, bb60]; + _50 = short_circuit(&&) copy _51 -> [eval: bb59, join: bb60]; } bb59: { - _53 = copy _2 < const 100_i64; + _58 = copy _1 * copy _2; + _50 = copy _58 > const 10_i64; goto -> bb60; } bb60: { - _52 = copy _53; - branch copy _53 -> [bb61, bb62]; + _49 = short_circuit(&&) copy _50 -> [eval: bb61, join: bb62]; } bb61: { - _52 = copy _1 != copy _2; - goto -> bb62; - } - - bb62: { - _51 = copy _52; - branch copy _52 -> [bb63, bb64]; - } - - bb63: { - _57 = copy _1 + copy _2; - _51 = copy _57 < const 150_i64; - goto -> bb64; - } - - bb64: { - _50 = copy _51; - branch copy _51 -> [bb65, bb66]; - } - - bb65: { - _58 = copy _1 * copy _2; - _50 = copy _58 > const 10_i64; - goto -> bb66; - } - - bb66: { - _49 = copy _50; - branch copy _50 -> [bb67, bb68]; - } - - bb67: { _59 = copy _1 - copy _2; _49 = copy _59 != const 42_i64; - goto -> bb68; - } - - bb68: { - branch copy _49 -> [bb69, bb70]; - } - - bb69: { - _60 = copy _1 % const 7_i64; - goto -> bb70; + goto -> bb62; } - bb70: { + bb62: { _0 = copy _1; - goto -> bb71; + goto -> bb63; } - bb71: { + bb63: { return; } } @@ -3855,8 +3732,7 @@ fn user.LoopStmts(items: int[], flag: bool) -> int { bb2: { _8 = copy _3; _7 = copy _8 < const 100_i64; - _6 = copy _7; - branch copy _7 -> [bb3, bb4]; + _6 = short_circuit(&&) copy _7 -> [eval: bb3, join: bb4]; } bb3: { @@ -3921,8 +3797,7 @@ fn user.LoopStmts(items: int[], flag: bool) -> int { bb14: { _28 = copy _25; _27 = copy _28 < const 100_i64; - _26 = copy _27; - branch copy _27 -> [bb15, bb16]; + _26 = short_circuit(&&) copy _27 -> [eval: bb15, join: bb16]; } bb15: { @@ -4016,8 +3891,7 @@ fn user.LoopStmts(items: int[], flag: bool) -> int { bb31: { _85 = copy _3; _84 = copy _85 < const 100_i64; - _83 = copy _84; - branch copy _84 -> [bb32, bb33]; + _83 = short_circuit(&&) copy _84 -> [eval: bb32, join: bb33]; } bb32: { @@ -4027,8 +3901,7 @@ fn user.LoopStmts(items: int[], flag: bool) -> int { } bb33: { - _82 = copy _83; - branch copy _83 -> [bb34, bb35]; + _82 = short_circuit(&&) copy _83 -> [eval: bb34, join: bb35]; } bb34: { @@ -4037,8 +3910,7 @@ fn user.LoopStmts(items: int[], flag: bool) -> int { } bb35: { - _81 = copy _82; - branch copy _82 -> [bb36, bb37]; + _81 = short_circuit(&&) copy _82 -> [eval: bb36, join: bb37]; } bb36: { @@ -4049,8 +3921,7 @@ fn user.LoopStmts(items: int[], flag: bool) -> int { } bb37: { - _80 = copy _81; - branch copy _81 -> [bb38, bb39]; + _80 = short_circuit(&&) copy _81 -> [eval: bb38, join: bb39]; } bb38: { @@ -4062,8 +3933,7 @@ fn user.LoopStmts(items: int[], flag: bool) -> int { } bb39: { - _79 = copy _80; - branch copy _80 -> [bb40, bb41]; + _79 = short_circuit(&&) copy _80 -> [eval: bb40, join: bb41]; } bb40: { @@ -4075,8 +3945,7 @@ fn user.LoopStmts(items: int[], flag: bool) -> int { } bb41: { - _78 = copy _79; - branch copy _79 -> [bb42, bb43]; + _78 = short_circuit(&&) copy _79 -> [eval: bb42, join: bb43]; } bb42: { @@ -4094,8 +3963,7 @@ fn user.LoopStmts(items: int[], flag: bool) -> int { bb44: { _108 = copy _3; _107 = copy _108 < const 100_i64; - _106 = copy _107; - branch copy _107 -> [bb45, bb46]; + _106 = short_circuit(&&) copy _107 -> [eval: bb45, join: bb46]; } bb45: { @@ -4105,8 +3973,7 @@ fn user.LoopStmts(items: int[], flag: bool) -> int { } bb46: { - _105 = copy _106; - branch copy _106 -> [bb47, bb48]; + _105 = short_circuit(&&) copy _106 -> [eval: bb47, join: bb48]; } bb47: { @@ -4115,8 +3982,7 @@ fn user.LoopStmts(items: int[], flag: bool) -> int { } bb48: { - _104 = copy _105; - branch copy _105 -> [bb49, bb50]; + _104 = short_circuit(&&) copy _105 -> [eval: bb49, join: bb50]; } bb49: { @@ -4127,8 +3993,7 @@ fn user.LoopStmts(items: int[], flag: bool) -> int { } bb50: { - _103 = copy _104; - branch copy _104 -> [bb51, bb52]; + _103 = short_circuit(&&) copy _104 -> [eval: bb51, join: bb52]; } bb51: { @@ -4140,8 +4005,7 @@ fn user.LoopStmts(items: int[], flag: bool) -> int { } bb52: { - _102 = copy _103; - branch copy _103 -> [bb53, bb54]; + _102 = short_circuit(&&) copy _103 -> [eval: bb53, join: bb54]; } bb53: { @@ -4153,8 +4017,7 @@ fn user.LoopStmts(items: int[], flag: bool) -> int { } bb54: { - _101 = copy _102; - branch copy _102 -> [bb55, bb56]; + _101 = short_circuit(&&) copy _102 -> [eval: bb55, join: bb56]; } bb55: { @@ -4166,8 +4029,7 @@ fn user.LoopStmts(items: int[], flag: bool) -> int { } bb56: { - _100 = copy _101; - branch copy _101 -> [bb57, bb58]; + _100 = short_circuit(&&) copy _101 -> [eval: bb57, join: bb58]; } bb57: { @@ -4178,8 +4040,7 @@ fn user.LoopStmts(items: int[], flag: bool) -> int { } bb58: { - _99 = copy _100; - branch copy _100 -> [bb59, bb60]; + _99 = short_circuit(&&) copy _100 -> [eval: bb59, join: bb60]; } bb59: { @@ -4190,8 +4051,7 @@ fn user.LoopStmts(items: int[], flag: bool) -> int { } bb60: { - _98 = copy _99; - branch copy _99 -> [bb61, bb62]; + _98 = short_circuit(&&) copy _99 -> [eval: bb61, join: bb62]; } bb61: { @@ -4218,8 +4078,7 @@ fn user.LoopStmts(items: int[], flag: bool) -> int { bb64: { _135 = copy _132; _134 = copy _135 < const 1000_i64; - _133 = copy _134; - branch copy _134 -> [bb65, bb66]; + _133 = short_circuit(&&) copy _134 -> [eval: bb65, join: bb66]; } bb65: { @@ -4245,8 +4104,7 @@ fn user.LoopStmts(items: int[], flag: bool) -> int { bb68: { _147 = copy _137; _146 = copy _147 < const 1000000_i64; - _145 = copy _146; - branch copy _146 -> [bb69, bb70]; + _145 = short_circuit(&&) copy _146 -> [eval: bb69, join: bb70]; } bb69: { @@ -4255,8 +4113,7 @@ fn user.LoopStmts(items: int[], flag: bool) -> int { } bb70: { - _144 = copy _145; - branch copy _145 -> [bb71, bb72]; + _144 = short_circuit(&&) copy _145 -> [eval: bb71, join: bb72]; } bb71: { @@ -4296,8 +4153,7 @@ fn user.LoopStmts(items: int[], flag: bool) -> int { bb77: { _170 = copy _3; _169 = copy _170 < const 100_i64; - _168 = copy _169; - branch copy _169 -> [bb78, bb79]; + _168 = short_circuit(&&) copy _169 -> [eval: bb78, join: bb79]; } bb78: { @@ -4307,8 +4163,7 @@ fn user.LoopStmts(items: int[], flag: bool) -> int { } bb79: { - _167 = copy _168; - branch copy _168 -> [bb80, bb81]; + _167 = short_circuit(&&) copy _168 -> [eval: bb80, join: bb81]; } bb80: { @@ -4317,8 +4172,7 @@ fn user.LoopStmts(items: int[], flag: bool) -> int { } bb81: { - _166 = copy _167; - branch copy _167 -> [bb82, bb83]; + _166 = short_circuit(&&) copy _167 -> [eval: bb82, join: bb83]; } bb82: { @@ -4329,8 +4183,7 @@ fn user.LoopStmts(items: int[], flag: bool) -> int { } bb83: { - _165 = copy _166; - branch copy _166 -> [bb84, bb85]; + _165 = short_circuit(&&) copy _166 -> [eval: bb84, join: bb85]; } bb84: { @@ -4342,8 +4195,7 @@ fn user.LoopStmts(items: int[], flag: bool) -> int { } bb85: { - _164 = copy _165; - branch copy _165 -> [bb86, bb87]; + _164 = short_circuit(&&) copy _165 -> [eval: bb86, join: bb87]; } bb86: { @@ -4355,8 +4207,7 @@ fn user.LoopStmts(items: int[], flag: bool) -> int { } bb87: { - _163 = copy _164; - branch copy _164 -> [bb88, bb89]; + _163 = short_circuit(&&) copy _164 -> [eval: bb88, join: bb89]; } bb88: { @@ -4374,8 +4225,7 @@ fn user.LoopStmts(items: int[], flag: bool) -> int { bb90: { _190 = copy _3; _189 = copy _190 < const 100_i64; - _188 = copy _189; - branch copy _189 -> [bb91, bb92]; + _188 = short_circuit(&&) copy _189 -> [eval: bb91, join: bb92]; } bb91: { @@ -4385,8 +4235,7 @@ fn user.LoopStmts(items: int[], flag: bool) -> int { } bb92: { - _187 = copy _188; - branch copy _188 -> [bb93, bb94]; + _187 = short_circuit(&&) copy _188 -> [eval: bb93, join: bb94]; } bb93: { @@ -4395,8 +4244,7 @@ fn user.LoopStmts(items: int[], flag: bool) -> int { } bb94: { - _186 = copy _187; - branch copy _187 -> [bb95, bb96]; + _186 = short_circuit(&&) copy _187 -> [eval: bb95, join: bb96]; } bb95: { @@ -4407,8 +4255,7 @@ fn user.LoopStmts(items: int[], flag: bool) -> int { } bb96: { - _185 = copy _186; - branch copy _186 -> [bb97, bb98]; + _185 = short_circuit(&&) copy _186 -> [eval: bb97, join: bb98]; } bb97: { @@ -4420,8 +4267,7 @@ fn user.LoopStmts(items: int[], flag: bool) -> int { } bb98: { - _184 = copy _185; - branch copy _185 -> [bb99, bb100]; + _184 = short_circuit(&&) copy _185 -> [eval: bb99, join: bb100]; } bb99: { @@ -4433,8 +4279,7 @@ fn user.LoopStmts(items: int[], flag: bool) -> int { } bb100: { - _183 = copy _184; - branch copy _184 -> [bb101, bb102]; + _183 = short_circuit(&&) copy _184 -> [eval: bb101, join: bb102]; } bb101: { @@ -4463,8 +4308,7 @@ fn user.LoopStmts(items: int[], flag: bool) -> int { bb104: { _213 = copy _203; _212 = copy _213 < const 1000000_i64; - _211 = copy _212; - branch copy _212 -> [bb105, bb106]; + _211 = short_circuit(&&) copy _212 -> [eval: bb105, join: bb106]; } bb105: { @@ -4473,8 +4317,7 @@ fn user.LoopStmts(items: int[], flag: bool) -> int { } bb106: { - _210 = copy _211; - branch copy _211 -> [bb107, bb108]; + _210 = short_circuit(&&) copy _211 -> [eval: bb107, join: bb108]; } bb107: { @@ -4501,8 +4344,7 @@ fn user.LoopStmts(items: int[], flag: bool) -> int { bb110: { _229 = copy _219; _228 = copy _229 < const 1000000_i64; - _227 = copy _228; - branch copy _228 -> [bb111, bb112]; + _227 = short_circuit(&&) copy _228 -> [eval: bb111, join: bb112]; } bb111: { @@ -4511,8 +4353,7 @@ fn user.LoopStmts(items: int[], flag: bool) -> int { } bb112: { - _226 = copy _227; - branch copy _227 -> [bb113, bb114]; + _226 = short_circuit(&&) copy _227 -> [eval: bb113, join: bb114]; } bb113: { @@ -4828,8 +4669,7 @@ fn user.LoopStmts(items: int[], flag: bool) -> int { _373 = copy _367; _371 = copy _372 * copy _373; _370 = copy _371 > const 500_i64; - _369 = copy _370; - branch copy _370 -> [bb147, bb148]; + _369 = short_circuit(&&) copy _370 -> [eval: bb147, join: bb148]; } bb147: { @@ -4907,8 +4747,7 @@ fn user.LoopStmts(items: int[], flag: bool) -> int { bb157: { _347 = copy _335; _346 = copy _347 < const 1000000_i64; - _345 = copy _346; - branch copy _346 -> [bb158, bb159]; + _345 = short_circuit(&&) copy _346 -> [eval: bb158, join: bb159]; } bb158: { @@ -4917,8 +4756,7 @@ fn user.LoopStmts(items: int[], flag: bool) -> int { } bb159: { - _344 = copy _345; - branch copy _345 -> [bb160, bb161]; + _344 = short_circuit(&&) copy _345 -> [eval: bb160, join: bb161]; } bb160: { @@ -4928,8 +4766,7 @@ fn user.LoopStmts(items: int[], flag: bool) -> int { } bb161: { - _343 = copy _344; - branch copy _344 -> [bb162, bb163]; + _343 = short_circuit(&&) copy _344 -> [eval: bb162, join: bb163]; } bb162: { @@ -4939,8 +4776,7 @@ fn user.LoopStmts(items: int[], flag: bool) -> int { } bb163: { - _342 = copy _343; - branch copy _343 -> [bb164, bb165]; + _342 = short_circuit(&&) copy _343 -> [eval: bb164, join: bb165]; } bb164: { @@ -5500,68 +5336,68 @@ fn user.MatchExprs(x: int, s: MatchStatus, r: MatchSuccess | MatchFailure, b: bo let _15: bool let _16: bool let _17: bool - let _18: bool + let _18: int let _19: int - let _20: int - let _21: bool - let _22: MatchSuccess // s - let _23: MatchSuccess - let _24: bool - let _25: MatchFailure // f - let _26: MatchFailure + let _20: bool + let _21: MatchSuccess // s + let _22: MatchSuccess + let _23: bool + let _24: MatchFailure // f + let _25: MatchFailure + let _26: bool let _27: bool - let _28: bool - let _29: MatchSuccess | MatchFailure // x - let _30: bool - let _31: int // n - let _32: bool - let _33: int - let _34: bool - let _35: int // n - let _36: bool - let _37: int - let _38: bool - let _39: int // n + let _28: MatchSuccess | MatchFailure // x + let _29: bool + let _30: int // n + let _31: bool + let _32: int + let _33: bool + let _34: int // n + let _35: bool + let _36: int + let _37: bool + let _38: int // n + let _39: bool let _40: bool let _41: bool let _42: bool let _43: bool let _44: bool - let _45: bool + let _45: int let _46: int let _47: int let _48: int let _49: int let _50: int let _51: int - let _52: int - let _53: string // result - let _54: bool - let _55: int // n + let _52: string // result + let _53: bool + let _54: int // n + let _55: bool let _56: bool let _57: bool let _58: bool let _59: bool - let _60: bool + let _60: int let _61: int let _62: int let _63: int let _64: int - let _65: int - let _66: bool - let _67: int // n + let _65: bool + let _66: int // n + let _67: bool let _68: bool let _69: bool let _70: bool let _71: bool - let _72: bool + let _72: int let _73: int let _74: int let _75: int let _76: int let _77: int let _78: int - let _79: int + let _79: void let _80: void let _81: void let _82: void @@ -5569,43 +5405,39 @@ fn user.MatchExprs(x: int, s: MatchStatus, r: MatchSuccess | MatchFailure, b: bo let _84: void let _85: void let _86: void - let _87: void + let _87: int let _88: int let _89: int - let _90: int + let _90: void let _91: void - let _92: void + let _92: int let _93: int let _94: int let _95: int let _96: int let _97: int let _98: int - let _99: int + let _99: void let _100: void let _101: void let _102: void - let _103: void + let _103: int let _104: int - let _105: int + let _105: void let _106: void - let _107: void + let _107: int let _108: int let _109: int - let _110: int + let _110: bool let _111: bool - let _112: bool + let _112: int let _113: bool - let _114: int + let _114: MatchSuccess // s let _115: bool - let _116: bool - let _117: MatchSuccess // s - let _118: bool - let _119: MatchSuccess - let _120: bool - let _121: bool - let _122: MatchFailure - let _123: int + let _116: MatchSuccess + let _117: bool + let _118: MatchFailure + let _119: int bb0: { switch copy _1 [0: bb1, otherwise: bb1]; @@ -5668,439 +5500,423 @@ fn user.MatchExprs(x: int, s: MatchStatus, r: MatchSuccess | MatchFailure, b: bo bb12: { _16 = copy _4 == const true; - branch copy _16 -> [bb14, bb13]; + branch copy _16 -> [bb13, bb13]; } bb13: { - _17 = copy _4 == const false; - branch copy _17 -> [bb14, bb14]; + switch copy _1 [0: bb14, otherwise: bb14]; } bb14: { - switch copy _1 [0: bb15, otherwise: bb15]; + _17 = is_type(copy _1, Int { attr: TyAttr { sap_parse_without_null: Unset, sap_pending_never: Unset, sap_in_progress_never: Unset, asserts: [] } }); + branch copy _17 -> [bb15, bb15]; } bb15: { - _18 = is_type(copy _1, Int { attr: TyAttr { sap_parse_without_null: Unset, sap_pending_never: Unset, sap_in_progress_never: Unset, asserts: [] } }); - branch copy _18 -> [bb16, bb16]; + _18 = discriminant(_2); + switch copy _18 [MatchStatus.Active: bb17, MatchStatus.Inactive: bb17, MatchStatus.Pending: bb17, otherwise: bb16] (exhaustive); } bb16: { - _19 = discriminant(_2); - switch copy _19 [MatchStatus.Active: bb18, MatchStatus.Inactive: bb18, MatchStatus.Pending: bb18, otherwise: bb17] (exhaustive); + unreachable; } bb17: { - unreachable; + switch copy _1 [0: bb18, 1: bb18, 2: bb18, 3: bb18, 4: bb18, otherwise: bb18]; } bb18: { - switch copy _1 [0: bb19, 1: bb19, 2: bb19, 3: bb19, 4: bb19, otherwise: bb19]; + switch copy _1 [0: bb19, 1: bb19, 2: bb19, otherwise: bb19]; } bb19: { - switch copy _1 [0: bb20, 1: bb20, 2: bb20, otherwise: bb20]; + _19 = discriminant(_2); + switch copy _19 [MatchStatus.Active: bb21, MatchStatus.Pending: bb21, MatchStatus.Inactive: bb21, otherwise: bb20] (exhaustive); } bb20: { - _20 = discriminant(_2); - switch copy _20 [MatchStatus.Active: bb22, MatchStatus.Pending: bb22, MatchStatus.Inactive: bb22, otherwise: bb21] (exhaustive); + unreachable; } bb21: { - unreachable; + _20 = is_type(copy _3, Class(TypeName { name: "MatchSuccess", module_path: ["user"], display_name: "MatchSuccess" }, TyAttr { sap_parse_without_null: Unset, sap_pending_never: Unset, sap_in_progress_never: Unset, asserts: [] })); + branch copy _20 -> [bb24, bb22]; } bb22: { - _21 = is_type(copy _3, Class(TypeName { name: "MatchSuccess", module_path: ["user"], display_name: "MatchSuccess" }, TyAttr { sap_parse_without_null: Unset, sap_pending_never: Unset, sap_in_progress_never: Unset, asserts: [] })); - branch copy _21 -> [bb25, bb23]; + _23 = is_type(copy _3, Class(TypeName { name: "MatchFailure", module_path: ["user"], display_name: "MatchFailure" }, TyAttr { sap_parse_without_null: Unset, sap_pending_never: Unset, sap_in_progress_never: Unset, asserts: [] })); + branch copy _23 -> [bb23, bb25]; } bb23: { - _24 = is_type(copy _3, Class(TypeName { name: "MatchFailure", module_path: ["user"], display_name: "MatchFailure" }, TyAttr { sap_parse_without_null: Unset, sap_pending_never: Unset, sap_in_progress_never: Unset, asserts: [] })); - branch copy _24 -> [bb24, bb26]; + _24 = copy _3; + _25 = copy _24; + goto -> bb25; } bb24: { - _25 = copy _3; - _26 = copy _25; - goto -> bb26; + _21 = copy _3; + _22 = copy _21; + goto -> bb25; } bb25: { - _22 = copy _3; - _23 = copy _22; - goto -> bb26; + _26 = is_type(copy _3, Class(TypeName { name: "MatchSuccess", module_path: ["user"], display_name: "MatchSuccess" }, TyAttr { sap_parse_without_null: Unset, sap_pending_never: Unset, sap_in_progress_never: Unset, asserts: [] })); + branch copy _26 -> [bb27, bb26]; } bb26: { - _27 = is_type(copy _3, Class(TypeName { name: "MatchSuccess", module_path: ["user"], display_name: "MatchSuccess" }, TyAttr { sap_parse_without_null: Unset, sap_pending_never: Unset, sap_in_progress_never: Unset, asserts: [] })); - branch copy _27 -> [bb28, bb27]; + _27 = is_type(copy _3, Class(TypeName { name: "MatchFailure", module_path: ["user"], display_name: "MatchFailure" }, TyAttr { sap_parse_without_null: Unset, sap_pending_never: Unset, sap_in_progress_never: Unset, asserts: [] })); + branch copy _27 -> [bb27, bb28]; } bb27: { - _28 = is_type(copy _3, Class(TypeName { name: "MatchFailure", module_path: ["user"], display_name: "MatchFailure" }, TyAttr { sap_parse_without_null: Unset, sap_pending_never: Unset, sap_in_progress_never: Unset, asserts: [] })); - branch copy _28 -> [bb28, bb29]; + _28 = copy _3; + goto -> bb28; } bb28: { - _29 = copy _3; - goto -> bb29; + switch copy _28 [0: bb29, 1: bb29, 2: bb29, 3: bb29, 4: bb29, 5: bb29, otherwise: bb29]; } bb29: { - switch copy _29 [0: bb30, 1: bb30, 2: bb30, 3: bb30, 4: bb30, 5: bb30, otherwise: bb30]; + _29 = is_type(copy _28, Int { attr: TyAttr { sap_parse_without_null: Unset, sap_pending_never: Unset, sap_in_progress_never: Unset, asserts: [] } }); + branch copy _29 -> [bb30, bb31]; } bb30: { - _30 = is_type(copy _29, Int { attr: TyAttr { sap_parse_without_null: Unset, sap_pending_never: Unset, sap_in_progress_never: Unset, asserts: [] } }); - branch copy _30 -> [bb31, bb32]; + _30 = copy _28; + _32 = copy _30; + _31 = copy _32 > const 0_i64; + branch copy _31 -> [bb33, bb31]; } bb31: { - _31 = copy _29; - _33 = copy _31; - _32 = copy _33 > const 0_i64; - branch copy _32 -> [bb34, bb32]; + _33 = is_type(copy _28, Int { attr: TyAttr { sap_parse_without_null: Unset, sap_pending_never: Unset, sap_in_progress_never: Unset, asserts: [] } }); + branch copy _33 -> [bb32, bb33]; } bb32: { - _34 = is_type(copy _29, Int { attr: TyAttr { sap_parse_without_null: Unset, sap_pending_never: Unset, sap_in_progress_never: Unset, asserts: [] } }); - branch copy _34 -> [bb33, bb34]; + _34 = copy _28; + _36 = copy _34; + _35 = copy _36 < const 0_i64; + branch copy _35 -> [bb33, bb33]; } bb33: { - _35 = copy _29; - _37 = copy _35; - _36 = copy _37 < const 0_i64; - branch copy _36 -> [bb34, bb34]; + _37 = is_type(copy _28, Int { attr: TyAttr { sap_parse_without_null: Unset, sap_pending_never: Unset, sap_in_progress_never: Unset, asserts: [] } }); + branch copy _37 -> [bb34, bb45]; } bb34: { - _38 = is_type(copy _29, Int { attr: TyAttr { sap_parse_without_null: Unset, sap_pending_never: Unset, sap_in_progress_never: Unset, asserts: [] } }); - branch copy _38 -> [bb35, bb46]; + _38 = copy _28; + _45 = copy _38; + _44 = copy _45 > const 0_i64; + _43 = short_circuit(&&) copy _44 -> [eval: bb35, join: bb36]; } bb35: { - _39 = copy _29; - _46 = copy _39; - _45 = copy _46 > const 0_i64; - _44 = copy _45; - branch copy _45 -> [bb36, bb37]; + _46 = copy _38; + _43 = copy _46 < const 100_i64; + goto -> bb36; } bb36: { - _47 = copy _39; - _44 = copy _47 < const 100_i64; - goto -> bb37; + _42 = short_circuit(&&) copy _43 -> [eval: bb37, join: bb38]; } bb37: { - _43 = copy _44; - branch copy _44 -> [bb38, bb39]; + _47 = copy _38; + _42 = copy _47 != const 42_i64; + goto -> bb38; } bb38: { - _48 = copy _39; - _43 = copy _48 != const 42_i64; - goto -> bb39; + _41 = short_circuit(&&) copy _42 -> [eval: bb39, join: bb40]; } bb39: { - _42 = copy _43; - branch copy _43 -> [bb40, bb41]; + _48 = copy _38; + _41 = copy _48 != const 13_i64; + goto -> bb40; } bb40: { - _49 = copy _39; - _42 = copy _49 != const 13_i64; - goto -> bb41; + _40 = short_circuit(&&) copy _41 -> [eval: bb41, join: bb42]; } bb41: { - _41 = copy _42; - branch copy _42 -> [bb42, bb43]; + _49 = copy _38; + _40 = copy _49 != const 7_i64; + goto -> bb42; } bb42: { - _50 = copy _39; - _41 = copy _50 != const 7_i64; - goto -> bb43; + _39 = short_circuit(&&) copy _40 -> [eval: bb43, join: bb44]; } bb43: { - _40 = copy _41; - branch copy _41 -> [bb44, bb45]; + _50 = copy _38; + _39 = copy _50 != const 99_i64; + goto -> bb44; } bb44: { - _51 = copy _39; - _40 = copy _51 != const 99_i64; - goto -> bb45; + branch copy _39 -> [bb45, bb45]; } bb45: { - branch copy _40 -> [bb46, bb46]; + switch copy _28 [0: bb46, 1: bb46, otherwise: bb46]; } bb46: { - switch copy _29 [0: bb47, 1: bb47, otherwise: bb47]; + switch copy _28 [0: bb47, 1: bb47, 2: bb47, 3: bb47, 4: bb47, 5: bb47, 6: bb47, 7: bb47, 8: bb47, 9: bb47, 10: bb47, 11: bb47, 12: bb47, 13: bb47, 14: bb47, 15: bb47, 16: bb47, 17: bb47, 18: bb47, 19: bb47, 20: bb47, 21: bb47, 22: bb47, 23: bb47, 24: bb47, otherwise: bb47]; } bb47: { - switch copy _29 [0: bb48, 1: bb48, 2: bb48, 3: bb48, 4: bb48, 5: bb48, 6: bb48, 7: bb48, 8: bb48, 9: bb48, 10: bb48, 11: bb48, 12: bb48, 13: bb48, 14: bb48, 15: bb48, 16: bb48, 17: bb48, 18: bb48, 19: bb48, 20: bb48, 21: bb48, 22: bb48, 23: bb48, 24: bb48, otherwise: bb48]; + _51 = discriminant(_21); + switch copy _51 [MatchStatus.Active: bb49, MatchStatus.Inactive: bb49, MatchStatus.Pending: bb49, otherwise: bb48] (exhaustive); } bb48: { - _52 = discriminant(_22); - switch copy _52 [MatchStatus.Active: bb50, MatchStatus.Inactive: bb50, MatchStatus.Pending: bb50, otherwise: bb49] (exhaustive); + unreachable; } bb49: { - unreachable; + switch copy _28 [0: bb50, otherwise: bb53]; } bb50: { - switch copy _29 [0: bb51, otherwise: bb54]; + branch copy _4 -> [bb52, bb51]; } bb51: { - branch copy _4 -> [bb53, bb52]; + _52 = const "this is the false branch also very long"; + goto -> bb53; } bb52: { - _53 = const "this is the false branch also very long"; - goto -> bb54; + _52 = const "this is the true branch with a very long string"; + goto -> bb53; } bb53: { - _53 = const "this is the true branch with a very long string"; - goto -> bb54; + _53 = is_type(copy _28, Int { attr: TyAttr { sap_parse_without_null: Unset, sap_pending_never: Unset, sap_in_progress_never: Unset, asserts: [] } }); + branch copy _53 -> [bb54, bb63]; } bb54: { - _54 = is_type(copy _29, Int { attr: TyAttr { sap_parse_without_null: Unset, sap_pending_never: Unset, sap_in_progress_never: Unset, asserts: [] } }); - branch copy _54 -> [bb55, bb64]; + _54 = copy _28; + _60 = copy _54; + _59 = copy _60 > const 0_i64; + _58 = short_circuit(&&) copy _59 -> [eval: bb55, join: bb56]; } bb55: { - _55 = copy _29; - _61 = copy _55; - _60 = copy _61 > const 0_i64; - _59 = copy _60; - branch copy _60 -> [bb56, bb57]; + _61 = copy _54; + _58 = copy _61 < const 100_i64; + goto -> bb56; } bb56: { - _62 = copy _55; - _59 = copy _62 < const 100_i64; - goto -> bb57; + _57 = short_circuit(&&) copy _58 -> [eval: bb57, join: bb58]; } bb57: { - _58 = copy _59; - branch copy _59 -> [bb58, bb59]; + _62 = copy _54; + _57 = copy _62 != const 42_i64; + goto -> bb58; } bb58: { - _63 = copy _55; - _58 = copy _63 != const 42_i64; - goto -> bb59; + _56 = short_circuit(&&) copy _57 -> [eval: bb59, join: bb60]; } bb59: { - _57 = copy _58; - branch copy _58 -> [bb60, bb61]; + _63 = copy _54; + _56 = copy _63 != const 13_i64; + goto -> bb60; } bb60: { - _64 = copy _55; - _57 = copy _64 != const 13_i64; - goto -> bb61; + _55 = short_circuit(&&) copy _56 -> [eval: bb61, join: bb62]; } bb61: { - _56 = copy _57; - branch copy _57 -> [bb62, bb63]; + _64 = copy _54; + _55 = copy _64 != const 7_i64; + goto -> bb62; } bb62: { - _65 = copy _55; - _56 = copy _65 != const 7_i64; - goto -> bb63; + branch copy _55 -> [bb63, bb63]; } bb63: { - branch copy _56 -> [bb64, bb64]; + _65 = is_type(copy _28, Int { attr: TyAttr { sap_parse_without_null: Unset, sap_pending_never: Unset, sap_in_progress_never: Unset, asserts: [] } }); + branch copy _65 -> [bb64, bb73]; } bb64: { - _66 = is_type(copy _29, Int { attr: TyAttr { sap_parse_without_null: Unset, sap_pending_never: Unset, sap_in_progress_never: Unset, asserts: [] } }); - branch copy _66 -> [bb65, bb74]; + _66 = copy _28; + _72 = copy _66; + _71 = copy _72 > const 0_i64; + _70 = short_circuit(&&) copy _71 -> [eval: bb65, join: bb66]; } bb65: { - _67 = copy _29; - _73 = copy _67; - _72 = copy _73 > const 0_i64; - _71 = copy _72; - branch copy _72 -> [bb66, bb67]; + _73 = copy _66; + _70 = copy _73 < const 100_i64; + goto -> bb66; } bb66: { - _74 = copy _67; - _71 = copy _74 < const 100_i64; - goto -> bb67; + _69 = short_circuit(&&) copy _70 -> [eval: bb67, join: bb68]; } bb67: { - _70 = copy _71; - branch copy _71 -> [bb68, bb69]; + _74 = copy _66; + _69 = copy _74 != const 42_i64; + goto -> bb68; } bb68: { - _75 = copy _67; - _70 = copy _75 != const 42_i64; - goto -> bb69; + _68 = short_circuit(&&) copy _69 -> [eval: bb69, join: bb70]; } bb69: { - _69 = copy _70; - branch copy _70 -> [bb70, bb71]; + _75 = copy _66; + _68 = copy _75 != const 13_i64; + goto -> bb70; } bb70: { - _76 = copy _67; - _69 = copy _76 != const 13_i64; - goto -> bb71; + _67 = short_circuit(&&) copy _68 -> [eval: bb71, join: bb72]; } bb71: { - _68 = copy _69; - branch copy _69 -> [bb72, bb73]; + _76 = copy _66; + _67 = copy _76 != const 7_i64; + goto -> bb72; } bb72: { - _77 = copy _67; - _68 = copy _77 != const 7_i64; - goto -> bb73; + branch copy _67 -> [bb73, bb73]; } bb73: { - branch copy _68 -> [bb74, bb74]; + _78 = copy _28; + _77 = copy _78 + const 1_i64; + switch copy _77 [0: bb74, otherwise: bb74]; } bb74: { - _79 = copy _29; - _78 = copy _79 + const 1_i64; - switch copy _78 [0: bb75, otherwise: bb75]; + _88 = copy _28; + _89 = copy _28; + _87 = copy _88 * copy _89; + _90 = copy _4 * copy _4; + _86 = copy _87 + copy _90; + _92 = copy _28; + _91 = copy _92 * copy _4; + _85 = copy _86 + copy _91; + _93 = copy _28; + _84 = copy _85 + copy _93; + _83 = copy _84 + copy _4; + _96 = copy _28; + _97 = copy _28; + _95 = copy _96 * copy _97; + _98 = copy _28; + _94 = copy _95 * copy _98; + _82 = copy _83 + copy _94; + _100 = copy _4 * copy _4; + _99 = copy _100 * copy _4; + _81 = copy _82 + copy _99; + _103 = copy _28; + _102 = copy _103 * copy _4; + _104 = copy _28; + _101 = copy _102 * copy _104; + _80 = copy _81 + copy _101; + _107 = copy _28; + _106 = copy _4 * copy _107; + _105 = copy _106 * copy _4; + _79 = copy _80 + copy _105; + switch copy _79 [0: bb75, otherwise: bb75]; } bb75: { - _89 = copy _29; - _90 = copy _29; - _88 = copy _89 * copy _90; - _91 = copy _4 * copy _4; - _87 = copy _88 + copy _91; - _93 = copy _29; - _92 = copy _93 * copy _4; - _86 = copy _87 + copy _92; - _94 = copy _29; - _85 = copy _86 + copy _94; - _84 = copy _85 + copy _4; - _97 = copy _29; - _98 = copy _29; - _96 = copy _97 * copy _98; - _99 = copy _29; - _95 = copy _96 * copy _99; - _83 = copy _84 + copy _95; - _101 = copy _4 * copy _4; - _100 = copy _101 * copy _4; - _82 = copy _83 + copy _100; - _104 = copy _29; - _103 = copy _104 * copy _4; - _105 = copy _29; - _102 = copy _103 * copy _105; - _81 = copy _82 + copy _102; - _108 = copy _29; - _107 = copy _4 * copy _108; - _106 = copy _107 * copy _4; - _80 = copy _81 + copy _106; - switch copy _80 [0: bb76, otherwise: bb76]; + _109 = copy _28; + _108 = copy _109 + const 1_i64; + switch copy _108 [0: bb76, otherwise: bb76]; } bb76: { - _110 = copy _29; - _109 = copy _110 + const 1_i64; - switch copy _109 [0: bb77, otherwise: bb77]; + switch copy _28 [0: bb77, otherwise: bb77]; } bb77: { - switch copy _29 [0: bb78, otherwise: bb78]; + switch copy _28 [0: bb78, otherwise: bb79]; } bb78: { - switch copy _29 [0: bb79, otherwise: bb81]; + _110 = copy _4 == const true; + branch copy _110 -> [bb79, bb79]; } bb79: { - _111 = copy _4 == const true; - branch copy _111 -> [bb81, bb80]; + switch copy _28 [0: bb85, 1: bb80, otherwise: bb88]; } bb80: { - _112 = copy _4 == const false; - branch copy _112 -> [bb81, bb81]; + _113 = is_type(copy _3, Class(TypeName { name: "MatchSuccess", module_path: ["user"], display_name: "MatchSuccess" }, TyAttr { sap_parse_without_null: Unset, sap_pending_never: Unset, sap_in_progress_never: Unset, asserts: [] })); + branch copy _113 -> [bb83, bb81]; } bb81: { - switch copy _29 [0: bb88, 1: bb82, otherwise: bb92]; + _117 = is_type(copy _3, Class(TypeName { name: "MatchFailure", module_path: ["user"], display_name: "MatchFailure" }, TyAttr { sap_parse_without_null: Unset, sap_pending_never: Unset, sap_in_progress_never: Unset, asserts: [] })); + branch copy _117 -> [bb82, bb88]; } bb82: { - _116 = is_type(copy _3, Class(TypeName { name: "MatchSuccess", module_path: ["user"], display_name: "MatchSuccess" }, TyAttr { sap_parse_without_null: Unset, sap_pending_never: Unset, sap_in_progress_never: Unset, asserts: [] })); - branch copy _116 -> [bb85, bb83]; + _118 = const null; + goto -> bb88; } bb83: { - _121 = is_type(copy _3, Class(TypeName { name: "MatchFailure", module_path: ["user"], display_name: "MatchFailure" }, TyAttr { sap_parse_without_null: Unset, sap_pending_never: Unset, sap_in_progress_never: Unset, asserts: [] })); - branch copy _121 -> [bb84, bb92]; + _114 = copy _3; + _115 = copy _4 == const true; + branch copy _115 -> [bb84, bb88]; } bb84: { - _122 = const null; - goto -> bb92; + _116 = copy _114; + goto -> bb88; } bb85: { - _117 = copy _3; - _118 = copy _4 == const true; - branch copy _118 -> [bb87, bb86]; + _111 = copy _4 == const true; + branch copy _111 -> [bb86, bb88]; } bb86: { - _120 = copy _4 == const false; - branch copy _120 -> [bb92, bb92]; + _112 = discriminant(_21); + switch copy _112 [MatchStatus.Active: bb88, MatchStatus.Inactive: bb88, MatchStatus.Pending: bb88, otherwise: bb87] (exhaustive); } bb87: { - _119 = copy _117; - goto -> bb92; + unreachable; } bb88: { - _113 = copy _4 == const true; - branch copy _113 -> [bb90, bb89]; + switch copy _28 [0: bb89, otherwise: bb92]; } bb89: { - _115 = copy _4 == const false; - branch copy _115 -> [bb92, bb92]; + branch copy _4 -> [bb90, bb92]; } bb90: { - _114 = discriminant(_22); - switch copy _114 [MatchStatus.Active: bb92, MatchStatus.Inactive: bb92, MatchStatus.Pending: bb92, otherwise: bb91] (exhaustive); + _119 = discriminant(_114); + switch copy _119 [MatchStatus.Active: bb92, MatchStatus.Inactive: bb92, MatchStatus.Pending: bb92, otherwise: bb91] (exhaustive); } bb91: { @@ -6108,37 +5924,20 @@ fn user.MatchExprs(x: int, s: MatchStatus, r: MatchSuccess | MatchFailure, b: bo } bb92: { - switch copy _29 [0: bb93, otherwise: bb96]; + switch copy _28 [0: bb94, otherwise: bb93]; } bb93: { - branch copy _4 -> [bb94, bb96]; - } - - bb94: { - _123 = discriminant(_117); - switch copy _123 [MatchStatus.Active: bb96, MatchStatus.Inactive: bb96, MatchStatus.Pending: bb96, otherwise: bb95] (exhaustive); - } - - bb95: { - unreachable; - } - - bb96: { - switch copy _29 [0: bb98, otherwise: bb97]; - } - - bb97: { _0 = const "other"; - goto -> bb99; + goto -> bb95; } - bb98: { + bb94: { _0 = const "zero"; - goto -> bb99; + goto -> bb95; } - bb99: { + bb95: { return; } } diff --git a/baml_language/crates/baml_tests/snapshots/format_checks/baml_tests__format_checks__06_codegen.snap b/baml_language/crates/baml_tests/snapshots/format_checks/baml_tests__format_checks__06_codegen.snap index 4a4f6f5ee4..a885593fc4 100644 --- a/baml_language/crates/baml_tests/snapshots/format_checks/baml_tests__format_checks__06_codegen.snap +++ b/baml_language/crates/baml_tests/snapshots/format_checks/baml_tests__format_checks__06_codegen.snap @@ -167,379 +167,242 @@ function user.AssertStmts(x: int, y: int) -> int { load_var x load_const 0 cmp_op > - store_var _10 - load_var _10 - store_var _9 - load_var _10 - pop_jump_if_false L0 + jump_if_false L0 + pop 1 load_var x load_const 100 cmp_op < - store_var _9 L0: - load_var _9 - store_var _8 - load_var _9 - pop_jump_if_false L1 + jump_if_false L1 + pop 1 load_var y load_const 0 cmp_op > - store_var _8 L1: - load_var _8 - store_var _7 - load_var _8 - pop_jump_if_false L2 + jump_if_false L2 + pop 1 load_var y load_const 100 cmp_op < - store_var _7 L2: - load_var _7 - store_var _6 - load_var _7 - pop_jump_if_false L3 + jump_if_false L3 + pop 1 load_var x load_var y cmp_op != - store_var _6 L3: - load_var _6 - store_var _5 - load_var _6 - pop_jump_if_false L4 + jump_if_false L4 + pop 1 load_var x load_var y bin_op + load_const 150 cmp_op < - store_var _5 L4: - load_var _5 - store_var _4 - load_var _5 - pop_jump_if_false L5 + jump_if_false L5 + pop 1 load_var x load_var y bin_op * load_const 10 cmp_op > - store_var _4 L5: - load_var _4 - store_var _3 - load_var _4 - pop_jump_if_false L6 - load_var x - load_var y - bin_op - - load_const 42 - cmp_op != - store_var _3 + jump_if_false L6 + pop 1 L6: - load_var _3 - pop_jump_if_false L7 - - L7: load_var x load_const 0 cmp_op > - store_var _25 - load_var _25 - store_var _24 - load_var _25 - pop_jump_if_false L8 + jump_if_false L7 + pop 1 load_var x load_const 100 cmp_op < - store_var _24 - L8: - load_var _24 - store_var _23 - load_var _24 - pop_jump_if_false L9 + L7: + jump_if_false L8 + pop 1 load_var y load_const 0 cmp_op > - store_var _23 - L9: - load_var _23 - store_var _22 - load_var _23 - pop_jump_if_false L10 + L8: + jump_if_false L9 + pop 1 load_var y load_const 100 cmp_op < - store_var _22 - L10: - load_var _22 - store_var _21 - load_var _22 - pop_jump_if_false L11 + L9: + jump_if_false L10 + pop 1 load_var x load_var y cmp_op != - store_var _21 - L11: - load_var _21 - store_var _20 - load_var _21 - pop_jump_if_false L12 + L10: + jump_if_false L11 + pop 1 load_var x load_var y bin_op + load_const 150 cmp_op < - store_var _20 - L12: - load_var _20 - store_var _19 - load_var _20 - pop_jump_if_false L13 + L11: + jump_if_false L12 + pop 1 load_var x load_var y bin_op * load_const 10 cmp_op > - store_var _19 - L13: - load_var _19 - store_var _18 - load_var _19 - pop_jump_if_false L14 + L12: + jump_if_false L13 + pop 1 load_var x load_var y bin_op - load_const 42 cmp_op != - store_var _18 - L14: - load_var _18 - store_var _17 - load_var _18 - pop_jump_if_false L15 + L13: + jump_if_false L14 + pop 1 load_var x load_const 7 bin_op % load_const 0 cmp_op == - store_var _17 - L15: - load_var _17 - store_var _16 - load_var _17 - pop_jump_if_false L16 + L14: + jump_if_false L15 + pop 1 load_var y load_const 3 bin_op % load_const 0 cmp_op == - store_var _16 - L16: - load_var _16 - store_var _15 - load_var _16 - pop_jump_if_false L17 - load_var x - load_var y - bin_op + - load_var x - load_var y - bin_op * - bin_op + - load_const 100 - cmp_op > - store_var _15 - - L17: - load_var _15 - pop_jump_if_false L18 + L15: + jump_if_false L16 + pop 1 - L18: + L16: load_var x load_const 0 cmp_op > - store_var _44 - load_var _44 - store_var _43 - load_var _44 - pop_jump_if_false L19 + jump_if_false L17 + pop 1 load_var x load_const 100 cmp_op < - store_var _43 - L19: - load_var _43 - store_var _42 - load_var _43 - pop_jump_if_false L20 + L17: + jump_if_false L18 + pop 1 load_var y load_const 0 cmp_op > - store_var _42 - L20: - load_var _42 - store_var _41 - load_var _42 - pop_jump_if_false L21 + L18: + jump_if_false L19 + pop 1 load_var y load_const 100 cmp_op < - store_var _41 - L21: - load_var _41 - store_var _40 - load_var _41 - pop_jump_if_false L22 + L19: + jump_if_false L20 + pop 1 load_var x load_var y cmp_op != - store_var _40 - L22: - load_var _40 - store_var _39 - load_var _40 - pop_jump_if_false L23 + L20: + jump_if_false L21 + pop 1 load_var x load_var y bin_op + load_const 150 cmp_op < - store_var _39 - L23: - load_var _39 - store_var _38 - load_var _39 - pop_jump_if_false L24 + L21: + jump_if_false L22 + pop 1 load_var x load_var y bin_op * load_const 10 cmp_op > - store_var _38 - L24: - load_var _38 - store_var _37 - load_var _38 - pop_jump_if_false L25 - load_var x - load_var y - bin_op - - load_const 42 - cmp_op != - store_var _37 - - L25: - load_var _37 - pop_jump_if_false L26 + L22: + jump_if_false L23 + pop 1 - L26: + L23: load_var x load_const 0 cmp_op > - store_var _56 - load_var _56 - store_var _55 - load_var _56 - pop_jump_if_false L27 + jump_if_false L24 + pop 1 load_var x load_const 100 cmp_op < - store_var _55 - L27: - load_var _55 - store_var _54 - load_var _55 - pop_jump_if_false L28 + L24: + jump_if_false L25 + pop 1 load_var y load_const 0 cmp_op > - store_var _54 - L28: - load_var _54 - store_var _53 - load_var _54 - pop_jump_if_false L29 + L25: + jump_if_false L26 + pop 1 load_var y load_const 100 cmp_op < - store_var _53 - L29: - load_var _53 - store_var _52 - load_var _53 - pop_jump_if_false L30 + L26: + jump_if_false L27 + pop 1 load_var x load_var y cmp_op != - store_var _52 - L30: - load_var _52 - store_var _51 - load_var _52 - pop_jump_if_false L31 + L27: + jump_if_false L28 + pop 1 load_var x load_var y bin_op + load_const 150 cmp_op < - store_var _51 - L31: - load_var _51 - store_var _50 - load_var _51 - pop_jump_if_false L32 + L28: + jump_if_false L29 + pop 1 load_var x load_var y bin_op * load_const 10 cmp_op > - store_var _50 - L32: - load_var _50 - store_var _49 - load_var _50 - pop_jump_if_false L33 - load_var x - load_var y - bin_op - - load_const 42 - cmp_op != - store_var _49 - - L33: - load_var _49 - pop_jump_if_false L34 + L29: + jump_if_false L30 + pop 1 - L34: + L30: load_var x return } @@ -586,14 +449,6 @@ function user.BasicClient$new() -> null { } function user.BinaryExprs(a: int, b: int, c: int, d: int, s: string, flag: bool) -> int { - load_var flag - pop_jump_if_false L0 - - L0: - load_var flag - pop_jump_if_false L1 - - L1: load_var a store_var mut_a load_var mut_a @@ -639,170 +494,90 @@ function user.BinaryExprs(a: int, b: int, c: int, d: int, s: string, flag: bool) load_var a load_const 0 cmp_op > - pop_jump_if_false L2 - - L2: - load_var a - load_const 0 - cmp_op > - pop_jump_if_false L3 - - L3: - load_var a - load_const 0 - cmp_op > - store_var _19 - load_var _19 - store_var _18 - load_var _19 - pop_jump_if_false L4 - load_var b - load_const 0 - cmp_op > - store_var _18 - - L4: - load_var _18 - pop_jump_if_false L5 + jump_if_false L0 + pop 1 - L5: + L0: load_var a load_const 0 cmp_op > - store_var _21 - load_var _21 - store_var _20 - load_var _21 - pop_jump_if_false L6 - jump L7 - - L6: - load_var b - load_const 0 - cmp_op > - store_var _20 + jump_if_false L1 + jump L2 - L7: - load_var _20 - pop_jump_if_false L8 + L1: + pop 1 - L8: + L2: load_var a load_const 0 cmp_op > - store_var _23 - load_var _23 - store_var _22 - load_var _23 - pop_jump_if_false L9 - load_var b - load_const 0 - cmp_op > - store_var _22 - - L9: - load_var _22 - pop_jump_if_false L10 + jump_if_false L3 + pop 1 - L10: + L3: load_var a load_const 0 cmp_op > - store_var _97 - load_var _97 - store_var _96 - load_var _97 - pop_jump_if_false L11 + jump_if_false L4 + pop 1 load_var b load_const 0 cmp_op > - store_var _96 - L11: - load_var _96 - store_var _95 - load_var _96 - pop_jump_if_false L12 + L4: + jump_if_false L5 + pop 1 load_var c load_const 0 cmp_op > - store_var _95 - L12: - load_var _95 - store_var _94 - load_var _95 - pop_jump_if_false L13 + L5: + jump_if_false L6 + pop 1 load_var d load_const 0 cmp_op > - store_var _94 - L13: - load_var _94 - store_var _93 - load_var _94 - pop_jump_if_false L14 + L6: + jump_if_false L7 + pop 1 load_var a load_var b cmp_op != - store_var _93 - L14: - load_var _93 - store_var _92 - load_var _93 - pop_jump_if_false L15 + L7: + jump_if_false L8 + pop 1 load_var b load_var c cmp_op != - store_var _92 - L15: - load_var _92 - store_var _91 - load_var _92 - pop_jump_if_false L16 + L8: + jump_if_false L9 + pop 1 load_var c load_var d cmp_op != - store_var _91 - L16: - load_var _91 - store_var _90 - load_var _91 - pop_jump_if_false L17 + L9: + jump_if_false L10 + pop 1 load_var a load_const 8002 cmp_op < - store_var _90 - L17: - load_var _90 - store_var _89 - load_var _90 - pop_jump_if_false L18 + L10: + jump_if_false L11 + pop 1 load_var b load_const 1000 cmp_op < - store_var _89 - L18: - load_var _89 - store_var _88 - load_var _89 - pop_jump_if_false L19 - load_var c - load_const 1000 - cmp_op < - store_var _88 - - L19: - load_var _88 - pop_jump_if_false L20 + L11: + jump_if_false L12 + pop 1 - L20: + L12: load_var a load_var b bin_op + @@ -812,11 +587,8 @@ function user.BinaryExprs(a: int, b: int, c: int, d: int, s: string, flag: bool) bin_op + load_const 0 cmp_op > - store_var _139 - load_var _139 - store_var _138 - load_var _139 - pop_jump_if_false L21 + jump_if_false L13 + pop 1 load_var a load_var b bin_op * @@ -826,13 +598,10 @@ function user.BinaryExprs(a: int, b: int, c: int, d: int, s: string, flag: bool) bin_op * load_const 10000 cmp_op < - store_var _138 - L21: - load_var _138 - store_var _137 - load_var _138 - pop_jump_if_false L22 + L13: + jump_if_false L14 + pop 1 load_var a load_var b bin_op - @@ -842,38 +611,12 @@ function user.BinaryExprs(a: int, b: int, c: int, d: int, s: string, flag: bool) bin_op - load_const 0 cmp_op != - store_var _137 - L22: - load_var _137 - store_var _136 - load_var _137 - pop_jump_if_false L23 - load_var a - load_var b - bin_op + - load_var c - load_var d - bin_op + - bin_op * - load_const 100 - cmp_op >= - store_var _136 - - L23: - load_var _136 - pop_jump_if_false L24 - jump L25 - - L24: - load_var a - load_const 1 - bin_op * - load_const 2 - cmp_op < - pop_jump_if_false L25 + L14: + jump_if_false L15 + pop 1 - L25: + L15: load_var mut_a load_var a load_var b @@ -921,67 +664,38 @@ function user.BinaryExprs(a: int, b: int, c: int, d: int, s: string, flag: bool) load_var a load_const 0 cmp_op > - store_var _199 - load_var _199 - store_var _198 - load_var _199 - pop_jump_if_false L26 + jump_if_false L16 + pop 1 load_var b load_const 0 cmp_op > - store_var _198 - L26: - load_var _198 - store_var _197 - load_var _198 - pop_jump_if_false L27 + L16: + jump_if_false L17 + pop 1 load_var c load_const 0 cmp_op > - store_var _197 - L27: - load_var _197 - store_var _196 - load_var _197 - pop_jump_if_false L28 + L17: + jump_if_false L18 + pop 1 load_var d load_const 0 cmp_op > - store_var _196 - L28: - load_var _196 - store_var _195 - load_var _196 - pop_jump_if_false L29 + L18: + jump_if_false L19 + pop 1 load_var a load_var b cmp_op != - store_var _195 - L29: - load_var _195 - store_var _194 - load_var _195 - pop_jump_if_false L30 - load_var b - load_var c - cmp_op != - store_var _194 - - L30: - load_var _194 - pop_jump_if_false L31 - - L31: - load_var a - load_const 2 - cmp_op == - pop_jump_if_false L32 + L19: + jump_if_false L20 + pop 1 - L32: + L20: load_var a load_var b bin_op + @@ -2013,16 +1727,11 @@ function user.LoopStmts(items: int[], flag: bool) -> int { load_var a load_const 100 cmp_op < - store_var _7 - load_var _7 - store_var _6 - load_var _7 - pop_jump_if_false L2 + jump_if_false L2 + pop 1 load_var flag - store_var _6 L2: - load_var _6 pop_jump_if_false L3 jump L181 @@ -2084,16 +1793,11 @@ function user.LoopStmts(items: int[], flag: bool) -> int { load_var i load_const 100 cmp_op < - store_var _27 - load_var _27 - store_var _26 - load_var _27 - pop_jump_if_false L12 + jump_if_false L12 + pop 1 load_var flag - store_var _26 L12: - load_var _26 pop_jump_if_false L13 jump L176 @@ -2195,72 +1899,52 @@ function user.LoopStmts(items: int[], flag: bool) -> int { load_var a load_const 100 cmp_op < - store_var _84 - load_var _84 - store_var _83 - load_var _84 - pop_jump_if_false L28 + jump_if_false L28 + pop 1 load_var result load_const 0 cmp_op > - store_var _83 L28: - load_var _83 - store_var _82 - load_var _83 - pop_jump_if_false L29 + jump_if_false L29 + pop 1 load_var flag - store_var _82 L29: - load_var _82 - store_var _81 - load_var _82 - pop_jump_if_false L30 + jump_if_false L30 + pop 1 load_var a load_var result cmp_op != - store_var _81 L30: - load_var _81 - store_var _80 - load_var _81 - pop_jump_if_false L31 + jump_if_false L31 + pop 1 load_var a load_var result bin_op + load_const 10000 cmp_op < - store_var _80 L31: - load_var _80 - store_var _79 - load_var _80 - pop_jump_if_false L32 + jump_if_false L32 + pop 1 load_var a load_var result bin_op * load_const 500 cmp_op > - store_var _79 L32: - load_var _79 - store_var _78 - load_var _79 - pop_jump_if_false L33 + jump_if_false L33 + pop 1 load_var a load_var result bin_op - load_const 42 cmp_op != - store_var _78 L33: - load_var _78 pop_jump_if_false L34 jump L165 @@ -2268,99 +1952,72 @@ function user.LoopStmts(items: int[], flag: bool) -> int { load_var a load_const 100 cmp_op < - store_var _107 - load_var _107 - store_var _106 - load_var _107 - pop_jump_if_false L35 + jump_if_false L35 + pop 1 load_var result load_const 0 cmp_op > - store_var _106 L35: - load_var _106 - store_var _105 - load_var _106 - pop_jump_if_false L36 + jump_if_false L36 + pop 1 load_var flag - store_var _105 L36: - load_var _105 - store_var _104 - load_var _105 - pop_jump_if_false L37 + jump_if_false L37 + pop 1 load_var a load_var result cmp_op != - store_var _104 L37: - load_var _104 - store_var _103 - load_var _104 - pop_jump_if_false L38 + jump_if_false L38 + pop 1 load_var a load_var result bin_op + load_const 10000 cmp_op < - store_var _103 L38: - load_var _103 - store_var _102 - load_var _103 - pop_jump_if_false L39 + jump_if_false L39 + pop 1 load_var a load_var result bin_op * load_const 500 cmp_op > - store_var _102 L39: - load_var _102 - store_var _101 - load_var _102 - pop_jump_if_false L40 + jump_if_false L40 + pop 1 load_var a load_var result bin_op - load_const 42 cmp_op != - store_var _101 L40: - load_var _101 - store_var _100 - load_var _101 - pop_jump_if_false L41 + jump_if_false L41 + pop 1 load_var a load_const 7 bin_op % load_const 0 cmp_op == - store_var _100 L41: - load_var _100 - store_var _99 - load_var _100 - pop_jump_if_false L42 + jump_if_false L42 + pop 1 load_var result load_const 3 bin_op % load_const 0 cmp_op == - store_var _99 L42: - load_var _99 - store_var _98 - load_var _99 - pop_jump_if_false L43 + jump_if_false L43 + pop 1 load_var a load_var result bin_op + @@ -2370,10 +2027,8 @@ function user.LoopStmts(items: int[], flag: bool) -> int { bin_op + load_const 99999 cmp_op > - store_var _98 L43: - load_var _98 pop_jump_if_false L44 jump L164 @@ -2385,16 +2040,11 @@ function user.LoopStmts(items: int[], flag: bool) -> int { load_var very_long_variable_name load_const 1000 cmp_op < - store_var _134 - load_var _134 - store_var _133 - load_var _134 - pop_jump_if_false L46 + jump_if_false L46 + pop 1 load_var flag - store_var _133 L46: - load_var _133 pop_jump_if_false L47 jump L163 @@ -2412,26 +2062,18 @@ function user.LoopStmts(items: int[], flag: bool) -> int { load_var extremely_long_loop_counter_variable load_const 1000000 cmp_op < - store_var _146 - load_var _146 - store_var _145 - load_var _146 - pop_jump_if_false L49 + jump_if_false L49 + pop 1 load_var flag - store_var _145 L49: - load_var _145 - store_var _144 - load_var _145 - pop_jump_if_false L50 + jump_if_false L50 + pop 1 load_var a load_const 0 cmp_op > - store_var _144 L50: - load_var _144 pop_jump_if_false L51 jump L162 @@ -2477,72 +2119,52 @@ function user.LoopStmts(items: int[], flag: bool) -> int { load_var a load_const 100 cmp_op < - store_var _169 - load_var _169 - store_var _168 - load_var _169 - pop_jump_if_false L56 + jump_if_false L56 + pop 1 load_var result load_const 0 cmp_op > - store_var _168 L56: - load_var _168 - store_var _167 - load_var _168 - pop_jump_if_false L57 + jump_if_false L57 + pop 1 load_var flag - store_var _167 L57: - load_var _167 - store_var _166 - load_var _167 - pop_jump_if_false L58 + jump_if_false L58 + pop 1 load_var a load_var result cmp_op != - store_var _166 L58: - load_var _166 - store_var _165 - load_var _166 - pop_jump_if_false L59 + jump_if_false L59 + pop 1 load_var a load_var result bin_op + load_const 10000 cmp_op < - store_var _165 L59: - load_var _165 - store_var _164 - load_var _165 - pop_jump_if_false L60 + jump_if_false L60 + pop 1 load_var a load_var result bin_op * load_const 500 cmp_op > - store_var _164 L60: - load_var _164 - store_var _163 - load_var _164 - pop_jump_if_false L61 + jump_if_false L61 + pop 1 load_var a load_var result bin_op - load_const 42 cmp_op != - store_var _163 L61: - load_var _163 pop_jump_if_false L62 jump L159 @@ -2550,72 +2172,52 @@ function user.LoopStmts(items: int[], flag: bool) -> int { load_var a load_const 100 cmp_op < - store_var _189 - load_var _189 - store_var _188 - load_var _189 - pop_jump_if_false L63 + jump_if_false L63 + pop 1 load_var result load_const 0 cmp_op > - store_var _188 L63: - load_var _188 - store_var _187 - load_var _188 - pop_jump_if_false L64 + jump_if_false L64 + pop 1 load_var flag - store_var _187 L64: - load_var _187 - store_var _186 - load_var _187 - pop_jump_if_false L65 + jump_if_false L65 + pop 1 load_var a load_var result cmp_op != - store_var _186 L65: - load_var _186 - store_var _185 - load_var _186 - pop_jump_if_false L66 + jump_if_false L66 + pop 1 load_var a load_var result bin_op + load_const 10000 cmp_op < - store_var _185 L66: - load_var _185 - store_var _184 - load_var _185 - pop_jump_if_false L67 + jump_if_false L67 + pop 1 load_var a load_var result bin_op * load_const 500 cmp_op > - store_var _184 L67: - load_var _184 - store_var _183 - load_var _184 - pop_jump_if_false L68 + jump_if_false L68 + pop 1 load_var a load_var result bin_op - load_const 42 cmp_op != - store_var _183 L68: - load_var _183 pop_jump_if_false L69 jump L158 @@ -2633,26 +2235,18 @@ function user.LoopStmts(items: int[], flag: bool) -> int { load_var extremely_long_loop_counter_variable load_const 1000000 cmp_op < - store_var _212 - load_var _212 - store_var _211 - load_var _212 - pop_jump_if_false L71 + jump_if_false L71 + pop 1 load_var flag - store_var _211 L71: - load_var _211 - store_var _210 - load_var _211 - pop_jump_if_false L72 + jump_if_false L72 + pop 1 load_var a load_const 0 cmp_op > - store_var _210 L72: - load_var _210 pop_jump_if_false L73 jump L157 @@ -2670,26 +2264,18 @@ function user.LoopStmts(items: int[], flag: bool) -> int { load_var extremely_long_loop_counter_variable load_const 1000000 cmp_op < - store_var _228 - load_var _228 - store_var _227 - load_var _228 - pop_jump_if_false L75 + jump_if_false L75 + pop 1 load_var flag - store_var _227 L75: - load_var _227 - store_var _226 - load_var _227 - pop_jump_if_false L76 + jump_if_false L76 + pop 1 load_var a load_const 0 cmp_op > - store_var _226 L76: - load_var _226 pop_jump_if_false L77 jump L156 @@ -2937,20 +2523,15 @@ function user.LoopStmts(items: int[], flag: bool) -> int { bin_op * load_const 500 cmp_op > - store_var _370 - load_var _370 - store_var _369 - load_var _370 - pop_jump_if_false L106 + jump_if_false L106 + pop 1 load_var i load_var j bin_op + load_const 100 cmp_op < - store_var _369 L106: - load_var _369 pop_jump_if_false L107 jump L108 @@ -2963,23 +2544,21 @@ function user.LoopStmts(items: int[], flag: bool) -> int { load_var i load_const 3 bin_op % - copy 0 load_const 0 cmp_op == pop_jump_if_false L109 - pop 1 jump L112 L109: - copy 0 + load_var i + load_const 3 + bin_op % load_const 1 cmp_op == pop_jump_if_false L110 - pop 1 jump L111 L110: - pop 1 load_var i load_var j bin_op * @@ -3032,46 +2611,32 @@ function user.LoopStmts(items: int[], flag: bool) -> int { load_var j load_const 1000000 cmp_op < - store_var _346 - load_var _346 - store_var _345 - load_var _346 - pop_jump_if_false L116 + jump_if_false L116 + pop 1 load_var flag - store_var _345 L116: - load_var _345 - store_var _344 - load_var _345 - pop_jump_if_false L117 + jump_if_false L117 + pop 1 load_var i load_const 0 cmp_op > - store_var _344 L117: - load_var _344 - store_var _343 - load_var _344 - pop_jump_if_false L118 + jump_if_false L118 + pop 1 load_var result load_const 0 cmp_op > - store_var _343 L118: - load_var _343 - store_var _342 - load_var _343 - pop_jump_if_false L119 + jump_if_false L119 + pop 1 load_var j load_const 42 cmp_op != - store_var _342 L119: - load_var _342 pop_jump_if_false L120 jump L121 @@ -3219,9 +2784,7 @@ function user.LoopStmts(items: int[], flag: bool) -> int { L135: load_var _297 - type_tag - load_const 0 - cmp_op == + is_type int pop_jump_if_false L136 load_var _297 load_const 100 @@ -3231,9 +2794,7 @@ function user.LoopStmts(items: int[], flag: bool) -> int { L136: load_var _297 - type_tag - load_const 0 - cmp_op == + is_type int pop_jump_if_false L137 load_var _297 store_var val @@ -3711,15 +3272,12 @@ function user.ManyNestedExprs(items: int[]) -> int { L2: load_var c - copy 0 load_const 0 cmp_op == pop_jump_if_false L3 - pop 1 jump L4 L3: - pop 1 load_var c load_const 2 bin_op * @@ -3819,49 +3377,35 @@ function user.ManyParams(first_parameter: int, second_parameter: string, third_p function user.MatchExprs(x: int, s: MatchStatus, r: MatchSuccess | MatchFailure, b: bool) -> string { load_var x - copy 0 load_const 0 cmp_op == pop_jump_if_false L0 - pop 1 L0: - pop 1 load_var x jump_table [L1, L1, L1, L1, L1], default L1 L1: 4 load_var x - copy 0 load_const 0 cmp_op == pop_jump_if_false L2 - pop 1 L2: - pop 1 load_var x - copy 0 load_const 0 cmp_op == pop_jump_if_false L3 - pop 1 L3: - pop 1 load_var x - copy 0 load_const 0 cmp_op == pop_jump_if_false L4 - pop 1 L4: - pop 1 load_var x - type_tag - load_const 0 - cmp_op == + is_type int pop_jump_if_false L5 load_var x load_const 0 @@ -3870,9 +3414,7 @@ function user.MatchExprs(x: int, s: MatchStatus, r: MatchSuccess | MatchFailure, L5: load_var x - type_tag - load_const 0 - cmp_op == + is_type int pop_jump_if_false L6 load_var x load_const 0 @@ -3881,28 +3423,23 @@ function user.MatchExprs(x: int, s: MatchStatus, r: MatchSuccess | MatchFailure, L6: load_var x - copy 0 load_const 0 cmp_op == pop_jump_if_false L7 - pop 1 L7: - copy 0 + load_var x load_const 1 cmp_op == pop_jump_if_false L8 - pop 1 L8: - copy 0 + load_var x load_const 42 cmp_op == pop_jump_if_false L9 - pop 1 L9: - pop 1 load_const "hello" load_const "hello" cmp_op == @@ -3920,392 +3457,297 @@ function user.MatchExprs(x: int, s: MatchStatus, r: MatchSuccess | MatchFailure, load_const true cmp_op == pop_jump_if_false L12 - jump L13 L12: - load_var b - load_const false + load_var x + load_const 0 cmp_op == pop_jump_if_false L13 L13: load_var x - copy 0 - load_const 0 - cmp_op == + is_type int pop_jump_if_false L14 - pop 1 L14: - pop 1 - load_var x - type_tag - load_const 0 + load_var s + discriminant + load_const MatchStatus.Active cmp_op == pop_jump_if_false L15 L15: load_var s discriminant - copy 0 - load_const MatchStatus.Active + load_const MatchStatus.Inactive cmp_op == pop_jump_if_false L16 - pop 1 L16: - copy 0 - load_const MatchStatus.Inactive - cmp_op == - pop_jump_if_false L17 - pop 1 - - L17: - pop 1 load_var x - jump_table [L18, L18, L18, L18, L18], default L18 + jump_table [L17, L17, L17, L17, L17], default L17 - L18: 4 + L17: 4 load_var x - copy 0 load_const 0 cmp_op == + pop_jump_if_false L18 + + L18: + load_var x + load_const 1 + cmp_op == pop_jump_if_false L19 - pop 1 L19: - copy 0 - load_const 1 + load_var x + load_const 2 cmp_op == pop_jump_if_false L20 - pop 1 L20: - copy 0 - load_const 2 + load_var s + discriminant + load_const MatchStatus.Active cmp_op == pop_jump_if_false L21 - pop 1 L21: - pop 1 load_var s discriminant - copy 0 - load_const MatchStatus.Active + load_const MatchStatus.Pending cmp_op == pop_jump_if_false L22 - pop 1 L22: - copy 0 - load_const MatchStatus.Pending - cmp_op == + load_var r + is_type MatchSuccess pop_jump_if_false L23 - pop 1 + jump L24 L23: - pop 1 load_var r - load_const MatchSuccess - cmp_op instanceof + is_type MatchFailure pop_jump_if_false L24 - jump L25 L24: load_var r - load_const MatchFailure - cmp_op instanceof + is_type MatchSuccess pop_jump_if_false L25 + jump L26 L25: load_var r - load_const MatchSuccess - cmp_op instanceof + is_type MatchFailure pop_jump_if_false L26 - jump L27 L26: load_var r - load_const MatchFailure - cmp_op instanceof - pop_jump_if_false L27 - - L27: - load_var r - jump_table [L28, L28, L28, L28, L28, L28], default L28 - - L28: 5 - load_var r - type_tag - load_const 0 - cmp_op == - pop_jump_if_false L29 + jump_table [L27, L27, L27, L27, L27, L27], default L27 + + L27: 5 + load_var r + is_type int + pop_jump_if_false L28 load_var r load_const 0 cmp_op > - pop_jump_if_false L29 - jump L30 + pop_jump_if_false L28 + jump L29 - L29: + L28: load_var r - type_tag - load_const 0 - cmp_op == - pop_jump_if_false L30 + is_type int + pop_jump_if_false L29 load_var r load_const 0 cmp_op < - pop_jump_if_false L30 + pop_jump_if_false L29 - L30: + L29: load_var r - type_tag - load_const 0 - cmp_op == - pop_jump_if_false L36 + is_type int + pop_jump_if_false L35 load_var r store_var n load_var n load_const 0 cmp_op > - store_var _45 - load_var _45 - store_var _44 - load_var _45 - pop_jump_if_false L31 + jump_if_false L30 + pop 1 load_var n load_const 100 cmp_op < - store_var _44 - L31: - load_var _44 - store_var _43 - load_var _44 - pop_jump_if_false L32 + L30: + jump_if_false L31 + pop 1 load_var n load_const 42 cmp_op != - store_var _43 - L32: - load_var _43 - store_var _42 - load_var _43 - pop_jump_if_false L33 + L31: + jump_if_false L32 + pop 1 load_var n load_const 13 cmp_op != - store_var _42 - L33: - load_var _42 - store_var _41 - load_var _42 - pop_jump_if_false L34 + L32: + jump_if_false L33 + pop 1 load_var n load_const 7 cmp_op != - store_var _41 - L34: - load_var _41 - store_var _40 - load_var _41 - pop_jump_if_false L35 + L33: + jump_if_false L34 + pop 1 load_var n load_const 99 cmp_op != - store_var _40 + + L34: + pop_jump_if_false L35 L35: - load_var _40 + load_var r + load_const 0 + cmp_op == pop_jump_if_false L36 L36: load_var r - copy 0 - load_const 0 + load_const 1 cmp_op == pop_jump_if_false L37 - pop 1 L37: - copy 0 - load_const 1 - cmp_op == - pop_jump_if_false L38 - pop 1 - - L38: - pop 1 load_var r - jump_table [L39, L39, L39, L39, L39, L39, L39, L39, L39, L39, L39, L39, L39, L39, L39, L39, L39, L39, L39, L39, L39, L39, L39, L39, L39], default L39 + jump_table [L38, L38, L38, L38, L38, L38, L38, L38, L38, L38, L38, L38, L38, L38, L38, L38, L38, L38, L38, L38, L38, L38, L38, L38, L38], default L38 - L39: 24 + L38: 24 load_var r discriminant - copy 0 load_const MatchStatus.Active cmp_op == - pop_jump_if_false L40 - pop 1 + pop_jump_if_false L39 - L40: - copy 0 + L39: + load_var r + discriminant load_const MatchStatus.Inactive cmp_op == - pop_jump_if_false L41 - pop 1 + pop_jump_if_false L40 - L41: - pop 1 + L40: load_var r - copy 0 load_const 0 cmp_op == - pop_jump_if_false L42 - pop 1 + pop_jump_if_false L41 - L42: - pop 1 - jump L45 - load_var b - pop_jump_if_false L43 + L41: jump L44 + load_var b + pop_jump_if_false L42 + jump L43 - L43: + L42: load_const "this is the false branch also very long" store_var result - jump L45 + jump L44 - L44: + L43: load_const "this is the true branch with a very long string" store_var result - L45: + L44: load_var r - type_tag - load_const 0 - cmp_op == - pop_jump_if_false L50 + is_type int + pop_jump_if_false L49 load_var r store_var n load_var n load_const 0 cmp_op > - store_var _60 - load_var _60 - store_var _59 - load_var _60 - pop_jump_if_false L46 + jump_if_false L45 + pop 1 load_var n load_const 100 cmp_op < - store_var _59 - L46: - load_var _59 - store_var _58 - load_var _59 - pop_jump_if_false L47 + L45: + jump_if_false L46 + pop 1 load_var n load_const 42 cmp_op != - store_var _58 - L47: - load_var _58 - store_var _57 - load_var _58 - pop_jump_if_false L48 + L46: + jump_if_false L47 + pop 1 load_var n load_const 13 cmp_op != - store_var _57 - L48: - load_var _57 - store_var _56 - load_var _57 - pop_jump_if_false L49 + L47: + jump_if_false L48 + pop 1 load_var n load_const 7 cmp_op != - store_var _56 - L49: - load_var _56 - pop_jump_if_false L50 + L48: + pop_jump_if_false L49 - L50: + L49: load_var r - type_tag - load_const 0 - cmp_op == - pop_jump_if_false L55 + is_type int + pop_jump_if_false L54 load_var r store_var n load_var n load_const 0 cmp_op > - store_var _72 - load_var _72 - store_var _71 - load_var _72 - pop_jump_if_false L51 + jump_if_false L50 + pop 1 load_var n load_const 100 cmp_op < - store_var _71 - L51: - load_var _71 - store_var _70 - load_var _71 - pop_jump_if_false L52 + L50: + jump_if_false L51 + pop 1 load_var n load_const 42 cmp_op != - store_var _70 - L52: - load_var _70 - store_var _69 - load_var _70 - pop_jump_if_false L53 + L51: + jump_if_false L52 + pop 1 load_var n load_const 13 cmp_op != - store_var _69 - L53: - load_var _69 - store_var _68 - load_var _69 - pop_jump_if_false L54 + L52: + jump_if_false L53 + pop 1 load_var n load_const 7 cmp_op != - store_var _68 - L54: - load_var _68 - pop_jump_if_false L55 + L53: + pop_jump_if_false L54 - L55: + L54: load_var r load_const 1 bin_op + - copy 0 load_const 0 cmp_op == - pop_jump_if_false L56 - pop 1 + pop_jump_if_false L55 - L56: - pop 1 + L55: load_var r load_var r bin_op * @@ -4345,184 +3787,126 @@ function user.MatchExprs(x: int, s: MatchStatus, r: MatchSuccess | MatchFailure, load_var b bin_op * bin_op + - copy 0 load_const 0 cmp_op == - pop_jump_if_false L57 - pop 1 + pop_jump_if_false L56 - L57: - pop 1 + L56: load_var r load_const 1 bin_op + - copy 0 + load_const 0 + cmp_op == + pop_jump_if_false L57 + + L57: + load_var r load_const 0 cmp_op == pop_jump_if_false L58 - pop 1 L58: - pop 1 load_var r - copy 0 load_const 0 cmp_op == pop_jump_if_false L59 - pop 1 L59: - pop 1 - load_var r - copy 0 - load_const 0 + jump L60 + load_var b + load_const true cmp_op == pop_jump_if_false L60 - pop 1 L60: - pop 1 - jump L62 - load_var b - load_const true + load_var r + load_const 0 cmp_op == pop_jump_if_false L61 - jump L62 + jump L65 L61: - load_var b - load_const false + load_var r + load_const 1 cmp_op == pop_jump_if_false L62 L62: + jump L67 load_var r - copy 0 - load_const 0 - cmp_op == + is_type MatchSuccess pop_jump_if_false L63 - pop 1 - jump L68 + jump L64 L63: - copy 0 - load_const 1 - cmp_op == - pop_jump_if_false L64 - pop 1 - - L64: - pop 1 - jump L73 load_var r - load_const MatchSuccess - cmp_op instanceof - pop_jump_if_false L65 - jump L66 - - L65: - load_var r - load_const MatchFailure - cmp_op instanceof - pop_jump_if_false L73 - jump L73 + is_type MatchFailure + pop_jump_if_false L67 + jump L67 - L66: + L64: load_var b load_const true cmp_op == pop_jump_if_false L67 - jump L73 - - L67: - load_var b - load_const false - cmp_op == - pop_jump_if_false L73 - jump L73 + jump L67 - L68: + L65: load_var b load_const true cmp_op == - pop_jump_if_false L69 - jump L70 - - L69: - load_var b - load_const false - cmp_op == - pop_jump_if_false L73 - jump L73 - - L70: + pop_jump_if_false L67 load_var r discriminant - copy 0 load_const MatchStatus.Active cmp_op == - pop_jump_if_false L71 - pop 1 + pop_jump_if_false L66 - L71: - copy 0 + L66: + load_var r + discriminant load_const MatchStatus.Inactive cmp_op == - pop_jump_if_false L72 - pop 1 - - L72: - pop 1 + pop_jump_if_false L67 - L73: + L67: load_var r - copy 0 load_const 0 cmp_op == - pop_jump_if_false L74 - pop 1 + pop_jump_if_false L68 - L74: - pop 1 - jump L77 + L68: + jump L70 load_var b - pop_jump_if_false L77 + pop_jump_if_false L70 load_var r discriminant - copy 0 load_const MatchStatus.Active cmp_op == - pop_jump_if_false L75 - pop 1 + pop_jump_if_false L69 - L75: - copy 0 + L69: + load_var r + discriminant load_const MatchStatus.Inactive cmp_op == - pop_jump_if_false L76 - pop 1 + pop_jump_if_false L70 - L76: - pop 1 - - L77: + L70: load_var r - copy 0 load_const 0 cmp_op == - pop_jump_if_false L78 - pop 1 - jump L79 + pop_jump_if_false L71 + jump L72 - L78: - pop 1 + L71: load_const "other" - jump L80 + jump L73 - L79: + L72: load_const "zero" - L80: + L73: return } @@ -4754,23 +4138,21 @@ function user.NestedLoops(matrix: int[][]) -> int { load_var val load_const 3 bin_op % - copy 0 load_const 0 cmp_op == pop_jump_if_false L6 - pop 1 jump L9 L6: - copy 0 + load_var val + load_const 3 + bin_op % load_const 1 cmp_op == pop_jump_if_false L7 - pop 1 jump L8 L7: - pop 1 load_var val store_var processed jump L10 @@ -4842,15 +4224,12 @@ function user.OtherExprs(a: int, b: int) -> int { L1: load_var a - copy 0 load_const 0 cmp_op == pop_jump_if_false L2 - pop 1 jump L3 L2: - pop 1 load_var a store_var x jump L4 diff --git a/baml_language/crates/baml_tests/snapshots/instanceof_removed/baml_tests__instanceof_removed__01_lexer__instanceof_removed.snap b/baml_language/crates/baml_tests/snapshots/instanceof_removed/baml_tests__instanceof_removed__01_lexer__instanceof_removed.snap new file mode 100644 index 0000000000..68767e6329 --- /dev/null +++ b/baml_language/crates/baml_tests/snapshots/instanceof_removed/baml_tests__instanceof_removed__01_lexer__instanceof_removed.snap @@ -0,0 +1,90 @@ +--- +source: crates/baml_tests/src/generated_tests.rs +--- +Slash "/" +Slash "/" +Word "The" +Error "`" +Instanceof "instanceof" +Error "`" +Word "operator" +Word "has" +Word "been" +Word "removed" +In "in" +Word "favor" +Word "of" +Error "`" +Match "match" +Error "`" +Dot "." +Slash "/" +Slash "/" +Word "Using" +Word "it" +Word "should" +Word "produce" +Word "error" +Word "E0098" +Word "with" +Word "a" +Word "migration" +Word "hint" +Dot "." +Class "class" +Word "Cat" +LBrace "{" +Word "name" +Word "string" +RBrace "}" +Class "class" +Word "Dog" +LBrace "{" +Word "breed" +Word "string" +RBrace "}" +Function "function" +Word "check_instanceof" +LParen "(" +Word "x" +Colon ":" +Word "Cat" +Pipe "|" +Word "Dog" +RParen ")" +Arrow "->" +Word "bool" +LBrace "{" +Word "x" +Instanceof "instanceof" +Word "Cat" +RBrace "}" +Function "function" +Word "check_instanceof_in_if" +LParen "(" +Word "x" +Colon ":" +Word "Cat" +Pipe "|" +Word "Dog" +RParen ")" +Arrow "->" +Word "string" +LBrace "{" +If "if" +LParen "(" +Word "x" +Instanceof "instanceof" +Word "Dog" +RParen ")" +LBrace "{" +Return "return" +Quote "\"" +Word "dog" +Quote "\"" +Semicolon ";" +RBrace "}" +Quote "\"" +Word "cat" +Quote "\"" +RBrace "}" diff --git a/baml_language/crates/baml_tests/snapshots/instanceof_removed/baml_tests__instanceof_removed__02_parser__instanceof_removed.snap b/baml_language/crates/baml_tests/snapshots/instanceof_removed/baml_tests__instanceof_removed__02_parser__instanceof_removed.snap new file mode 100644 index 0000000000..8d5d528249 --- /dev/null +++ b/baml_language/crates/baml_tests/snapshots/instanceof_removed/baml_tests__instanceof_removed__02_parser__instanceof_removed.snap @@ -0,0 +1,93 @@ +--- +source: crates/baml_tests/src/generated_tests.rs +--- +=== SYNTAX TREE === +SOURCE_FILE + CLASS_DEF + KW_CLASS "class" + WORD "Cat" + L_BRACE "{" + FIELD + WORD "name" + TYPE_EXPR "string" + WORD "string" + R_BRACE "}" + CLASS_DEF + KW_CLASS "class" + WORD "Dog" + L_BRACE "{" + FIELD + WORD "breed" + TYPE_EXPR "string" + WORD "string" + R_BRACE "}" + FUNCTION_DEF + KW_FUNCTION "function" + WORD "check_instanceof" + PARAMETER_LIST + L_PAREN "(" + PARAMETER + WORD "x" + COLON ":" + TYPE_EXPR "Cat | Dog" + WORD "Cat" + PIPE "|" + WORD "Dog" + R_PAREN ")" + ARROW "->" + TYPE_EXPR "bool" + WORD "bool" + EXPR_FUNCTION_BODY + BLOCK_EXPR + L_BRACE "{" + BINARY_EXPR "x instanceof Cat" + WORD "x" + KW_INSTANCEOF "instanceof" + WORD "Cat" + R_BRACE "}" + FUNCTION_DEF + KW_FUNCTION "function" + WORD "check_instanceof_in_if" + PARAMETER_LIST + L_PAREN "(" + PARAMETER + WORD "x" + COLON ":" + TYPE_EXPR "Cat | Dog" + WORD "Cat" + PIPE "|" + WORD "Dog" + R_PAREN ")" + ARROW "->" + TYPE_EXPR "string" + WORD "string" + EXPR_FUNCTION_BODY + BLOCK_EXPR + L_BRACE "{" + IF_EXPR + KW_IF "if" + PAREN_EXPR + L_PAREN "(" + BINARY_EXPR "x instanceof Dog" + WORD "x" + KW_INSTANCEOF "instanceof" + WORD "Dog" + R_PAREN ")" + BLOCK_EXPR + L_BRACE "{" + RETURN_STMT + KW_RETURN "return" + STRING_LITERAL "dog" + QUOTE """ + WORD "dog" + QUOTE """ + SEMICOLON ";" + R_BRACE "}" + STRING_LITERAL "cat" + QUOTE """ + WORD "cat" + QUOTE """ + R_BRACE "}" + +=== ERRORS === +None diff --git a/baml_language/crates/baml_tests/snapshots/instanceof_removed/baml_tests__instanceof_removed__03_hir.snap b/baml_language/crates/baml_tests/snapshots/instanceof_removed/baml_tests__instanceof_removed__03_hir.snap new file mode 100644 index 0000000000..e453a26fe0 --- /dev/null +++ b/baml_language/crates/baml_tests/snapshots/instanceof_removed/baml_tests__instanceof_removed__03_hir.snap @@ -0,0 +1,16 @@ +--- +source: crates/baml_tests/src/generated_tests.rs +--- +=== HIR2 === +class user.Cat { + name: string +} +class user.Dog { + breed: string +} +function user.check_instanceof(x: user.Cat | user.Dog) -> bool [expr] { + { } +} +function user.check_instanceof_in_if(x: user.Cat | user.Dog) -> string [expr] { + { if () { return "dog" } } "cat" +} diff --git a/baml_language/crates/baml_tests/snapshots/instanceof_removed/baml_tests__instanceof_removed__04_5_mir.snap b/baml_language/crates/baml_tests/snapshots/instanceof_removed/baml_tests__instanceof_removed__04_5_mir.snap new file mode 100644 index 0000000000..2ef1a4768d --- /dev/null +++ b/baml_language/crates/baml_tests/snapshots/instanceof_removed/baml_tests__instanceof_removed__04_5_mir.snap @@ -0,0 +1,33 @@ +--- +source: crates/baml_tests/src/generated_tests.rs +--- +=== MIR2 === +fn user.check_instanceof(x: Cat | Dog) -> bool { + // Locals: + let _0: bool // _0 // return + let _1: Cat | Dog // x // param + let _2: null + + bb0: { + _2 = call const fn baml.sys.panic(const "parse error") -> [bb1]; + } + + bb1: { + unreachable; + } +} + +fn user.check_instanceof_in_if(x: Cat | Dog) -> string { + // Locals: + let _0: string // _0 // return + let _1: Cat | Dog // x // param + let _2: null + + bb0: { + _2 = call const fn baml.sys.panic(const "parse error") -> [bb1]; + } + + bb1: { + unreachable; + } +} diff --git a/baml_language/crates/baml_tests/snapshots/instanceof_removed/baml_tests__instanceof_removed__04_tir.snap b/baml_language/crates/baml_tests/snapshots/instanceof_removed/baml_tests__instanceof_removed__04_tir.snap new file mode 100644 index 0000000000..61733ef4a9 --- /dev/null +++ b/baml_language/crates/baml_tests/snapshots/instanceof_removed/baml_tests__instanceof_removed__04_tir.snap @@ -0,0 +1,30 @@ +--- +source: crates/baml_tests/src/generated_tests.rs +--- +=== TIR2 === +class user.Cat { + name: string +} +class user.Dog { + breed: string +} +function user.check_instanceof(x: user.Cat | user.Dog) -> bool throws never { + { : unknown + : unknown + } +} +function user.check_instanceof_in_if(x: user.Cat | user.Dog) -> string throws never { + { : "cat" + if ( : unknown) : void + { : never + return "dog" : "dog" + } + "cat" : "cat" + } +} +class user.Cat$stream { + name: null | string +} +class user.Dog$stream { + breed: null | string +} diff --git a/baml_language/crates/baml_tests/snapshots/instanceof_removed/baml_tests__instanceof_removed__05_diagnostics.snap b/baml_language/crates/baml_tests/snapshots/instanceof_removed/baml_tests__instanceof_removed__05_diagnostics.snap new file mode 100644 index 0000000000..72a310504a --- /dev/null +++ b/baml_language/crates/baml_tests/snapshots/instanceof_removed/baml_tests__instanceof_removed__05_diagnostics.snap @@ -0,0 +1,23 @@ +--- +source: crates/baml_tests/src/generated_tests.rs +--- +=== COMPILER2 DIAGNOSTICS === + [hir] Error: `instanceof` is no longer supported. Use a `match` expression for type checking instead. + ╭─[ instanceof_removed.baml:13:7 ] + │ + 13 │ x instanceof Cat + │ ─────┬──── + │ ╰────── use `match` instead + │ + │ Note: Error code: E0098 +────╯ + + [hir] Error: `instanceof` is no longer supported. Use a `match` expression for type checking instead. + ╭─[ instanceof_removed.baml:17:11 ] + │ + 17 │ if (x instanceof Dog) { + │ ─────┬──── + │ ╰────── use `match` instead + │ + │ Note: Error code: E0098 +────╯ diff --git a/baml_language/crates/baml_tests/snapshots/instanceof_removed/baml_tests__instanceof_removed__06_codegen.snap b/baml_language/crates/baml_tests/snapshots/instanceof_removed/baml_tests__instanceof_removed__06_codegen.snap new file mode 100644 index 0000000000..ee4a5cbee7 --- /dev/null +++ b/baml_language/crates/baml_tests/snapshots/instanceof_removed/baml_tests__instanceof_removed__06_codegen.snap @@ -0,0 +1,22 @@ +--- +source: crates/baml_tests/src/generated_tests.rs +--- +function user.check_instanceof(x: Cat | Dog) -> bool { + load_const "parse error" + call baml.sys.panic + pop 1 + jump L0 + + L0: + unreachable +} + +function user.check_instanceof_in_if(x: Cat | Dog) -> string { + load_const "parse error" + call baml.sys.panic + pop 1 + jump L0 + + L0: + unreachable +} diff --git a/baml_language/crates/baml_tests/snapshots/instanceof_removed/baml_tests__instanceof_removed__10_formatter__instanceof_removed.snap b/baml_language/crates/baml_tests/snapshots/instanceof_removed/baml_tests__instanceof_removed__10_formatter__instanceof_removed.snap new file mode 100644 index 0000000000..d64c55fddb --- /dev/null +++ b/baml_language/crates/baml_tests/snapshots/instanceof_removed/baml_tests__instanceof_removed__10_formatter__instanceof_removed.snap @@ -0,0 +1,24 @@ +--- +source: crates/baml_tests/src/generated_tests.rs +--- +// The `instanceof` operator has been removed in favor of `match`. +// Using it should produce error E0098 with a migration hint. + +class Cat { + name: string +} + +class Dog { + breed: string +} + +function check_instanceof(x: Cat | Dog) -> bool { + x instanceof Cat +} + +function check_instanceof_in_if(x: Cat | Dog) -> string { + if (x instanceof Dog) { + return "dog"; + } + "cat" +} diff --git a/baml_language/crates/baml_tests/snapshots/literal_union_arithmetic/baml_tests__literal_union_arithmetic__06_codegen.snap b/baml_language/crates/baml_tests/snapshots/literal_union_arithmetic/baml_tests__literal_union_arithmetic__06_codegen.snap index 7ffb97d9d3..ad52459d1b 100644 --- a/baml_language/crates/baml_tests/snapshots/literal_union_arithmetic/baml_tests__literal_union_arithmetic__06_codegen.snap +++ b/baml_language/crates/baml_tests/snapshots/literal_union_arithmetic/baml_tests__literal_union_arithmetic__06_codegen.snap @@ -78,23 +78,19 @@ function user.loopMatchAccum() -> int { load_var sum store_var _5 load_var i - copy 0 load_const 0 cmp_op == pop_jump_if_false L3 - pop 1 jump L6 L3: - copy 0 + load_var i load_const 1 cmp_op == pop_jump_if_false L4 - pop 1 jump L5 L4: - pop 1 load_const 30 store_var _6 jump L7 @@ -122,23 +118,19 @@ function user.loopMatchAccum() -> int { function user.matchAdd(x: int) -> int { load_var x - copy 0 load_const 1 cmp_op == pop_jump_if_false L0 - pop 1 jump L3 L0: - copy 0 + load_var x load_const 2 cmp_op == pop_jump_if_false L1 - pop 1 jump L2 L1: - pop 1 load_const 0 jump L4 diff --git a/baml_language/crates/baml_tests/snapshots/match_exhaustiveness/baml_tests__match_exhaustiveness__04_5_mir.snap b/baml_language/crates/baml_tests/snapshots/match_exhaustiveness/baml_tests__match_exhaustiveness__04_5_mir.snap index 6c80e2362b..4ee294d4f9 100644 --- a/baml_language/crates/baml_tests/snapshots/match_exhaustiveness/baml_tests__match_exhaustiveness__04_5_mir.snap +++ b/baml_language/crates/baml_tests/snapshots/match_exhaustiveness/baml_tests__match_exhaustiveness__04_5_mir.snap @@ -7,29 +7,23 @@ fn user.BoolLiteralAfterTypedBool(b: bool) -> string { let _0: string // _0 // return let _1: bool // b // param let _2: bool - let _3: bool bb0: { _2 = is_type(copy _1, Bool { attr: TyAttr { sap_parse_without_null: Unset, sap_pending_never: Unset, sap_in_progress_never: Unset, asserts: [] } }); - branch copy _2 -> [bb3, bb1]; + branch copy _2 -> [bb2, bb1]; } bb1: { - _3 = copy _1 == const true; - branch copy _3 -> [bb2, bb4]; - } - - bb2: { _0 = const "unreachable"; - goto -> bb4; + goto -> bb3; } - bb3: { + bb2: { _0 = const "any bool"; - goto -> bb4; + goto -> bb3; } - bb4: { + bb3: { return; } } @@ -38,25 +32,13 @@ fn user.BoolUnionPattern(b: bool) -> string { // Locals: let _0: string // _0 // return let _1: bool // b // param - let _2: bool - let _3: bool bb0: { - _2 = copy _1 == const true; - branch copy _2 -> [bb2, bb1]; - } - - bb1: { - _3 = copy _1 == const false; - branch copy _3 -> [bb2, bb3]; - } - - bb2: { _0 = const "covered"; - goto -> bb3; + goto -> bb1; } - bb3: { + bb1: { return; } } @@ -112,39 +94,33 @@ fn user.DuplicateBoolLiteral(b: bool) -> string { let _1: bool // b // param let _2: bool let _3: bool - let _4: bool bb0: { _2 = copy _1 == const true; - branch copy _2 -> [bb5, bb1]; + branch copy _2 -> [bb4, bb1]; } bb1: { _3 = copy _1 == const true; - branch copy _3 -> [bb4, bb2]; + branch copy _3 -> [bb3, bb2]; } bb2: { - _4 = copy _1 == const false; - branch copy _4 -> [bb3, bb6]; - } - - bb3: { _0 = const "false"; - goto -> bb6; + goto -> bb5; } - bb4: { + bb3: { _0 = const "second true"; - goto -> bb6; + goto -> bb5; } - bb5: { + bb4: { _0 = const "first true"; - goto -> bb6; + goto -> bb5; } - bb6: { + bb5: { return; } } @@ -267,29 +243,23 @@ fn user.EnumVariantAfterTypedEnum(s: Status) -> string { let _0: string // _0 // return let _1: Status // s // param let _2: bool - let _3: bool bb0: { _2 = is_type(copy _1, Enum(TypeName { name: "Status", module_path: ["user"], display_name: "Status" }, TyAttr { sap_parse_without_null: Unset, sap_pending_never: Unset, sap_in_progress_never: Unset, asserts: [] })); - branch copy _2 -> [bb3, bb1]; + branch copy _2 -> [bb2, bb1]; } bb1: { - _3 = copy _1 == const user.Status.Active; - branch copy _3 -> [bb2, bb4]; - } - - bb2: { _0 = const "unreachable"; - goto -> bb4; + goto -> bb3; } - bb3: { + bb2: { _0 = const "any status"; - goto -> bb4; + goto -> bb3; } - bb4: { + bb3: { return; } } @@ -379,29 +349,23 @@ fn user.ExhaustiveBool(b: bool) -> string { let _0: string // _0 // return let _1: bool // b // param let _2: bool - let _3: bool bb0: { _2 = copy _1 == const true; - branch copy _2 -> [bb3, bb1]; + branch copy _2 -> [bb2, bb1]; } bb1: { - _3 = copy _1 == const false; - branch copy _3 -> [bb2, bb4]; - } - - bb2: { _0 = const "no"; - goto -> bb4; + goto -> bb3; } - bb3: { + bb2: { _0 = const "yes"; - goto -> bb4; + goto -> bb3; } - bb4: { + bb3: { return; } } @@ -590,19 +554,13 @@ fn user.ExhaustiveEnumWithTypedPattern(s: Status) -> string { // Locals: let _0: string // _0 // return let _1: Status // s // param - let _2: bool bb0: { - _2 = is_type(copy _1, Enum(TypeName { name: "Status", module_path: ["user"], display_name: "Status" }, TyAttr { sap_parse_without_null: Unset, sap_pending_never: Unset, sap_in_progress_never: Unset, asserts: [] })); - branch copy _2 -> [bb1, bb2]; - } - - bb1: { _0 = const "any status"; - goto -> bb2; + goto -> bb1; } - bb2: { + bb1: { return; } } @@ -791,29 +749,23 @@ fn user.ExhaustiveNullableEnumWithTypedPattern(s: Status?) -> string { let _0: string // _0 // return let _1: Status? // s // param let _2: bool - let _3: bool bb0: { _2 = is_type(copy _1, Enum(TypeName { name: "Status", module_path: ["user"], display_name: "Status" }, TyAttr { sap_parse_without_null: Unset, sap_pending_never: Unset, sap_in_progress_never: Unset, asserts: [] })); - branch copy _2 -> [bb3, bb1]; + branch copy _2 -> [bb2, bb1]; } bb1: { - _3 = copy _1 == const null; - branch copy _3 -> [bb2, bb4]; - } - - bb2: { _0 = const "no status"; - goto -> bb4; + goto -> bb3; } - bb3: { + bb2: { _0 = const "some status"; - goto -> bb4; + goto -> bb3; } - bb4: { + bb3: { return; } } @@ -1249,52 +1201,38 @@ fn user.JumpTableClassTypeTag(pet: Cat | Dog | Bird | Fish) -> string { // Locals: let _0: string // _0 // return let _1: Cat | Dog | Bird | Fish // pet // param - let _2: bool - let _3: bool - let _4: bool - let _5: bool + let _2: int bb0: { - _2 = is_type(copy _1, Class(TypeName { name: "Cat", module_path: ["user"], display_name: "Cat" }, TyAttr { sap_parse_without_null: Unset, sap_pending_never: Unset, sap_in_progress_never: Unset, asserts: [] })); - branch copy _2 -> [bb7, bb1]; + _2 = type_tag(_1); + switch copy _2 [Cat: bb5, Dog: bb4, Bird: bb3, Fish: bb2, otherwise: bb1] (exhaustive); } bb1: { - _3 = is_type(copy _1, Class(TypeName { name: "Dog", module_path: ["user"], display_name: "Dog" }, TyAttr { sap_parse_without_null: Unset, sap_pending_never: Unset, sap_in_progress_never: Unset, asserts: [] })); - branch copy _3 -> [bb6, bb2]; + unreachable; } bb2: { - _4 = is_type(copy _1, Class(TypeName { name: "Bird", module_path: ["user"], display_name: "Bird" }, TyAttr { sap_parse_without_null: Unset, sap_pending_never: Unset, sap_in_progress_never: Unset, asserts: [] })); - branch copy _4 -> [bb5, bb3]; - } - - bb3: { - _5 = is_type(copy _1, Class(TypeName { name: "Fish", module_path: ["user"], display_name: "Fish" }, TyAttr { sap_parse_without_null: Unset, sap_pending_never: Unset, sap_in_progress_never: Unset, asserts: [] })); - branch copy _5 -> [bb4, bb8]; - } - - bb4: { _0 = const "fish"; - goto -> bb8; + goto -> bb6; } - bb5: { + bb3: { _0 = const "bird"; - goto -> bb8; + goto -> bb6; } - bb6: { + bb4: { _0 = const "dog"; - goto -> bb8; + goto -> bb6; } - bb7: { + bb5: { _0 = const "cat"; - goto -> bb8; + goto -> bb6; } - bb8: { + bb6: { return; } } @@ -1343,52 +1281,38 @@ fn user.JumpTableTypeTag(value: int | string | bool | float) -> string { // Locals: let _0: string // _0 // return let _1: int | string | bool | float // value // param - let _2: bool - let _3: bool - let _4: bool - let _5: bool + let _2: int bb0: { - _2 = is_type(copy _1, Int { attr: TyAttr { sap_parse_without_null: Unset, sap_pending_never: Unset, sap_in_progress_never: Unset, asserts: [] } }); - branch copy _2 -> [bb7, bb1]; + _2 = type_tag(_1); + switch copy _2 [int: bb5, string: bb4, bool: bb3, float: bb2, otherwise: bb1] (exhaustive); } bb1: { - _3 = is_type(copy _1, String { attr: TyAttr { sap_parse_without_null: Unset, sap_pending_never: Unset, sap_in_progress_never: Unset, asserts: [] } }); - branch copy _3 -> [bb6, bb2]; + unreachable; } bb2: { - _4 = is_type(copy _1, Bool { attr: TyAttr { sap_parse_without_null: Unset, sap_pending_never: Unset, sap_in_progress_never: Unset, asserts: [] } }); - branch copy _4 -> [bb5, bb3]; - } - - bb3: { - _5 = is_type(copy _1, Float { attr: TyAttr { sap_parse_without_null: Unset, sap_pending_never: Unset, sap_in_progress_never: Unset, asserts: [] } }); - branch copy _5 -> [bb4, bb8]; - } - - bb4: { _0 = const "float"; - goto -> bb8; + goto -> bb6; } - bb5: { + bb3: { _0 = const "bool"; - goto -> bb8; + goto -> bb6; } - bb6: { + bb4: { _0 = const "string"; - goto -> bb8; + goto -> bb6; } - bb7: { + bb5: { _0 = const "int"; - goto -> bb8; + goto -> bb6; } - bb8: { + bb6: { return; } } @@ -1768,7 +1692,6 @@ fn user.NestedMatchSpans(x: int, y: bool) -> string { let _2: bool // y // param let _3: bool let _4: bool - let _5: bool bb0: { switch copy _1 [0: bb2, otherwise: bb1]; @@ -1776,40 +1699,35 @@ fn user.NestedMatchSpans(x: int, y: bool) -> string { bb1: { _0 = const "other"; - goto -> bb8; + goto -> bb7; } bb2: { _3 = copy _2 == const true; - branch copy _3 -> [bb7, bb3]; + branch copy _3 -> [bb6, bb3]; } bb3: { _4 = copy _2 == const false; - branch copy _4 -> [bb6, bb4]; + branch copy _4 -> [bb5, bb4]; } bb4: { - _5 = copy _2 == const true; - branch copy _5 -> [bb5, bb8]; - } - - bb5: { _0 = const "unreachable nested"; - goto -> bb8; + goto -> bb7; } - bb6: { + bb5: { _0 = const "zero false"; - goto -> bb8; + goto -> bb7; } - bb7: { + bb6: { _0 = const "zero true"; - goto -> bb8; + goto -> bb7; } - bb8: { + bb7: { return; } } @@ -2515,29 +2433,23 @@ fn user.ParenthesizedSingleLiteral(b: bool) -> string { let _0: string // _0 // return let _1: bool // b // param let _2: bool - let _3: bool bb0: { _2 = copy _1 == const true; - branch copy _2 -> [bb3, bb1]; + branch copy _2 -> [bb2, bb1]; } bb1: { - _3 = copy _1 == const false; - branch copy _3 -> [bb2, bb4]; - } - - bb2: { _0 = const "no"; - goto -> bb4; + goto -> bb3; } - bb3: { + bb2: { _0 = const "yes"; - goto -> bb4; + goto -> bb3; } - bb4: { + bb3: { return; } } diff --git a/baml_language/crates/baml_tests/snapshots/match_exhaustiveness/baml_tests__match_exhaustiveness__06_codegen.snap b/baml_language/crates/baml_tests/snapshots/match_exhaustiveness/baml_tests__match_exhaustiveness__06_codegen.snap index 6126296ded..d80110a939 100644 --- a/baml_language/crates/baml_tests/snapshots/match_exhaustiveness/baml_tests__match_exhaustiveness__06_codegen.snap +++ b/baml_language/crates/baml_tests/snapshots/match_exhaustiveness/baml_tests__match_exhaustiveness__06_codegen.snap @@ -3,17 +3,11 @@ source: crates/baml_tests/src/generated_tests.rs --- function user.BoolLiteralAfterTypedBool(b: bool) -> string { load_var b - type_tag - load_const 2 - cmp_op == + is_type bool pop_jump_if_false L0 jump L1 L0: - load_var b - load_const true - cmp_op == - pop_jump_if_false L2 load_const "unreachable" jump L2 @@ -25,19 +19,6 @@ function user.BoolLiteralAfterTypedBool(b: bool) -> string { } function user.BoolUnionPattern(b: bool) -> string { - load_var b - load_const true - cmp_op == - pop_jump_if_false L0 - jump L1 - - L0: - load_var b - load_const false - cmp_op == - pop_jump_if_false L1 - - L1: load_const "covered" return } @@ -62,28 +43,23 @@ function user.BoolWithCatchAll(b: bool) -> string { function user.DeeplyNestedLiterals(x: 200 | 201 | 204) -> string { load_var x - copy 0 load_const 200 cmp_op == pop_jump_if_false L0 - pop 1 L0: - copy 0 + load_var x load_const 201 cmp_op == pop_jump_if_false L1 - pop 1 L1: - copy 0 + load_var x load_const 204 cmp_op == pop_jump_if_false L2 - pop 1 L2: - pop 1 load_const "all success codes" return } @@ -103,10 +79,6 @@ function user.DuplicateBoolLiteral(b: bool) -> string { jump L2 L1: - load_var b - load_const false - cmp_op == - pop_jump_if_false L4 load_const "false" jump L4 @@ -124,23 +96,20 @@ function user.DuplicateBoolLiteral(b: bool) -> string { function user.DuplicateEnumVariant(s: Status) -> string { load_var s discriminant - copy 0 load_const Status.Active cmp_op == pop_jump_if_false L0 - pop 1 jump L3 L0: - copy 0 + load_var s + discriminant load_const Status.Inactive cmp_op == pop_jump_if_false L1 - pop 1 jump L2 L1: - pop 1 load_const "pending" jump L4 @@ -157,15 +126,12 @@ function user.DuplicateEnumVariant(s: Status) -> string { function user.DuplicateIntLiteral(x: int) -> string { load_var x - copy 0 load_const 1 cmp_op == pop_jump_if_false L0 - pop 1 jump L1 L0: - pop 1 load_const "other" jump L2 @@ -222,18 +188,11 @@ function user.DuplicateStringLiteral(role: "user" | "assistant" | "system") -> s function user.EnumVariantAfterTypedEnum(s: Status) -> string { load_var s - type_tag - load_const 5 - cmp_op == + is_type Status pop_jump_if_false L0 jump L1 L0: - load_var s - load_const user.Status.Active - alloc_variant user.Status - cmp_op == - pop_jump_if_false L2 load_const "unreachable" jump L2 @@ -247,23 +206,20 @@ function user.EnumVariantAfterTypedEnum(s: Status) -> string { function user.EnumVariantUnion(s: Status) -> string { load_var s discriminant - copy 0 load_const Status.Active cmp_op == pop_jump_if_false L0 - pop 1 jump L2 L0: - copy 0 + load_var s + discriminant load_const Status.Pending cmp_op == pop_jump_if_false L1 - pop 1 jump L2 L1: - pop 1 load_const "inactive" jump L3 @@ -277,23 +233,20 @@ function user.EnumVariantUnion(s: Status) -> string { function user.EnumVariantUnionWithCatchAll(s: Status) -> string { load_var s discriminant - copy 0 load_const Status.Active cmp_op == pop_jump_if_false L0 - pop 1 jump L2 L0: - copy 0 + load_var s + discriminant load_const Status.Pending cmp_op == pop_jump_if_false L1 - pop 1 jump L2 L1: - pop 1 load_const "other" jump L3 @@ -306,22 +259,18 @@ function user.EnumVariantUnionWithCatchAll(s: Status) -> string { function user.ExhaustiveAliasOfLiterals(code: 200 | 201) -> string { load_var code - copy 0 load_const 200 cmp_op == pop_jump_if_false L0 - pop 1 jump L2 L0: - copy 0 + load_var code load_const 201 cmp_op == pop_jump_if_false L1 - pop 1 L1: - pop 1 jump L3 load_const "created" jump L3 @@ -341,10 +290,6 @@ function user.ExhaustiveBool(b: bool) -> string { jump L1 L0: - load_var b - load_const false - cmp_op == - pop_jump_if_false L2 load_const "no" jump L2 @@ -357,8 +302,7 @@ function user.ExhaustiveBool(b: bool) -> string { function user.ExhaustiveClassPlusLiteral(x: Success | 200) -> string { load_var x - load_const Success - cmp_op instanceof + is_type Success pop_jump_if_false L0 jump L1 @@ -402,15 +346,13 @@ function user.ExhaustiveCustomBool(b: true | false) -> string { function user.ExhaustiveDoubleMaybe(dm: (Success | Failure)??) -> string { load_var dm - load_const Success - cmp_op instanceof + is_type Success pop_jump_if_false L0 jump L3 L0: load_var dm - load_const Failure - cmp_op instanceof + is_type Failure pop_jump_if_false L1 jump L2 @@ -438,23 +380,20 @@ function user.ExhaustiveDoubleMaybe(dm: (Success | Failure)??) -> string { function user.ExhaustiveEnumAllVariants(s: Status) -> string { load_var s discriminant - copy 0 load_const Status.Active cmp_op == pop_jump_if_false L0 - pop 1 jump L3 L0: - copy 0 + load_var s + discriminant load_const Status.Inactive cmp_op == pop_jump_if_false L1 - pop 1 jump L2 L1: - pop 1 load_const "pending" jump L4 @@ -472,15 +411,12 @@ function user.ExhaustiveEnumAllVariants(s: Status) -> string { function user.ExhaustiveEnumWithCatchAll(s: Status) -> string { load_var s discriminant - copy 0 load_const Status.Active cmp_op == pop_jump_if_false L0 - pop 1 jump L1 L0: - pop 1 load_const "other" jump L2 @@ -492,13 +428,6 @@ function user.ExhaustiveEnumWithCatchAll(s: Status) -> string { } function user.ExhaustiveEnumWithTypedPattern(s: Status) -> string { - load_var s - type_tag - load_const 5 - cmp_op == - pop_jump_if_false L0 - - L0: load_const "any status" return } @@ -516,30 +445,25 @@ function user.ExhaustiveFalseType(f: false) -> string { function user.ExhaustiveIntLiterals(code: 200 | 201 | 204) -> string { load_var code - copy 0 load_const 200 cmp_op == pop_jump_if_false L0 - pop 1 jump L4 L0: - copy 0 + load_var code load_const 201 cmp_op == pop_jump_if_false L1 - pop 1 jump L3 L1: - copy 0 + load_var code load_const 204 cmp_op == pop_jump_if_false L2 - pop 1 L2: - pop 1 jump L5 load_const "No Content" jump L5 @@ -564,9 +488,7 @@ function user.ExhaustiveLiteralWithBaseType(x: int) -> string { L0: load_var x - type_tag - load_const 0 - cmp_op == + is_type int pop_jump_if_false L2 load_const "other" jump L2 @@ -660,17 +582,11 @@ function user.ExhaustiveNullableEnum(s: Status?) -> string { function user.ExhaustiveNullableEnumWithTypedPattern(s: Status?) -> string { load_var s - type_tag - load_const 5 - cmp_op == + is_type Status pop_jump_if_false L0 jump L1 L0: - load_var s - load_const null - cmp_op == - pop_jump_if_false L2 load_const "no status" jump L2 @@ -683,9 +599,7 @@ function user.ExhaustiveNullableEnumWithTypedPattern(s: Status?) -> string { function user.ExhaustiveOptional(x: int?) -> string { load_var x - type_tag - load_const 0 - cmp_op == + is_type int pop_jump_if_false L0 jump L1 @@ -728,14 +642,11 @@ function user.ExhaustiveOptionalSingleton(code: 200?) -> string { function user.ExhaustiveSingletonInt(code: 200) -> string { load_var code - copy 0 load_const 200 cmp_op == pop_jump_if_false L0 - pop 1 L0: - pop 1 load_const "OK" return } @@ -786,15 +697,13 @@ function user.ExhaustiveTrueType(t: true) -> string { function user.ExhaustiveTypeAlias(r: Success | Failure) -> string { load_var r - load_const Success - cmp_op instanceof + is_type Success pop_jump_if_false L0 jump L1 L0: load_var r - load_const Failure - cmp_op instanceof + is_type Failure pop_jump_if_false L2 load_var r load_field .reason @@ -810,8 +719,7 @@ function user.ExhaustiveTypeAlias(r: Success | Failure) -> string { function user.ExhaustiveTypeAliasWithCatchAll(r: Success | Failure) -> string { load_var r - load_const Success - cmp_op instanceof + is_type Success pop_jump_if_false L0 jump L1 @@ -829,15 +737,12 @@ function user.ExhaustiveTypeAliasWithCatchAll(r: Success | Failure) -> string { function user.ExhaustiveWithNamedCatchAll(x: int) -> int { load_var x - copy 0 load_const 1 cmp_op == pop_jump_if_false L0 - pop 1 jump L1 L0: - pop 1 load_var x load_const 1 bin_op + @@ -852,23 +757,19 @@ function user.ExhaustiveWithNamedCatchAll(x: int) -> int { function user.ExhaustiveWithUnderscore(x: int) -> int { load_var x - copy 0 load_const 1 cmp_op == pop_jump_if_false L0 - pop 1 jump L3 L0: - copy 0 + load_var x load_const 2 cmp_op == pop_jump_if_false L1 - pop 1 jump L2 L1: - pop 1 load_const 0 jump L4 @@ -891,9 +792,7 @@ function user.GetStatus() -> Status { function user.GuardedOnlyNonExhaustive(x: int) -> int { load_var x - type_tag - load_const 0 - cmp_op == + is_type int pop_jump_if_false L0 load_var x load_const 0 @@ -903,9 +802,7 @@ function user.GuardedOnlyNonExhaustive(x: int) -> int { L0: load_var x - type_tag - load_const 0 - cmp_op == + is_type int pop_jump_if_false L2 load_var x load_const 0 @@ -927,9 +824,7 @@ function user.GuardedOnlyNonExhaustive(x: int) -> int { function user.GuardedWithFallback(x: int) -> int { load_var x - type_tag - load_const 0 - cmp_op == + is_type int pop_jump_if_false L0 load_var x load_const 0 @@ -953,44 +848,36 @@ function user.GuardedWithFallback(x: int) -> int { function user.HighLineNumberNonExhaustive(s: Status) -> string { load_var s discriminant - copy 0 load_const Status.Active cmp_op == pop_jump_if_false L0 - pop 1 L0: - pop 1 load_const "active only" return } function user.IntLiteralUnionPattern(code: 200 | 201 | 204) -> string { load_var code - copy 0 load_const 200 cmp_op == pop_jump_if_false L0 - pop 1 jump L3 L0: - copy 0 + load_var code load_const 201 cmp_op == pop_jump_if_false L1 - pop 1 jump L3 L1: - copy 0 + load_var code load_const 204 cmp_op == pop_jump_if_false L2 - pop 1 L2: - pop 1 jump L4 load_const "no content" jump L4 @@ -1004,46 +891,30 @@ function user.IntLiteralUnionPattern(code: 200 | 201 | 204) -> string { function user.JumpTableClassTypeTag(pet: Cat | Dog | Bird | Fish) -> string { load_var pet - load_const Cat - cmp_op instanceof - pop_jump_if_false L0 - jump L5 - - L0: - load_var pet - load_const Dog - cmp_op instanceof - pop_jump_if_false L1 - jump L4 - - L1: - load_var pet - load_const Bird - cmp_op instanceof - pop_jump_if_false L2 - jump L3 + type_tag + dense_tag [Cat, Dog, Bird, Fish] + jump_table [L3, L2, L1, L0], default L5 - L2: - load_var pet - load_const Fish - cmp_op instanceof - pop_jump_if_false L6 + L0: Fish load_const "fish" - jump L6 + jump L4 - L3: + L1: Bird load_const "bird" - jump L6 + jump L4 - L4: + L2: Dog load_const "dog" - jump L6 + jump L4 - L5: + L3: Cat load_const "cat" - L6: + L4: return + + L5: + unreachable } function user.JumpTableEnumDiscriminant(state: DispatchState) -> int { @@ -1076,56 +947,33 @@ function user.JumpTableEnumDiscriminant(state: DispatchState) -> int { function user.JumpTableTypeTag(value: int | string | bool | float) -> string { load_var value type_tag - load_const 0 - cmp_op == - pop_jump_if_false L0 - jump L5 - - L0: - load_var value - type_tag - load_const 1 - cmp_op == - pop_jump_if_false L1 - jump L4 - - L1: - load_var value - type_tag - load_const 2 - cmp_op == - pop_jump_if_false L2 - jump L3 + jump_table [L3, L2, L1, _, L0], default L5 - L2: - load_var value - type_tag - load_const 4 - cmp_op == - pop_jump_if_false L6 + L0: float load_const "float" - jump L6 + jump L4 - L3: + L1: bool load_const "bool" - jump L6 + jump L4 - L4: + L2: string load_const "string" - jump L6 + jump L4 - L5: + L3: int load_const "int" - L6: + L4: return + + L5: + unreachable } function user.LiteralAfterTypedPattern(x: int) -> string { load_var x - type_tag - load_const 0 - cmp_op == + is_type int pop_jump_if_false L0 jump L1 @@ -1146,54 +994,21 @@ function user.LiteralAfterTypedPattern(x: int) -> string { function user.LiteralUnionWithCatchAll(code: int) -> string { load_var code - copy 0 - load_const 400 - cmp_op == - pop_jump_if_false L0 - pop 1 - jump L4 + dense_tag [200, 201, 400, 404] + jump_table [L2, L2, L1, L1], default L0 L0: - copy 0 - load_const 400 - cmp_op < - pop_jump_if_false L2 - copy 0 - load_const 200 - cmp_op == - pop_jump_if_false L1 - pop 1 - jump L5 - - L1: - copy 0 - load_const 201 - cmp_op == - pop_jump_if_false L2 - pop 1 - jump L5 - - L2: - copy 0 - load_const 404 - cmp_op == - pop_jump_if_false L3 - pop 1 - jump L4 - - L3: - pop 1 load_const "other" - jump L6 + jump L3 - L4: + L1: 404 load_const "client error" - jump L6 + jump L3 - L5: + L2: 201 load_const "success" - L6: + L3: return } @@ -1201,23 +1016,21 @@ function user.MatchArithmeticScrutinee(a: int, b: int) -> string { load_var a load_var b bin_op + - copy 0 load_const 0 cmp_op == pop_jump_if_false L0 - pop 1 jump L3 L0: - copy 0 + load_var a + load_var b + bin_op + load_const 1 cmp_op == pop_jump_if_false L1 - pop 1 jump L2 L1: - pop 1 load_const "other" jump L4 @@ -1238,23 +1051,23 @@ function user.MatchComplexScrutinee(x: int) -> string { bin_op * load_const 1 bin_op + - copy 0 load_const 1 cmp_op == pop_jump_if_false L0 - pop 1 jump L3 L0: - copy 0 + load_var x + load_const 2 + bin_op * + load_const 1 + bin_op + load_const 3 cmp_op == pop_jump_if_false L1 - pop 1 jump L2 L1: - pop 1 load_const "other" jump L4 @@ -1272,23 +1085,19 @@ function user.MatchComplexScrutinee(x: int) -> string { function user.MatchFunctionCallScrutinee() -> string { call user.GetStatus discriminant - copy 0 load_const Status.Active cmp_op == pop_jump_if_false L0 - pop 1 jump L3 L0: - copy 0 + discriminant load_const Status.Inactive cmp_op == pop_jump_if_false L1 - pop 1 jump L2 L1: - pop 1 load_const "pending" jump L4 @@ -1319,9 +1128,7 @@ function user.MixedPatterns(x: int) -> string { L1: load_var x - type_tag - load_const 0 - cmp_op == + is_type int pop_jump_if_false L2 load_var x load_const 0 @@ -1351,23 +1158,20 @@ function user.MixedPatterns(x: int) -> string { function user.MultipleUnreachableSpans(s: Status) -> string { load_var s discriminant - copy 0 load_const Status.Active cmp_op == pop_jump_if_false L0 - pop 1 jump L3 L0: - copy 0 + load_var s + discriminant load_const Status.Inactive cmp_op == pop_jump_if_false L1 - pop 1 jump L2 L1: - pop 1 load_const "pending" jump L4 @@ -1385,23 +1189,20 @@ function user.MultipleUnreachableSpans(s: Status) -> string { function user.NestedEnumMatch(s1: Status, s2: Status) -> string { load_var s1 discriminant - copy 0 load_const Status.Active cmp_op == pop_jump_if_false L0 - pop 1 jump L3 L0: - copy 0 + load_var s1 + discriminant load_const Status.Inactive cmp_op == pop_jump_if_false L1 - pop 1 jump L2 L1: - pop 1 load_const "first pending" jump L8 @@ -1412,23 +1213,20 @@ function user.NestedEnumMatch(s1: Status, s2: Status) -> string { L3: load_var s2 discriminant - copy 0 load_const Status.Active cmp_op == pop_jump_if_false L4 - pop 1 jump L7 L4: - copy 0 + load_var s2 + discriminant load_const Status.Inactive cmp_op == pop_jump_if_false L5 - pop 1 jump L6 L5: - pop 1 load_const "first active, second pending" jump L8 @@ -1446,137 +1244,50 @@ function user.NestedEnumMatch(s1: Status, s2: Status) -> string { function user.NestedEnumVariantUnions(s: Status) -> string { load_var s discriminant - copy 0 load_const Status.Active cmp_op == pop_jump_if_false L0 - pop 1 L0: - copy 0 + load_var s + discriminant load_const Status.Pending cmp_op == pop_jump_if_false L1 - pop 1 L1: - pop 1 load_const "all statuses" return } function user.NestedLiteralUnions(code: int) -> string { load_var code - copy 0 - load_const 400 - cmp_op == - pop_jump_if_false L0 - pop 1 - jump L9 + dense_tag [200, 201, 202, 204, 400, 404, 405, 406, 409] + jump_table [L2, L2, L2, L2, L1, L1, L1, L1, L1], default L0 L0: - copy 0 - load_const 400 - cmp_op < - pop_jump_if_false L4 - copy 0 - load_const 202 - cmp_op == - pop_jump_if_false L1 - pop 1 - jump L10 - - L1: - copy 0 - load_const 202 - cmp_op < - pop_jump_if_false L3 - copy 0 - load_const 200 - cmp_op == - pop_jump_if_false L2 - pop 1 - jump L10 - - L2: - copy 0 - load_const 201 - cmp_op == - pop_jump_if_false L3 - pop 1 - jump L10 - - L3: - copy 0 - load_const 204 - cmp_op == - pop_jump_if_false L4 - pop 1 - jump L10 - - L4: - copy 0 - load_const 406 - cmp_op == - pop_jump_if_false L5 - pop 1 - jump L9 - - L5: - copy 0 - load_const 406 - cmp_op < - pop_jump_if_false L7 - copy 0 - load_const 404 - cmp_op == - pop_jump_if_false L6 - pop 1 - jump L9 - - L6: - copy 0 - load_const 405 - cmp_op == - pop_jump_if_false L7 - pop 1 - jump L9 - - L7: - copy 0 - load_const 409 - cmp_op == - pop_jump_if_false L8 - pop 1 - jump L9 - - L8: - pop 1 load_const "other" - jump L11 + jump L3 - L9: + L1: 409 load_const "client error" - jump L11 + jump L3 - L10: + L2: 204 load_const "success" - L11: + L3: return } function user.NestedMatchSpans(x: int, y: bool) -> string { load_var x - copy 0 load_const 0 cmp_op == pop_jump_if_false L0 - pop 1 jump L1 L0: - pop 1 load_const "other" jump L6 @@ -1595,10 +1306,6 @@ function user.NestedMatchSpans(x: int, y: bool) -> string { jump L4 L3: - load_var y - load_const true - cmp_op == - pop_jump_if_false L6 load_const "unreachable nested" jump L6 @@ -1615,15 +1322,13 @@ function user.NestedMatchSpans(x: int, y: bool) -> string { function user.NestedTypeAlias(mr: (Success | Failure)?) -> string { load_var mr - load_const Success - cmp_op instanceof + is_type Success pop_jump_if_false L0 jump L3 L0: load_var mr - load_const Failure - cmp_op instanceof + is_type Failure pop_jump_if_false L1 jump L2 @@ -1650,22 +1355,19 @@ function user.NestedTypeAlias(mr: (Success | Failure)?) -> string { function user.NestedTypeBindings(r: Success | Failure) -> int { load_var r - load_const Success - cmp_op instanceof + is_type Success pop_jump_if_false L0 jump L2 L0: load_var r - load_const Failure - cmp_op instanceof + is_type Failure pop_jump_if_false L1 jump L2 L1: load_var r - load_const Failure - cmp_op instanceof + is_type Failure pop_jump_if_false L3 load_const 1 jump L3 @@ -1679,14 +1381,11 @@ function user.NestedTypeBindings(r: Success | Failure) -> int { function user.NonExhaustiveAliasOfLiterals(code: 200 | 201) -> string { load_var code - copy 0 load_const 200 cmp_op == pop_jump_if_false L0 - pop 1 L0: - pop 1 load_const "ok" return } @@ -1715,22 +1414,18 @@ function user.NonExhaustiveBoolMissingTrue(b: bool) -> string { function user.NonExhaustiveClassPlusLiteralMissingClass(x: Success | 200) -> string { load_var x - copy 0 load_const 200 cmp_op == pop_jump_if_false L0 - pop 1 L0: - pop 1 load_const "http ok" return } function user.NonExhaustiveClassPlusLiteralMissingLiteral(x: Success | 200) -> string { load_var x - load_const Success - cmp_op instanceof + is_type Success pop_jump_if_false L0 load_var x load_field .data @@ -1752,15 +1447,13 @@ function user.NonExhaustiveCustomBoolMissing(b: true | false) -> string { function user.NonExhaustiveDoubleMaybeMissingNull(dm: (Success | Failure)??) -> string { load_var dm - load_const Success - cmp_op instanceof + is_type Success pop_jump_if_false L0 jump L1 L0: load_var dm - load_const Failure - cmp_op instanceof + is_type Failure pop_jump_if_false L2 load_var dm load_field .reason @@ -1777,14 +1470,11 @@ function user.NonExhaustiveDoubleMaybeMissingNull(dm: (Success | Failure)??) -> function user.NonExhaustiveEnumMissingMultiple(s: Status) -> string { load_var s discriminant - copy 0 load_const Status.Active cmp_op == pop_jump_if_false L0 - pop 1 L0: - pop 1 load_const "active" return } @@ -1792,22 +1482,19 @@ function user.NonExhaustiveEnumMissingMultiple(s: Status) -> string { function user.NonExhaustiveEnumMissingOne(s: Status) -> string { load_var s discriminant - copy 0 load_const Status.Active cmp_op == pop_jump_if_false L0 - pop 1 jump L2 L0: - copy 0 + load_var s + discriminant load_const Status.Inactive cmp_op == pop_jump_if_false L1 - pop 1 L1: - pop 1 jump L3 load_const "inactive" jump L3 @@ -1821,15 +1508,13 @@ function user.NonExhaustiveEnumMissingOne(s: Status) -> string { function user.NonExhaustiveFullResultMissingOne(r: Success | Failure | Pending) -> string { load_var r - load_const Success - cmp_op instanceof + is_type Success pop_jump_if_false L0 jump L1 L0: load_var r - load_const Failure - cmp_op instanceof + is_type Failure pop_jump_if_false L2 load_var r load_field .reason @@ -1845,22 +1530,18 @@ function user.NonExhaustiveFullResultMissingOne(r: Success | Failure | Pending) function user.NonExhaustiveIntLiteral(code: 200 | 201 | 204) -> string { load_var code - copy 0 load_const 200 cmp_op == pop_jump_if_false L0 - pop 1 jump L2 L0: - copy 0 + load_var code load_const 201 cmp_op == pop_jump_if_false L1 - pop 1 L1: - pop 1 jump L3 load_const "Created" jump L3 @@ -1897,30 +1578,27 @@ function user.NonExhaustiveMixedLiterals(x: 200 | "success" | true) -> string { function user.NonExhaustiveNullableEnumMissingNull(s: Status?) -> string { load_var s discriminant - copy 0 load_const Status.Active cmp_op == pop_jump_if_false L0 - pop 1 jump L4 L0: - copy 0 + load_var s + discriminant load_const Status.Inactive cmp_op == pop_jump_if_false L1 - pop 1 jump L3 L1: - copy 0 + load_var s + discriminant load_const Status.Pending cmp_op == pop_jump_if_false L2 - pop 1 L2: - pop 1 jump L5 load_const "pending" jump L5 @@ -1973,14 +1651,11 @@ function user.NonExhaustiveNullableEnumMissingVariant(s: Status?) -> string { function user.NonExhaustiveOptionalMissingNull(code: 200?) -> string { load_var code - copy 0 load_const 200 cmp_op == pop_jump_if_false L0 - pop 1 L0: - pop 1 load_const "OK" return } @@ -2031,8 +1706,7 @@ function user.NonExhaustiveTrueTypeWrongLiteral(t: true) -> string { function user.NonExhaustiveTypeAliasMissingOne(r: Success | Failure) -> string { load_var r - load_const Success - cmp_op instanceof + is_type Success pop_jump_if_false L0 load_var r load_field .data @@ -2061,22 +1735,19 @@ function user.OptionalWithCatchAll(x: int?) -> string { function user.OverlappingTypedPatterns(r: Success | Failure) -> string { load_var r - load_const Success - cmp_op instanceof + is_type Success pop_jump_if_false L0 jump L3 L0: load_var r - load_const Success - cmp_op instanceof + is_type Success pop_jump_if_false L1 jump L2 L1: load_var r - load_const Failure - cmp_op instanceof + is_type Failure pop_jump_if_false L4 L2: @@ -2092,22 +1763,19 @@ function user.OverlappingTypedPatterns(r: Success | Failure) -> string { function user.OverlappingWithBindings(r: Success | Failure) -> string { load_var r - load_const Success - cmp_op instanceof + is_type Success pop_jump_if_false L0 jump L3 L0: load_var r - load_const Success - cmp_op instanceof + is_type Success pop_jump_if_false L1 jump L2 L1: load_var r - load_const Failure - cmp_op instanceof + is_type Failure pop_jump_if_false L4 L2: @@ -2130,10 +1798,6 @@ function user.ParenthesizedSingleLiteral(b: bool) -> string { jump L1 L0: - load_var b - load_const false - cmp_op == - pop_jump_if_false L2 load_const "no" jump L2 @@ -2146,15 +1810,13 @@ function user.ParenthesizedSingleLiteral(b: bool) -> string { function user.PartialUnionPatternCoverage(r: Success | Failure | Pending) -> string { load_var r - load_const Success - cmp_op instanceof + is_type Success pop_jump_if_false L0 jump L1 L0: load_var r - load_const Failure - cmp_op instanceof + is_type Failure pop_jump_if_false L1 L1: @@ -2164,22 +1826,19 @@ function user.PartialUnionPatternCoverage(r: Success | Failure | Pending) -> str function user.PartialUnionPlusCatchAll(r: Success | Failure | Pending) -> string { load_var r - load_const Success - cmp_op instanceof + is_type Success pop_jump_if_false L0 jump L2 L0: load_var r - load_const Failure - cmp_op instanceof + is_type Failure pop_jump_if_false L1 jump L2 L1: load_var r - load_const Pending - cmp_op instanceof + is_type Pending pop_jump_if_false L3 load_const "pending" jump L3 @@ -2193,9 +1852,7 @@ function user.PartialUnionPlusCatchAll(r: Success | Failure | Pending) -> string function user.StringLiteralAfterStringType(s: string) -> string { load_var s - type_tag - load_const 1 - cmp_op == + is_type string pop_jump_if_false L0 jump L1 @@ -2274,43 +1931,34 @@ function user.StringLiteralWithCatchAll(role: "user" | "assistant" | "system") - function user.ThreeLevelNestedMatch(x: int, y: int, z: int) -> string { load_var x - copy 0 load_const 0 cmp_op == pop_jump_if_false L0 - pop 1 jump L1 L0: - pop 1 load_const "x nonzero" jump L6 L1: load_var y - copy 0 load_const 0 cmp_op == pop_jump_if_false L2 - pop 1 jump L3 L2: - pop 1 load_const "y nonzero" jump L6 L3: load_var z - copy 0 load_const 0 cmp_op == pop_jump_if_false L4 - pop 1 jump L5 L4: - pop 1 load_const "z nonzero" jump L6 @@ -2323,15 +1971,13 @@ function user.ThreeLevelNestedMatch(x: int, y: int, z: int) -> string { function user.UnionPatternCoversAll(r: Success | Failure) -> string { load_var r - load_const Success - cmp_op instanceof + is_type Success pop_jump_if_false L0 jump L1 L0: load_var r - load_const Failure - cmp_op instanceof + is_type Failure pop_jump_if_false L1 L1: @@ -2351,14 +1997,11 @@ function user.UnreachableMultiple(x: int) -> string { function user.WrongLiteralTypeBoolWithInt(b: bool) -> string { load_var b - copy 0 load_const 42 cmp_op == pop_jump_if_false L0 - pop 1 L0: - pop 1 load_const "wrong type" return } diff --git a/baml_language/crates/baml_tests/snapshots/namespaces_type_resolution/baml_tests__namespaces_type_resolution__04_5_mir.snap b/baml_language/crates/baml_tests/snapshots/namespaces_type_resolution/baml_tests__namespaces_type_resolution__04_5_mir.snap index a65463f203..85903f7cc6 100644 --- a/baml_language/crates/baml_tests/snapshots/namespaces_type_resolution/baml_tests__namespaces_type_resolution__04_5_mir.snap +++ b/baml_language/crates/baml_tests/snapshots/namespaces_type_resolution/baml_tests__namespaces_type_resolution__04_5_mir.snap @@ -38,39 +38,33 @@ fn user.RootEnumMatch(s: Status) -> string { let _1: Status // s // param let _2: bool let _3: bool - let _4: bool bb0: { _2 = copy _1 == const user.Status.Ok; - branch copy _2 -> [bb5, bb1]; + branch copy _2 -> [bb4, bb1]; } bb1: { _3 = copy _1 == const user.Status.Failed; - branch copy _3 -> [bb4, bb2]; + branch copy _3 -> [bb3, bb2]; } bb2: { - _4 = copy _1 == const user.Status.Pending; - branch copy _4 -> [bb3, bb6]; - } - - bb3: { _0 = const "pending"; - goto -> bb6; + goto -> bb5; } - bb4: { + bb3: { _0 = const "failed"; - goto -> bb6; + goto -> bb5; } - bb5: { + bb4: { _0 = const "ok"; - goto -> bb6; + goto -> bb5; } - bb6: { + bb5: { return; } } @@ -372,39 +366,33 @@ fn user.auth.AuthMatchesLlmStatus(s: llm.Status) -> string { let _1: llm.Status // s // param let _2: bool let _3: bool - let _4: bool bb0: { _2 = copy _1 == const user.auth.root.llm.Status.Ready; - branch copy _2 -> [bb5, bb1]; + branch copy _2 -> [bb4, bb1]; } bb1: { _3 = copy _1 == const user.auth.root.llm.Status.Generating; - branch copy _3 -> [bb4, bb2]; + branch copy _3 -> [bb3, bb2]; } bb2: { - _4 = copy _1 == const user.auth.root.llm.Status.Done; - branch copy _4 -> [bb3, bb6]; - } - - bb3: { _0 = const "done"; - goto -> bb6; + goto -> bb5; } - bb4: { + bb3: { _0 = const "generating"; - goto -> bb6; + goto -> bb5; } - bb5: { + bb4: { _0 = const "ready"; - goto -> bb6; + goto -> bb5; } - bb6: { + bb5: { return; } } @@ -415,39 +403,33 @@ fn user.auth.AuthMatchesRootStatus(s: Status) -> string { let _1: Status // s // param let _2: bool let _3: bool - let _4: bool bb0: { _2 = copy _1 == const user.auth.root.Status.Ok; - branch copy _2 -> [bb5, bb1]; + branch copy _2 -> [bb4, bb1]; } bb1: { _3 = copy _1 == const user.auth.root.Status.Failed; - branch copy _3 -> [bb4, bb2]; + branch copy _3 -> [bb3, bb2]; } bb2: { - _4 = copy _1 == const user.auth.root.Status.Pending; - branch copy _4 -> [bb3, bb6]; - } - - bb3: { _0 = const "pending"; - goto -> bb6; + goto -> bb5; } - bb4: { + bb3: { _0 = const "failed"; - goto -> bb6; + goto -> bb5; } - bb5: { + bb4: { _0 = const "ok"; - goto -> bb6; + goto -> bb5; } - bb6: { + bb5: { return; } } @@ -747,39 +729,33 @@ fn user.llm.LlmMatchesRootStatus(s: Status) -> string { let _1: Status // s // param let _2: bool let _3: bool - let _4: bool bb0: { _2 = copy _1 == const user.llm.root.Status.Ok; - branch copy _2 -> [bb5, bb1]; + branch copy _2 -> [bb4, bb1]; } bb1: { _3 = copy _1 == const user.llm.root.Status.Failed; - branch copy _3 -> [bb4, bb2]; + branch copy _3 -> [bb3, bb2]; } bb2: { - _4 = copy _1 == const user.llm.root.Status.Pending; - branch copy _4 -> [bb3, bb6]; - } - - bb3: { _0 = const "pending"; - goto -> bb6; + goto -> bb5; } - bb4: { + bb3: { _0 = const "failed"; - goto -> bb6; + goto -> bb5; } - bb5: { + bb4: { _0 = const "ok"; - goto -> bb6; + goto -> bb5; } - bb6: { + bb5: { return; } } @@ -1115,39 +1091,33 @@ fn user.llm.openai.OpenAIMatchesAuthRole(r: auth.Role) -> string { let _1: auth.Role // r // param let _2: bool let _3: bool - let _4: bool bb0: { _2 = copy _1 == const user.llm.openai.root.auth.Role.Admin; - branch copy _2 -> [bb5, bb1]; + branch copy _2 -> [bb4, bb1]; } bb1: { _3 = copy _1 == const user.llm.openai.root.auth.Role.User; - branch copy _3 -> [bb4, bb2]; + branch copy _3 -> [bb3, bb2]; } bb2: { - _4 = copy _1 == const user.llm.openai.root.auth.Role.Guest; - branch copy _4 -> [bb3, bb6]; - } - - bb3: { _0 = const "guest"; - goto -> bb6; + goto -> bb5; } - bb4: { + bb3: { _0 = const "user"; - goto -> bb6; + goto -> bb5; } - bb5: { + bb4: { _0 = const "admin"; - goto -> bb6; + goto -> bb5; } - bb6: { + bb5: { return; } } @@ -1158,39 +1128,33 @@ fn user.llm.openai.OpenAIMatchesLlmStatus(s: llm.Status) -> string { let _1: llm.Status // s // param let _2: bool let _3: bool - let _4: bool bb0: { _2 = copy _1 == const user.llm.openai.root.llm.Status.Ready; - branch copy _2 -> [bb5, bb1]; + branch copy _2 -> [bb4, bb1]; } bb1: { _3 = copy _1 == const user.llm.openai.root.llm.Status.Generating; - branch copy _3 -> [bb4, bb2]; + branch copy _3 -> [bb3, bb2]; } bb2: { - _4 = copy _1 == const user.llm.openai.root.llm.Status.Done; - branch copy _4 -> [bb3, bb6]; - } - - bb3: { _0 = const "done"; - goto -> bb6; + goto -> bb5; } - bb4: { + bb3: { _0 = const "generating"; - goto -> bb6; + goto -> bb5; } - bb5: { + bb4: { _0 = const "ready"; - goto -> bb6; + goto -> bb5; } - bb6: { + bb5: { return; } } @@ -1201,39 +1165,33 @@ fn user.llm.openai.OpenAIMatchesRootStatus(s: Status) -> string { let _1: Status // s // param let _2: bool let _3: bool - let _4: bool bb0: { _2 = copy _1 == const user.llm.openai.root.Status.Ok; - branch copy _2 -> [bb5, bb1]; + branch copy _2 -> [bb4, bb1]; } bb1: { _3 = copy _1 == const user.llm.openai.root.Status.Failed; - branch copy _3 -> [bb4, bb2]; + branch copy _3 -> [bb3, bb2]; } bb2: { - _4 = copy _1 == const user.llm.openai.root.Status.Pending; - branch copy _4 -> [bb3, bb6]; - } - - bb3: { _0 = const "pending"; - goto -> bb6; + goto -> bb5; } - bb4: { + bb3: { _0 = const "failed"; - goto -> bb6; + goto -> bb5; } - bb5: { + bb4: { _0 = const "ok"; - goto -> bb6; + goto -> bb5; } - bb6: { + bb5: { return; } } diff --git a/baml_language/crates/baml_tests/snapshots/namespaces_type_resolution/baml_tests__namespaces_type_resolution__06_codegen.snap b/baml_language/crates/baml_tests/snapshots/namespaces_type_resolution/baml_tests__namespaces_type_resolution__06_codegen.snap index 4f13643e70..e062f7e71d 100644 --- a/baml_language/crates/baml_tests/snapshots/namespaces_type_resolution/baml_tests__namespaces_type_resolution__06_codegen.snap +++ b/baml_language/crates/baml_tests/snapshots/namespaces_type_resolution/baml_tests__namespaces_type_resolution__06_codegen.snap @@ -33,11 +33,6 @@ function user.RootEnumMatch(s: Status) -> string { jump L2 L1: - load_var s - load_const user.Status.Pending - alloc_variant user.Status - cmp_op == - pop_jump_if_false L4 load_const "pending" jump L4 @@ -71,15 +66,12 @@ function user.RootMixedConfigs(root_cfg: Config, llm_cfg: llm.Config) -> string function user.RootThrowClass(x: int) -> string { load_var x - copy 0 load_const 0 cmp_op == pop_jump_if_false L0 - pop 1 jump L1 L0: - pop 1 load_const "ok" return @@ -94,15 +86,12 @@ function user.RootThrowClass(x: int) -> string { function user.RootThrowEnum(x: int) -> string { load_var x - copy 0 load_const 0 cmp_op == pop_jump_if_false L0 - pop 1 jump L1 L0: - pop 1 load_const "ok" return @@ -166,23 +155,20 @@ function user.auth.AuthConstruct(provider: string) -> Config { function user.auth.AuthEnumMatch(r: void) -> string { load_var r discriminant - copy 0 load_const Role.Admin cmp_op == pop_jump_if_false L0 - pop 1 jump L3 L0: - copy 0 + load_var r + discriminant load_const Role.User cmp_op == pop_jump_if_false L1 - pop 1 jump L2 L1: - pop 1 load_const "guest" jump L4 @@ -233,10 +219,6 @@ function user.auth.AuthMatchesLlmStatus(s: llm.Status) -> string { jump L2 L1: - load_var s - load_const undefined_enum::user.auth.root.llm.Status.Done - cmp_op == - pop_jump_if_false L4 load_const "done" jump L4 @@ -266,10 +248,6 @@ function user.auth.AuthMatchesRootStatus(s: Status) -> string { jump L2 L1: - load_var s - load_const undefined_enum::user.auth.root.Status.Pending - cmp_op == - pop_jump_if_false L4 load_const "pending" jump L4 @@ -298,15 +276,12 @@ function user.auth.AuthMixedErrors(auth_err: Error, root_err: Error, llm_err: ll function user.auth.AuthThrowClass(x: int) -> string { load_var x - copy 0 load_const 0 cmp_op == pop_jump_if_false L0 - pop 1 jump L1 L0: - pop 1 load_const "ok" return @@ -321,15 +296,12 @@ function user.auth.AuthThrowClass(x: int) -> string { function user.auth.AuthThrowEnum(x: int) -> string { load_var x - copy 0 load_const 0 cmp_op == pop_jump_if_false L0 - pop 1 jump L1 L0: - pop 1 load_const "ok" return @@ -341,15 +313,12 @@ function user.auth.AuthThrowEnum(x: int) -> string { function user.auth.AuthThrowsRootError(x: int) -> string { load_var x - copy 0 load_const 0 cmp_op == pop_jump_if_false L0 - pop 1 jump L1 L0: - pop 1 load_const "ok" return @@ -416,23 +385,20 @@ function user.llm.LlmConstruct(model: string) -> Config { function user.llm.LlmEnumMatch(s: Status) -> string { load_var s discriminant - copy 0 load_const Status.Ready cmp_op == pop_jump_if_false L0 - pop 1 jump L3 L0: - copy 0 + load_var s + discriminant load_const Status.Generating cmp_op == pop_jump_if_false L1 - pop 1 jump L2 L1: - pop 1 load_const "done" jump L4 @@ -473,10 +439,6 @@ function user.llm.LlmMatchesRootStatus(s: Status) -> string { jump L2 L1: - load_var s - load_const undefined_enum::user.llm.root.Status.Pending - cmp_op == - pop_jump_if_false L4 load_const "pending" jump L4 @@ -508,15 +470,12 @@ function user.llm.LlmResponseConstruct(text: string) -> void { function user.llm.LlmThrowClass(x: int) -> string { load_var x - copy 0 load_const 0 cmp_op == pop_jump_if_false L0 - pop 1 jump L1 L0: - pop 1 load_const "ok" return @@ -531,15 +490,12 @@ function user.llm.LlmThrowClass(x: int) -> string { function user.llm.LlmThrowEnum(x: int) -> string { load_var x - copy 0 load_const 0 cmp_op == pop_jump_if_false L0 - pop 1 jump L1 L0: - pop 1 load_const "ok" return @@ -551,15 +507,12 @@ function user.llm.LlmThrowEnum(x: int) -> string { function user.llm.LlmThrowsRootEnum(x: int) -> string { load_var x - copy 0 load_const 0 cmp_op == pop_jump_if_false L0 - pop 1 jump L1 L0: - pop 1 load_const "ok" return @@ -571,15 +524,12 @@ function user.llm.LlmThrowsRootEnum(x: int) -> string { function user.llm.LlmThrowsRootError(x: int) -> string { load_var x - copy 0 load_const 0 cmp_op == pop_jump_if_false L0 - pop 1 jump L1 L0: - pop 1 load_const "ok" return @@ -652,23 +602,20 @@ function user.llm.openai.OpenAIConstruct(key: string) -> void { function user.llm.openai.OpenAIEnumMatch(m: void) -> string { load_var m discriminant - copy 0 load_const Model.GPT4o cmp_op == pop_jump_if_false L0 - pop 1 jump L3 L0: - copy 0 + load_var m + discriminant load_const Model.GPT4oMini cmp_op == pop_jump_if_false L1 - pop 1 jump L2 L1: - pop 1 load_const "o3" jump L4 @@ -709,10 +656,6 @@ function user.llm.openai.OpenAIMatchesAuthRole(r: auth.Role) -> string { jump L2 L1: - load_var r - load_const undefined_enum::user.llm.openai.root.auth.Role.Guest - cmp_op == - pop_jump_if_false L4 load_const "guest" jump L4 @@ -742,10 +685,6 @@ function user.llm.openai.OpenAIMatchesLlmStatus(s: llm.Status) -> string { jump L2 L1: - load_var s - load_const undefined_enum::user.llm.openai.root.llm.Status.Done - cmp_op == - pop_jump_if_false L4 load_const "done" jump L4 @@ -775,10 +714,6 @@ function user.llm.openai.OpenAIMatchesRootStatus(s: Status) -> string { jump L2 L1: - load_var s - load_const undefined_enum::user.llm.openai.root.Status.Pending - cmp_op == - pop_jump_if_false L4 load_const "pending" jump L4 @@ -802,23 +737,20 @@ function user.llm.openai.OpenAIMixedErrors(openai_err: Error, root_err: Error, l function user.llm.openai.OpenAIMixedModels(openai_model: void, llm_model: llm.Model) -> string { load_var openai_model discriminant - copy 0 load_const Model.GPT4o cmp_op == pop_jump_if_false L0 - pop 1 jump L3 L0: - copy 0 + load_var openai_model + discriminant load_const Model.GPT4oMini cmp_op == pop_jump_if_false L1 - pop 1 jump L2 L1: - pop 1 load_const "o3" jump L4 @@ -835,15 +767,12 @@ function user.llm.openai.OpenAIMixedModels(openai_model: void, llm_model: llm.Mo function user.llm.openai.OpenAIThrowClass(x: int) -> string { load_var x - copy 0 load_const 0 cmp_op == pop_jump_if_false L0 - pop 1 jump L1 L0: - pop 1 load_const "ok" return @@ -858,15 +787,12 @@ function user.llm.openai.OpenAIThrowClass(x: int) -> string { function user.llm.openai.OpenAIThrowEnum(x: int) -> string { load_var x - copy 0 load_const 0 cmp_op == pop_jump_if_false L0 - pop 1 jump L1 L0: - pop 1 load_const "ok" return diff --git a/baml_language/crates/baml_tests/snapshots/null_handling/baml_tests__null_handling__04_5_mir.snap b/baml_language/crates/baml_tests/snapshots/null_handling/baml_tests__null_handling__04_5_mir.snap index 84260c2ff4..b5192399bf 100644 --- a/baml_language/crates/baml_tests/snapshots/null_handling/baml_tests__null_handling__04_5_mir.snap +++ b/baml_language/crates/baml_tests/snapshots/null_handling/baml_tests__null_handling__04_5_mir.snap @@ -991,15 +991,11 @@ fn user.NullCoalescing(user: User?, fallback: string, count: int) -> string { } bb90: { - branch copy _117 -> [bb91, bb91]; - } - - bb91: { _0 = copy _4; - goto -> bb92; + goto -> bb91; } - bb92: { + bb91: { return; } } diff --git a/baml_language/crates/baml_tests/snapshots/null_handling/baml_tests__null_handling__06_codegen.snap b/baml_language/crates/baml_tests/snapshots/null_handling/baml_tests__null_handling__06_codegen.snap index aa2403463b..4fa686318a 100644 --- a/baml_language/crates/baml_tests/snapshots/null_handling/baml_tests__null_handling__06_codegen.snap +++ b/baml_language/crates/baml_tests/snapshots/null_handling/baml_tests__null_handling__06_codegen.snap @@ -903,7 +903,7 @@ function user.NullCoalescing(user: User?, fallback: string, count: int) -> strin load_const null cmp_op == pop_jump_if_false L80 - jump L83 + jump L82 L80: load_var user @@ -911,28 +911,15 @@ function user.NullCoalescing(user: User?, fallback: string, count: int) -> strin load_const null cmp_op == pop_jump_if_false L81 - jump L83 + jump L82 L81: load_var user load_const null cmp_op == pop_jump_if_false L82 - jump L83 L82: - load_var user - load_field .1 - load_field .0 - jump L84 - - L83: - load_const null - - L84: - pop_jump_if_false L85 - - L85: load_var d1 return } diff --git a/baml_language/crates/baml_tests/snapshots/parser_expressions/baml_tests__parser_expressions__04_5_mir.snap b/baml_language/crates/baml_tests/snapshots/parser_expressions/baml_tests__parser_expressions__04_5_mir.snap index ea11fa1b11..6b1d308f86 100644 --- a/baml_language/crates/baml_tests/snapshots/parser_expressions/baml_tests__parser_expressions__04_5_mir.snap +++ b/baml_language/crates/baml_tests/snapshots/parser_expressions/baml_tests__parser_expressions__04_5_mir.snap @@ -106,21 +106,15 @@ fn user.TestPrecedence() -> bool { let _1: int // a let _2: int let _3: bool // d - let _4: true - let _5: false bb0: { _1 = const 14_i64; _2 = copy _1; - _4 = const true; - _3 = copy _4; - branch copy _4 -> [bb3, bb1]; + _3 = short_circuit(||) const true -> [eval: bb1, join: bb3]; } bb1: { - _5 = const false; - _3 = copy _5; - branch copy _5 -> [bb2, bb3]; + _3 = short_circuit(&&) const false -> [eval: bb2, join: bb3]; } bb2: { diff --git a/baml_language/crates/baml_tests/snapshots/parser_expressions/baml_tests__parser_expressions__06_codegen.snap b/baml_language/crates/baml_tests/snapshots/parser_expressions/baml_tests__parser_expressions__06_codegen.snap index 795f68d03c..ed5dbaec13 100644 --- a/baml_language/crates/baml_tests/snapshots/parser_expressions/baml_tests__parser_expressions__06_codegen.snap +++ b/baml_language/crates/baml_tests/snapshots/parser_expressions/baml_tests__parser_expressions__06_codegen.snap @@ -36,20 +36,16 @@ function user.IndexAccess(users: IndexUser[]) -> string { function user.TestPrecedence() -> bool { load_const true - store_var d - load_const true - pop_jump_if_false L0 + jump_if_false L0 jump L1 L0: + pop 1 load_const false - store_var d - load_const false - pop_jump_if_false L1 + jump_if_false L1 + pop 1 load_const false - store_var d L1: - load_var d return } diff --git a/baml_language/crates/baml_tests/tests/assignments.rs b/baml_language/crates/baml_tests/tests/assignments.rs index 896ca27c9c..f62b550b75 100644 --- a/baml_language/crates/baml_tests/tests/assignments.rs +++ b/baml_language/crates/baml_tests/tests/assignments.rs @@ -752,7 +752,7 @@ async fn array_element_method_field_assignment() { "# ); - insta::assert_snapshot!(output.bytecode, @r" + insta::assert_snapshot!(output.bytecode, @" function Container.get_data(self: null) -> Data { load_var self load_field .data @@ -766,26 +766,20 @@ async fn array_element_method_field_assignment() { function main() -> int { alloc_instance Container - copy 0 alloc_instance Data - copy 0 load_const 10 - store_field .value - store_field .data + init_field .value + init_field .data alloc_instance Container - copy 0 alloc_instance Data - copy 0 load_const 20 - store_field .value - store_field .data + init_field .value + init_field .data alloc_instance Container - copy 0 alloc_instance Data - copy 0 load_const 30 - store_field .value - store_field .data + init_field .value + init_field .data alloc_array 3 store_var containers load_var containers @@ -844,7 +838,7 @@ async fn method_call_then_array_access_assignment() { "# ); - insta::assert_snapshot!(output.bytecode, @r" + insta::assert_snapshot!(output.bytecode, @" function Container.get_nested(self: null) -> Item[] { load_var self load_field .data @@ -863,21 +857,17 @@ async fn method_call_then_array_access_assignment() { bin_op + store_field .value alloc_instance Container - copy 0 alloc_instance Item - copy 0 load_const 10 - store_field .value + init_field .value alloc_instance Item - copy 0 load_const 20 - store_field .value + init_field .value alloc_instance Item - copy 0 load_const 30 - store_field .value + init_field .value alloc_array 3 - store_field .data + init_field .data load_field .data load_const 1 load_array_element @@ -918,7 +908,7 @@ async fn method_call_field_assignment() { "# ); - insta::assert_snapshot!(output.bytecode, @r" + insta::assert_snapshot!(output.bytecode, @" function Factory.get_counter(self: null) -> Counter { load_var self load_field .counter @@ -933,12 +923,10 @@ async fn method_call_field_assignment() { bin_op + store_field .value alloc_instance Factory - copy 0 alloc_instance Counter - copy 0 load_const 10 - store_field .value - store_field .counter + init_field .value + init_field .counter call user.Factory.get_counter load_field .value return diff --git a/baml_language/crates/baml_tests/tests/bytecode_format/snapshots/bytecode_format__bytecode_display_expanded.snap b/baml_language/crates/baml_tests/tests/bytecode_format/snapshots/bytecode_format__bytecode_display_expanded.snap index 5404daa605..a302610f15 100644 --- a/baml_language/crates/baml_tests/tests/bytecode_format/snapshots/bytecode_format__bytecode_display_expanded.snap +++ b/baml_language/crates/baml_tests/tests/bytecode_format/snapshots/bytecode_format__bytecode_display_expanded.snap @@ -146,25 +146,25 @@ function testing.TestCollector.register_test(self: null, name: string, body: () 36 pop_jump_if_false +2 (to 38) 37 jump +4 (to 41) 38 load_var 5 (full_name) - 39 store_var 12 (final_name) + 39 store_var 11 (final_name) 40 jump +14 (to 54) 41 load_var 5 (full_name) 42 load_const 4 ("#") 43 bin_op + - 44 store_var 13 (_30) + 44 store_var 12 (_30) 45 load_var 6 (count) 46 load_const 5 (1) 47 bin_op + 48 call 100 (baml.unstable.string) - 49 store_var 14 (_32) - 50 load_var 13 (_30) - 51 load_var 14 (_32) + 49 store_var 13 (_32) + 50 load_var 12 (_30) + 51 load_var 13 (_32) 52 bin_op + - 53 store_var 12 (final_name) + 53 store_var 11 (final_name) 54 load_var 1 (self) 55 load_field 1 (tests) 56 alloc_instance 95 (TestRegistration) - 57 load_var 12 (final_name) + 57 load_var 11 (final_name) 58 init_field 0 (name) 59 load_var 3 (body) 60 init_field 1 (body) @@ -182,25 +182,23 @@ function testing.TestCollector.register_test(self: null, name: string, body: () 72 load_field 0 (name) 73 load_var 5 (full_name) 74 cmp_op == - 75 store_var 11 (_20) - 76 load_var 11 (_20) - 77 load_var 11 (_20) - 78 pop_jump_if_false +2 (to 80) - 79 jump +5 (to 84) - 80 load_var 10 (t) - 81 load_field 0 (name) - 82 load_var 7 (hash_prefix) - 83 call 36 (baml.String.startsWith) - 84 pop_jump_if_false +5 (to 89) - 85 load_var 6 (count) - 86 load_const 5 (1) - 87 bin_op + - 88 store_var 6 (count) - 89 load_var 9 (__for_idx) - 90 load_const 5 (1) - 91 bin_op + - 92 store_var 9 (__for_idx) - 93 jump -66 (to 27) + 75 jump_if_false +2 (to 77) + 76 jump +6 (to 82) + 77 pop 1 + 78 load_var 10 (t) + 79 load_field 0 (name) + 80 load_var 7 (hash_prefix) + 81 call 36 (baml.String.startsWith) + 82 pop_jump_if_false +5 (to 87) + 83 load_var 6 (count) + 84 load_const 5 (1) + 85 bin_op + + 86 store_var 6 (count) + 87 load_var 9 (__for_idx) + 88 load_const 5 (1) + 89 bin_op + + 90 store_var 9 (__for_idx) + 91 jump -64 (to 27) } function testing.TestCollector.register_test_set(self: null, name: string, collector: (testing.TestCollector) -> null, runner: (() -> testing.TestSetReport) -> () -> testing.TestSetReport?) -> null { @@ -243,25 +241,25 @@ function testing.TestCollector.register_test_set(self: null, name: string, colle 36 pop_jump_if_false +2 (to 38) 37 jump +4 (to 41) 38 load_var 5 (full_name) - 39 store_var 12 (final_name) + 39 store_var 11 (final_name) 40 jump +14 (to 54) 41 load_var 5 (full_name) 42 load_const 4 ("#") 43 bin_op + - 44 store_var 13 (_30) + 44 store_var 12 (_30) 45 load_var 6 (count) 46 load_const 5 (1) 47 bin_op + 48 call 100 (baml.unstable.string) - 49 store_var 14 (_32) - 50 load_var 13 (_30) - 51 load_var 14 (_32) + 49 store_var 13 (_32) + 50 load_var 12 (_30) + 51 load_var 13 (_32) 52 bin_op + - 53 store_var 12 (final_name) + 53 store_var 11 (final_name) 54 load_var 1 (self) 55 load_field 2 (testsets) 56 alloc_instance 97 (TestSetRegistration) - 57 load_var 12 (final_name) + 57 load_var 11 (final_name) 58 init_field 0 (name) 59 load_var 3 (collector) 60 init_field 1 (collector) @@ -279,25 +277,23 @@ function testing.TestCollector.register_test_set(self: null, name: string, colle 72 load_field 0 (name) 73 load_var 5 (full_name) 74 cmp_op == - 75 store_var 11 (_20) - 76 load_var 11 (_20) - 77 load_var 11 (_20) - 78 pop_jump_if_false +2 (to 80) - 79 jump +5 (to 84) - 80 load_var 10 (ts) - 81 load_field 0 (name) - 82 load_var 7 (hash_prefix) - 83 call 36 (baml.String.startsWith) - 84 pop_jump_if_false +5 (to 89) - 85 load_var 6 (count) - 86 load_const 5 (1) - 87 bin_op + - 88 store_var 6 (count) - 89 load_var 9 (__for_idx) - 90 load_const 5 (1) - 91 bin_op + - 92 store_var 9 (__for_idx) - 93 jump -66 (to 27) + 75 jump_if_false +2 (to 77) + 76 jump +6 (to 82) + 77 pop 1 + 78 load_var 10 (ts) + 79 load_field 0 (name) + 80 load_var 7 (hash_prefix) + 81 call 36 (baml.String.startsWith) + 82 pop_jump_if_false +5 (to 87) + 83 load_var 6 (count) + 84 load_const 5 (1) + 85 bin_op + + 86 store_var 6 (count) + 87 load_var 9 (__for_idx) + 88 load_const 5 (1) + 89 bin_op + + 90 store_var 9 (__for_idx) + 91 jump -64 (to 27) } function testing.TestRegistry.expand_set(self: null, name: string) -> (testing.SerializedTest | testing.SerializedTestSet)[] { @@ -507,7 +503,7 @@ function testing.TestRegistry.serialize(self: null) -> (testing.SerializedTest | 10 call 17 (baml.Array.length) 11 cmp_op < 12 pop_jump_if_false +2 (to 14) - 13 jump +68 (to 81) + 13 jump +67 (to 80) 14 load_var 1 (self) 15 load_field 0 (collector) 16 load_field 2 (testsets) @@ -533,64 +529,63 @@ function testing.TestRegistry.serialize(self: null) -> (testing.SerializedTest | 36 call 16 (baml.Map.get) 37 store_var 8 (expanded) 38 load_var 8 (expanded) - 39 load_const 1 (testing.TestRegistry) - 40 cmp_op instanceof - 41 pop_jump_if_false +2 (to 43) - 42 jump +15 (to 57) - 43 load_var 8 (expanded) - 44 load_const 2 (null) - 45 cmp_op == - 46 pop_jump_if_false +30 (to 76) - 47 load_var 2 (items) - 48 alloc_instance 96 (SerializedTest) - 49 load_const 3 ("lazyTestSet") - 50 init_field 0 (type) - 51 load_var 7 (ts) - 52 load_field 0 (name) - 53 init_field 1 (name) - 54 call 10 (baml.Array.push) - 55 pop 1 - 56 jump +20 (to 76) - 57 load_var 8 (expanded) - 58 store_var 9 (sub) - 59 load_var 7 (ts) - 60 load_field 0 (name) - 61 store_var 10 (_31) - 62 load_var 9 (sub) - 63 call 130 (testing.TestRegistry.serialize) - 64 store_var 11 (_33) - 65 load_var 2 (items) - 66 alloc_instance 94 (SerializedTestSet) - 67 load_var 10 (_31) - 68 init_field 0 (name) - 69 load_var 11 (_33) - 70 init_field 1 (items) - 71 load_var 9 (sub) - 72 load_field 2 (loading_time_ms) - 73 init_field 2 (loadingTimeMs) - 74 call 10 (baml.Array.push) - 75 pop 1 - 76 load_var 6 (__for_idx_1) - 77 load_const 4 (1) - 78 bin_op + - 79 store_var 6 (__for_idx_1) - 80 jump -60 (to 20) - 81 load_var 2 (items) - 82 alloc_instance 96 (SerializedTest) - 83 load_const 5 ("test") - 84 init_field 0 (type) - 85 load_var 3 (_3) - 86 load_var 4 (__for_idx) - 87 load_array_element - 88 load_field 0 (name) - 89 init_field 1 (name) - 90 call 10 (baml.Array.push) - 91 pop 1 - 92 load_var 4 (__for_idx) - 93 load_const 4 (1) - 94 bin_op + - 95 store_var 4 (__for_idx) - 96 jump -88 (to 8) + 39 is_type 1 + 40 pop_jump_if_false +2 (to 42) + 41 jump +15 (to 56) + 42 load_var 8 (expanded) + 43 load_const 2 (null) + 44 cmp_op == + 45 pop_jump_if_false +30 (to 75) + 46 load_var 2 (items) + 47 alloc_instance 96 (SerializedTest) + 48 load_const 3 ("lazyTestSet") + 49 init_field 0 (type) + 50 load_var 7 (ts) + 51 load_field 0 (name) + 52 init_field 1 (name) + 53 call 10 (baml.Array.push) + 54 pop 1 + 55 jump +20 (to 75) + 56 load_var 8 (expanded) + 57 store_var 9 (sub) + 58 load_var 7 (ts) + 59 load_field 0 (name) + 60 store_var 10 (_31) + 61 load_var 9 (sub) + 62 call 130 (testing.TestRegistry.serialize) + 63 store_var 11 (_33) + 64 load_var 2 (items) + 65 alloc_instance 94 (SerializedTestSet) + 66 load_var 10 (_31) + 67 init_field 0 (name) + 68 load_var 11 (_33) + 69 init_field 1 (items) + 70 load_var 9 (sub) + 71 load_field 2 (loading_time_ms) + 72 init_field 2 (loadingTimeMs) + 73 call 10 (baml.Array.push) + 74 pop 1 + 75 load_var 6 (__for_idx_1) + 76 load_const 4 (1) + 77 bin_op + + 78 store_var 6 (__for_idx_1) + 79 jump -59 (to 20) + 80 load_var 2 (items) + 81 alloc_instance 96 (SerializedTest) + 82 load_const 5 ("test") + 83 init_field 0 (type) + 84 load_var 3 (_3) + 85 load_var 4 (__for_idx) + 86 load_array_element + 87 load_field 0 (name) + 88 init_field 1 (name) + 89 call 10 (baml.Array.push) + 90 pop 1 + 91 load_var 4 (__for_idx) + 92 load_const 4 (1) + 93 bin_op + + 94 store_var 4 (__for_idx) + 95 jump -87 (to 8) } function testing.run_test(body: () -> null, runner: (() -> testing.TestReport) -> () -> testing.TestReport?) -> testing.TestReport { @@ -604,22 +599,20 @@ function testing.run_test(body: () -> null, runner: (() -> testing.TestReport) - 7 load_const 0 (null) 8 cmp_op == 9 pop_jump_if_false +2 (to 11) - 10 jump +11 (to 21) + 10 jump +9 (to 19) 11 load_var 2 (runner) - 12 type_tag - 13 load_const 1 (8) - 14 cmp_op == - 15 pop_jump_if_false +8 (to 23) - 16 load_var 3 (base_run) - 17 load_var 2 (runner) - 18 call_indirect - 19 store_var 4 (effective_run) - 20 jump +3 (to 23) - 21 load_var 3 (base_run) - 22 store_var 4 (effective_run) - 23 load_var 4 (effective_run) - 24 call_indirect - 25 return + 12 is_type 1 + 13 pop_jump_if_false +8 (to 21) + 14 load_var 3 (base_run) + 15 load_var 2 (runner) + 16 call_indirect + 17 store_var 4 (effective_run) + 18 jump +3 (to 21) + 19 load_var 3 (base_run) + 20 store_var 4 (effective_run) + 21 load_var 4 (effective_run) + 22 call_indirect + 23 return } function testing.run_testset(run_children: () -> testing.TestSetReport, runner: (() -> testing.TestSetReport) -> () -> testing.TestSetReport?) -> testing.TestSetReport { @@ -627,22 +620,20 @@ function testing.run_testset(run_children: () -> testing.TestSetReport, runner: 1 load_const 0 (null) 2 cmp_op == 3 pop_jump_if_false +2 (to 5) - 4 jump +11 (to 15) + 4 jump +9 (to 13) 5 load_var 2 (runner) - 6 type_tag - 7 load_const 1 (8) - 8 cmp_op == - 9 pop_jump_if_false +8 (to 17) - 10 load_var 1 (run_children) - 11 load_var 2 (runner) - 12 call_indirect - 13 store_var 3 (effective_run) - 14 jump +3 (to 17) - 15 load_var 1 (run_children) - 16 store_var 3 (effective_run) - 17 load_var 3 (effective_run) - 18 call_indirect - 19 return + 6 is_type 1 + 7 pop_jump_if_false +8 (to 15) + 8 load_var 1 (run_children) + 9 load_var 2 (runner) + 10 call_indirect + 11 store_var 3 (effective_run) + 12 jump +3 (to 15) + 13 load_var 1 (run_children) + 14 store_var 3 (effective_run) + 15 load_var 3 (effective_run) + 16 call_indirect + 17 return } function user.User.promote(self: null, bonus: int) -> Report { @@ -672,201 +663,146 @@ function user.User.promote(self: null, bonus: int) -> Report { } function user.http_status(code: int) -> string { - 0 load_var 1 (code) - 1 copy 0 - 2 load_const 0 (400) - 3 cmp_op == - 4 pop_jump_if_false +3 (to 7) - 5 pop 1 - 6 jump +49 (to 55) - 7 copy 0 - 8 load_const 0 (400) - 9 cmp_op < - 10 pop_jump_if_false +23 (to 33) - 11 copy 0 - 12 load_const 1 (201) - 13 cmp_op == - 14 pop_jump_if_false +3 (to 17) - 15 pop 1 - 16 jump +45 (to 61) - 17 copy 0 - 18 load_const 1 (201) - 19 cmp_op < - 20 pop_jump_if_false +7 (to 27) - 21 copy 0 - 22 load_const 2 (200) - 23 cmp_op == - 24 pop_jump_if_false +3 (to 27) - 25 pop 1 - 26 jump +38 (to 64) - 27 copy 0 - 28 load_const 3 (204) - 29 cmp_op == - 30 pop_jump_if_false +3 (to 33) - 31 pop 1 - 32 jump +26 (to 58) - 33 copy 0 - 34 load_const 4 (404) - 35 cmp_op == - 36 pop_jump_if_false +3 (to 39) - 37 pop 1 - 38 jump +14 (to 52) - 39 copy 0 - 40 load_const 5 (500) - 41 cmp_op == - 42 pop_jump_if_false +3 (to 45) - 43 pop 1 - 44 jump +5 (to 49) - 45 pop 1 - 46 load_const 6 ("Unknown") - 47 store_var 2 (label) - 48 jump +18 (to 66) - 49 load_const 7 ("Internal Server Error") - 50 store_var 2 (label) - 51 jump +15 (to 66) - 52 load_const 8 ("Not Found") - 53 store_var 2 (label) - 54 jump +12 (to 66) - 55 load_const 9 ("Bad Request") - 56 store_var 2 (label) - 57 jump +9 (to 66) - 58 load_const 10 ("No Content") - 59 store_var 2 (label) - 60 jump +6 (to 66) - 61 load_const 11 ("Created") - 62 store_var 2 (label) - 63 jump +3 (to 66) - 64 load_const 12 ("OK") - 65 store_var 2 (label) - 66 load_const 13 ("HTTP ") - 67 load_var 2 (label) - 68 bin_op + - 69 return + 0 load_var 1 (code) + 1 dense_tag 0 + 2 jump_table 0, +1 ([to 21, to 18, to 15, to 12, to 9, to 6], default to 3) + 3 load_const 0 ("Unknown") + 4 store_var 2 (label) + 5 jump +18 (to 23) + 6 load_const 1 ("Internal Server Error") + 7 store_var 2 (label) + 8 jump +15 (to 23) + 9 load_const 2 ("Not Found") + 10 store_var 2 (label) + 11 jump +12 (to 23) + 12 load_const 3 ("Bad Request") + 13 store_var 2 (label) + 14 jump +9 (to 23) + 15 load_const 4 ("No Content") + 16 store_var 2 (label) + 17 jump +6 (to 23) + 18 load_const 5 ("Created") + 19 store_var 2 (label) + 20 jump +3 (to 23) + 21 load_const 6 ("OK") + 22 store_var 2 (label) + 23 load_const 7 ("HTTP ") + 24 load_var 2 (label) + 25 bin_op + + 26 return } function user.score(input: User | ErrorInfo, threshold: int) -> Report { 0 load_var 1 (input) - 1 load_const 0 (User) - 2 cmp_op instanceof - 3 pop_jump_if_false +2 (to 5) - 4 jump +9 (to 13) - 5 load_var 1 (input) - 6 load_const 1 (ErrorInfo) - 7 cmp_op instanceof - 8 pop_jump_if_false +8 (to 16) - 9 load_var 1 (input) - 10 load_field 1 (message) - 11 store_var 3 (name) - 12 jump +4 (to 16) - 13 load_var 1 (input) - 14 load_field 0 (name) - 15 store_var 3 (name) - 16 load_var 1 (input) - 17 load_const 0 (User) - 18 cmp_op instanceof - 19 pop_jump_if_false +2 (to 21) - 20 jump +10 (to 30) + 1 is_type 0 + 2 pop_jump_if_false +2 (to 4) + 3 jump +8 (to 11) + 4 load_var 1 (input) + 5 is_type 1 + 6 pop_jump_if_false +8 (to 14) + 7 load_var 1 (input) + 8 load_field 1 (message) + 9 store_var 3 (name) + 10 jump +4 (to 14) + 11 load_var 1 (input) + 12 load_field 0 (name) + 13 store_var 3 (name) + 14 load_var 1 (input) + 15 is_type 0 + 16 pop_jump_if_false +2 (to 18) + 17 jump +9 (to 26) + 18 load_var 1 (input) + 19 is_type 1 + 20 pop_jump_if_false +9 (to 29) 21 load_var 1 (input) - 22 load_const 1 (ErrorInfo) - 23 cmp_op instanceof - 24 pop_jump_if_false +9 (to 33) - 25 load_var 1 (input) - 26 load_field 0 (code) - 27 unary_op - + 22 load_field 0 (code) + 23 unary_op - + 24 store_var 4 (age) + 25 jump +4 (to 29) + 26 load_var 1 (input) + 27 load_field 1 (age) 28 store_var 4 (age) - 29 jump +4 (to 33) - 30 load_var 1 (input) - 31 load_field 1 (age) - 32 store_var 4 (age) + 29 load_var 1 (input) + 30 is_type 0 + 31 pop_jump_if_false +2 (to 33) + 32 jump +7 (to 39) 33 load_var 1 (input) - 34 load_const 0 (User) - 35 cmp_op instanceof - 36 pop_jump_if_false +2 (to 38) - 37 jump +8 (to 45) - 38 load_var 1 (input) - 39 load_const 1 (ErrorInfo) - 40 cmp_op instanceof - 41 pop_jump_if_false +19 (to 60) - 42 load_var 2 (threshold) - 43 store_var 5 (role_mult) - 44 jump +16 (to 60) - 45 load_var 1 (input) - 46 load_field 2 (role) - 47 discriminant - 48 jump_table 0, +75 ([to 58, to 55, to 52, to 49], default to 123) + 34 is_type 1 + 35 pop_jump_if_false +19 (to 54) + 36 load_var 2 (threshold) + 37 store_var 5 (role_mult) + 38 jump +16 (to 54) + 39 load_var 1 (input) + 40 load_field 2 (role) + 41 discriminant + 42 jump_table 0, +69 ([to 52, to 49, to 46, to 43], default to 111) + 43 load_var 2 (threshold) + 44 store_var 5 (role_mult) + 45 jump +9 (to 54) + 46 load_var 2 (threshold) + 47 store_var 5 (role_mult) + 48 jump +6 (to 54) 49 load_var 2 (threshold) 50 store_var 5 (role_mult) - 51 jump +9 (to 60) + 51 jump +3 (to 54) 52 load_var 2 (threshold) 53 store_var 5 (role_mult) - 54 jump +6 (to 60) - 55 load_var 2 (threshold) - 56 store_var 5 (role_mult) - 57 jump +3 (to 60) - 58 load_var 2 (threshold) - 59 store_var 5 (role_mult) - 60 load_var 4 (age) - 61 load_const 2 (2) - 62 bin_op * - 63 load_var 5 (role_mult) - 64 bin_op + - 65 store_var 6 (base) - 66 load_var 6 (base) - 67 load_var 5 (role_mult) - 68 cmp_op > - 69 pop_jump_if_false +2 (to 71) - 70 jump +4 (to 74) - 71 load_var 6 (base) - 72 store_var 7 (capped) - 73 jump +3 (to 76) - 74 load_var 5 (role_mult) - 75 store_var 7 (capped) - 76 load_var 7 (capped) - 77 load_var 5 (role_mult) - 78 bin_op + - 79 store_var 8 (total) - 80 load_var 8 (total) - 81 load_var 2 (threshold) - 82 cmp_op > - 83 store_var 10 (_37) - 84 load_var 10 (_37) - 85 load_const 3 (true) - 86 cmp_op == - 87 pop_jump_if_false +2 (to 89) - 88 jump +8 (to 96) - 89 load_var 10 (_37) - 90 load_const 4 (false) - 91 cmp_op == - 92 pop_jump_if_false +6 (to 98) - 93 load_const 5 (" [LOW]") - 94 store_var 9 (suffix) - 95 jump +3 (to 98) - 96 load_const 6 (" [HIGH]") - 97 store_var 9 (suffix) - 98 load_var 6 (base) - 99 load_var 7 (capped) - 100 load_var 8 (total) - 101 alloc_array 3 - 102 store_var 11 (scores) - 103 load_var 11 (scores) - 104 load_const 7 (0) - 105 load_var 8 (total) - 106 store_array_element - 107 alloc_instance 5 (Report) - 108 load_var 11 (scores) - 109 load_const 7 (0) - 110 load_array_element - 111 init_field 0 (score) - 112 load_var 3 (name) - 113 load_var 9 (suffix) - 114 bin_op + - 115 init_field 1 (label) - 116 load_var 6 (base) - 117 load_var 5 (role_mult) - 118 load_const 8 ("base") - 119 load_const 9 ("role") - 120 alloc_map 2 - 121 init_field 2 (breakdown) - 122 return - 123 unreachable + 54 load_var 4 (age) + 55 load_const 2 (2) + 56 bin_op * + 57 load_var 5 (role_mult) + 58 bin_op + + 59 store_var 6 (base) + 60 load_var 6 (base) + 61 load_var 5 (role_mult) + 62 cmp_op > + 63 pop_jump_if_false +2 (to 65) + 64 jump +4 (to 68) + 65 load_var 6 (base) + 66 store_var 7 (capped) + 67 jump +3 (to 70) + 68 load_var 5 (role_mult) + 69 store_var 7 (capped) + 70 load_var 7 (capped) + 71 load_var 5 (role_mult) + 72 bin_op + + 73 store_var 8 (total) + 74 load_var 8 (total) + 75 load_var 2 (threshold) + 76 cmp_op > + 77 load_const 3 (true) + 78 cmp_op == + 79 pop_jump_if_false +2 (to 81) + 80 jump +4 (to 84) + 81 load_const 4 (" [LOW]") + 82 store_var 9 (suffix) + 83 jump +3 (to 86) + 84 load_const 5 (" [HIGH]") + 85 store_var 9 (suffix) + 86 load_var 6 (base) + 87 load_var 7 (capped) + 88 load_var 8 (total) + 89 alloc_array 3 + 90 store_var 10 (scores) + 91 load_var 10 (scores) + 92 load_const 6 (0) + 93 load_var 8 (total) + 94 store_array_element + 95 alloc_instance 5 (Report) + 96 load_var 10 (scores) + 97 load_const 6 (0) + 98 load_array_element + 99 init_field 0 (score) + 100 load_var 3 (name) + 101 load_var 9 (suffix) + 102 bin_op + + 103 init_field 1 (label) + 104 load_var 6 (base) + 105 load_var 5 (role_mult) + 106 load_const 7 ("base") + 107 load_const 8 ("role") + 108 alloc_map 2 + 109 init_field 2 (breakdown) + 110 return + 111 unreachable } diff --git a/baml_language/crates/baml_tests/tests/bytecode_format/snapshots/bytecode_format__bytecode_display_expanded_unoptimized.snap b/baml_language/crates/baml_tests/tests/bytecode_format/snapshots/bytecode_format__bytecode_display_expanded_unoptimized.snap index fc84ae8478..92e712f65e 100644 --- a/baml_language/crates/baml_tests/tests/bytecode_format/snapshots/bytecode_format__bytecode_display_expanded_unoptimized.snap +++ b/baml_language/crates/baml_tests/tests/bytecode_format/snapshots/bytecode_format__bytecode_display_expanded_unoptimized.snap @@ -150,25 +150,25 @@ function testing.TestCollector.register_test(self: null, name: string, body: () 36 pop_jump_if_false +2 (to 38) 37 jump +4 (to 41) 38 load_var 6 (full_name) - 39 store_var 13 (final_name) + 39 store_var 12 (final_name) 40 jump +14 (to 54) 41 load_var 6 (full_name) 42 load_const 4 ("#") 43 bin_op + - 44 store_var 14 (_30) + 44 store_var 13 (_30) 45 load_var 7 (count) 46 load_const 5 (1) 47 bin_op + 48 call 100 (baml.unstable.string) - 49 store_var 15 (_32) - 50 load_var 14 (_30) - 51 load_var 15 (_32) + 49 store_var 14 (_32) + 50 load_var 13 (_30) + 51 load_var 14 (_32) 52 bin_op + - 53 store_var 13 (final_name) + 53 store_var 12 (final_name) 54 load_var 1 (self) 55 load_field 1 (tests) 56 alloc_instance 95 (TestRegistration) - 57 load_var 13 (final_name) + 57 load_var 12 (final_name) 58 init_field 0 (name) 59 load_var 3 (body) 60 init_field 1 (body) @@ -188,25 +188,23 @@ function testing.TestCollector.register_test(self: null, name: string, body: () 74 load_field 0 (name) 75 load_var 6 (full_name) 76 cmp_op == - 77 store_var 12 (_20) - 78 load_var 12 (_20) - 79 load_var 12 (_20) - 80 pop_jump_if_false +2 (to 82) - 81 jump +5 (to 86) - 82 load_var 11 (t) - 83 load_field 0 (name) - 84 load_var 8 (hash_prefix) - 85 call 36 (baml.String.startsWith) - 86 pop_jump_if_false +5 (to 91) - 87 load_var 7 (count) - 88 load_const 5 (1) - 89 bin_op + - 90 store_var 7 (count) - 91 load_var 10 (__for_idx) - 92 load_const 5 (1) - 93 bin_op + - 94 store_var 10 (__for_idx) - 95 jump -68 (to 27) + 77 jump_if_false +2 (to 79) + 78 jump +6 (to 84) + 79 pop 1 + 80 load_var 11 (t) + 81 load_field 0 (name) + 82 load_var 8 (hash_prefix) + 83 call 36 (baml.String.startsWith) + 84 pop_jump_if_false +5 (to 89) + 85 load_var 7 (count) + 86 load_const 5 (1) + 87 bin_op + + 88 store_var 7 (count) + 89 load_var 10 (__for_idx) + 90 load_const 5 (1) + 91 bin_op + + 92 store_var 10 (__for_idx) + 93 jump -66 (to 27) } function testing.TestCollector.register_test_set(self: null, name: string, collector: (testing.TestCollector) -> null, runner: (() -> testing.TestSetReport) -> () -> testing.TestSetReport?) -> null { @@ -249,25 +247,25 @@ function testing.TestCollector.register_test_set(self: null, name: string, colle 36 pop_jump_if_false +2 (to 38) 37 jump +4 (to 41) 38 load_var 6 (full_name) - 39 store_var 13 (final_name) + 39 store_var 12 (final_name) 40 jump +14 (to 54) 41 load_var 6 (full_name) 42 load_const 4 ("#") 43 bin_op + - 44 store_var 14 (_30) + 44 store_var 13 (_30) 45 load_var 7 (count) 46 load_const 5 (1) 47 bin_op + 48 call 100 (baml.unstable.string) - 49 store_var 15 (_32) - 50 load_var 14 (_30) - 51 load_var 15 (_32) + 49 store_var 14 (_32) + 50 load_var 13 (_30) + 51 load_var 14 (_32) 52 bin_op + - 53 store_var 13 (final_name) + 53 store_var 12 (final_name) 54 load_var 1 (self) 55 load_field 2 (testsets) 56 alloc_instance 97 (TestSetRegistration) - 57 load_var 13 (final_name) + 57 load_var 12 (final_name) 58 init_field 0 (name) 59 load_var 3 (collector) 60 init_field 1 (collector) @@ -287,25 +285,23 @@ function testing.TestCollector.register_test_set(self: null, name: string, colle 74 load_field 0 (name) 75 load_var 6 (full_name) 76 cmp_op == - 77 store_var 12 (_20) - 78 load_var 12 (_20) - 79 load_var 12 (_20) - 80 pop_jump_if_false +2 (to 82) - 81 jump +5 (to 86) - 82 load_var 11 (ts) - 83 load_field 0 (name) - 84 load_var 8 (hash_prefix) - 85 call 36 (baml.String.startsWith) - 86 pop_jump_if_false +5 (to 91) - 87 load_var 7 (count) - 88 load_const 5 (1) - 89 bin_op + - 90 store_var 7 (count) - 91 load_var 10 (__for_idx) - 92 load_const 5 (1) - 93 bin_op + - 94 store_var 10 (__for_idx) - 95 jump -68 (to 27) + 77 jump_if_false +2 (to 79) + 78 jump +6 (to 84) + 79 pop 1 + 80 load_var 11 (ts) + 81 load_field 0 (name) + 82 load_var 8 (hash_prefix) + 83 call 36 (baml.String.startsWith) + 84 pop_jump_if_false +5 (to 89) + 85 load_var 7 (count) + 86 load_const 5 (1) + 87 bin_op + + 88 store_var 7 (count) + 89 load_var 10 (__for_idx) + 90 load_const 5 (1) + 91 bin_op + + 92 store_var 10 (__for_idx) + 93 jump -66 (to 27) } function testing.TestRegistry.expand_set(self: null, name: string) -> (testing.SerializedTest | testing.SerializedTestSet)[] { @@ -506,107 +502,106 @@ function testing.TestRegistry.run_test(self: null, name: string) -> testing.Test } function testing.TestRegistry.serialize(self: null) -> (testing.SerializedTest | testing.SerializedTestSet)[] { - 0 alloc_array 0 - 1 store_var 3 (items) - 2 load_var 1 (self) - 3 load_field 0 (collector) - 4 load_field 1 (tests) - 5 store_var 4 (_3) - 6 load_const 0 (0) - 7 store_var 5 (__for_idx) - 8 load_var 5 (__for_idx) - 9 load_var 4 (_3) - 10 call 17 (baml.Array.length) - 11 cmp_op < - 12 pop_jump_if_false +2 (to 14) - 13 jump +70 (to 83) - 14 load_var 1 (self) - 15 load_field 0 (collector) - 16 load_field 2 (testsets) - 17 store_var 7 (_15) - 18 load_const 0 (0) - 19 store_var 8 (__for_idx_1) - 20 load_var 8 (__for_idx_1) - 21 load_var 7 (_15) - 22 call 17 (baml.Array.length) - 23 cmp_op < - 24 pop_jump_if_false +2 (to 26) - 25 jump +5 (to 30) - 26 load_var 3 (items) - 27 store_var 2 (_0) - 28 load_var 2 (_0) - 29 return - 30 load_var 7 (_15) - 31 load_var 8 (__for_idx_1) - 32 load_array_element - 33 store_var 9 (ts) - 34 load_var 1 (self) - 35 load_field 1 (expansions) - 36 load_var 9 (ts) - 37 load_field 0 (name) - 38 call 16 (baml.Map.get) - 39 store_var 10 (expanded) - 40 load_var 10 (expanded) - 41 load_const 1 (testing.TestRegistry) - 42 cmp_op instanceof - 43 pop_jump_if_false +2 (to 45) - 44 jump +15 (to 59) - 45 load_var 10 (expanded) - 46 load_const 2 (null) - 47 cmp_op == - 48 pop_jump_if_false +30 (to 78) - 49 load_var 3 (items) - 50 alloc_instance 96 (SerializedTest) - 51 load_const 3 ("lazyTestSet") - 52 init_field 0 (type) - 53 load_var 9 (ts) - 54 load_field 0 (name) - 55 init_field 1 (name) - 56 call 10 (baml.Array.push) - 57 pop 1 - 58 jump +20 (to 78) - 59 load_var 10 (expanded) - 60 store_var 11 (sub) - 61 load_var 9 (ts) - 62 load_field 0 (name) - 63 store_var 12 (_31) - 64 load_var 11 (sub) - 65 call 130 (testing.TestRegistry.serialize) - 66 store_var 13 (_33) - 67 load_var 3 (items) - 68 alloc_instance 94 (SerializedTestSet) - 69 load_var 12 (_31) - 70 init_field 0 (name) - 71 load_var 13 (_33) - 72 init_field 1 (items) - 73 load_var 11 (sub) - 74 load_field 2 (loading_time_ms) - 75 init_field 2 (loadingTimeMs) - 76 call 10 (baml.Array.push) - 77 pop 1 - 78 load_var 8 (__for_idx_1) - 79 load_const 4 (1) - 80 bin_op + - 81 store_var 8 (__for_idx_1) - 82 jump -62 (to 20) - 83 load_var 4 (_3) - 84 load_var 5 (__for_idx) - 85 load_array_element - 86 store_var 6 (t) - 87 load_var 3 (items) - 88 alloc_instance 96 (SerializedTest) - 89 load_const 5 ("test") - 90 init_field 0 (type) - 91 load_var 6 (t) - 92 load_field 0 (name) - 93 init_field 1 (name) - 94 call 10 (baml.Array.push) - 95 pop 1 - 96 load_var 5 (__for_idx) - 97 load_const 4 (1) - 98 bin_op + - 99 store_var 5 (__for_idx) - 100 jump -92 (to 8) + 0 alloc_array 0 + 1 store_var 3 (items) + 2 load_var 1 (self) + 3 load_field 0 (collector) + 4 load_field 1 (tests) + 5 store_var 4 (_3) + 6 load_const 0 (0) + 7 store_var 5 (__for_idx) + 8 load_var 5 (__for_idx) + 9 load_var 4 (_3) + 10 call 17 (baml.Array.length) + 11 cmp_op < + 12 pop_jump_if_false +2 (to 14) + 13 jump +69 (to 82) + 14 load_var 1 (self) + 15 load_field 0 (collector) + 16 load_field 2 (testsets) + 17 store_var 7 (_15) + 18 load_const 0 (0) + 19 store_var 8 (__for_idx_1) + 20 load_var 8 (__for_idx_1) + 21 load_var 7 (_15) + 22 call 17 (baml.Array.length) + 23 cmp_op < + 24 pop_jump_if_false +2 (to 26) + 25 jump +5 (to 30) + 26 load_var 3 (items) + 27 store_var 2 (_0) + 28 load_var 2 (_0) + 29 return + 30 load_var 7 (_15) + 31 load_var 8 (__for_idx_1) + 32 load_array_element + 33 store_var 9 (ts) + 34 load_var 1 (self) + 35 load_field 1 (expansions) + 36 load_var 9 (ts) + 37 load_field 0 (name) + 38 call 16 (baml.Map.get) + 39 store_var 10 (expanded) + 40 load_var 10 (expanded) + 41 is_type 1 + 42 pop_jump_if_false +2 (to 44) + 43 jump +15 (to 58) + 44 load_var 10 (expanded) + 45 load_const 2 (null) + 46 cmp_op == + 47 pop_jump_if_false +30 (to 77) + 48 load_var 3 (items) + 49 alloc_instance 96 (SerializedTest) + 50 load_const 3 ("lazyTestSet") + 51 init_field 0 (type) + 52 load_var 9 (ts) + 53 load_field 0 (name) + 54 init_field 1 (name) + 55 call 10 (baml.Array.push) + 56 pop 1 + 57 jump +20 (to 77) + 58 load_var 10 (expanded) + 59 store_var 11 (sub) + 60 load_var 9 (ts) + 61 load_field 0 (name) + 62 store_var 12 (_31) + 63 load_var 11 (sub) + 64 call 130 (testing.TestRegistry.serialize) + 65 store_var 13 (_33) + 66 load_var 3 (items) + 67 alloc_instance 94 (SerializedTestSet) + 68 load_var 12 (_31) + 69 init_field 0 (name) + 70 load_var 13 (_33) + 71 init_field 1 (items) + 72 load_var 11 (sub) + 73 load_field 2 (loading_time_ms) + 74 init_field 2 (loadingTimeMs) + 75 call 10 (baml.Array.push) + 76 pop 1 + 77 load_var 8 (__for_idx_1) + 78 load_const 4 (1) + 79 bin_op + + 80 store_var 8 (__for_idx_1) + 81 jump -61 (to 20) + 82 load_var 4 (_3) + 83 load_var 5 (__for_idx) + 84 load_array_element + 85 store_var 6 (t) + 86 load_var 3 (items) + 87 alloc_instance 96 (SerializedTest) + 88 load_const 5 ("test") + 89 init_field 0 (type) + 90 load_var 6 (t) + 91 load_field 0 (name) + 92 init_field 1 (name) + 93 call 10 (baml.Array.push) + 94 pop 1 + 95 load_var 5 (__for_idx) + 96 load_const 4 (1) + 97 bin_op + + 98 store_var 5 (__for_idx) + 99 jump -91 (to 8) } function testing.run_test(body: () -> null, runner: (() -> testing.TestReport) -> () -> testing.TestReport?) -> testing.TestReport { @@ -620,24 +615,22 @@ function testing.run_test(body: () -> null, runner: (() -> testing.TestReport) - 7 load_const 0 (null) 8 cmp_op == 9 pop_jump_if_false +2 (to 11) - 10 jump +13 (to 23) + 10 jump +11 (to 21) 11 load_var 2 (runner) - 12 type_tag - 13 load_const 1 (8) - 14 cmp_op == - 15 pop_jump_if_false +10 (to 25) - 16 load_var 2 (runner) - 17 store_var 5 (r) - 18 load_var 3 (base_run) - 19 load_var 5 (r) - 20 call_indirect - 21 store_var 4 (effective_run) - 22 jump +3 (to 25) - 23 load_var 3 (base_run) - 24 store_var 4 (effective_run) - 25 load_var 4 (effective_run) - 26 call_indirect - 27 return + 12 is_type 1 + 13 pop_jump_if_false +10 (to 23) + 14 load_var 2 (runner) + 15 store_var 5 (r) + 16 load_var 3 (base_run) + 17 load_var 5 (r) + 18 call_indirect + 19 store_var 4 (effective_run) + 20 jump +3 (to 23) + 21 load_var 3 (base_run) + 22 store_var 4 (effective_run) + 23 load_var 4 (effective_run) + 24 call_indirect + 25 return } function testing.run_testset(run_children: () -> testing.TestSetReport, runner: (() -> testing.TestSetReport) -> () -> testing.TestSetReport?) -> testing.TestSetReport { @@ -645,24 +638,22 @@ function testing.run_testset(run_children: () -> testing.TestSetReport, runner: 1 load_const 0 (null) 2 cmp_op == 3 pop_jump_if_false +2 (to 5) - 4 jump +13 (to 17) + 4 jump +11 (to 15) 5 load_var 2 (runner) - 6 type_tag - 7 load_const 1 (8) - 8 cmp_op == - 9 pop_jump_if_false +10 (to 19) - 10 load_var 2 (runner) - 11 store_var 4 (r) - 12 load_var 1 (run_children) - 13 load_var 4 (r) - 14 call_indirect - 15 store_var 3 (effective_run) - 16 jump +3 (to 19) - 17 load_var 1 (run_children) - 18 store_var 3 (effective_run) - 19 load_var 3 (effective_run) - 20 call_indirect - 21 return + 6 is_type 1 + 7 pop_jump_if_false +10 (to 17) + 8 load_var 2 (runner) + 9 store_var 4 (r) + 10 load_var 1 (run_children) + 11 load_var 4 (r) + 12 call_indirect + 13 store_var 3 (effective_run) + 14 jump +3 (to 17) + 15 load_var 1 (run_children) + 16 store_var 3 (effective_run) + 17 load_var 3 (effective_run) + 18 call_indirect + 19 return } function user.User.promote(self: null, bonus: int) -> Report { @@ -696,219 +687,164 @@ function user.User.promote(self: null, bonus: int) -> Report { } function user.http_status(code: int) -> string { - 0 load_const 0 ("HTTP ") - 1 store_var 2 (prefix) - 2 load_var 1 (code) - 3 copy 0 - 4 load_const 1 (400) - 5 cmp_op == - 6 pop_jump_if_false +3 (to 9) - 7 pop 1 - 8 jump +49 (to 57) - 9 copy 0 - 10 load_const 1 (400) - 11 cmp_op < - 12 pop_jump_if_false +23 (to 35) - 13 copy 0 - 14 load_const 2 (201) - 15 cmp_op == - 16 pop_jump_if_false +3 (to 19) - 17 pop 1 - 18 jump +45 (to 63) - 19 copy 0 - 20 load_const 2 (201) - 21 cmp_op < - 22 pop_jump_if_false +7 (to 29) - 23 copy 0 - 24 load_const 3 (200) - 25 cmp_op == - 26 pop_jump_if_false +3 (to 29) - 27 pop 1 - 28 jump +38 (to 66) - 29 copy 0 - 30 load_const 4 (204) - 31 cmp_op == - 32 pop_jump_if_false +3 (to 35) - 33 pop 1 - 34 jump +26 (to 60) - 35 copy 0 - 36 load_const 5 (404) - 37 cmp_op == - 38 pop_jump_if_false +3 (to 41) - 39 pop 1 - 40 jump +14 (to 54) - 41 copy 0 - 42 load_const 6 (500) - 43 cmp_op == - 44 pop_jump_if_false +3 (to 47) - 45 pop 1 - 46 jump +5 (to 51) - 47 pop 1 - 48 load_const 7 ("Unknown") - 49 store_var 3 (label) - 50 jump +18 (to 68) - 51 load_const 8 ("Internal Server Error") - 52 store_var 3 (label) - 53 jump +15 (to 68) - 54 load_const 9 ("Not Found") - 55 store_var 3 (label) - 56 jump +12 (to 68) - 57 load_const 10 ("Bad Request") - 58 store_var 3 (label) - 59 jump +9 (to 68) - 60 load_const 11 ("No Content") - 61 store_var 3 (label) - 62 jump +6 (to 68) - 63 load_const 12 ("Created") - 64 store_var 3 (label) - 65 jump +3 (to 68) - 66 load_const 13 ("OK") - 67 store_var 3 (label) - 68 load_var 2 (prefix) - 69 load_var 3 (label) - 70 bin_op + - 71 return + 0 load_const 0 ("HTTP ") + 1 store_var 2 (prefix) + 2 load_var 1 (code) + 3 dense_tag 0 + 4 jump_table 0, +1 ([to 23, to 20, to 17, to 14, to 11, to 8], default to 5) + 5 load_const 1 ("Unknown") + 6 store_var 3 (label) + 7 jump +18 (to 25) + 8 load_const 2 ("Internal Server Error") + 9 store_var 3 (label) + 10 jump +15 (to 25) + 11 load_const 3 ("Not Found") + 12 store_var 3 (label) + 13 jump +12 (to 25) + 14 load_const 4 ("Bad Request") + 15 store_var 3 (label) + 16 jump +9 (to 25) + 17 load_const 5 ("No Content") + 18 store_var 3 (label) + 19 jump +6 (to 25) + 20 load_const 6 ("Created") + 21 store_var 3 (label) + 22 jump +3 (to 25) + 23 load_const 7 ("OK") + 24 store_var 3 (label) + 25 load_var 2 (prefix) + 26 load_var 3 (label) + 27 bin_op + + 28 return } function user.score(input: User | ErrorInfo, threshold: int) -> Report { 0 load_var 1 (input) - 1 load_const 0 (User) - 2 cmp_op instanceof - 3 pop_jump_if_false +2 (to 5) - 4 jump +11 (to 15) - 5 load_var 1 (input) - 6 load_const 1 (ErrorInfo) - 7 cmp_op instanceof - 8 pop_jump_if_false +12 (to 20) - 9 load_var 1 (input) - 10 store_var 5 (e) - 11 load_var 5 (e) - 12 load_field 1 (message) - 13 store_var 3 (name) - 14 jump +6 (to 20) - 15 load_var 1 (input) - 16 store_var 4 (u) - 17 load_var 4 (u) - 18 load_field 0 (name) - 19 store_var 3 (name) - 20 load_var 1 (input) - 21 load_const 0 (User) - 22 cmp_op instanceof - 23 pop_jump_if_false +2 (to 25) - 24 jump +12 (to 36) + 1 is_type 0 + 2 pop_jump_if_false +2 (to 4) + 3 jump +10 (to 13) + 4 load_var 1 (input) + 5 is_type 1 + 6 pop_jump_if_false +12 (to 18) + 7 load_var 1 (input) + 8 store_var 5 (e) + 9 load_var 5 (e) + 10 load_field 1 (message) + 11 store_var 3 (name) + 12 jump +6 (to 18) + 13 load_var 1 (input) + 14 store_var 4 (u) + 15 load_var 4 (u) + 16 load_field 0 (name) + 17 store_var 3 (name) + 18 load_var 1 (input) + 19 is_type 0 + 20 pop_jump_if_false +2 (to 22) + 21 jump +11 (to 32) + 22 load_var 1 (input) + 23 is_type 1 + 24 pop_jump_if_false +13 (to 37) 25 load_var 1 (input) - 26 load_const 1 (ErrorInfo) - 27 cmp_op instanceof - 28 pop_jump_if_false +13 (to 41) - 29 load_var 1 (input) - 30 store_var 8 (e) - 31 load_var 8 (e) - 32 load_field 0 (code) - 33 unary_op - - 34 store_var 6 (age) - 35 jump +6 (to 41) - 36 load_var 1 (input) - 37 store_var 7 (u) - 38 load_var 7 (u) - 39 load_field 1 (age) - 40 store_var 6 (age) + 26 store_var 8 (e) + 27 load_var 8 (e) + 28 load_field 0 (code) + 29 unary_op - + 30 store_var 6 (age) + 31 jump +6 (to 37) + 32 load_var 1 (input) + 33 store_var 7 (u) + 34 load_var 7 (u) + 35 load_field 1 (age) + 36 store_var 6 (age) + 37 load_var 1 (input) + 38 is_type 0 + 39 pop_jump_if_false +2 (to 41) + 40 jump +7 (to 47) 41 load_var 1 (input) - 42 load_const 0 (User) - 43 cmp_op instanceof - 44 pop_jump_if_false +2 (to 46) - 45 jump +8 (to 53) - 46 load_var 1 (input) - 47 load_const 1 (ErrorInfo) - 48 cmp_op instanceof - 49 pop_jump_if_false +21 (to 70) - 50 load_var 2 (threshold) - 51 store_var 9 (role_mult) - 52 jump +18 (to 70) - 53 load_var 1 (input) - 54 store_var 10 (u) - 55 load_var 10 (u) - 56 load_field 2 (role) - 57 discriminant - 58 jump_table 0, +81 ([to 68, to 65, to 62, to 59], default to 139) + 42 is_type 1 + 43 pop_jump_if_false +21 (to 64) + 44 load_var 2 (threshold) + 45 store_var 9 (role_mult) + 46 jump +18 (to 64) + 47 load_var 1 (input) + 48 store_var 10 (u) + 49 load_var 10 (u) + 50 load_field 2 (role) + 51 discriminant + 52 jump_table 0, +75 ([to 62, to 59, to 56, to 53], default to 127) + 53 load_var 2 (threshold) + 54 store_var 9 (role_mult) + 55 jump +9 (to 64) + 56 load_var 2 (threshold) + 57 store_var 9 (role_mult) + 58 jump +6 (to 64) 59 load_var 2 (threshold) 60 store_var 9 (role_mult) - 61 jump +9 (to 70) + 61 jump +3 (to 64) 62 load_var 2 (threshold) 63 store_var 9 (role_mult) - 64 jump +6 (to 70) - 65 load_var 2 (threshold) - 66 store_var 9 (role_mult) - 67 jump +3 (to 70) - 68 load_var 2 (threshold) - 69 store_var 9 (role_mult) - 70 load_var 6 (age) - 71 load_const 2 (2) - 72 bin_op * - 73 load_var 9 (role_mult) - 74 bin_op + - 75 store_var 11 (base) - 76 load_var 11 (base) - 77 load_var 9 (role_mult) - 78 cmp_op > - 79 pop_jump_if_false +2 (to 81) - 80 jump +4 (to 84) - 81 load_var 11 (base) - 82 store_var 12 (capped) - 83 jump +3 (to 86) - 84 load_var 9 (role_mult) - 85 store_var 12 (capped) - 86 load_var 12 (capped) - 87 load_var 9 (role_mult) - 88 bin_op + - 89 store_var 13 (total) - 90 load_var 13 (total) - 91 load_var 2 (threshold) - 92 cmp_op > - 93 store_var 15 (_37) - 94 load_var 15 (_37) - 95 load_const 3 (true) - 96 cmp_op == - 97 pop_jump_if_false +2 (to 99) - 98 jump +8 (to 106) - 99 load_var 15 (_37) - 100 load_const 4 (false) - 101 cmp_op == - 102 pop_jump_if_false +6 (to 108) - 103 load_const 5 (" [LOW]") - 104 store_var 14 (suffix) - 105 jump +3 (to 108) - 106 load_const 6 (" [HIGH]") - 107 store_var 14 (suffix) - 108 load_var 3 (name) - 109 load_var 14 (suffix) - 110 bin_op + - 111 store_var 16 (label) - 112 load_var 11 (base) - 113 load_var 12 (capped) - 114 load_var 13 (total) - 115 alloc_array 3 - 116 store_var 17 (scores) - 117 load_var 17 (scores) - 118 load_const 7 (0) - 119 load_var 13 (total) - 120 store_array_element - 121 load_var 17 (scores) - 122 load_const 7 (0) - 123 load_array_element - 124 store_var 18 (top) - 125 load_var 11 (base) - 126 load_var 9 (role_mult) - 127 load_const 8 ("base") - 128 load_const 9 ("role") - 129 alloc_map 2 - 130 store_var 19 (breakdown) - 131 alloc_instance 5 (Report) - 132 load_var 18 (top) - 133 init_field 0 (score) - 134 load_var 16 (label) - 135 init_field 1 (label) - 136 load_var 19 (breakdown) - 137 init_field 2 (breakdown) - 138 return - 139 unreachable + 64 load_var 6 (age) + 65 load_const 2 (2) + 66 bin_op * + 67 load_var 9 (role_mult) + 68 bin_op + + 69 store_var 11 (base) + 70 load_var 11 (base) + 71 load_var 9 (role_mult) + 72 cmp_op > + 73 pop_jump_if_false +2 (to 75) + 74 jump +4 (to 78) + 75 load_var 11 (base) + 76 store_var 12 (capped) + 77 jump +3 (to 80) + 78 load_var 9 (role_mult) + 79 store_var 12 (capped) + 80 load_var 12 (capped) + 81 load_var 9 (role_mult) + 82 bin_op + + 83 store_var 13 (total) + 84 load_var 13 (total) + 85 load_var 2 (threshold) + 86 cmp_op > + 87 load_const 3 (true) + 88 cmp_op == + 89 pop_jump_if_false +2 (to 91) + 90 jump +4 (to 94) + 91 load_const 4 (" [LOW]") + 92 store_var 14 (suffix) + 93 jump +3 (to 96) + 94 load_const 5 (" [HIGH]") + 95 store_var 14 (suffix) + 96 load_var 3 (name) + 97 load_var 14 (suffix) + 98 bin_op + + 99 store_var 15 (label) + 100 load_var 11 (base) + 101 load_var 12 (capped) + 102 load_var 13 (total) + 103 alloc_array 3 + 104 store_var 16 (scores) + 105 load_var 16 (scores) + 106 load_const 6 (0) + 107 load_var 13 (total) + 108 store_array_element + 109 load_var 16 (scores) + 110 load_const 6 (0) + 111 load_array_element + 112 store_var 17 (top) + 113 load_var 11 (base) + 114 load_var 9 (role_mult) + 115 load_const 7 ("base") + 116 load_const 8 ("role") + 117 alloc_map 2 + 118 store_var 18 (breakdown) + 119 alloc_instance 5 (Report) + 120 load_var 17 (top) + 121 init_field 0 (score) + 122 load_var 15 (label) + 123 init_field 1 (label) + 124 load_var 18 (breakdown) + 125 init_field 2 (breakdown) + 126 return + 127 unreachable } diff --git a/baml_language/crates/baml_tests/tests/bytecode_format/snapshots/bytecode_format__bytecode_display_textual.snap b/baml_language/crates/baml_tests/tests/bytecode_format/snapshots/bytecode_format__bytecode_display_textual.snap index 540964c3d6..865d826bc3 100644 --- a/baml_language/crates/baml_tests/tests/bytecode_format/snapshots/bytecode_format__bytecode_display_textual.snap +++ b/baml_language/crates/baml_tests/tests/bytecode_format/snapshots/bytecode_format__bytecode_display_textual.snap @@ -234,13 +234,11 @@ function testing.TestCollector.register_test(self: null, name: string, body: () load_field .name load_var full_name cmp_op == - store_var _20 - load_var _20 - load_var _20 - pop_jump_if_false L9 + jump_if_false L9 jump L10 L9: + pop 1 load_var t load_field .name load_var hash_prefix @@ -355,13 +353,11 @@ function testing.TestCollector.register_test_set(self: null, name: string, colle load_field .name load_var full_name cmp_op == - store_var _20 - load_var _20 - load_var _20 - pop_jump_if_false L9 + jump_if_false L9 jump L10 L9: + pop 1 load_var ts load_field .name load_var hash_prefix @@ -669,8 +665,7 @@ function testing.TestRegistry.serialize(self: null) -> (testing.SerializedTest | call baml.Map.get store_var expanded load_var expanded - load_const testing.TestRegistry - cmp_op instanceof + is_type testing.TestRegistry pop_jump_if_false L5 jump L6 @@ -752,9 +747,7 @@ function testing.run_test(body: () -> null, runner: (() -> testing.TestReport) - L0: load_var runner - type_tag - load_const 8 - cmp_op == + is_type (() -> testing.TestReport) -> () -> testing.TestReport pop_jump_if_false L2 load_var base_run load_var runner @@ -781,9 +774,7 @@ function testing.run_testset(run_children: () -> testing.TestSetReport, runner: L0: load_var runner - type_tag - load_const 8 - cmp_op == + is_type (() -> testing.TestSetReport) -> () -> testing.TestSetReport pop_jump_if_false L2 load_var run_children load_var runner @@ -829,97 +820,44 @@ function user.User.promote(self: null, bonus: int) -> Report { function user.http_status(code: int) -> string { load_var code - copy 0 - load_const 400 - cmp_op == - pop_jump_if_false L0 - pop 1 - jump L8 + dense_tag [200, 201, 204, 400, 404, 500] + jump_table [L6, L5, L4, L3, L2, L1], default L0 L0: - copy 0 - load_const 400 - cmp_op < - pop_jump_if_false L3 - copy 0 - load_const 201 - cmp_op == - pop_jump_if_false L1 - pop 1 - jump L10 - - L1: - copy 0 - load_const 201 - cmp_op < - pop_jump_if_false L2 - copy 0 - load_const 200 - cmp_op == - pop_jump_if_false L2 - pop 1 - jump L11 - - L2: - copy 0 - load_const 204 - cmp_op == - pop_jump_if_false L3 - pop 1 - jump L9 - - L3: - copy 0 - load_const 404 - cmp_op == - pop_jump_if_false L4 - pop 1 - jump L7 - - L4: - copy 0 - load_const 500 - cmp_op == - pop_jump_if_false L5 - pop 1 - jump L6 - - L5: - pop 1 load_const "Unknown" store_var label - jump L12 + jump L7 - L6: + L1: 500 load_const "Internal Server Error" store_var label - jump L12 + jump L7 - L7: + L2: 404 load_const "Not Found" store_var label - jump L12 + jump L7 - L8: + L3: 400 load_const "Bad Request" store_var label - jump L12 + jump L7 - L9: + L4: 204 load_const "No Content" store_var label - jump L12 + jump L7 - L10: + L5: 201 load_const "Created" store_var label - jump L12 + jump L7 - L11: + L6: 200 load_const "OK" store_var label - L12: + L7: load_const "HTTP " load_var label bin_op + @@ -928,15 +866,13 @@ function user.http_status(code: int) -> string { function user.score(input: User | ErrorInfo, threshold: int) -> Report { load_var input - load_const User - cmp_op instanceof + is_type User pop_jump_if_false L0 jump L1 L0: load_var input - load_const ErrorInfo - cmp_op instanceof + is_type ErrorInfo pop_jump_if_false L2 load_var input load_field .message @@ -950,15 +886,13 @@ function user.score(input: User | ErrorInfo, threshold: int) -> Report { L2: load_var input - load_const User - cmp_op instanceof + is_type User pop_jump_if_false L3 jump L4 L3: load_var input - load_const ErrorInfo - cmp_op instanceof + is_type ErrorInfo pop_jump_if_false L5 load_var input load_field .code @@ -973,15 +907,13 @@ function user.score(input: User | ErrorInfo, threshold: int) -> Report { L5: load_var input - load_const User - cmp_op instanceof + is_type User pop_jump_if_false L6 jump L7 L6: load_var input - load_const ErrorInfo - cmp_op instanceof + is_type ErrorInfo pop_jump_if_false L12 load_var threshold store_var role_mult @@ -1042,18 +974,12 @@ function user.score(input: User | ErrorInfo, threshold: int) -> Report { load_var total load_var threshold cmp_op > - store_var _37 - load_var _37 load_const true cmp_op == pop_jump_if_false L16 jump L17 L16: - load_var _37 - load_const false - cmp_op == - pop_jump_if_false L18 load_const " [LOW]" store_var suffix jump L18 diff --git a/baml_language/crates/baml_tests/tests/exceptions.rs b/baml_language/crates/baml_tests/tests/exceptions.rs index 598555ccb7..c8d64fdcbd 100644 --- a/baml_language/crates/baml_tests/tests/exceptions.rs +++ b/baml_language/crates/baml_tests/tests/exceptions.rs @@ -153,15 +153,12 @@ async fn catch_literal_int_match() { call user.fails jump L2 load_var e - copy 0 load_const 42 cmp_op == pop_jump_if_false L0 - pop 1 jump L1 L0: - pop 1 load_var e throw_if_panic load_const 2 @@ -290,9 +287,7 @@ async fn typed_binding_string() { call user.fails jump L2 load_var e - type_tag - load_const 1 - cmp_op == + is_type string pop_jump_if_false L0 jump L1 @@ -335,9 +330,7 @@ async fn typed_binding_int() { call user.fails jump L2 load_var e - type_tag - load_const 0 - cmp_op == + is_type int pop_jump_if_false L0 jump L1 @@ -394,17 +387,13 @@ async fn typed_binding_dispatch_string_vs_int() { call user.fails jump L4 load_var e - type_tag - load_const 1 - cmp_op == + is_type string pop_jump_if_false L0 jump L3 L0: load_var e - type_tag - load_const 0 - cmp_op == + is_type int pop_jump_if_false L1 jump L2 @@ -486,9 +475,7 @@ async fn typed_binding_plus_wildcard() { call user.fails jump L2 load_var e - type_tag - load_const 1 - cmp_op == + is_type string pop_jump_if_false L0 jump L1 @@ -551,17 +538,13 @@ async fn named_binding_string_access_value() { call user.fails jump L4 load_var e - type_tag - load_const 1 - cmp_op == + is_type string pop_jump_if_false L0 jump L3 L0: load_var e - type_tag - load_const 0 - cmp_op == + is_type int pop_jump_if_false L1 jump L2 @@ -625,17 +608,13 @@ async fn named_binding_int_access_value() { call user.fails jump L4 load_var e - type_tag - load_const 1 - cmp_op == + is_type string pop_jump_if_false L0 jump L3 L0: load_var e - type_tag - load_const 0 - cmp_op == + is_type int pop_jump_if_false L1 jump L2 @@ -691,8 +670,7 @@ async fn catch_user_class_single_arm() { call user.fails jump L2 load_var e - load_const NetworkError - cmp_op instanceof + is_type NetworkError pop_jump_if_false L0 jump L1 @@ -756,15 +734,13 @@ async fn catch_two_user_classes_dispatch_first() { call user.fails jump L4 load_var e - load_const NetworkError - cmp_op instanceof + is_type NetworkError pop_jump_if_false L0 jump L3 L0: load_var e - load_const ParseError - cmp_op instanceof + is_type ParseError pop_jump_if_false L1 jump L2 @@ -853,8 +829,7 @@ async fn catch_user_class_plus_wildcard() { call user.fails jump L2 load_var e - load_const NetworkError - cmp_op instanceof + is_type NetworkError pop_jump_if_false L0 jump L1 @@ -949,22 +924,19 @@ async fn catch_three_user_classes_plus_wildcard() { call user.api jump L6 load_var e - load_const AuthError - cmp_op instanceof + is_type AuthError pop_jump_if_false L0 jump L5 L0: load_var e - load_const NotFound - cmp_op instanceof + is_type NotFound pop_jump_if_false L1 jump L4 L1: load_var e - load_const RateLimit - cmp_op instanceof + is_type RateLimit pop_jump_if_false L2 jump L3 @@ -1025,8 +997,7 @@ async fn named_class_binding_access_field() { call user.fails jump L2 load_var e - load_const NetworkError - cmp_op instanceof + is_type NetworkError pop_jump_if_false L0 jump L1 @@ -1094,15 +1065,13 @@ async fn named_class_binding_dispatch_access_fields() { call user.fails jump L4 load_var e - load_const NetworkError - cmp_op instanceof + is_type NetworkError pop_jump_if_false L0 jump L3 L0: load_var e - load_const ParseError - cmp_op instanceof + is_type ParseError pop_jump_if_false L1 jump L2 @@ -1162,8 +1131,7 @@ async fn bare_class_single_arm() { call user.fails jump L2 load_var e - load_const NetworkError - cmp_op instanceof + is_type NetworkError pop_jump_if_false L0 jump L1 @@ -1227,15 +1195,13 @@ async fn bare_class_dispatch_first() { call user.fails jump L4 load_var e - load_const NetworkError - cmp_op instanceof + is_type NetworkError pop_jump_if_false L0 jump L3 L0: load_var e - load_const ParseError - cmp_op instanceof + is_type ParseError pop_jump_if_false L1 jump L2 @@ -1324,8 +1290,7 @@ async fn bare_class_plus_wildcard() { call user.fails jump L2 load_var e - load_const NetworkError - cmp_op instanceof + is_type NetworkError pop_jump_if_false L0 jump L1 @@ -1445,8 +1410,7 @@ async fn catch_division_by_zero() { call user.divides jump L2 load_var e - load_const baml.panics.DivisionByZero - cmp_op instanceof + is_type baml.panics.DivisionByZero pop_jump_if_false L0 jump L1 @@ -1481,8 +1445,7 @@ async fn catch_index_out_of_bounds() { call user.oob jump L2 load_var e - load_const baml.panics.IndexOutOfBounds - cmp_op instanceof + is_type baml.panics.IndexOutOfBounds pop_jump_if_false L0 jump L1 @@ -1535,8 +1498,7 @@ async fn catch_map_key_not_found() { call user.bad jump L2 load_var e - load_const baml.panics.MapKeyNotFound - cmp_op instanceof + is_type baml.panics.MapKeyNotFound pop_jump_if_false L0 jump L1 @@ -1581,8 +1543,7 @@ async fn catch_negative_index_as_index_out_of_bounds() { call user.bad jump L2 load_var e - load_const baml.panics.IndexOutOfBounds - cmp_op instanceof + is_type baml.panics.IndexOutOfBounds pop_jump_if_false L0 jump L1 @@ -1630,8 +1591,7 @@ async fn named_panic_binding_division_by_zero_field() { call user.divides jump L2 load_var e - load_const baml.panics.DivisionByZero - cmp_op instanceof + is_type baml.panics.DivisionByZero pop_jump_if_false L0 jump L1 @@ -1668,8 +1628,7 @@ async fn named_panic_binding_index_out_of_bounds_fields() { call user.oob jump L2 load_var e - load_const baml.panics.IndexOutOfBounds - cmp_op instanceof + is_type baml.panics.IndexOutOfBounds pop_jump_if_false L0 jump L1 @@ -1716,8 +1675,7 @@ async fn named_panic_binding_index_out_of_bounds_length() { call user.oob jump L2 load_var e - load_const baml.panics.IndexOutOfBounds - cmp_op instanceof + is_type baml.panics.IndexOutOfBounds pop_jump_if_false L0 jump L1 @@ -1773,8 +1731,7 @@ async fn named_panic_binding_map_key_not_found_field() { call user.bad jump L2 load_var e - load_const baml.panics.MapKeyNotFound - cmp_op instanceof + is_type baml.panics.MapKeyNotFound pop_jump_if_false L0 jump L1 @@ -1931,8 +1888,7 @@ async fn panic_arm_plus_wildcard_panic_fires() { call user.risky jump L2 load_var e - load_const baml.panics.DivisionByZero - cmp_op instanceof + is_type baml.panics.DivisionByZero pop_jump_if_false L0 jump L1 @@ -2014,8 +1970,7 @@ async fn panic_arm_plus_wildcard_no_error() { call user.risky jump L2 load_var e - load_const baml.panics.DivisionByZero - cmp_op instanceof + is_type baml.panics.DivisionByZero pop_jump_if_false L0 jump L1 @@ -2086,15 +2041,13 @@ async fn user_class_plus_panic_plus_wildcard_class_fires() { call user.risky jump L4 load_var e - load_const AppError - cmp_op instanceof + is_type AppError pop_jump_if_false L0 jump L3 L0: load_var e - load_const baml.panics.DivisionByZero - cmp_op instanceof + is_type baml.panics.DivisionByZero pop_jump_if_false L1 jump L2 @@ -2178,15 +2131,13 @@ async fn user_class_plus_panic_plus_wildcard_panic_fires() { call user.risky jump L4 load_var e - load_const AppError - cmp_op instanceof + is_type AppError pop_jump_if_false L0 jump L3 L0: load_var e - load_const baml.panics.DivisionByZero - cmp_op instanceof + is_type baml.panics.DivisionByZero pop_jump_if_false L1 jump L2 @@ -2628,22 +2579,19 @@ async fn four_arms_division_by_zero_fires() { call user.risky jump L6 load_var e - load_const baml.panics.DivisionByZero - cmp_op instanceof + is_type baml.panics.DivisionByZero pop_jump_if_false L0 jump L5 L0: load_var e - load_const baml.panics.IndexOutOfBounds - cmp_op instanceof + is_type baml.panics.IndexOutOfBounds pop_jump_if_false L1 jump L4 L1: load_var e - load_const AppError - cmp_op instanceof + is_type AppError pop_jump_if_false L2 jump L3 @@ -2847,22 +2795,19 @@ async fn four_arms_no_error() { call user.risky jump L6 load_var e - load_const baml.panics.DivisionByZero - cmp_op instanceof + is_type baml.panics.DivisionByZero pop_jump_if_false L0 jump L5 L0: load_var e - load_const baml.panics.IndexOutOfBounds - cmp_op instanceof + is_type baml.panics.IndexOutOfBounds pop_jump_if_false L1 jump L4 L1: load_var e - load_const AppError - cmp_op instanceof + is_type AppError pop_jump_if_false L2 jump L3 @@ -2999,8 +2944,7 @@ async fn wrong_panic_pattern_propagates() { call user.divides jump L2 load_var e - load_const baml.panics.IndexOutOfBounds - cmp_op instanceof + is_type baml.panics.IndexOutOfBounds pop_jump_if_false L0 jump L1 @@ -3077,71 +3021,20 @@ async fn panic_alias_catches_any_panic() { function main() -> int { call user.divides - jump L9 + jump L2 load_var e - load_const baml.panics.DivisionByZero - cmp_op instanceof - pop_jump_if_false L0 - jump L8 + type_tag + jump_table [L1, L1, L1, _, _, L1, _, _, _, L1, L1, _, L1, L1], default L0 L0: - load_var e - load_const baml.panics.IndexOutOfBounds - cmp_op instanceof - pop_jump_if_false L1 - jump L8 - - L1: - load_var e - load_const baml.panics.MapKeyNotFound - cmp_op instanceof - pop_jump_if_false L2 - jump L8 - - L2: - load_var e - load_const baml.panics.StackOverflow - cmp_op instanceof - pop_jump_if_false L3 - jump L8 - - L3: - load_var e - load_const baml.panics.AssertionFailed - cmp_op instanceof - pop_jump_if_false L4 - jump L8 - - L4: - load_var e - load_const baml.panics.Unreachable - cmp_op instanceof - pop_jump_if_false L5 - jump L8 - - L5: - load_var e - load_const baml.panics.UserPanic - cmp_op instanceof - pop_jump_if_false L6 - jump L8 - - L6: - load_var e - load_const baml.panics.AllocFailure - cmp_op instanceof - pop_jump_if_false L7 - jump L8 - - L7: load_var e throw - L8: + L1: Unreachable load_const 1 unary_op - - L9: + L2: return } "); @@ -3169,72 +3062,21 @@ async fn panic_alias_plus_wildcard_dispatch() { function main() -> int { load_const 0 call user.risky - jump L9 + jump L2 load_var e - load_const baml.panics.DivisionByZero - cmp_op instanceof - pop_jump_if_false L0 - jump L8 + type_tag + jump_table [L1, L1, L1, _, _, L1, _, _, _, L1, L1, _, L1, L1], default L0 L0: - load_var e - load_const baml.panics.IndexOutOfBounds - cmp_op instanceof - pop_jump_if_false L1 - jump L8 - - L1: - load_var e - load_const baml.panics.MapKeyNotFound - cmp_op instanceof - pop_jump_if_false L2 - jump L8 - - L2: - load_var e - load_const baml.panics.StackOverflow - cmp_op instanceof - pop_jump_if_false L3 - jump L8 - - L3: - load_var e - load_const baml.panics.AssertionFailed - cmp_op instanceof - pop_jump_if_false L4 - jump L8 - - L4: - load_var e - load_const baml.panics.Unreachable - cmp_op instanceof - pop_jump_if_false L5 - jump L8 - - L5: - load_var e - load_const baml.panics.UserPanic - cmp_op instanceof - pop_jump_if_false L6 - jump L8 - - L6: - load_var e - load_const baml.panics.AllocFailure - cmp_op instanceof - pop_jump_if_false L7 - jump L8 - - L7: load_var e throw_if_panic load_const 2 - jump L9 + jump L2 - L8: + L1: Unreachable load_const 1 - L9: + L2: return } @@ -3350,8 +3192,7 @@ async fn nested_inner_catches_panic_outer_catches_rethrow() { store_var x jump L2 load_var e - load_const baml.panics.DivisionByZero - cmp_op instanceof + is_type baml.panics.DivisionByZero pop_jump_if_false L0 jump L1 @@ -3512,15 +3353,12 @@ async fn throw_in_match_arm_propagates() { insta::assert_snapshot!(output.bytecode, @r#" function main() -> string { load_const 2 - copy 0 load_const 1 cmp_op == pop_jump_if_false L0 - pop 1 jump L1 L0: - pop 1 load_const "boom" throw @@ -3553,8 +3391,7 @@ async fn caught_panic_has_accessible_fields() { call user.oob jump L2 load_var e - load_const baml.panics.IndexOutOfBounds - cmp_op instanceof + is_type baml.panics.IndexOutOfBounds pop_jump_if_false L0 jump L1 @@ -3584,186 +3421,521 @@ async fn caught_panic_has_accessible_fields() { } // ============================================================================ -// §11 — Optimized catch dispatch (OptLevel::Two bytecode snapshots) +// §12 — Catch switch optimization: TypeTag dispatch for typed catch arms // ============================================================================ +/// 4+ typed catch arms (no wildcard) → type_tag + jump_table dispatch. +/// Without wildcard, the default branch rethrows. #[tokio::test] -async fn catch_four_primitive_type_arms() { - // 4 primitive type arms: string, int, bool, null + wildcard. - let output = baml_test_optimized!( +async fn catch_four_typed_arms_jump_table() { + let output = baml_test!( r#" - function fails() -> int { - throw "oops" + class ErrA { x int } + class ErrB { x int } + class ErrC { x int } + class ErrD { x int } + + function risky(mode: int) -> int { + if (mode == 0) { throw ErrA { x: 1 } } + if (mode == 1) { throw ErrB { x: 2 } } + if (mode == 2) { throw ErrC { x: 3 } } + throw ErrD { x: 4 } } function main() -> int { - fails() catch (e) { - _: string => 1, - _: int => 2, - _: bool => 3, - _: null => 4, - _ => 0 + risky(2) catch (e) { + _: ErrA => 10, + _: ErrB => 20, + _: ErrC => 30, + _: ErrD => 40 } } "# ); insta::assert_snapshot!(output.bytecode, @r#" - function fails() -> int { - load_const "oops" - throw - } - function main() -> int { - call user.fails - jump L8 + load_const 2 + call user.risky + jump L5 load_var e type_tag - load_const 1 - cmp_op == - pop_jump_if_false L0 - jump L7 + jump_table [L1, L2, L4, _, L3], default L0 L0: load_var e - type_tag + throw + + L1: ErrD + load_const 40 + jump L5 + + L2: ErrC + load_const 30 + jump L5 + + L3: ErrB + load_const 20 + jump L5 + + L4: ErrA + load_const 10 + + L5: + return + } + + function risky(mode: int) -> int { + load_var mode load_const 0 cmp_op == + pop_jump_if_false L0 + jump L5 + + L0: + load_var mode + load_const 1 + cmp_op == pop_jump_if_false L1 - jump L6 + jump L4 L1: - load_var e - type_tag + load_var mode load_const 2 cmp_op == pop_jump_if_false L2 - jump L5 + jump L3 L2: - load_var e - type_tag - load_const 3 - cmp_op == - pop_jump_if_false L3 - jump L4 - - L3: - load_var e - throw_if_panic - load_const 0 - jump L8 - - L4: + alloc_instance ErrD load_const 4 - jump L8 + init_field .x + throw - L5: + L3: + alloc_instance ErrC load_const 3 - jump L8 + init_field .x + throw - L6: + L4: + alloc_instance ErrB load_const 2 - jump L8 + init_field .x + throw - L7: + L5: + alloc_instance ErrA load_const 1 - - L8: - return + init_field .x + throw } "#); - - assert_eq!(output.result, Ok(BexExternalValue::Int(1))); + assert_eq!(output.result, Ok(BexExternalValue::Int(30))); } +/// 4+ typed catch arms with wildcard → type_tag + jump_table + throw_if_panic. #[tokio::test] -async fn catch_four_primitive_wildcard_on_float() { - // Throw a float — no arm matches, falls through to wildcard. - let output = baml_test_optimized!( +async fn catch_four_typed_arms_plus_wildcard_jump_table() { + let output = baml_test!( r#" - function fails() -> int { - throw 3.14 + class ErrA { x int } + class ErrB { x int } + class ErrC { x int } + class ErrD { x int } + + function risky(mode: int) -> int { + if (mode == 0) { throw ErrA { x: 1 } } + if (mode == 1) { throw ErrB { x: 2 } } + if (mode == 2) { throw ErrC { x: 3 } } + if (mode == 3) { throw ErrD { x: 4 } } + throw "unknown" } function main() -> int { - fails() catch (e) { - _: string => 1, - _: int => 2, - _: bool => 3, - _: null => 4, - _ => 0 + risky(4) catch (e) { + _: ErrA => 10, + _: ErrB => 20, + _: ErrC => 30, + _: ErrD => 40, + _ => 99 } } "# ); - insta::assert_snapshot!(output.bytecode, @" - function fails() -> int { - load_const 3.14 - throw - } - + insta::assert_snapshot!(output.bytecode, @r#" function main() -> int { - call user.fails - jump L8 + load_const 4 + call user.risky + jump L5 load_var e type_tag - load_const 1 - cmp_op == - pop_jump_if_false L0 - jump L7 + jump_table [L1, L2, L4, _, L3], default L0 L0: load_var e - type_tag - load_const 0 - cmp_op == - pop_jump_if_false L1 - jump L6 + throw_if_panic + load_const 99 + jump L5 - L1: - load_var e - type_tag - load_const 2 - cmp_op == - pop_jump_if_false L2 + L1: ErrD + load_const 40 jump L5 - L2: - load_var e - type_tag - load_const 3 - cmp_op == - pop_jump_if_false L3 - jump L4 + L2: ErrC + load_const 30 + jump L5 - L3: - load_var e - throw_if_panic + L3: ErrB + load_const 20 + jump L5 + + L4: ErrA + load_const 10 + + L5: + return + } + + function risky(mode: int) -> int { + load_var mode load_const 0 - jump L8 + cmp_op == + pop_jump_if_false L0 + jump L7 + + L0: + load_var mode + load_const 1 + cmp_op == + pop_jump_if_false L1 + jump L6 + + L1: + load_var mode + load_const 2 + cmp_op == + pop_jump_if_false L2 + jump L5 + + L2: + load_var mode + load_const 3 + cmp_op == + pop_jump_if_false L3 + jump L4 + + L3: + load_const "unknown" + throw L4: + alloc_instance ErrD load_const 4 - jump L8 + init_field .x + throw L5: + alloc_instance ErrC load_const 3 - jump L8 + init_field .x + throw L6: + alloc_instance ErrB load_const 2 - jump L8 + init_field .x + throw L7: + alloc_instance ErrA load_const 1 + init_field .x + throw + } + "#); + assert_eq!(output.result, Ok(BexExternalValue::Int(99))); +} - L8: +/// 2 typed catch arms → sequential is_type chain (below 4-arm switch threshold). +#[tokio::test] +async fn catch_two_typed_arms_sequential_chain() { + let output = baml_test!( + r#" + class NetworkError { url string } + class ParseError { msg string } + + function fails(mode: int) -> int { + if (mode == 0) { throw NetworkError { url: "http://x" } } + throw ParseError { msg: "bad" } + } + + function main() -> int { + fails(1) catch (e) { + _: NetworkError => 1, + _: ParseError => 2 + } + } + "# + ); + + insta::assert_snapshot!(output.bytecode, @r#" + function fails(mode: int) -> int { + load_var mode + load_const 0 + cmp_op == + pop_jump_if_false L0 + jump L1 + + L0: + alloc_instance ParseError + load_const "bad" + init_field .msg + throw + + L1: + alloc_instance NetworkError + load_const "http://x" + init_field .url + throw + } + + function main() -> int { + load_const 1 + call user.fails + jump L4 + load_var e + is_type NetworkError + pop_jump_if_false L0 + jump L3 + + L0: + load_var e + is_type ParseError + pop_jump_if_false L1 + jump L2 + + L1: + load_var e + throw + + L2: + load_const 2 + jump L4 + + L3: + load_const 1 + + L4: return } - "); + "#); + assert_eq!(output.result, Ok(BexExternalValue::Int(2))); +} + +/// Mixed literal + typed catch arms → no switch optimization (falls back to chain). +#[tokio::test] +async fn catch_mixed_literal_and_typed_no_switch() { + let output = baml_test!( + r#" + class AppError { code int } + + function fails(mode: int) -> int { + if (mode == 0) { throw "boom" } + throw AppError { code: 404 } + } + + function main() -> int { + fails(0) catch (e) { + "boom" => 1, + _: AppError => 2, + _ => 3 + } + } + "# + ); + + insta::assert_snapshot!(output.bytecode, @r#" + function fails(mode: int) -> int { + load_var mode + load_const 0 + cmp_op == + pop_jump_if_false L0 + jump L1 + + L0: + alloc_instance AppError + load_const 404 + init_field .code + throw + + L1: + load_const "boom" + throw + } + + function main() -> int { + load_const 0 + call user.fails + jump L4 + load_var e + load_const "boom" + cmp_op == + pop_jump_if_false L0 + jump L3 + + L0: + load_var e + is_type AppError + pop_jump_if_false L1 + jump L2 + L1: + load_var e + throw_if_panic + load_const 3 + jump L4 + + L2: + load_const 2 + jump L4 + + L3: + load_const 1 + + L4: + return + } + "#); + assert_eq!(output.result, Ok(BexExternalValue::Int(1))); +} + +// ============================================================================ +// §13 — Optimized catch dispatch (from canary: primitive TypeTag + instanceof tests) +// ============================================================================ + +#[tokio::test] +async fn catch_four_primitive_type_arms() { + // 4 primitive type arms: string, int, bool, null + wildcard. + let output = baml_test_optimized!( + r#" + function fails() -> int { + throw "oops" + } + + function main() -> int { + fails() catch (e) { + _: string => 1, + _: int => 2, + _: bool => 3, + _: null => 4, + _ => 0 + } + } + "# + ); + + insta::assert_snapshot!(output.bytecode, @r#" + function fails() -> int { + load_const "oops" + throw + } + + function main() -> int { + call user.fails + jump L5 + load_var e + type_tag + jump_table [L3, L4, L2, L1], default L0 + + L0: + load_var e + throw_if_panic + load_const 0 + jump L5 + + L1: null + load_const 4 + jump L5 + + L2: bool + load_const 3 + jump L5 + + L3: int + load_const 2 + jump L5 + + L4: string + load_const 1 + + L5: + return + } + "#); + assert_eq!(output.result, Ok(BexExternalValue::Int(1))); +} + +#[tokio::test] +async fn catch_four_primitive_wildcard_on_float() { + // Throw a float — no arm matches, falls through to wildcard. + let output = baml_test_optimized!( + r#" + function fails() -> int { + throw 3.14 + } + + function main() -> int { + fails() catch (e) { + _: string => 1, + _: int => 2, + _: bool => 3, + _: null => 4, + _ => 0 + } + } + "# + ); + + insta::assert_snapshot!(output.bytecode, @r#" + function fails() -> int { + load_const 3.14 + throw + } + + function main() -> int { + call user.fails + jump L5 + load_var e + type_tag + jump_table [L3, L4, L2, L1], default L0 + + L0: + load_var e + throw_if_panic + load_const 0 + jump L5 + + L1: null + load_const 4 + jump L5 + + L2: bool + load_const 3 + jump L5 + + L3: int + load_const 2 + jump L5 + + L4: string + load_const 1 + + L5: + return + } + "#); assert_eq!(output.result, Ok(BexExternalValue::Int(0))); } @@ -3787,7 +3959,7 @@ async fn catch_three_type_arms_bool_throw() { "# ); - insta::assert_snapshot!(output.bytecode, @" + insta::assert_snapshot!(output.bytecode, @r#" function fails() -> int { load_const true throw @@ -3797,25 +3969,19 @@ async fn catch_three_type_arms_bool_throw() { call user.fails jump L6 load_var e - type_tag - load_const 1 - cmp_op == + is_type string pop_jump_if_false L0 jump L5 L0: load_var e - type_tag - load_const 0 - cmp_op == + is_type int pop_jump_if_false L1 jump L4 L1: load_var e - type_tag - load_const 2 - cmp_op == + is_type bool pop_jump_if_false L2 jump L3 @@ -3839,15 +4005,13 @@ async fn catch_three_type_arms_bool_throw() { L6: return } - "); - + "#); assert_eq!(output.result, Ok(BexExternalValue::Int(3))); } #[tokio::test] async fn catch_four_user_classes_instanceof_chain() { - // User class arms use instanceof checks, not type-tag dispatch. - // Even with 4+ arms, these remain sequential instanceof chains. + // User class arms — with this branch, 4+ class arms use TypeTag dispatch. let output = baml_test_optimized!( r#" class AuthError { reason: string } @@ -3936,60 +4100,36 @@ async fn catch_four_user_classes_instanceof_chain() { function main() -> int { load_const 2 call user.api - jump L8 - load_var e - load_const AuthError - cmp_op instanceof - pop_jump_if_false L0 - jump L7 - - L0: - load_var e - load_const NotFound - cmp_op instanceof - pop_jump_if_false L1 - jump L6 - - L1: - load_var e - load_const RateLimit - cmp_op instanceof - pop_jump_if_false L2 jump L5 - - L2: load_var e - load_const Timeout - cmp_op instanceof - pop_jump_if_false L3 - jump L4 + type_tag + jump_table [L3, L2, _, _, L4, L1], default L0 - L3: + L0: load_var e throw_if_panic load_const 0 - jump L8 + jump L5 - L4: + L1: Timeout load_const 4 - jump L8 + jump L5 - L5: + L2: RateLimit load_const 3 - jump L8 + jump L5 - L6: + L3: NotFound load_const 2 - jump L8 + jump L5 - L7: + L4: AuthError load_const 1 - L8: + L5: return } "#); - assert_eq!(output.result, Ok(BexExternalValue::Int(3))); } @@ -4112,7 +4252,6 @@ async fn catch_four_literal_arms() { return } "#); - assert_eq!(output.result, Ok(BexExternalValue::Int(20))); } @@ -4153,63 +4292,39 @@ async fn catch_mixed_named_and_anonymous_bindings() { function main() -> string { call user.fails - jump L8 - load_var e - load_const NetworkError - cmp_op instanceof - pop_jump_if_false L0 - jump L7 - - L0: - load_var e - load_const AuthError - cmp_op instanceof - pop_jump_if_false L1 - jump L6 - - L1: - load_var e - load_const NotFound - cmp_op instanceof - pop_jump_if_false L2 jump L5 - - L2: load_var e - load_const RateLimit - cmp_op instanceof - pop_jump_if_false L3 - jump L4 + type_tag + jump_table [L2, _, L1, L4, _, _, L3], default L0 - L3: + L0: load_var e throw_if_panic load_const "unknown" - jump L8 + jump L5 - L4: + L1: RateLimit load_const "rate limited" - jump L8 + jump L5 - L5: + L2: NotFound load_var e load_field .path - jump L8 + jump L5 - L6: + L3: AuthError load_var e load_field .reason - jump L8 + jump L5 - L7: + L4: NetworkError load_var e load_field .url - L8: + L5: return } "#); - assert_eq!( output.result, Ok(BexExternalValue::String("http://example.com".into())) diff --git a/baml_language/crates/baml_tests/tests/if_else.rs b/baml_language/crates/baml_tests/tests/if_else.rs index 30baea81f0..8dc07b8082 100644 --- a/baml_language/crates/baml_tests/tests/if_else.rs +++ b/baml_language/crates/baml_tests/tests/if_else.rs @@ -784,14 +784,11 @@ async fn if_else_logical_and() { insta::assert_snapshot!(output.bytecode, @" function main() -> int { load_const true - store_var _1 - load_const true - pop_jump_if_false L0 + jump_if_false L0 + pop 1 load_const true - store_var _1 L0: - load_var _1 pop_jump_if_false L1 jump L2 @@ -823,17 +820,14 @@ async fn if_else_logical_or() { insta::assert_snapshot!(output.bytecode, @" function main() -> int { load_const false - store_var _1 - load_const false - pop_jump_if_false L0 + jump_if_false L0 jump L1 L0: + pop 1 load_const true - store_var _1 L1: - load_var _1 pop_jump_if_false L2 jump L3 diff --git a/baml_language/crates/baml_tests/tests/instanceof.rs b/baml_language/crates/baml_tests/tests/instanceof.rs deleted file mode 100644 index c1722f4e1b..0000000000 --- a/baml_language/crates/baml_tests/tests/instanceof.rs +++ /dev/null @@ -1,71 +0,0 @@ -//! Tests that the `instanceof` operator produces a compile error -//! suggesting `match` instead. - -use std::path::Path; - -use baml_compiler_diagnostics::Severity; -use baml_project::ProjectDatabase; - -fn get_errors(source: &str) -> Vec { - let mut db = ProjectDatabase::new(); - db.set_project_root(Path::new(".")); - db.add_file("test.baml", source); - - let project = db.get_project().expect("project must be set"); - let all_files = db.get_source_files(); - let diagnostics = baml_project::collect_diagnostics(&db, project, &all_files); - diagnostics - .iter() - .filter(|d| matches!(d.severity, Severity::Error)) - .map(|d| d.message.clone()) - .collect() -} - -#[test] -#[ignore = "compiler2: instanceof produces different error count/messages than expected"] -fn instanceof_produces_error() { - let errors = get_errors( - " - class StopTool { - action string - } - - function main() -> bool { - let t = StopTool { action: \"stop\" }; - t instanceof StopTool - } - ", - ); - - assert_eq!(errors.len(), 1); - assert!(errors[0].contains("instanceof")); - assert!(errors[0].contains("match")); -} - -#[test] -#[ignore = "compiler2: instanceof produces different error count/messages than expected"] -fn instanceof_in_if_produces_error() { - let errors = get_errors( - " - class Foo { - field string - } - - class Bar { - other int - } - - function main() -> string { - let x = Foo { field: \"test\" }; - if (x instanceof Foo) { - return x.field; - } else { - return \"not foo\"; - } - } - ", - ); - - assert!(!errors.is_empty()); - assert!(errors.iter().any(|e| e.contains("instanceof"))); -} diff --git a/baml_language/crates/baml_tests/tests/match_basics.rs b/baml_language/crates/baml_tests/tests/match_basics.rs index a96e8a9e91..000400b8e9 100644 --- a/baml_language/crates/baml_tests/tests/match_basics.rs +++ b/baml_language/crates/baml_tests/tests/match_basics.rs @@ -102,23 +102,19 @@ async fn match_literal_int_first_arm() { insta::assert_snapshot!(output.bytecode, @" function main() -> int { load_const 1 - copy 0 load_const 1 cmp_op == pop_jump_if_false L0 - pop 1 jump L3 L0: - copy 0 + load_const 1 load_const 2 cmp_op == pop_jump_if_false L1 - pop 1 jump L2 L1: - pop 1 load_const 0 jump L4 @@ -155,23 +151,19 @@ async fn match_literal_int_second_arm() { insta::assert_snapshot!(output.bytecode, @" function main() -> int { load_const 2 - copy 0 load_const 1 cmp_op == pop_jump_if_false L0 - pop 1 jump L3 L0: - copy 0 + load_const 2 load_const 2 cmp_op == pop_jump_if_false L1 - pop 1 jump L2 L1: - pop 1 load_const 0 jump L4 @@ -208,23 +200,19 @@ async fn match_literal_int_fallback() { insta::assert_snapshot!(output.bytecode, @" function main() -> int { load_const 999 - copy 0 load_const 1 cmp_op == pop_jump_if_false L0 - pop 1 jump L3 L0: - copy 0 + load_const 999 load_const 2 cmp_op == pop_jump_if_false L1 - pop 1 jump L2 L1: - pop 1 load_const 0 jump L4 @@ -257,8 +245,6 @@ async fn match_literal_int_single_arm() { insta::assert_snapshot!(output.bytecode, @" function main() -> int { - load_const 1 - pop 1 load_const 100 return } @@ -282,12 +268,6 @@ async fn match_literal_null() { insta::assert_snapshot!(output.bytecode, @r#" function main() -> string { - load_const null - load_const null - cmp_op == - pop_jump_if_false L0 - - L0: load_const "was null" return } @@ -322,10 +302,6 @@ async fn match_literal_bool_true() { jump L1 L0: - load_const true - load_const false - cmp_op == - pop_jump_if_false L2 load_const "no" jump L2 @@ -366,10 +342,6 @@ async fn match_literal_bool_false() { jump L1 L0: - load_const false - load_const false - cmp_op == - pop_jump_if_false L2 load_const "no" jump L2 @@ -402,12 +374,6 @@ async fn match_literal_bool_exhaustive_constant() { insta::assert_snapshot!(output.bytecode, @r#" function main() -> string { - load_const true - load_const true - cmp_op == - pop_jump_if_false L0 - - L0: load_const "yes" return } @@ -441,54 +407,21 @@ async fn match_union_literal_first() { insta::assert_snapshot!(output.bytecode, @r#" function main() -> string { load_const 200 - copy 0 - load_const 400 - cmp_op == - pop_jump_if_false L0 - pop 1 - jump L4 + dense_tag [200, 201, 400, 404] + jump_table [L2, L2, L1, L1], default L0 L0: - copy 0 - load_const 400 - cmp_op < - pop_jump_if_false L2 - copy 0 - load_const 200 - cmp_op == - pop_jump_if_false L1 - pop 1 - jump L5 - - L1: - copy 0 - load_const 201 - cmp_op == - pop_jump_if_false L2 - pop 1 - jump L5 - - L2: - copy 0 - load_const 404 - cmp_op == - pop_jump_if_false L3 - pop 1 - jump L4 - - L3: - pop 1 load_const "other" - jump L6 + jump L3 - L4: + L1: 404 load_const "client error" - jump L6 + jump L3 - L5: + L2: 201 load_const "success" - L6: + L3: return } "#); @@ -517,54 +450,21 @@ async fn match_union_literal_second() { insta::assert_snapshot!(output.bytecode, @r#" function main() -> string { load_const 201 - copy 0 - load_const 400 - cmp_op == - pop_jump_if_false L0 - pop 1 - jump L4 + dense_tag [200, 201, 400, 404] + jump_table [L2, L2, L1, L1], default L0 L0: - copy 0 - load_const 400 - cmp_op < - pop_jump_if_false L2 - copy 0 - load_const 200 - cmp_op == - pop_jump_if_false L1 - pop 1 - jump L5 - - L1: - copy 0 - load_const 201 - cmp_op == - pop_jump_if_false L2 - pop 1 - jump L5 - - L2: - copy 0 - load_const 404 - cmp_op == - pop_jump_if_false L3 - pop 1 - jump L4 - - L3: - pop 1 load_const "other" - jump L6 + jump L3 - L4: + L1: 404 load_const "client error" - jump L6 + jump L3 - L5: + L2: 201 load_const "success" - L6: + L3: return } "#); @@ -594,134 +494,25 @@ async fn match_union_large() { insta::assert_snapshot!(output.bytecode, @r#" function main() -> string { load_const 204 - copy 0 - load_const 403 - cmp_op == - pop_jump_if_false L0 - pop 1 - jump L13 + dense_tag [200, 201, 202, 204, 400, 401, 403, 404, 500, 501, 502, 503] + jump_table [L3, L3, L3, L3, L2, L2, L2, L2, L1, L1, L1, L1], default L0 L0: - copy 0 - load_const 403 - cmp_op < - pop_jump_if_false L6 - copy 0 - load_const 204 - cmp_op == - pop_jump_if_false L1 - pop 1 - jump L14 - - L1: - copy 0 - load_const 204 - cmp_op < - pop_jump_if_false L4 - copy 0 - load_const 201 - cmp_op == - pop_jump_if_false L2 - pop 1 - jump L14 - - L2: - copy 0 - load_const 201 - cmp_op < - pop_jump_if_false L3 - copy 0 - load_const 200 - cmp_op == - pop_jump_if_false L3 - pop 1 - jump L14 - - L3: - copy 0 - load_const 202 - cmp_op == - pop_jump_if_false L4 - pop 1 - jump L14 - - L4: - copy 0 - load_const 400 - cmp_op == - pop_jump_if_false L5 - pop 1 - jump L13 - - L5: - copy 0 - load_const 401 - cmp_op == - pop_jump_if_false L6 - pop 1 - jump L13 - - L6: - copy 0 - load_const 501 - cmp_op == - pop_jump_if_false L7 - pop 1 - jump L12 - - L7: - copy 0 - load_const 501 - cmp_op < - pop_jump_if_false L9 - copy 0 - load_const 404 - cmp_op == - pop_jump_if_false L8 - pop 1 - jump L13 - - L8: - copy 0 - load_const 500 - cmp_op == - pop_jump_if_false L9 - pop 1 - jump L12 - - L9: - copy 0 - load_const 502 - cmp_op == - pop_jump_if_false L10 - pop 1 - jump L12 - - L10: - copy 0 - load_const 503 - cmp_op == - pop_jump_if_false L11 - pop 1 - jump L12 - - L11: - pop 1 load_const "other" - jump L15 + jump L4 - L12: + L1: 503 load_const "server error" - jump L15 + jump L4 - L13: + L2: 404 load_const "client error" - jump L15 + jump L4 - L14: + L3: 204 load_const "success" - L15: + L4: return } "#); @@ -751,134 +542,25 @@ async fn match_union_client_error() { insta::assert_snapshot!(output.bytecode, @r#" function main() -> string { load_const 404 - copy 0 - load_const 403 - cmp_op == - pop_jump_if_false L0 - pop 1 - jump L13 + dense_tag [200, 201, 202, 204, 400, 401, 403, 404, 500, 501, 502, 503] + jump_table [L3, L3, L3, L3, L2, L2, L2, L2, L1, L1, L1, L1], default L0 L0: - copy 0 - load_const 403 - cmp_op < - pop_jump_if_false L6 - copy 0 - load_const 204 - cmp_op == - pop_jump_if_false L1 - pop 1 - jump L14 - - L1: - copy 0 - load_const 204 - cmp_op < - pop_jump_if_false L4 - copy 0 - load_const 201 - cmp_op == - pop_jump_if_false L2 - pop 1 - jump L14 - - L2: - copy 0 - load_const 201 - cmp_op < - pop_jump_if_false L3 - copy 0 - load_const 200 - cmp_op == - pop_jump_if_false L3 - pop 1 - jump L14 - - L3: - copy 0 - load_const 202 - cmp_op == - pop_jump_if_false L4 - pop 1 - jump L14 - - L4: - copy 0 - load_const 400 - cmp_op == - pop_jump_if_false L5 - pop 1 - jump L13 - - L5: - copy 0 - load_const 401 - cmp_op == - pop_jump_if_false L6 - pop 1 - jump L13 - - L6: - copy 0 - load_const 501 - cmp_op == - pop_jump_if_false L7 - pop 1 - jump L12 - - L7: - copy 0 - load_const 501 - cmp_op < - pop_jump_if_false L9 - copy 0 - load_const 404 - cmp_op == - pop_jump_if_false L8 - pop 1 - jump L13 - - L8: - copy 0 - load_const 500 - cmp_op == - pop_jump_if_false L9 - pop 1 - jump L12 - - L9: - copy 0 - load_const 502 - cmp_op == - pop_jump_if_false L10 - pop 1 - jump L12 - - L10: - copy 0 - load_const 503 - cmp_op == - pop_jump_if_false L11 - pop 1 - jump L12 - - L11: - pop 1 load_const "other" - jump L15 + jump L4 - L12: + L1: 503 load_const "server error" - jump L15 + jump L4 - L13: + L2: 404 load_const "client error" - jump L15 + jump L4 - L14: + L3: 204 load_const "success" - L15: + L4: return } "#); @@ -907,31 +589,26 @@ async fn match_union_with_duplicates() { insta::assert_snapshot!(output.bytecode, @r#" function main() -> string { load_const 1 - copy 0 load_const 1 cmp_op == pop_jump_if_false L0 - pop 1 jump L4 L0: - copy 0 + load_const 1 load_const 2 cmp_op == pop_jump_if_false L1 - pop 1 jump L4 L1: - copy 0 + load_const 1 load_const 3 cmp_op == pop_jump_if_false L2 - pop 1 jump L3 L2: - pop 1 load_const "other" jump L5 @@ -975,23 +652,19 @@ async fn match_as_expression_in_arithmetic() { insta::assert_snapshot!(output.bytecode, @" function main() -> int { load_const 2 - copy 0 load_const 1 cmp_op == pop_jump_if_false L0 - pop 1 jump L3 L0: - copy 0 + load_const 2 load_const 2 cmp_op == pop_jump_if_false L1 - pop 1 jump L2 L1: - pop 1 load_const 0 jump L4 @@ -1035,23 +708,19 @@ async fn match_nested() { insta::assert_snapshot!(output.bytecode, @" function main() -> int { load_const 1 - copy 0 load_const 1 cmp_op == pop_jump_if_false L0 - pop 1 jump L3 L0: - copy 0 + load_const 1 load_const 2 cmp_op == pop_jump_if_false L1 - pop 1 jump L2 L1: - pop 1 load_const 0 jump L8 @@ -1061,23 +730,19 @@ async fn match_nested() { L3: load_const 2 - copy 0 load_const 1 cmp_op == pop_jump_if_false L4 - pop 1 jump L7 L4: - copy 0 + load_const 2 load_const 2 cmp_op == pop_jump_if_false L5 - pop 1 jump L6 L5: - pop 1 load_const 10 jump L8 @@ -1387,9 +1052,7 @@ async fn match_string_literal_with_typed_fallback() { L1: load_var s - type_tag - load_const 1 - cmp_op == + is_type string pop_jump_if_false L4 load_const 0 jump L4 @@ -1701,23 +1364,20 @@ async fn match_negative_int_with_variable() { function main() -> string { load_const 1 unary_op - - copy 0 load_const -1 cmp_op == pop_jump_if_false L0 - pop 1 jump L3 L0: - copy 0 + load_const 1 + unary_op - load_const 0 cmp_op == pop_jump_if_false L1 - pop 1 jump L2 L1: - pop 1 load_const "other" jump L4 @@ -1967,43 +1627,34 @@ async fn match_three_levels_nested() { insta::assert_snapshot!(output.bytecode, @r#" function classify(x: int, y: int, z: int) -> string { load_var x - copy 0 load_const 0 cmp_op == pop_jump_if_false L0 - pop 1 jump L1 L0: - pop 1 load_const "x nonzero" jump L6 L1: load_var y - copy 0 load_const 0 cmp_op == pop_jump_if_false L2 - pop 1 jump L3 L2: - pop 1 load_const "y nonzero" jump L6 L3: load_var z - copy 0 load_const 0 cmp_op == pop_jump_if_false L4 - pop 1 jump L5 L4: - pop 1 load_const "z nonzero" jump L6 @@ -2054,43 +1705,34 @@ async fn match_three_levels_nested_middle() { insta::assert_snapshot!(output.bytecode, @r#" function classify(x: int, y: int, z: int) -> string { load_var x - copy 0 load_const 0 cmp_op == pop_jump_if_false L0 - pop 1 jump L1 L0: - pop 1 load_const "x nonzero" jump L6 L1: load_var y - copy 0 load_const 0 cmp_op == pop_jump_if_false L2 - pop 1 jump L3 L2: - pop 1 load_const "y nonzero" jump L6 L3: load_var z - copy 0 load_const 0 cmp_op == pop_jump_if_false L4 - pop 1 jump L5 L4: - pop 1 load_const "z nonzero" jump L6 @@ -2152,9 +1794,7 @@ async fn match_optional_null_pattern() { L0: load_var x - type_tag - load_const 0 - cmp_op == + is_type int pop_jump_if_false L2 load_const "some" jump L2 @@ -2205,9 +1845,7 @@ async fn match_optional_value_pattern() { L0: load_var x - type_tag - load_const 0 - cmp_op == + is_type int pop_jump_if_false L2 load_const "some" jump L2 @@ -2266,9 +1904,7 @@ async fn match_optional_with_literal_and_typed() { L1: load_var x - type_tag - load_const 0 - cmp_op == + is_type int pop_jump_if_false L4 load_const "other" jump L4 @@ -2317,23 +1953,21 @@ async fn match_arithmetic_scrutinee() { load_var a load_var b bin_op + - copy 0 load_const 0 cmp_op == pop_jump_if_false L0 - pop 1 jump L3 L0: - copy 0 + load_var a + load_var b + bin_op + load_const 1 cmp_op == pop_jump_if_false L1 - pop 1 jump L2 L1: - pop 1 load_const "other" jump L4 @@ -2386,15 +2020,12 @@ async fn match_function_call_scrutinee() { insta::assert_snapshot!(output.bytecode, @r#" function classify() -> string { call user.helper - copy 0 load_const 42 cmp_op == pop_jump_if_false L0 - pop 1 jump L1 L0: - pop 1 load_const "other" jump L2 @@ -2653,8 +2284,7 @@ async fn match_mixed_instanceof_and_literal() { init_field .code store_var x load_var x - load_const Result - cmp_op instanceof + is_type Result pop_jump_if_false L0 load_var x load_field .code @@ -2696,10 +2326,6 @@ async fn match_bool_variable_exhaustive() { jump L1 L0: - load_var flag - load_const false - cmp_op == - pop_jump_if_false L2 load_const "no" jump L2 diff --git a/baml_language/crates/baml_tests/tests/match_optimization.rs b/baml_language/crates/baml_tests/tests/match_optimization.rs index 20b1c7684d..f583503689 100644 --- a/baml_language/crates/baml_tests/tests/match_optimization.rs +++ b/baml_language/crates/baml_tests/tests/match_optimization.rs @@ -552,62 +552,29 @@ async fn match_binary_search_first_arm() { insta::assert_snapshot!(output.bytecode, @" function main() -> int { load_const 0 - copy 0 - load_const 60 - cmp_op == - pop_jump_if_false L0 - pop 1 - jump L5 + dense_tag [0, 30, 60, 99] + jump_table [L4, L3, L2, L1], default L0 L0: - copy 0 - load_const 60 - cmp_op < - pop_jump_if_false L2 - copy 0 - load_const 0 - cmp_op == - pop_jump_if_false L1 - pop 1 - jump L7 - - L1: - copy 0 - load_const 30 - cmp_op == - pop_jump_if_false L2 - pop 1 - jump L6 - - L2: - copy 0 - load_const 99 - cmp_op == - pop_jump_if_false L3 - pop 1 - jump L4 - - L3: - pop 1 load_const 999 - jump L8 + jump L5 - L4: + L1: 99 load_const 199 - jump L8 + jump L5 - L5: + L2: 60 load_const 160 - jump L8 + jump L5 - L6: + L3: 30 load_const 130 - jump L8 + jump L5 - L7: + L4: 0 load_const 100 - L8: + L5: return } "); @@ -635,62 +602,29 @@ async fn match_binary_search_middle_arm() { insta::assert_snapshot!(output.bytecode, @" function main() -> int { load_const 60 - copy 0 - load_const 60 - cmp_op == - pop_jump_if_false L0 - pop 1 - jump L5 + dense_tag [0, 30, 60, 99] + jump_table [L4, L3, L2, L1], default L0 L0: - copy 0 - load_const 60 - cmp_op < - pop_jump_if_false L2 - copy 0 - load_const 0 - cmp_op == - pop_jump_if_false L1 - pop 1 - jump L7 - - L1: - copy 0 - load_const 30 - cmp_op == - pop_jump_if_false L2 - pop 1 - jump L6 - - L2: - copy 0 - load_const 99 - cmp_op == - pop_jump_if_false L3 - pop 1 - jump L4 - - L3: - pop 1 load_const 999 - jump L8 + jump L5 - L4: + L1: 99 load_const 199 - jump L8 + jump L5 - L5: + L2: 60 load_const 160 - jump L8 + jump L5 - L6: + L3: 30 load_const 130 - jump L8 + jump L5 - L7: + L4: 0 load_const 100 - L8: + L5: return } "); @@ -718,62 +652,29 @@ async fn match_binary_search_last_arm() { insta::assert_snapshot!(output.bytecode, @" function main() -> int { load_const 99 - copy 0 - load_const 60 - cmp_op == - pop_jump_if_false L0 - pop 1 - jump L5 + dense_tag [0, 30, 60, 99] + jump_table [L4, L3, L2, L1], default L0 L0: - copy 0 - load_const 60 - cmp_op < - pop_jump_if_false L2 - copy 0 - load_const 0 - cmp_op == - pop_jump_if_false L1 - pop 1 - jump L7 - - L1: - copy 0 - load_const 30 - cmp_op == - pop_jump_if_false L2 - pop 1 - jump L6 - - L2: - copy 0 - load_const 99 - cmp_op == - pop_jump_if_false L3 - pop 1 - jump L4 - - L3: - pop 1 load_const 999 - jump L8 + jump L5 - L4: + L1: 99 load_const 199 - jump L8 + jump L5 - L5: + L2: 60 load_const 160 - jump L8 + jump L5 - L6: + L3: 30 load_const 130 - jump L8 + jump L5 - L7: + L4: 0 load_const 100 - L8: + L5: return } "); @@ -801,62 +702,29 @@ async fn match_binary_search_fallback() { insta::assert_snapshot!(output.bytecode, @" function main() -> int { load_const 50 - copy 0 - load_const 60 - cmp_op == - pop_jump_if_false L0 - pop 1 - jump L5 + dense_tag [0, 30, 60, 99] + jump_table [L4, L3, L2, L1], default L0 L0: - copy 0 - load_const 60 - cmp_op < - pop_jump_if_false L2 - copy 0 - load_const 0 - cmp_op == - pop_jump_if_false L1 - pop 1 - jump L7 - - L1: - copy 0 - load_const 30 - cmp_op == - pop_jump_if_false L2 - pop 1 - jump L6 - - L2: - copy 0 - load_const 99 - cmp_op == - pop_jump_if_false L3 - pop 1 - jump L4 - - L3: - pop 1 load_const 999 - jump L8 + jump L5 - L4: + L1: 99 load_const 199 - jump L8 + jump L5 - L5: + L2: 60 load_const 160 - jump L8 + jump L5 - L6: + L3: 30 load_const 130 - jump L8 + jump L5 - L7: + L4: 0 load_const 100 - L8: + L5: return } "); @@ -886,90 +754,37 @@ async fn match_binary_search_very_sparse() { insta::assert_snapshot!(output.bytecode, @" function main() -> int { load_const 500 - copy 0 - load_const 300 - cmp_op == - pop_jump_if_false L0 - pop 1 - jump L8 + dense_tag [0, 100, 200, 300, 400, 500] + jump_table [L6, L5, L4, L3, L2, L1], default L0 L0: - copy 0 - load_const 300 - cmp_op < - pop_jump_if_false L3 - copy 0 - load_const 100 - cmp_op == - pop_jump_if_false L1 - pop 1 - jump L10 - - L1: - copy 0 - load_const 100 - cmp_op < - pop_jump_if_false L2 - copy 0 - load_const 0 - cmp_op == - pop_jump_if_false L2 - pop 1 - jump L11 - - L2: - copy 0 - load_const 200 - cmp_op == - pop_jump_if_false L3 - pop 1 - jump L9 - - L3: - copy 0 - load_const 400 - cmp_op == - pop_jump_if_false L4 - pop 1 - jump L7 - - L4: - copy 0 - load_const 500 - cmp_op == - pop_jump_if_false L5 - pop 1 - jump L6 - - L5: - pop 1 load_const 9999 - jump L12 + jump L7 - L6: + L1: 500 load_const 1500 - jump L12 + jump L7 - L7: + L2: 400 load_const 1400 - jump L12 + jump L7 - L8: + L3: 300 load_const 1300 - jump L12 + jump L7 - L9: + L4: 200 load_const 1200 - jump L12 + jump L7 - L10: + L5: 100 load_const 1100 - jump L12 + jump L7 - L11: + L6: 0 load_const 1000 - L12: + L7: return } "); @@ -998,74 +813,33 @@ async fn match_binary_search_large_values() { insta::assert_snapshot!(output.bytecode, @" function main() -> int { load_const 750 - copy 0 - load_const 750 - cmp_op == - pop_jump_if_false L0 - pop 1 - jump L7 + dense_tag [250, 500, 750, 1000, 1250] + jump_table [L5, L4, L3, L2, L1], default L0 L0: - copy 0 - load_const 750 - cmp_op < - pop_jump_if_false L2 - copy 0 - load_const 250 - cmp_op == - pop_jump_if_false L1 - pop 1 - jump L9 - - L1: - copy 0 - load_const 500 - cmp_op == - pop_jump_if_false L2 - pop 1 - jump L8 - - L2: - copy 0 - load_const 1000 - cmp_op == - pop_jump_if_false L3 - pop 1 - jump L6 - - L3: - copy 0 - load_const 1250 - cmp_op == - pop_jump_if_false L4 - pop 1 - jump L5 - - L4: - pop 1 load_const 9999 - jump L10 + jump L6 - L5: + L1: 1250 load_const 2250 - jump L10 + jump L6 - L6: + L2: 1000 load_const 2000 - jump L10 + jump L6 - L7: + L3: 750 load_const 1750 - jump L10 + jump L6 - L8: + L4: 500 load_const 1500 - jump L10 + jump L6 - L9: + L5: 250 load_const 1250 - L10: + L6: return } "); @@ -1095,62 +869,29 @@ async fn match_binary_search_sparse_four_arms_param() { insta::assert_snapshot!(output.bytecode, @" function classify(x: int) -> int { load_var x - copy 0 - load_const 60 - cmp_op == - pop_jump_if_false L0 - pop 1 - jump L5 + dense_tag [0, 30, 60, 99] + jump_table [L4, L3, L2, L1], default L0 L0: - copy 0 - load_const 60 - cmp_op < - pop_jump_if_false L2 - copy 0 - load_const 0 - cmp_op == - pop_jump_if_false L1 - pop 1 - jump L7 - - L1: - copy 0 - load_const 30 - cmp_op == - pop_jump_if_false L2 - pop 1 - jump L6 - - L2: - copy 0 - load_const 99 - cmp_op == - pop_jump_if_false L3 - pop 1 - jump L4 - - L3: - pop 1 load_const 999 - jump L8 + jump L5 - L4: + L1: 99 load_const 199 - jump L8 + jump L5 - L5: + L2: 60 load_const 160 - jump L8 + jump L5 - L6: + L3: 30 load_const 130 - jump L8 + jump L5 - L7: + L4: 0 load_const 100 - L8: + L5: return } "); @@ -1182,23 +923,19 @@ async fn match_if_else_chain_three_arms() { insta::assert_snapshot!(output.bytecode, @" function classify(x: int) -> int { load_var x - copy 0 load_const 0 cmp_op == pop_jump_if_false L0 - pop 1 jump L3 L0: - copy 0 + load_var x load_const 1 cmp_op == pop_jump_if_false L1 - pop 1 jump L2 L1: - pop 1 load_const 999 jump L4 @@ -1292,62 +1029,29 @@ async fn match_density_below_50_percent() { insta::assert_snapshot!(output.bytecode, @" function main() -> int { load_const 6 - copy 0 - load_const 6 - cmp_op == - pop_jump_if_false L0 - pop 1 - jump L5 + dense_tag [0, 3, 6, 9] + jump_table [L4, L3, L2, L1], default L0 L0: - copy 0 - load_const 6 - cmp_op < - pop_jump_if_false L2 - copy 0 - load_const 0 - cmp_op == - pop_jump_if_false L1 - pop 1 - jump L7 - - L1: - copy 0 - load_const 3 - cmp_op == - pop_jump_if_false L2 - pop 1 - jump L6 - - L2: - copy 0 - load_const 9 - cmp_op == - pop_jump_if_false L3 - pop 1 - jump L4 - - L3: - pop 1 load_const 999 - jump L8 + jump L5 - L4: + L1: 9 load_const 109 - jump L8 + jump L5 - L5: + L2: 6 load_const 106 - jump L8 + jump L5 - L6: + L3: 3 load_const 103 - jump L8 + jump L5 - L7: + L4: 0 load_const 100 - L8: + L5: return } "); @@ -1485,62 +1189,29 @@ async fn match_large_range_sparse() { insta::assert_snapshot!(output.bytecode, @" function main() -> int { load_const 1000 - copy 0 - load_const 1000 - cmp_op == - pop_jump_if_false L0 - pop 1 - jump L5 + dense_tag [0, 500, 1000, 1500] + jump_table [L4, L3, L2, L1], default L0 L0: - copy 0 - load_const 1000 - cmp_op < - pop_jump_if_false L2 - copy 0 - load_const 0 - cmp_op == - pop_jump_if_false L1 - pop 1 - jump L7 - - L1: - copy 0 - load_const 500 - cmp_op == - pop_jump_if_false L2 - pop 1 - jump L6 - - L2: - copy 0 - load_const 1500 - cmp_op == - pop_jump_if_false L3 - pop 1 - jump L4 - - L3: - pop 1 load_const 9999 - jump L8 + jump L5 - L4: + L1: 1500 load_const 4 - jump L8 + jump L5 - L5: + L2: 1000 load_const 3 - jump L8 + jump L5 - L6: + L3: 500 load_const 2 - jump L8 + jump L5 - L7: + L4: 0 load_const 1 - L8: + L5: return } "); @@ -1884,62 +1555,29 @@ async fn match_binary_search_negative_sparse() { insta::assert_snapshot!(output.bytecode, @r#" function classify(x: int) -> string { load_var x - copy 0 - load_const -10 - cmp_op == - pop_jump_if_false L0 - pop 1 - jump L5 + dense_tag [-100, -50, -10, -1] + jump_table [L4, L3, L2, L1], default L0 L0: - copy 0 - load_const -10 - cmp_op < - pop_jump_if_false L2 - copy 0 - load_const -100 - cmp_op == - pop_jump_if_false L1 - pop 1 - jump L7 - - L1: - copy 0 - load_const -50 - cmp_op == - pop_jump_if_false L2 - pop 1 - jump L6 - - L2: - copy 0 - load_const -1 - cmp_op == - pop_jump_if_false L3 - pop 1 - jump L4 - - L3: - pop 1 load_const "other" - jump L8 + jump L5 - L4: + L1: -1 load_const "d" - jump L8 + jump L5 - L5: + L2: -10 load_const "c" - jump L8 + jump L5 - L6: + L3: -50 load_const "b" - jump L8 + jump L5 - L7: + L4: -100 load_const "a" - L8: + L5: return } @@ -1976,62 +1614,29 @@ async fn match_binary_search_spanning_zero_sparse() { insta::assert_snapshot!(output.bytecode, @r#" function classify(x: int) -> string { load_var x - copy 0 - load_const 1 - cmp_op == - pop_jump_if_false L0 - pop 1 - jump L5 + dense_tag [-100, -1, 1, 100] + jump_table [L4, L3, L2, L1], default L0 L0: - copy 0 - load_const 1 - cmp_op < - pop_jump_if_false L2 - copy 0 - load_const -100 - cmp_op == - pop_jump_if_false L1 - pop 1 - jump L7 - - L1: - copy 0 - load_const -1 - cmp_op == - pop_jump_if_false L2 - pop 1 - jump L6 - - L2: - copy 0 - load_const 100 - cmp_op == - pop_jump_if_false L3 - pop 1 - jump L4 - - L3: - pop 1 load_const "other" - jump L8 + jump L5 - L4: + L1: 100 load_const "hundred" - jump L8 + jump L5 - L5: + L2: 1 load_const "one" - jump L8 + jump L5 - L6: + L3: -1 load_const "neg one" - jump L8 + jump L5 - L7: + L4: -100 load_const "neg hundred" - L8: + L5: return } @@ -2180,62 +1785,29 @@ async fn match_range_exceeds_limit_uses_binary_search() { insta::assert_snapshot!(output.bytecode, @" function classify(x: int) -> int { load_var x - copy 0 - load_const 200 - cmp_op == - pop_jump_if_false L0 - pop 1 - jump L5 + dense_tag [0, 100, 200, 300] + jump_table [L4, L3, L2, L1], default L0 L0: - copy 0 - load_const 200 - cmp_op < - pop_jump_if_false L2 - copy 0 - load_const 0 - cmp_op == - pop_jump_if_false L1 - pop 1 - jump L7 - - L1: - copy 0 - load_const 100 - cmp_op == - pop_jump_if_false L2 - pop 1 - jump L6 - - L2: - copy 0 - load_const 300 - cmp_op == - pop_jump_if_false L3 - pop 1 - jump L4 - - L3: - pop 1 load_const 0 - jump L8 + jump L5 - L4: + L1: 300 load_const 4 - jump L8 + jump L5 - L5: + L2: 200 load_const 3 - jump L8 + jump L5 - L6: + L3: 100 load_const 2 - jump L8 + jump L5 - L7: + L4: 0 load_const 1 - L8: + L5: return } "); @@ -2357,118 +1929,45 @@ async fn match_binary_search_eight_arms() { insta::assert_snapshot!(output.bytecode, @" function classify(x: int) -> int { load_var x - copy 0 - load_const 40 - cmp_op == - pop_jump_if_false L0 - pop 1 - jump L11 + dense_tag [0, 10, 20, 30, 40, 50, 60, 70] + jump_table [L8, L7, L6, L5, L4, L3, L2, L1], default L0 L0: - copy 0 - load_const 40 - cmp_op < - pop_jump_if_false L4 - copy 0 - load_const 20 - cmp_op == - pop_jump_if_false L1 - pop 1 - jump L13 - - L1: - copy 0 - load_const 20 - cmp_op < - pop_jump_if_false L3 - copy 0 - load_const 0 - cmp_op == - pop_jump_if_false L2 - pop 1 - jump L15 - - L2: - copy 0 - load_const 10 - cmp_op == - pop_jump_if_false L3 - pop 1 - jump L14 - - L3: - copy 0 - load_const 30 - cmp_op == - pop_jump_if_false L4 - pop 1 - jump L12 - - L4: - copy 0 - load_const 60 - cmp_op == - pop_jump_if_false L5 - pop 1 - jump L9 - - L5: - copy 0 - load_const 60 - cmp_op < - pop_jump_if_false L6 - copy 0 - load_const 50 - cmp_op == - pop_jump_if_false L6 - pop 1 - jump L10 - - L6: - copy 0 - load_const 70 - cmp_op == - pop_jump_if_false L7 - pop 1 - jump L8 - - L7: - pop 1 load_const 99 - jump L16 + jump L9 - L8: + L1: 70 load_const 7 - jump L16 + jump L9 - L9: + L2: 60 load_const 6 - jump L16 + jump L9 - L10: + L3: 50 load_const 5 - jump L16 + jump L9 - L11: + L4: 40 load_const 4 - jump L16 + jump L9 - L12: + L5: 30 load_const 3 - jump L16 + jump L9 - L13: + L6: 20 load_const 2 - jump L16 + jump L9 - L14: + L7: 10 load_const 1 - jump L16 + jump L9 - L15: + L8: 0 load_const 0 - L16: + L9: return } "); diff --git a/baml_language/crates/baml_tests/tests/match_types.rs b/baml_language/crates/baml_tests/tests/match_types.rs index ad6fd2987d..cc35e10339 100644 --- a/baml_language/crates/baml_tests/tests/match_types.rs +++ b/baml_language/crates/baml_tests/tests/match_types.rs @@ -101,15 +101,13 @@ async fn match_typed_pattern_second_arm() { init_field .reason store_var result load_var result - load_const Success - cmp_op instanceof + is_type Success pop_jump_if_false L0 jump L1 L0: load_var result - load_const Failure - cmp_op instanceof + is_type Failure pop_jump_if_false L2 load_const "failure: " load_var result @@ -225,49 +223,28 @@ async fn match_typed_discard_patterns_typetag_switch_path() { function classify(x: int | string | bool | float) -> int { load_var x type_tag - load_const 0 - cmp_op == - pop_jump_if_false L0 - jump L5 - - L0: - load_var x - type_tag - load_const 1 - cmp_op == - pop_jump_if_false L1 - jump L4 - - L1: - load_var x - type_tag - load_const 2 - cmp_op == - pop_jump_if_false L2 - jump L3 + jump_table [L3, L2, L1, _, L0], default L5 - L2: - load_var x - type_tag - load_const 4 - cmp_op == - pop_jump_if_false L6 + L0: float load_const 4 - jump L6 + jump L4 - L3: + L1: bool load_const 3 - jump L6 + jump L4 - L4: + L2: string load_const 2 - jump L6 + jump L4 - L5: + L3: int load_const 1 - L6: + L4: return + + L5: + unreachable } function main() -> int { @@ -310,8 +287,7 @@ async fn match_guard_true() { init_field .value store_var s load_var s - load_const Score - cmp_op instanceof + is_type Score pop_jump_if_false L0 load_var s load_field .value @@ -322,8 +298,7 @@ async fn match_guard_true() { L0: load_var s - load_const Score - cmp_op instanceof + is_type Score pop_jump_if_false L1 load_var s load_field .value @@ -334,8 +309,7 @@ async fn match_guard_true() { L1: load_var s - load_const Score - cmp_op instanceof + is_type Score pop_jump_if_false L4 load_const "needs work" jump L4 @@ -384,8 +358,7 @@ async fn match_guard_fallthrough() { init_field .value store_var s load_var s - load_const Score - cmp_op instanceof + is_type Score pop_jump_if_false L0 load_var s load_field .value @@ -396,8 +369,7 @@ async fn match_guard_fallthrough() { L0: load_var s - load_const Score - cmp_op instanceof + is_type Score pop_jump_if_false L1 load_var s load_field .value @@ -408,8 +380,7 @@ async fn match_guard_fallthrough() { L1: load_var s - load_const Score - cmp_op instanceof + is_type Score pop_jump_if_false L4 load_const "needs work" jump L4 @@ -458,8 +429,7 @@ async fn match_guard_all_fail() { init_field .value store_var s load_var s - load_const Score - cmp_op instanceof + is_type Score pop_jump_if_false L0 load_var s load_field .value @@ -470,8 +440,7 @@ async fn match_guard_all_fail() { L0: load_var s - load_const Score - cmp_op instanceof + is_type Score pop_jump_if_false L1 load_var s load_field .value @@ -482,8 +451,7 @@ async fn match_guard_all_fail() { L1: load_var s - load_const Score - cmp_op instanceof + is_type Score pop_jump_if_false L4 load_const "needs work" jump L4 @@ -785,9 +753,7 @@ async fn match_mixed_literal_typed_guard() { L1: load_var x - type_tag - load_const 0 - cmp_op == + is_type int pop_jump_if_false L4 load_const "other int" jump L4 @@ -853,9 +819,7 @@ async fn match_mixed_literal_typed_guard_fallthrough() { L1: load_var x - type_tag - load_const 0 - cmp_op == + is_type int pop_jump_if_false L4 load_const "other int" jump L4 @@ -909,8 +873,7 @@ async fn match_guard_on_typed_pattern_field_access() { insta::assert_snapshot!(output.bytecode, @r#" function classify(result: Success | Failure) -> string { load_var result - load_const Success - cmp_op instanceof + is_type Success pop_jump_if_false L0 load_var result load_field .data @@ -921,15 +884,13 @@ async fn match_guard_on_typed_pattern_field_access() { L0: load_var result - load_const Success - cmp_op instanceof + is_type Success pop_jump_if_false L1 jump L2 L1: load_var result - load_const Failure - cmp_op instanceof + is_type Failure pop_jump_if_false L4 load_const "failure" jump L4 @@ -984,8 +945,7 @@ async fn match_guard_on_typed_pattern_field_access_fails() { insta::assert_snapshot!(output.bytecode, @r#" function classify(result: Success | Failure) -> string { load_var result - load_const Success - cmp_op instanceof + is_type Success pop_jump_if_false L0 load_var result load_field .data @@ -996,15 +956,13 @@ async fn match_guard_on_typed_pattern_field_access_fails() { L0: load_var result - load_const Success - cmp_op instanceof + is_type Success pop_jump_if_false L1 jump L2 L1: load_var result - load_const Failure - cmp_op instanceof + is_type Failure pop_jump_if_false L4 load_const "failure" jump L4 @@ -1066,23 +1024,20 @@ async fn match_enum_variant_first() { function classify(s: Status) -> string { load_var s discriminant - copy 0 load_const Status.Active cmp_op == pop_jump_if_false L0 - pop 1 jump L3 L0: - copy 0 + load_var s + discriminant load_const Status.Inactive cmp_op == pop_jump_if_false L1 - pop 1 jump L2 L1: - pop 1 load_const "pending" jump L4 @@ -1138,23 +1093,20 @@ async fn match_enum_variant_last() { function classify(s: Status) -> string { load_var s discriminant - copy 0 load_const Status.Active cmp_op == pop_jump_if_false L0 - pop 1 jump L3 L0: - copy 0 + load_var s + discriminant load_const Status.Inactive cmp_op == pop_jump_if_false L1 - pop 1 jump L2 L1: - pop 1 load_const "pending" jump L4 @@ -1214,23 +1166,20 @@ async fn match_enum_variant_with_wildcard() { function classify(s: Status) -> string { load_var s discriminant - copy 0 load_const Status.Active cmp_op == pop_jump_if_false L0 - pop 1 jump L3 L0: - copy 0 + load_var s + discriminant load_const Status.Inactive cmp_op == pop_jump_if_false L1 - pop 1 jump L2 L1: - pop 1 load_const "other" jump L4 @@ -1286,23 +1235,20 @@ async fn match_enum_variant_with_wildcard_matched() { function classify(s: Status) -> string { load_var s discriminant - copy 0 load_const Status.Active cmp_op == pop_jump_if_false L0 - pop 1 jump L3 L0: - copy 0 + load_var s + discriminant load_const Status.Inactive cmp_op == pop_jump_if_false L1 - pop 1 jump L2 L1: - pop 1 load_const "other" jump L4 @@ -1427,22 +1373,19 @@ async fn match_class_types_exhaustive_first() { insta::assert_snapshot!(output.bytecode, @r#" function classify(animal: Cat | Dog | Bird) -> string { load_var animal - load_const Cat - cmp_op instanceof + is_type Cat pop_jump_if_false L0 jump L3 L0: load_var animal - load_const Dog - cmp_op instanceof + is_type Dog pop_jump_if_false L1 jump L2 L1: load_var animal - load_const Bird - cmp_op instanceof + is_type Bird pop_jump_if_false L4 load_const "bird: " load_var animal @@ -1506,22 +1449,19 @@ async fn match_class_types_exhaustive_last() { insta::assert_snapshot!(output.bytecode, @r#" function classify(animal: Cat | Dog | Bird) -> string { load_var animal - load_const Cat - cmp_op instanceof + is_type Cat pop_jump_if_false L0 jump L3 L0: load_var animal - load_const Dog - cmp_op instanceof + is_type Dog pop_jump_if_false L1 jump L2 L1: load_var animal - load_const Bird - cmp_op instanceof + is_type Bird pop_jump_if_false L4 load_const "bird: " load_var animal @@ -1585,15 +1525,13 @@ async fn match_class_types_non_exhaustive_wildcard() { insta::assert_snapshot!(output.bytecode, @r#" function classify(animal: Cat | Dog | Bird) -> string { load_var animal - load_const Cat - cmp_op instanceof + is_type Cat pop_jump_if_false L0 jump L3 L0: load_var animal - load_const Dog - cmp_op instanceof + is_type Dog pop_jump_if_false L1 jump L2 @@ -1657,15 +1595,13 @@ async fn match_class_types_non_exhaustive_matched() { insta::assert_snapshot!(output.bytecode, @r#" function classify(animal: Cat | Dog | Bird) -> string { load_var animal - load_const Cat - cmp_op instanceof + is_type Cat pop_jump_if_false L0 jump L3 L0: load_var animal - load_const Dog - cmp_op instanceof + is_type Dog pop_jump_if_false L1 jump L2 @@ -1731,49 +1667,28 @@ async fn match_union_type_four_patterns_type_tag() { function identify(x: int | string | bool | float) -> string { load_var x type_tag - load_const 0 - cmp_op == - pop_jump_if_false L0 - jump L5 + jump_table [L3, L2, L1, _, L0], default L5 - L0: - load_var x - type_tag - load_const 1 - cmp_op == - pop_jump_if_false L1 - jump L4 - - L1: - load_var x - type_tag - load_const 2 - cmp_op == - pop_jump_if_false L2 - jump L3 - - L2: - load_var x - type_tag - load_const 4 - cmp_op == - pop_jump_if_false L6 + L0: float load_const "decimal" - jump L6 + jump L4 - L3: + L1: bool load_const "boolean" - jump L6 + jump L4 - L4: + L2: string load_const "text" - jump L6 + jump L4 - L5: + L3: int load_const "integer" - L6: + L4: return + + L5: + unreachable } "#); @@ -1812,8 +1727,7 @@ async fn match_multiple_typed_patterns_with_guards() { insta::assert_snapshot!(output.bytecode, @r#" function classify(result: Success | Failure, strict: bool) -> string { load_var result - load_const Success - cmp_op instanceof + is_type Success pop_jump_if_false L0 load_var result load_field .code @@ -1824,8 +1738,7 @@ async fn match_multiple_typed_patterns_with_guards() { L0: load_var result - load_const Success - cmp_op instanceof + is_type Success pop_jump_if_false L1 load_var strict pop_jump_if_false L1 @@ -1833,15 +1746,13 @@ async fn match_multiple_typed_patterns_with_guards() { L1: load_var result - load_const Success - cmp_op instanceof + is_type Success pop_jump_if_false L2 jump L3 L2: load_var result - load_const Failure - cmp_op instanceof + is_type Failure pop_jump_if_false L6 load_const "failure" jump L6 @@ -1876,3 +1787,408 @@ async fn match_multiple_typed_patterns_with_guards() { Ok(BexExternalValue::String("redirect".to_string())) ); } + +// ============================================================================ +// Step 1.6 — Explicit TypeTag dispatch shape verification +// ============================================================================ + +/// 4+ sequentially-declared class types → dense type tags → JumpTable dispatch. +#[tokio::test] +async fn match_class_type_tag_jump_table() { + let output = baml_test!( + r#" + class Cat { name string } + class Dog { name string } + class Bird { name string } + class Fish { name string } + + function describe(animal: Cat | Dog | Bird | Fish) -> string { + match (animal) { + c: Cat => "cat", + d: Dog => "dog", + b: Bird => "bird", + f: Fish => "fish" + } + } + function main() -> string { + describe(Dog { name: "Rex" }) + } + "# + ); + + // Bytecode must use type_tag + jump_table, not instanceof chains. + assert!( + output.bytecode.contains("type_tag"), + "expected type_tag instruction in bytecode:\n{}", + output.bytecode + ); + assert!( + output.bytecode.contains("jump_table"), + "expected jump_table instruction in bytecode:\n{}", + output.bytecode + ); + assert!( + !output.bytecode.contains("instanceof"), + "unexpected instanceof instruction in bytecode:\n{}", + output.bytecode + ); + + insta::assert_snapshot!(output.bytecode, @r#" + function describe(animal: Cat | Dog | Bird | Fish) -> string { + load_var animal + type_tag + jump_table [L1, L0, _, L3, L2], default L5 + + L0: Fish + load_const "fish" + jump L4 + + L1: Bird + load_const "bird" + jump L4 + + L2: Dog + load_const "dog" + jump L4 + + L3: Cat + load_const "cat" + + L4: + return + + L5: + unreachable + } + + function main() -> string { + alloc_instance Dog + load_const "Rex" + init_field .name + call user.describe + return + } + "#); + + assert_eq!( + output.result, + Ok(BexExternalValue::String("dog".to_string())) + ); +} + +/// <4 class types → is_type sequential chain (below jump_table threshold). +#[tokio::test] +async fn match_class_type_is_type_chain() { + let output = baml_test!( + r#" + class Cat { name string } + class Dog { name string } + + function describe(animal: Cat | Dog) -> string { + match (animal) { + c: Cat => "cat", + d: Dog => "dog" + } + } + function main() -> string { + describe(Cat { name: "Whiskers" }) + } + "# + ); + + insta::assert_snapshot!(output.bytecode, @r#" + function describe(animal: Cat | Dog) -> string { + load_var animal + is_type Cat + pop_jump_if_false L0 + jump L1 + + L0: + load_var animal + is_type Dog + pop_jump_if_false L2 + load_const "dog" + jump L2 + + L1: + load_const "cat" + + L2: + return + } + + function main() -> string { + alloc_instance Cat + load_const "Whiskers" + init_field .name + call user.describe + return + } + "#); + + assert_eq!( + output.result, + Ok(BexExternalValue::String("cat".to_string())) + ); +} + +/// Mixed class + primitive union with 4+ typed arms → type_tag + switch. +#[tokio::test] +async fn match_mixed_class_primitive_type_tag_switch() { + let output = baml_test!( + r#" + class MyClass { value int } + + function classify(x: MyClass | int | string | bool) -> string { + match (x) { + c: MyClass => "class", + n: int => "int", + s: string => "string", + b: bool => "bool" + } + } + function main() -> string { + classify(MyClass { value: 42 }) + } + "# + ); + + insta::assert_snapshot!(output.bytecode, @r#" + function classify(x: MyClass | int | string | bool) -> string { + load_var x + type_tag + dense_tag [MyClass, int, string, bool] + jump_table [L3, L2, L1, L0], default L5 + + L0: bool + load_const "bool" + jump L4 + + L1: string + load_const "string" + jump L4 + + L2: int + load_const "int" + jump L4 + + L3: MyClass + load_const "class" + + L4: + return + + L5: + unreachable + } + + function main() -> string { + alloc_instance MyClass + load_const 42 + init_field .value + call user.classify + return + } + "#); + + assert_eq!( + output.result, + Ok(BexExternalValue::String("class".to_string())) + ); +} + +/// Sparse class tags (filler classes create gaps between tag values) +/// with 4+ arms → type_tag + match_hash + jump_table. +#[tokio::test] +async fn match_sparse_class_type_tag_perfect_hash() { + let output = baml_test!( + r#" + class Cat { name string } + class Filler1 { x int } + class Filler2 { x int } + class Filler3 { x int } + class Filler4 { x int } + class Dog { name string } + class Filler5 { x int } + class Filler6 { x int } + class Filler7 { x int } + class Filler8 { x int } + class Bird { name string } + class Filler9 { x int } + class Filler10 { x int } + class Filler11 { x int } + class Filler12 { x int } + class Fish { name string } + + function describe(animal: Cat | Dog | Bird | Fish) -> string { + match (animal) { + c: Cat => "cat", + d: Dog => "dog", + b: Bird => "bird", + f: Fish => "fish" + } + } + function main() -> string { + describe(Dog { name: "Rex" }) + } + "# + ); + + assert_eq!( + output.result, + Ok(BexExternalValue::String("dog".to_string())) + ); +} + +/// Null in a 4+ arm TypeTag switch → jump_table includes the null tag slot. +#[tokio::test] +async fn match_null_in_type_tag_jump_table() { + let output = baml_test!( + r#" + function classify(x: int | string | bool | null) -> string { + match (x) { + _: int => "int", + _: string => "string", + _: bool => "bool", + _: null => "null" + } + } + function main() -> string { + classify(null) + } + "# + ); + + insta::assert_snapshot!(output.bytecode, @r#" + function classify(x: int | string | bool | null) -> string { + load_var x + type_tag + jump_table [L3, L2, L1, L0], default L5 + + L0: null + load_const "null" + jump L4 + + L1: bool + load_const "bool" + jump L4 + + L2: string + load_const "string" + jump L4 + + L3: int + load_const "int" + + L4: + return + + L5: + unreachable + } + + function main() -> string { + load_const null + call user.classify + return + } + "#); + assert_eq!( + output.result, + Ok(BexExternalValue::String("null".to_string())) + ); +} + +/// Null mixed with class types in TypeTag switch. +#[tokio::test] +async fn match_null_with_class_types_type_tag() { + let output = baml_test!( + r#" + class Cat { name string } + class Dog { name string } + + function classify(x: Cat | Dog | null) -> string { + match (x) { + c: Cat => "cat", + d: Dog => "dog", + _: null => "nothing" + } + } + function main() -> string { + classify(null) + } + "# + ); + + insta::assert_snapshot!(output.bytecode, @r#" + function classify(x: Cat | Dog | null) -> string { + load_var x + is_type Cat + pop_jump_if_false L0 + jump L3 + + L0: + load_var x + is_type Dog + pop_jump_if_false L1 + jump L2 + + L1: + load_var x + is_type null + pop_jump_if_false L4 + load_const "nothing" + jump L4 + + L2: + load_const "dog" + jump L4 + + L3: + load_const "cat" + + L4: + return + } + + function main() -> string { + load_const null + call user.classify + return + } + "#); + assert_eq!( + output.result, + Ok(BexExternalValue::String("nothing".to_string())) + ); +} + +/// Dense class tags (4+ arms, sequential) still use direct JumpTable, no match_hash. +#[tokio::test] +async fn match_dense_class_still_uses_direct_jump_table() { + let output = baml_test!( + r#" + class Cat { name string } + class Dog { name string } + class Bird { name string } + class Fish { name string } + + function describe(animal: Cat | Dog | Bird | Fish) -> string { + match (animal) { + c: Cat => "cat", + d: Dog => "dog", + b: Bird => "bird", + f: Fish => "fish" + } + } + function main() -> string { + describe(Dog { name: "Rex" }) + } + "# + ); + + assert_eq!( + output.result, + Ok(BexExternalValue::String("dog".to_string())) + ); +} diff --git a/baml_language/crates/baml_tests/tests/operators.rs b/baml_language/crates/baml_tests/tests/operators.rs index 3b9d71ff0f..a2aad57f57 100644 --- a/baml_language/crates/baml_tests/tests/operators.rs +++ b/baml_language/crates/baml_tests/tests/operators.rs @@ -556,14 +556,11 @@ async fn logical_and() { insta::assert_snapshot!(output.bytecode, @" function main() -> bool { load_const true - store_var _0 - load_const true - pop_jump_if_false L0 + jump_if_false L0 + pop 1 load_const false - store_var _0 L0: - load_var _0 return } "); @@ -584,17 +581,14 @@ async fn logical_or() { insta::assert_snapshot!(output.bytecode, @" function main() -> bool { load_const true - store_var _0 - load_const true - pop_jump_if_false L0 + jump_if_false L0 jump L1 L0: + pop 1 load_const false - store_var _0 L1: - load_var _0 return } "); @@ -619,8 +613,8 @@ async fn short_circuit_and() { insta::assert_snapshot!(output.bytecode, @" function main() -> bool { load_const true - load_const true - pop_jump_if_false L0 + jump_if_false L0 + pop 1 call user.ret_bool L0: @@ -653,11 +647,11 @@ async fn short_circuit_or() { insta::assert_snapshot!(output.bytecode, @" function main() -> bool { load_const true - load_const true - pop_jump_if_false L0 + jump_if_false L0 jump L1 L0: + pop 1 call user.ret_bool L1: diff --git a/baml_language/crates/bex_vm/src/debug.rs b/baml_language/crates/bex_vm/src/debug.rs index 7e44acbea7..e23c083e8d 100644 --- a/baml_language/crates/bex_vm/src/debug.rs +++ b/baml_language/crates/bex_vm/src/debug.rs @@ -147,7 +147,9 @@ pub(crate) fn display_instruction( .map(|m| format!("({})", m.as_str())) .unwrap_or_default() } - Instruction::Jump(offset) | Instruction::PopJumpIfFalse(offset) => { + Instruction::Jump(offset) + | Instruction::PopJumpIfFalse(offset) + | Instruction::JumpIfFalse(offset) => { format!("(to {})", instruction_ptr.wrapping_add_signed(*offset)) } Instruction::AllocInstance(index) | Instruction::AllocVariant(index) => { @@ -169,6 +171,13 @@ pub(crate) fn display_instruction( Instruction::JumpTable { table_idx, default } => { format!("(table {table_idx}, default {default:+})") } + Instruction::DenseTag(table_idx) => { + if let Some(table) = function.bytecode.match_hash_tables.get(*table_idx) { + format!("({})", table.key_names.join(", ")) + } else { + format!("(hash table {table_idx})") + } + } Instruction::Pop(_) | Instruction::Copy(_) | Instruction::BinOp(_) @@ -185,6 +194,7 @@ pub(crate) fn display_instruction( | Instruction::Throw | Instruction::Discriminant | Instruction::TypeTag + | Instruction::IsType(_) | Instruction::ThrowIfPanic | Instruction::Unreachable | Instruction::MakeClosure(_, _) @@ -307,9 +317,11 @@ fn instruction_color(instruction: &Instruction) -> Color { Instruction::BinOp(_) | Instruction::CmpOp(_) | Instruction::UnaryOp(_) => { Color::BrightBlue } - Instruction::Jump(_) | Instruction::PopJumpIfFalse(_) | Instruction::JumpTable { .. } => { - Color::Yellow - } + Instruction::Jump(_) + | Instruction::PopJumpIfFalse(_) + | Instruction::JumpIfFalse(_) + | Instruction::JumpTable { .. } + | Instruction::DenseTag(_) => Color::Yellow, Instruction::Call(_) | Instruction::CallIndirect => Color::Magenta, Instruction::Return | Instruction::Pop(_) | Instruction::Copy(_) | Instruction::Throw => { Color::Red @@ -323,9 +335,10 @@ fn instruction_color(instruction: &Instruction) -> Color { Color::BrightRed } Instruction::VizEnter(_) | Instruction::VizExit(_) => Color::BrightYellow, - Instruction::Discriminant | Instruction::TypeTag | Instruction::ThrowIfPanic => { - Color::BrightBlue - } + Instruction::Discriminant + | Instruction::TypeTag + | Instruction::IsType(_) + | Instruction::ThrowIfPanic => Color::BrightBlue, Instruction::Unreachable => Color::BrightRed, Instruction::MakeClosure(_, _) | Instruction::MakeCell => Color::Cyan, Instruction::LoadDeref(_) | Instruction::LoadCapture(_) | Instruction::CaptureRef(_) => { @@ -532,7 +545,9 @@ fn display_bytecode_textual(function: &Function) -> String { for (ip, instruction) in instructions.iter().enumerate() { match instruction { - Instruction::Jump(offset) | Instruction::PopJumpIfFalse(offset) => { + Instruction::Jump(offset) + | Instruction::PopJumpIfFalse(offset) + | Instruction::JumpIfFalse(offset) => { let target = ip.wrapping_add_signed(*offset); jump_targets.insert(target); } @@ -659,6 +674,14 @@ fn display_instruction_textual( .unwrap_or_else(|| format!("?{target}")); format!("pop_jump_if_false {label}") } + Instruction::JumpIfFalse(offset) => { + let target = ip.wrapping_add_signed(*offset); + let label = label_map + .get(&target) + .cloned() + .unwrap_or_else(|| format!("?{target}")); + format!("jump_if_false {label}") + } Instruction::JumpTable { table_idx, default } => { let default_target = ip.wrapping_add_signed(*default); let default_label = label_map @@ -753,6 +776,19 @@ fn display_instruction_textual( // --- Type introspection --- Instruction::Discriminant => "discriminant".to_string(), Instruction::TypeTag => "type_tag".to_string(), + Instruction::IsType(const_idx) => { + let name = meta_str(const_idx); + format!("is_type {name}") + } + Instruction::DenseTag(table_idx) => { + let names = function + .bytecode + .match_hash_tables + .get(*table_idx) + .map(|t| t.key_names.join(", ")) + .unwrap_or_default(); + format!("dense_tag [{names}]") + } Instruction::ThrowIfPanic => "throw_if_panic".to_string(), // --- Closures and cells --- @@ -940,7 +976,9 @@ fn display_expanded_metadata(ip: usize, instruction: &Instruction, function: &Fu .unwrap_or_default(), // Jumps: show absolute target address. - Instruction::Jump(offset) | Instruction::PopJumpIfFalse(offset) => { + Instruction::Jump(offset) + | Instruction::PopJumpIfFalse(offset) + | Instruction::JumpIfFalse(offset) => { let target = ip.wrapping_add_signed(*offset); format!("(to {target})") } diff --git a/baml_language/crates/bex_vm/src/vm.rs b/baml_language/crates/bex_vm/src/vm.rs index bb9d2cdda7..b6f6aea3d5 100644 --- a/baml_language/crates/bex_vm/src/vm.rs +++ b/baml_language/crates/bex_vm/src/vm.rs @@ -1901,6 +1901,28 @@ impl BexVm { } } + Instruction::JumpIfFalse(offset) => { + let top = self.stack.ensure_stack_top()?; + let condition = self.stack[top]; + + match condition { + Value::Bool(value) => { + if !value { + self.frames[*frame_idx].instruction_ptr = instruction_ptr + .checked_add_signed(offset) + .ok_or(VmInternalError::InvalidJump)?; + } + } + other => { + return Err(VmInternalError::TypeError { + expected: Type::Bool, + got: self.type_of(&other), + } + .into()); + } + } + } + Instruction::Throw => { let value = self.stack.ensure_pop()?; self.try_unwind_exception(frame_idx, function, value)?; @@ -2071,15 +2093,6 @@ impl BexVm { CmpOp::LtEq => left <= right, CmpOp::Gt => left > right, CmpOp::GtEq => left >= right, - - CmpOp::InstanceOf => { - return Err(VmInternalError::CannotApplyCmpOp { - left: Type::Int, - right: Type::Int, - op, - } - .into()); - } }), #[allow(clippy::float_cmp)] @@ -2091,15 +2104,6 @@ impl BexVm { CmpOp::LtEq => left <= right, CmpOp::Gt => left > right, CmpOp::GtEq => left >= right, - - CmpOp::InstanceOf => { - return Err(VmInternalError::CannotApplyCmpOp { - left: Type::Float, - right: Type::Float, - op, - } - .into()); - } }), // Mixed int/float comparisons: promote int to float. @@ -2113,15 +2117,6 @@ impl BexVm { CmpOp::LtEq => left <= right, CmpOp::Gt => left > right, CmpOp::GtEq => left >= right, - - CmpOp::InstanceOf => { - return Err(VmInternalError::CannotApplyCmpOp { - left: Type::Int, - right: Type::Float, - op, - } - .into()); - } }) } @@ -2135,15 +2130,6 @@ impl BexVm { CmpOp::LtEq => left <= right, CmpOp::Gt => left > right, CmpOp::GtEq => left >= right, - - CmpOp::InstanceOf => { - return Err(VmInternalError::CannotApplyCmpOp { - left: Type::Float, - right: Type::Int, - op, - } - .into()); - } }) } @@ -2161,14 +2147,6 @@ impl BexVm { CmpOp::LtEq => left <= right, CmpOp::Gt => left > right, CmpOp::GtEq => left >= right, - CmpOp::InstanceOf => { - return Err(VmInternalError::CannotApplyCmpOp { - left: Type::Object(ObjectType::String), - right: Type::Object(ObjectType::String), - op, - } - .into()); - } }) } @@ -2228,21 +2206,6 @@ impl BexVm { CmpOp::Eq => left == right, CmpOp::NotEq => left != right, - CmpOp::InstanceOf => { - // null/non-object is never an instance of anything. - match left { - Value::Object(left_ptr) => match self.get_object(left_ptr) { - Object::Instance(instance) => { - let right_ptr = - self.as_object_ptr(&right, ObjectType::Class)?; - instance.class == right_ptr - } - _ => false, - }, - _ => false, - } - } - _ => { return Err(VmInternalError::CannotApplyCmpOp { left: self.type_of(&left), @@ -3058,6 +3021,58 @@ impl BexVm { self.stack.push(Value::Int(tag)); } + Instruction::IsType(const_idx) => { + let value = self.stack.ensure_pop()?; + let expected = &function.bytecode.resolved_constants[const_idx]; + let result = match expected { + Value::Object(class_ptr) => { + // Class identity check: is value an instance of this class? + match value { + Value::Object(val_ptr) => match self.get_object(val_ptr) { + Object::Instance(instance) => instance.class == *class_ptr, + _ => false, + }, + _ => false, + } + } + Value::Int(tag) => { + // Type tag check: does value's type tag match? + value_type_tag(&value) == *tag + } + _ => false, + }; + self.stack.push(Value::Bool(result)); + } + + #[allow( + clippy::cast_sign_loss, + clippy::cast_lossless, + clippy::cast_possible_truncation + )] + Instruction::DenseTag(table_idx) => { + let tag = match self.stack.ensure_pop()? { + Value::Int(t) => t, + other => { + // TypeTag always pushes Int, so this shouldn't happen. + return Err(VmInternalError::TypeError { + expected: Type::Int, + got: self.type_of(&other), + } + .into()); + } + }; + let table = &function.bytecode.match_hash_tables[table_idx]; + let h = + ((tag as u64).wrapping_mul(table.multiply) >> table.shift) & table.mask as u64; + let entry = &table.entries[h as usize]; + if entry.expected_tag == tag { + self.stack.push(Value::Int(i64::from(entry.dense_index))); + } else { + // Tag not in this match — sentinel for jump_table default + self.stack.push(Value::Int(-1)); + } + } + Instruction::ThrowIfPanic => { let value = self.stack.ensure_pop()?; let is_panic = match value { diff --git a/baml_language/crates/bex_vm_types/src/bytecode.rs b/baml_language/crates/bex_vm_types/src/bytecode.rs index bcc92a86ab..c2842155d7 100644 --- a/baml_language/crates/bex_vm_types/src/bytecode.rs +++ b/baml_language/crates/bex_vm_types/src/bytecode.rs @@ -71,6 +71,55 @@ impl JumpTableData { } } +// ============================================================================ +// Perfect Hash Table Data Structure +// ============================================================================ + +/// Compile-time minimal perfect hash table for O(1) type dispatch. +/// +/// Used by `Instruction::DenseTag` to remap a sparse type tag to a dense +/// `[0, K-1]` index for jump table dispatch. The hash function is: +/// +/// `h(tag) = ((tag as u64).wrapping_mul(multiply) >> shift) & mask` +/// +/// Each entry stores the expected tag for verification — if the runtime tag +/// doesn't match, the value is not in the match and dispatch falls to default. +/// +/// The hash constants are found by brute-force search at compile time. +/// For K ≤ 20 arms, the search completes in microseconds. +/// +/// References: +/// - Neumann & Göbbert, "Improving Switch Statement Performance with Hashing +/// Optimized at Compile Time" +/// - Dietz 1992, "Coding Multiway Branches Using Customized Hash Functions" +/// - Proposed for LLVM (issue #96971), Roslyn (#66604), Go (#34381) +#[derive(Clone, Debug, PartialEq)] +pub struct MatchHashTable { + /// Multiplicative hash constant, found at compile time. + pub multiply: u64, + /// Right-shift amount applied after multiplication. + pub shift: u8, + /// Bitmask applied after shift. Always `table_size - 1` (power of 2). + pub mask: u8, + /// Verification + dispatch entries. `entries[h(tag)]` contains: + /// - `expected_tag`: the type tag that should hash to this slot + /// - `dense_index`: the dense arm index `[0, K-1]` for jump table dispatch + /// Unused slots have `expected_tag = i64::MIN` (sentinel). + pub entries: Vec, + /// Human-readable names for keys in arm order (display only). + /// `key_names[i]` is the name for the i-th arm (e.g. "int", "`MyClass`"). + pub key_names: Vec, +} + +/// Single entry in a [`MatchHashTable`]. +#[derive(Clone, Debug, PartialEq)] +pub struct MatchHashEntry { + /// The type tag expected at this slot (for verification). + pub expected_tag: i64, + /// Dense arm index `[0, K-1]` — fed into the subsequent jump table. + pub dense_index: u8, +} + /// Individual bytecode instruction. /// /// For faster iteration we'll start with an in-memory data structure that @@ -182,6 +231,12 @@ pub enum Instruction { /// it, ensuring the condition doesn't leak on the evaluation stack. PopJumpIfFalse(isize), + /// Peek at the top of the stack and jump if the value is false. + /// Unlike `PopJumpIfFalse`, this does NOT pop the value — it stays on + /// the stack regardless of the branch taken. Used for short-circuit + /// `&&` / `||` where the tested value is also the expression result. + JumpIfFalse(isize), + /// Performs an arithmetic binary operation. /// /// Format: `BIN_OP op` where `op` is the binary operation to perform. @@ -354,12 +409,34 @@ pub enum Instruction { /// /// Stack: `[any_value]` -> `[type_tag: Int]` /// - /// Used for jump table dispatch on union types (instanceof patterns). + /// Used for jump table dispatch on union types (type patterns in match). /// Type tags are global constants: /// - Primitives: `int=0`, `string=1`, `bool=2`, `null=3`, `float=4` /// - Classes: assigned unique IDs starting at 100 TypeTag, + /// Check if the value on top of the stack matches the type identified by + /// the constant at index `i`. The constant is either: + /// - `Value::Object(class_ptr)` — class identity check (`inst.class == class_ptr`) + /// - `Value::Int(tag)` — type tag check (`value_type_tag(value) == tag`) + /// + /// Pops the value, pushes `Bool` result. + IsType(usize), + + /// Remap a sparse type tag to a dense index via perfect hash lookup. + /// + /// Pops the type tag (from a preceding `TypeTag` instruction), computes + /// `h(tag) = ((tag as u64).wrapping_mul(M) >> S) & mask`, verifies the + /// entry's expected tag, and pushes the dense arm index. On verification + /// failure (tag not in the match), pushes `-1` as a sentinel — the + /// subsequent `JumpTable`'s default arm handles this. + /// + /// Design rationale: perfect hashing replaces O(log K) `BinarySearch` with + /// O(1) dispatch for sparse ≥4-arm `TypeTag` switches. Memory per match + /// site scales with K (arms) not N (total classes). See [`MatchHashTable`] + /// for algorithm references. + DenseTag(usize), + /// If the top-of-stack value is a panic instance (`baml.panics.*`), throw it. /// Otherwise pop the value and continue to the next instruction. /// @@ -537,7 +614,6 @@ pub enum CmpOp { LtEq, Gt, GtEq, - InstanceOf, } #[derive(Clone, Copy, Debug, PartialEq)] @@ -572,7 +648,6 @@ impl std::fmt::Display for CmpOp { CmpOp::LtEq => "<=", CmpOp::Gt => ">", CmpOp::GtEq => ">=", - CmpOp::InstanceOf => "instanceof", }) } } @@ -601,6 +676,7 @@ impl std::fmt::Display for Instruction { Instruction::Copy(i) => write!(f, "COPY {i}"), Instruction::Jump(o) => write!(f, "JUMP {o:+}"), Instruction::PopJumpIfFalse(o) => write!(f, "POP_JUMP_IF_FALSE {o:+}"), + Instruction::JumpIfFalse(o) => write!(f, "JUMP_IF_FALSE {o:+}"), Instruction::BinOp(op) => write!(f, "BIN_OP {op}"), Instruction::CmpOp(op) => write!(f, "CMP_OP {op}"), Instruction::UnaryOp(op) => write!(f, "UNARY_OP {op}"), @@ -632,6 +708,8 @@ impl std::fmt::Display for Instruction { } Instruction::Discriminant => f.write_str("DISCRIMINANT"), Instruction::TypeTag => f.write_str("TYPE_TAG"), + Instruction::IsType(i) => write!(f, "IS_TYPE {i}"), + Instruction::DenseTag(i) => write!(f, "DENSE_TAG {i}"), Instruction::ThrowIfPanic => f.write_str("THROW_IF_PANIC"), Instruction::Unreachable => f.write_str("UNREACHABLE"), Instruction::MakeClosure(obj_idx, count) => { @@ -763,6 +841,10 @@ pub struct Bytecode { /// Jump tables for switch dispatch (indexed by `JumpTable` instruction). pub jump_tables: Vec, + /// Perfect hash tables for sparse `TypeTag` switch dispatch. + /// Indexed by `DenseTag` instruction operand. + pub match_hash_tables: Vec, + /// Line table mapping bytecode PCs to source spans. /// /// Entries are run-length encoded by PC ranges. @@ -793,6 +875,7 @@ impl Bytecode { constants: Vec::new(), resolved_constants: Vec::new(), jump_tables: Vec::new(), + match_hash_tables: Vec::new(), line_table: Vec::new(), meta: Vec::new(), exception_table: Vec::new(), diff --git a/baml_language/crates/tools_onionskin/src/compiler.rs b/baml_language/crates/tools_onionskin/src/compiler.rs index b5d406c15b..942c6f027d 100644 --- a/baml_language/crates/tools_onionskin/src/compiler.rs +++ b/baml_language/crates/tools_onionskin/src/compiler.rs @@ -448,7 +448,6 @@ fn binop_sym(op: &baml_compiler2_ast::BinaryOp) -> &'static str { BinaryOp::BitXor => "^", BinaryOp::Shl => "<<", BinaryOp::Shr => ">>", - BinaryOp::Instanceof => "instanceof", BinaryOp::NullCoalesce => "??", } }