Xo v0.9.0
Spec draft 0.9.
Module proxies (decision 0126):
XO_PROXYlists proxy URLs,direct(git), andoff, with Go’s fallback rules (comma after 404/410, pipe after any error; defaultdirect), andXO_NOPROXYpatterns go to git. The read only protocol serves@v/list,.mod,.zip, and.lockper module;xo mod proxy serve [--fetch] [<cache dir>]serves a module cache and can fill it from git, as a team cache. Proxied trees are checked against xo.lock exactly as git fetches are.Checksum log (decision 0124): with
XO_SUMDB=<name>+<id>+<key> [<url>], the first fetch of every module version must match the log’s record, be in its signed Merkle tree, and extend every tree the module cache saw before; otherwiseXO1313and nothing is cached. An unreachable log or malformed settings isXO1314;XO_NOSUMDBkeeps private modules out. The default staysoff(no public log yet).Definition hashes (decision 0125, the last step of 0086):
xo query hash <name>prints a content hash per top level definition that survives renaming locals and changes with what the definition, or one it calls in the module, computes.xo publish --checklists the definitions (private ones too) changed since the last tag, including those changed only through a helper, next to the API comparison.std/http speaks HTTP/2 over TLS on every backend (decision 0121):
with_tlsservers andhttpsclients agree onh2by ALPN, plainhttpstays HTTP/1.1, and nothing in the API changes. Requests of a connection run at once, each as a task; a fault in a streamed body resets only its stream. The native backends implement it in C (frames, HPACK, flow control) with net/http’s limits (250 streams, the header list limit with 431, the rapid reset limit) and its error texts, and their client keeps HTTP/2 connections for later requests as Go’s does. Over one connection the native server does 87.8k requests per second in the HTTP benchmark, the Go backend 58.2k (bench/results/http2-2026-10-10.md).Native test binaries (
xo test --no-gc, decision 0120): property tests andtesting.genwork, with the Go backend’s generated values for a seed, the same failing case, and the same shrunk input in the report. ASetorMapkeyed by a sized integer (Set[Int8]) now builds natively.xo test --realgives native tests real capabilities for the effects they declare (fs,fs.read,fs.write,net,clock,env,stdio,proc), as on the Go backend.Native backends: deferred code that faults no longer skips the rest of the cleanup. The deferred code registered before it and that of the blocks around it runs, the last fault is reported, as on the Go backend (core 3.5, decision 0085 amendment), and a task that caught such a fault with
task.isolatesees cancellation again (it could deadlock before).LLVM backend (decision 0119): generated code is compiled for the same CPU and features as the C runtime (clang’s defaults): LSE atomics for shared reference counts on darwin/arm64, outline atomics (LSE when the CPU has it) on Linux arm64. HTTP benchmark +3.5 percent.
Native
Mutex(decision 0118): a writer’s release hands the lock to the readers waiting, so they no longer wake only to park again: a contended readers and writer load (bench/perf/concrwmutex, new) went from 967 to 131 ms and from 6.3 to 0.25 s of CPU. Long lived scopes (the native HTTP server’s connections) no longer keep every finished task until they close, and the native allocator moves freed blocks between threads, so a server’s memory stays flat (28 MB instead of 295 MB after 300k TLS connections).Native HTTPS (decision 0117): full TLS handshakes about 1.6 times as fast and at half the CPU (9.7k to 15k per second in the HTTP benchmark, Go backend 17k): X25519 key shares through HACL*, faster P-256 inverses, a spinning lock for Mbed TLS’s key store, SHA instructions. The native server now sends session tickets as Go’s does (TLS 1.3 resumption with ECDHE, TLS 1.2 tickets; keys rotate daily), so returning clients skip the certificate signature.
bench/perf/http’s loadgen has-resume(RESUME=1in run.sh).xo lsp(decision 0114): a Language Server Protocol server with diagnostics and their fixes as quick fixes, hover (signature, doc, or type), definition (into std too), references, rename, document symbols, semantic tokens, and formatting. The VS Code extension starts it; ```xo fences in Markdown are highlighted.VS Code syntax highlighting (decision 0113):
editors/vscode/registers.xofiles with a TextMate grammar covering spec section 1 (keywords, contextual words, interpolation, raw and bytes strings, durations, sets, effects inuses). Install by symlinking it into~/.vscode/extensions.use c "<header>" [link "<lib>"]... [as name] [uses ...]on the native backends (decisions 0093 stage 2 and 0116): bindings generated from the header with clang (xo bind --c), scalars, Str, Bytes with a length, flat structs, and opaque handles at the boundary, theffieffect unless the line narrows it, XO1104/XO1105 for headers and names that do not bind, XO1201 on the Go backend. Calls are not recorded yet (native builds do not record).Decision 0111: passing one variable, or overlapping places (
xandx.f,xs[i]andxs[j]unless both indexes are different literals, avarreceiver and an argument naming it), to twovarparameters of one call isXO0503on every backend; the native backends no longer report XO1201 for it.Decision 0112: a value that holds a handle (
Mutex,Atomic,Chan,Task,Scope,Listener,Conn,task.Group, the std/http and std/sql handles, the test fakes) or a function value is not comparable:==/!=, Map keys, Set elements,derive Eq/Hash, andT: Eq/Hasharguments areXO0404with a note naming the field that holds it;derive Ordalready was. The checker now looks into struct fields and enum payloads forEq(it looked only at type arguments).Native backends (decisions 0107 to 0109):
Proc.run,os.tasks(a scope around main and around each test), method values (let f = x.method),map_concurrent_until, base64,Range.to_list,Rand.shuffle,target(a compile time constant), and in native test binaries MemFs (every method,fail_next, case sensitivity,files), FakeProcrunandon_run,os.proc.exitinsidetesting.run_main, andrun_mainof a main that cannot fail. Fixed natively: a receive arm after anafterarm lost its value; a?in a closure printed the function’s frame name without.closure, and one in aMutex.withclosure none;FakeClock.advancereturned before the tasks it woke ran; the success type ofErr(e)?was XO1201. One binding passed to twovarparameters is now XO1201 natively (the Go backend shares it, the native backends copied it).TestCompileProperty (decision 0110): random checker valid programs over the optional syntax must build on the Go backend and with
--no-gc, and run alike.Fixes from Codex r3: a
selectarm that receives without binding the value no longer generates Go that fails to compile; the “propagate with ?” fix is offered only where?can propagate; help notes for XO0205 in closures and for passingTestOswhereOsis expected.Native backends (
--no-gc, LLVM and arm64): interface types as values (decision 0100): dynamic dispatch, adapters for methods that cannot fail, error conversion, interface to interface conversion, Display and Debug of the dynamic value, and==;x.debug()on any value.Native backends: newtypes (
Email("x"),e.0read and assigned,Email(p)patterns, Debug,==, Map keys) and list patterns ([],[a, .., z]). Fixed: an or pattern whose alternatives bind the same name (Leaf(v) | Node(left: v, ..)) read the first alternative’s binding natively, a stale value when another alternative matched.Native backends (decision 0101): Float32,
Secret[T]redacted in every output path,Buf[T],<onderive Ordtypes,Int32.tryand the other sizedtrys,Int.pow,clamp,min/maxon Str, Set union, intersect, difference,Map.filter,List.insert,remove_at,indexed(),xs + ys, std defaults (crypto.token(r),Chan.new()),get_or_faulton any key type, builtin collections through interfaces, shorthand field patterns.==on a value holding a function or runtime handle outside an interface is now XO1201 natively (it failed in code generation). New test TestGenGoldensNative runs 66 internal/gen goldens on Go, LLVM, and arm64 and compares them.Native backends:
std/path(std/native/path.xo),task.semaphore,task.once(a fault in its function is not stored natively: the next caller runs it again), andtask.map_concurrent(std/native/task.xo); Debug text ofResult[T, Never]values. Fixed: natively, a task whose value is a Result failed its scope when that value was anErr; shorthand struct patternsBag{items}.Native backends (decision 0102): every Fs method and attenuation (
sub,cwd,read_only,write_only, escapesDenied),Stdio.write, and standard input reads (read_line,read_all,lines) that a cancellation or a stop request ends withIoErr.Canceled. Fixed natively:task.with_timeoutreturnsTimedOutwhen its closure finishes after the deadline without a cancellation point, as on the Go backend.Native backends: error frames (decision 0104): each
?records the function, location, and parameters lazily, and main’s error report printsat ...lines identical to the Go backend’s.Native Fs and standard input code is compiled into a program only when it calls them (XO_FS, XO_STDIN; hello world unchanged). Fixed: hybrid builds (
--hybrid) declared a Bool result’s argument size rounded up (go vet -asmdecl).xo test --no-gc:FakeStdio.set_inputfeeds the reads; fixed a hang afterFakeProc.signal_afterfired (the virtual clock stopped moving).Native backends: all of
std/time(decision 0094; std/native/time.xo and std/llrt/xo_time.c, a port of Go’s TZif reader and zone lookup): zones with the same per use embedded data (or--tzdata=system), civil time, gaps and overlaps, layouts, strict parsing with the same error texts and faults; alsoDuration.from_secs,scale,ceil_seconds,Time.parse_rfc3339, andTime.UNIX_EPOCHas a value. Programs that do not use std/time do not grow. TestTimeDiff compares about 70,000 lines with the Go backend. Fixed: the arm64 backend failed to assemble functions over 8192 instructions (tbzrange).HTTPS in std/http on every backend (std.md 4.6, decisions 0105 and 0106):
http.tls_cert(cert_pem, key_pem),Server.with_tls(certs)(SNI picks among the certificates),Client.with_roots(pem), andTlsErr. The native backends (--no-gc) speak TLS 1.2 and 1.3 through Mbed TLS 4.1.1 (vendored inthird_party/mbedtls, built once per compiler into the user cache, linked only into programs that use std/http), verify against the system’s roots on macOS and Linux, and report Go’s error texts; anhttpsURL is no longerXO1201there. std/http speaks HTTP/1.1 only, also over TLS: the Go backend’s client no longer negotiates HTTP/2. Failed TLS handshakes are not logged.bench/perf/http/run.sh:SCHEME=httpsserves the Xo configs over TLS andNEWCONN=1opens a connection (and a handshake) per request.