semantifold 0.11.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/build/src/backends/c.d.ts +2 -1
- package/build/src/backends/c.d.ts.map +1 -1
- package/build/src/backends/c.js +26 -5
- package/build/src/backends/cpp.js +3 -1
- package/build/src/backends/effects.js +3 -2
- package/build/src/backends/java.js +33 -14
- package/build/src/backends/javascript.js +83 -18
- package/build/src/backends/ordered-expressions.d.ts +19 -7
- package/build/src/backends/ordered-expressions.d.ts.map +1 -1
- package/build/src/backends/ordered-expressions.js +46 -11
- package/build/src/backends/php.js +33 -14
- package/build/src/backends/ruby.js +37 -12
- package/build/src/backends/shared.d.ts.map +1 -1
- package/build/src/backends/shared.js +92 -37
- package/build/src/backends/typescript.js +83 -18
- package/build/src/frontends/c.d.ts +34 -1
- package/build/src/frontends/c.d.ts.map +1 -1
- package/build/src/frontends/c.js +128 -21
- package/build/src/frontends/java.d.ts +8 -0
- package/build/src/frontends/java.d.ts.map +1 -1
- package/build/src/frontends/java.js +77 -33
- package/build/src/frontends/javascript-typescript.d.ts +2 -0
- package/build/src/frontends/javascript-typescript.d.ts.map +1 -1
- package/build/src/frontends/javascript-typescript.js +118 -37
- package/build/src/frontends/php.d.ts +8 -0
- package/build/src/frontends/php.d.ts.map +1 -1
- package/build/src/frontends/php.js +68 -29
- package/build/src/frontends/ruby.d.ts +3 -0
- package/build/src/frontends/ruby.d.ts.map +1 -1
- package/build/src/frontends/ruby.js +103 -30
- package/build/src/language-registry.d.ts.map +1 -1
- package/build/src/language-registry.js +16 -1
- package/build/src/semantic/effects.d.ts.map +1 -1
- package/build/src/semantic/effects.js +18 -6
- package/build/src/semantic/lifetime.js +28 -4
- package/build/src/semantic/provenance.d.ts.map +1 -1
- package/build/src/semantic/provenance.js +26 -7
- package/build/src/semantic/types.d.ts +76 -16
- package/build/src/semantic/types.d.ts.map +1 -1
- package/build/src/semantic/types.js +23 -7
- package/build/src/semantic/validate.d.ts.map +1 -1
- package/build/src/semantic/validate.js +106 -35
- package/docs/adr/20261026-typed-error-handling-extension.md +55 -0
- package/docs/adr-numeric-tower.md +156 -0
- package/docs/architecture.md +1 -1
- package/docs/c.md +1 -1
- package/docs/language-support.md +10 -9
- package/docs/testing.md +8 -5
- package/package.json +1 -1
- package/src/backends/c.js +23 -4
- package/src/backends/cpp.js +1 -0
- package/src/backends/effects.js +2 -1
- package/src/backends/java.js +33 -14
- package/src/backends/javascript.js +83 -18
- package/src/backends/ordered-expressions.js +46 -10
- package/src/backends/php.js +33 -14
- package/src/backends/ruby.js +36 -12
- package/src/backends/shared.js +89 -42
- package/src/backends/typescript.js +83 -18
- package/src/frontends/c.js +135 -19
- package/src/frontends/java.js +82 -33
- package/src/frontends/javascript-typescript.js +130 -38
- package/src/frontends/php.js +68 -27
- package/src/frontends/ruby.js +107 -28
- package/src/language-registry.js +15 -0
- package/src/semantic/effects.js +17 -5
- package/src/semantic/lifetime.js +29 -3
- package/src/semantic/provenance.js +27 -12
- package/src/semantic/types.js +24 -6
- package/src/semantic/validate.js +105 -36
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# ADR: Typed error handling extension — ordered catches, finally/ensure, and rethrow
|
|
2
|
+
|
|
3
|
+
Date: 2026-10-26
|
|
4
|
+
Task: [011 — Typed errors and handling](../../todo/011-typed-errors-and-handling.md) completion (STOE-62)
|
|
5
|
+
Status: Accepted for the original-five cohort (PHP, Ruby, JavaScript/JSDoc, TypeScript, Java)
|
|
6
|
+
|
|
7
|
+
## Context
|
|
8
|
+
|
|
9
|
+
Task 011 delivered exactly one nominal catch per `TryStatement` and explicitly rejected `finally`/`ensure`, multiple catches, retry/rethrow, and checked-error effects. Every cohort language has had multi-handler and completion-block syntax from its first release, so the delivered subset could not express ordinary idiomatic error handling. This ADR records the extension decisions and the boundaries that stay closed.
|
|
10
|
+
|
|
11
|
+
## Decision 1 — `TryStatement` owns an ordered handler list plus one optional completion block
|
|
12
|
+
|
|
13
|
+
`TryStatement` now carries `handlers: CatchHandler[]` and an optional `finallyBody: Block`. `CatchHandler` is a first-class semantic node (`catchType`, immutable `catchBinding`, `catchBody`) and joins the `SemanticNode` union so provenance, symbols, and mappings treat each handler independently.
|
|
14
|
+
|
|
15
|
+
- Handlers are evaluated strictly top to bottom and match their exact nominal `ErrorType` identity only. Broad, union, multi-type, and default handlers remain rejected.
|
|
16
|
+
- Semantic validation keeps the Task 011 reachability rule per handler: a handler whose exact nominal identity cannot escape the protected body — including one shadowed by an earlier handler for the same identity — fails with `UNREACHABLE_HANDLER`. Duplicate or shadowed binding names fail with `DUPLICATE_BINDING`.
|
|
17
|
+
- Flow analysis composes as before: the protected body's escaping raises minus each handled identity, unioned with each handler body's raises. Catch entry still discards known-value and optional-presence facts for every outer mutable binding assignable anywhere in the protected body.
|
|
18
|
+
- `finallyBody` runs on every normal and abrupt completion of the try. A completion block that completes abruptly replaces the pending completion **on the paths where it does**, and its own raises escape; on the paths where it completes normally the pending completion passes through unchanged. Both path sets are therefore reachable: the completion block's raises escape *and* the pending raises — from the protected body, from handler bodies, and from earlier statements of the enclosing block — still escape. The try completes normally only when the completion block completes normally and some protected or handler path completes normally. A `try` with no handler is accepted only when it has a completion block.
|
|
19
|
+
- `lifetime.js`, `effects.js`, and `provenance.js` traverse every handler and the completion block; the completion block is a distinct scope that cannot see any handler binding.
|
|
20
|
+
|
|
21
|
+
## Decision 2 — Rethrow is an explicit statement against one in-scope catch binding
|
|
22
|
+
|
|
23
|
+
`RethrowStatement {binding, bindingNode}` re-propagates the exact caught value of a handler binding that is lexically in scope. `bindingNode` is the exact `CatchBinding` node, so provenance and generated mappings resolve a rethrow to the handler that introduced the binding rather than to a free name.
|
|
24
|
+
|
|
25
|
+
- Frontends accept only a rethrow whose target is a known in-scope catch binding of error type: JavaScript/TypeScript `throw <binding>`, PHP `throw $<binding>`, Java `throw <binding>`, Ruby `raise <binding>` and bare `raise` inside an exact rescue (bare `raise` re-raises the handler's own exception, which is exactly the current binding).
|
|
26
|
+
- A rethrow adds the binding's nominal identity to the enclosing block's escaping raises, so an outer handler for that identity stays reachable and an outer handler for an unraisable identity still fails `UNREACHABLE_HANDLER`.
|
|
27
|
+
- Rethrowing a binding that is not in scope, or throwing any non-binding value, stays rejected (`INVALID_RETHROW` semantically, `UNSUPPORTED_SYNTAX` in a frontend).
|
|
28
|
+
- Ruby `retry`, `redo`, rescue modifiers, and `else` clauses remain rejected. `retry` re-executes the protected body, which is a control-flow construct rather than error propagation, and the semantic model has no node for it; this is a permanent exclusion unless a later task designs one explicitly.
|
|
29
|
+
|
|
30
|
+
## Decision 3 — JavaScript and TypeScript express ordered multi-catch as the guarded dispatch chain
|
|
31
|
+
|
|
32
|
+
JavaScript and TypeScript have no sibling `catch` clauses for one `try`, so the exact native profile for an ordered handler list is a guarded dispatch chain: each clause keeps the Task 011 `instanceof` guard, and the unmatched branch of a non-final guard rethrows its own binding into a nested `try` whose single clause is the next handler, while the matched branch is the handler body in the guard's `else` block. The final guard keeps the plain unmatched rethrow and its handler body as the following statements.
|
|
33
|
+
|
|
34
|
+
- The backend emits this chain and the frontend flattens it back to one ordered handler list, so generated source reparses to the same meaning and never exposes target-only boilerplate as semantic statements.
|
|
35
|
+
- Faithfulness is the reason for the shape. Nesting the protected body inside successive `try` blocks would let a later handler catch an error raised by an earlier handler body, which the ordered-list meaning forbids: a handler-body raise must escape outward past every remaining sibling handler. In the dispatch chain each handler body runs outside its own guard, so a handler-body raise propagates past all remaining handlers, exactly like a sibling clause in PHP, Ruby, and Java. The `else` branch is what keeps a handled error from also reaching the next clause.
|
|
36
|
+
- The chain is recognized only in its exact emitted form: one binding per clause, the exact guard, the exact rethrow of that clause's own binding, and for a dispatch clause no finalizer or extra statement in the dispatch `try` and no statements after the single guarded `if` in the clause body. A single-handler clause may spell its handler body either as the statements after the plain rethrow or as the guard's `else` block, which is what the backend emits for a one-element list with a completion block. Any other shape is `UNSUPPORTED_SYNTAX`.
|
|
37
|
+
|
|
38
|
+
## Decision 4 — Checked-error effects in signatures stay permanently excluded
|
|
39
|
+
|
|
40
|
+
No `throws`/`raises` clause, error union, or result type is added to function, constructor, or method signatures, and this is a permanent exclusion for this feature rather than deferred work.
|
|
41
|
+
|
|
42
|
+
- Propagation stays implicit, exactly as Task 011 established: internal fixed-point analysis follows direct calls so a `try` around a helper call is reachable without any declaration, and each call site's escaping error set is derived from resolved callee effects.
|
|
43
|
+
- Cohorts where checked propagation is mandatory are not approximated. Java generation keeps using unchecked `RuntimeException` subclasses and rejects `throws` clauses and checked types; the deferred cohort (Kotlin, Python, C#, Go, C, C++, Rust, Swift, Wasm) continues to reject any typed-error IR with located `UNSUPPORTED_CAPABILITY` — there is no C status code, Rust `Result`, Go error return, or Wasm trap substitute.
|
|
44
|
+
- Constructors and reference-class members may catch errors internally but still cannot expose an unchecked-error effect through a receiver call, consistent with Task 033.
|
|
45
|
+
|
|
46
|
+
## Decision 5 — Cohort boundaries and diagnostics are unchanged
|
|
47
|
+
|
|
48
|
+
The extension adds no capability to the deferred ten targets and no new public feature flag: `typedErrors` remains true only for the original five. Frontend form errors remain `UNSUPPORTED_SYNTAX`, semantic type/control errors keep their stable semantic codes (`INVALID_ERROR_HANDLER`, `INVALID_RETHROW`, `UNREACHABLE_HANDLER`, `DUPLICATE_BINDING`, `NON_ERROR_RAISE`, `UNKNOWN_ERROR`), and target inability remains `UNSUPPORTED_CAPABILITY` raised before any writer allocation.
|
|
49
|
+
|
|
50
|
+
## Consequences
|
|
51
|
+
|
|
52
|
+
- Idiomatic multi-handler and `finally`/`ensure` programs in the original five now parse, generate, reparse to one meaning, and execute on real toolchains.
|
|
53
|
+
- Every previously accepted single-handler program keeps its exact meaning; the single-handler shape is the one-element case of the ordered list, and the historical Task 011 and Task 013 fixtures are unchanged.
|
|
54
|
+
- Rejected constructs stay rejected with the same codes, so the extension is additive: broad/default handlers, arbitrary thrown values, union catches, `retry`/`else`/rescue modifiers, try-with-resources, checked signatures, causes/suppression, and non-cohort lowering remain outside the model.
|
|
55
|
+
- JavaScript/TypeScript generated output for multi-handler IR is longer than the source-level ideal, because the guard chain is the price of faithful ordered semantics on a target with one clause per `try`.
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
# ADR: Portable numeric tower — FloatType and DecimalType
|
|
2
|
+
|
|
3
|
+
- Status: `proposed` (accepted on merge of this ADR; implementation proceeds per the follow-up plan below)
|
|
4
|
+
- Issue: STOE-57 (Semantifold core — floating-point and decimal numeric types across all 15 languages)
|
|
5
|
+
- Depends on: the STOE-56 integer-operator contract (division, remainder, exponentiation, bitwise/shift, compound/update assignment) for the integer side of every shared spelling; this ADR does not decide those semantics and never changes them.
|
|
6
|
+
|
|
7
|
+
## Context
|
|
8
|
+
|
|
9
|
+
Today every program is integer/boolean/string only. Floating point, decimal, and bigint are explicitly unsupported (`docs/language-support.md`, "Diagnostics and limitations"). Idiomatic numeric programs — money, ratios, measurements, unit conversions — cannot be written at all, and the STOE-56 operator work makes the gap sharper: once integer `/` and `%` exist, users will expect `/` on non-integer operands to mean division, not truncation.
|
|
10
|
+
|
|
11
|
+
The repository's guiding principles (AGENTS.md, `todo/README.md`) constrain the design:
|
|
12
|
+
|
|
13
|
+
1. Model meaning once: the semantic IR carries types and operations, never target spelling.
|
|
14
|
+
2. Fail loudly: a target that cannot honor a contract reports `UNSUPPORTED_CAPABILITY` before any partial artifact.
|
|
15
|
+
3. Prefer a precise portable profile: overflow, rounding, and formatting require explicit contracts, not symbol substitution.
|
|
16
|
+
4. No implicit coercion: equality and arithmetic never mix types silently.
|
|
17
|
+
5. Existing integer semantics are frozen: safe-range literals, checked arithmetic, and the exit-70 overflow convention stay exactly as delivered.
|
|
18
|
+
|
|
19
|
+
This ADR decides the shape of the numeric tower. Per-language literal spellings, generated-helper shapes, and acceptance corpora are separate per-language specs (see Follow-ups).
|
|
20
|
+
|
|
21
|
+
## Decision 1 — The tower has three implemented numeric types now; bigint is a separate follow-up
|
|
22
|
+
|
|
23
|
+
`SemanticTypeName` grows from `"integer" | "boolean" | "string"` to include `"float"` and `"decimal"`. `integer` keeps its exact current contract (JavaScript safe-integer literals, per-target checked arithmetic, single unsigned mathematical zero). `float` and `decimal` are new, distinct, non-coercible scalar types.
|
|
24
|
+
|
|
25
|
+
`bigint` is **not** part of this tower. It is deferred to its own task (see Decision 5) because unbounded width changes formatting, equality, collection-key, and target-native stories in ways orthogonal to fixed-width float/decimal.
|
|
26
|
+
|
|
27
|
+
## Decision 2 — FloatType is IEEE-754 binary64 with a strict fail-loud subset
|
|
28
|
+
|
|
29
|
+
FloatType is exactly the target's native double (`binary64`). The language profile is the **stricter fail-loud subset**, not the full IEEE-754 program: NaN and ±Infinity are **unobservable** — no literal produces them, no accepted operation can produce them, and no builtin (`sqrt`, `log`, …) exists to produce them.
|
|
30
|
+
|
|
31
|
+
### Literals
|
|
32
|
+
|
|
33
|
+
Canonical decimal float literals only:
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
float-literal = [ "-" ] digits [ "." digits ] [ ( "e" | "E" ) [ "+" | "-" ] digits ]
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
- A float literal must carry a fractional part or an exponent: `1.5`, `-2.25`, `1.0`, `1e3`, `1.0e-9` are float; `1` is integer. `1.` and `.5` are rejected (`UNSUPPORTED_SYNTAX`).
|
|
40
|
+
- Underscore separators, hexadecimal/binary floats (`0x1p3`), `f`/`d` suffixes, `inf`, `nan`, and any spelling whose correctly-rounded value is ±Infinity are rejected at the frontend (`NUMERIC_LITERAL_OUT_OF_RANGE` for magnitude, `UNSUPPORTED_SYNTAX` for spelling).
|
|
41
|
+
- A decimal literal maps to its correctly-rounded binary64 value (round-half-even ties). The semantic value is that binary64 value, not the source digits.
|
|
42
|
+
|
|
43
|
+
### Operations
|
|
44
|
+
|
|
45
|
+
`+`, `-`, `*`, `/`, unary `-`, `<`, `<=`, `>`, `>=`, `==`, `!=` on float operands are the IEEE-754 binary64 operations of the target. `/` is selected by operand type: `integer / integer` is the STOE-56 integer-division contract, `float / float` is IEEE division, `decimal / decimal` is the decimal contract (Decision 3). Mixed numeric operands are rejected (Decision 4).
|
|
46
|
+
|
|
47
|
+
### Fail-loud policy
|
|
48
|
+
|
|
49
|
+
- **Division by zero** (`x / 0.0`, including a computed zero divisor) is a runtime failure: the program terminates with exit code `70` and the exact single stderr line `semantifold: float arithmetic failure\n`. This matches the delivered Rust/Zig integer-overflow convention (`semantifold: integer overflow\n`, exit 70).
|
|
50
|
+
- **Overflow to non-finite** (a `+`, `-`, `*`, or `/` whose exact result rounds away from the finite binary64 range) is the same runtime failure. Generated code checks finiteness after each float operation using the target's native classification (`isfinite`, `Float.isFinite`, `f64::is_finite`, `f64.isFinite`, `Float::finite?`, `math::isfinite`, …) and raises the identical failure.
|
|
51
|
+
- **Compile-time-known** overflow or division by zero in a literal operation tree is rejected before emission, exactly as integer trees are rejected today.
|
|
52
|
+
- **Underflow** to zero is permitted and observable as `0.0` (or `-0.0` internally). Denormal values are permitted.
|
|
53
|
+
- **NaN is unreachable**: because no accepted operation can produce it (the only IEEE-invalid candidate, `0.0 / 0.0`, is division by zero and fails first), no NaN policy, payload, or quiet/signaling distinction is ever observable. This is the ADR's NaN/Infinity policy: they are excluded from the language, not formatted or compared.
|
|
54
|
+
|
|
55
|
+
### Equality and ordering
|
|
56
|
+
|
|
57
|
+
Float equality is IEEE value equality on binary64. `-0.0 == 0.0` is true; the sign of zero is otherwise unobservable (printing canonicalizes `-0.0` to `0`, matching the delivered JavaScript/TypeScript `-0` handling). Ordering is IEEE total ordering with the NaN case unreachable. Equality never coerces: `float == integer` and `float == decimal` are validation errors.
|
|
58
|
+
|
|
59
|
+
### Canonical printed form
|
|
60
|
+
|
|
61
|
+
Printed floats use one canonical text contract, independent of each target's native `toString`/`printf` defaults:
|
|
62
|
+
|
|
63
|
+
1. **Shortest round-trip**: the shortest decimal digit string that parses back to the exact same binary64 value (Ryu/Grisu-class correctly-rounded shortest representation).
|
|
64
|
+
2. **Notation rule**: positional notation when the decimal exponent of the value is in `(-5, 17)`; otherwise scientific notation `d.dddde±X` with a lowercase `e`, explicit exponent sign, and no exponent leading zeros.
|
|
65
|
+
3. **Type-preserving text**: the canonical form always carries a decimal point or an exponent, so a printed float re-parses as a float, never as an integer: `1.0`, not `1`; `2.5e10`, not `25000000000`.
|
|
66
|
+
4. Negative zero prints as `0` (the single unsigned zero convention).
|
|
67
|
+
|
|
68
|
+
Backends must not print floats through a target's default scalar rendering unless a per-language spec **proves** that native rendering is exactly this contract for the qualified toolchain (JavaScript `Number.prototype.toString`, Python `repr`, Ruby `Float#to_s`, Go `strconv.FormatFloat(v, 'g', -1, 64)`, Rust `{}` for `f64`, Java `Double.toString`, .NET Core-style `double.ToString()`, and Zig `std.fmt` are candidates; C `printf`, C++ iostreams, PHP's `precision`-controlled conversion, and Swift/Dart defaults are not). Where native rendering is not provably conformant, the backend emits a compiler-owned generated shortest-round-trip formatter (the same private-support-module pattern already used for Kotlin checked helpers, Swift Unicode-scalar equality helpers, and the C/C++ ordered-expression runtime). The acceptance corpus pins exact printed bytes per target.
|
|
69
|
+
|
|
70
|
+
## Decision 3 — DecimalType is fixed-precision 28-significant-digit decimal with round-half-even
|
|
71
|
+
|
|
72
|
+
DecimalType models a fixed-precision decimal number:
|
|
73
|
+
|
|
74
|
+
- **Coefficient**: at most 28 significant digits (chosen because it is the exact intersection of `.NET System.Decimal` and Python `decimal` default precision, and is trivially representable by `java.math.BigDecimal`/`ruby BigDecimal`/`BigInt`/128-bit-integer helpers on every other target).
|
|
75
|
+
- **Scale**: 0 through 28 fractional digits. Scale is part of the value: `1.50` and `1.5` are different decimal values for arithmetic and printing, and both are legal literals.
|
|
76
|
+
- **Literals**: canonical decimal spelling `[ "-" ] digits [ "." digits ]` with at most 28 significant digits and at most 28 fractional digits. No exponent spelling for decimal literals (it would make scale ambiguous). Out-of-range literals report `NUMERIC_LITERAL_OUT_OF_RANGE`. Source spelling per language is a facade question (Decision 6).
|
|
77
|
+
- **Arithmetic**: `+`, `-`, `*`, unary `-`, `/`, `<`, `<=`, `>`, `>=`, `==`, `!=`.
|
|
78
|
+
- `+`/`-`: exact; result scale is `max(scale_a, scale_b)`. A result needing more than 28 significant digits is a runtime failure (exit 70, `semantifold: decimal arithmetic failure\n`).
|
|
79
|
+
- `*`: exact; result scale is `scale_a + scale_b`. If the exact product needs more than 28 significant digits **or** more than 28 fractional digits, the result is rounded to the contract with **round-half-even (banker's rounding)**; if the rounded coefficient still exceeds 28 significant digits, it is the same runtime failure.
|
|
80
|
+
- `/`: division is total for nonzero divisors. The exact quotient is used when representable within 28 significant digits and scale ≤ 28; otherwise the quotient is rounded to scale 28 with **round-half-even**. Division by zero is the exit-70 runtime failure.
|
|
81
|
+
- Compile-time-known decimal overflow or division by zero is rejected before emission.
|
|
82
|
+
- **Rounding rule is round-half-even everywhere** this type rounds (literal rounding is exact by grammar; only `*` scale reduction and `/` round). This is the ADR's explicit "banker's rounding" decision; no target's native half-up default may leak through — backends must pin the rounding mode explicitly (`MathContext(28, RoundingMode.HALF_EVEN)`, `decimal.localcontext(prec=28, rounding=ROUND_HALF_EVEN)`, `MidpointRounding.ToEven`, generated helpers' explicit tie rule).
|
|
83
|
+
- **Equality** is numeric-value equality and is **scale-insensitive**: `1.50 == 1.5` is true. Ordering compares numeric values. Equality never coerces across `integer`/`float`/`decimal`.
|
|
84
|
+
- **Printed form** is exact positional notation preserving the value's scale, including trailing zeros: `1.50` prints as `1.50`. No exponent form. This makes decimal printing deterministic on every target by construction.
|
|
85
|
+
|
|
86
|
+
## Decision 4 — No implicit promotion, ever
|
|
87
|
+
|
|
88
|
+
There is no implicit `integer → float`, `integer → decimal`, or `float → decimal` conversion in any operand position: not in arithmetic, comparisons, equality, assignments, initializers, arguments, returns, or print. A mixed numeric operand pair is a located `TYPE_MISMATCH` at the operator, exactly like today's mixed-type rules. `float` and `decimal` are first-class declared types with their own per-language source spellings (per-language specs).
|
|
89
|
+
|
|
90
|
+
Explicit conversion builtins (`toFloat`, `toDecimal`, `truncate`, `round`) are **out of scope for this issue** and are a follow-up candidate: they are value-changing operations whose rounding contracts must be specified separately, and the fail-loud design does not need them to make the tower usable (literals already carry their type).
|
|
91
|
+
|
|
92
|
+
## Decision 5 — bigint is a separate follow-up task
|
|
93
|
+
|
|
94
|
+
bigint is deliberately excluded from this tower:
|
|
95
|
+
|
|
96
|
+
- Its width is unbounded, so "target-native representation" means `java.math.BigInteger`, Python `int`, Ruby `Integer`, .NET `System.Numerics.BigInteger`, `BigInt` in JS, and generated multi-limb helpers in C/C++/Go/Rust/Swift/Dart/Zig/PHP/Wasm — a materially different support matrix than "native double" for float.
|
|
97
|
+
- Its equality/formatting interaction with the existing safe-integer contract is a trap: Python and Ruby `int` are already unbounded, Go `int64` is architecture-sized, and Java `int` is 32-bit. Adding bigint without re-deciding the integer contract would silently change what `integer` means on those targets.
|
|
98
|
+
- The practical value (exact large counting, IDs) is separable from the practical value of float/decimal (ratios, money).
|
|
99
|
+
|
|
100
|
+
Follow-up: a dedicated bigint task that first decides whether `integer` itself becomes unbounded on dynamic targets or bigint remains a separate type, then proceeds with the same ADR → per-language specs → acceptance chain.
|
|
101
|
+
|
|
102
|
+
## Decision 6 — Target-native representations
|
|
103
|
+
|
|
104
|
+
Float is native on every target; decimal is native where conformant and compiler-owned-support elsewhere, consistent with the hub-and-spoke stdlib design (`docs/standard-library-portability.md`): source-language compatibility facades carry the literal/operation spellings, canonical contracts join them, and target host providers own the native bindings. Application source never resolves provider names.
|
|
105
|
+
|
|
106
|
+
| Target | `float` representation | `decimal` representation |
|
|
107
|
+
| --- | --- | --- |
|
|
108
|
+
| PHP | native `float` | generated compiler-owned fixed-precision helper (no conformant native decimal; GMP is not guaranteed) |
|
|
109
|
+
| Ruby | native `Float` | native `BigDecimal` (compiler-owned `require "bigdecimal"` scaffold, pinned precision context) |
|
|
110
|
+
| JavaScript | native `number` | generated compiler-owned helper over `BigInt` (unscaled + scale) |
|
|
111
|
+
| TypeScript | native `number` | same generated helper, typed |
|
|
112
|
+
| Java | native `double` | native `java.math.BigDecimal` with pinned `MathContext(28, HALF_EVEN)` |
|
|
113
|
+
| Kotlin | native `Double` | native `java.math.BigDecimal` with pinned `MathContext` |
|
|
114
|
+
| Python | native `float` | native `decimal.Decimal` under a compiler-owned `decimal.localcontext(prec=28, rounding=ROUND_HALF_EVEN)` scaffold |
|
|
115
|
+
| C# | native `double` | native `System.Decimal` (28–29 digits; the 28-digit contract sits inside it; division truncation at scale 28 is pinned explicitly) |
|
|
116
|
+
| Go | native `float64` | generated compiler-owned helper (no standard decimal type; `math/big` imports stay outside the generated-support boundary) |
|
|
117
|
+
| C | native `double` | generated compiler-owned helper over `__int128` unscaled + scale (qualified Clang 21) |
|
|
118
|
+
| C++ | native `double` | generated compiler-owned helper over `__int128` unscaled + scale |
|
|
119
|
+
| Rust | native `f64` | generated compiler-owned helper over `i128` unscaled + scale (checked) |
|
|
120
|
+
| Swift | native `Double` | generated compiler-owned helper over `Int128` (Swift 6.3 stdlib) unscaled + scale |
|
|
121
|
+
| Dart | native `double` | generated compiler-owned helper over `BigInt` unscaled + scale |
|
|
122
|
+
| Zig | native `f64` | generated compiler-owned helper over `i128` unscaled + scale (checked) |
|
|
123
|
+
| Wasm | native `f64` ops (`f64.add/sub/mul/div`, `f64.lt/le/gt/ge/eq`) | generated multi-limb helper over `i64` pairs in linear memory |
|
|
124
|
+
|
|
125
|
+
Every generated helper is compiler-owned private support (same class as the existing Kotlin checked-integer helpers, Swift Unicode-scalar helpers, C/C++ ordered-expression runtime, and Wasm scratch arena), emitted only when reachable, and never name-resolvable from portable source.
|
|
126
|
+
|
|
127
|
+
## Interaction with existing integer checked arithmetic
|
|
128
|
+
|
|
129
|
+
- Nothing in this ADR changes integer literals, integer checked arithmetic, the safe-integer range, Java's 32-bit profile, Go's wrap behavior exception, or the exit-70 overflow convention. `integer` is untouched.
|
|
130
|
+
- The `/` and `%` spellings introduced by STOE-56 are **type-selected operations**, not overloaded symbols: the semantic operation is chosen by the resolved operand types before any target spelling exists (`IntegerDivide` per STOE-56's contract; `FloatDivide`; `DecimalDivide`). A mixed pair never selects an operation.
|
|
131
|
+
- Integer operands may not appear where a float or decimal operation is selected, and vice versa; there is no promotion path and no common "numeric" supertype.
|
|
132
|
+
- The fail-loud runtime convention is shared: numeric failures exit 70 with one fixed stderr line per family (`semantifold: integer overflow`, `semantifold: float arithmetic failure`, `semantifold: decimal arithmetic failure`).
|
|
133
|
+
|
|
134
|
+
## Rejection cases and diagnostics
|
|
135
|
+
|
|
136
|
+
- Mixed numeric operands (arithmetic, comparison, equality, assignment, argument, return): located `TYPE_MISMATCH`.
|
|
137
|
+
- Unsupported or malformed float/decimal literal spelling: located `UNSUPPORTED_SYNTAX` (including `1.`, `.5`, underscores, hex floats, suffixes, `inf`/`nan`).
|
|
138
|
+
- Literal magnitude/scale outside the type contract: `NUMERIC_LITERAL_OUT_OF_RANGE`.
|
|
139
|
+
- Compile-time-known float non-finite result or decimal overflow/division-by-zero in a literal tree: located `NUMERIC_LITERAL_OUT_OF_RANGE` before emission (same posture as integer tree rejection).
|
|
140
|
+
- A target whose qualified toolchain cannot honor the contract for a requested module: located `UNSUPPORTED_CAPABILITY` before any partial artifact (fail-loud, never a silently different behavior).
|
|
141
|
+
- Runtime float division-by-zero/overflow and decimal overflow/division-by-zero: exit 70 with the family's fixed stderr line.
|
|
142
|
+
|
|
143
|
+
## Consequences
|
|
144
|
+
|
|
145
|
+
- The semantic IR gains `FloatLiteral`, `DecimalLiteral`, `FloatType`/`DecimalType` scalar names, and the corresponding typed operations; all fifteen frontends and all text backends plus Wasm must implement the exact profiles or reject transactionally.
|
|
146
|
+
- Printed float output becomes a specified canonical contract, which means several backends gain generated support code rather than using native printing — accepted deliberately: determinism outranks emission brevity, and the private-support-module pattern is already established.
|
|
147
|
+
- Decimal's scale-sensitive value model makes `1.50 != 1.5` as values but equal as numbers; documentation must state this plainly because it surprises most readers.
|
|
148
|
+
- The 28-digit decimal contract is a real limitation (no 34-digit `decimal64`, no arbitrary precision); it is chosen for exact native conformance on the widest set of qualified toolchains rather than maximum precision.
|
|
149
|
+
- Existing programs are unaffected: no existing literal, operator, or print behavior changes, and every existing spec stays green.
|
|
150
|
+
|
|
151
|
+
## Follow-ups (implementation chain)
|
|
152
|
+
|
|
153
|
+
1. Per-language specs (one focused spec file per language, per repo convention): exact frontend literal/type spellings, facade shapes for decimal where native spelling is absent, backend emission and helper shapes, native-rendering conformance proofs for float printing, and the accept/reject corpus including division-by-zero, overflow, rounding-tie, and scale cases.
|
|
154
|
+
2. Shared semantic validation and IR extension specs; Wasm lowering spec.
|
|
155
|
+
3. Real-toolchain acceptance per target in the canonical lane (and exact-head TensorBuzz CI), with byte-exact printed output pinned.
|
|
156
|
+
4. Separate follow-up issues: explicit conversion builtins (with their own rounding contracts) and the bigint decision task.
|
package/docs/architecture.md
CHANGED
|
@@ -5,7 +5,7 @@ Semantifold has three explicit layers.
|
|
|
5
5
|
Task 018's C path follows these layers through the unchanged root-bundled private legacy CST boundary. `src/frontends/c.js` owns all C trees and exhaustive profile validation. `src/backends/native-validation.js` checks native graph/location limits and `c-validation.js` checks C memory limits; `ordered-expressions.js` plans occurrence-specific evaluation without modifying IR; `c.js` emits mapped `program.c`; and `c-runtime.js` supplies the synthetic immutable-slice/arena header. The C frontend reconstructs candidates only from complete CST regions, resolves them with the existing semantic validator, and compares the original whole CST against the re-parsed canonical backend plan before returning a module. This same-language validation keeps target generation free of parser objects. See the [C profile](c.md).
|
|
6
6
|
|
|
7
7
|
1. Frontend adapters in `src/frontends/` invoke mature language parsers and translate only recognized syntax. Babel owns JavaScript and TypeScript parsing, `php-parser` owns PHP, Prism owns Ruby, Lezer owns Java, and qualified Tree-sitter bindings and grammars own Python, C#, Go, C, CPP, Rust, Swift, Kotlin, Dart, and Zig. Their AST/CST objects never appear in the public API. Adapters enumerate supported children and reject every other parser node; filtering syntax away is not a normalization strategy. Prism node offsets and comment byte ranges are converted from UTF-8 to JavaScript UTF-16 offsets before semantic location construction and metadata source slicing. Tree-sitter adapters reject lone surrogates, exhaustively traverse named/anonymous/comment/directive/error/missing children, and verify reported boundaries through the shared UTF-8-byte-to-UTF-16 converter. The Go adapter additionally collapses only exact `package main`, optional `fmt`, ordered semantic functions, and a final zero-argument `func main()` into the shared module/entry shape; assignment occurrences, not Go's uniformly mutable `var` syntax, determine semantic local mutability.
|
|
8
|
-
2. `src/semantic/` defines the shared JSDoc-typed discriminated representation and one-based, filename-bearing UTF-16 source locations. `provenance.js` indexes exact source content, deterministic semantic paths/node IDs, resolved symbol IDs, parser-token subranges, and closed source/derived/synthetic origins without exposing parser objects. Task 010's `SemanticProgram` adds dependency-first logical modules, a caller-order multi-source registry, explicit import/export nodes, one selected entry module, and stable module-qualified declaration identities; native filenames, package names, and target paths remain outside semantic resolution. Value types are scalar `integer`/`boolean`/`string` references or recursive acyclic `ListType`, unordered string-keyed `MapType`, insertion-ordered `OrderedMapType`, `OptionalType`, nominal `RecordType`, nominal Task 033 `ReferenceType`, and Task 034 linear `OwnedResourceType`/`OwnedReferenceType`; `void` exists only as a callable return. Task 012 adds invariant unbounded declaration-owned `TypeParameter` values, scoped `TypeVariableReference` leaves, and exact ordered arguments on generic record types. Task 011 adds nominal `ErrorType` only for catch bindings and error constructions, never as an ordinary parameter/local/return value. Task 034 adds compiler-authorized capability/resource/failure/operation declarations, complete resolved `EffectCallExpression` sites, and explicit `OwnedMoveExpression`/`OwnedBorrowExpression` nodes. Every function and reference class/member has a deterministic declaration identity; every list, ordered-map, condition-controlled loop, capability member, and effect site has a deterministic module-local identity. Every direct function, constructor, receiver method, and effect call carries exact resolution facts. Literal and access nodes distinguish scalars, collections, presence/absence, nominal values/references/resources, errors, receivers, and private fields. Unary and binary expressions carry a closed semantic `operation` and explicit result `type`, never a source operator symbol. Every function, constructor, method, entry point, branch, loop, try body, and catch body owns a located `Block` containing ordered `Statement` nodes. Statements additionally include exact `ForEachMapStatement`, strict-Boolean precondition `WhileStatement`, identity-resolved `BreakStatement`/`ContinueStatement`, private receiver-field writes, `RaiseStatement`,
|
|
8
|
+
2. `src/semantic/` defines the shared JSDoc-typed discriminated representation and one-based, filename-bearing UTF-16 source locations. `provenance.js` indexes exact source content, deterministic semantic paths/node IDs, resolved symbol IDs, parser-token subranges, and closed source/derived/synthetic origins without exposing parser objects. Task 010's `SemanticProgram` adds dependency-first logical modules, a caller-order multi-source registry, explicit import/export nodes, one selected entry module, and stable module-qualified declaration identities; native filenames, package names, and target paths remain outside semantic resolution. Value types are scalar `integer`/`boolean`/`string` references or recursive acyclic `ListType`, unordered string-keyed `MapType`, insertion-ordered `OrderedMapType`, `OptionalType`, nominal `RecordType`, nominal Task 033 `ReferenceType`, and Task 034 linear `OwnedResourceType`/`OwnedReferenceType`; `void` exists only as a callable return. Task 012 adds invariant unbounded declaration-owned `TypeParameter` values, scoped `TypeVariableReference` leaves, and exact ordered arguments on generic record types. Task 011 adds nominal `ErrorType` only for catch bindings and error constructions, never as an ordinary parameter/local/return value. Task 034 adds compiler-authorized capability/resource/failure/operation declarations, complete resolved `EffectCallExpression` sites, and explicit `OwnedMoveExpression`/`OwnedBorrowExpression` nodes. Every function and reference class/member has a deterministic declaration identity; every list, ordered-map, condition-controlled loop, capability member, and effect site has a deterministic module-local identity. Every direct function, constructor, receiver method, and effect call carries exact resolution facts. Literal and access nodes distinguish scalars, collections, presence/absence, nominal values/references/resources, errors, receivers, and private fields. Unary and binary expressions carry a closed semantic `operation` and explicit result `type`, never a source operator symbol. Every function, constructor, method, entry point, branch, loop, try body, and catch body owns a located `Block` containing ordered `Statement` nodes. Statements additionally include exact `ForEachMapStatement`, strict-Boolean precondition `WhileStatement`, identity-resolved `BreakStatement`/`ContinueStatement`, private receiver-field writes, `RaiseStatement`, `RethrowStatement` with its exact `CatchBinding` node, and `TryStatement` with an ordered `CatchHandler[]` list plus an optional `finallyBody` completion block; general exception constructs beyond that model have no semantic node. `lifetime.js` retains distinct normal/return/raise/break/continue ownership states and proves every owner terminal or transferred at every reachable scope exit. Task 033 class identity/private state remains single-module semantics; Task 034's effect/resource nodes stay compiler-authorized and single-module by default, Task 035 extends Task 010 `SemanticProgram` with versioned canonical capability modules and negotiated provider link requirements, and Task 036 adds exact facade-resolution evidence plus compiler-owned source/module roles and a complete selected-facade descriptor.
|
|
9
9
|
3. Source and binary backends in `src/backends/` validate their semantic capabilities before emitting target syntax or bytes. Shared validation owns recursive block shape, reachability and all-path abrupt-completion analysis, recursively complete expressions, exhaustive operation/type signatures, function signatures, lexical binding scopes, mutability, declaration-before-use, strict condition/return/argument/initializer/assignment types, fixed-point propagation of nominal unchecked errors, and Task 034 capability/effect/resource graph integrity. `backends/effects.js` rejects unsupported authority/target/context combinations before writer allocation and plans nested eager effects into statement-local collision-safe temporaries in semantic evaluation order. Dependency-first Task 010 validation carries ordinary inferred error sets across resolved function-import identities; both single-module generation and program mode accept compiler-authorized capability modules, while program mode additionally resolves their canonical standard-library contract references and links negotiated target host provider artifacts before any writer allocation. Target-specific validation adds identifier rules, entry-scaffolding name collisions, numeric/memory/resource bounds, and target string representation. The single-module original five retain one fixed compiler-owned Task 034 probe binding, and program mode materializes the same probe behavior as a tree-shaken provider artifact under a protected native binding. Text emitters write through `SourceWriter`; the Wasm encoder writes through an annotated binary writer so every byte range is semantic or explicitly synthetic. A backend either returns a complete executable program/project/application set or raises `UNSUPPORTED_CAPABILITY`; it never silently drops nodes, exposes a partial artifact, or lets an emitter discover malformed shape through a native error.
|
|
10
10
|
|
|
11
11
|
`src/language-registry.js` is the sole ordered dispatch and discovery source. Its deeply frozen records bind implementations to independent frontend, text-backend, binary-backend, application-backend, interoperability, and non-executing developer-check capabilities. Public descriptors derive role booleans, semantic feature flags, artifact multiplicity, round-trip support, rich/v3/byte mapping forms, acceptance stages/toolchains, and the immutable check `supported`/stage/tool subset from those records; all fifteen text targets advertise target-owned check plans. `generalFunctionsAndCalls` is true for PHP, Ruby, JavaScript, TypeScript, Java, Dart, and Zig; every later feature flag remains true only for the original five. `supportedLanguages` is the derived compatibility view containing IDs with both a frontend and text backend. Duplicate IDs, unknown record fields, malformed capabilities, unordered acceptance stages, executing developer-check stages, and undeclared check tools fail as `INVALID_REGISTRY`.
|
package/docs/c.md
CHANGED
|
@@ -28,7 +28,7 @@ Canonical `main` calls `semantifold_cleanup()` once after semantic entry stateme
|
|
|
28
28
|
|
|
29
29
|
The unchanged root-bundled private legacy boundary supplies recursively frozen C CST data from exact `tree-sitter@0.21.1` and `tree-sitter-c@0.23.2`. `src/frontends/c-parser.js` describes that private plain-data boundary; native handles and parser-specific objects never enter semantic values. Every child, field, anonymous token, extra, error, missing node and recovery flag is traversed before meaning is returned. Coordinates and token origins refer to the original UTF-16 source, including astral characters and CRLF. There is no parser-source scan, compiler-based interpretation, preprocessing fallback or source-text recovery.
|
|
30
30
|
|
|
31
|
-
Ordinary source contains the exact support-header include, at least one `static` semantic function
|
|
31
|
+
Ordinary source contains the exact support-header include, at least one `static` semantic function plus final `int main(void)`. Task 005 semantic functions accept any number of explicitly typed named parameters, including zero — spelled `void` for an empty list — and return exact scalars or `void`. A file-scope `static` prototype may precede a function definition for forward and mutual recursion; every prototype must agree exactly with its unique definition, and a prototype without a definition is rejected. Bodies admit initialized single scalar locals, plain assignment, prints through the matching scalar helper, explicit returns and braced conditionals, including `else if`. A non-void function returns a value on every reachable path; a void function may fall through or use a bare `return;`, and a bare return in a non-void function is rejected. A void call is legal only as a standalone statement. `const` locals are immutable; other locals are mutable, and parameters are immutable. Main ends with the exact cleanup call and `return 0`. Raw calls must refer to a preceding function definition or prototype; caller-authored forward calls requiring implicit C declarations are rejected, and canonical generated prototypes support forward calls and recursion.
|
|
32
32
|
|
|
33
33
|
Only direct semantic calls and the exact scalar/literal/print helpers are recognized. Caller-authored calls nested in an operator operand or another call's argument are rejected because ordinary C does not establish the required semantic evaluation order. Trigraph preprocessing in parser-backed comments or string content is rejected. No user preprocessing, typedef substitution, global data, complex/multiple declarator, qualifier beyond local `const`, array, struct, union, enum, pointer, function pointer, cast, address/dereference, member access, ownership call, `sizeof`, comma/ternary, loop, switch, goto, update, compound operator, conversion or arbitrary external call is accepted.
|
|
34
34
|
|
package/docs/language-support.md
CHANGED
|
@@ -84,7 +84,7 @@ The shared negative corpus covers parser recovery, missing types, truthiness, mi
|
|
|
84
84
|
|
|
85
85
|
## Task 005 general functions and calls
|
|
86
86
|
|
|
87
|
-
PHP, Ruby, JavaScript with JSDoc, TypeScript, Java, Dart, and Zig accept any number of required positional parameters, including zero. The module resolver collects every unique function signature before validating bodies, assigns declarations deterministic `function:N` identities, and binds each direct call to that identity plus its ordered parameter and return types. This permits forward calls, nested calls, direct recursion, and unambiguous mutual recursion without overload or dynamic dispatch. Arity and every positional scalar type must match exactly.
|
|
87
|
+
PHP, Ruby, JavaScript with JSDoc, TypeScript, Java, C, Dart, and Zig accept any number of required positional parameters, including zero. The module resolver collects every unique function signature before validating bodies, assigns declarations deterministic `function:N` identities, and binds each direct call to that identity plus its ordered parameter and return types. This permits forward calls, nested calls, direct recursion, and unambiguous mutual recursion without overload or dynamic dispatch. Arity and every positional scalar type must match exactly.
|
|
88
88
|
|
|
89
89
|
Function returns are `integer`, `boolean`, `string`, or `void`. A non-void function has an expression on every reachable return and returns on every reachable path. A void function may fall through or use a bare return, and its call is legal only as an `ExpressionStatement`. Void is never a value: it cannot type a parameter or local, initialize or assign a binding, become an argument or operand, be printed, or be returned as a value. Generation revalidates declaration identities and complete resolved signatures in externally supplied IR. Function names, parameters, return annotations, callees, arguments, and resolution references retain deterministic parser-backed provenance and mappings.
|
|
90
90
|
|
|
@@ -94,10 +94,11 @@ The source profiles remain deliberately exact:
|
|
|
94
94
|
- JavaScript accepts only declarations spelled `function name(...)` with a JSDoc annotation for every identifier parameter and an explicit scalar or `void` return annotation. TypeScript requires the corresponding explicit keyword annotations. Both reject optional/default/rest/destructured parameters, overloads, async/generators, arrows/function expressions, methods, callbacks and higher-order callees, optional/receiver/spread calls, and general TypeScript assertions. Task 012 adds only its exact generic declaration/application profile; Task 006 has one type-only exception for the exact independently validated `Map#get(...)!` round-trip form.
|
|
95
95
|
- PHP accepts simple typed by-value required parameters, an explicit scalar or `void` return, and unqualified direct calls. Defaults, variadics, named/unpacked/by-reference arguments, by-reference parameters or returns, methods, dynamic/qualified calls, implicit nullability, and missing types are rejected; Task 007 adds only its exact explicit nullable profile.
|
|
96
96
|
- Java accepts the established private-static semantic method profile with explicit supported parameter and scalar-or-void result types inside `public final class Main`, plus receiver-free direct calls and the exact entry method. Generated zero-parameter signatures named `getClass`, `hashCode`, `clone`, `toString`, `notify`, `notifyAll`, `wait`, or `finalize` conflict with inherited `java.lang.Object` instance methods and are rejected before emission; nonconflicting scalar-parameter overloads of those names remain available. Overload sets, instance/receiver/qualified/static-import ambiguity, varargs, bounded/defaulted/wildcard/raw generics, constructors, throws clauses, and non-static semantic methods are rejected.
|
|
97
|
+
- C accepts file-scope `static` semantic functions with any number of explicitly typed named parameters — spelled `void` for an empty list — exact scalar or `void` returns, and unqualified direct calls. A `static` prototype may precede a definition for forward or mutual recursion and must agree exactly with its unique definition; a prototype without a definition, a conflicting prototype, an implicit declaration, a bare return in a non-void function, or `void` used as a parameter, local, argument, operand or printed value is rejected. Generated zero-parameter signatures spell `(void)` and generated prototypes make forward calls and recursion compilable under `-Wstrict-prototypes`/`-Wmissing-prototypes`. See [C source and target](c.md).
|
|
97
98
|
- Dart accepts synchronous top-level functions with exact explicit `int`, `bool`, `String`, or `void` returns, explicitly typed required positional parameters, initialized typed locals, direct calls, and exact `void main()`/`print`. Inference, `var`, inferred `final`, optional/named/default arguments, methods/classes, generics, imports, dynamic/nullability, collections, exceptions, async, and isolates are rejected. Generated Dart alone may contain formatter-added trailing commas and the complete canonical private runtime prefix; neither grants those forms to ordinary source. See [Dart source and target](dart.md).
|
|
98
99
|
- Zig accepts top-level functions with exact explicit `i64`, `bool`, `[]const u8`, or `void` returns, explicitly typed required positional parameters, initialized typed `const`/`var` locals, direct calls, and the exact generated support/`pub fn main()` shell. Inference, optional/default/rest parameters, methods, containers, user slices/pointers/allocators, imports beyond the canonical standard-library support, error unions, `comptime`, generics, reflection, FFI, async, and concurrency are rejected. See [Zig source and target](zig.md).
|
|
99
100
|
|
|
100
|
-
Python, C#, C
|
|
101
|
+
Python, C#, C++, Rust, Swift, Kotlin/JVM, Go, and browser Wasm remain on their Task 025 Tasks 001–004 profile. Their registry feature flag is false, their existing exact-two/scalar-return behavior is unchanged, and a Task-005-only arity, void return, or void call statement is a located `UNSUPPORTED_CAPABILITY` before source, bytes, or a partial artifact set is exposed. This boundary does not broaden the future Task 013 aggregate acceptance.
|
|
101
102
|
|
|
102
103
|
## Task 006 immutable lists and maps
|
|
103
104
|
|
|
@@ -205,18 +206,18 @@ Each parser adapter recognizes only its canonical native module form: named rela
|
|
|
205
206
|
|
|
206
207
|
## Task 011 typed errors and handling
|
|
207
208
|
|
|
208
|
-
The original-five cohort implements one nominal unchecked-error model: `ErrorDeclaration`, identity-bound `ErrorType`, `ErrorConstruction` with exactly one semantic string message, abrupt `RaiseStatement`, and `TryStatement` with one exact `catchType`, one immutable block-local `CatchBinding`, and one `catchBody`. `ErrorMessageRead` is the only portable member operation.
|
|
209
|
+
The original-five cohort implements one nominal unchecked-error model: `ErrorDeclaration`, identity-bound `ErrorType`, `ErrorConstruction` with exactly one semantic string message, abrupt `RaiseStatement`, `RethrowStatement` re-propagating one in-scope catch binding through its exact `CatchBinding` node, and `TryStatement` with an ordered `CatchHandler[]` list plus an optional `finallyBody` completion block. Each handler carries one exact `catchType`, one immutable block-local `CatchBinding`, and one `catchBody`. `ErrorMessageRead` is the only portable member operation. Handlers are evaluated top to bottom, and a handler removes only its exact nominal identity from the protected block's escaping errors; unmatched errors and errors raised by a handler continue outward past every remaining sibling handler, and a handler whose exact identity cannot escape — including one shadowed by an earlier handler for the same identity — fails `UNREACHABLE_HANDLER`. Catch entry discards known-value and optional-presence facts for every outer mutable binding assignable anywhere in the protected body, because a raise may transfer control after any preceding assignment. `finallyBody` runs on every normal and abrupt completion; a completion block that itself completes abruptly replaces the pending completion and its raises, while a normally completing completion block passes the pending completion through unchanged. A `try` without handlers is accepted only with a completion block. Internal fixed-point analysis follows direct calls so a catch around a helper call is reachable without adding checked exception declarations or public effect lists. Raise, return, break, and continue remain abrupt for reachability and return-completeness analysis.
|
|
209
210
|
|
|
210
211
|
The accepted parser-backed forms are deliberately exact:
|
|
211
212
|
|
|
212
|
-
- Ruby uses an empty `class E < StandardError`, `raise E, message`, and `begin` with
|
|
213
|
-
- JavaScript/JSDoc and TypeScript use an empty `class E extends Error`, `throw new E(message)`, and
|
|
214
|
-
- PHP uses `final class E extends RuntimeException`, exact construction/throw,
|
|
215
|
-
- Java uses a final unchecked `RuntimeException` subclass with exactly one `String message` constructor forwarding to `super(message)`, exact construction/throw, and one
|
|
213
|
+
- Ruby uses an empty `class E < StandardError`, `raise E, message`, `raise <binding>` or bare `raise` inside one exact rescue, and `begin` with an ordered list of explicit `rescue E => error` clauses plus one optional `ensure` block. Default/broad rescue, rescue modifiers, retry, redo, else, and fatal/system classes are rejected.
|
|
214
|
+
- JavaScript/JSDoc and TypeScript use an empty `class E extends Error`, `throw new E(message)`, `throw <binding>`, and catches whose first statement is the exact `if (!(error instanceof E)) { throw error }` unmatched-value guard. Because the language has no sibling catch clauses, an ordered multi-handler list is written and emitted as a guarded dispatch chain: each non-final guard rethrows its own binding into a nested `try` whose single clause is the next handler, and each handler body runs outside its guard so a handler-body raise escapes past every remaining handler. An imported error alias resolves as a constructor only when no parameter, local, iteration, or catch binding shadows that spelling. Unguarded catches, alternate narrowing/assertions, arbitrary thrown values, `AggregateError`, causes, and any deviation from the exact chain shape are rejected. The guards and dispatch nesting are target syntax only and reparse back into one ordered semantic handler list, together with an optional native `finally` block.
|
|
215
|
+
- PHP uses `final class E extends RuntimeException`, exact construction/throw, `throw $<binding>`, an ordered list of `catch (E $error)` clauses, and one optional `finally` block. Broad `Throwable`/`Exception`/`Error` catches, previous exceptions, throw expressions in value contexts, and rethrow of a non-binding value are rejected. Generated namespaced projects qualify the native `\RuntimeException` base explicitly.
|
|
216
|
+
- Java uses a final unchecked `RuntimeException` subclass with exactly one `String message` constructor forwarding to `super(message)`, exact construction/throw, `throw <binding>`, an ordered list of typed catches, and one optional `finally` block. Checked exceptions, `throws`, union catches, resources, broad catches, and suppressed/cause behavior are rejected.
|
|
216
217
|
|
|
217
218
|
Error declaration/type/construction, raise, try/body, catch type/binding/body, and message-read nodes participate in deterministic source provenance, symbols, rich mappings, and Source Map v3. Task 010 programs preserve module-qualified `module#error:N` identities through imports, generation, and reparse. All backend graph, location, target-name, handler-shape, binding, payload, and effect validation completes before writer allocation. Kotlin, Python, C#, Go, C, C++, Rust, Swift, and browser Wasm advertise `typedErrors: false` and reject any Task 011 IR with located `UNSUPPORTED_CAPABILITY`; there is no C status-code, Rust `Result`, Wasm trap, or other substitute.
|
|
218
219
|
|
|
219
|
-
This feature does not model arbitrary error values,
|
|
220
|
+
This feature does not model arbitrary error values, union/broad/default handlers, checked effects or `throws` signatures (a permanent exclusion — see the [typed-error handling extension ADR](adr/20261026-typed-error-handling-extension.md)), error result types, stack traces, causes, suppression, fatal/process errors, asynchronous rejection, Ruby `retry`/`redo`/`else`/rescue modifiers, try-with-resources, general classes/methods, or standard-library portability. It adds no pairwise rewrite and does not expand Task 013's historical Tasks 001–008 cohort.
|
|
220
221
|
|
|
221
222
|
## Task 012 type parameters and generic declarations
|
|
222
223
|
|
|
@@ -353,7 +354,7 @@ All fifteen frontends preserve exact input content and parser-backed semantic to
|
|
|
353
354
|
|
|
354
355
|
`UNSUPPORTED_LANGUAGE` is exclusive to unknown or misspelled IDs. `UNSUPPORTED_ROLE` reports a known ID without the requested frontend, text/binary/application backend, artifact, or interoperability role, including C#, Go, C, Rust, Dart, and Zig through a legacy single-artifact API. `UNSUPPORTED_CAPABILITY` reports a role that exists but cannot represent the requested semantic module or target feature. `UNSUPPORTED_SYNTAX` reports parsed constructs outside the subset with the best parser-provided source location. Missing public annotations use `MISSING_TYPE`; a present unsupported type is `UNSUPPORTED_SYNTAX` at that annotation. Parser failures use `PARSE_ERROR`. Semantic resolution/type failures use `DUPLICATE_BINDING`, `UNRESOLVED_BINDING`, `USE_BEFORE_DECLARATION`, `IMMUTABLE_ASSIGNMENT`, and `TYPE_MISMATCH`; Task 005 additionally uses `VOID_AS_VALUE`, `VOID_RETURN_VALUE`, and `MISSING_RETURN_VALUE`. Task 033 adds `DUPLICATE_CLASS`, `DUPLICATE_PRIVATE_FIELD`, `DUPLICATE_CONSTRUCTOR`, `DUPLICATE_METHOD`, `INCOMPLETE_INITIALIZATION`, `ILLEGAL_PRIVATE_ACCESS`, `UNKNOWN_CLASS`, `INVALID_METHOD_RECEIVER`, `UNKNOWN_METHOD`, `CONSTRUCTOR_ARITY_MISMATCH`, and `METHOD_ARITY_MISMATCH`. Task 034 adds `MISSING_CAPABILITY_AUTHORITY`, `INVALID_CAPABILITY_AUTHORITY`, `UNDECLARED_EFFECT`, `UNDECLARED_FAILURE`, `INVALID_EFFECT_ORDER`, `RESOURCE_TYPE_MISMATCH`, `RESOURCE_MOVE_REQUIRED`, `INVALID_RESOURCE_TRANSFER`, `RESOURCE_ALIAS`, `INVALID_RESOURCE_BORROW`, `BORROW_ESCAPE`, `USE_AFTER_MOVE`, `USE_AFTER_CLOSE`, `RESOURCE_STATE_MISMATCH`, `LEAKED_RESOURCE`, and `FAILURE_ABSENCE_CONFLATION`; a repeated close in the bounded probe uses its declared terminal failure rather than `DOUBLE_CLOSE`. Task 035 adds `STDLIB_CONTRACT_INVALID`, `STDLIB_MODULE_UNKNOWN`, `STDLIB_OPERATION_UNDECLARED`, `STDLIB_VERSION_INCOMPATIBLE`, `STDLIB_VERSION_MALFORMED`, `STDLIB_PROVIDER_INVALID`, `STDLIB_PROVIDER_MISSING`, `STDLIB_PROVIDER_AMBIGUOUS`, `STDLIB_PROVIDER_DEPENDENCY_MISSING`, `STDLIB_PROVIDER_CYCLE`, `STDLIB_LINK_FAILURE`, and `STDLIB_NAME_COLLISION`. Task 036 adds `STDLIB_FACADE_INVALID`, `STDLIB_FACADE_IDENTITY_UNPROVED`, `STDLIB_FACADE_MEMBER_UNSUPPORTED`, `STDLIB_FACADE_VERSION_MALFORMED`, `STDLIB_FACADE_VERSION_INCOMPATIBLE`, `STDLIB_FACADE_DYNAMIC_REFERENCE`, `STDLIB_FACADE_REFLECTION_UNSUPPORTED`, `STDLIB_FACADE_NATIVE_EXTENSION_UNSUPPORTED`, `STDLIB_FACADE_SHADOWED`, `STDLIB_FACADE_REOPENED`, `STDLIB_FACADE_AUTHORITY_FORGED`, `STDLIB_FACADE_NAME_COLLISION`, `STDLIB_FACADE_DECLARATION_MISMATCH`, and `STDLIB_FACADE_DESCRIPTOR_INVALID`; Task 037 additionally uses `STDLIB_PROTECTED_BINDING_ACCESS` for portable attempts to reach canonical operations directly. Resolution failures retain parser locations, while descriptor/link failures are transactional. Flow failures use `NON_BOOLEAN_CONDITION`, `MISSING_RETURN`, `UNREACHABLE_STATEMENT`, `ILLEGAL_RETURN_CONTEXT`, `ILLEGAL_BREAK_CONTEXT`, `ILLEGAL_CONTINUE_CONTEXT`, `INVALID_LOOP_TARGET`, `CROSS_FUNCTION_LOOP_TARGET`, and `UNSUPPORTED_STATEMENT`. Backends validate complete recursive block shape, flow, scalar payloads, binding/type agreement, call resolutions, mutability, loop context, identifiers, target bounds, and fixed artifact contracts before emission. Missing or structurally invalid caller IR reports located `UNSUPPORTED_CAPABILITY` rather than leaking a native error or producing partial source. A non-`Main.java` Java basename, non-`Program.cs` C# filename, non-`main.go` Go filename, non-`program.c` C filename, non-`src/main.rs` Rust filename, non-`bin/program.dart` Dart filename, or non-`src/main.zig` Zig filename uses that same contract. Artifact-set generation requires a non-empty string language/target ID and accepts only the explicit `text`, `binary`, and `application` backend-role strings; malformed IDs or roles report `INVALID_ARTIFACT_SET` without coercing caller input, while an unknown string ID reports `UNSUPPORTED_LANGUAGE` and a valid role absent from a known registry record reports `UNSUPPORTED_ROLE`. Optional registry roles and backend metadata may be omitted with `undefined`; present malformed values, including `null`, report `INVALID_REGISTRY`. Invalid registry, artifact-set, byte-map, discovery, and runner inputs have their own normalized diagnostics rather than leaking native type or subprocess errors.
|
|
355
356
|
|
|
356
|
-
Boxed or coercive types, inferred public types, TypeScript literal/enum/`any`/`unknown`/arbitrary-union types, PHP aliases or implicit nullable declarations, Ruby `boolish`, Java `Boolean`/`char`, universal null values, floating point, decimal, bigint, symbols, regex values, and byte strings are unsupported. Task 048 admits exactly two expression families: strict-Boolean conditional (ternary) expressions in the original-five cohort, and parser-proved string interpolation in PHP, Ruby, JavaScript, and TypeScript lowered to checked `StringConcat` concatenation of pre-rendered parts with `string`-typed segments only. Division, remainder, exponentiation, bitwise/shift/update/compound operations, Ruby/PHP `and`/`or`, loose equality, nullish forms, optional/safe navigation, ranges, general casts/assertions, collection/object equality, operator overloading, implicit coercion, and constant folding are also excluded; the sole assertion exception is Task 006's exact TypeScript `Map#get(...)!` wrapper, whose lookup must be independently total. Task 007 supplies only the exact explicit optional model, Task 008 only exact ordered list loops, Task 010 only explicit supplied source projects, Task 011 only the exact nominal unchecked-error model above, Task 012 only the exact unbounded invariant generic profile above, Task 014 only the exact ordered-map pair loop above, Task 032 only the exact strict-Boolean precondition loop above, Task 033 only the exact closed reference-class profile above, Task 034 only the explicit single-module probe effect/resource profile above, and Task 048 only the exact cohort ternary and interpolation lowering above. There is no truthiness, implicit-return or general statement-value model, async/generator model, open/inherited/virtual class model, implicit import discovery, post-condition/basic/range loops, arbitrary or ordinary-map iteration, switches, exception behavior beyond Task 011 and declared Task 034 failures, overloads, advanced generic/effect type systems, implicit resource cleanup, unsafe native handles, package-manager resolution, or source-format preservation. The sole value-free expression statements are resolved void function and method calls in the original-five cohort. Ruby type comments remain a bounded RBS-style convention rather than a full signature parser, and single-module Java generation retains the fixed `Main.java` contract.
|
|
357
|
+
Boxed or coercive types, inferred public types, TypeScript literal/enum/`any`/`unknown`/arbitrary-union types, PHP aliases or implicit nullable declarations, Ruby `boolish`, Java `Boolean`/`char`, universal null values, floating point, decimal, bigint, symbols, regex values, and byte strings are unsupported. The portable floating-point/decimal numeric tower is decided in the [numeric tower ADR](adr-numeric-tower.md) and remains unimplemented; nothing in that ADR changes the integer semantics documented above, and `bigint` stays a separate follow-up decision. Task 048 admits exactly two expression families: strict-Boolean conditional (ternary) expressions in the original-five cohort, and parser-proved string interpolation in PHP, Ruby, JavaScript, and TypeScript lowered to checked `StringConcat` concatenation of pre-rendered parts with `string`-typed segments only. Division, remainder, exponentiation, bitwise/shift/update/compound operations, Ruby/PHP `and`/`or`, loose equality, nullish forms, optional/safe navigation, ranges, general casts/assertions, collection/object equality, operator overloading, implicit coercion, and constant folding are also excluded; the sole assertion exception is Task 006's exact TypeScript `Map#get(...)!` wrapper, whose lookup must be independently total. Task 007 supplies only the exact explicit optional model, Task 008 only exact ordered list loops, Task 010 only explicit supplied source projects, Task 011 only the exact nominal unchecked-error model above, Task 012 only the exact unbounded invariant generic profile above, Task 014 only the exact ordered-map pair loop above, Task 032 only the exact strict-Boolean precondition loop above, Task 033 only the exact closed reference-class profile above, Task 034 only the explicit single-module probe effect/resource profile above, and Task 048 only the exact cohort ternary and interpolation lowering above. There is no truthiness, implicit-return or general statement-value model, async/generator model, open/inherited/virtual class model, implicit import discovery, post-condition/basic/range loops, arbitrary or ordinary-map iteration, switches, exception behavior beyond Task 011 and declared Task 034 failures, overloads, advanced generic/effect type systems, implicit resource cleanup, unsafe native handles, package-manager resolution, or source-format preservation. The sole value-free expression statements are resolved void function and method calls in the original-five cohort. Ruby type comments remain a bounded RBS-style convention rather than a full signature parser, and single-module Java generation retains the fixed `Main.java` contract.
|
|
357
358
|
|
|
358
359
|
CPP uses its own [C++20 scalar and scaffold profile](cpp.md), official CPP grammar, one `program.cpp`, by-value owned strings and checked arithmetic. The shared native occurrence planner is backend-only; CPP owns its exact marker and CST recognizer. Native O0/O2 and ASan/UBSan/leak execution are mandatory.
|
|
359
360
|
|
package/docs/testing.md
CHANGED
|
@@ -36,7 +36,7 @@ These tests generate and publish source artifacts only. They do not run `javac`,
|
|
|
36
36
|
|
|
37
37
|
## Task 005 general function signatures and calls
|
|
38
38
|
|
|
39
|
-
Task 005 stays bounded to PHP, Ruby, JavaScript with JSDoc, TypeScript, and
|
|
39
|
+
Task 005 stays bounded to PHP, Ruby, JavaScript with JSDoc, TypeScript, Java, Dart, Zig, and C. `general-function-signatures-and-calls.spec.js` proves equivalent zero-, one-, and three-parameter signatures, complete signature collection, nested calls, terminating recursion, scalar/void returns, and the stable semantic failures. `general-function-frontend-validation.spec.js` owns the excluded parameter, call, method, receiver, dispatch, annotation, overload, generic, and higher-order source forms. `general-function-backend-validation.spec.js` proves generation/reparse, exact external-IR signature revalidation, target conflicts, the public feature flag, and transactional rejection from every deferred Task 025/Wasm target. `general-function-provenance-and-mapping.spec.js` proves deterministic declaration/resolution identities and exact signature/call mappings. `general-function-runtime-execution.spec.js` invokes real PHP, Ruby, Node, local `tsc` followed by Node, `javac`, and `java`, requiring exact `ready\n6\n` output; a missing command fails. `c-task005-acceptance.spec.js` proves the C cohort extension: generated-C reparse with identical meaning, the C fixture through every original-five text target, and real Clang execution of each original-five Task-005 fixture and the C fixture at `-O0`/`-O2` with and without sanitizers, including prototype-mediated mutual recursion and bare void returns.
|
|
40
40
|
|
|
41
41
|
Run the Task 005 files individually and sequentially:
|
|
42
42
|
|
|
@@ -46,13 +46,14 @@ npx velocious-test spec/general-function-frontend-validation.spec.js
|
|
|
46
46
|
npx velocious-test spec/general-function-backend-validation.spec.js
|
|
47
47
|
npx velocious-test spec/general-function-provenance-and-mapping.spec.js
|
|
48
48
|
npx velocious-test spec/general-function-runtime-execution.spec.js
|
|
49
|
+
npx velocious-test spec/c-task005-acceptance.spec.js
|
|
49
50
|
```
|
|
50
51
|
|
|
51
|
-
Task 013 is the separate terminal original-five aggregate gate. Task 005 does not expand that matrix or adopt Python, C#, C
|
|
52
|
+
Task 013 is the separate terminal original-five aggregate gate. Task 005 does not expand that matrix or adopt Python, C#, C++, Rust, Swift, Kotlin/JVM, Go, or browser Wasm beyond focused capability rejection.
|
|
52
53
|
|
|
53
54
|
## Task 011 typed errors and handling
|
|
54
55
|
|
|
55
|
-
Task 011 uses
|
|
56
|
+
Task 011 uses eight focused specs. Semantic validation covers exact nominal types, string-only construction, abrupt raise, exact handler consumption, unmatched and handler-originated propagation, immutable scoped catch bindings, catch-entry invalidation of optional-presence facts after protected-body assignments, message-only access, unreachable handlers, and fixed-point direct-call effects without checked signatures. Frontend coverage parses the canonical Ruby, JavaScript/JSDoc, TypeScript, PHP, and Java forms through their real parser nodes and rejects arbitrary throws, broad or unguarded catches, JavaScript-family imported error aliases shadowed by parameter/local/iteration/catch bindings, union or default handlers, checked exceptions, Ruby `retry`/`redo`/`else`/rescue modifiers, resources, and any deviation from the exact JavaScript/TypeScript guarded dispatch chain. Backend coverage reparses all five generated targets, checks target guard spelling, rejects PHP assigned-only special names in catch bindings, validates caller-authored shapes before allocation, and requires deterministic `UNSUPPORTED_CAPABILITY` from every other registered target. Registry and provenance specs lock the public `typedErrors` flag, error/catch symbols, source roles, and rich/v3 mapping behavior. The handling-extension spec locks the ordered multi-catch, `finally`/`ensure`, and rethrow extension across the cohort: one semantic meaning from each language's exact native form, generation and reparse for all five targets, handler-body raises escaping past remaining sibling handlers, completion-block interaction with return/raise and nested try, rethrow of a lexically visible outer catch binding, `UNREACHABLE_HANDLER` for a handler shadowed by an earlier handler of the same identity, and multi-file program preservation.
|
|
56
57
|
|
|
57
58
|
The multi-file runtime spec parses five caller-supplied TypeScript modules, preserves both exported `module#error:0` identities plus a transitive two-hop function effect through every generated project and reparse, and executes PHP, Ruby, Node, strict TypeScript through `tsc` plus Node, and Java through `javac` plus `java`. Its exact `ok\nbad\ndeep\n` output proves the non-throw path, one exact catch, and an unmatched error raised by the inner handler propagating to the outer exact handler. Missing commands fail; no test skips, downloads, substitutes, or scans source text.
|
|
58
59
|
|
|
@@ -64,10 +65,12 @@ npx velocious-test spec/typed-error-frontends.spec.js
|
|
|
64
65
|
npx velocious-test spec/typed-error-backends.spec.js
|
|
65
66
|
npx velocious-test spec/typed-error-language-registry.spec.js
|
|
66
67
|
npx velocious-test spec/typed-error-provenance-and-mapping.spec.js
|
|
68
|
+
npx velocious-test spec/typed-error-handling-extension.spec.js
|
|
67
69
|
LANG=C.UTF-8 LC_ALL=C.UTF-8 npx velocious-test spec/typed-error-multifile-runtime.spec.js
|
|
70
|
+
LANG=C.UTF-8 LC_ALL=C.UTF-8 npx velocious-test spec/typed-error-handling-extension-runtime.spec.js
|
|
68
71
|
```
|
|
69
72
|
|
|
70
|
-
Task 011 adds no checked exceptions/effects, arbitrary thrown values,
|
|
73
|
+
Task 011's handling extension adds ordered multi-catch, `finally`/`ensure`, and explicit rethrow for the original five and still adds no checked exceptions/effects or `throws` signatures, arbitrary thrown values, union/broad/default catches, Ruby `retry`/`redo`/`else`/rescue modifiers, try-with-resources, async rejection, result types, non-original-five lowering, standard-library portability, pairwise rewrites, publication, or deployment behavior. Task 013 remains the historical Tasks 001–008 compatibility gate.
|
|
71
74
|
|
|
72
75
|
## Task 012 type parameters and generic declarations
|
|
73
76
|
|
|
@@ -378,7 +381,7 @@ Task 016 extends the same fail-loud discovery and staged runner to configured Py
|
|
|
378
381
|
|
|
379
382
|
Task 017 adds the distinct ordered `restore` stage and configured .NET SDK 10 discovery. C# acceptance generates exactly `Program.cs` and `Semantifold.csproj`, then invokes `dotnet restore Semantifold.csproj --source . --no-cache --force --disable-parallel --nologo`, `dotnet build Semantifold.csproj --configuration Release --no-restore --nologo --warnaserror`, and `dotnet exec bin/Release/net10.0/Semantifold.dll` as separate stages. Tests set the .NET telemetry/first-run/no-logo/invariant variables and an isolated absolute `NUGET_PACKAGES` path. They require exact exit/stdout and no warning output. Two fresh directories must produce identical ordered artifact text/mappings/provenance and identical `Semantifold.dll`, `.deps.json`, and `.runtimeconfig.json`; path-bearing restore bookkeeping under `obj/` is intentionally not compared. Missing .NET, restore dependency/network demand, warnings, SDK major mismatch, or nondeterministic compiler output fails without a skip.
|
|
380
383
|
|
|
381
|
-
Task 024 adds configured Go 1.26.x discovery restricted to Linux/amd64; the patch release is read from the discovered toolchain rather than fixed by runtime tests. Acceptance obtains `GOROOT` from that exact `go`, requires the companion `gofmt` beside the configured or discovered executable to resolve to the same `GOROOT/bin/gofmt`, and checks `gofmt -d` before native execution. Each dependency-free generated module is run with `GOTOOLCHAIN=local`, `GOPROXY=off`, `GOSUMDB=off`, `GOVCS=off`, `CGO_ENABLED=0`, `GOENV=off`, `GOWORK=off`, `GOOS=linux`, `GOARCH=amd64`, and isolated HOME/GOPATH/
|
|
384
|
+
Task 024 adds configured Go 1.26.x discovery restricted to Linux/amd64; the patch release is read from the discovered toolchain rather than fixed by runtime tests. Acceptance obtains `GOROOT` from that exact `go`, requires the companion `gofmt` beside the configured or discovered executable to resolve to the same `GOROOT/bin/gofmt`, and checks `gofmt -d` before native execution. Each dependency-free generated module is run with `GOTOOLCHAIN=local`, `GOPROXY=off`, `GOSUMDB=off`, `GOVCS=off`, `CGO_ENABLED=0`, `GOENV=off`, `GOWORK=off`, `GOOS=linux`, `GOARCH=amd64`, and isolated HOME/GOPATH/module caches over one content-addressed run-scoped Go build cache, which is removed when the test process exits. The short-lived discovery fixture also writes exact `off` mode into its isolated Go telemetry directory before invoking the tool, then asserts that the mode file is the only telemetry state: Go 1.26 otherwise starts an unawaited uploader sidecar even in default `local` mode, which can recreate `upload/` while the fixture root is being removed. Cleanup errors remain fatal. Stages run offline `go build`, mandatory `go vet`, and `go run`, while compiler-only differential cases use the matching `go tool compile`. Tests require exact status/stdout, unchanged `go.mod`/`main.go`, no `go.sum`/`go.work`/vendor tree, and byte-identical binaries and output in two fresh directories. Missing, wrong-minor, wrong-architecture, mismatched-gofmt, download, cgo, workspace, vet, formatting, nondeterminism, telemetry leakage, or cleanup evidence fails without a skip.
|
|
382
385
|
|
|
383
386
|
Task 023 adds exact Kotlin/JVM 2.4.20 discovery on pinned OpenJDK 25 Ubuntu runtimes. Ubuntu26.04 development uses exact `25.0.4.1+1-1~26.04.4`; Ubuntu24.04 TensorBuzz uses exact `25.0.4.1+1-1~24.04.4`. Bootstrap and discovery accept only those two qualified compiler-reported package identities, never arbitrary output. Original and generated `Program.kt` files compile with `-language-version 2.4 -api-version 2.4 -jvm-target 25 -Werror -include-runtime` into runnable `Program.jar` files, then execute through the exact `java25` discovery contract. Every process uses a fresh directory and exact status/stderr/UTF-8 stdout assertions; no test downloads a compiler or dependency.
|
|
384
387
|
|
package/package.json
CHANGED
package/src/backends/c.js
CHANGED
|
@@ -16,7 +16,7 @@ import {SourceWriter} from "./writer.js"
|
|
|
16
16
|
/** @typedef {import("../semantic/types.js").Block} Block */
|
|
17
17
|
/** @typedef {import("./ordered-expressions.js").PlannedValue} PlannedValue */
|
|
18
18
|
/** @typedef {import("./ordered-expressions.js").PlannedStep} PlannedStep */
|
|
19
|
-
/** @typedef {{id: string, steps: PlannedStep[], value
|
|
19
|
+
/** @typedef {{id: string, steps: PlannedStep[], value?: PlannedValue, terminal?: PlannedStep}} StatementPlan */
|
|
20
20
|
|
|
21
21
|
const binaryTokens = new Map([
|
|
22
22
|
["IntegerAdd", "semantifold_integer_add"], ["IntegerSubtract", "semantifold_integer_subtract"],
|
|
@@ -125,7 +125,7 @@ class CEmitter {
|
|
|
125
125
|
module.functions.forEach((declaration, index) => {
|
|
126
126
|
const parameters = declaration.parameters.map((parameter) => `${emitScalarType("c", parameter.type)} ${parameter.name}`).join(", ")
|
|
127
127
|
|
|
128
|
-
this.synthetic(`static ${emitScalarType("c", declaration.returnType)} ${declaration.name}(${parameters});\n`, declaration, `/functions/${index}`)
|
|
128
|
+
this.synthetic(`static ${emitScalarType("c", declaration.returnType)} ${declaration.name}(${declaration.parameters.length ? parameters : "void"});\n`, declaration, `/functions/${index}`)
|
|
129
129
|
})
|
|
130
130
|
module.functions.forEach((declaration, index) => {
|
|
131
131
|
const path = `/functions/${index}`
|
|
@@ -135,6 +135,7 @@ class CEmitter {
|
|
|
135
135
|
this.synthetic(" ", declaration, path)
|
|
136
136
|
this.mapped(declaration.name, declaration, path, "name")
|
|
137
137
|
this.synthetic("(", declaration, path)
|
|
138
|
+
if (!declaration.parameters.length) this.synthetic("void", declaration, path)
|
|
138
139
|
declaration.parameters.forEach((parameter, parameterIndex) => {
|
|
139
140
|
const parameterPath = `${path}/parameters/${parameterIndex}`
|
|
140
141
|
|
|
@@ -181,7 +182,25 @@ class CEmitter {
|
|
|
181
182
|
|
|
182
183
|
this.synthetic(`${indent}/* ${marker} begin ${plan.id} ${signature} */\n`, statement, path)
|
|
183
184
|
this.steps(plan.steps, indent)
|
|
185
|
+
if (statement.kind == "ReturnStatement" && !statement.expression) {
|
|
186
|
+
this.synthetic(indent, statement, path)
|
|
187
|
+
this.mapped("return", statement, path)
|
|
188
|
+
this.synthetic(";\n", statement, path)
|
|
189
|
+
this.synthetic(`${indent}/* ${marker} end ${plan.id} ${signature} */\n`, statement, path)
|
|
190
|
+
return
|
|
191
|
+
}
|
|
192
|
+
if (statement.kind == "ExpressionStatement") {
|
|
193
|
+
if (!plan.terminal) throw new Error("Missing validated C terminal call.")
|
|
194
|
+
this.synthetic(indent, statement, path)
|
|
195
|
+
this.operation(plan.terminal)
|
|
196
|
+
this.synthetic(";\n", statement, path)
|
|
197
|
+
this.synthetic(`${indent}/* ${marker} end ${plan.id} ${signature} */\n`, statement, path)
|
|
198
|
+
return
|
|
199
|
+
}
|
|
184
200
|
this.synthetic(indent, statement, path)
|
|
201
|
+
const value = plan.value
|
|
202
|
+
|
|
203
|
+
if (!value) throw new Error("Missing validated C statement value plan.")
|
|
185
204
|
if (statement.kind == "LocalDeclaration") {
|
|
186
205
|
if (!statement.mutable) this.synthetic("const ", statement, path)
|
|
187
206
|
this.mapped(emitScalarType("c", statement.type), statement.type, `${path}/type`, "type")
|
|
@@ -196,9 +215,9 @@ class CEmitter {
|
|
|
196
215
|
this.mapped("=", statement, path, "operator")
|
|
197
216
|
this.synthetic(" ", statement, path)
|
|
198
217
|
} else if (statement.kind == "ReturnStatement") this.mapped("return ", statement, path)
|
|
199
|
-
else if (statement.kind == "PrintStatement") this.mapped(`semantifold_print_${
|
|
218
|
+
else if (statement.kind == "PrintStatement") this.mapped(`semantifold_print_${value.type}(`, statement, path)
|
|
200
219
|
else this.mapped("if (", statement, path)
|
|
201
|
-
this.value(
|
|
220
|
+
this.value(value)
|
|
202
221
|
if (statement.kind == "IfStatement") {
|
|
203
222
|
this.synthetic(") {\n", statement, path)
|
|
204
223
|
this.block(statement.consequent, `${path}/consequent`, `${indent} `)
|
package/src/backends/cpp.js
CHANGED
|
@@ -153,6 +153,7 @@ class CppEmitter {
|
|
|
153
153
|
const plan = this.plans.get(path)
|
|
154
154
|
|
|
155
155
|
if (!plan) throw new Error("Missing validated C++ statement plan.")
|
|
156
|
+
if (!plan.value) throw new Error("Missing validated C++ statement value plan.")
|
|
156
157
|
const marker = `semantifold:ordered-expression:cpp:v1`
|
|
157
158
|
const signature = statementSignature(statement)
|
|
158
159
|
|
package/src/backends/effects.js
CHANGED
|
@@ -478,7 +478,8 @@ function blockExcluded(block) {
|
|
|
478
478
|
}
|
|
479
479
|
const nested = statement.kind == "IfStatement" ? [statement.consequent, ...(statement.alternate ? [statement.alternate] : [])] :
|
|
480
480
|
statement.kind == "ForEachStatement" || statement.kind == "ForEachMapStatement" || statement.kind == "WhileStatement" ? [statement.body] :
|
|
481
|
-
statement.kind == "TryStatement" ? [statement.body, statement.
|
|
481
|
+
statement.kind == "TryStatement" ? [statement.body, ...statement.handlers.map((handler) => handler.catchBody),
|
|
482
|
+
...(statement.finallyBody ? [statement.finallyBody] : [])] : []
|
|
482
483
|
for (const child of nested) {
|
|
483
484
|
const found = blockExcluded(child)
|
|
484
485
|
if (found) return found
|