feat(llvm/phi): Phase 277 P1 - fail-fast validation for PHI strict mode
## Summary Implemented fail-fast validation for PHI ordering and value resolution in strict mode. ## Changes ### P1-1: Strict mode for "PHI after terminator" - File: `src/llvm_py/phi_wiring/wiring.py::ensure_phi` - Behavior: `NYASH_LLVM_PHI_STRICT=1` → RuntimeError if PHI created after terminator - Default: Warning only (no regression) ### P1-2: Strict mode for "fallback 0" - File: `src/llvm_py/phi_wiring/wiring.py::wire_incomings` - Behavior: Strict mode forbids silent fallback to 0 (2 locations) - Location 1: Unresolvable incoming value - Location 2: Type coercion failure - Error messages point to next debug file: `llvm_builder.py::_value_at_end_i64` ### P1-3: Connect verify_phi_ordering() to execution path - File: `src/llvm_py/builders/function_lower.py` - Behavior: Verify PHI ordering after all instructions emitted - Debug mode: Shows "✅ All N blocks have correct PHI ordering" - Strict mode: Raises RuntimeError with block list if violations found ## Testing ✅ Test 1: strict=OFF - passes without errors ✅ Test 2: strict=ON - passes without errors (no violations in test fixtures) ✅ Test 3: debug mode - verify_phi_ordering() connected and running ## Scope - LLVM harness (Python) changes only - No new environment variables (uses existing 3 from Phase 277 P2) - No JoinIR/Rust changes (root fix is Phase 279) - Default behavior unchanged (strict mode opt-in) ## Next Steps - Phase 278: Remove deprecated env var support - Phase 279: Root fix - unify "2本のコンパイラ" pipelines 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@ -1,14 +1,20 @@
|
||||
# Phase 269: Pattern8 への Frag 適用(P0=test-only → P1=実装)
|
||||
|
||||
Status: 🚧 進行中(P1)
|
||||
Date: 2025-12-21
|
||||
Status: ✅ 完了(P1)
|
||||
Date: 2025-12-22
|
||||
|
||||
## サブフェーズ状況
|
||||
|
||||
- **P1(Pattern8 EdgeCFG lowering)**: ✅(SSA の `i` PHI を含めて閉じた)
|
||||
- **P1.1(call_method return type)**: ✅(署名SSOTで型注釈)
|
||||
- **P1.2(static box の this/me → static call 正規化)**: ✅(runtime receiver を禁止して SSOT 化)
|
||||
|
||||
## 目的
|
||||
|
||||
**Pattern8(BoolPredicateScan)を EdgeCFG Fragment(Frag + emit_frag)で実装し、pattern番号の列挙を “exit配線” に収束させる。**
|
||||
|
||||
- **P0**: test-only stub + 最小 fixture/smoke で “入口” を固定(DONE)
|
||||
- **P1**: 実装(MIR CFG層で Frag を組み、emit_frag で terminator を SSOT 化)(IN PROGRESS)
|
||||
- **P1**: 実装(MIR CFG層で Frag を組み、emit_frag で terminator を SSOT 化)(DONE)
|
||||
|
||||
## 実装範囲(重要:スコープ境界)
|
||||
|
||||
@ -52,13 +58,57 @@ set_branch_with_edge_args() / set_jump_with_edge_args() (Phase 260 SSOT)
|
||||
- header/body/step は `i_current` を参照
|
||||
- step で `i_next = i_current + 1` を作り、backedge の入力にする
|
||||
|
||||
### 完了確認(P1)
|
||||
|
||||
- Pattern8 Frag lower が header に PHI を挿入し、`i_current` を `Compare/substring/step` の参照に使用する
|
||||
- integration smoke:
|
||||
- `tools/smokes/v2/profiles/integration/apps/phase269_p0_pattern8_frag_vm.sh` PASS
|
||||
- `tools/smokes/v2/profiles/integration/apps/phase259_p0_is_integer_vm.sh` PASS(回帰なし)
|
||||
|
||||
## P1.2(DONE): static box の `this/me` を static call に正規化(runtime receiver 禁止)
|
||||
|
||||
### 目的(SSOT)
|
||||
|
||||
static box 内の `this.method(...)` / `me.method(...)` を **runtime receiver(NewBox / 文字列 receiver)にしない**。
|
||||
compile-time に `current_static_box.method/arity` の canonical key を構築し、static call へ正規化する。
|
||||
|
||||
### SSOT / 禁止(再掲)
|
||||
|
||||
- SSOT:
|
||||
- `comp_ctx.current_static_box`(box 名の唯一の出どころ)
|
||||
- `BoxName.method/arity`(canonical key: call_method 署名注釈と共用)
|
||||
- 禁止:
|
||||
- `emit_string("StringUtils")` などの文字列レシーバによる by-name 的回避
|
||||
- static box の this/me を `NewBox` で runtime object 化(退行の原因)
|
||||
|
||||
### 実装(責務分離)
|
||||
|
||||
- `src/mir/builder/calls/build.rs`
|
||||
- MethodCall の共通入口で `This/Me` receiver を最優先で検出し、static call に正規化する
|
||||
- box 名は `comp_ctx.current_static_box` のみから取り出す(ハードコード禁止)
|
||||
- `src/mir/builder/stmts.rs`
|
||||
- static/instance の文脈エラーを Fail-Fast で明確化(誤誘導のメッセージ整理)
|
||||
- `src/mir/builder/control_flow/joinir/patterns/pattern8_scan_bool_predicate.rs`
|
||||
- **現状の安全策**: static box 文脈の loop は Pattern8 対象外にし、汎用 lowering(Pattern1 等)へ戻す
|
||||
- 目的: receiver 正規化を “1箇所” に収束させ、Pattern8 が runtime receiver を作る経路を封じる
|
||||
- 撤去条件: Pattern8 が「正規化後の MethodCall(static call key)」前提で安全に動くことを fixture/smoke で確認できたら、この除外を削除する
|
||||
|
||||
### 検証(fixture/smoke)
|
||||
|
||||
- `apps/tests/phase269_p1_2_this_method_in_loop_min.hako`
|
||||
- `tools/smokes/v2/profiles/integration/apps/phase269_p1_2_this_method_in_loop_vm.sh`
|
||||
- 受け入れ条件:
|
||||
- MIR dump に `const "StringUtils"` が receiver として出ない
|
||||
- `call_method StringUtils.is_digit/1`(または同等の static call)になる
|
||||
|
||||
## テスト手順(固定)
|
||||
|
||||
1. `cargo build --release`
|
||||
2. `cargo test -p nyash-rust --lib --release`
|
||||
3. `HAKORUNE_BIN=./target/release/hakorune bash tools/smokes/v2/profiles/integration/apps/phase259_p0_is_integer_vm.sh`
|
||||
4. `HAKORUNE_BIN=./target/release/hakorune bash tools/smokes/v2/profiles/integration/apps/phase269_p0_pattern8_frag_vm.sh`
|
||||
5. `./tools/smokes/v2/run.sh --profile quick`(45/46 を維持)
|
||||
5. `HAKORUNE_BIN=./target/release/hakorune bash tools/smokes/v2/profiles/integration/apps/phase269_p1_2_this_method_in_loop_vm.sh`
|
||||
6. `./tools/smokes/v2/run.sh --profile quick`(45/46 を維持)
|
||||
|
||||
## P0(historical)
|
||||
|
||||
|
||||
166
docs/development/current/main/phases/phase-272/README.md
Normal file
166
docs/development/current/main/phases/phase-272/README.md
Normal file
@ -0,0 +1,166 @@
|
||||
Status: Active
|
||||
Date: 2025-12-22
|
||||
Scope: Pattern6/7 を `Frag + emit_frag()` へ段階吸収(pattern列挙の増殖を止める)
|
||||
Related:
|
||||
- Design SSOT: `docs/development/current/main/design/edgecfg-fragments.md`
|
||||
- Phase 269(Pattern8 Frag): `docs/development/current/main/phases/phase-269/README.md`
|
||||
- Phase 270(Pattern9 bridge): `docs/development/current/main/phases/phase-270/README.md`
|
||||
|
||||
# Phase 272(P0): Pattern6/7 を Frag+emit_frag へ吸収(段階適用)
|
||||
|
||||
## ステータス
|
||||
|
||||
- **P0.1(Pattern6)**: ✅ 完了(Frag+emit_frag 経路へ移行)
|
||||
- **P0.2(Pattern7)**: ✅ 完了(Frag+emit_frag 経路へ移行)
|
||||
|
||||
## 目的
|
||||
|
||||
- Pattern6/7(scan系)の CFG 構築を “pattern番号ごとの推測分岐” から外し、**EdgeCFG Frag 合成(ExitKind/wires/branches)**に収束させる。
|
||||
- terminator emission を SSOT(`emit_frag()`)へ集約し、block の successors/preds 同期漏れを構造で防ぐ。
|
||||
|
||||
## スコープ境界
|
||||
|
||||
### ✅ 触る
|
||||
- `src/mir/builder/control_flow/joinir/patterns/pattern6_scan_with_init.rs`
|
||||
- `src/mir/builder/control_flow/joinir/patterns/pattern7_split_scan.rs`
|
||||
- `src/mir/builder/emission/`(Pattern8 と同じ “薄い入口” の追加)
|
||||
|
||||
### ❌ 触らない
|
||||
- merge/EdgeCFG plumbing(Phase 260-268 の SSOT は維持)
|
||||
- cf_loop の非JoinIR経路追加(JoinIR-only hard-freeze 維持)
|
||||
- by-name ハードコード(Box名/Pattern名文字列での分岐増殖など)
|
||||
|
||||
## 入口SSOT(fixture/smoke)
|
||||
|
||||
### Pattern6(index_of)
|
||||
- fixture: `apps/tests/phase254_p0_index_of_min.hako`(exit=1)
|
||||
- smoke (VM): `tools/smokes/v2/profiles/integration/apps/phase254_p0_index_of_vm.sh`
|
||||
|
||||
### Pattern7(split)
|
||||
- fixture: `apps/tests/phase256_p0_split_min.hako`(exit=3)
|
||||
- smoke (VM): `tools/smokes/v2/profiles/integration/apps/phase256_p0_split_vm.sh`
|
||||
|
||||
## 方針(P0)
|
||||
|
||||
P0 は “両方一気に” ではなく、以下の順で段階適用する。
|
||||
|
||||
1. Pattern6 を `Frag + emit_frag()` に切り替え(wiring を SSOT 化)
|
||||
2. Pattern7 を `Frag + emit_frag()` に切り替え(副作用 push を含む)
|
||||
3. 旧 JoinIR 経路の撤去条件が満たせた時点で削除(本READMEに明記)
|
||||
|
||||
## 実装ガイド(共通)
|
||||
|
||||
- PHI は block 先頭(after existing phis)へ挿入し、入力を `[(pred_bb, val)]` の形で固定する:
|
||||
- `crate::mir::ssot::cf_common::insert_phi_at_head_spanned`
|
||||
- terminator emission は `crate::mir::builder::control_flow::edgecfg::api::emit_frag` に集約する。
|
||||
- Pattern8 の構造(参考):
|
||||
- emission 入口: `src/mir/builder/emission/loop_predicate_scan.rs`
|
||||
|
||||
## P0.1: Pattern6(index_of)— Frag 化
|
||||
|
||||
### 狙い
|
||||
- loop 骨格(header/body/step/after + early return)を Frag に落とし、Jump/Branch/Return を `emit_frag()` に集約する。
|
||||
|
||||
### 実装結果(✅ 完了)
|
||||
|
||||
- emission 入口を新設し、Pattern6 の terminator emission を `emit_frag()`(SSOT)へ集約
|
||||
- 新規: `src/mir/builder/emission/loop_scan_with_init.rs`
|
||||
- 更新: `src/mir/builder/emission/mod.rs`
|
||||
- Pattern6 の JoinIRConversionPipeline 経路を撤去し、Frag 経路へ切り替え
|
||||
- 更新: `src/mir/builder/control_flow/joinir/patterns/pattern6_scan_with_init.rs`
|
||||
- P0 スコープ:
|
||||
- forward scan(`step=1`)のみ適用
|
||||
- reverse/dynamic needle 等は `Ok(None)` で不適用(既定挙動不変)
|
||||
- 旧 DCE 対策(exit PHI 用の post-loop guard)を撤去(Frag が Return を直接 emit するため)
|
||||
|
||||
### 最小 CFG 形(forward scan)
|
||||
- blocks: `header`, `body`, `step`, `after`, `ret_found`
|
||||
- header:
|
||||
- `i_current = phi [i_init, preheader], [i_next, step_bb]`
|
||||
- `cond_loop = (i_current < len)`
|
||||
- branch: true→body, false→after
|
||||
- body:
|
||||
- `ch = s.substring(i_current, i_current+1)`
|
||||
- `cond_match = (ch == needle)`
|
||||
- branch: true→ret_found, false→step
|
||||
- step:
|
||||
- `i_next = i_current + 1`
|
||||
- jump: →header
|
||||
- ret_found:
|
||||
- wire: `Return(i_current)`
|
||||
- after:
|
||||
- P0 では `return -1` を既存 AST lowering に任せてもよい(after を current_block にする)
|
||||
|
||||
### 受け入れ
|
||||
- `cargo test -p nyash-rust --lib --release`
|
||||
- `HAKORUNE_BIN=./target/release/hakorune bash tools/smokes/v2/profiles/integration/apps/phase254_p0_index_of_vm.sh`
|
||||
|
||||
### 追加の検証(推奨)
|
||||
|
||||
- `NYASH_VM_DUMP_MIR=1 ./target/release/hakorune --backend vm apps/tests/phase254_p0_index_of_min.hako` で PHI/terminator を確認(任意)
|
||||
|
||||
## P0.2: Pattern7(split)— Frag 化
|
||||
|
||||
### 狙い
|
||||
- Pattern7 の terminator 配線(if/loop の遷移)を Frag に集約し、副作用(`result.push`)を含む形でも CFG を壊さない。
|
||||
|
||||
### 注意点(Fail-Fast)
|
||||
- carriers が複数(`i`, `start`)なので、header の PHI を 2 本(以上)で SSA を閉じる必要がある。
|
||||
- `result.push` は副作用なので、block 配置(評価順)を壊さない(P0は固定形のみ受理)。
|
||||
|
||||
### 実装結果(✅ 完了)
|
||||
|
||||
- Pattern7 の JoinIRConversionPipeline 経路を撤去し、Frag+emit_frag 経路へ切り替え
|
||||
- 更新: `src/mir/builder/control_flow/joinir/patterns/pattern7_split_scan.rs`
|
||||
- emission 入口を新設し、terminator emission を `emit_frag()`(SSOT)へ集約
|
||||
- 新規: `src/mir/builder/emission/loop_split_scan.rs`
|
||||
- 更新: `src/mir/builder/emission/mod.rs`
|
||||
- CFG 形(P0):
|
||||
- blocks: `header`, `body`, `then`, `else`, `step`, `after`(+ 入口 preheader)
|
||||
- header: PHI(`i_current`, `start_current`) + loop condition
|
||||
- body: delimiter match check
|
||||
- then: `result.push(segment)` + `start_next_then` 計算(dominance 安全)
|
||||
- else: `i_next_else = i_current + 1`
|
||||
- step: PHI(`i_next`, `start_next`) + jump header
|
||||
- Compare は `CompareOp::Le`(`i <= limit`)を使用(固定形)
|
||||
|
||||
### リファクタ結果(共通SSOT)
|
||||
|
||||
Phase 272 P0.2 完了後、Pattern6/7/8 の重複を以下へ収束した(仕様不変):
|
||||
|
||||
- PHI 挿入の薄いラッパ: `src/mir/builder/emission/phi.rs`
|
||||
- variable_map の fail-fast 取得: `src/mir/builder/variable_context.rs`(`require(name, ctx)`)
|
||||
- can_lower の戦略メモ: `src/mir/builder/control_flow/joinir/patterns/router.rs`(`CanLowerStrategy`)
|
||||
|
||||
### 受け入れ
|
||||
- `HAKORUNE_BIN=./target/release/hakorune bash tools/smokes/v2/profiles/integration/apps/phase256_p0_split_vm.sh`
|
||||
- PASS(exit=3)
|
||||
- MIR 形(PHI/Branch/Jump/BoxCall(push))を目視確認できること(任意)
|
||||
|
||||
## Next(planned): Phase 273(design-first)— Pattern を Plan Extractor に降格して裾広がりを止める
|
||||
|
||||
Phase 272(P0)で “terminator SSOT(emit_frag)へ寄せる” を完了したら、次は上流の収束(compiler flow の一本化)を行う。
|
||||
|
||||
- 相談メモ(外部レビュー用): `docs/development/current/main/investigations/phase-272-frag-plan-architecture-consult.md`
|
||||
- ねらい:
|
||||
- Pattern = **Plan 抽出(pure)** に降格(builder を触らない)
|
||||
- Plan = `seq/if/loop/exit/effect/let` の固定語彙(増殖しない)
|
||||
- PlanLowerer が block/value/phi を作る唯一の箱(emit_frag は SSOT のまま)
|
||||
- 受け入れ(最小):
|
||||
- extractor が `next_block_id/next_value_id/insert_phi_*` を呼ばない(純関数)
|
||||
- Plan→Frag→emit_frag の本線が 1 本になる(pattern番号列挙を中心にしない)
|
||||
|
||||
## 旧 JoinIR 経路の撤去条件(SSOT)
|
||||
|
||||
旧 `JoinIRConversionPipeline` 系の経路を削るのは、以下を満たした後に行う。
|
||||
|
||||
1. Pattern6/7 の fixture/smoke が Frag 経路で PASS
|
||||
2. `tools/smokes/v2/run.sh --profile quick` が悪化しない
|
||||
3. router から該当 pattern の “旧経路” が消せる(最小差分で削除可能)
|
||||
|
||||
## テスト手順(固定)
|
||||
|
||||
1. `cargo build --release`
|
||||
2. `cargo test -p nyash-rust --lib --release`
|
||||
3. `HAKORUNE_BIN=./target/release/hakorune bash tools/smokes/v2/profiles/integration/apps/phase254_p0_index_of_vm.sh`
|
||||
4. `HAKORUNE_BIN=./target/release/hakorune bash tools/smokes/v2/profiles/integration/apps/phase256_p0_split_vm.sh`
|
||||
@ -0,0 +1,90 @@
|
||||
# Phase 274 P1 — Implement `TypeOp` on Rust VM (Instruction Guide)
|
||||
|
||||
Goal: make `x.is("T")` / `x.as("T")` runnable on the primary backend (Rust VM), aligning language docs and runtime behavior.
|
||||
|
||||
Scope: small, behavior-preserving where possible; fail-fast on unsupported type operations.
|
||||
|
||||
SSOT:
|
||||
- Language semantics: `docs/reference/language/types.md`
|
||||
- MIR instruction: `MirInstruction::TypeOp` (`src/mir/instruction.rs`)
|
||||
- Rust VM executor: `src/backend/mir_interpreter/*`
|
||||
|
||||
---
|
||||
|
||||
## 1) What already exists
|
||||
|
||||
- Frontend lowering emits `MirInstruction::TypeOp` for `.is("Type")` / `.as("Type")`:
|
||||
- `src/mir/builder/exprs.rs`
|
||||
- type-name mapping: `src/mir/builder/calls/special_handlers.rs` (`parse_type_name_to_mir`)
|
||||
- A fixture exists for P1 acceptance:
|
||||
- `apps/tests/phase274_p1_typeop_is_as_min.hako`
|
||||
- smoke: `tools/smokes/v2/profiles/integration/apps/phase274_p1_typeop_is_as_vm.sh`
|
||||
|
||||
---
|
||||
|
||||
## 2) Implementation plan (Rust VM)
|
||||
|
||||
### 2.1 Add instruction execution
|
||||
|
||||
Implement execution of:
|
||||
- `MirInstruction::TypeOp { dst, op: Check, value, ty }` → `dst = Bool(matches(value, ty))`
|
||||
- `MirInstruction::TypeOp { dst, op: Cast, value, ty }` → `dst = value` if matches, else `TypeError`
|
||||
|
||||
Files:
|
||||
- `src/backend/mir_interpreter/handlers/type_ops.rs` (new module is OK)
|
||||
- `src/backend/mir_interpreter/handlers/mod.rs` (dispatch arm)
|
||||
- `src/backend/mir_interpreter/mod.rs` (import `MirType`, `TypeOpKind` into interpreter module)
|
||||
|
||||
### 2.2 Matching rules (minimal, fail-fast)
|
||||
|
||||
Match by `MirType`:
|
||||
- `Integer/Float/Bool/String/Void`: accept both primitive VM values and their core Box equivalents when present.
|
||||
- `Box("Foo")`: accept:
|
||||
- user-defined `InstanceBox` where `class_name == "Foo"`
|
||||
- builtin/plugin boxes where `type_name() == "Foo"`
|
||||
- (best-effort) builtin `InstanceBox(from_any_box)` inner `type_name() == "Foo"`
|
||||
- Others:
|
||||
- `Unknown` matches anything (diagnostic-friendly).
|
||||
|
||||
Do not add new environment variables. Keep behavior deterministic and fail-fast.
|
||||
|
||||
---
|
||||
|
||||
## 3) Testing / Verification
|
||||
|
||||
### 3.1 Build
|
||||
|
||||
```bash
|
||||
cargo build --release
|
||||
```
|
||||
|
||||
### 3.2 Smoke (required)
|
||||
|
||||
```bash
|
||||
HAKORUNE_BIN=./target/release/hakorune bash \
|
||||
tools/smokes/v2/profiles/integration/apps/phase274_p1_typeop_is_as_vm.sh
|
||||
```
|
||||
|
||||
Expected:
|
||||
- PASS (exit=3)
|
||||
|
||||
### 3.3 Optional MIR inspection
|
||||
|
||||
```bash
|
||||
NYASH_VM_DUMP_MIR=1 ./target/release/hakorune --backend vm \
|
||||
apps/tests/phase274_p1_typeop_is_as_min.hako
|
||||
```
|
||||
|
||||
Confirm:
|
||||
- MIR contains `TypeOp(check, ...)` and `TypeOp(cast, ...)`.
|
||||
|
||||
---
|
||||
|
||||
## 4) Acceptance criteria (P1)
|
||||
|
||||
- Rust VM executes `TypeOp` (no “unimplemented instruction”).
|
||||
- `phase274_p1_typeop_is_as_vm.sh` passes.
|
||||
- No new env vars are introduced.
|
||||
- Docs remain consistent with runtime:
|
||||
- `docs/reference/language/types.md` describes runtime `TypeOp` behavior.
|
||||
|
||||
@ -0,0 +1,151 @@
|
||||
# Phase 274 P2 (impl): LLVM (llvmlite harness) TypeOp alignment
|
||||
|
||||
Status: planned / design-first
|
||||
|
||||
Goal: make LLVM harness execution match the SSOT semantics in `docs/reference/language/types.md` for:
|
||||
- `TypeOp(Check, value, ty)` → `Bool`
|
||||
- `TypeOp(Cast, value, ty)` → `value` or `TypeError`
|
||||
|
||||
Primary reference implementation (SSOT runtime): `src/backend/mir_interpreter/handlers/type_ops.rs`
|
||||
|
||||
---
|
||||
|
||||
## 0. What is currently wrong (must-fix)
|
||||
|
||||
LLVM harness TypeOp is stubbed in `src/llvm_py/instructions/typeop.py`:
|
||||
- `is`: returns 0 for most types (IntegerBox is “non-zero” heuristic)
|
||||
- `cast/as`: pass-through (never errors)
|
||||
|
||||
This conflicts with SSOT:
|
||||
- `is` must reflect actual runtime type match.
|
||||
- `as` must fail-fast (`TypeError`) on mismatch.
|
||||
|
||||
Note:
|
||||
- It is OK if the compiler constant-folds trivial cases (e.g. `1.is("Integer")`).
|
||||
- For P2 verification, you must use a fixture that keeps `TypeOp` in MIR (runtime-unknown / union value).
|
||||
|
||||
---
|
||||
|
||||
## 1. Acceptance criteria (minimum)
|
||||
|
||||
1) Behavior parity with Rust VM (SSOT)
|
||||
- With `NYASH_LLVM_USE_HARNESS=1` and `--backend llvm`, this fixture behaves the same as VM:
|
||||
- `apps/tests/phase274_p2_typeop_primitives_only.hako` (recommended: harness-safe baseline)
|
||||
|
||||
2) Fail-fast
|
||||
- `as` on mismatch must raise a TypeError (not return 0 / pass-through).
|
||||
- `is` must return `0/1` deterministically (no “unknown → 0” unless it is truly not a match).
|
||||
|
||||
3) No hardcode / no new env sprawl
|
||||
- No “BoxName string match special-cases” except small alias normalization shared with frontend (`IntegerBox`/`StringBox` etc.).
|
||||
- Do not add new environment variables for behavior.
|
||||
|
||||
---
|
||||
|
||||
## 2. Design constraint: LLVM harness value representation (key risk)
|
||||
|
||||
In llvmlite harness, a runtime “value” is currently represented as an `i64`, but it mixes:
|
||||
- raw integers (from `const i64`)
|
||||
- boxed handles (e.g. strings are boxed to handles via `nyash.box.from_i8_string`)
|
||||
- various call/bridge conventions
|
||||
|
||||
Because a handle is also an `i64`, **the harness cannot reliably decide at runtime** whether an `i64` is “raw int” or “handle”, unless the value representation is made uniform.
|
||||
|
||||
This means TypeOp parity cannot be achieved reliably without addressing representation.
|
||||
|
||||
---
|
||||
|
||||
## 3. Recommended implementation strategy (P2): make representation uniform for TypeOp
|
||||
|
||||
### Strategy A (recommended): “all values are handles” in LLVM harness
|
||||
|
||||
Make every runtime value in llvmlite harness be a handle (i64) to a boxed value:
|
||||
- integers: `nyash.box.from_i64(i64) -> handle`
|
||||
- floats: `nyash.box.from_f64(f64) -> handle`
|
||||
- strings: already boxed (`nyash.box.from_i8_string`)
|
||||
- bool/void: use existing conventions (or add kernel shims if needed)
|
||||
|
||||
Then TypeOp becomes implementable via runtime introspection on handles.
|
||||
|
||||
#### A.1 Kernel helper needed (small, SSOT-friendly)
|
||||
|
||||
Add a kernel export (in `crates/nyash_kernel/src/lib.rs`) that checks a handle’s runtime type:
|
||||
- `nyash.any.is_type_h(handle: i64, type_name: *const i8) -> i64` (0/1)
|
||||
- optionally `nyash.any.cast_h(handle: i64, type_name: *const i8) -> i64` (handle or 0; but prefer fail-fast at caller)
|
||||
|
||||
Implementation rule:
|
||||
- Must use actual runtime object type (builtins + plugin boxes + InstanceBox class name).
|
||||
- Must not guess via resolver/type facts.
|
||||
|
||||
#### A.2 LLVM harness lowering
|
||||
|
||||
Update `src/llvm_py/instructions/typeop.py`:
|
||||
- Always resolve `src_val` as handle (`i64`).
|
||||
- `check/is`: call `nyash.any.is_type_h(src_val, type_name_ptr)` → i64 0/1
|
||||
- `cast/as`: call `is_type_h`; if false, emit a runtime error (use existing “panic”/error path if available) or call a kernel `nyash.panic.type_error` style function (add if missing).
|
||||
|
||||
Also update other lowerers incrementally so that values feeding TypeOp are handles (start with the fixture path).
|
||||
|
||||
### Strategy B (fallback): keep mixed representation, but document divergence (not parity)
|
||||
|
||||
If Strategy A is too large for P2, constrain scope:
|
||||
- Implement `TypeOp` using compile-time `resolver.value_types` hints.
|
||||
- Document clearly in Phase 274 README: LLVM harness TypeOp is “best-effort using type facts” and is not SSOT-correct under re-assignment.
|
||||
|
||||
This keeps the harness useful for SSA/CFG validation, but is not runtime-parity.
|
||||
|
||||
Note: Strategy B should be treated as temporary and must be called out as backend divergence in docs.
|
||||
|
||||
---
|
||||
|
||||
## 4. Concrete work items (P2)
|
||||
|
||||
1) Audit current failure path
|
||||
- Identify how LLVM harness reports runtime errors today (type errors, asserts).
|
||||
- Prefer a single runtime helper rather than sprinkling Python exceptions.
|
||||
|
||||
1.5) Fix MIR JSON emission for TypeOp (required)
|
||||
|
||||
The LLVM harness consumes MIR JSON emitted by the Rust runner.
|
||||
If `TypeOp` is missing in that JSON, the harness will never see it (and the JSON can become invalid due to missing defs).
|
||||
|
||||
Checklist:
|
||||
- `src/runner/mir_json_emit.rs` must emit `{"op":"typeop", ...}` in **both** emitters:
|
||||
- `emit_mir_json_for_harness` (nyash_rust::mir) ✅ already supports TypeOp
|
||||
- `emit_mir_json_for_harness_bin` (crate::mir) ⚠️ ensure TypeOp is included
|
||||
|
||||
2) Add kernel introspection helper(s)
|
||||
- `crates/nyash_kernel/src/lib.rs`: add `nyash.any.is_type_h`.
|
||||
- It must handle:
|
||||
- primitives boxed (`IntegerBox`, `FloatBox`, `BoolBox`, `StringBox`, `VoidBox`)
|
||||
- `InstanceBox` user classes (by `class_name`)
|
||||
- plugin boxes (by metadata / resolved type name)
|
||||
|
||||
3) Implement real `TypeOp` lowering
|
||||
- `src/llvm_py/instructions/typeop.py`:
|
||||
- normalize `target_type` aliases (same mapping as frontend docs: `Int` → `IntegerBox`, etc.)
|
||||
- `is` → call kernel check
|
||||
- `as`/`cast` → check then return src or TypeError
|
||||
|
||||
4) Add LLVM smoke (integration)
|
||||
- New script (name suggestion):
|
||||
- `tools/smokes/v2/profiles/integration/apps/phase274_p2_typeop_is_as_llvm.sh`
|
||||
- Run:
|
||||
- `NYASH_LLVM_USE_HARNESS=1 ./target/release/hakorune --backend llvm apps/tests/phase274_p2_typeop_primitives_only.hako`
|
||||
- Expect: exit code `3` (same as VM).
|
||||
|
||||
---
|
||||
|
||||
## 5. Notes / non-goals (P2)
|
||||
|
||||
- Do not implement a full static type system here.
|
||||
- Do not add rule logic to the resolver (no “guessing chains”).
|
||||
- Do not add new environment variables for behavior selection.
|
||||
- If you must limit scope, limit it by fixtures and document it in Phase 274 README as explicit divergence.
|
||||
|
||||
### Fixture rule (important)
|
||||
|
||||
To avoid “TypeOp disappeared” false negatives:
|
||||
- Do not use pure compile-time constants for `is/as` checks.
|
||||
- Prefer a union value formed by a runtime-unknown branch (e.g. `process.argv().size() > 0`).
|
||||
- Note: `env.process.argv` is currently not supported on Rust VM, and `env.get` is not linked for LLVM AOT yet; keep harness fixtures minimal unless the required externs are implemented in NyRT.
|
||||
162
docs/development/current/main/phases/phase-274/P3-DECISIONS.md
Normal file
162
docs/development/current/main/phases/phase-274/P3-DECISIONS.md
Normal file
@ -0,0 +1,162 @@
|
||||
# Phase 274 P3 (decision): Coercion SSOT (truthiness / `==` / `+`)
|
||||
|
||||
Status: accepted (2025-12-22) / pending implementation
|
||||
|
||||
This document freezes **what coercions mean** at the language level, so runtime behavior cannot “emerge” from resolver/type-facts.
|
||||
|
||||
SSOT anchor (current executable behavior): `docs/reference/language/types.md`
|
||||
Phase overview: `docs/development/current/main/phases/phase-274/README.md`
|
||||
Implementation phase: `docs/development/current/main/phases/phase-275/README.md`
|
||||
|
||||
---
|
||||
|
||||
## 1) Terms
|
||||
|
||||
In this doc, “coercion” means: when operands/types differ, do we:
|
||||
- convert implicitly,
|
||||
- return a deterministic result (e.g. `false`),
|
||||
- or fail-fast (`TypeError`)?
|
||||
|
||||
Target constraints:
|
||||
- Fail-Fast where it prevents silent bugs
|
||||
- No “JS-style surprise coercion”
|
||||
- Dynamic runtime remains (no static type system required)
|
||||
- Backend parity (VM/LLVM) or explicit divergence, never accidental drift
|
||||
|
||||
---
|
||||
|
||||
## 2) Proposed SSOT (recommended)
|
||||
|
||||
Based on the project philosophy, the recommended SSOT choice is:
|
||||
- **truthiness: A1**
|
||||
- **`==`: B2 (Number-only)**
|
||||
- **`+`: C2 (Number-only promotion)**
|
||||
|
||||
The sections below define each choice precisely.
|
||||
|
||||
---
|
||||
|
||||
## 3) truthiness (boolean context)
|
||||
|
||||
### Decision: A1 (Fail-Fast)
|
||||
|
||||
`Void` in condition is **TypeError**.
|
||||
|
||||
Allowed in boolean context:
|
||||
- `Bool` → itself
|
||||
- `Integer` → `0` false, non-zero true
|
||||
- `Float` → `0.0` false, non-zero true
|
||||
- `String` → empty false, otherwise true
|
||||
|
||||
Disallowed (TypeError):
|
||||
- `Void` (always error)
|
||||
- `BoxRef` (by default)
|
||||
- Exception: only **explicit bridge boxes** may be unboxed to the corresponding primitive for truthiness:
|
||||
- `BoolBox` / `IntegerBox` / `StringBox`
|
||||
- `VoidBox` is treated as `Void` → TypeError
|
||||
|
||||
Recommended explicit patterns:
|
||||
- existence check: `x != Void`
|
||||
- type check: `x.is("T")` / `x.as("T")`
|
||||
- explicit conversion (if we add it): `bool(x)` (but `bool(Void)` remains TypeError)
|
||||
|
||||
Implementation impact (where to change):
|
||||
- Rust VM: `src/backend/abi_util.rs::to_bool_vm`
|
||||
- LLVM harness: must match the VM semantics used for branch conditions
|
||||
|
||||
---
|
||||
|
||||
## 4) `==` (equality)
|
||||
|
||||
### Decision: B2 (Number-only)
|
||||
|
||||
Rules:
|
||||
- Same-kind primitives compare normally.
|
||||
- `Int` ↔ `Float` comparisons are allowed (Number-only).
|
||||
- `Bool` is **not** a number: `Bool` ↔ `Int/Float` has no coercion.
|
||||
- Other mixed kinds: deterministic **`false`** (not an error).
|
||||
- `BoxRef == BoxRef`: identity only.
|
||||
|
||||
#### Precise rule for `Int == Float` (avoid “accidental true”)
|
||||
|
||||
To avoid float rounding making `true` incorrectly:
|
||||
|
||||
For `Int == Float` (or `Float == Int`):
|
||||
1) If Float is NaN → `false`
|
||||
2) If Float is finite, integral (fractional part is 0), and within `i64` exact range:
|
||||
- convert Float → Int exactly, then compare Ints
|
||||
3) Otherwise → `false`
|
||||
|
||||
Migration note:
|
||||
- If legacy behavior existed for `1 == true`, prefer a transition phase where it becomes **TypeError first** (to surface bugs), then settle to `false` if desired.
|
||||
|
||||
Implementation impact:
|
||||
- Rust VM: `src/backend/abi_util.rs::eq_vm` (and any helpers)
|
||||
- LLVM harness: must mirror the same decision for `compare ==` lowering
|
||||
|
||||
---
|
||||
|
||||
## 5) `+` (add / concat)
|
||||
|
||||
### Decision: C2 (Number-only promotion)
|
||||
|
||||
Rules:
|
||||
- `Int + Int` → `Int`
|
||||
- `Float + Float` → `Float`
|
||||
- `Int + Float` / `Float + Int` → `Float` (promote Int→Float)
|
||||
- `String + String` → concat
|
||||
- `String + non-string` / `non-string + String` → **TypeError** (no implicit stringify)
|
||||
- Other combos → TypeError
|
||||
|
||||
Implementation impact:
|
||||
- Rust VM: `src/backend/mir_interpreter/helpers.rs::eval_binop` (BinaryOp::Add)
|
||||
- LLVM harness: binop `+` lowering must follow the same coercion rules
|
||||
|
||||
---
|
||||
|
||||
## 6) Minimum test matrix (SSOT lock)
|
||||
|
||||
### 6.1 truthiness
|
||||
|
||||
- Bool: `if true`, `if false`
|
||||
- Int: `if 0`, `if 1`, `if -1`
|
||||
- Float: `if 0.0`, `if 0.5`, `if NaN` (define if NaN counts as truthy)
|
||||
- String: `if ""`, `if "a"`
|
||||
- Void: `if Void` → TypeError (A1)
|
||||
- BoxRef:
|
||||
- bridge: `BoolBox(true)`, `IntegerBox(0)`, `StringBox("")`
|
||||
- non-bridge: `Foo()` → TypeError
|
||||
|
||||
### 6.2 equality
|
||||
|
||||
- same-kind primitives
|
||||
- Int↔Float:
|
||||
- `1 == 1.0` true
|
||||
- `1 == 1.1` false
|
||||
- `NaN == NaN` false
|
||||
- Bool↔Int:
|
||||
- `true == 1` (explicitly decide: TypeError during migration vs final false)
|
||||
- BoxRef identity:
|
||||
- same handle true, different handles false
|
||||
|
||||
### 6.3 plus
|
||||
|
||||
- Int/Float add
|
||||
- Int+Float promotion
|
||||
- String+String concat
|
||||
- String mixed TypeError
|
||||
|
||||
---
|
||||
|
||||
## 7) Migration plan (if changing behavior)
|
||||
|
||||
Recommended two-step approach:
|
||||
|
||||
1) Compatibility freeze (Phase 274)
|
||||
- Document current behavior (already in `types.md`)
|
||||
- Add warnings / diagnostics where possible (no new env sprawl)
|
||||
|
||||
2) Switch semantics (Phase 275 or later)
|
||||
- Implement A1/B2/C2 in VM and LLVM
|
||||
- Add fixtures to lock the SSOT
|
||||
- Ensure error messages provide “fix-it” guidance (`str(x)`, `x != Void`, etc.)
|
||||
123
docs/development/current/main/phases/phase-274/README.md
Normal file
123
docs/development/current/main/phases/phase-274/README.md
Normal file
@ -0,0 +1,123 @@
|
||||
# Phase 274 (active): Type SSOT Alignment (local + dynamic runtime)
|
||||
|
||||
Status: active / design-first
|
||||
|
||||
Goal: make the **language-level type semantics** and the **runtime behavior** consistent and easy to reason about, without turning Nyash into a statically typed language.
|
||||
|
||||
This phase is about:
|
||||
- clarifying SSOT docs,
|
||||
- closing “frontend emits it but VM can’t run it” gaps,
|
||||
- and preventing “type facts / resolver guessing” from silently becoming language semantics.
|
||||
|
||||
---
|
||||
|
||||
## Background (why this exists)
|
||||
|
||||
Current state:
|
||||
- Nyash is dynamic at runtime (VM executes tagged values).
|
||||
- MIR builder attaches type metadata (`value_types`, `value_origin_newbox`) for routing/optimization.
|
||||
- Some docs describe stricter semantics than the VM actually implements.
|
||||
- `TypeOp` is emitted by the frontend for `is/as`.
|
||||
|
||||
Problems:
|
||||
- Spec drift: quick docs vs runtime behavior differ (truthiness / equality / compare / `+`).
|
||||
- Capability drift across backends: “VM is correct” but “LLVM harness differs” (TypeOp).
|
||||
- Type metadata risks becoming implicit semantics via resolver fallback chains.
|
||||
|
||||
SSOT decisions should be expressed in:
|
||||
- language docs (meaning),
|
||||
- runtime (execution),
|
||||
- and only then optimization facts (rewrite / routing).
|
||||
|
||||
---
|
||||
|
||||
## SSOT references
|
||||
|
||||
- Language type semantics (SSOT): `docs/reference/language/types.md`
|
||||
- VM semantics source: `src/backend/abi_util.rs`, `src/backend/mir_interpreter/helpers.rs`
|
||||
- MIR type vocabulary: `src/mir/types.rs`
|
||||
- Call certainty vocabulary: `src/mir/definitions/call_unified.rs`
|
||||
|
||||
---
|
||||
|
||||
## Scope (P0/P1/P2/P3)
|
||||
|
||||
### P0 (docs-only): establish SSOT and remove contradictions
|
||||
|
||||
Deliverables:
|
||||
- `docs/reference/language/types.md` (SSOT)
|
||||
- Quick-reference points to SSOT and stops contradicting runtime
|
||||
|
||||
Acceptance:
|
||||
- docs no longer claim semantics that the Rust VM clearly violates.
|
||||
|
||||
### P1 (impl): make `TypeOp` runnable on Rust VM
|
||||
|
||||
Goal:
|
||||
- `x.is("T")` / `x.as("T")` lowering exists already; make it executable on the primary backend (Rust VM).
|
||||
|
||||
Acceptance (minimum):
|
||||
- Rust VM implements `MirInstruction::TypeOp { op: Check|Cast, value, ty }`
|
||||
- Add a small executable fixture/smoke that exercises `is/as`
|
||||
- No new env vars; fail-fast errors on unsupported casts/checks
|
||||
|
||||
Implementation guide:
|
||||
- `docs/development/current/main/phases/phase-274/P1-INSTRUCTIONS.md`
|
||||
|
||||
Status: ✅ done (2025-12-22)
|
||||
|
||||
Artifacts:
|
||||
- Fixture: `apps/tests/phase274_p1_typeop_is_as_min.hako`
|
||||
- Smoke: `tools/smokes/v2/profiles/integration/apps/phase274_p1_typeop_is_as_vm.sh`
|
||||
- VM handler: `src/backend/mir_interpreter/handlers/type_ops.rs`
|
||||
|
||||
### P2 (impl): align LLVM (llvmlite harness) `TypeOp` to SSOT
|
||||
|
||||
Goal:
|
||||
- Make LLVM harness behavior match Rust VM (SSOT) for `TypeOp(Check/Cast)`.
|
||||
|
||||
Current mismatch:
|
||||
- `src/llvm_py/instructions/typeop.py` is stubbed:
|
||||
- `is` returns 0 for most types (special-cases `IntegerBox` as “non-zero”)
|
||||
- `cast/as` are pass-through
|
||||
|
||||
Acceptance (minimum):
|
||||
- With `NYASH_LLVM_USE_HARNESS=1` + `--backend llvm`, the P1 fixture has the same observable result as Rust VM.
|
||||
- Unsupported cases fail-fast (TypeError), not silent 0/“passthrough”.
|
||||
- No new environment-variable toggles; differences must be fixed or explicitly documented.
|
||||
|
||||
Implementation guide:
|
||||
- `docs/development/current/main/phases/phase-274/P2-INSTRUCTIONS.md`
|
||||
|
||||
Status: ✅ done (2025-12-22)
|
||||
|
||||
Artifacts:
|
||||
- Kernel type check helper: `crates/nyash_kernel/src/lib.rs` (`nyash.any.is_type_h`)
|
||||
- LLVM TypeOp lowering: `src/llvm_py/instructions/typeop.py`
|
||||
- MIR JSON emission fix (bin): `src/runner/mir_json_emit.rs` (emit `op:"typeop"`)
|
||||
- Fixture (LLVM-safe): `apps/tests/phase274_p2_typeop_primitives_only.hako`
|
||||
- Smoke (LLVM): `tools/smokes/v2/profiles/integration/apps/phase274_p2_typeop_is_as_llvm.sh`
|
||||
|
||||
### P3 (decision + optional impl): tighten or document coercions
|
||||
|
||||
Decision points to settle (SSOT):
|
||||
- Truthiness for arbitrary BoxRef (allow “any object is truthy” vs fail-fast)
|
||||
- Equality cross-coercions (`int↔bool`, `int↔float`) — keep, restrict, or gate behind a profile
|
||||
- `+` mixed numeric types (`int+float`) — keep TypeError or add explicit conversions
|
||||
|
||||
Acceptance:
|
||||
- whichever behavior is chosen becomes consistent across backends and docs.
|
||||
|
||||
Decision memo (P3):
|
||||
- `docs/development/current/main/phases/phase-274/P3-DECISIONS.md`
|
||||
|
||||
Status:
|
||||
- P3 decisions are ✅ accepted; implementation is tracked in Phase 275.
|
||||
|
||||
---
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Full static typing / inference engine
|
||||
- Widening the language surface area (new keywords) as the first move
|
||||
- Adding more environment-variable toggles as a long-term solution
|
||||
@ -0,0 +1,147 @@
|
||||
# Phase 275 P0 (impl): Coercion SSOT rollout
|
||||
|
||||
Status: planned / implementation guide
|
||||
|
||||
This is the “next instruction sheet” for implementing the accepted coercion SSOT (A1/B2/C2) across backends.
|
||||
|
||||
SSOT decisions:
|
||||
- `docs/development/current/main/phases/phase-274/P3-DECISIONS.md`
|
||||
|
||||
---
|
||||
|
||||
## Scope (P0)
|
||||
|
||||
Implement and lock these three rule sets:
|
||||
- truthiness: `Void`/`BoxRef` fail-fast (with bridge-box exceptions)
|
||||
- equality: B2 (Number-only, precise Int↔Float)
|
||||
- `+`: C2 (Number-only promotion; String+String only; String mixed → TypeError)
|
||||
|
||||
Backends in scope:
|
||||
- Rust VM (primary SSOT)
|
||||
- LLVM harness (llvmlite path) parity with Rust VM
|
||||
|
||||
Out of scope:
|
||||
- adding new language features (keywords)
|
||||
- expanding env var toggles
|
||||
- rewriting the optimizer broadly (only touch what is required to enforce semantics)
|
||||
|
||||
---
|
||||
|
||||
## Step 0: Lock reference + plan
|
||||
|
||||
- Keep `docs/reference/language/types.md` as “current executable SSOT” until the implementation is complete.
|
||||
- After implementation + tests land, update `types.md` to the new semantics (it becomes SSOT again).
|
||||
|
||||
---
|
||||
|
||||
## Step 1: Rust VM — truthiness (A1)
|
||||
|
||||
Target:
|
||||
- `src/backend/abi_util.rs::to_bool_vm` (or equivalent truthiness entry)
|
||||
|
||||
Changes:
|
||||
- `Void` in boolean context → return `TypeError`
|
||||
- `BoxRef`:
|
||||
- allow only explicit bridge boxes: BoolBox/IntegerBox/StringBox
|
||||
- treat VoidBox as Void (→ TypeError)
|
||||
- other BoxRef types → TypeError
|
||||
|
||||
Acceptance:
|
||||
- A dedicated fixture demonstrates `if Void { ... }` is a runtime error (fail-fast).
|
||||
|
||||
---
|
||||
|
||||
## Step 2: Rust VM — equality (B2)
|
||||
|
||||
Target:
|
||||
- `src/backend/abi_util.rs::eq_vm` (and any helpers it relies on)
|
||||
|
||||
Changes:
|
||||
- Remove Bool↔Int coercion.
|
||||
- Keep Int↔Float comparison, but make it precise:
|
||||
- if Float is NaN → false
|
||||
- if Float is integral and within i64 exact range → compare as Int exactly
|
||||
- otherwise → false
|
||||
- Mixed kinds (except Int↔Float) → false (not error).
|
||||
- BoxRef equality stays identity.
|
||||
|
||||
Acceptance:
|
||||
- Tests cover:
|
||||
- `1 == 1.0` true
|
||||
- `1 == 1.1` false
|
||||
- `true == 1` false (or TypeError if you choose a transition rule; document explicitly)
|
||||
|
||||
---
|
||||
|
||||
## Step 3: Rust VM — `+` (C2)
|
||||
|
||||
Target:
|
||||
- `src/backend/mir_interpreter/helpers.rs::eval_binop` for `BinaryOp::Add`
|
||||
|
||||
Changes:
|
||||
- Numeric:
|
||||
- Int+Int → Int
|
||||
- Float+Float → Float
|
||||
- Int+Float / Float+Int → Float (Int promoted to Float)
|
||||
- String:
|
||||
- String+String → concat
|
||||
- String mixed → TypeError (no implicit stringify)
|
||||
- Everything else → TypeError
|
||||
|
||||
Acceptance:
|
||||
- Tests cover:
|
||||
- `1 + 2.0` → `3.0`
|
||||
- `"a" + "b"` → `"ab"`
|
||||
- `"a" + 1` → TypeError
|
||||
|
||||
---
|
||||
|
||||
## Step 4: LLVM harness parity
|
||||
|
||||
Targets (likely):
|
||||
- `src/llvm_py/instructions/binop.py` for `+`
|
||||
- `src/llvm_py/instructions/compare.py` for `==`
|
||||
- truthiness for branch conditions (inspect branch lowering):
|
||||
- `src/llvm_py/instructions/controlflow/branch.py`
|
||||
- and any “truthy” conversions used in control-flow lowering
|
||||
|
||||
Notes:
|
||||
- LLVM harness uses MIR JSON metadata (`value_types`) for discriminating raw vs handle.
|
||||
- Keep behavior identical to Rust VM; do not re-introduce “string mixed concat”.
|
||||
|
||||
Acceptance:
|
||||
- VM and LLVM smokes use the same fixtures and produce identical exit codes.
|
||||
|
||||
---
|
||||
|
||||
## Step 5: Fixtures + smoke tests (SSOT lock)
|
||||
|
||||
Create minimal, self-contained fixtures under `apps/tests/` and add smokes under `tools/smokes/v2/profiles/integration/apps/`.
|
||||
|
||||
Suggested fixtures (names; adjust as needed):
|
||||
- `apps/tests/phase275_p0_truthiness_void_error_min.hako`
|
||||
- `apps/tests/phase275_p0_eq_number_only_min.hako`
|
||||
- `apps/tests/phase275_p0_plus_number_only_min.hako`
|
||||
|
||||
Smoke targets:
|
||||
- VM: `..._vm.sh`
|
||||
- LLVM: `..._llvm.sh` (harness/EXE path consistent with current infra)
|
||||
|
||||
Rules:
|
||||
- No reliance on hidden env toggles.
|
||||
- If a test needs runtime-unknown values, avoid externs that aren’t supported on VM/LLVM.
|
||||
|
||||
---
|
||||
|
||||
## Step 6: Update SSOT docs
|
||||
|
||||
After the implementation is complete and tests pass:
|
||||
- Update `docs/reference/language/types.md` to the new semantics:
|
||||
- truthiness: Void/BoxRef fail-fast
|
||||
- equality: B2
|
||||
- `+`: C2
|
||||
- Update Phase 274/275 status:
|
||||
- `docs/development/current/main/phases/phase-274/README.md`
|
||||
- `docs/development/current/main/10-Now.md`
|
||||
- `docs/development/current/main/30-Backlog.md`
|
||||
|
||||
@ -0,0 +1,74 @@
|
||||
# Phase 275 P1 (docs/lock): Update types.md + lock coercion matrix
|
||||
|
||||
Status: planned / instruction sheet
|
||||
|
||||
This is the follow-up after Phase 275 P0 (implementation) lands.
|
||||
|
||||
Prereq:
|
||||
- Phase 275 P0 is complete and VM/LLVM smokes are green.
|
||||
- Decisions are frozen in `docs/development/current/main/phases/phase-274/P3-DECISIONS.md`.
|
||||
|
||||
---
|
||||
|
||||
## 1) Update the language SSOT doc
|
||||
|
||||
File:
|
||||
- `docs/reference/language/types.md`
|
||||
|
||||
Edits:
|
||||
- Move “Accepted (Phase 275)” rules from “pending” into the **current executable SSOT** sections:
|
||||
- truthiness: `Void` and non-bridge `BoxRef` become `TypeError`
|
||||
- `==`: B2 (Number-only) with precise Int↔Float; Bool↔Number has no coercion
|
||||
- `+`: C2 (Number-only promotion) + String+String only; String mixed `TypeError`
|
||||
- Add a short “Migration notes” subsection:
|
||||
- how to rewrite old code (`x != Void`, `str(x)`, interpolation)
|
||||
- explicitly call out any intentionally-breaking changes
|
||||
|
||||
Acceptance:
|
||||
- `types.md` no longer describes the legacy behavior (e.g. `Void -> false` in conditions, `"a"+1` concat).
|
||||
|
||||
---
|
||||
|
||||
## 2) Add the minimum test matrix fixtures (SSOT lock)
|
||||
|
||||
Goal:
|
||||
- prevent semantic drift by locking the truthiness / `==` / `+` matrix in fixtures + smokes.
|
||||
|
||||
Add fixtures under `apps/tests/` (minimal, self-contained):
|
||||
- `apps/tests/phase275_p0_truthiness_min.hako`
|
||||
- `apps/tests/phase275_p0_eq_min.hako`
|
||||
- `apps/tests/phase275_p0_plus_min.hako`
|
||||
|
||||
Rules:
|
||||
- no env toggles required
|
||||
- no dependency on unsupported externs/symbols on LLVM line
|
||||
|
||||
Add smokes under `tools/smokes/v2/profiles/integration/apps/`:
|
||||
- `phase275_p0_truthiness_vm.sh` / `phase275_p0_truthiness_llvm.sh`
|
||||
- `phase275_p0_eq_vm.sh` / `phase275_p0_eq_llvm.sh`
|
||||
- `phase275_p0_plus_vm.sh` / `phase275_p0_plus_llvm.sh`
|
||||
|
||||
Acceptance:
|
||||
- all 6 smokes pass (expected exit codes fixed and documented in the scripts).
|
||||
|
||||
---
|
||||
|
||||
## 3) Update “Now / Backlog”
|
||||
|
||||
Files:
|
||||
- `docs/development/current/main/10-Now.md`
|
||||
- `docs/development/current/main/30-Backlog.md`
|
||||
- `docs/development/current/main/phases/phase-275/README.md`
|
||||
|
||||
Edits:
|
||||
- Mark Phase 275 as ✅ complete (P0 done; P1 done).
|
||||
- Move any remaining work (warnings/lints, broader coercion coverage) into Phase 276+ entries.
|
||||
|
||||
---
|
||||
|
||||
## 4) Optional: add diagnostics without new env vars (future phase)
|
||||
|
||||
If you want to surface legacy patterns proactively (without changing semantics again), create a new phase and keep it strictly “diagnostic-only”:
|
||||
- warnings for `if Void`, string-mixed `+`, etc.
|
||||
- no behavior toggles via new env vars
|
||||
|
||||
50
docs/development/current/main/phases/phase-275/README.md
Normal file
50
docs/development/current/main/phases/phase-275/README.md
Normal file
@ -0,0 +1,50 @@
|
||||
# Phase 275: Coercion Implementation (truthiness / `==` / `+`)
|
||||
|
||||
Status: ✅ completed (Phase 275 P0)
|
||||
|
||||
Goal: implement the accepted coercion SSOT across backends (Rust VM + LLVM harness) and update language docs and fixtures so behavior cannot drift.
|
||||
|
||||
Accepted SSOT decisions:
|
||||
- `docs/development/current/main/phases/phase-274/P3-DECISIONS.md`
|
||||
|
||||
Language SSOT (updated after Phase 275):
|
||||
- `docs/reference/language/types.md`
|
||||
|
||||
---
|
||||
|
||||
## What changes in this phase
|
||||
|
||||
Implement these semantics (A1/B2/C2):
|
||||
|
||||
1) truthiness (boolean context)
|
||||
- `Void` in condition → **TypeError**
|
||||
- `BoxRef` in condition → **TypeError**, except explicit bridge boxes (BoolBox/IntegerBox/StringBox)
|
||||
- “object is always truthy” remains prohibited
|
||||
|
||||
2) equality (`==`)
|
||||
- allow Int↔Float numeric comparison only (precise rule; no accidental true via float rounding)
|
||||
- Bool is not a number: no Bool↔Int coercion
|
||||
- other mixed kinds: deterministic `false` (not error)
|
||||
- BoxRef equality: identity only
|
||||
|
||||
3) `+`
|
||||
- numeric add: Int+Int, Float+Float, Int↔Float promotion to Float
|
||||
- string concat: **String+String only**
|
||||
- String mixed (`"a"+1`, `1+"a"`) → TypeError (no implicit stringify)
|
||||
|
||||
---
|
||||
|
||||
## Acceptance criteria (minimum)
|
||||
|
||||
- Rust VM behavior matches `P3-DECISIONS.md` for truthiness / `==` / `+`.
|
||||
- LLVM harness behavior matches Rust VM (no backend divergence).
|
||||
- `docs/reference/language/types.md` is updated to reflect the new executable SSOT.
|
||||
- New fixtures + smokes lock behavior (VM + LLVM) without introducing environment-variable sprawl.
|
||||
- No by-name hardcoding for “special cases”; if something must be special, it must be a documented bridge rule.
|
||||
|
||||
---
|
||||
|
||||
## Implementation guide
|
||||
|
||||
- `docs/development/current/main/phases/phase-275/P0-INSTRUCTIONS.md`
|
||||
- `docs/development/current/main/phases/phase-275/P1-INSTRUCTIONS.md`
|
||||
@ -0,0 +1,65 @@
|
||||
# Phase 276 P0: Quick wins (LLVM harness maintainability)
|
||||
|
||||
Status: ✅ completed (2025-12-22)
|
||||
|
||||
This sheet documents what Phase 276 P0 targeted (and can be used to reproduce the intent for future refactors).
|
||||
|
||||
Reference completion:
|
||||
- `docs/development/current/main/phases/phase-276/P0-COMPLETION.md`
|
||||
|
||||
Non-goals:
|
||||
- pipeline unification / order drift fixes (tracked as Phase 279)
|
||||
- new language features
|
||||
- new env vars
|
||||
|
||||
---
|
||||
|
||||
## Task 1: Remove noisy debug leftovers
|
||||
|
||||
Target:
|
||||
- `src/llvm_py/phi_wiring/wiring.py`
|
||||
|
||||
Acceptance:
|
||||
- no stacktrace spam in normal runs
|
||||
|
||||
---
|
||||
|
||||
## Task 2: Consolidate PHI dst type lookup into SSOT
|
||||
|
||||
Create:
|
||||
- `src/llvm_py/phi_wiring/type_helper.py`
|
||||
|
||||
Provide:
|
||||
- `get_phi_dst_type(...)` (SSOT for “what is the intended type of this ValueId?”)
|
||||
- `dst_type_to_llvm_type(...)` (SSOT for MIR dst_type → LLVM IR type)
|
||||
|
||||
Integrate (remove duplicate logic):
|
||||
- `src/llvm_py/phi_wiring/tagging.py`
|
||||
- `src/llvm_py/llvm_builder.py`
|
||||
- `src/llvm_py/phi_wiring/wiring.py`
|
||||
|
||||
Acceptance:
|
||||
- the same ValueId gets the same effective type across these entry points
|
||||
- adding a new MIR dst_type requires changing only `type_helper.py`
|
||||
|
||||
---
|
||||
|
||||
## Task 3: Make PHI type mismatch visible
|
||||
|
||||
Target:
|
||||
- `src/llvm_py/phi_wiring/wiring.py`
|
||||
|
||||
Policy:
|
||||
- important mismatch warnings should be visible even without debug env vars
|
||||
- keep the full trace behind the existing debug gate
|
||||
|
||||
---
|
||||
|
||||
## Task 4: Confirm the change does not regress existing smokes
|
||||
|
||||
Minimum:
|
||||
- build still succeeds
|
||||
- representative LLVM harness runs still pass (no new failures)
|
||||
|
||||
Note:
|
||||
- If a failure points to “two pipelines / ordering drift”, treat it as Phase 279 scope.
|
||||
22
docs/development/current/main/phases/phase-276/README.md
Normal file
22
docs/development/current/main/phases/phase-276/README.md
Normal file
@ -0,0 +1,22 @@
|
||||
# Phase 276 P0: Quick wins (type helper SSOT)
|
||||
|
||||
Status: ✅ completed (2025-12-22)
|
||||
|
||||
Goal: keep the LLVM harness maintainable by removing “local heuristics” and consolidating type lookup into a single SSOT helper.
|
||||
|
||||
Scope (P0):
|
||||
- Consolidate PHI destination type lookup into `type_helper.py` (SSOT).
|
||||
- Remove noisy debug leftovers.
|
||||
- Make “PHI type mismatch” more visible (fail-fast friendly diagnostics).
|
||||
|
||||
Docs:
|
||||
- Instructions: `docs/development/current/main/phases/phase-276/P0-INSTRUCTIONS.md`
|
||||
- Completion: `docs/development/current/main/phases/phase-276/P0-COMPLETION.md`
|
||||
|
||||
Note (important):
|
||||
- The “two pipelines / order drift” root cause is tracked separately as **Phase 279** (pipeline SSOT unification).
|
||||
|
||||
Non-goals:
|
||||
- introduce new language features
|
||||
- widen the type system (no Union/Any work here)
|
||||
- broad optimizer rewrite
|
||||
74
docs/development/current/main/phases/phase-277/P0-DESIGN.md
Normal file
74
docs/development/current/main/phases/phase-277/P0-DESIGN.md
Normal file
@ -0,0 +1,74 @@
|
||||
# Phase 277 P0: PHI型推論ドキュメント整備(design-first)
|
||||
|
||||
Status: planned / docs
|
||||
|
||||
Goal: Phase 275/276 で導入・修正された PHI 型推論(MIR→LLVM harness)について、導線・責務・SSOT を “迷子にならない形” で固定する。
|
||||
|
||||
Scope:
|
||||
- 実装のリファクタではなく **ドキュメントSSOT**。
|
||||
- “どのファイルが何の責務か” を明確にし、次回のデバッグで「触る場所」を 1 本化する。
|
||||
|
||||
Non-goals:
|
||||
- 新しい env var 追加
|
||||
- PHI アルゴリズム変更(仕様変更)
|
||||
- 既定挙動変更
|
||||
|
||||
---
|
||||
|
||||
## 1) SSOT の入口を定義する
|
||||
|
||||
必ず最初に入口を 1 箇所に固定する:
|
||||
- `docs/development/current/main/phases/phase-277/README.md` を「入口SSOT」にする
|
||||
- PHI 関連の “概念” と “コードの参照先” を README から辿れるようにする
|
||||
|
||||
---
|
||||
|
||||
## 2) “2本のパイプライン” を防ぐ説明を入れる
|
||||
|
||||
このセッションで顕在化した事故:
|
||||
- ルートAでは BinOp 型伝播→PHI 型解決
|
||||
- ルートBでは PHI 型解決→BinOp 型伝播
|
||||
|
||||
結果:
|
||||
- 同じ fixture が片方で PASS、片方で FAIL(実質 “2本のコンパイラ”)
|
||||
|
||||
P0 では、これを README で明示し、根治フェーズ(Phase 279)へ誘導する文言を固定する。
|
||||
|
||||
---
|
||||
|
||||
## 3) コード責務マップ(最低限)
|
||||
|
||||
以下の “責務の箱” を docs に書き出す(箇条書きでよい):
|
||||
|
||||
- **MIR 側(型情報のSSOT)**
|
||||
- `MirInstruction.dst_type` の意味(instruction-local SSOT)
|
||||
- `value_types`(propagation/inference の結果)の意味(analysis SSOT)
|
||||
- PHI の `dst_type` の意味(PHI-local SSOT)
|
||||
|
||||
- **JoinIR / bridge 側**
|
||||
- “どのルートで type propagation が走るか”
|
||||
- “どの段で value_types が更新されるか”
|
||||
|
||||
- **LLVM harness 側(消費者)**
|
||||
- `type_helper.py` が SSOT であること(Phase 276 P0)
|
||||
- `dst_type_to_llvm_type` の contract(`f64` / `i64` handle / `void`)
|
||||
|
||||
---
|
||||
|
||||
## 4) デバッグ導線(最小)
|
||||
|
||||
“迷子防止” のため、以下を docs に固定する:
|
||||
- PHI関連の推奨 env var(Phase 277 P2 の統合版)
|
||||
- `NYASH_LLVM_DEBUG_PHI=1`
|
||||
- `NYASH_LLVM_DEBUG_PHI_TRACE=1`
|
||||
- `NYASH_LLVM_PHI_STRICT=1`
|
||||
- 典型的な確認コマンド(1〜2本だけ)
|
||||
- 失敗時に「次に見るファイル」を 1 行で指示(type_helper → wiring → resolver の順など)
|
||||
|
||||
---
|
||||
|
||||
## 5) 完了条件
|
||||
|
||||
- README から “PHI型推論の導線” が 1 本で読める
|
||||
- “2本のパイプライン” の危険と、根治フェーズ(Phase 279)へのリンクが明示されている
|
||||
- 既存 docs との矛盾がない(Phase 275/276/277 の整合)
|
||||
@ -0,0 +1,79 @@
|
||||
# Phase 277 P0: PHI型推論 SSOT docs を完成させる(Claude Code 指示書)
|
||||
|
||||
Status: instructions / docs-only
|
||||
|
||||
目的:
|
||||
- PHI型推論(MIR→LLVM harness)の導線・責務・SSOT を **1本に固定**し、次回のデバッグで迷子にならないようにする。
|
||||
|
||||
スコープ:
|
||||
- docs のみ(設計と導線の固定)
|
||||
- 実装変更・仕様変更は行わない
|
||||
|
||||
Non-goals:
|
||||
- “2本のコンパイラ(パイプライン差)” の根治(Phase 279)
|
||||
- env var 追加(禁止)
|
||||
|
||||
入口SSOT:
|
||||
- `docs/development/current/main/phases/phase-277/README.md`
|
||||
|
||||
参照:
|
||||
- P0設計メモ: `docs/development/current/main/phases/phase-277/P0-DESIGN.md`
|
||||
- P2完了(env var 統合): `docs/development/current/main/phases/phase-277/P2-COMPLETION.md`
|
||||
- env vars: `docs/reference/environment-variables.md`
|
||||
- type helper SSOT(Phase 276 P0): `src/llvm_py/phi_wiring/type_helper.py`
|
||||
|
||||
---
|
||||
|
||||
## Step 1: README を “PHI型推論の地図” にする
|
||||
|
||||
`docs/development/current/main/phases/phase-277/README.md` に以下の節を追加/更新して、READMEだけ読めば導線が分かる状態にする。
|
||||
|
||||
必須内容(短くてOK):
|
||||
- 何が SSOT か(どのファイルが “決める” か)
|
||||
- どこが consumer か(LLVM harness が何を期待するか)
|
||||
- どこを見れば原因が特定できるか(迷子防止)
|
||||
|
||||
最低限の “責務マップ”:
|
||||
- MIR 側:
|
||||
- `MirInstruction.dst_type`(instruction-local)
|
||||
- propagated `value_types`(analysis)
|
||||
- PHI `dst_type`(PHI-local)
|
||||
- LLVM harness 側:
|
||||
- PHI env var SSOT: `src/llvm_py/phi_wiring/debug_helper.py`
|
||||
- 型取得 SSOT: `src/llvm_py/phi_wiring/type_helper.py`
|
||||
- PHI placeholder SSOT: `src/llvm_py/phi_wiring/wiring.py::ensure_phi`
|
||||
- 順序検証: `src/llvm_py/phi_placement.py`(現状は verify/report)
|
||||
|
||||
注意:
|
||||
- llvmlite は基本 “命令の並べ替え” ができないことを明記する(PHI-first は生成時に守る)。
|
||||
- “2本のパイプライン” 問題は Phase 279 へリンクし、P0 で根治しないことを明確化する。
|
||||
|
||||
---
|
||||
|
||||
## Step 2: デバッグ導線(最小)を README に固定
|
||||
|
||||
README に以下を固定する(1〜2コマンドだけ、冗長にしない):
|
||||
|
||||
- 推奨 env var(Phase 277 P2 統合版)
|
||||
- `NYASH_LLVM_DEBUG_PHI=1`
|
||||
- `NYASH_LLVM_DEBUG_PHI_TRACE=1`
|
||||
- `NYASH_LLVM_PHI_STRICT=1`
|
||||
|
||||
- 典型コマンド(例)
|
||||
- `NYASH_LLVM_DEBUG_PHI=1 NYASH_LLVM_USE_HARNESS=1 ./target/release/hakorune --backend llvm apps/tests/<fixture>.hako`
|
||||
|
||||
- 失敗時の “次に見るファイル” を 1 行で指示(固定順)
|
||||
- `type_helper.py → wiring.py → llvm_builder.py → resolver.py`
|
||||
|
||||
---
|
||||
|
||||
## Step 3: docs 整合チェック(最小)
|
||||
|
||||
確認:
|
||||
- `docs/development/current/main/10-Now.md` の “次にやる” が Phase 277 を指していること
|
||||
- `docs/development/current/main/30-Backlog.md` の Phase 277/278/279 が矛盾していないこと
|
||||
|
||||
Acceptance:
|
||||
- README が入口SSOTとして成立(READMEだけで導線が追える)
|
||||
- P0-DESIGN/P1-VALIDATION/P2-COMPLETION へリンクがある
|
||||
- 新しい env var を増やしていない
|
||||
@ -0,0 +1,100 @@
|
||||
# Phase 277 P1: PHI順序検証を fail-fast 導線に接続する(Claude Code 指示書)
|
||||
|
||||
Status: instructions / validation
|
||||
|
||||
目的:
|
||||
- PHI の “順序違反/配線欠落/型不整合” を **原因箇所で止める**(fail-fast)。
|
||||
- strict mode(`NYASH_LLVM_PHI_STRICT=1`)が “実際に効く” 状態にする。
|
||||
|
||||
重要な制約:
|
||||
- Phase 277 P1 は **LLVM harness(Python)側の検証強化**が主。JoinIR/Rust の型伝播パイプラインには踏み込まない(根治は Phase 279)。
|
||||
- env var 増殖禁止(既存の3つのみ)。
|
||||
- by-name hardcode 禁止(特定関数名の例外分岐などは増やさない)。
|
||||
|
||||
参照:
|
||||
- 検証方針: `docs/development/current/main/phases/phase-277/P1-VALIDATION.md`
|
||||
- PHI placeholder SSOT: `src/llvm_py/phi_wiring/wiring.py::ensure_phi`
|
||||
- PHI ordering verifier: `src/llvm_py/phi_placement.py::verify_phi_ordering`
|
||||
- 実行導線(現状SSOT): `src/llvm_py/builders/function_lower.py`(`_finalize_phis` 経路)
|
||||
- env vars SSOT: `src/llvm_py/phi_wiring/debug_helper.py`
|
||||
|
||||
---
|
||||
|
||||
## Step 1: strict mode で “PHIが遅く作られた” を即死にする
|
||||
|
||||
対象:
|
||||
- `src/llvm_py/phi_wiring/wiring.py::ensure_phi`
|
||||
|
||||
現状:
|
||||
- `bb.terminator` がある状態で PHI を作ろうとすると warning を出すだけ。
|
||||
|
||||
P1の変更:
|
||||
- `NYASH_LLVM_PHI_STRICT=1` のときは fail-fast:
|
||||
- 例: `raise RuntimeError(...)`(block_id/dst_vid を含める)
|
||||
- strict 以外は従来どおり warning + 継続(既定挙動を壊さない)。
|
||||
|
||||
Acceptance:
|
||||
- “PHI after terminator” が strict で必ず落ちる。
|
||||
- エラー文に `block_id`, `dst_vid`, “next file” を含める(迷子防止)。
|
||||
|
||||
---
|
||||
|
||||
## Step 2: strict mode で “fallback 0” を禁止する
|
||||
|
||||
対象候補(実態に合わせて最小1箇所から):
|
||||
- `src/llvm_py/phi_wiring/wiring.py::wire_incomings`
|
||||
- もしくは `src/llvm_py/llvm_builder.py::finalize_phis`(ローカル実装が残っているので注意)
|
||||
|
||||
方針:
|
||||
- incoming が解決できずに `0` を入れる分岐があるなら、strict で Err にする。
|
||||
- Err には `block_id/dst_vid/pred_bid` を必ず含める。
|
||||
|
||||
注意:
|
||||
- どの finalize 経路が SSOT かを明確にする(現状は `builders/function_lower.py` が実行導線)。
|
||||
- “2本の finalize 実装” を統合するのは Phase 279 のスコープ。P1では SSOT 経路に検証を接続する。
|
||||
|
||||
Acceptance:
|
||||
- strict で silent fallback が残っていない(少なくとも PHI incoming の “解決不能→0” は落ちる)。
|
||||
|
||||
---
|
||||
|
||||
## Step 3: `verify_phi_ordering()` を実行導線に接続する
|
||||
|
||||
対象:
|
||||
- `src/llvm_py/phi_placement.py::verify_phi_ordering(builder)`
|
||||
|
||||
現状:
|
||||
- 定義されているが、実行導線から呼ばれていない。
|
||||
- llvmlite は reorder ができないため、verify/report の位置が重要。
|
||||
|
||||
接続点(推奨):
|
||||
- `src/llvm_py/builders/function_lower.py` の関数 lowering の終盤:
|
||||
- `lower_terminators(...)` の後(全命令が出揃った後)
|
||||
- strict のときは NG を Err にする
|
||||
- debug のときは block ごとのサマリを stderr に出す(`NYASH_LLVM_DEBUG_PHI=1`)
|
||||
|
||||
Acceptance:
|
||||
- strict で ordering NG を確実に検出して落とせる。
|
||||
- debug で NG block の数と block_id が出る(過剰ログは避ける)。
|
||||
|
||||
---
|
||||
|
||||
## Step 4: 最小の回帰確認
|
||||
|
||||
目的:
|
||||
- “検証が増えたせいで全部が落ちる” を避けつつ、狙った違反を確実に捕まえる。
|
||||
|
||||
推奨:
|
||||
- 代表 fixture を1つ選び、まず strict=OFF で PASS、strict=ON でも PASS を確認(正常系)。
|
||||
- 既知の壊れ方(PHI late create / missing incoming)を意図的に起こす最小再現があるなら、それで strict で落ちることも確認。
|
||||
|
||||
No new env vars.
|
||||
|
||||
---
|
||||
|
||||
## Completion criteria
|
||||
|
||||
- strict mode が “順序違反/配線欠落” を原因箇所で fail-fast できる
|
||||
- `verify_phi_ordering()` が実行導線に接続されている
|
||||
- 既定(strict=OFF)での挙動は壊さない
|
||||
- Phase 279(根治)へ繋がる前提が docs で明確になっている
|
||||
102
docs/development/current/main/phases/phase-277/P1-VALIDATION.md
Normal file
102
docs/development/current/main/phases/phase-277/P1-VALIDATION.md
Normal file
@ -0,0 +1,102 @@
|
||||
# Phase 277 P1: PHI順序検証強化(validation)
|
||||
|
||||
Status: planned / validation
|
||||
|
||||
Goal: PHI placement/order を fail-fast で検出しやすくし、LLVM harness の “後段で壊れる” ではなく “原因箇所で止まる” を実現する。
|
||||
|
||||
Scope:
|
||||
- 検証とエラーメッセージの改善(実装は最小・局所)
|
||||
- “順序違反” と “型不整合” の可観測性を上げる
|
||||
|
||||
Non-goals:
|
||||
- 新しい env var 追加
|
||||
- 大規模なパイプライン統一(Phase 279)
|
||||
|
||||
---
|
||||
|
||||
## 1) 何を検証するか(契約SSOT)
|
||||
|
||||
最低限、この契約を SSOT として明文化する:
|
||||
|
||||
- **Block内順序**:
|
||||
- PHI 群
|
||||
- non-PHI 命令群
|
||||
- terminator(Branch/Jump/Return)
|
||||
- この順序以外は “バグ” として扱う
|
||||
|
||||
- **PHI 入力の完全性**:
|
||||
- incoming が欠ける場合は fail-fast(既定で silent fallback をしない)
|
||||
- strict mode(`NYASH_LLVM_PHI_STRICT=1`)では必ず Err
|
||||
|
||||
- **型整合**:
|
||||
- `dst_type` と実際に生成する LLVM type が一致していること
|
||||
- mismatch を “CRITICAL” として可視化する(Phase 276 P0 の方針を踏襲)
|
||||
|
||||
---
|
||||
|
||||
## 2) 実装ポイント(現状コードに合わせた最小)
|
||||
|
||||
現状の構造(要点):
|
||||
- llvmlite は “命令の並べ替え” が基本できないため、PHI-first は **生成時**に守る必要がある
|
||||
- `src/llvm_py/phi_placement.py` は “reorder” ではなく “verify/report” が主
|
||||
- PHI 配線は `finalize_phis` で行われる(PHI placeholder 作成→incoming 配線)
|
||||
- 実際のSSOT呼び出しは `src/llvm_py/builders/function_lower.py` の `_finalize_phis(builder, context)` 経路
|
||||
- `NyashLLVMBuilder.finalize_phis()` は別実装が残っており、P1では **どちらをSSOTにするか**を明示する
|
||||
|
||||
実装点(推奨):
|
||||
|
||||
1) **“PHIを遅く作ってしまった” を strict で即死**
|
||||
- 対象: `src/llvm_py/phi_wiring/wiring.py::ensure_phi`
|
||||
- すでに `bb.terminator` を検知して warning を出している
|
||||
- P1では `NYASH_LLVM_PHI_STRICT=1` のとき、ここを fail-fast(例: `raise` / `unreachable` 相当)にする
|
||||
- 期待効果: “順序違反の原因” で止まる
|
||||
|
||||
2) **fallback 0 の採用を strict で禁止**
|
||||
- 対象: PHI incoming を解決できず `0` を選ぶ箇所
|
||||
- `src/llvm_py/llvm_builder.py` および `src/llvm_py/phi_wiring/wiring.py::wire_incomings` に存在
|
||||
- P1では strict のとき:
|
||||
- “missing snapshot / unresolved” を明示エラーにする
|
||||
- エラー文に `block_id / dst_vid / pred_bid` を含める
|
||||
|
||||
3) **PHI ordering verifier を “実行経路に接続”**
|
||||
- 現状 `src/llvm_py/phi_placement.py::verify_phi_ordering(builder)` が未使用
|
||||
- P1では呼び出し点を 1 箇所に固定する:
|
||||
- 候補: `src/llvm_py/builders/function_lower.py` の `lower_terminators(...)` 後
|
||||
- strict のときは ordering NG を Err にする
|
||||
- debug のときは詳細を stderr に出す(`NYASH_LLVM_DEBUG_PHI=1`)
|
||||
|
||||
補足:
|
||||
- ここで reorder はできないので、verifier は “最後に怒る” ではなく
|
||||
“生成時の契約が破られていないことを確認する” 目的で使う
|
||||
|
||||
---
|
||||
|
||||
## 3) エラーメッセージ(迷子防止)
|
||||
|
||||
エラー文は必ず以下を含める:
|
||||
- block id
|
||||
- dst ValueId(PHIの対象)
|
||||
- expected vs actual(型/順序)
|
||||
- 次に見るファイル(1つ、固定)
|
||||
|
||||
推奨:
|
||||
- ordering なら `src/llvm_py/phi_wiring/wiring.py`(PHI生成の入口)
|
||||
- missing incoming なら `src/llvm_py/llvm_builder.py`(snapshot/value解決の入口)
|
||||
|
||||
---
|
||||
|
||||
## 4) 最小テスト
|
||||
|
||||
P1 の目的は “検証が働くこと” なので、最小の再現でよい:
|
||||
- 既存の PHI を含む fixture を 1 つ選ぶ(Phase 275 のものなど)
|
||||
- strict mode で実行して、違反があれば落ちることを確認する
|
||||
|
||||
No new CI jobs.
|
||||
|
||||
---
|
||||
|
||||
## 5) 完了条件
|
||||
|
||||
- PHI順序違反が “原因箇所で” fail-fast する
|
||||
- strict mode が意味を持つ(silent fallback が残っていない)
|
||||
- 既存の正常ケース(代表スモーク)が退行しない
|
||||
@ -4,6 +4,15 @@
|
||||
|
||||
Phase 275/276で完了したFloat型PHI対応・型取得SSOT化の後続改善として、PHI関連の環境変数統合・ドキュメント整備を実施。
|
||||
|
||||
このPhaseの狙いは「PHIまわりの迷子を無くす」こと:
|
||||
- どの層が何を決めるか(SSOT)を固定する
|
||||
- PHI順序/配線の違反を “後段で壊れる” ではなく “原因で止まる” に寄せる
|
||||
- そして根治として「2本のコンパイラ(パイプライン差による二重バグ)」を Phase 279 で潰せるように導線を引く
|
||||
|
||||
入口(関連):
|
||||
- Now: `docs/development/current/main/10-Now.md`
|
||||
- Backlog: `docs/development/current/main/30-Backlog.md`
|
||||
|
||||
---
|
||||
|
||||
## サブフェーズ一覧
|
||||
@ -13,8 +22,12 @@ Phase 275/276で完了したFloat型PHI対応・型取得SSOT化の後続改善
|
||||
- 目的: Phase 275/276で実装したPHI型推論ロジックのドキュメント化
|
||||
- 内容:
|
||||
- MIR型伝播 → LLVM IR型生成のフロー図
|
||||
- type_helper.py の設計ドキュメント
|
||||
- type_helper.py(LLVM harness 側の型取得SSOT)の設計ドキュメント
|
||||
- PHI型推論のベストプラクティス
|
||||
- 設計メモ(このPhase配下のSSOT案):
|
||||
- `docs/development/current/main/phases/phase-277/P0-DESIGN.md`
|
||||
- 指示書(Claude Code):
|
||||
- `docs/development/current/main/phases/phase-277/P0-INSTRUCTIONS.md`
|
||||
|
||||
### Phase 277 P1: PHI順序検証強化(予定)
|
||||
|
||||
@ -23,6 +36,10 @@ Phase 275/276で完了したFloat型PHI対応・型取得SSOT化の後続改善
|
||||
- phi_placement.py の検証ロジック強化
|
||||
- LLVM IR仕様準拠チェック(PHI → 非PHI → terminator)
|
||||
- 順序違反時のエラーメッセージ改善
|
||||
- 検証メモ(このPhase配下のSSOT案):
|
||||
- `docs/development/current/main/phases/phase-277/P1-VALIDATION.md`
|
||||
- 指示書(Claude Code):
|
||||
- `docs/development/current/main/phases/phase-277/P1-INSTRUCTIONS.md`
|
||||
|
||||
### Phase 277 P2: PHI関連環境変数の統合・整理 ✅
|
||||
|
||||
@ -65,6 +82,7 @@ NYASH_LLVM_PHI_STRICT=1
|
||||
- **Phase 275**: Float型PHI対応(MIR型伝播 → LLVM IR double生成)
|
||||
- **Phase 276**: 型取得SSOT化(type_helper.py)
|
||||
- **Phase 278**: 後方互換性削除(旧環境変数サポート削除予定)
|
||||
- **Phase 279**: パイプラインSSOT統一(“2本のコンパイラ” 根治)
|
||||
|
||||
---
|
||||
|
||||
@ -74,12 +92,36 @@ NYASH_LLVM_PHI_STRICT=1
|
||||
phase-277/
|
||||
├── README.md # 本ファイル(Phase 277概要)
|
||||
├── P2-COMPLETION.md # P2完了報告
|
||||
├── P0-DESIGN.md # P0設計ドキュメント(予定)
|
||||
└── P1-VALIDATION.md # P1検証強化ドキュメント(予定)
|
||||
├── P0-DESIGN.md # P0設計ドキュメント(docs)
|
||||
└── P1-VALIDATION.md # P1検証強化ドキュメント(validation)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 重要なSSOT(どこが何を決めるか)
|
||||
|
||||
最小の地図(迷ったらここから辿る):
|
||||
|
||||
- **PHI env var(統合SSOT)**: `src/llvm_py/phi_wiring/debug_helper.py`
|
||||
- 使う側は `is_phi_debug_enabled()` / `is_phi_trace_enabled()` / `is_phi_strict_enabled()` だけを見る
|
||||
- 旧 env var の撤去は Phase 278
|
||||
|
||||
- **LLVM harness 側の型取得SSOT**: `src/llvm_py/phi_wiring/type_helper.py`
|
||||
- `get_phi_dst_type(...)` と `dst_type_to_llvm_type(...)` が入口
|
||||
- “PHIのdst_typeをどこから取るか” をここに集約する(Phase 276 P0)
|
||||
|
||||
- **PHI placeholder を block head に作るSSOT**: `src/llvm_py/phi_wiring/wiring.py::ensure_phi`
|
||||
- llvmlite は “後から命令を並べ替える” が基本できない
|
||||
- よって PHI は “作るタイミング” が勝負(PHI-first の契約をここで守る)
|
||||
|
||||
- **順序検証(verifier)**: `src/llvm_py/phi_placement.py`
|
||||
- 現状は “並べ替え” ではなく “検証/レポート” のみ(llvmlite制約)
|
||||
- Phase 277 P1 で fail-fast 導線を強化する(strict mode の意味を強くする)
|
||||
|
||||
- **根治(パイプライン二重化の解消)**: `docs/development/current/main/phases/phase-279/README.md`
|
||||
|
||||
---
|
||||
|
||||
## 今後の予定
|
||||
|
||||
1. **Phase 277 P0**: PHI型推論ドキュメント整備
|
||||
|
||||
@ -0,0 +1,89 @@
|
||||
# Phase 278 P0: Remove deprecated PHI debug env vars
|
||||
|
||||
Status: planned / cleanup
|
||||
|
||||
Goal: remove legacy PHI-related environment variables that were consolidated in Phase 277 P2, so the ecosystem converges to a single set of PHI debug knobs.
|
||||
|
||||
SSOT references:
|
||||
- Consolidation completion: `docs/development/current/main/phases/phase-277/P2-COMPLETION.md`
|
||||
- Env var reference (current): `docs/reference/environment-variables.md`
|
||||
|
||||
Target SSOT (post-Phase 278):
|
||||
- `NYASH_LLVM_DEBUG_PHI=1`
|
||||
- `NYASH_LLVM_DEBUG_PHI_TRACE=1`
|
||||
- `NYASH_LLVM_PHI_STRICT=1`
|
||||
|
||||
Non-goals:
|
||||
- introduce new debug toggles
|
||||
- change PHI behavior (only remove deprecated inputs)
|
||||
- change CLI defaults
|
||||
|
||||
---
|
||||
|
||||
## 1) Identify deprecated env vars (remove inputs)
|
||||
|
||||
Remove support for the legacy variables that were “merged” in Phase 277 P2.
|
||||
|
||||
Expected deprecated set (verify in `docs/reference/environment-variables.md`):
|
||||
- `NYASH_LLVM_PHI_DEBUG`
|
||||
- `NYASH_PHI_TYPE_DEBUG`
|
||||
- `NYASH_PHI_ORDERING_DEBUG`
|
||||
- `NYASH_LLVM_TRACE_PHI`
|
||||
- `NYASH_LLVM_VMAP_TRACE`
|
||||
- `NYASH_PYVM_DEBUG_PHI` (if still present in code; PyVM line is historical)
|
||||
|
||||
Policy:
|
||||
- Do not keep “silent compatibility”.
|
||||
- If a deprecated var is detected, print a one-line error with the replacement and exit non-zero.
|
||||
|
||||
Rationale:
|
||||
- Keeping deprecated behavior is how env var sprawl comes back.
|
||||
|
||||
---
|
||||
|
||||
## 2) Update the runtime checks to accept only the SSOT set
|
||||
|
||||
Target:
|
||||
- `src/llvm_py/phi_wiring/debug_helper.py` (SSOT entry for PHI debug flags)
|
||||
|
||||
Acceptance:
|
||||
- only the SSOT variables are read
|
||||
- deprecated variables trigger a clear failure message (replacement shown)
|
||||
|
||||
---
|
||||
|
||||
## 3) Update documentation (SSOT)
|
||||
|
||||
Update:
|
||||
- `docs/reference/environment-variables.md`
|
||||
|
||||
Requirements:
|
||||
- Remove deprecated entries from tables (or move them to a “Removed in Phase 278” section).
|
||||
- Keep examples only with `NYASH_LLVM_DEBUG_PHI`, `NYASH_LLVM_DEBUG_PHI_TRACE`, `NYASH_LLVM_PHI_STRICT`.
|
||||
- Add a short migration note:
|
||||
- “If you used X, replace with Y.”
|
||||
|
||||
---
|
||||
|
||||
## 4) Add/Update smoke coverage
|
||||
|
||||
Minimum:
|
||||
- One LLVM harness smoke that runs with:
|
||||
- `NYASH_LLVM_DEBUG_PHI=1`
|
||||
- `NYASH_LLVM_DEBUG_PHI_TRACE=1`
|
||||
- `NYASH_LLVM_PHI_STRICT=1`
|
||||
and verifies the run completes (no strict-mode violations in the fixture).
|
||||
|
||||
Deprecation enforcement:
|
||||
- One smoke (or a small shell snippet in an existing test) that sets a deprecated var and expects a non-zero exit.
|
||||
|
||||
No new env vars.
|
||||
|
||||
---
|
||||
|
||||
## 5) Completion criteria
|
||||
|
||||
- Deprecated env vars no longer affect behavior (removed from code paths).
|
||||
- Deprecated env vars cause a fail-fast error with a replacement hint.
|
||||
- Docs reflect only the SSOT set.
|
||||
- The representative smokes remain green.
|
||||
18
docs/development/current/main/phases/phase-278/README.md
Normal file
18
docs/development/current/main/phases/phase-278/README.md
Normal file
@ -0,0 +1,18 @@
|
||||
# Phase 278 (planned): Remove deprecated PHI debug env vars
|
||||
|
||||
Status: planned / cleanup
|
||||
|
||||
Goal: remove legacy PHI debug environment variables that were consolidated in Phase 277 P2, so the ecosystem converges to a single, memorable set.
|
||||
|
||||
Reference:
|
||||
- Consolidation doc: `docs/development/current/main/phases/phase-277/P2-COMPLETION.md`
|
||||
- Env var reference: `docs/reference/environment-variables.md` (PHI デバッグ関連)
|
||||
|
||||
Target SSOT (post-Phase 278):
|
||||
- `NYASH_LLVM_DEBUG_PHI=1`
|
||||
- `NYASH_LLVM_DEBUG_PHI_TRACE=1`
|
||||
- `NYASH_LLVM_PHI_STRICT=1`
|
||||
|
||||
Implementation guide:
|
||||
- `docs/development/current/main/phases/phase-278/P0-INSTRUCTIONS.md`
|
||||
|
||||
@ -0,0 +1,96 @@
|
||||
# Phase 279 P0: Unify type propagation pipeline (SSOT)
|
||||
|
||||
Status: planned / implementation
|
||||
|
||||
Problem:
|
||||
- Type propagation and PHI type resolution are executed in multiple routes with different orderings.
|
||||
- This creates “double bugs”: the same fixture can pass in one route and fail in another, even when the frontend and MIR are identical.
|
||||
|
||||
Goal:
|
||||
- Define a single **TypePropagationPipeline** (SSOT) with a fixed order, and make all routes call it.
|
||||
|
||||
Constraints:
|
||||
- No new environment variables.
|
||||
- No by-name hardcode dispatch.
|
||||
- Fail-fast on invariants (no silent fallback).
|
||||
|
||||
---
|
||||
|
||||
## 1) Define the SSOT pipeline (single entry)
|
||||
|
||||
Create one entry function (name is flexible, keep it unambiguous):
|
||||
- `run_type_propagation_pipeline(module: &mut MirModule, mode: ...) -> Result<(), String>`
|
||||
|
||||
Fixed order (SSOT):
|
||||
1. Copy type propagation
|
||||
2. BinOp type re-propagation
|
||||
3. PHI type resolution
|
||||
4. Minimal follow-ups required for downstream typing (Compare / TypeOp / etc.), only if already needed by current backends
|
||||
|
||||
Hard rule:
|
||||
- No route may run PHI type resolution before BinOp re-propagation.
|
||||
|
||||
Rationale:
|
||||
- PHI type inference depends on stabilized incoming value types.
|
||||
|
||||
---
|
||||
|
||||
## 2) Route integration (remove local ordering)
|
||||
|
||||
Make all relevant routes call the SSOT entry and remove/disable any local ordering logic.
|
||||
|
||||
Known routes to check (examples; confirm in code):
|
||||
- Builder lifecycle path (emits MIR directly)
|
||||
- JoinIR → MIR bridge path
|
||||
- Any “analysis/json emit” path that downstream LLVM harness relies on for `value_types`
|
||||
|
||||
Acceptance:
|
||||
- Each route calls the same SSOT entry.
|
||||
- There is no remaining “partial pipeline” that can reorder steps.
|
||||
|
||||
---
|
||||
|
||||
## 3) Fail-fast order guard (prevent regressions)
|
||||
|
||||
Add an invariant checker that makes order drift obvious:
|
||||
- If PHI type resolution is invoked while BinOp re-propagation has not run, return `Err(...)`.
|
||||
|
||||
This is not a feature toggle. It is a structural guard.
|
||||
|
||||
---
|
||||
|
||||
## 4) Backends: define the contract for `value_types`
|
||||
|
||||
Document (in code/doc) what the downstream expects:
|
||||
- A `ValueId` that is `f64` must be consistently typed as `f64` across:
|
||||
- MIR instruction dst_type
|
||||
- propagated/inferred `value_types`
|
||||
- PHI dst_type
|
||||
- “i64 handle” vs “unboxed f64” must be consistent for the LLVM harness.
|
||||
|
||||
Avoid “best-effort” inference at the harness layer. If the type is unknown, fail-fast where the SSOT contract is violated.
|
||||
|
||||
---
|
||||
|
||||
## 5) Minimal acceptance tests
|
||||
|
||||
Minimum:
|
||||
- A representative fixture that exercises:
|
||||
- Copy chain
|
||||
- BinOp promotion (e.g. Int+Float)
|
||||
- PHI over that promoted value
|
||||
and is executed via all relevant routes.
|
||||
- The MIR JSON `value_types` is consistent across routes.
|
||||
|
||||
Suggested validation commands (keep local, do not add CI jobs):
|
||||
- `cargo build --release`
|
||||
- relevant smoke(s): `tools/smokes/v2/run.sh --profile quick`
|
||||
|
||||
---
|
||||
|
||||
## 6) Completion criteria
|
||||
|
||||
- No route-specific ordering logic remains.
|
||||
- Order guard prevents PHI-before-BinOp execution.
|
||||
- A reproduction fixture cannot diverge across routes due to type propagation ordering.
|
||||
- Documentation points to this phase as the SSOT for “pipeline unification”.
|
||||
25
docs/development/current/main/phases/phase-279/README.md
Normal file
25
docs/development/current/main/phases/phase-279/README.md
Normal file
@ -0,0 +1,25 @@
|
||||
# Phase 279 (planned): Type propagation pipeline SSOT unification
|
||||
|
||||
Status: planned / implementation
|
||||
|
||||
Goal: eliminate “two compiler pipelines” by making type propagation run through **one SSOT entry** with a fixed order across routes, so the same fixture cannot pass in one route and fail in another purely due to ordering drift.
|
||||
|
||||
Background trigger:
|
||||
- A real incident occurred where PHI type resolution ran before BinOp re-propagation in one route, but after it in another, producing LLVM parity breakage. This is effectively “two compilers”.
|
||||
|
||||
Scope:
|
||||
- Define a single type propagation pipeline entry (SSOT).
|
||||
- Make every route that emits MIR/LLVM metadata call that SSOT entry.
|
||||
- Add fail-fast guards that make order drift impossible to miss.
|
||||
|
||||
SSOT references:
|
||||
- Current status log: `docs/development/current/main/10-Now.md`
|
||||
- Backlog entry: `docs/development/current/main/30-Backlog.md`
|
||||
|
||||
Implementation guide:
|
||||
- `docs/development/current/main/phases/phase-279/P0-INSTRUCTIONS.md`
|
||||
|
||||
Non-goals:
|
||||
- new language features (no Union/Any)
|
||||
- broad optimizer rewrite
|
||||
- adding new environment variables
|
||||
Reference in New Issue
Block a user