2025-12-17 05:03:07 +09:00
|
|
|
|
# Phase 100: Pinned Read‑Only Captures(設計メモ)
|
|
|
|
|
|
|
|
|
|
|
|
## 目的
|
|
|
|
|
|
|
|
|
|
|
|
JoinIR lowering のスコープ解決(`ConditionEnv → LoopBodyLocalEnv → CapturedEnv`)の前提を崩さずに、
|
|
|
|
|
|
「ループ外で定義した local(動的式でも可)」をループ内で receiver として参照できるようにする。
|
|
|
|
|
|
|
|
|
|
|
|
例(問題の形):
|
|
|
|
|
|
|
|
|
|
|
|
```nyash
|
|
|
|
|
|
local s = "a" + "b" // 条件式とは無関係
|
|
|
|
|
|
loop(i < n) {
|
|
|
|
|
|
local ch = s.substring(i, i + 1) // receiver 解決が必要
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## 方針(SSOT)
|
|
|
|
|
|
|
|
|
|
|
|
- “Pinned local” は **新しい Pattern ではなく**、ループに入ってくる **read‑only 入力**として扱う。
|
|
|
|
|
|
- SSOT は増やさず `CapturedEnv` に統合し、`CapturedKind` で区別する:
|
|
|
|
|
|
- `Explicit`: 従来の capture
|
|
|
|
|
|
- `Pinned`: ループ内参照のために必要な read‑only local
|
|
|
|
|
|
|
|
|
|
|
|
## Fail‑Fast 契約
|
|
|
|
|
|
|
|
|
|
|
|
- `Pinned` は loop body 内で **再代入されない**(assignment target に現れたら拒否)。
|
|
|
|
|
|
- loop entry 時点で host 側 ValueId が存在する(`variable_map` に無い場合は拒否)。
|
|
|
|
|
|
- 初期化が loop より後にある等、支配関係が曖昧な場合は拒否(理由付き)。
|
|
|
|
|
|
- init 式の副作用有無は問わない(評価はホスト側で1回、Pinned は値を渡すだけ)。
|
|
|
|
|
|
|
|
|
|
|
|
## Loop Canonicalizer との関係(混ぜない)
|
|
|
|
|
|
|
|
|
|
|
|
- Phase 100 は「ループ外の値が loop 内で見えない」問題(capture/wiring)を解く。
|
|
|
|
|
|
- Loop Canonicalizer は「ループ内の不変式を外に出す」AST 変換であり、別ユースケース。
|
|
|
|
|
|
|
2025-12-17 05:18:09 +09:00
|
|
|
|
## 予定(段階投入)
|
|
|
|
|
|
|
|
|
|
|
|
この Phase は「自由度を上げるほどバグが出やすい領域」なので、段階投入で進める。
|
|
|
|
|
|
|
|
|
|
|
|
- **P1(read‑only)**: ループ外 local を `Pinned` として `CapturedEnv` に取り込み、receiver 解決を通す。
|
|
|
|
|
|
- Fail‑Fast: loop body 内で再代入される変数は pinned 禁止(理由付き)
|
|
|
|
|
|
- **P2(mutable)**: loop body 内で再代入される外側変数は `Pinned` では扱わず、LoopState(carrier/env)に昇格して運ぶ。
|
|
|
|
|
|
- 初期は “更新形を限定” して shape guard を作り、未対応は Fail‑Fast(曖昧に通さない)
|
|
|
|
|
|
- **P3(hoist)**: ループ内の不変式の巻き上げは Loop Canonicalizer 側で扱う(別ユースケースとして分離)
|
2025-12-17 05:43:54 +09:00
|
|
|
|
|
|
|
|
|
|
## P1: Pinned Local Captures (Explicit/Pinned) - 完了 ✅
|
|
|
|
|
|
|
|
|
|
|
|
Implemented CapturedKind enum to distinguish:
|
|
|
|
|
|
- **Explicit**: Traditional captures (condition variables, carriers)
|
|
|
|
|
|
- **Pinned**: Phase 100 read-only loop-outer locals used as method receivers
|
|
|
|
|
|
|
|
|
|
|
|
**Status**: P1-1 through P1-5 complete - pinned receiver functionality validated with fixture+smoke tests.
|
|
|
|
|
|
|
|
|
|
|
|
Example (now works):
|
|
|
|
|
|
```hako
|
|
|
|
|
|
local s = "a" + "b" + "c" # Dynamic construction (loop-outer)
|
|
|
|
|
|
loop(i < 1) {
|
|
|
|
|
|
local ch = s.substring(i, i+1) # Pinned receiver works!
|
|
|
|
|
|
print(i)
|
|
|
|
|
|
i = i + 1
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**Smoke test**: phase100_pinned_local_receiver_vm.sh
|
|
|
|
|
|
|
|
|
|
|
|
### Implementation Details
|
|
|
|
|
|
|
|
|
|
|
|
**Search Order (SSOT)**:
|
|
|
|
|
|
1. ConditionEnv (loop-outer scope)
|
|
|
|
|
|
2. LoopBodyLocalEnv (body-local variables - Phase 226 cascading)
|
|
|
|
|
|
3. CapturedEnv (pinned loop-outer locals)
|
|
|
|
|
|
4. CarrierInfo (mutable carriers)
|
|
|
|
|
|
|
|
|
|
|
|
**Key Components**:
|
|
|
|
|
|
- `CapturedKind` enum: Distinguishes Explicit vs Pinned captures
|
|
|
|
|
|
- `PinnedLocalAnalyzer`: Identifies loop-outer read-only locals
|
|
|
|
|
|
- `loop_body_local_init.rs`: Updated receiver resolution with full search order
|
|
|
|
|
|
|
|
|
|
|
|
### Test Coverage
|
|
|
|
|
|
|
|
|
|
|
|
- **Fixture**: `apps/tests/phase100_pinned_local_receiver_min.hako`
|
|
|
|
|
|
- **Smoke Test**: `tools/smokes/v2/profiles/integration/apps/phase100_pinned_local_receiver_vm.sh`
|
|
|
|
|
|
- **Regression**: Phase 96 and Phase 94 smoke tests pass
|
2025-12-17 06:10:26 +09:00
|
|
|
|
|
|
|
|
|
|
## P2: Mutable Accumulators (s = s + x form only)
|
|
|
|
|
|
|
|
|
|
|
|
**Constraint**: Mutable variables are LIMITED to accumulator pattern.
|
|
|
|
|
|
|
|
|
|
|
|
**Allowed form**:
|
|
|
|
|
|
- `s = s + x` where x ∈ {Const, BodyLocal, Captured, Pinned, Carrier}
|
|
|
|
|
|
|
|
|
|
|
|
**Fail-Fast (未対応)**:
|
|
|
|
|
|
- Any other mutation: `s = f(...)` / `s = x + s` / `s += x` / multiple updates / conditional updates
|
|
|
|
|
|
- Reason: Shape explosion prevention, safe accumulator support (JSON/CSV building)
|
|
|
|
|
|
|
|
|
|
|
|
**Example** (now works):
|
|
|
|
|
|
```hako
|
|
|
|
|
|
local out = ""
|
|
|
|
|
|
loop(i < 2) {
|
|
|
|
|
|
local ch = "a"
|
|
|
|
|
|
out = out + ch # Accumulator form → LoopState carrier
|
|
|
|
|
|
i = i + 1
|
|
|
|
|
|
}
|
|
|
|
|
|
print(out.length()) # Output: 2
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**Implementation**:
|
|
|
|
|
|
- **P2-1**: MutableAccumulatorAnalyzer (AST shape detection only)
|
|
|
|
|
|
- **P2-2**: Pattern2 wiring (ScopeManager delegates read-only check)
|
|
|
|
|
|
- **P2-3**: Lowering (carrier update emission)
|
|
|
|
|
|
- **P2-4**: Integration test (fixture + smoke, length-based validation)
|
2025-12-17 06:31:21 +09:00
|
|
|
|
|
|
|
|
|
|
**LLVM EXE parity**: P2 accumulator を LLVM EXE smoke でも固定(phase100_mutable_accumulator_llvm_exe.sh)
|
2025-12-17 16:33:03 +09:00
|
|
|
|
|
|
|
|
|
|
## P3: String Accumulator (out = out + ch, Variable RHS only)
|
|
|
|
|
|
|
|
|
|
|
|
**Constraint**: String accumulator は最小形のみサポート。
|
|
|
|
|
|
|
|
|
|
|
|
**Allowed form**:
|
|
|
|
|
|
- `out = out + ch` where ch ∈ {Variable (string-ish)}
|
|
|
|
|
|
- RHS must be Variable (not Literal, not MethodCall)
|
|
|
|
|
|
|
|
|
|
|
|
**Fail-Fast (未対応)**:
|
|
|
|
|
|
- Literal RHS: `out = out + "x"` (P3.1 で解禁予定)
|
|
|
|
|
|
- MethodCall RHS: `out = out + substring(...)` (P3.2 で解禁予定)
|
|
|
|
|
|
- Non-string-ish RHS: 型判定は既存の box facts に委譲
|
|
|
|
|
|
|
|
|
|
|
|
**Example** (now works):
|
|
|
|
|
|
```hako
|
|
|
|
|
|
local out = ""
|
|
|
|
|
|
local s = "abc"
|
|
|
|
|
|
loop(i < 3) {
|
|
|
|
|
|
local ch = s.substring(i, i+1) # body-local string 1文字
|
|
|
|
|
|
out = out + ch # String accumulator
|
|
|
|
|
|
print(out.length()) # Output: 1, 2, 3
|
|
|
|
|
|
i = i + 1
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**Implementation**:
|
|
|
|
|
|
- **P3-1**: AccumulatorKind::{Int, String} 追加(型判定は委譲)
|
|
|
|
|
|
- **P3-2**: StringAccumulatorEmitter 専用箱(JoinIR lowering)
|
|
|
|
|
|
- **P3-3**: Pattern2 wiring(string carrier 昇格、emitter 分岐)
|
|
|
|
|
|
- **P3-4**: Fixture + smokes(VM + LLVM EXE)
|