fix: switch lowering supports any number of cases (was capped at 8)
ci / build (ubuntu) (push) Has been cancelled
ci / unit + fmt (push) Has been cancelled
ci / examples (push) Has been cancelled
ci / goldens + tools (push) Has been cancelled
ci / apps (push) Has been cancelled
ci / selfhost smoke (push) Has been cancelled
ci / macos smoke (push) Has been cancelled
ci / windows smoke (push) Has been cancelled
ci / CI gate (push) Has been cancelled
selfhost-loop / bootstrap determinism (push) Has been cancelled

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.
This commit is contained in:
2026-07-28 01:58:03 +03:00
parent d517c62380
commit 1f8289059a
3 changed files with 82 additions and 36 deletions
+1 -1
View File
@@ -5,7 +5,7 @@ BUILD_DIR := build
# Project-local nimcache so CI can cache compiles (default is ~/.cache/nim). # Project-local nimcache so CI can cache compiles (default is ~/.cache/nim).
NIMFLAGS ?= --nimcache:nimcache 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. # 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 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
+63
View File
@@ -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;
}
+18 -35
View File
@@ -3475,46 +3475,29 @@ module HirLower {
current = Lcx_LowerBlock(ctx, stmt.refStmtElse, -1); current = Lcx_LowerBlock(ctx, stmt.refStmtElse, -1);
} }
// Cases in reverse order (from caseBlock) // 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 { if stmt.refStmtBlock != null as *Block {
let caseBlock: *Block = stmt.refStmtBlock; let caseBlock: *Block = stmt.refStmtBlock;
var caseCount: int = caseBlock.stmtCount; var total: int = 0;
// 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 cs: *Stmt = caseBlock.firstStmt; var cs: *Stmt = caseBlock.firstStmt;
while cs != null as *Stmt && ci < 8 { while cs != null as *Stmt {
if ci == 0 { c0 = cs; } total = total + 1;
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;
cs = cs.nextStmt; cs = cs.nextStmt;
} }
while caseCount > 0 { var ri: int = total;
caseCount = caseCount - 1; while ri > 0 {
var c: *Stmt = null as *Stmt; ri = ri - 1;
if caseCount == 0 { c = c0; } // Find ri-th case by walking from start
if caseCount == 1 { c = c1; } var i: int = 0;
if caseCount == 2 { c = c2; } cs = caseBlock.firstStmt;
if caseCount == 3 { c = c3; } while cs != null as *Stmt && i < ri {
if caseCount == 4 { c = c4; } cs = cs.nextStmt;
if caseCount == 5 { c = c5; } i = i + 1;
if caseCount == 6 { c = c6; } }
if caseCount == 7 { c = c7; } if cs != null as *Stmt {
if c != null as *Stmt { let caseVal: *HirNode = Lcx_LowerExpr(ctx, cs.child1);
let caseVal: *HirNode = Lcx_LowerExpr(ctx, c.child1); let caseBody: *HirNode = Lcx_LowerBlock(ctx, cs.refStmtBlock, -1);
let caseBody: *HirNode = Lcx_LowerBlock(ctx, c.refStmtBlock, -1);
let cond: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode; let cond: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
cond.kind = hBinary; cond.kind = hBinary;
cond.intValue = 74; // tkEq cond.intValue = 74; // tkEq