From 1f8289059a8643bf41df8f6661e2cc759acb1fab Mon Sep 17 00:00:00 2001 From: dimgigov Date: Tue, 28 Jul 2026 01:58:03 +0300 Subject: [PATCH] fix: switch lowering supports any number of cases (was capped at 8) Replaced fixed-size locals (c0..c7) with O(n^2) linked-list walk that handles arbitrary case counts via reverse-index traversal. Added switch.bux test with 11 and 10 cases. --- Makefile | 2 +- examples/switch.bux | 63 +++++++++++++++++++++++++++++++++++++++++++++ src/hir_lower.bux | 53 +++++++++++++------------------------- 3 files changed, 82 insertions(+), 36 deletions(-) create mode 100644 examples/switch.bux diff --git a/Makefile b/Makefile index eed535a..9d6096f 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ BUILD_DIR := build # Project-local nimcache so CI can cache compiles (default is ~/.cache/nim). NIMFLAGS ?= --nimcache:nimcache -EXAMPLES := hello fibonacci factorial structs enums methods algebraic_enums generics generics_struct generic_infer generic_infer2 extend_generic pattern_matching strings strings2 map result_option try_operator ownership ownership_checked ownership_release drop_early_return lifetime_elision ctfe ctfe_crc async concurrency os_time process json iter trait_bounds channel sync jwt stdlib_ergonomics tuples func_ptr map_remove array_iter_extra string_extra multi_closure iter_hof closure_control match_let string_interp iter_generic generic_infer_hof struct_tuple_pat match_block nested_patterns match_guards pattern_shadow move_field move_field_partial move_field_remaining move_field_nested move_field_ptr move_cross_fn c_precedence macro_twice macro_repeat macro_nested macro_hygiene macro_unhygienic macro_stmt_pat macro_tt macro_tt_raw macro_type collections_extra generic_enum +EXAMPLES := hello fibonacci factorial structs enums methods algebraic_enums generics generics_struct generic_infer generic_infer2 extend_generic pattern_matching strings strings2 map result_option try_operator ownership ownership_checked ownership_release drop_early_return lifetime_elision ctfe ctfe_crc async concurrency os_time process json iter trait_bounds channel sync jwt stdlib_ergonomics tuples func_ptr map_remove array_iter_extra string_extra multi_closure iter_hof closure_control match_let string_interp iter_generic generic_infer_hof struct_tuple_pat match_block nested_patterns match_guards pattern_shadow move_field move_field_partial move_field_remaining move_field_nested move_field_ptr move_cross_fn c_precedence macro_twice macro_repeat macro_nested macro_hygiene macro_unhygienic macro_stmt_pat macro_tt macro_tt_raw macro_type collections_extra generic_enum switch # Platform smoke (macOS CI): full EXAMPLES still runs on Linux. EXAMPLES_SMOKE := hello ownership ownership_release strings map move_field move_field_partial move_field_remaining move_field_nested move_field_ptr move_cross_fn c_precedence macro_twice macro_repeat macro_nested macro_hygiene macro_unhygienic macro_stmt_pat macro_tt macro_tt_raw ctfe_crc diff --git a/examples/switch.bux b/examples/switch.bux new file mode 100644 index 0000000..9b1d304 --- /dev/null +++ b/examples/switch.bux @@ -0,0 +1,63 @@ +// switch.bux — Test switch with many cases (>8) + +import Std::Io::{PrintLine, PrintInt}; + +func Main() -> int { + var x: int = 7; + + switch x { + case 0: + PrintLine("zero"); + case 1: + PrintLine("one"); + case 2: + PrintLine("two"); + case 3: + PrintLine("three"); + case 4: + PrintLine("four"); + case 5: + PrintLine("five"); + case 6: + PrintLine("six"); + case 7: + PrintLine("seven"); + case 8: + PrintLine("eight"); + case 9: + PrintLine("nine"); + case 10: + PrintLine("ten"); + default: + PrintLine("other"); + } + + // Test with match at end + x = 15; + switch x { + case 0: + PrintLine("a"); + case 1: + PrintLine("b"); + case 5: + PrintLine("c"); + case 10: + PrintLine("d"); + case 15: + PrintLine("fifteen-ok"); + case 20: + PrintLine("e"); + case 25: + PrintLine("f"); + case 30: + PrintLine("g"); + case 35: + PrintLine("h"); + case 40: + PrintLine("i"); + default: + PrintLine("default"); + } + + return 0; +} diff --git a/src/hir_lower.bux b/src/hir_lower.bux index 8ae4ec6..45cd180 100644 --- a/src/hir_lower.bux +++ b/src/hir_lower.bux @@ -3475,46 +3475,29 @@ module HirLower { current = Lcx_LowerBlock(ctx, stmt.refStmtElse, -1); } // Cases in reverse order (from caseBlock) + // Walk linked list: count total, then for each i from total-1 down to 0, + // walk from start to find the i-th case. O(n²) but handles any count. if stmt.refStmtBlock != null as *Block { let caseBlock: *Block = stmt.refStmtBlock; - var caseCount: int = caseBlock.stmtCount; - // Collect cases into fixed-size locals for reverse iteration - var c0: *Stmt = null as *Stmt; - var c1: *Stmt = null as *Stmt; - var c2: *Stmt = null as *Stmt; - var c3: *Stmt = null as *Stmt; - var c4: *Stmt = null as *Stmt; - var c5: *Stmt = null as *Stmt; - var c6: *Stmt = null as *Stmt; - var c7: *Stmt = null as *Stmt; - var ci: int = 0; + var total: int = 0; var cs: *Stmt = caseBlock.firstStmt; - while cs != null as *Stmt && ci < 8 { - if ci == 0 { c0 = cs; } - if ci == 1 { c1 = cs; } - if ci == 2 { c2 = cs; } - if ci == 3 { c3 = cs; } - if ci == 4 { c4 = cs; } - if ci == 5 { c5 = cs; } - if ci == 6 { c6 = cs; } - if ci == 7 { c7 = cs; } - ci = ci + 1; + while cs != null as *Stmt { + total = total + 1; cs = cs.nextStmt; } - while caseCount > 0 { - caseCount = caseCount - 1; - var c: *Stmt = null as *Stmt; - if caseCount == 0 { c = c0; } - if caseCount == 1 { c = c1; } - if caseCount == 2 { c = c2; } - if caseCount == 3 { c = c3; } - if caseCount == 4 { c = c4; } - if caseCount == 5 { c = c5; } - if caseCount == 6 { c = c6; } - if caseCount == 7 { c = c7; } - if c != null as *Stmt { - let caseVal: *HirNode = Lcx_LowerExpr(ctx, c.child1); - let caseBody: *HirNode = Lcx_LowerBlock(ctx, c.refStmtBlock, -1); + var ri: int = total; + while ri > 0 { + ri = ri - 1; + // Find ri-th case by walking from start + var i: int = 0; + cs = caseBlock.firstStmt; + while cs != null as *Stmt && i < ri { + cs = cs.nextStmt; + i = i + 1; + } + if cs != null as *Stmt { + let caseVal: *HirNode = Lcx_LowerExpr(ctx, cs.child1); + let caseBody: *HirNode = Lcx_LowerBlock(ctx, cs.refStmtBlock, -1); let cond: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode; cond.kind = hBinary; cond.intValue = 74; // tkEq