feat(joinir): Phase 200-B/C/D capture analysis + Phase 201-A reserved_value_ids infra
Phase 200-B: FunctionScopeCaptureAnalyzer implementation - analyze_captured_vars_v2() with structural loop matching - CapturedEnv for immutable function-scope variables - ParamRole::Condition for condition-only variables Phase 200-C: ConditionEnvBuilder extension - build_with_captures() integrates CapturedEnv into ConditionEnv - fn_body propagation through LoopPatternContext to Pattern 2 Phase 200-D: E2E verification - capture detection working for base, limit, n etc. - Test files: phase200d_capture_minimal.hako, phase200d_capture_in_condition.hako Phase 201-A: MirBuilder reserved_value_ids infrastructure - reserved_value_ids: HashSet<ValueId> field in MirBuilder - next_value_id() skips reserved IDs - merge/mod.rs sets/clears reserved IDs around JoinIR merge Phase 201: JoinValueSpace design document - Param/Local/PHI disjoint regions design - API: alloc_param(), alloc_local(), reserve_phi() - Migration plan for Pattern 1-4 lowerers 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
25
apps/tests/phase200_digits_atoi_min.hako
Normal file
25
apps/tests/phase200_digits_atoi_min.hako
Normal file
@ -0,0 +1,25 @@
|
||||
// Phase 200-B: Minimal atoi with digits capture
|
||||
static box Main {
|
||||
main() {
|
||||
local s = "123"
|
||||
local digits = "0123456789" // ← Captured var
|
||||
|
||||
local i = 0
|
||||
local v = 0
|
||||
local n = s.length()
|
||||
|
||||
loop(i < n) {
|
||||
local ch = s.substring(i, i+1)
|
||||
local pos = digits.indexOf(ch) // ← Uses captured digits
|
||||
|
||||
if pos < 0 {
|
||||
break
|
||||
}
|
||||
|
||||
v = v * 10 + pos
|
||||
i = i + 1
|
||||
}
|
||||
|
||||
print(v) // Expected: 123
|
||||
}
|
||||
}
|
||||
25
apps/tests/phase200_digits_parse_number_min.hako
Normal file
25
apps/tests/phase200_digits_parse_number_min.hako
Normal file
@ -0,0 +1,25 @@
|
||||
// Phase 200-B: Minimal parse_number with digits capture
|
||||
static box Main {
|
||||
main() {
|
||||
local s = "42abc"
|
||||
local digits = "0123456789" // ← Captured var
|
||||
|
||||
local p = 0
|
||||
local num_str = ""
|
||||
local n = s.length()
|
||||
|
||||
loop(p < n) {
|
||||
local ch = s.substring(p, p+1)
|
||||
local digit_pos = digits.indexOf(ch) // ← Uses captured digits
|
||||
|
||||
if digit_pos < 0 {
|
||||
break
|
||||
}
|
||||
|
||||
num_str = num_str + ch
|
||||
p = p + 1
|
||||
}
|
||||
|
||||
print(num_str) // Expected: "42"
|
||||
}
|
||||
}
|
||||
31
apps/tests/phase200d_capture_in_condition.hako
Normal file
31
apps/tests/phase200d_capture_in_condition.hako
Normal file
@ -0,0 +1,31 @@
|
||||
// Phase 200-D: Capture variable used in CONDITION (not just body)
|
||||
// This test verifies that captured variables can be used in break conditions.
|
||||
//
|
||||
// Key points:
|
||||
// - limit is captured (function-scoped constant)
|
||||
// - Used in break condition: if v > limit { break }
|
||||
// - This proves ConditionEnv.captured is properly connected
|
||||
|
||||
static box Main {
|
||||
main() {
|
||||
local limit = 50 // Captured var (used in break condition)
|
||||
|
||||
local i = 0
|
||||
local v = 0
|
||||
local n = 100 // Loop up to 100 times
|
||||
|
||||
// Pattern 2: loop with break using captured var in condition
|
||||
loop(i < n) {
|
||||
v = v + 10
|
||||
i = i + 1
|
||||
|
||||
// Break condition uses CAPTURED variable 'limit'
|
||||
if v > limit {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// v should be 60 (broke when v=60 > limit=50)
|
||||
print(v) // Expected: 60
|
||||
}
|
||||
}
|
||||
38
apps/tests/phase200d_capture_minimal.hako
Normal file
38
apps/tests/phase200d_capture_minimal.hako
Normal file
@ -0,0 +1,38 @@
|
||||
// Phase 200-D: Minimal capture test (Pattern 2)
|
||||
// This test verifies that captured variables work in JoinIR Pattern 2
|
||||
// without using substring or other unsupported methods.
|
||||
//
|
||||
// Key points:
|
||||
// - base/offset are captured (function-scoped constants)
|
||||
// - No substring/indexOf in body-local init (Phase 193 limitation)
|
||||
// - Simple accumulation using captured value
|
||||
// - Break condition: i == 100 (never true, just to trigger Pattern 2)
|
||||
|
||||
static box Main {
|
||||
main() {
|
||||
local base = 10 // Captured var (used in multiplication)
|
||||
local offset = 5 // Captured var (used in addition)
|
||||
|
||||
local i = 0
|
||||
local v = 0
|
||||
local n = 3 // Loop 3 times
|
||||
|
||||
// Pattern 2: loop with break (break never fires)
|
||||
loop(i < n) {
|
||||
// Simple break condition that never fires
|
||||
if i == 100 {
|
||||
break
|
||||
}
|
||||
|
||||
// Use captured variable in accumulation
|
||||
// v = v + base
|
||||
// For i=0: v = 0 + 10 = 10
|
||||
// For i=1: v = 10 + 10 = 20
|
||||
// For i=2: v = 20 + 10 = 30
|
||||
v = v + base
|
||||
i = i + 1
|
||||
}
|
||||
|
||||
print(v) // Expected: 30
|
||||
}
|
||||
}
|
||||
33
apps/tests/phase200d_digits_accumulate.hako
Normal file
33
apps/tests/phase200d_digits_accumulate.hako
Normal file
@ -0,0 +1,33 @@
|
||||
// Phase 200-D: Digits accumulation test (no body-local in condition)
|
||||
// This test verifies captured variable (digits) with string accumulation
|
||||
// without requiring Pattern 5 body-local promotion.
|
||||
//
|
||||
// Key constraints:
|
||||
// - Loop condition uses only LoopParam (p) and OuterLocal (n)
|
||||
// - No body-local variables in break/if conditions
|
||||
// - digits is captured and used in loop body
|
||||
|
||||
static box Main {
|
||||
main() {
|
||||
local digits = "0123456789" // Captured var
|
||||
local s = "abc" // Input string
|
||||
|
||||
local p = 0
|
||||
local result = ""
|
||||
local n = s.length() // n = 3
|
||||
|
||||
// Simple loop: iterate exactly n times
|
||||
loop(p < n) {
|
||||
local ch = s.substring(p, p + 1)
|
||||
|
||||
// Use digits to check if char is a digit (result not used in condition)
|
||||
local is_digit = digits.indexOf(ch) // digits: captured
|
||||
|
||||
// Always append the char (no conditional break)
|
||||
result = result + ch
|
||||
p = p + 1
|
||||
}
|
||||
|
||||
print(result) // Expected: "abc"
|
||||
}
|
||||
}
|
||||
35
apps/tests/phase200d_digits_simple.hako
Normal file
35
apps/tests/phase200d_digits_simple.hako
Normal file
@ -0,0 +1,35 @@
|
||||
// Phase 200-D: Simple digits capture test (Pattern 2 with break)
|
||||
// This test verifies that captured variables (digits) work in JoinIR Pattern 2.
|
||||
//
|
||||
// Key constraints:
|
||||
// - Loop condition uses only LoopParam (i) and OuterLocal (n)
|
||||
// - Break condition uses outer-scoped variable (maxIter), NOT body-local
|
||||
// - digits is captured and used in loop body
|
||||
|
||||
static box Main {
|
||||
main() {
|
||||
local digits = "0123456789" // Captured var
|
||||
local s = "12" // Input string
|
||||
|
||||
local i = 0
|
||||
local v = 0
|
||||
local n = s.length() // n = 2
|
||||
local maxIter = 10 // Safety limit (outer-scoped, not body-local)
|
||||
|
||||
// Pattern 2: loop with break (break condition uses outer var)
|
||||
loop(i < n) {
|
||||
// Break if too many iterations (uses outer var, not body-local)
|
||||
if i > maxIter {
|
||||
break
|
||||
}
|
||||
|
||||
local ch = s.substring(i, i + 1)
|
||||
local d = digits.indexOf(ch) // digits: captured
|
||||
|
||||
v = v * 10 + d
|
||||
i = i + 1
|
||||
}
|
||||
|
||||
print(v) // Expected: 12
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user