ADR-0011: Surface-to-Core T2 Boundary
Status: accepted
Date: 2026-07-25
Context
Core-N0 separates I64 and F64, but the current Surface AST stores every
numeric literal as Number(f64) and the bridge interpreter dynamically chooses
SmallInt or Float. Function parameters and returns also have no parser-
produced scalar signatures, while the bridge typechecker admits Any.
Inferring I64 or F64 from incomplete Surface information would make
elaboration heuristic and could disagree with current behavior. Treating
Any as a Core type would violate the canonical typed-kernel boundary.
Options considered
| Option | Benefit | Cost |
|---|---|---|
Reuse bridge Num/Any directly | Minimal implementation | No sound Core type or numeric contract |
| Guess types from use sites | Broad early coverage | Order-sensitive inference and silent semantic drift |
| Explicit typed input manifest plus conservative subset | Deterministic and fail closed | Smaller initial Surface coverage |
Decision
Split Roadmap T2 into bounded admission profiles.
T2A elaborates a pure Surface kernel with:
- an explicit ordered input manifest using
Bool,I64, orF64; AssignandIfstatements;Bool, numeric, and variable operands;F64 + F64andF64 - F64;- one declared result variable;
- deterministic ANF local numbering and explicit elaboration budgets.
Surface numeric literals are classified exactly at the current bridge boundary:
finite value with abs(fract(value)) < F64_EPSILON → I64(value)
otherwise → F64(value)
The I64 conversion is truncating and saturating at the signed 64-bit bounds,
matching the current bridge behavior. T2A admits an I64 value but rejects
integer arithmetic because Surface has no accepted overflow mode. Mixed
I64/F64 arithmetic, equality, relational comparison, unary operations,
calls, loops, collections, actions, effects, and mutation through references
fail closed.
The input manifest is authoritative for otherwise free input variables. Input names are elaboration bindings, not Core semantics; manifest order determines Core parameter order. Binding is exact in arity, order, and scalar tag; T2A performs no numeric coercion.
An If is lowered in continuation-passing form. Each branch receives its own
immutable environment and the remaining Surface continuation is elaborated
inside both branches. This preserves Surface reassignment behavior without
introducing phi nodes into Core-N0. Source-step and Core-node budgets bound code
duplication. Both syntactic branches are validated even when the condition is a
literal. Separate branch environments prevent bindings from leaking, while a
single global allocator makes all Core local IDs unique and deterministic.
T2A canonical output is one pure P1V0 function with ID 0, manifest-ordered
parameters, ANF let nodes for operations and assignments, and no constant
folding. Hard caps of 256 inputs, 256 visited source nodes, and 256 emitted Core
nodes protect the recursive elaborator independently of caller budgets.
Differential comparison observes Bool and I64 exactly. It observes F64
by bits, preserving signed zero and canonicalizing every NaN payload to
0x7ff8000000000000. The initial evidence compares both evaluators within the
same host floating-point environment; cross-target rounding-mode and FTZ/DAZ
reproducibility are not yet claimed.
T2B may add direct functions and calls only after Surface scalar parameter,
return, and numeric-mode contracts are explicit enough to avoid Any.
Rationale
- No dynamic
Anyleaks into Core-N0. - Numeric representation is selected by a declared rule, not a compiler guess.
- The first parity corpus can exercise dynamic control flow with typed inputs.
- Continuation lowering preserves Core’s semantic/machine separation.
- The narrow profile can grow monotonically through new accepted contracts.
Trade-offs
- Common integer programs and all Surface functions initially fail elaboration.
- Continuation duplication can grow exponentially for nested branches.
- A separate typed manifest is required until Surface syntax carries the same information.
- The bridge’s hybrid
Numbehavior remains debt rather than becoming canonical Core behavior.
Consequences
Positive: Surface-to-Core parity begins on a sound, deterministic boundary.
Negative: T2A is deliberately not a general Surface compiler.
Mitigation: diagnostics name the unsupported construct, structural path, source location when available, and profile. Budgets fail before sealing an oversized artifact. Differential tests compare bridge and Core outcomes for every admitted construct.
Revisit trigger
Replace the manifest or widen numeric operations when Surface syntax and checking provide explicit scalar types and integer modes. Replace continuation duplication when measured T2 workloads repeatedly exhaust its budget; any replacement must remain semantic and must not import SSA phi nodes into Core-N0.