Files
hakorune/docs/development/current/main/phase191-body-local-integration.md
nyash-codex ba33bfc323 feat(joinir): Phase 191 body-local init integration into Pattern2
- Integrated LoopBodyLocalInitLowerer into Pattern2 lowering
- Fixed ValueId double-allocation issue (delegate to InitLowerer)
- Added body_ast parameter to lower_loop_with_break_minimal()
- Fixed json_program_loop.rs test for body-local scope
- New test: phase191_body_local_atoi.hako (expected: 123)

Supported init expressions:
- Integer literals, variable references, binary operations

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-09 03:40:25 +09:00

292 lines
9.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 191: Body-Local Init & Update Lowering Integration
**Status**: Ready for Implementation
**Date**: 2025-12-09
**Prerequisite**: Phase 190-impl complete (NumberAccumulation + PHI wiring fixed)
---
## 目的
Phase 184/186 で作った `LoopBodyLocalEnv`, `UpdateEnv`, `LoopBodyLocalInitLowerer`
Pattern2/4 の中に本番統合して、`local digit = ...` などの body-local を JoinIR/MIR にきちんと落とす。
P2/P4 の既存ループ(`_atoi` などを、body-local を含んだ形でも JoinIR で動かせるようにする。
---
## Task 191-1: 対象ケースの再確認1本に絞る
### 目標
「いま body-local が原因で JoinIR を OFF にしている代表ループ」を 1 本だけ選ぶ。
### 候補
```nyash
// _atoi 簡略版
local digit = s[i] - '0'
result = result * 10 + digit
```
または body-local を使う最小テスト:
```nyash
// phase191_body_local_atoi.hako
static box Main {
main() {
local result = 0
local i = 0
loop(i < 3) {
local digit = i + 1 // body-local
result = result * 10 + digit
i = i + 1
}
print(result) // Expected: 123
return 0
}
}
```
### 成果物
- 選定した代表ループのファイルパス
- なぜ現在 JoinIR で通らないかの説明body-local init が emit されていない)
---
## Task 191-2: LoopBodyLocalInitLowerer の統合
### 対象ファイル
- `src/mir/builder/control_flow/joinir/patterns/pattern2_with_break.rs`
- 必要なら `pattern4_with_continue.rs`
### 実装内容
ループ lowering の流れの中で:
1. ループ本体 AST を `LoopBodyLocalInitLowerer` に渡して、
body-local の `local` 定義 (`digit = ...`) を JoinIR に emit しつつ、
`LoopBodyLocalEnv``name -> join_id` を登録させる。
2. ここで emit するのは「init 分」だけUpdate 分は既に `CarrierUpdateEmitter` + `UpdateEnv` に任せる)。
### init emission の位置
- ループ body の **先頭**carrier update より前)に出るようにする。
- JoinIR 側の ValueId 空間は、Phase 190-impl-D での衝突回避ルール (`body_local_start_offset`) に従う。
### コード変更例
```rust
// pattern2_with_break.rs 内
// Phase 191: Emit body-local initializations
let body_local_env = LoopBodyLocalInitLowerer::lower_init(
&body_ast,
&mut join_builder,
&mut alloc_body_local_value,
)?;
// Phase 191: Create UpdateEnv with both condition and body-local
let update_env = UpdateEnv::new(condition_env.clone(), body_local_env.clone());
// Emit carrier updates using UpdateEnv
for (carrier_name, update_expr) in &carrier_info.updates {
CarrierUpdateEmitter::emit_carrier_update_with_env(
&mut join_builder,
carrier_name,
update_expr,
&update_env,
)?;
}
```
---
## Task 191-3: UpdateEnv 統合の確認
### 対象ファイル
- `src/mir/join_ir/lowering/carrier_update_emitter.rs`(すでに UpdateEnv 対応済み)
### 確認内容
選んだ代表ループで:
- `UpdateEnv` が:
- `ConditionEnv`LoopParam / OuterLocal
- `LoopBodyLocalEnv`(今回 lower した init の join_id
両方を見て `digit``temp` を正しく解決できているか確認。
### ユニットテスト追加
body-local `digit` を含む NumberAccumulation 更新を与えて、
JoinIR 側で Mul/Add 生成がエラーなく通ること:
```rust
#[test]
fn test_number_accumulation_with_body_local() {
// UpdateEnv with body-local "digit" → ValueId(10)
let mut body_local_env = LoopBodyLocalEnv::new();
body_local_env.register("digit", ValueId(10));
let condition_env = ConditionEnv::new();
let update_env = UpdateEnv::new(condition_env, body_local_env);
// Resolve "digit" should return ValueId(10)
assert_eq!(update_env.resolve("digit"), Some(ValueId(10)));
}
```
---
## Task 191-4: E2E テスト
### テストケース
191-1 で選んだ Minimal `_atoi` ループを:
- body-local 版(`local digit = ...`)に戻して、
- `NYASH_JOINIR_CORE=1` で実行 → 期待値が返ることを確認。
```bash
# E2E テスト実行
./target/release/hakorune apps/tests/phase191_body_local_atoi.hako
# Expected output: 123
```
### 退行チェック
- 既存の int-only ループbody-local なし)が引き続き動くこと
- `phase190_atoi_impl.hako` → 12
- `phase190_parse_number_impl.hako` → 123
- Trim/JsonParser の P5/P2 ラインに影響がないこと
---
## Task 191-5: ドキュメント更新
### 更新対象
1. **phase184-body-local-mir-lowering.md** / **phase186-body-local-init-lowering.md**:
- 「Phase 191 で Pattern2/4 に統合完了」と追記
- 代表ループ名と、init → UpdateEnv → PHI → ExitLine までの流れを 1 ケースだけ図解
2. **CURRENT_TASK.md**:
- Phase 191: 完了マーク
- 残タスク(もし他のループは後回しにするなら)を更新
3. **joinir-architecture-overview.md**:
- Section 7.2 の残タスク「body-local 変数の init + update lowering」を完了マークに更新
---
## 成功基準
- [x] 代表ループbody-local 版 `_atoi`)が JoinIR only で期待値を返す
- [x] 既存テストphase190_*.hakoが退行しない
- [x] UpdateEnv が body-local 変数を正しく解決できる
- [x] ドキュメントが更新されている
---
## 実装完了レポート (2025-12-09)
### 実装概要
Phase 191 が**完全成功**しました! `LoopBodyLocalInitLowerer` を Pattern2 に統合し、body-local 変数の初期化式を JoinIR に正しく降下できるようになりました。
### 主な変更
1. **`loop_with_break_minimal.rs`**:
- 関数シグネチャに `body_ast: &[ASTNode]` を追加
- `body_local_env: Option<&mut LoopBodyLocalEnv>` に変更mutable
- ループ body の先頭で `LoopBodyLocalInitLowerer` を呼び出し、初期化式を JoinIR に emit
- Carrier update の前に body-local init を配置(正しい依存順序)
2. **`pattern2_with_break.rs`**:
- `collect_body_local_variables` 呼び出しを削除ValueId の二重割り当てを回避)
- 空の `LoopBodyLocalEnv` を作成し、`LoopBodyLocalInitLowerer` に委譲
- `lower_loop_with_break_minimal``_body` AST を渡すよう修正
3. **テストファイル**:
- `apps/tests/phase191_body_local_atoi.hako` 新規作成
- `local digit = i + 1` パターンで body-local 変数を使用
- `result = result * 10 + digit` で NumberAccumulation パターン検証
4. **テスト修正**:
- `tests/json_program_loop.rs``program_loop_body_local_exit` を修正
- スコープ外の body-local 変数参照を削除(正しい動作に修正)
### 実行結果
```bash
$ ./target/release/hakorune apps/tests/phase191_body_local_atoi.hako
123 # ✅ 期待値通り
$ ./target/release/hakorune apps/tests/phase190_atoi_impl.hako
12 # ✅ 退行なし
$ ./target/release/hakorune apps/tests/phase190_parse_number_impl.hako
123 # ✅ 退行なし
$ cargo test --release json_program_loop
test json_loop_simple_verifies ... ok
test json_loop_body_local_exit_verifies ... ok
test json_loop_with_continue_verifies ... ok
# ✅ 全テストパス
```
### 技術的発見
1. **ValueId 二重割り当て問題**:
- 旧実装: `collect_body_local_variables` が ValueId を事前割り当て → `LoopBodyLocalInitLowerer` がスキップ
- 解決: 空の `LoopBodyLocalEnv` を渡し、`LoopBodyLocalInitLowerer` に完全委譲
2. **JoinIR ValueId 空間**:
- JoinIR は独立した ValueId 空間を持つ0 から開始)
- Host ValueId へのマッピングは merge 段階で実施
- `body_local_start_offset` は Host 空間の概念であり、JoinIR には不要
3. **UpdateEnv の優先順位**:
- ConditionEnvループパラメータ・条件変数が最優先
- LoopBodyLocalEnvbody-local 変数)がフォールバック
- この順序により、シャドウイングが正しく動作
### 制限事項
現在の実装では以下の初期化式のみサポート:
- 整数リテラル: `local x = 42`
- 変数参照: `local y = i`
- 二項演算: `local z = i + 1`, `local w = pos - start`
未サポートFail-Fast:
- メソッド呼び出し: `local s = str.substring(...)`
- 文字列操作: `local t = s + "abc"`
- 複雑な式: ネストした呼び出し、非算術演算
### 次のステップ
Phase 191 で以下が達成されました:
- [x] Pattern 2 への body-local init lowering 統合
- [x] UpdateEnv による body-local 変数解決
- [x] NumberAccumulation パターンでの動作検証
- [x] 既存テストの退行なし
今後の拡張:
- Pattern 4 (continue) への同様の統合(必要に応じて)
- 文字列初期化式のサポートPhase 188 の延長)
- メソッド呼び出し初期化式のサポートPhase 192+
---
## 関連ファイル
### インフラPhase 184 で実装済み)
- `src/mir/join_ir/lowering/loop_body_local_env.rs`
- `src/mir/join_ir/lowering/update_env.rs`
- `src/mir/join_ir/lowering/carrier_update_emitter.rs`
### 統合対象
- `src/mir/builder/control_flow/joinir/patterns/pattern2_with_break.rs`
- `src/mir/builder/control_flow/joinir/patterns/pattern4_with_continue.rs`
### テストファイル
- `apps/tests/phase191_body_local_atoi.hako`(新規作成)
- `apps/tests/phase190_atoi_impl.hako`(退行確認)
- `apps/tests/phase190_parse_number_impl.hako`(退行確認)