148 lines
4.1 KiB
Markdown
148 lines
4.1 KiB
Markdown
|
|
# 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`
|
|||
|
|
|