Root cause: When compiling multiple static boxes, metadata from using statements
and previous box compilations (variable_map, value_origin_newbox, value_types)
leaked into subsequent compilations, causing parameters to be incorrectly typed.
For example, "args" parameter was incorrectly inferred as "ParserBox" instead of
its actual type.
Changes:
1. lifecycle.rs:95-97: Clear metadata before compiling each non-Main static box
2. decls.rs:42-44: Clear metadata before Phase 1 compilation in build_static_main_box
3. exprs.rs:170-172: Clear metadata before processing static box methods
4. builder_calls.rs:164-178: Add debug traces for value_origin_newbox/value_types
Impact:
- Fixes StageBArgsBox.resolve_src ValueId(21) undefined error
- Prevents "ParserBox" type contamination of parameters
- Ensures clean compilation context for each static box
Note: Revealed new bug in StageBBodyExtractorBox (Copy from undefined ValueId(114))
which needs separate investigation.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixes test compilation errors caused by adding callee: Option<Callee> field
to MirInstruction::Call in previous commits.
Changes:
- tests/mir_instruction_unit.rs:
- Add callee: None to all Call instruction constructions
- Ensures backward compatibility with existing tests
- src/mir/instruction/tests.rs:
- Add callee: None to Call instruction in phi_merge_if test
- Maintains test correctness after Call signature change
- src/mir/value_id.rs:
- Add ValueId::INVALID constant (u32::MAX)
- Provides clear sentinel value for invalid/placeholder IDs
- src/mir/phi_core/loopform_builder.rs:
- Replace deprecated ValueId::from() with ValueId::new()
- Replace deprecated BasicBlockId::from() with BasicBlockId::new()
- Ensures consistency with updated ID construction patterns
Test Status:
- Original errors from our commit: 6 → 0 ✅
- Remaining errors: 45 (pre-existing, unrelated to our changes)
- 14: Missing interpreter module (legacy)
- 11: Missing VM in backend::vm (moved)
- 7: Missing jit module (archived)
- 5: Missing MirInterpreter methods (legacy)
- 4: Missing Box operator methods (pre-existing)
All test errors related to LocalSSA and Call instruction changes are resolved.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit investigates ValueId(21) undefined error in Stage-B compilation.
Changes:
- src/mir/builder/builder_calls.rs:
- Add NYASH_DEBUG_PARAM_RECEIVER=1 trace for method call receivers
- Track variable_map lookups and ValueId mismatches
- Log receiver origin and current_block context
- src/mir/builder/utils.rs:
- Fix start_new_block() to avoid overwriting existing blocks
- Check if block exists before calling add_block()
- Prevents Copy instructions from being lost
- src/mir/function.rs:
- Add warning log when replacing existing block
- Helps detect block overwrite issues
- lang/src/mir/builder/ (Hako files):
- Add debug prints for method lowering paths
- These were not used (Stage-B uses Rust MIR Builder)
- Kept for future Hako MIR Builder debugging
Key Discovery:
- Stage-B compilation uses Rust MIR Builder, not Hako MIR Builder
- ValueId(21) is undefined receiver in StageBArgsBox.resolve_src/1
- MIR shows: call_method ParserBox.length() [recv: %21] but %21 has no definition
- Likely caused by LocalSSA Copy emission failure or block overwrite
Next: Fix LocalSSA to ensure receiver Copy is properly emitted and preserved
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
**Problem**: is_parameter() was too simple, checking only ValueId which changes
through copies/PHIs. This caused parameters like 'data' to be misclassified as
carriers, leading to incorrect PHI construction.
**Solution**: Track original parameter names at function entry.
**Changes**:
1. **Added function_param_names field** (builder.rs):
- HashSet<String> to track original parameter names
- Populated in lower_static_method_as_function()
- Cleared and repopulated for each new function
2. **Improved is_parameter()** (loop_builder.rs):
- Check name against function_param_names instead of ValueId
- More reliable than checking func.params (ValueIds change)
- __pin$*$@* variables correctly classified as carriers
- Added debug logging with NYASH_LOOPFORM_DEBUG
3. **Enhanced debug output** (loopform_builder.rs):
- Show carrier/pinned classification during prepare_structure()
- Show variable_map state after emit_header_phis()
**Test Results**:
- ✅ 'args' correctly identified as parameter (was working)
- ✅ 'data' now correctly identified as parameter (was broken)
- ✅ __pin variables correctly classified as carriers
- ✅ PHI values allocated and variable_map updated correctly
- ⚠️ ValueId undefined errors persist (separate issue)
**Remaining Issue**:
ValueId(10) undefined error suggests PHI visibility problem or VM verification
issue. Needs further investigation of emit_phi_at_block_start() or VM executor.
**Backward Compatibility**:
- Flag OFF: 100% existing behavior preserved (legacy path unchanged)
- Feature-flagged with NYASH_LOOPFORM_PHI_V2=1
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
**Problem**: Loop body PHIs reference ValueIds that don't exist at header exit.
Example: bb6 uses %14 from bb3, but %14 is only defined in bb6.
**Partial Fix**:
1. Create header_exit_snapshot from PHI values + new pinned variables
2. Use snapshot for loop body PHI creation instead of current variable_map
3. Use snapshot for exit PHI generation
**Progress**: Error moved from BasicBlockId(4) to BasicBlockId(3)
**Remaining**: Circular dependency - PHIs reference other PHIs in same block.
Need to ensure snapshot contains only values defined before header branch.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
**Goal**: Create 100-line minimal test case to reproduce SSA/ValueId
bugs in Stage-B compilation without the complexity of full compiler_stageb.hako.
**Files added**:
1. **lang/src/compiler/tests/stageb_min_sample.hako** (65 lines)
- Pattern 1: Method call in if-block before loop (TestArgs.process)
- Pattern 2: Simple method calls without loops (TestSimple.run)
- Pattern 3: Nested if/loop with method calls (TestNested.complex)
- All patterns reproduce ValueId SSA bugs
2. **tools/test_stageb_min.sh** (executable test script)
- Test 1: Direct VM execution
- Test 2: Stage-B compilation pipeline
- Test 3: MIR verification
**Test results** (as of commit):
Test 1 (Direct VM):
```
❌ ValueId(14) error in TestArgs.process/1
(different from ValueId(17) in Stage-B!)
```
Test 2 (Stage-B):
```
❌ ValueId(17) error in StageBArgsBox.resolve_src/1
(expected, same as full compiler_stageb.hako)
```
Test 3 (MIR verification):
```
✅ No verification errors
(verifier doesn't catch these specific SSA bugs)
```
**Findings**:
- Multiple ValueId SSA bugs exist (14, 17, etc.)
- MIR verifier needs enhancement to catch receiver use-before-def
- Minimal harness successfully reproduces issues for easier debugging
**Next steps** (not in this commit):
- Fix ValueId(14) in TestArgs.process
- Fix ValueId(17) in StageBArgsBox.resolve_src
- Enhance MIR verifier to catch Method receiver SSA bugs
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
**Problem**: NewBox for static boxes (e.g., HakoCli) failed with
"Plugins disabled, cannot create HakoCli" error when NYASH_DISABLE_PLUGINS=1.
**Root cause**: static box declarations were only registered with
VM's register_static_box_decl for singleton persistence, but NOT
included in InlineUserBoxFactory's decls. This caused UnifiedBoxRegistry
to skip User factory and fall through to Plugin factory, which failed
when plugins were disabled.
**Fix**: Include static_box_decls in InlineUserBoxFactory's decls map.
Static boxes remain singletons (VM ensures single instance via
register_static_box_decl), but now they're also advertised by User factory
so NewBox doesn't incorrectly route to Plugin factory.
**Implementation** (src/runner/modes/vm.rs):
1. Add static_box_decls to InlineUserBoxFactory decls after nonstatic_decls
2. nonstatic takes precedence if name conflicts (rare but possible)
3. StaticName -> StaticNameInstance aliases still work as before
**Behavior** (UnifiedBoxRegistry with StrictPluginFirst policy):
- try factory#1 Plugin → fails (NYASH_DISABLE_PLUGINS=1)
- try factory#2 User → succeeds (static box found in decls)
- NewBox creates InstanceBox, VM ensures singleton semantics
**Verification**:
- Test case: /tmp/test_static_newbox.hako
- NewBox TestStatic successfully creates instance via User factory
- Methods callable, return values correct
- No plugin errors with NYASH_DISABLE_PLUGINS=1
**Impact**: static box new instances now work correctly without plugins,
matching Rust layer's design where static boxes are user-defined types
(not plugin types) with singleton semantics.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
**Problem**: "use of undefined value ValueId(22)" errors in Stage-B
when method receivers were used after loops.
**Root cause**: prepare_loop_variables_with() explicitly skipped
pinned variables (like __pin$6438$@recv) when creating loop header
PHIs. Comment claimed they were "materialized via entry-phi in loop
body" but this only happened for the body block, not for loop exit
merge points. When receivers were used after loops, there was no PHI
to merge values from different control flow paths.
**Fix**: Remove the skip logic (lines 174-177) so pinned variables
get PHIs at loop headers and exits like any other variable.
**Impact**:
- ValueId(22) error eliminated in Stage-B selfhost tests
- MIR verification passes with NYASH_VM_VERIFY_MIR=1
- Pinned slots now correctly merge across loop boundaries
- No more "use of undefined value" for cross-loop receivers
**Test case**: /tmp/test_loop_recv.hako demonstrates receiver usage
in loop and after loop exit, now works correctly.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Phase 25.1b: Complete SSA fix - eliminate all global ValueId usage in function contexts.
Root cause: ~75 locations throughout MIR builder were using global value
generator (self.value_gen.next()) instead of function-local allocator
(f.next_value_id()), causing SSA verification failures and runtime
"use of undefined value" errors.
Solution:
- Added next_value_id() helper that automatically chooses correct allocator
- Fixed 19 files with ~75 occurrences of ValueId allocation
- All function-context allocations now use function-local IDs
Files modified:
- src/mir/builder/utils.rs: Added next_value_id() helper, fixed 8 locations
- src/mir/builder/builder_calls.rs: 17 fixes
- src/mir/builder/ops.rs: 8 fixes
- src/mir/builder/stmts.rs: 7 fixes
- src/mir/builder/emission/constant.rs: 6 fixes
- src/mir/builder/rewrite/*.rs: 10 fixes
- + 13 other files
Verification:
- cargo build --release: SUCCESS
- Simple tests with NYASH_VM_VERIFY_MIR=1: Zero undefined errors
- Multi-parameter static methods: All working
Known remaining: ValueId(22) in Stage-B (separate issue to investigate)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Phase 25.1b: Resolve 'Undefined value %0' errors in static methods.
Root cause: Parameters and constants were allocated using global value
generator (self.value_gen.next()) instead of function-local allocator
(f.next_value_id()), causing SSA verification failures.
Fixes:
- src/mir/builder/decls.rs: Use function-local ID for parameter binding
- src/mir/builder/emission/constant.rs: Context-aware constant emission
- src/mir/builder/builder_calls.rs: Function-local param allocation
Verification:
- NYASH_VM_VERIFY_MIR=1: Zero 'Undefined value %0' errors
- Simple test cases: All pass
- Stage-B compiler: %0 errors completely resolved
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add propagate_copy_types() to track MatI64 through copy instructions
- Fix PHI detection bug: indexOf("{") → indexOf("\"op\":\"")
- Add 4-iteration loop for multi-step propagation chains
- Enhance diagnostics with MatI64 vids list and skip reasons
This fixes type propagation for complex SSA patterns where MatI64 types
flow through multiple copy and phi instructions. Small test cases now
pass successfully.
Note: microbench matmul_core still has issues - investigating separately.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
## Problem
Phase 25 numeric_core transformation wasn't working in microbench.sh:
- NYASH_AOT_NUMERIC_CORE=1 was set by user externally
- But wasn't propagated to hakorune_emit_mir.sh
- Result: BoxCall(mul_naive) remained instead of Call("NyNumericMatI64.mul_naive")
## Solution
Add explicit env var propagation in microbench.sh (line 933-934):
```bash
NYASH_AOT_NUMERIC_CORE="${NYASH_AOT_NUMERIC_CORE:-0}" \
NYASH_AOT_NUMERIC_CORE_TRACE="${NYASH_AOT_NUMERIC_CORE_TRACE:-0}" \
```
This ensures user-set NYASH_AOT_NUMERIC_CORE is passed through to:
hakorune_emit_mir.sh → Provider → AotPrep → numeric_core.hako
## Verification
Tested with:
```bash
NYASH_AOT_NUMERIC_CORE=1 tools/perf/microbench.sh --case matmul_core --backend llvm --exe --runs 1 --n 4
```
Now transformation works correctly (pending numeric_core phi propagation fix).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Complete imports pipeline by extracting using statements and passing to MirBuilder:
- Extract "using X as Y" from source file using grep/sed
- Build JSON map {"Y":"Y"} for all imports
- Set HAKO_MIRBUILDER_IMPORTS environment variable
- extern_provider reads this and passes to program_json_to_mir_json_with_imports()
- MapVars::resolve() recognizes MatI64 as valid static box reference
Test result:
✅ /tmp/test_imports.hako (using MatI64) → MIR JSON generated without errors
✅ No "undefined variable: MatI64" error
✅ boxcall with MatI64.new() properly resolved
Phase 21.8 Step 7 complete - imports pipeline fully functional!
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
**Step 6 Complete**: Extract using imports from .hako source in pipeline
Changes:
- Modified collect_using_and_strip() to return (cleaned, prelude_paths, imports)
- Build imports HashMap from seen_aliases (alias -> alias mapping)
- Updated all 6 call sites to handle new 3-tuple return type
- This provides MirBuilder with info about which names are valid static box references
Next: Wire imports through extern_provider to MirBuilder (Step 7)
Related: #phase-21.8 MatI64/IntArrayCore integration
Phase 21.8 foundation for MatI64/IntArrayCore integration
Changes:
- Add `imports: HashMap<String, String>` to BridgeEnv
- Extend MapVars::resolve() to check imports and create static box references
- Add BridgeEnv::with_imports() to initialize with using imports map
- Add parse_json_v0_to_module_with_imports() to json_v0_bridge
- Add program_json_to_mir_json_with_imports() to mir_builder.rs
- Maintain backward compatibility via empty HashMap defaults
Next: Wire using extraction from pipeline and test with MatI64
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>