Project status
Z is pre-alpha. It has moved beyond a toy compiler, but it is not ready for production or compatibility-sensitive use.
Implemented and exercised
Section titled “Implemented and exercised”String.fromBytes(bytes)validates borrowed byte arrays/Slices and returns independently owned UTF-8 text.Utf8Errorfromstd/textreports the start of malformed input; embedded NUL bytes are preserved. Conversion scans and copies, rather than consuming the source or silently replacing invalid bytes.- TypeScript-shaped parsing, nominal static types, generics, enums, exhaustive
matching,
Option<T>,Result<T, E>, typed errors, and statically dispatched trait constraints that specialize across module-private boundaries - Value structs, ARC-managed classes, weak references, explicit ownership capabilities, deterministic cleanup, and conservative non-escaping views
- Native C-family code generation through Clang. Both emitters protect embedded source strings from C trigraph translation, preserving literal bytes in ordinary and async code without disabling strict compiler warnings.
- Source-body nonescape checking for inline borrowed callbacks in both frontends.
Direct synchronous invocation and proven named helper forwarding are accepted;
returning, storing, or capturing the callback requires an owned environment.
Owned
movecallbacks and ARC-backed received Objective-C blocks stay retainable. Native inline callable arguments use call-scoped storage and deterministic cleanup, including typed failure - Header-first C and Objective-C interop, substantial
.zdcontracts, and selected C++ values, methods, namespaces, RAII, and move-only resources - An experimental native subclass tier in both compilers: main-isolated
Objective-C
extends, checkedoverride/selector signatures, designated superclass construction, realsuperdispatch, and native ARC identity. Native parsing, safety-fact normalization, declaration contract checks, and structural initialization/receiver checks, and first native body typing with property/alias checking are present. Target-aware Objective-C availability errors cover subclass bases, overrides, native calls, and properties, including aliases and nested callbacks. The restricted body audit covers cleanup-bearing borrowed locals and locally handled errors; synchronous bodies reject async scopes, consuming Objective-C calls, and variadic/projected native calls in both compiler paths. Checked native class-entry plans and independently tested Objective-C interface emission are present. Dedicated native body lowering and implementation emission pass the same 200-iteration dispatch/capture/cleanup oracle as Stage 0 at-O0and-O2under strict Clang and UBSan. Native entries forward to the superclass during construction (including local ARC cleanup) and destruction; only ready instances execute Z overrides. Ready entries allow nested shared reads and trap reentrantinout thisconflicts, including during local cleanup. A scoped native ARC hold preserves receiver lifetime. Private inline scalars/plain native values, ownedString, and strong imported Objective-C reference fields (including nullable slots) plus sourcedeinitwork, with checked initialization, guarded direct access, and reverse-order cleanup. New native callback selectors and checked inlineobjc.selector(Type.method)registrations preserve target signature, Ready state, and executor proof. Checked synchronous full-expressionsuperdelegation releases only the current receiver loan and restores it before continuation/cleanup, enabling mutable AppKit zoom reentry without bypassing other active loans. Synchronous subclasses also coexist with async code: native ARC references survive supported main-isolated timer/continuation frames and are cleaned up on completion and cancellation. Broader resource fields, escaping field loans, async overrides, native-object thread transfer, and general suspension composition remain future work. - A visible native WebKit round trip whose executable
main, consuming portable application builder, selected platform-module seam, UI ownership, protocol handler and ownedobjc.adapt<P>carrier with directid<P>ABI, dynamicidclass narrowing, typed JSON routing, frozen synchronous and suspending Z service dispatch, generated TypeScript binding, applicationTaskScope, and registration lifetime are implemented in Z. A foreign WebKit callback can now submit an owned request, suspend inside the Z service, publish its result onthread.main, and be cancelled and joined before lifecycle shutdown - Arrays, strings, maps, sets, filesystem/process primitives, synchronization, arenas, JSON foundations, and inline Z tests
- Readonly collections preserve ownership and deterministic cleanup in both
semantic frontends, including aliases and nested aggregate/optional storage.
Readonly access does not imply borrowing or implicit shared ownership.
Native
lengthreads retain theirusizetype through readonly collection fields, aliases, and views, including template interpolation in callbacks. Explicit readonly Array copies own independent storage without becoming mutable; both frontends reject mutating frozen-collection methods - Structured tasks, cancellation, executor placement, dedicated native threads,
Mutex<T>,Once<T>, and bounded MPSCChannel<T>communication across proven slices. Channels split shareable asyncSender<T>from unique asyncReceiver<T>, preserve unsent ownership throughChannelClosed<T>, suspend task frames under backpressure, wake cleanly on close/cancellation, and expose explicit blockingSyncSender<T>/SyncReceiver<T>conversions for native threads. Both compilers execute bounded synchronous channels, including owned unsent errors, queue cleanup, cross-module storage, and readiness signaling from a yielding worker that is later cancelled and joined. Native worker capture lookup includes renamed object-destructured sender bindings; storing fallible owned values in arrays and channels transfers normalized temporaries without duplicate cleanup. Stage 0 nested value-producing async matches share one destination and preserve cleanup across suspension, early return, failure, and cancellation. These are composition fixes with existing syntax, not a general expansion of async storage support. Native asynchronous send/receive still reportZ0700; async waiter execution uses Stage 0. Native custom-destructor and foreign-resource transfer proofs remain conservative. The hybridbun run z check <source> --native-frontendretains native semantic parity coverage. Unbounded and rendezvous storage, selection, timeouts, and concurrent receive loans remain future tiers - Native synchronous block callbacks, including non-void and typed throwing callbacks,
can own local Strings, value aggregates, and consuming match payloads. Each
invocation cleans up locals and owned parameters on return, failure, loop exit, or normal completion;
returns, value-producing matches, and nested
tryuse the callback’s own result/error context rather than the containing function’s signature. Direct RHSmatchassignments work for locals and fields, including captured class state, with scalar selected-arm ordering and exact-once owned replacement. Fallible subjects and early-exit arms preserve the old owner. Custom constructor fields distinguish first initialization from replacement rather than invoking cleanup on zeroed storage. Owned captures retain their separate environment lifetime. Borrowed expression and block captures use stack environments and address original scalar, String, Array, cleanup-bearing value-struct, and ARC-handle storage. Mutable replacement releases the former value without taking ownership of the binding. Inline non-owning callbacks preserve conditional and loop evaluation without a heap environment. Ownedmoveexpression and block closures preserve mutable captured state across calls. Exact cleanup-order, nonescape, and allocation-balance tests cover both compiler paths, including zero heap allocations for the borrowed reads, mutation, nested forwarding, and parent-parameter probes. Async block return lowering, direct Array-method mutation capture inference, and addressable move-only resource captures remain staged. Inline callbacks and other call-scoped temporaries in the right operand of||/&&run only when that operand runs, and loop conditions build them per iteration, in both compilers. Borrowed environments in async enclosing functions reject until suspension-stable storage is proven. Ordinary object destructuring rejects root or nested inline custom cleanup in both semantic frontends, including aliased and generic payloads; supported owned value fields and ARC aliases remain available - Stored synchronous callables and a Stage 0 stored-async task tier that
preserve either
thread.mainisolation or arbitrary-threadthread.anysharing without scheduling implicitly. Native lowering now preserves the distinct asynchronous start-frame ABI through aggregate storage and executes captured, nonthrowing expression closures. Primitive and ownership-bearing results transfer exactly once; an unclaimed owned result is destroyed with its frame. A void closure containing one directawait scheduler.yield()now polls and requeues on a local FIFO executor. The first linear asyncmainshape now owns its callable local, one ordinary local, and child invocation in a persistent heap frame. A cleanup-free local crosses as the child’s single value argument; a cleanup-bearing owner crosses as an address-stableinborrow and remains guarded by frame live state. Both normal completion and unfinished-frame destruction clean that owner exactly once. Parent-frame cancellation now propagates through the dynamic child poll, wakes the parent, and deterministically unwinds child, callable, and owner storage. Publictask handler(...)preservesTask<T, E>through explicit join, typedtry/attempt, cancellation, lexical implicit join, and borrowed-owner cleanup in Stage 0. The fixed-point emitter now executes the first bounded unnamed-scope version: one stored nonthrowing void callable, one hot task, one explicit join or cancellation join, and one optional borrowed owner. Typed task results/errors, implicit scope-exit join, broader native function bodies, and multiple locals, arguments, and children remain in progress. Direct calls to imported async free functions with cleanup-free signatures now also cross the fixed-point compiler through a heap-owned task adapter. They may complete immediately or begin with one directawait scheduler.yield()before a synchronous tail. A directly awaited non-consuming ARC class method may use that one-yield shape with borrowed or cleanup-free arguments and an owned result or typed error; the frame retains the receiver and transfers or destroys that result exactly once. A direct async adapter whose body is one tailreturn await handler(...)now composes a real heap-owned parent and dynamic child. It stores owned and borrowed inputs, resumes through the child’s waiter, propagates cancellation, and transfers or destroys the owned result exactly once. General suspending bodies with locals and control flow around the child remain the next library-composition boundary. A non-consuming ARC class method may use that same tail adapter; its parent frame retains the receiver through child completion and cancellation. Straight-line locals declared before the await are frame-owned as well, so cleanup-bearing request values may be lent safely to the suspended child. Non-suspending prefix loops and early-result branches also execute in that frame. A final consuming enum/Optionmatch may move one selected owned async handler into guarded frame storage, suspend only that arm, and complete alternate arms immediately. Synchronous and asynchronous callable types retain distinct native identities even when their ordinary signatures match. Direct async free functions may use the same prefixed/matched tail-child frame. A direct free function may also initialize one local from an awaited ARC method, resume through the child waiter, and run a synchronous suffix against that frame-owned result. A generic captured async closure may directly await a non-consuming service method; each cold invocation retains its environment, preserves borrowed arguments through the join, and forwards cancellation to the child. - An opt-in application pressure test stores
thread.anyasync service handlers in a frozen map and executes a complete headless bridge request through a real suspension and typed response. The native semantic frontend agrees on the graph. Its retained, cleanup-bearingSearchService.invokemethod and the containingAsyncServices.invokerouter now emit natively through loops, maps, owned locals, a consuming handler match, and a dynamic child await. The outer bridge’s decoded-request match now emits through a free-function child frame, and its dispatch helper can retain an awaited service result across suspension before running the synchronous response-mapping suffix. The generic captured async service adapter emits and executes too. The complete smoke now executes both its synchronous health route and its suspended search route under strict C compilation. This pressure test closed consuming-match owner transfer, cleanup-bearing immediate arms, ARC value-argument retains, and synchronous control flow after an await. Both requests now run inside one ordinary reusable async free function whose heap-owned frame advances through two awaited children. Parameters and owned locals remain stable across both joins and cancellation follows the active child. The second join may be an explicitawait on thread.main publish(value)call after an ordinary service method await; borrowed value parameters are addressed from stable parent-frame storage rather than a transient poll-stack alias. ImportedTaskScopeandTaskControlidentities are canonical across modules; aggregate field copies use their runtime retain operations, and scope finalization happens after an activereturn awaitchild rather than before it starts. Scheduled ARC captures receive exactly one storage retain. Arbitrary suspension counts, branches between awaits, and equivalent general async method bodies remain future frame tiers. The visible WebKit application now imports and executes this surface rather than keeping it headless-only. The fixed-point compiler also executes its explicitnew TaskScope()lifecycle: a foreign callback schedules an owned async closure onthread.main, cleanup-bearing captures and moved parameters transfer through guarded frame storage, andclose()joins or cancels them exactly once. A scope with no enclosing Z task owns a serial fallback executor on the current thread; it does not create an OS thread. Z Notes now runs this complete path through the standalone native driver without a Stage 0 override. - The native launch-listener prerequisite now retains a
TaskScopecaptured by an ordinarythread.spawn(async move ...)closure. A terminal await of a named nonthrowingvoid/i32yielding helper propagates worker cancellation into its cold frame and joins nested children before releasing parent storage. Admitted main-thread wakeups remain separately owned and joined by the scope. Bounded UBSan tests cover pre-entry cancellation, closed-scope rejection, repeated teardown, and exact reference counts. General worker bodies and cross-threadTaskControlcapture remain staged. The real endpoint/inbox/main-host probe now passes under both compilers and UBSan, including competing startup, closed-scope rejection, idle/partial-client cancellation, and startup failure. Root owned match initializers also work in the native yielding frame, with cleanup on failed initialization and cancellation. Checked call identities now survive native-header private-name mangling across modules, including aliased internal async imports. The channel-based readiness/startup probe passes both compilers under UBSan. The complete native Zapp startup gate now passes automatic primary/secondary forwarding and endpoint/service-startup rollback, without starting secondary AppKit or services. Early terminal awaited returns now compose with one later awaited local in native function and supported method frames, including owned results and cancellation cleanup. Both paths reuse one child-invocation slot. Sequential rootTaskScope.close()/cancel()joins now preserve that awaited value, resume each following segment once, and join admitted work before parent cleanup. Same-executor joins suspend on a waiter; separate-driver scopes retain the existing synchronous bridge. Root-localnew TaskScope()values now join their children on function exit, including cancellation that skips an explicit await. Errors and cancellation cancel the remaining work; normal return waits for it. Moving the handle does not remove this obligation. Sequentialawait worker.cancel()also preserves the owned result. Native worker handles join before parent storage is released on early return, failure, or parent cancellation; explicitly cancelling a worker does not cancel its parent. Pending main-thread requests wake the join frame and are serviced before it finishes. Nested scope constructors and branch-local storage across suspension remain staged. Returned consuming matches can nest around one terminal child await; enclosing payloads remain owned by the frame. Single-statementselectblocks and payload-free arms are supported; throwing parents usetry await. Root destructuring preserves selected locals across continuation states and cleans unselected fields in its synchronous extraction group. These cases reuse the existing frame rather than allocating a separate task per match or binding. Direct timer awaits in nativemainstill require a future resumable entry frame; that boundary now reportsZ0700at the await. Linear main-executor callee timer frames also preservematch-initialized owners, direct scope joins between timers, failed initialization, and early completion with deterministic cleanup. Stage 0 prepares inline callbacks in consuming match subjects and transfers inline awaited Results into owned synchronous call parameters. This is not general suspension parity: dedicated-worker-to-main timer segments with early completion still require a separate forwarding implementation and are rejected before C emission. - Language-server highlighting, hover, completion, diagnostics, and
z.jsonschema support - Exact
z.jsonsource-module mappings for bare imports in Stage 0, the editor, and the fixed-point native compiler; package versions and installation remain separate future tooling - Versioned
.zbuild.jsonoverlays resolve tool-generated modules for one project invocation in Stage 0 and the fixed-point native driver without mutating authored source orz.json. Both drivers additionally execute source-hashed, exact-target checked call adaptation without rewriting authored files. A packaged generated module may authorize its one synthetic replacement access to a same-packageinternalmethod without exposing that method to authored consumers; the fixed-point driver owns the SHA-256 and native AST transformation. Validated adapter references are function links rather than source-initialization edges, allowing adapters to import entry-defined exported types. Ordinary import cycles, including those inside generated source, remain errors; generated roots and their dependencies stay checked, emitted, and tracked for build invalidation. - A standalone native compiler capable of rebuilding its own constrained source graph to a fixed point
- Compiler API 2 and
z metadata, exposing versioned checked public symbols and public call sites to tools without source scanning. External tools can derive typed service bindings from this artifact.
Partial and still expanding
Section titled “Partial and still expanding”- Feature composition across every ownership, closure, async, and storage shape
- General suspension and thread-affine continuation behavior
- Library-scale C++ coverage and generated Swift/Objective-C compatibility
- The self-hosted compiler’s authority over the complete language surface
- Standard-library breadth, diagnostics polish, compilation latency, and editor refactoring support
- Linux and Windows validation comparable to the project’s macOS pressure tests
- Target-selected platform compilation with inactive-code-aware editor behavior; the manifest target is authoritative today, while source-level compile-time selection syntax remains deliberately unsettled
Self-hosting versus retiring Stage 0
Section titled “Self-hosting versus retiring Stage 0”Z’s native compiler can rebuild its own Z implementation; consecutive compiler generations must emit identical C. That proves self-compilation, not complete feature parity with the TypeScript Stage 0 compiler.
Some language features still require Stage 0, and foreign metadata/Swift preparation still depend on TypeScript tooling. The packaged header adapter currently includes Bun’s standalone runtime. Generated Z applications do not include that compiler runtime.
The goal is a native-first toolchain with no TypeScript implementation dependency, not indefinite development of two compilers. A historical bootstrap seed can remain available after it leaves the everyday build workflow.
Planned, not promised as current behavior
Section titled “Planned, not promised as current behavior”- A package manager, registry, lockfile, and compatibility policy
- Production release artifacts and a stable bootstrap chain
- Native Swift ABI interoperability, Swift value types, protocols, and concurrency
- ORC cycle collection, richer regions, and carefully bounded shape-stability or local-uniqueness analysis
- Fuzzing, sanitizer coverage, deterministic release engineering, debugger integration, and ecosystem-scale hardening
The repository’s bootstrap status is the source of truth for temporary implementation limits. The language reference records accepted semantics independently of which bootstrap tiers execute every shape today.