P1: Carrier Initialization Builder (HIGH) ✅ - New module: carrier_init_builder.rs (197 lines, 8 tests) - Refactored loop_header_phi_builder.rs (-34 lines) - Centralized CarrierInit value generation (SSOT) - Eliminates scattered match patterns across header PHI, exit line - Consistent debug output: [carrier_init_builder] format - Net: -34 lines of duplicated logic P2: Remaining DebugOutputBox Migration (QUICK) ✅ - Migrated carrier_info.rs::record_promoted_binding() - Uses DebugOutputBox for JOINIR_DEBUG checks - Maintains JOINIR_TEST_DEBUG override for test diagnostics - Consistent log formatting: [context/category] message - Net: +3 lines (SSOT migration) P3: Error Message Centralization (LOW) ✅ - New module: error_tags.rs (136 lines, 5 tests) - Migrated 3 error sites: * ownership/relay:runtime_unsupported (plan_validator.rs) * joinir/freeze (control_flow/mod.rs) * (ExitLine errors were debug messages, not returns) - Centralized error tag generation (freeze, exit_line_contract, ownership_relay_unsupported, etc.) - Net: +133 lines (SSOT module + tests) Total changes: - New files: carrier_init_builder.rs (197), error_tags.rs (136) - Modified: 6 files - Production code: +162 lines (SSOT investment) - Tests: 987/987 PASS (982→987, +5 new tests) - Phase 81 ExitLine: 2/2 PASS - Zero compilation errors/warnings Benefits: ✅ Single Responsibility: Each helper has one concern ✅ Testability: 13 new unit tests (8 carrier init, 5 error tags) ✅ Consistency: Uniform debug/error formatting ✅ SSOT: Centralized CarrierInit and error tag generation ✅ Discoverability: Easy to find all error types 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Ownership Analysis Module
Responsibility Boundary
This module is responsible for analysis only:
- ✅ Collecting reads/writes from AST/ProgramJSON
- ✅ Determining variable ownership (owned/relay/capture)
- ✅ Producing OwnershipPlan for downstream lowering
This module does NOT:
- ❌ Generate MIR instructions
- ❌ Modify JoinIR structures
- ❌ Perform lowering transformations
Core Types
| Type | Purpose |
|---|---|
ScopeId |
Unique scope identifier |
ScopeOwnedVar |
Variable defined in this scope |
RelayVar |
Write to ancestor-owned variable |
CapturedVar |
Read-only reference to ancestor |
OwnershipPlan |
Complete analysis result |
Invariants
carriers = owned_vars.filter(is_written)- No variable in both owned and relay
- No variable in both owned and captures
- Relay implies ancestor ownership exists
Design Philosophy
「読むのは自由、管理は直下 owned だけ」
- Owned: Variable defined in this scope (unique owner)
- Carrier: Owned AND written (managed as loop_step argument)
- Capture: Read-only reference to ancestor (via CapturedEnv)
- Relay: Write to ancestor → relay up to owner (exit PHI at owner)
Phase Status
- Phase 56: ✅ Interface skeleton
- Phase 57: ⏳ OwnershipAnalyzer implementation
- Phase 58: ⏳ P2 plumbing
- Phase 59: ⏳ P3 plumbing
- Phase 60: ⏳ Cleanup dev heuristics
- Phase 61: ⏳ Canonical promotion decision
Usage (Future)
let plan = OwnershipAnalyzer::analyze(&ast_node, parent_scope);
plan.verify_invariants()?;
let carriers: Vec<_> = plan.carriers().collect();
Example
local limit = 100 // owned by outer
loop {
local sum = 0 // owned by loop
if sum < limit { // limit = capture (read-only)
sum++ // sum = carrier (owned + written)
}
}
OwnershipPlan (loop scope):
owned_vars: [sum(written),limit(read-only)]relay_writes: []captures: [limit]condition_captures: [limit]
References
- Design Doc: phase56-ownership-relay-design.md
- JoinIR Architecture: joinir-architecture-overview.md