Files
hakorune/src/mir/join_ir/ownership
nyash-codex 33f03d9775 refactor(joinir): Phase 86 - Carrier init builder, debug migration, error tags
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>
2025-12-13 21:48:02 +09:00
..

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

  1. carriers = owned_vars.filter(is_written)
  2. No variable in both owned and relay
  3. No variable in both owned and captures
  4. 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