2025-12-09 03:18:05 +09:00
|
|
|
|
# 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」を完了マークに更新
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 成功基準
|
|
|
|
|
|
|
2025-12-09 03:40:25 +09:00
|
|
|
|
- [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(ループパラメータ・条件変数)が最優先
|
|
|
|
|
|
- LoopBodyLocalEnv(body-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+)
|
2025-12-09 03:18:05 +09:00
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 関連ファイル
|
|
|
|
|
|
|
|
|
|
|
|
### インフラ(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`(退行確認)
|