Typed Core NAUX v0.1
Status: accepted architecture; bounded Stage 1 validated; Stage 2 R0-D validated; Stage 3 definitional CoreVM0 validated
Schema name: core-n0
Schema version: 0.1.0
Implementation status:
- implemented: std-only Rust seed schema for the
P1V0,P1V1,P1V2,P1V3,P1V4, andP1V5profiles; - implemented: deterministic semantic encoding, SHA-256 hash, fail-closed verifier, and verified-artifact interpreter entry;
- implemented: scalar/tuple/sum values, direct calls, tail calls,
let,if, exhaustivecase, explicit numeric modes, and read-onlyArray<F64>; - implemented: deterministic evaluation budgets and typed overflow/bounds outcomes;
- implemented: T2A deterministic elaboration for the admitted pure scalar
Assign/IfSurface subset, exact typed input binding, independent Core verification, fixed semantic-hash evidence, and bridge/Core differential parity; - implemented: T2B annotated closed direct functions, forward/recursive calls, proper tail calls, exact scalar signatures, global elaboration budgets, stable-hash evidence, and bridge/Core differential parity;
- implemented: P1V1 invocation-local logical store, lexical region scope,
non-escaping shared scalar references, exact
State<rho>/Alloc<rho>checking, stable-hash evidence, and freshness/alias/order rejection corpus; - implemented: P1V2 bounded existential closures with ordered tuple environments, exact construction/call/effect verification, live reference capture, alias-visible shared-store calls, bounded recursion, no escape, and stable-hash evidence;
- implemented: P1V3 typed user operations and linear lexical handlers with explicit captures, affine implicit resumption, dynamic call/closure effect subtraction, forwarding, unhandled outcomes, and stable-hash evidence;
- implemented: P1V4 direct affine
Uniquescalar ownership with repeated load/store borrows, single move throughUse/direct call/tail call, branch-local dataflow, containment/escape rejection, and stable-hash evidence; - implemented: P1V5 one-in/one-out anchored ownership return with return-as-move, caller rebinding, branch/tail return, same-region replacement, closed-region rejection, and stable-hash evidence;
- pending: escaping/general store semantics, general borrowing, aggregate ownership, escaping/general closures, first-class/multi-shot handlers, region polymorphism, and full effect rows.
The encoding contract is
core_n0_semantic_encoding_v1.md, and the
admitted Surface boundary is
surface_core_t2_contract.md, with the direct
function extension in
surface_core_t2b_contract.md. The bounded
logical-store profile is fixed by
core_store_p1v1_contract.md, and bounded
existential closures are fixed by
core_closure_p1v2_contract.md, and linear
handlers are fixed by
core_handler_p1v3_contract.md, and bounded
affine ownership is fixed by
core_unique_p1v4_contract.md, and bounded
ownership return is fixed by
core_ownership_return_p1v5_contract.md.
The bounded Stage 1 exit is accepted in
stage1_exit_audit.md. The CoreVM0 seed and
definitional artifact are fixed by
corevm0_nucleus_contract.md and
corevm0_core_artifact_contract.md.
The active next boundary is the specializer extension required by
p1_lighthouse_contract.md.
1. Purpose
Core-N0 is the canonical semantic language used by:
- the definitional interpreter;
- binding-time analysis;
- partial evaluation and residualization;
- translation validation;
- formal models and independent checkers.
It is intentionally smaller than Surface NAUX and higher-level than SSA. Surface syntax may evolve without changing Core semantics. Machine concerns may evolve without entering the semantic kernel.
2. Design invariants
- Call-by-value.
- Left-to-right evaluation.
- Typed before evaluation or residualization.
- A-normal form: non-trivial intermediate computations are named.
- Local bindings are immutable; mutation occurs only through explicit store operations.
- Effects and logical lifetime regions are explicit.
- No implicit fallthrough, coercion, destructor, finalizer, or physical address.
- No phi nodes, dominance, registers, stack slots, ABI, or instruction encoding.
- Every serialized program has a deterministic semantic hash.
3. Types
τ ::=
Unit
| Bool
| I64
| F64
| Text
| Bytes
| Tuple<τ...>
| Sum<Name { Constructor(τ...)... }>
| Array<ρ, μ, τ>
| Ref<ρ, μ, τ>
| Function<(τ...), ε, τ>
| Closure<(τ...), ε, τ>
μ ::= Read | Unique | Shared
Text and Bytes are semantic primitives with versioned operations. Higher
collections such as maps, graphs, sets, and priority queues are library or
opaque ADT layers, not new kernel mechanisms.
Any, host pointers, Rc, Rust enums, and host collection types do not appear
in Core-N0.
4. Functions and closures
A direct function has a stable FunctionId, typed parameters, an effect row,
and a result type.
The behavioral closure type is:
Closure<(Args...), Effects, Result>
After closure conversion its internal representation is:
∃Env. (
FunctionId<(Env, Args...), Effects, Result>,
Env
)
The verifier must prove that the packed environment type matches the selected function. The existential representation is internal; it is not required in Surface NAUX syntax.
P1V0 and P1V1 do not admit closure creation or closure calls. P1V2
admits PackClosure and CallClosure with a canonical ordered tuple
environment. Closure values remain local to one function invocation; neither
closure nor reference values may cross results, and closures may not cross
parameters. A captured reference must be active, and its call borrows the
creator’s logical store.
5. Terms
Illustrative canonical form:
program ::= declarations entry
function ::= fn id type_params region_params (params) ! effects -> type = term
term ::=
let x: τ = atom in term
| let x: τ = primitive(args) in term
| let x: τ = call function(args) in term
| let x: τ = call_closure closure(args) in term
| if value then term else term
| case value of Constructor(bindings) => term ...
| perform Effect.operation(args) as x in term
| handle term with handler
| tailcall function(args)
| return value
atom ::= literal | local | tuple | constructor | function_ref | closure_pack
Surface loops elaborate to tail recursion in Core-N0. A later lowering may introduce CFG blocks and block parameters before SSA construction.
6. Evaluation
The Core interpreter evaluates:
⟨term, environment, logical_store, handlers⟩
⇓
⟨result, logical_store', effect_trace⟩
Evaluation order is left-to-right. A result is either:
Return(value)
Error(error_value)
Host panic, host allocation failure, or host undefined behavior is never a valid Core result.
7. Numeric semantics
I64
Core has no ambiguous integer arithmetic opcode. Operations name their mode:
i64.add.checked
i64.add.wrap
i64.add.saturating
i64.sub.checked
i64.sub.wrap
i64.mul.checked
i64.mul.wrap
Checked operations return a typed error/result rather than causing undefined behavior. Surface elaboration chooses a mode explicitly. A proof-backed pass may replace checked arithmetic with a cheaper operation only when overflow is impossible for the exact program point.
F64
- IEEE-754 binary64.
- Left-to-right evaluation.
- No reassociation, contraction, reciprocal approximation, or fast-math by default.
- NaN is unordered and unequal, including to itself.
+0.0and-0.0compare equal but retain their IEEE behavior in operations.- NaN payload and physical bit pattern are not observable in Core-N0 v0.1.
- Relaxed numeric modes require a future explicit contract and distinct benchmark baselines.
8. Effects
An effect row is a closed or extensible set:
ε ::= Pure | { EffectName<parameters>... | effect_variable }
Initial semantic effects:
State<ρ>
Alloc<ρ>
Error<E>
IO
FFI<capability>
UnsafeMemory<capability>
A known handler may be specialized. An unknown handler or dynamic effect operation is residualized.
P1V3 implements the first bounded handler subset. Operation IDs carry exact
inline signatures. Handler environments are explicit, clauses execute outside
their selected boundary for forwarding, and a successful clause result resumes
the structural continuation exactly once. Typed error or unhandled propagation
resumes it zero times. The continuation is not yet a first-class Core value.
Static evaluation is restricted to:
- pure computation;
- specializer-owned internal allocation;
- immutable static program reads;
- bounded static control flow.
IO, FFI, time, randomness, environment access, external state, and nondeterminism are forbidden during default specialization.
9. Logical regions
ρ is logical lifetime and ownership evidence. It does not name a physical
allocator.
Core-N0 v0.1 supports:
- lexical
region ρ in term; - simple region parameters;
alloc<ρ>;- no-escape verification;
- region-contained cycles;
- acyclic escaping ownership.
The implemented P1V1 subset currently covers the first four items only for
non-escaping shared scalar cells. Region-contained cycles and acyclic escaping
ownership remain architectural targets, not implemented claims.
The implemented P1V4 subset adds direct non-escaping unique scalar cells.
Its verifier tracks whether each owner is live or moved independently of Rust
ownership. Load/store borrow; Use, direct call, and tail call move. Unique
owners cannot yet occur in aggregates, captures, entry parameters, or results.
The implemented P1V5 subset permits one internal direct unique result when
exactly one matching direct unique parameter anchors the caller-owned region.
Return moves the owner. A successful call binds the returned or fresh
same-region replacement owner; error paths return none. Entry and unanchored
results remain rejected.
A backend may choose registers, scalar replacement, stack, arena, region heap, or general heap when that choice preserves observable behavior.
General region polymorphism, weak references, and general shared escaping cycles are outside v0.1.
10. Identity and mutation
The logical store maps fresh, opaque locations to values. Two references alias when they carry the same logical location.
Observable:
- freshness;
- alias-visible mutation;
- effect order;
- explicit resource lifecycle.
Not observable:
- numeric address;
- physical placement;
- RC traffic;
- internal reclamation time;
- object movement;
- scalar replacement.
Core-N0 v0.1 has no same_identity, address-to-integer conversion, weak
reference, implicit destructor, or finalizer. Identity can be observed only
indirectly through behavior such as mutation through aliases.
11. Unsafe memory and FFI
Raw operations live behind typed capabilities:
Ptr<AddressSpace, Mutability, T>
raw_load(capability, ptr)
raw_store(capability, ptr, value)
raw_offset(capability, ptr, offset)
foreign_call(contract_hash, capability, args)
They carry UnsafeMemory or FFI effects. The contract defines ABI,
ownership, lifetime, mutation, errors, threading, callbacks, and hidden-state
dependencies.
FFI is opaque to specialization unless its contract proves the relevant
property. A pure foreign operation must also be independent of time,
randomness, locale, thread-local state, and hidden mutable state.
12. Binding-time judgment
Binding time is not a runtime type:
Γs ; Γd ⊢ expression : τ @ b ! ε
b ::= Static | Dynamic
Static ≤ Dynamic
Join:
join(Static, Static) = Static
join(Static, Dynamic) = Dynamic
join(Dynamic, Static) = Dynamic
join(Dynamic, Dynamic) = Dynamic
Soundness requirements:
- a static expression has no free dynamic dependency;
- a default static expression performs no observable effect;
- a dynamic condition makes the selected computation dynamic unless both residual branches can be emitted safely;
- recursion and static unfolding obey declared budgets.
Annotations constrain inference:
static
dynamic
specialize
no_specialize
specialization_budget
Budget and policy do not change program semantics.
13. Verification
Before interpretation, specialization, or lowering, the verifier checks:
- unique IDs and deterministic ordering;
- type correctness;
- constructor and pattern exhaustiveness;
- function and closure signature compatibility;
- effect-row correctness;
- region lifetime and no-escape rules;
- capability use;
- ANF binding and scope;
- binding-time evidence when present;
- schema version and semantic hash.
Malformed Core fails closed.
Artifact identity and interpreter-semantics identity are separate:
semantic_hash(program)
interpreter_semantics_hash(profile)
Semantic Identity Encoding v1 locks P1V0–P1V5 independently of Rust source
layout. The accepted bounded Stage 1 evidence matrix is
stage1_exit_audit.md. Binding-time evidence is not
part of the runtime type system. B0-A now validates the exact P1V0 request,
policy, entry manifest, provenance hashes, and budgets. B0-B assigns stable
intraprocedural node judgments with conservative control contamination and
effect eligibility. B0-C adds direct/tail-call summaries, incoming-control
flow, deterministic recursive convergence, and cumulative budgets. B0-D seals
canonical evidence and independently replays it against the verified source
and request. Complete B0 is validated; R0-A static-value/request identity is
implemented and validated. R0-B1/R0-B2 effect-safe bounded static evaluation,
R0-C1/R0-C2 verified residual generation, and R0-D sealed regenerative
validation are implemented and validated. The generic CoreVM0 P1V0 artifact
and its full static ProgramImage are also bounded-differential validated.
Dispatch erasure remains closed pending partially-static aggregates,
polyvariant loop specialization, mixed-result unfolding, and dynamic-control
residualization.
14. P1 admitted subsets
P1 v0 admits only:
Unit,Bool,I64,F64;- read-only
Array<ρ, Read, F64>; - direct first-order functions;
- tuples and the opcode ADT;
let,if,case, tail recursion, and return;- explicit checked bounds behavior;
- pure computation and typed errors.
Excluded:
- closures;
- general heap mutation;
- user algebraic handlers;
- IO and FFI;
- weak references and shared cycles;
- concurrency;
- general maps/text processing;
- region polymorphism;
- dynamic code loading.
The exclusion is a specializer scope limit, not a claim that the full language will never support these constructs.
P1V1 is a verifier-profile extension, not an implemented Futamura projection.
It adds lexical Region plus RefAlloc, RefLoad, and RefStore for
non-escaping Shared scalar references. It does not silently add those
operations to the P1 lighthouse workload.
P1V2 is another verifier-profile extension, not an implemented Futamura projection. It adds bounded existential closure construction and calls without admitting escaping closures or changing the P1 lighthouse workload.
P1V3 is another verifier-profile extension, not an implemented Futamura projection. It adds typed operations and affine implicit lexical handlers without adding handlers to the P1 lighthouse workload.
P1V4 is another verifier-profile extension, not an implemented Futamura projection. It adds bounded affine direct ownership without adding mutation or ownership to the P1 lighthouse workload.
P1V5 is another verifier-profile extension, not an implemented Futamura projection. It adds bounded ownership return without adding ownership to the P1 lighthouse workload.
15. Evolution rule
Any change to an existing Core tag, field order, normalization rule, or semantic meaning requires:
- an accepted ADR;
- schema version change;
- interpreter and verifier update;
- semantic-hash policy update;
- migration or explicit rejection of older artifacts;
- parity and residualization regressions.
An append-only profile extension may allocate previously unused tags under the
existing encoding domain only through an ADR, with strict unknown-profile
rejection and locked proof vectors. ADR-0013 applies that rule to P1V1;
ADR-0014 applies it to P1V2; existing P1V0 and P1V1 bytes and hashes
remain unchanged. ADR-0015 applies it to P1V3; all older profile vectors
remain unchanged. ADR-0016 applies it to P1V4; all older profile vectors
remain unchanged. ADR-0017 applies it to P1V5; all older profile vectors
remain unchanged. ADR-0018 freezes the profiled interpreter-semantics identity
and exact P1V0 semantic input to B0; future profile append operations cannot
change an older identity vector.