docs: Phase 191 completion + Phase 192 Complex addend design

- Updated joinir-architecture-overview.md:
  - Phase 191 body-local init integration complete
  - Phase 192 Complex addend normalization strategy
- Updated CURRENT_TASK.md: Phase 191 complete with results
- Created phase192-complex-addend-design.md:
  - ComplexAddendNormalizer design (temp variable decomposition)
  - Integration with LoopUpdateAnalyzer and Pattern2
  - No changes to emission layer (reuses existing boxes)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
nyash-codex
2025-12-09 03:59:53 +09:00
parent ba33bfc323
commit b7bf4a721e
3 changed files with 488 additions and 17 deletions

View File

@ -245,13 +245,18 @@
- **E2E 結果**: `phase190_atoi_impl.hako` → 12 ✅、`phase190_parse_number_impl.hako` → 123 ✅ - **E2E 結果**: `phase190_atoi_impl.hako` → 12 ✅、`phase190_parse_number_impl.hako` → 123 ✅
- ExitLine contract Verifier 追加(`#[cfg(debug_assertions)]` - ExitLine contract Verifier 追加(`#[cfg(debug_assertions)]`
- **残課題**: body-local 変数 assignment は JoinIR 未対応Phase 191 で対応) - **残課題**: body-local 変数 assignment は JoinIR 未対応Phase 191 で対応)
- [ ] **Phase 191: Body-Local Init & Update Lowering Integration** - [x] **Phase 191: Body-Local Init & Update Lowering Integration** ✅ (2025-12-09)
- **目的**: LoopBodyLocalEnv / UpdateEnv / LoopBodyLocalInitLowerer を Pattern2/4 に本番統合 - **目的**: LoopBodyLocalEnv / UpdateEnv / LoopBodyLocalInitLowerer を Pattern2 に本番統合完了
- 191-1: 対象ケース選定body-local が原因で JoinIR OFF な代表ループ 1 本) - 191-1: 対象ケース選定`phase191_body_local_atoi.hako` 作成 ✅
- 191-2: LoopBodyLocalInitLowerer の Pattern2 統合init emission 位置は body 先頭) - 191-2: LoopBodyLocalInitLowerer の Pattern2 統合init emission 位置は body 先頭)
- 191-3: UpdateEnv 統合確認ConditionEnv + LoopBodyLocalEnv 両方から解決) - 191-3: UpdateEnv 統合確認ConditionEnv + LoopBodyLocalEnv 両方から解決)
- 191-4: E2E テストbody-local 版 _atoi で期待値確認) - 191-4: E2E テスト → 期待値 123 出力確認 ✅
- 191-5: ドキュメント更新phase184/186.md + CURRENT_TASK - 191-5: ドキュメント更新
- **成果**:
- 対応済み init 式: 整数リテラル、変数参照、二項演算(`local digit = i + 1`
- UpdateEnv 優先順位: ConditionEnv→ LoopBodyLocalEnvフォールバック
- ValueId 二重割当問題を修正(空の LoopBodyLocalEnv を InitLowerer に委譲)
- **テスト**: phase191_body_local_atoi.hako → 123 ✅、退行なし
- [ ] Phase 192: Complex addend 対応 - [ ] Phase 192: Complex addend 対応
- `v = v * 10 + f(x)` のような method call を含むパターン - `v = v * 10 + f(x)` のような method call を含むパターン
- addend を事前計算して temp に格納 - addend を事前計算して temp に格納

View File

@ -169,11 +169,12 @@ JoinIR ラインで守るべきルールを先に書いておくよ:
- else-continue を then-continue へ正規化し、Select ベースの continue を簡潔にする。 - else-continue を then-continue へ正規化し、Select ベースの continue を簡潔にする。
- ループ本体で実際に更新されるキャリアだけを抽出Pattern 4 で不要キャリアを排除)。 - ループ本体で実際に更新されるキャリアだけを抽出Pattern 4 で不要キャリアを排除)。
- **LoopBodyLocalEnv / UpdateEnv / CarrierUpdateEmitterPhase 184** - **LoopBodyLocalEnv / UpdateEnv / CarrierUpdateEmitterPhase 184, 191統合完了**
- ファイル: - ファイル:
- `src/mir/join_ir/lowering/loop_body_local_env.rs` - `src/mir/join_ir/lowering/loop_body_local_env.rs`
- `src/mir/join_ir/lowering/update_env.rs` - `src/mir/join_ir/lowering/update_env.rs`
- `src/mir/join_ir/lowering/carrier_update_emitter.rs` - `src/mir/join_ir/lowering/carrier_update_emitter.rs`
- `src/mir/join_ir/lowering/loop_with_break_minimal.rs`Phase 191統合
- 責務: - 責務:
- **LoopBodyLocalEnv**: ループ本体で宣言された body-local 変数の名前→ValueId マッピングを管理(箱化設計)。 - **LoopBodyLocalEnv**: ループ本体で宣言された body-local 変数の名前→ValueId マッピングを管理(箱化設計)。
- **UpdateEnv**: 条件変数ConditionEnvと body-local 変数LoopBodyLocalEnvを統合した変数解決層。 - **UpdateEnv**: 条件変数ConditionEnvと body-local 変数LoopBodyLocalEnvを統合した変数解決層。
@ -181,10 +182,13 @@ JoinIR ラインで守るべきルールを先に書いておくよ:
- **CarrierUpdateEmitter**: UpdateExpr を JoinIR 命令に変換する際、UpdateEnv を使用して body-local 変数をサポート。 - **CarrierUpdateEmitter**: UpdateExpr を JoinIR 命令に変換する際、UpdateEnv を使用して body-local 変数をサポート。
- `emit_carrier_update_with_env()`: UpdateEnv 版Phase 184 新規) - `emit_carrier_update_with_env()`: UpdateEnv 版Phase 184 新規)
- `emit_carrier_update()`: ConditionEnv 版(後方互換) - `emit_carrier_update()`: ConditionEnv 版(後方互換)
- **LoopBodyLocalInitLowerer**: Phase 191 で Pattern2 に統合完了。
- 対応済み init 式: 整数リテラル、変数参照、二項演算(`local digit = i + 1`
- UpdateEnv の優先順位により ConditionEnv → LoopBodyLocalEnv の順で変数解決
- 設計原則: - 設計原則:
- **箱理論**: 各 Box が単一責任を持ち、境界明確。 - **箱理論**: 各 Box が単一責任を持ち、境界明確。
- **決定性**: BTreeMap 使用で一貫した順序保証PHI 生成の決定性)。 - **決定性**: BTreeMap 使用で一貫した順序保証PHI 生成の決定性)。
- **保守的**: Pattern5 (Trim) は対象外、Phase 185 で統合予定 - **Phase 192予定**: Complex addend`v = v*10 + f(x)`)は Normalizer で temp に落としてから NumberAccumulation に載せる
### 2.3 キャリア / Exit / Boundary ライン ### 2.3 キャリア / Exit / Boundary ライン
@ -489,18 +493,19 @@ JoinIR は Rust 側だけでなく、将来的に .hako selfhost コンパイラ
- ✅ JoinIRVerifier: デバッグビルドで契約検証 - ✅ JoinIRVerifier: デバッグビルドで契約検証
- ✅ ExitLine Contract Verifier: PHI 配線検証Phase 190-impl-D - ✅ ExitLine Contract Verifier: PHI 配線検証Phase 190-impl-D
### 7.2 残タスクPhase 191+ で対応予定) ### 7.2 残タスクPhase 192+ で対応予定)
1. **body-local 変数の init + update loweringPhase 186/191 ライン)** 1. **body-local 変数の init + update lowering** → Phase 191 完了
- `local digit = s[i] - '0'` のような body-local 変数の JoinIR/MIR 生成 - `local digit = i + 1` のような body-local 変数の JoinIR/MIR 生成完了
- LoopBodyLocalEnv / UpdateEnv インフラは Phase 184 で実装済み - 対応済み: 整数リテラル、変数参照、二項演算
- Pattern2/4 への統合が残タスク - テスト: `phase191_body_local_atoi.hako` → 期待値 123 ✅
2. **Complex addend 対応Phase 191+** 2. **Complex addend 対応Phase 192**
- `v = v * 10 + digits.indexOf(ch)` のような method call を含む NumberAccumulation - `v = v * 10 + digits.indexOf(ch)` のような method call を含む NumberAccumulation
- 現状は Fail-Fast で拒否、将来的に addend を事前計算して temp に格納 - 方針: ComplexAddendNormalizer で `temp = f(x)` に分解してから NumberAccumulation に載せる
- 現状は Fail-Fast で拒否
3. **JsonParser 残り複雑ループ・selfhost ループへの適用** 3. **JsonParser 残り複雑ループ・selfhost ループへの適用Phase 193+**
- `_parse_array` / `_parse_object`P4 Continue + 複数 MethodCall - `_parse_array` / `_parse_object`P4 Continue + 複数 MethodCall
- `_unescape_string`(複雑なキャリア + flatten - `_unescape_string`(複雑なキャリア + flatten
- selfhost `.hako` コンパイラの全ループを JoinIR で処理 - selfhost `.hako` コンパイラの全ループを JoinIR で処理

View File

@ -0,0 +1,461 @@
# Phase 192: Complex Addend Design (Doc-Only)
**Status**: Design Phase
**Date**: 2025-12-09
**Prerequisite**: Phase 191 complete (body-local init integrated)
---
## 目的
`result = result * 10 + digits.indexOf(ch)` のような
「加算側がメソッド呼び出し」のパターンを、
既存の NumberAccumulation ラインの前処理として安全に扱えるようにする。
新パターンは増やさず、Analyzer/Lowerer の箱を拡張するだけに留める。
---
## Section 1: 対象 RHS パターン一覧
### 1.1 JsonParser._parse_number (lines 106-142)
**ループパターン**:
```nyash
local num_str = ""
local digits = "0123456789"
loop(p < s.length()) {
local ch = s.substring(p, p+1)
local digit_pos = digits.indexOf(ch)
if digit_pos < 0 {
break
}
num_str = num_str + ch // ← StringAppendChar (already supported)
p = p + 1
}
```
**Note**: このループは string accumulation なので Phase 192 の対象外(既にサポート済み)。
### 1.2 JsonParser._atoi (lines 436-467)
**ループパターン**:
```nyash
local v = 0
local digits = "0123456789"
loop(i < n) {
local ch = s.substring(i, i+1)
if ch < "0" || ch > "9" { break }
local pos = digits.indexOf(ch)
if pos < 0 { break }
v = v * 10 + pos // ← NumberAccumulation with body-local (Phase 191 supported)
i = i + 1
}
```
**Current Status**: Phase 191 で body-local `pos` が使えるようになったので対応可能。
### 1.3 Complex Addend Pattern (Target for Phase 192)
**パターン**:
```nyash
local v = 0
local digits = "0123456789"
loop(i < n) {
local ch = s.substring(i, i+1)
v = v * 10 + digits.indexOf(ch) // ← Complex addend (method call)
i = i + 1
}
```
**AST Form**:
```
Assign(
lhs = "v",
rhs = BinaryOp(
op = Add,
left = BinaryOp(
op = Mul,
left = Variable("v"),
right = Literal(Integer(10))
),
right = MethodCall(
receiver = Variable("digits"),
method = "indexOf",
args = [Variable("ch")]
)
)
)
```
**Characteristics**:
- LHS appears exactly once in RHS (in left-most multiplication)
- Base: 10 (constant)
- Addend: **MethodCall** (complex expression)
- Current behavior: Rejected as `UpdateKind::Complex`
---
## Section 2: LHS 出現回数とMethod Call位置の整理
### 2.1 パターンマトリクス
| Pattern | LHS Count | Base | Addend | Current | Phase 192 |
|---------|-----------|------|--------|---------|-----------|
| `v = v * 10 + pos` | 1 | Const(10) | Variable | NumberAccumulation | No change |
| `v = v * 10 + 5` | 1 | Const(10) | Const(5) | NumberAccumulation | No change |
| `v = v * 10 + digits.indexOf(ch)` | 1 | Const(10) | MethodCall | **Complex** | **Normalize** |
| `v = v * base + x` | 1 | Variable | Variable | Complex | Fail-Fast |
| `v = v * 10 + (a + b)` | 1 | Const(10) | BinaryOp | Complex | Future |
| `v = v * 10 + v` | 2 | Const(10) | Variable | Complex | Fail-Fast |
### 2.2 Method Call の位置分類
**Type A: Addend に Method CallPhase 192 対象)**:
```nyash
v = v * 10 + digits.indexOf(ch)
```
→ Normalize: `temp = digits.indexOf(ch); v = v * 10 + temp`
**Type B: Base に Method Call対象外**:
```nyash
v = v * getBase() + x
```
→ Fail-Fastbase は constant のみ許可)
**Type C: LHS 側に Method Call対象外**:
```nyash
v = obj.getValue() * 10 + x
```
→ Fail-FastLHS は simple variable のみ)
### 2.3 Nested Method Call将来拡張
```nyash
v = v * 10 + parser.parse(s.substring(i, i+1))
```
→ Phase 192 では Fail-Fast、Phase 193+ で対応検討
---
## Section 3: Temp 分解戦略
### 3.1 正規化アプローチ
**Before (Complex)**:
```nyash
result = result * 10 + digits.indexOf(ch)
```
**After (Normalized)**:
```nyash
local temp_digit = digits.indexOf(ch)
result = result * 10 + temp_digit
```
### 3.2 正規化の責務
新しい箱: **ComplexAddendNormalizer**
**入力**:
- `Assign(lhs, complex_rhs)` where `complex_rhs` has MethodCall in addend
- ループ本体 ASTtemp 変数を挿入する位置情報)
**出力**:
- `temp` 定義: `local temp = methodCall(...)`
- 正規化された Assign: `lhs = lhs * base + temp`
**配置**:
- Pattern2 lowering の前処理ライン(`can_lower` の前)
---
## Section 4: ComplexAddendNormalizer 擬似コード
### 4.1 検出ロジック
```rust
fn is_complex_addend_pattern(update_expr: &UpdateExpr) -> bool {
// Check structure: lhs = lhs * base + addend
let UpdateExpr::BinOp { op: BinOpKind::Add, left, right } = update_expr else {
return false;
};
// Left side: lhs * base (multiplication)
let UpdateRhs::BinOp { op: BinOpKind::Mul, .. } = left else {
return false;
};
// Right side (addend): MethodCall or complex expression
matches!(right, UpdateRhs::MethodCall(_) | UpdateRhs::BinaryOp(_))
}
```
### 4.2 正規化ロジック
```rust
fn normalize_complex_addend(
lhs: &str,
update_expr: &UpdateExpr,
body_ast: &mut Vec<ASTNode>,
) -> Result<(String, UpdateExpr), String> {
// Extract addend (method call or complex expression)
let addend = extract_addend(update_expr)?;
// Generate temp variable name
let temp_name = format!("temp_{}_addend", lhs);
// Insert temp assignment at current position
// local temp = digits.indexOf(ch)
let temp_init = ASTNode::LocalDeclaration {
name: temp_name.clone(),
init: Some(Box::new(addend.to_ast())),
};
body_ast.insert(0, temp_init); // Insert at loop body start
// Create normalized update expression
// lhs = lhs * 10 + temp
let normalized_expr = UpdateExpr::BinOp {
op: BinOpKind::Add,
left: Box::new(UpdateRhs::BinOp {
op: BinOpKind::Mul,
left: Box::new(UpdateRhs::Variable(lhs.to_string())),
right: Box::new(UpdateRhs::Const(extract_base(update_expr)?)),
}),
right: Box::new(UpdateRhs::Variable(temp_name.clone())),
};
Ok((temp_name, normalized_expr))
}
```
### 4.3 Phase 191 との統合
Phase 191 の `LoopBodyLocalInitLowerer``temp` の初期化を処理:
- `local temp_digit = digits.indexOf(ch)` → JoinIR に emit
- `LoopBodyLocalEnv``temp_digit -> join_id` を登録
- `UpdateEnv``temp_digit` を解決して NumberAccumulation に渡す
---
## Section 5: LoopUpdateAnalyzer との責務分け
### 5.1 現在のフロー
```
AST → LoopUpdateAnalyzer → UpdateKind::Complex (Fail-Fast)
```
### 5.2 Phase 192 フロー
```
AST → LoopUpdateAnalyzer → UpdateKind::Complex
↓ (Pattern2 内 can_lower 前)
ComplexAddendNormalizer → 前処理 (temp 生成)
再度 LoopUpdateAnalyzer → UpdateKind::NumberAccumulation { base: 10 }
Pattern2 lowering → JoinIR emission
```
### 5.3 can_lower の変更点
**Before (Phase 191)**:
```rust
fn can_lower_carrier_updates(updates: &HashMap<String, UpdateExpr>) -> bool {
for (name, update_expr) in updates {
let kind = classify_update_kind(update_expr);
match kind {
UpdateKind::Complex => {
eprintln!("[joinir/freeze] Complex carrier update");
return false;
}
_ => { /* OK */ }
}
}
true
}
```
**After (Phase 192)**:
```rust
fn can_lower_carrier_updates_with_normalization(
updates: &HashMap<String, UpdateExpr>,
body_ast: &mut Vec<ASTNode>,
) -> Result<HashMap<String, UpdateExpr>, String> {
let mut normalized_updates = HashMap::new();
for (name, update_expr) in updates {
let kind = classify_update_kind(update_expr);
match kind {
UpdateKind::Complex => {
// Try normalization
if is_complex_addend_pattern(update_expr) {
let (temp_name, normalized_expr) =
ComplexAddendNormalizer::normalize(name, update_expr, body_ast)?;
// Re-classify
let normalized_kind = classify_update_kind(&normalized_expr);
if matches!(normalized_kind, UpdateKind::NumberAccumulation { .. }) {
normalized_updates.insert(name.clone(), normalized_expr);
} else {
return Err("[joinir/freeze] Normalization failed".to_string());
}
} else {
return Err("[joinir/freeze] Complex pattern not supported".to_string());
}
}
_ => {
normalized_updates.insert(name.clone(), update_expr.clone());
}
}
}
Ok(normalized_updates)
}
```
---
## Section 6: Emission 側への影響
### 6.1 JoinIR Emission変更なし
ComplexAddendNormalizer で前処理するため、既存の emission ラインは変更不要:
1. **LoopBodyLocalInitLowerer** (Phase 191):
- `local temp_digit = digits.indexOf(ch)` を JoinIR に emit
- MethodCall の lowering は既存の式 lowerer に委譲
2. **CarrierUpdateLowerer** (Phase 190):
- `result = result * 10 + temp_digit` を NumberAccumulation として emission
- `temp_digit` は UpdateEnv 経由で解決
3. **NumberAccumulation emission**:
- Phase 190 の 2-instruction emission そのまま:
```
tmp = result * 10
result = tmp + temp_digit
```
### 6.2 設計原則
- **Separation of Concerns**: 正規化 (ComplexAddendNormalizer) と emission (CarrierUpdateLowerer) を分離
- **Reusability**: 既存の body-local init / NumberAccumulation emission を再利用
- **Fail-Fast**: 対応できないパターンは明示的エラー
---
## Section 7: Implementation Phases (TBD)
### Phase 192-impl-A: ComplexAddendNormalizer 実装
1. `is_complex_addend_pattern()` 検出ロジック
2. `normalize_complex_addend()` 正規化ロジック
3. temp 変数生成とAST挿入
### Phase 192-impl-B: Pattern2 統合
1. `can_lower` を `can_lower_with_normalization` に拡張
2. 正規化後の UpdateExpr で再解析
3. Unit tests (5+ cases)
### Phase 192-impl-C: E2E テスト
1. `phase192_complex_addend_atoi.hako` 作成
2. `result = result * 10 + digits.indexOf(ch)` パターンで期待値確認
3. 退行テストphase191_*.hako
---
## Section 8: 制約と Non-Goals
### 8.1 対応パターン
**Phase 192 で対応**:
- Addend に simple MethodCall: `v = v * 10 + digits.indexOf(ch)`
- Addend が Variable の場合は Phase 191 で対応済み
**Phase 192 で非対応(将来拡張)**:
- Nested method call: `v = v * 10 + parser.parse(s.substring(...))`
- Complex binary expression: `v = v * 10 + (a + b * c)`
- Multiple method calls in same update
### 8.2 Fail-Fast ケース
- Base が variable: `v = v * base + f(x)`
- LHS が複数回出現: `v = v * 10 + v`
- Method call が base 側: `v = v * getBase() + x`
---
## Appendix A: AST Examples
### A.1 Before Normalization
**Source**:
```nyash
result = result * 10 + digits.indexOf(ch)
```
**AST**:
```
Assignment {
target: Variable { name: "result" },
value: BinaryOp {
operator: Add,
left: BinaryOp {
operator: Mul,
left: Variable { name: "result" },
right: Literal { value: Integer(10) },
},
right: MethodCall {
receiver: Variable { name: "digits" },
method: "indexOf",
args: [Variable { name: "ch" }],
},
},
}
```
### A.2 After Normalization
**Source**:
```nyash
local temp_result_addend = digits.indexOf(ch)
result = result * 10 + temp_result_addend
```
**AST**:
```
[
LocalDeclaration {
name: "temp_result_addend",
init: Some(MethodCall {
receiver: Variable { name: "digits" },
method: "indexOf",
args: [Variable { name: "ch" }],
}),
},
Assignment {
target: Variable { name: "result" },
value: BinaryOp {
operator: Add,
left: BinaryOp {
operator: Mul,
left: Variable { name: "result" },
right: Literal { value: Integer(10) },
},
right: Variable { name: "temp_result_addend" },
},
},
]
```
---
## Revision History
- **2025-12-09**: Initial design document (Section 1-8)