Files
hakorune/docs/development/current/main/design/control-tree.md
2025-12-18 04:27:30 +09:00

209 lines
8.9 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.

# ControlTree / StepTree構造SSOT
Status: **SSOTdesign / vocabulary**
Scope: **AST の“構造”だけ**を表現し、JoinIR/MIR の値・PHI・ブロックを一切持たない。
目的:
- ループ/if の **ネスト構造**を SSOT として持ち、pattern/policy の増殖先を「構造」側へ寄せる。
- LoopSkeletonloop_canonicalizer の骨格)を壊さずに、“次の一般化”の受け皿を追加する。
## 基本方針(禁止事項)
StepTree は「制御構造の形」だけを表す。
禁止(混ぜない):
- `ValueId` / `BlockId` / `PHI` / `JoinInst` / `MirInstruction` など、値やCFGの概念
- backend 依存の最適化・型情報
- “動くための lowering” ロジック(生成・変換)
許可(持ってよいもの):
- AST の最小要約(条件の形 / 文の種類 / span など)
- Capability / Feature例: has_loop, has_if, has_return などの分類)
## 用語
### ControlTree
StepTree後述を含む、構造SSOTの総称。将来 “BlockTree / BoundaryTree” 等を追加しても、値は持たない。
### StepTree
AST を「構造ード」に落とした木または木Block列で、次のード種別を持つ:
- `Block`: 文の列(順序を保持)
- `If`: `cond` と then/else の `Block`
- `Loop`: `cond` と body の `Block`
- `Stmt`: 構造化していない文Local/Assign/Return/Break/Continue/Expr などを tag 化)
## StepTreeContract構造契約SSOT
StepTreeContract は「この構造が何を含み、何を要求するか」を最小の契約として宣言する。
lowering/PHI/CFG の判断にはまだ使わないdev-onlyだが、再解析の増殖を防ぐための SSOT になる。
最小フィールド案P1:
- `exits`: `return` / `break` / `continue` の存在(構造だけ)
- `writes`: 変数への書き込み(最小は `Assignment target=Variable(name)``Local` 宣言の集合)
- `required_caps`: capability 宣言(例: `NestedLoop`, `TryCatch`, `Throw`, `Lambda` など)
- `cond_sig`: if/loop 条件式の要約(下記)
### Facts→Decision→Emit 設計Phase 120
**責務分離Box-First原則**:
1. **StepTreeBuilderBox**: 構造 + facts 抽出まで
- AST を StepNode 木に変換
- `StepTreeFacts` を収集exits/writes/required_caps/cond_sig の生データ)
- 意思決定・整形・署名生成はしない
2. **StepTreeContractBox**: facts → contract の整形のみ
- `StepTreeFacts` を受け取り `StepTreeContract` に整形
- BTreeSet で安定性保証(順序決定性)
- 意思決定はしないfacts をそのまま contract に移す)
3. **StepTree→Normalized**: contract を読むだけ
- AST 再解析禁止
- contract に含まれる情報のみで lowering 判断
- 将来実装時の契約
**不変条件**:
- `signature_basis_string()` の決定性維持(既定挙動不変)
- facts は順序に依存しないBTreeSet 使用)
- contract 生成は冪等(同じ facts から同じ contract
### cond の SSOTPhase 119
**SSOT**: `cond`**AST 参照ID/ハンドル)** を保持する。
- `StepNode::If` / `StepNode::Loop``cond_ast: Option<AstNodeHandle>` を追加。
- `AstNodeHandle` は AST 参照の軽量表現(将来的に `AstExprId` 等に移行可能)。
- **Phase 119**: `&ASTNode` 直接参照(ライフタイム制約あり)として実装。
- dev-only 用途なので、将来の ID 化は別 Phase で対応可能。
**派生**: `cond_sig` は署名/ログ/差分検知用の派生表現。
- `AstSummary` から計算される要約文字列(比較・統計・ログ用)。
- `StepTreeSignature``signature_basis_string()` に含まれる。
- **Span は含めない**(決定性保証)。
**不変条件**:
- `cond_ast``signature_basis_string()` に混ぜない(既存の署名安定性を維持)。
- `AstSummary``cond_ast` から計算され、構造分類/契約固定の責務を持つ。
**将来計画**Phase 119 以降):
- StepTree→Normalized 変換箱を実装する際は、`cond_ast` を lowering 入力として活用する。
- `cond_sig` は表示/署名用途として維持される。
## StepTreeSignature構造署名
StepTreeSignature は StepTreeContract + node kinds の “安定な基底文字列” を hash した識別子。
用途:
- dev-only ログの検索キー
- “同型ループ/同型if” の増殖検知(再解析の増殖防止)
注意:
- `Span` 等の位置情報は signature に含めない(入力差でブレるため)。
## Capability段階投入のSSOT
StepTree は capability を“宣言”し、未対応は **Fail-Fastdev-only / strict** で止める。
想定する段階(例):
1. **if-only**ネストifまで: Phase 110 P1
2. loop-in-if / if-in-loop: Phase 111+(予定)
3. nested loop: capability guard のまま別Phaseで解禁
## 位置づけLoopSkeleton との関係)
- **LoopSkeleton**`loop_canonicalizer`: loop 1個の骨格を正規化して、JoinIR に渡せる形へ整える。
- **StepTree**control_tree: 関数/ブロック全体の“構造”をSSOT化し、ネスト対応の入口を提供する。
両者は競合しない:
- LoopSkeleton は “loop単体の正規化” が責務
- StepTree は “構造の観測と分類” が責務値やCFGを持たない
## デバッグ出力dev-only
- 既定では出さない(既定挙動不変)。
- `NYASH_JOINIR_DEV=1` のときのみ StepTree をダンプするprefix は `[trace:dev] control_tree/step_tree`)。
- StepTree は routing の入力にしない(当面は parity/観測のみ。routing SSOT は feature extractor + analyzer
## Phase 121: StepTree→Normalized Shadow Lowering
**目的**: StepTree構造SSOTから Normalized 形式への最小ルートを確立し、if-only パターンで VM/LLVM との parity を検証する。
**スコープ**: if-onlyloop無しのみ。loop は capability guard で拒否。
### 設計ルール
**入力SSOT**:
- `StepTree` + `StepTreeContract`facts 再解析禁止)
- contract に含まれる情報のみで lowering 判断
**出力**:
- `JoinModule`Normalized 方言)
- または "Normalized 相当の中間" を JoinIR 既存型で表現
**実行条件**:
- `joinir_dev_enabled()` のときのみ shadow 変換を実行dev-only
- `joinir_strict_enabled()` のときのみ mismatch を Fail-Fast
**禁止事項**:
- fallback 禁止: shadow 変換失敗時は "disabled 扱い" ではなく dev-only で理由ログ、strict で Fail-Fast
- env 直読み禁止(`src/config/env/*` 経由必須)
- ハードコード禁止fixture 名や変数名で分岐しない)
### 責務分離Box化
**`StepTreeNormalizedShadowLowererBox`**:
- 責務: StepTree→JoinModule 変換if-only限定
- 入力: `&StepTree`
- 出力: `Result<Option<(JoinModule, JoinFragmentMeta)>, String>`
- `Ok(None)` = if-only 対象外(例: loop 含む)
- `Ok(Some(...))` = shadow 生成成功
- `Err(...)` = 生成できるはずなのに壊れている
**`normalized_shadow/contracts.rs`**:
- 責務: "if-only に限定" チェック、capability 拒否理由の SSOT
- Unsupported capability の明示的列挙Loop / Break / Continue 等)
**`normalized_shadow/parity.rs`**:
- 責務: router/既存経路との比較dev ログ / strict fail-fast
- 比較対象: 出口契約(`exits`)と writes の一致(最小で壊れにくい)
- strict mode では `freeze_with_hint` でエラーhint 必須)
### Parity検証最小セット
**比較対象**(値の一致まではやらない):
- `StepTreeContract.exits` / `writes`
- 既存ルータ・既存抽出から得られる "exit/writes" 相当
**不一致時の挙動**:
- dev mode: 1行ログ `[trace:dev] phase121/shadow/parity_mismatch: ...`
- strict mode: `error_tags::freeze_with_hint("phase121/shadow/parity_mismatch", msg, hint)`
### デバッグ出力dev-only
**1行ログ形式**:
```
[trace:dev] phase121/shadow: step_tree_sig=... shadow_lowered=true/false reason=... exits=... writes=...
```
**strict fail-fast**:
- "if-only なのに shadow が作れない" は即座に `freeze_with_hint`
- hint 空禁止必ず具体的な理由を1行で記述
### 配線場所
**`src/mir/builder/calls/lowering.rs`**:
- `lower_function_body` 内の StepTree capability guard 近辺
- `joinir_dev_enabled()` のときのみ shadow lowerer を呼ぶ
- 既存の本番経路はそのまま実行(結果は一切変えない)
### テスト戦略
**Smoke tests**:
- 既存 fixture を流用(`phase103_if_only_merge_min.hako` 等)
- VM ラインと LLVM ライン両方で実行
- `HAKO_JOINIR_STRICT=1` で strict mode 検証
- `NYASH_JOINIR_DEV=1` で dev mode ログ確認
**期待値**:
- 既存と同じ数値出力output_validator.sh で比較)
- strict mode で落ちない(= shadow parity mismatch が無い)