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:
2025-12-22 14:48:37 +09:00
parent 6e749b791e
commit 757193891f
74 changed files with 4178 additions and 575 deletions

View File

@ -72,7 +72,7 @@ Rust製インタープリターによる高性能実行と、直感的な構文
```nyash
box ClassName {
# フィールド宣言Phase 12.7形式)
field1: TypeBox # フィールド型アノテーション(P0では無視
field1: TypeBox # 型アノテーション(現状は契約として未強制。SSOT: docs/reference/language/types.md
field2: TypeBox
field3 # 型なしも可
@ -133,10 +133,9 @@ static box Main {
```
注意静的Boxのメソッド引数規約
- 静的Boxのメソッドは先頭に暗黙の `self`= Singletonが存在する
- つまり呼び出し側 `Main.main()` のように書いても、意味論上は `Main.main(self, ...)` の形になる。
- VM/MIR/LLVM いずれのバックエンドでも、この規約に基づき引数個数arityを判定する
- 開発時のガイドライン: 静的Box内に定義する全メソッドは「先頭に `self` を取る」形で設計すること(将来の最適化や検証で一貫性を保つため)。
- 静的Boxのメソッドは **暗黙の receiver`me/self`)を持たない**(既定)
- 呼び出し側 `Main.main()` は、`Main.main/<arity>` のような **receiver無しの関数呼び出し**へ正規化される。
- 静的Box内で `me/this` を instance receiver として扱うのは禁止Fail-Fast。必要なら `Main.some_static()` の形で呼び出す
📌 **構文に関する注意(`static method` について)**

View File

@ -19,6 +19,9 @@ Imports and namespaces
Variables and scope
- See: reference/language/variables-and-scope.md — Block-scoped locals, assignment resolution, and strong/weak reference guidance.
Type system (SSOT)
- See: reference/language/types.md — runtime truthiness, `+`/compare/equality semantics, and the role/limits of MIR type facts.
Grammar (EBNF)
- See: reference/language/EBNF.md — Stage2 grammar specification used by parser implementations.
- Unified Members (stored/computed/once/birth_once): see reference/language/EBNF.md “Box Members (Phase 15)” and the Language Reference section. Default ON (disable with `NYASH_ENABLE_UNIFIED_MEMBERS=0`).

View File

@ -40,21 +40,18 @@ Semicolons and ASI (Automatic Semicolon Insertion)
- Ambiguous continuations; parser must FailFast with a clear message.
Truthiness (boolean context)
- `Bool` → itself
- `Integer``0` is false; nonzero is true
- `String` → empty string is false; otherwise true
- `Array`/`Map` → nonnull is true (size is not consulted)
- `null`/`void` → false
- SSOT: `reference/language/types.md`runtime truthiness
- 実行上は `Bool/Integer/Float/String/Void` が中心。`BoxRef` は一部のコアBoxのみ許可され、その他は `TypeError`Fail-Fast
Equality and Comparison
- `==` and `!=` compare primitive values (Integer/Bool/String). No implicit crosstype coercion.
- Box/Instance comparisons should use explicit methods (`equals`), or be normalized by the builder.
- Compare operators `< <= > >=` are defined on integers (MVP).
- SSOT: `reference/language/types.md``==`/`!=``< <= > >=` の runtime 仕様)
- `==` は一部の cross-kind`Integer↔Bool`, `Integer↔Float`)を best-effort で扱う。その他は `false`
- `< <= > >=``Integer/Float/String` の **同型同士**のみ(異型は `TypeError`)。
String and Numeric `+`
- If either side is `String`, `+` is string concatenation.
- If both sides are numeric, `+` is addition.
- Other mixes are errors (dev: warn; prod: error) — keep it explicit必要なら `str(x)` を使う)。
- SSOT: `reference/language/types.md`runtime `+` 仕様)
- `Integer+Integer`, `Float+Float` は加算。片側が `String` なら文字列連結(相手は文字列化)。
- それ以外(例: `Integer+Bool`, `Integer+Float`)は `TypeError`Fail-Fast)。
Blocks and Control
- `if (cond) { ... } [else { ... }]`

View File

@ -0,0 +1,133 @@
# Type System (SSOT)
Status: Draft / active (2025-12)
This document defines the **current, executable** type semantics of Nyash/Hakorune.
Implementation is currently anchored to the Rust VM (`src/backend/mir_interpreter/*`).
If another backend differs from this document, treat it as a bug unless explicitly noted.
Coercion SSOT status:
- Decisions: `docs/development/current/main/phases/phase-274/P3-DECISIONS.md`
- Implemented (VM + LLVM parity): Phase 275 P0
---
## 1. Big Picture
- Nyash is **dynamically typed**: runtime values carry a tag (Integer/Float/Bool/String/Void/BoxRef/Future).
- `local` declares a variable; **re-assignment is allowed** (single keyword policy).
- There is currently no static type checker. Some parts of MIR carry **type facts** as metadata for optimization / routing, not for semantics.
Terminology (SSOT):
- **Runtime type**: what the VM executes on (`VMValue`).
- **MIR type facts**: builder annotations (`MirType`, `value_types`, `value_origin_newbox`, `TypeCertainty`).
---
## 2. Variables and Re-assignment
- `local x` / `local x = expr` introduces a mutable local variable.
- Re-assignment is always allowed: `x = expr`.
- “Immutable locals” (let/const) are not part of the language today; they can be introduced later as lint/strict checks without changing core semantics.
Note: Field type annotations like `field: TypeBox` exist in syntax, but are currently **not enforced** as a type contract (docs-only) — see `LANGUAGE_REFERENCE_2025.md`.
---
## 3. Boolean Context (truthiness)
Boolean context means:
- `if (cond) { ... }`
- `loop(cond) { ... }`
- `!cond`
- branch conditions generated from `&&` / `||` lowering
Runtime rule (SSOT) is implemented by `to_bool_vm` (`src/backend/abi_util.rs`):
- `Bool` → itself
- `Integer``0` is false; non-zero is true
- `Float``0.0` is false; non-zero is true
- `String` → empty string is false; otherwise true
- `Void`**TypeError** (fail-fast)
- `BoxRef`:
- bridge boxes only:
- `BoolBox` / `IntegerBox` / `StringBox` are unboxed and coerced like their primitive equivalents
- `VoidBox` is treated as `Void`**TypeError**
- other BoxRef types → **TypeError**
- `Future` → error (`TypeError`)
This is intentionally fail-fast: “any object is truthy” is **not** assumed by default today.
---
## 4. Operators: `+`, comparisons, equality
### 4.1 `+` (BinaryOp::Add)
Runtime semantics are defined in the Rust VM (`eval_binop` in `src/backend/mir_interpreter/helpers.rs`):
- Numeric addition:
- `Integer + Integer``Integer`
- `Float + Float``Float`
- Numeric promotion:
- `Integer + Float` / `Float + Integer``Float` (promote int→float)
- String concatenation:
- `String + String``String`
- Other combinations are `TypeError` (e.g., `Integer + Bool`, `Integer + Float`, `Bool + Bool`, `BoxRef + ...`).
Dev-only note:
- `NYASH_VM_TOLERATE_VOID=1` (or `--dev` paths) may tolerate `Void` in some arithmetic as a safety valve; do not rely on it for spec.
### 4.2 `< <= > >=` (CompareOp)
Runtime semantics (`eval_cmp` in `src/backend/mir_interpreter/helpers.rs`):
- `Integer <=> Integer`
- `Float <=> Float`
- `String <=> String` (lexicographic)
- Other combinations are `TypeError`.
### 4.3 `==` / `!=`
Equality is implemented as `eq_vm` (`src/backend/abi_util.rs`) and used by comparisons:
- Same-kind equality for primitives: `Integer/Float/Bool/String/Void`.
- Cross-kind coercions (Number-only):
- `Integer``Float` only, with a precise rule (avoid accidental true via float rounding)
- `BoxRef == BoxRef` is pointer identity (`Arc::ptr_eq`).
- `Void` is treated as equal to `BoxRef(VoidBox)` and `BoxRef(MissingBox)` for backward compatibility.
- Other mixed kinds are `false` (not an error).
Precise rule for `Int == Float` (or `Float == Int`):
- if Float is NaN → false
- if Float is finite, integral, and exactly representable as i64 → compare as i64
- otherwise → false
---
## 5. `is` / `as` and TypeOp
Source patterns like `x.is("TypeName")` / `x.as("TypeName")` are lowered to MIR `TypeOp(Check/Cast)` (see `src/mir/builder/exprs.rs`).
Runtime behavior (Rust VM):
- `TypeOp(Check, value, ty)` produces a `Bool`.
- `TypeOp(Cast, value, ty)` returns the input value if it matches; otherwise `TypeError`.
Backend note:
- LLVM (llvmlite harness) must match this SSOT; if it differs, treat it as a bug.
- Tracking: Phase 274 P2 (`docs/development/current/main/phases/phase-274/P2-INSTRUCTIONS.md`).
---
## 6. MIR Type Facts (non-semantic metadata)
MIR has a lightweight type vocabulary (`MirType` in `src/mir/types.rs`) and per-value metadata:
- `value_types: ValueId -> MirType` (type annotations / inferred hints)
- `value_origin_newbox: ValueId -> BoxName` (origin facts for “Known receiver”)
- `TypeCertainty::{Known, Union}` used by call routing (`src/mir/definitions/call_unified.rs`)
Important rule:
- These facts are for **optimization/routing** (e.g., Known-only rewrite, callee resolution) and must not be treated as semantic truth.
If you need semantics, define it at the runtime layer (VM) and then optionally optimize by using these facts.