feat(loop-phi): Add body-local variable PHI generation for Rust AST loops
Phase 25.1c/k: Fix ValueId undefined errors in loops with body-local variables **Problem:** - FuncScannerBox.scan_all_boxes/1 and BreakFinderBox._find_loops/2 had ValueId undefined errors for variables declared inside loop bodies - LoopFormBuilder only generated PHIs for preheader variables, missing body-locals - Example: `local ch = s.substring(i, i+1)` inside loop → undefined on next iteration **Solution:** 1. **Rust AST path** (src/mir/loop_builder.rs): - Detect body-local variables by comparing body_end_vars vs current_vars - Generate empty PHI nodes at loop header for body-local variables - Seal PHIs with latch + continue snapshot inputs after seal_phis() - Added HAKO_LOOP_PHI_TRACE=1 logging for debugging 2. **JSON v0 path** (already fixed in previous session): - src/runner/json_v0_bridge/lowering/loop_.rs handles body-locals - Uses same strategy but for JSON v0 bridge lowering **Results:** - ✅ FuncScannerBox.scan_all_boxes: 41 body-local PHIs generated - ✅ Main.main (demo harness): 23 body-local PHIs generated - ⚠️ Still some ValueId undefined errors remaining (exit PHI issue) **Files changed:** - src/mir/loop_builder.rs: body-local PHI generation logic - lang/src/compiler/entry/func_scanner.hako: debug logging - /tmp/stageb_funcscan_demo.hako: test harness 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@ -101,6 +101,20 @@
|
||||
- `CalleeResolverBox::classify_box_kind` に `BreakFinderBox` / `PhiInjectorBox` / `LoopSSA` を追加し、
|
||||
Stage‑1/Stage‑B 用 LoopSSA 箱を `CalleeBoxKind::StaticCompiler` として明示。
|
||||
|
||||
**IfForm / empty else-branch の SSA fix(Stage‑1 UsingResolverFull 対応)**
|
||||
- `src/mir/builder/if_form.rs`:
|
||||
- `if cond { then }`(else なし)のパターンで、
|
||||
- else-entry 用に pre_if の `variable_map` から PHI ノードを生成したあと、
|
||||
- その PHI 適用後の `variable_map` を `else_var_map_end_opt=Some(...)` として merge フェーズに渡すように修正。
|
||||
- 以前は empty else の場合に `else_var_map_end_opt` が `None` になっており、
|
||||
`merge_modified_vars` が pre_if の古い ValueId にフォールバックして、
|
||||
merge ブロックで未定義の `%0` などを参照するケースがあった(`Stage1UsingResolverFull.main/0` の UndefinedValue)。
|
||||
- 修正後は then/else 両ブランチで「PHI 適用後の variable_map」が merge に渡されるため、
|
||||
empty else でも header/merge の SSA が崩れない。
|
||||
- 検証:
|
||||
- `src/tests/mir_stage1_using_resolver_verify.rs::mir_stage1_using_resolver_full_collect_entries_verifies` が
|
||||
`MirVerifier` 緑になり、`Stage1UsingResolverFull.main/0()` の merge ブロックで PHI 後の値(例: `%24`)を正しく参照していることを MIR dump で確認済み。
|
||||
|
||||
**.hako 側の今後(25.1k 後半)**
|
||||
- `LoopSSA.stabilize_merges(json)` を Rust LoopForm v2 の Carrier/Pinned 規約に合わせて実装する(現在はほぼ stub)。
|
||||
- Stage‑B Test2(`tools/test_stageb_min.sh`)で得られる Program(JSON v0) に対し、
|
||||
@ -173,6 +187,33 @@
|
||||
- pinned 変数の扱い、
|
||||
- exit PHI の構築
|
||||
を Rust LoopForm v2 に合わせて実装。
|
||||
- 2025-11-19 追記:
|
||||
- `BreakFinderBox._find_loops/2` については、まず .hako 側を「region box」的に整理した。
|
||||
- `header_pos` / `header_id` / `exit_pos` / `exit_id` まわりの異常系を `continue` ではなく
|
||||
`next_i` ローカルへの代入で表現し、1 イテレーションの末尾で `i = next_i` に合流させる形に変更。
|
||||
- これにより、LoopForm v2 / LoopSSA 側から見ると「単一 region 内での分岐+最後に合流」という構造になり、
|
||||
carrier/pinned 検出や今後の SSA 解析が行いやすくなった(挙動は従来と同じ)。
|
||||
|
||||
### 2-5. static box / me セマンティクス(観測タスクへ移行)
|
||||
|
||||
- 現状:
|
||||
- `static box StringHelpers` のようなユーティリティ箱で、`me.starts_with(src, i, kw)` のように
|
||||
同一箱内のヘルパー(`starts_with`)を `me.` 経由で呼んでいたため、Stage‑3 降下時に引数ずれが発生していた。
|
||||
- 具体的には、`StringHelpers.starts_with_kw/3` → `StringHelpers.starts_with/3` の降下で
|
||||
実際の呼び出しが `starts_with("StringHelpers", src, i, kw)` のような 4 引数形になり、
|
||||
`starts_with(src, i, pat)` 側では `src="StringHelpers"` / `i=<ソース全文>` となって、
|
||||
`if i + m > n` が `String > Integer(13)` 比較に化けていた。
|
||||
- 対応(完了済み・局所修正):
|
||||
- `lang/src/shared/common/string_helpers.hako` の `starts_with_kw` を、
|
||||
`if me.starts_with(src, i, kw) == 0` から `if starts_with(src, i, kw) == 0` に書き換え、
|
||||
static box ユーティリティに対する `me` 依存を除去した。
|
||||
- これにより、`starts_with` 内でのガード比較 `i + m > n` はすべて整数同士となり、
|
||||
Stage‑B fib ケースで発生していた `String("...") > Integer(13)` の TypeError は解消済み。
|
||||
- 今後(Phase 25.1p 以降):
|
||||
- static box 全般における `me` セマンティクス(本当に「シングルトンインスタンス」として扱う箱と、
|
||||
純粋な名前空間箱をどう区別するか)は、25.1p の DebugLog フェーズで観測しながら設計を詰める。
|
||||
- 実際に Rust 層(`build_me_expression` / `lower_static_method_as_function` / `FunctionDefBuilder::is_instance_method`)を
|
||||
統一規約に寄せる作業は、25.1p 以降のサブタスクとして扱う(現時点では局所修正でバグのみ解消)。
|
||||
|
||||
---
|
||||
|
||||
@ -191,9 +232,13 @@
|
||||
|
||||
ここから先は「どのフェーズを進めるか」をそのときの優先度で選ぶ感じだよ。
|
||||
|
||||
1. **Stage‑B 型エラーの根治(Phase 25.1c か新フェーズ 25.1n)**
|
||||
- `Main.main` 内の `String(...) > Integer(13)` 比較を最小ケースで再現。
|
||||
- Stage‑B BodyExtractor / ParserBox / TestBox のどこで型が崩れているかを箱単位で切り分け。
|
||||
1. **Stage‑B / BreakFinder / FuncScanner ラインの SSA 根治(Phase 25.1m 続き)**
|
||||
- JSON v0 bridge の Loop lowering で、`backedge_to_cond` が `Jump` だけでなく `Branch(cond→header/exit)` も認識するように修正(`loop_.rs`)。
|
||||
→ BreakFinderBox._find_loops/2 のような break を含むループでも header PHI が正しく張られるようにした。
|
||||
- BreakFinderBox は解析用の static box として扱い、`me._find_loops` / `me._jumps_to` を `BreakFinderBox._find_loops` / `_jumps_to` に正規化。
|
||||
→ `me` 未定義による Undefined ValueId を避ける。
|
||||
- Stage‑B → Stage‑1 パーサ経路は、`ParserBox.parse_program2` のトップレベルループではなく、`parse_block2` で `Main.main` の本文をブロックとしてパースし、
|
||||
Program(JSON v0) を自前で組み立てる構造に変更(Stage‑B 固まり問題を回避)。
|
||||
|
||||
2. **Stage‑B 再入ガード `env.set/2` の整理**
|
||||
- 再入ガードを Box 内 state で持つ方向か、Rust 側に dev 専用 extern を追加するかを決める。
|
||||
@ -221,3 +266,34 @@
|
||||
|
||||
以上が 2025-11-18 時点の Phase 21.8 / 25 / 25.1 / 25.2 ラインの「いまどこ」「なに済み」「なに残り」だよ。
|
||||
次にどの箱から攻めるか決めたら、ここに箇条書きで足していこうね。
|
||||
|
||||
|
||||
## 2-? Stage‑B FuncScanner (in progress)
|
||||
|
||||
- StageBFuncScannerBox を compiler_stageb.hako 内に追加し、StageBDriverBox.main からは FuncScannerBox ではなくこちらを呼ぶように変更。
|
||||
- VM 側の Undefined value (BreakFinderBox._find_loops/2, FuncScannerBox.scan_all_boxes/1) は解消済みで、Stage‑B 自体は rc=0 まで到達する。
|
||||
- ただし現時点では StageBFuncScannerBox.scan_all_boxes(src) が fib サンプルに対しても defs=[] を返しており、Program(JSON) に TestBox.fib が載っていない状態。
|
||||
- 一度 StageBDriverBox から直接 FuncScannerBox.scan_all_boxes を呼ぶ構成も試したが、この経路では FuncScannerBox.scan_all_boxes/1 で再び Undefined value が発生したため、現状は StageBFuncScannerBox 経由に戻している(Stage‑B rc は 0、defs は空)。
|
||||
- StageBFuncScannerBox._find_matching_brace は FuncScannerBox._find_matching_brace と同じ balanced scan アルゴリズムに揃えつつ、`[funcscan/debug] _find_matching_brace enter open_idx=... n=...` ログで挙動を観測できるようにしてある(close_idx=-1 になる原因調査用)。
|
||||
- Rust Stage‑3 パーサ側の `using` パースを拡張し、`using "lang.compiler.parser.box" as ParserBox` 形式を受理するようにしたうえで、FuncScannerBox 自身もこの形に正規化した。
|
||||
- これにより `lang/src/compiler/entry/func_scanner.hako` 単体に対して `--dump-mir` が通るようになり、FuncScannerBox.scan_all_boxes/1 の MIR を直接観測できる。
|
||||
- 次の担当者は StageBFuncScannerBox.scan_all_boxes/1 と FuncScannerBox.scan_all_boxes/1 の MIR を比較しつつ、Stage‑B body 抽出→FuncScanner→defs 注入の導線と LoopBuilder 側の exit PHI を突き合わせてほしい。
|
||||
|
||||
|
||||
## 3. Static Box フィールド仕様ドキュメント(完了)
|
||||
|
||||
- docs/reference/language/LANGUAGE_REFERENCE_2025.md: 3.4 Static Boxパターンに「static box 内のフィールドはすべて static フィールドとして扱われる」旨を明記した。
|
||||
- 言語仕様としては、static box の中では `PI: FloatBox` のような宣言で十分であり、追加の `static` キーワードは不要であることをドキュメント上で固定。
|
||||
- 次タスク候補(Claude Code 向け): static box / box のフィールド挙動に関するサンプルとテスト(VM/JSON v0 両方)の整理、および static box 上の `me` セマンティクス(25.1p DebugLog フェーズと連動)の仕様化。
|
||||
|
||||
|
||||
## 3. Phase 25.1q — LoopForm Front Unification (planning)
|
||||
|
||||
- 目的: Rust AST ルート (LoopBuilder) と JSON v0 ルート (json_v0_bridge::lower_loop_stmt) のループ lowering フロントを整理し、PHI/LoopForm の SSOT を phi_core + LoopFormBuilder に明示的に寄せる。
|
||||
- 現状:
|
||||
- Rust AST → MIR は LoopBuilder + LoopFormBuilder で統一済み。
|
||||
- JSON v0 → MIR は loop_.rs から同じ phi_core を呼んでいるが、ファイルが分かれており、LLM/人間が誤って JSON 側だけを触るケースがあった。
|
||||
- 25.1q でやること(設計メモレベル):
|
||||
- docs に「LoopForm/PHI の意味論は phi_core が SSOT」と明記。
|
||||
- loop_.rs を「JSON から LoopForm に渡す薄いアダプタ」に限定し、余計なデバッグや独自分岐を増やさない方針を固定。
|
||||
- 将来的に JSON v0 → AST → MirBuilder に寄せる統合案を設計メモとして整理(実装は 25.2 以降)。
|
||||
|
||||
Reference in New Issue
Block a user