Files
hakorune/docs/development/current/main/phase56-ownership-relay-design.md
2025-12-12 22:15:41 +09:00

286 lines
8.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Phase 56: Ownership-Relay Design
## Overview
「読むのは自由、管理は直下 owned だけ」アーキテクチャの設計文書。
Phase 56 は **インターフェース設計のみ**。実装は Phase 57 以降。
## Core Definitions
### owned (所有)
- 変数を定義したスコープが唯一の owner
- Loop直下の `local x` → そのループが owned
- body-localif/block内の local→ 最も内側の enclosing loop が owned
**例**:
```rust
fn outer() {
local a = 0 // owned by outer
loop {
local b = 0 // owned by this loop
if cond {
local c = 0 // owned by enclosing loop (not if!)
}
}
}
```
### carriers (管理対象)
- `carriers = writes ∩ owned`
- そのスコープが定義 AND 更新する変数のみ
- loop_step の引数として管理
**重要**: Carrier は「所有 AND 更新」のみ。読み取り専用の owned 変数は carrier ではない。
### captures (読み取り参照)
- `captures = reads \ owned` (かつ carriers ではない)
- 祖先スコープの変数への read-only アクセス
- CapturedEnv / ConditionEnv 経由
**例**:
```rust
local limit = 100
loop {
local sum = 0
if sum < limit { // limit は capture (read-only)
sum++ // sum は carrier (owned + written)
}
}
```
### relay (中継)
- 内側スコープが祖先 owned を更新する場合
- 更新責務を owner へ昇格relay up
- 中間ループは引数として素通し
**例**:
```rust
loop outer {
local total = 0 // owned by outer
loop inner {
total++ // relay to outer (inner doesn't own total)
}
}
// outer の exit PHI で total を merge
```
## Invariants (不変条件)
1. **Ownership Uniqueness**: 各変数は唯一の owner を持つ
2. **Carrier Locality**: carriers = writes ∩ owned (借用なし)
3. **Relay Propagation**: writes \ owned → owner に昇格
4. **Capture Read-Only**: captures は read-only (PHI 不要)
## Shadowing Rules
```nyash
local x = 0 // outer owned
loop {
local x = 1 // inner owned (shadows outer)
// 外の x は capture 可能だが、inner の x が優先
print(x) // inner の x (1)
}
print(x) // outer の x (0)
```
- Shadowing = 新しい ownership 発生
- 名前解決は最内スコープ優先
- 外の x は capture として参照可能だが、内の x が存在する限り内が優先
## Multi-Writer Merge
```nyash
loop outer {
local total = 0
if a { loop inner1 { total++ } } // relay to outer
if b { loop inner2 { total-- } } // relay to outer
}
// outer の exit PHI で merge
```
- Relay は「更新意図の伝達」
- 実際の PHI merge は owner 側で実行
- 複数の内側ループが同じ変数を relay → owner の exit PHI で統合
## JoinIR Mapping
### Current System → New System
| Current | New |
|---------|-----|
| CarrierInfo.carriers | OwnershipPlan.owned_vars (where is_written=true) |
| promoted_loopbodylocals | (absorbed into owned analysis) |
| CapturedEnv | OwnershipPlan.captures |
| ConditionEnv | OwnershipPlan.condition_captures |
| (implicit) | OwnershipPlan.relay_writes |
### OwnershipPlan Structure
```rust
pub struct OwnershipPlan {
pub scope_id: ScopeId,
pub owned_vars: Vec<ScopeOwnedVar>, // All owned vars (carriers = is_written subset)
pub relay_writes: Vec<RelayVar>,
pub captures: Vec<CapturedVar>,
pub condition_captures: Vec<CapturedVar>,
}
```
**設計意図**:
- `owned_vars`: このスコープが所有する変数(更新されるものは carriers
- `relay_writes`: 祖先の変数への書き込みowner へ昇格)
- `captures`: 祖先の変数への読み取り専用参照
- `condition_captures`: captures のうち、条件式で使われるもの
## Implementation Phases
- **Phase 56**: Design + interface skeleton (this phase) ✅
- **Phase 57**: OwnershipAnalyzer implementation (dev-only)
- **Phase 58**: P2 plumbing (dev-only)
- **Phase 59**: P3 plumbing (dev-only)
- **Phase 60**: Single-hop relay threading for fixtures (dev-only)
- **Phase 61**: P3 側の接続点を決めて段階接続dev-only
- まずは fixtures ルートProgram(JSON v0)で、if-sum+break を別箱として構造的に接続する
- 詳細: `docs/development/current/main/PHASE_61_SUMMARY.md`
- MIR→JoinIR の本番ルート(`pattern3_with_if_phi.rs`)へ寄せるのは別フェーズで設計→接続
## Module Boundary
`src/mir/join_ir/ownership/` - 責務は「解析のみ」
**This module does**:
- ✅ Collect reads/writes from AST/ProgramJSON
- ✅ Determine variable ownership (owned/relay/capture)
- ✅ Produce OwnershipPlan for downstream lowering
**This module does NOT**:
- ❌ Generate MIR instructions
- ❌ Modify JoinIR structures
- ❌ Perform lowering transformations
Lowering/MIR生成は既存モジュールが担当。
## Example Ownership Plans
### Example 1: Simple Loop
```nyash
local sum = 0
loop {
sum++
}
```
**OwnershipPlan (loop scope)**:
- `owned_vars`: [`sum` (is_written=true)]
- `relay_writes`: []
- `captures`: []
### Example 2: Nested Loop with Relay
```nyash
local total = 0
loop outer {
loop inner {
total++
}
}
```
**OwnershipPlan (inner loop)**:
- `owned_vars`: []
- `relay_writes`: [`total` → relay to outer]
- `captures`: []
**OwnershipPlan (outer loop)**:
- `owned_vars`: [`total` (is_written=true, via relay)]
- `relay_writes`: []
- `captures`: []
### Example 3: Capture + Carrier
```nyash
local limit = 100
loop {
local sum = 0
if sum < limit {
sum++
}
}
```
**OwnershipPlan (loop scope)**:
- `owned_vars`: [`sum` (is_written=true)]
- `relay_writes`: []
- `captures`: [`limit` (read-only)]
- `condition_captures`: [`limit`]
## References
- **Phase 53-54**: Structural axis expansion
- **Phase 43/245B**: Normalized JoinIR completion
- **ChatGPT discussion**: 「読むのは自由、管理は直下だけ」設計
- **JoinIR Architecture**: [joinir-architecture-overview.md](joinir-architecture-overview.md)
## Phase 57: Algorithm Implementation
### Analysis Steps
1. **Scope Tree Construction**
- Function/Loop/Block/If each get a ScopeId
- Parent-child relationships tracked
2. **Variable Collection (per scope)**
- `defined`: Variables declared with `local` in this scope
- `reads`: All variable reads (including nested)
- `writes`: All variable writes (including nested)
- `condition_reads`: Variables read in loop/if conditions
3. **Ownership Assignment**
- Body-local rule: `local` in if/block → enclosing Loop/Function owns it
- `owned_vars` = variables defined in Loop/Function scopes
4. **Plan Generation**
- `carriers` = owned_vars where is_written=true
- `relay_writes` = writes - owned (find owner in ancestors)
- `captures` = reads - owned - writes (read-only)
- `condition_captures` = captures ∩ condition_reads
### Implementation Details
**Input JSON Compatibility**:
- テスト用の簡易スキーマ: top-level `functions` + stmt/expr の `kind` を解釈
- Program(JSON v0): top-level `defs` + stmt/expr の `type` を解釈
- `Local` は JSON v0 で「新規束縛」と「rebind/update」が混在し得るため、
解析では「scope chain で既に定義済みなら write / 未定義なら define」として扱うdev-only 前提)。
- Note: `docs/private` は submodule のため、fixture JSON を参照する場合は submodule 側で追跡されていることを前提とする。
**Body-Local Ownership Rule**:
```rust
// Example: local in if/block → enclosing loop owns it
loop {
if cond {
local temp = 0 // owned by LOOP, not if!
}
}
```
**Relay Path Construction**:
- Walk up ancestor chain to find owner
- Collect only Loop scopes in relay_path (skip If/Block)
- Inner loop → Outer loop → Function (relay chain)
**Invariant Verification** (debug builds):
- No variable in multiple categories (owned/relay/capture)
- All relay_writes have valid owners
- condition_captures ⊆ captures
## Status
- ✅ Phase 56: Design + interface skeleton completed
- ✅ Phase 57: Analyzer implemented (dev-only)
- ✅ Phase 58-59: plan_to_lowering helpers (P2/P3) with Fail-Fast relay
- ✅ Phase 60 (dev-only): single-hop relay threading for P2 fixtures
- `plan_to_p2_inputs_with_relay` promotes relay_writes to carriers (relay_path.len()<=1 only)
- Frontend Break(P2) lowering uses ownership-with-relay; legacy path preserved for comparison
- P3 stays analysis-only; real threading is Phase 61+