On this page
[Unreleased] Added [1.0.2] - 2026-09-20 Added Fixed [1.0.1] - 2026-09-20 Added Fixed [1.0.0] - 2026-09-20 Removed Changed Fixed [0.9.0] - 2026-09-20 Added Fixed [0.8.0] - 2026-09-20 Changed Added Fixed Removed [0.7.0] - 2026-09-19 Added Fixed [0.6.1] - 2026-09-19 Added Fixed [0.6.0] - 2026-09-19 Changed Added Fixed [0.5.0] - 2026-09-19 Added Fixed [0.4.0] - 2026-09-19 Added Fixed [0.3.0] - 2026-09-19 Added Fixed [0.2.1] - 2026-09-19 Added Fixed [0.2.0] - 2026-09-19 Added Changed Fixed [0.1.0] - 2026-09-18 AddedChangelog
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning. The file is validated in CI with patchnotes. The line under each version header is the release's name, a place on a mountain; docs/release-names.md has the scheme.
[Unreleased]#
Added#
- A Sublime Text build system,
editors/sublime/Nexium.sublime-build: Ctrl+B runs the file, the variants test, check, build and print its effects, and a diagnostic's--> file:line:colline is clickable. - The roadmap's "run from the editor, everywhere": the same Ctrl+B in every supported editor, a one-line
--format shortdiagnostic for problem matchers and--format jsonfor tools.
[1.0.2] - 2026-09-20#
Annapurna: Terray — the one who carried the frostbitten summiters down: the hotfix. The x86-64 binaries of 1.0.0 and 1.0.1 were built for the CPU of the machine that built them and crashed on any other; every build is for the architecture's baseline now, and the release refuses one that is not. With it, the fuzzer's two fixes, UTF-8 on the Windows console, and the day's documentation.
Added#
--cpu baseline|native|<name>andNX_CPU, the CPU a build without--targetis for;nx doctorprints it.os.arch(), the architecture a program runs on.- Nexium programs print UTF-8 on the Windows console: the generated
mainswitches the console to code page 65001 for the program's life and restores it at exit, sonx versionshowsRébuffatrather thanR├⌐buffat. - The roadmap's "tools only this language can have":
nx explainfor an effect's provenance, an effects lockfile for CI, proofs as editor code lenses, profiling by effect, record-and-replay tests,nx layout, the ownership trace, asking the checker for its facts, a binary-pattern debugger and workbench, hot reload, one-liners, REPL sessions as files, watch mode, snapshot tests, a Jupyter kernel and the playground, each pencilled into a minor. - The roadmap names what 1.0 is not yet (memory safety, maturity, numbers, ecosystem) and where each is answered; the numbers page (benchmarks against C, Rust, Go and Python, published on the site) moves from 1.6 to 1.1; a second maintainer and a list of packages are ecosystem items; the Always section commits to the corpus growing with the language, the sanitizers and the fuzzer on every release, and a patch within a week of a fix.
- The README's Status section describes 1.0 (language-stable, early ecosystem, with the roadmap's ledger of what it is not yet) instead of the first implementation; the six translated READMEs are regenerated from the current English one, which had moved on to the self-hosted compiler, the site, the Topo, the editors and the 1.0 numbers while they still described
cargo installand a Rust compiler. - The
CIworkflow skips a push that changes only prose and pictures; thePagesworkflow builds the site from them and validates the changelog, so a roadmap edit no longer runs the compiler on three platforms.CONTRIBUTING.mdsays what runs on a push. - The roadmap's page zero:
KNOWN_ISSUES.mdis where every open bug waits with its fix, no theme starts over a bug a user can hit, and the four an outside review of 1.0.1 found (a contained panic leaks what it acquired,nexium.lockis never read, the Python wrapper drops writes through a list, effect notes miss their witness) are the 1.0.2 patch, with the wording fixes forSECURITY.md, the README and the package names; 1.3 begins by splittingcheck.nx. - The roadmap opens with the quick wins in order: twenty-nine items sorted by how soon each can ship (hours, a day or two, about a week), each landing as a patch or in 1.1 when done, ahead of the themes.
- The roadmap's niche roads: the PowerShell one-liner (
irm | iex), the one-C-file install (curl bootstrap/nx.c | cc), torrents with web seeds and magnet links, IPFS pins,nx installfrom a portable copy, portable mode, MSIX with an.appinstaller,gh release downloadand the configuration-management modules, and a QR code. - The roadmap's "every road in": the installation channels beyond the setup and the script, per platform (winget, Chocolatey, Scoop, MSI, MSIX; Homebrew, MacPorts, a notarized
.pkg; apt and dnf repositories, PPA, COPR, OBS, AUR, Alpine, Nix, Guix, Snap; BSD ports), containers and dev containers, wheels and npm packages that carry the binary, version managers andnxup, the extension registries, and the same channels as targets of theinstallerartifact for Nexium programs. - The roadmap's 1.5 gains 32-bit architectures:
i686Windows and Linux,armv7,riscv32andthumb, with the list of places 64 bits are assumed today (runtime handles and sizes, 64-bit binary segments on a 32-bit word, the compile-time interpreter'susize, the C header import'slongandsize_t), the cross-compiled test run in CI, the compiler self-hosted as 32-bit, and the tiers. - The roadmap's Topo as a course and the docs, more: exercises the compiler grades (fill in the blank, fix the error, write it), quizzes checked against
nx effectsand the recorded outputs, predict-the- output cards, code blocks that run in the page once the compiler runs in the browser, progress on the route map, more project chapters; std reference pages fromnx doc, an error index, a cookbook, in-depth guides, a style guide, site search and docs per release. - The roadmap's "installer grown up": a setup mutex against two wizards at once, upgrade/repair/remove of an installed version, closing a running
nxbefore files are replaced, checks for disk space, a pending reboot and antivirus quarantine, the toolchain choice, the wizard in six languages, Authenticode signing, logs and rollback, silent-install parameters and an MSI, an ARM64 setup, and a Check for updates entry. - The roadmap's clickable line: the REPL with an inspector window,
:plotand:watch, the installer's editor page andnx upgrade,nx topoas an interactive tutorial runner,std.tui, the visual tools (size treemap, leak explorer, effect graph, ownership timeline), nexium-gui grown up, and the Hut, a native IDE in nexium-gui.
Fixed#
- The x86-64 release binaries of 1.0.0 and 1.0.1 crashed with an illegal instruction on machines without AVX-512 (
nx versionandnx doctorworked; anything that compiled did not):zig cccompiles for the CPU it runs on, and the release runner's had AVX-512. Every build without--targetis now for the architecture's baseline (plain x86-64) unless--cpu nativeorNX_CPUasks for more; the bootstrap scripts do the same; the release workflow and CI refuse an x86-64 compiler that contains AVX instructions. - A module name used as a value (
let x = math,var t = thread) crashed the checker with an index out of bounds; it is the diagnostic "mathis a module, not a value" now (found by the fuzzer in CI). - A compile-time call chain that never bottoms out overflowed the compiler's stack (found by the fuzzer, which moved the call to
fibintofib); the compile-time interpreter stops at 32 nested calls with a diagnostic. The limit is low because each nested call costs about 165 KiB of the compiler's stack;KNOWN_ISSUES.mdhas the fix that raises it. - The documentation site's menu no longer jumps back to the top on every page: it is its own scroll box, and a page load reset it, so reading the chapters in order meant scrolling the menu down again each time. The menu now keeps its position from page to page and brings the current page's entry into view when it is not.
- The links under "Files" in the release notes were relative, so GitHub resolved them under the tag page and every one was a 404. The notes script writes the download URL now, and the notes of every release since 0.2.0 were corrected in place.
[1.0.1] - 2026-09-20#
Annapurna: Rébuffat — the guide who roped the snow-blind party together on the descent: a patch that ties the loose ends after the summit. The fixes the tutorial found, the documentation site and the Topo, and support for eleven editors; the language is the same.
Added#
- Editor support beyond VS Code and Sublime Text, under
editors/: Vim (syntax, indent,:NxRunand friends,:make), Neovim (a plugin that registers the tree-sitter parser, its queries andnx lspwith the built-in client), Helix (language config and queries in Helix's scopes, with indents and text objects), Zed (an extension with grammar, outline and the language server), Emacs (nexium-modewith Eglot and lsp-mode registration), Kate and the other KSyntaxHighlighting editors, Notepad++ (a User Defined Language), nano, and a guide for JetBrains IDEs (TextMate bundle plus LSP4IJ). CI loads or compiles every one of them. - The documentation site, londopy.github.io/nexium, built by
site/build.nx, a Nexium program that renders the Markdown in the repository (the docs, the project files, the tutorial) with the language's own token rules for highlighting, and published by a Pages workflow on every push. The test harness builds it. - The Topo,
topo/: a 23-chapter tutorial from installing the compiler to a neural network, a GUI, a network service and a library shipped to C, Python and Node. Every program in it (topo/code/) is run by the harness against its recorded output, and the diagnostics it shows are recorded too. Writing it found the bugs below. - The documentation site on a phone: one column with the navigation behind a Menu button, tables that scroll sideways instead of widening the page, grids and code blocks that stay inside the screen, larger touch targets, and a theme colour for the browser chrome. Every page is checked at 375 pixels wide.
assets/social-preview.svgand its 1280x640 PNG, the card GitHub and the chat apps show for a link to the repository (uploaded under the repository's settings); every page of the documentation site carries Open Graph tags pointing at it.- The roadmap's 1.7, the seam both ways: Python (strings, NumPy without copies, dataclasses and exception classes from the Nexium types,
ref classhandles, callbacks, a CPython extension wheel, Python from Nexium), Rust (error sets as enums, strings and handles,no_stdcrates, source crates, Rust crates from Nexium), C++, Go, Java and Kotlin, C#, Ruby, Lua, Swift and the browser asartifactkinds, a versioned ABI document, buffers that leave,nx ship --abi-check, and@cImportaccepting more of C. Release name Annapurna II; Gangapurna moves to 1.8. - The roadmap's 1.2, memory safety without a garbage collector: the view rules V1 to V5 (returning, storing, growth, moving, arenas),
unsafefor what the checker cannot prove, what stays (moves, scope-exit destruction, reference counting,weak, leak detection) and how the rules roll out under the stability policy, which now says that undefined behaviour may become an error in a minor. The later themes move down one number.
Fixed#
- A function checked on demand while a constant was evaluated at compile time could name a global before its type was resolved, and the checker crashed on the unresolved type (found by the fuzzer on its second run in CI). A global's type now resolves on first use; the program gets the diagnostic for the compile-time global access instead.
- A bare
break,continueorreturnends amatcharm before the comma (_ => break,parsed the comma as an expression). A one-element array literal can be indexed and sliced ([x][..],[x][0]): only a[that opens[]Tor[N]Twith a type after it starts an array type in expression position. Set!Twidens into!Twhere!Tis expected (an initializer, an argument, a return);!Tnever narrows into a named set. Type names now spell a named set (Parse!i32), so the diagnostic no longer reads "expected!i32but found!i32".- The
!effectdiagnostic's first note points at the call that brings the effect in, not at the function's header. - Two dangling views, found by running the tutorial's programs on Linux and the compiler under AddressSanitizer:
std.http.parse_urlkept the host as a view of a value that was released when theif letended, so every URL with a port could fail withNotFoundwhere the freed memory was reused; and the checker read a syntax node through a pointer after adding nodes to the tree (a binary pattern with a computed size). Both are the case the roadmap's 1.2 makes an error; a CI job now builds the compiler with the sanitizers, checks every source with it, and runs every spec case and tutorial program built with them. for k in mover aMapemitted C that did not compile: the keys are collected into an ownedListthe loop walks and releases, the same asfor k in m.keys().m[key]crashed the emitter; it is the lookupm.get(key)is. Both have a spec case now.@weak(x)did not parse (weakis a keyword); a builtin may be spelled with one. A weak reference created in a struct literal was retained a second time on its way into the field and its storage never freed; and an object whose fields held a weak reference back to it could be freed while its own fields were still being released. Aref classnow holds its storage until its fields are gone; both cases are spec cases.
[1.0.0] - 2026-09-20#
Annapurna: Summit — the top of the mountain the project has been on since 0.1: the language stops changing under people's feet, the compiler is written in itself, and nothing but Nexium, one C file and a runtime header is left.
Removed#
- The first compiler, written in Rust (
bootstrap/rust/,Cargo.toml,Cargo.lock), and with it the last Rust toolchain use in CI: thelintjob now runsnx fmt --checkon the tree andnx checkon the compiler, the harness and the fuzzer. The repository is Nexium, one generated C file, and the runtime header (decision 90). The git history keeps the crate.
Changed#
- The architecture tour (
docs/architecture.md) names the files underself/; the specification's status table and the roadmap record that the port is complete.
Fixed#
- The fuzzer wrote its cases under
nx-out/fuzz, the path of its own executable on Linux and macOS, so CI's fuzz job could not start; it works undernx-out/fuzzing.
[0.9.0] - 2026-09-20#
Annapurna: Summit Ridge — the last ridge, nothing left but walking up: every tool in Nexium, the shipped nx the Nexium one, a harness and a fuzzer in Nexium, the stability policy and the tiers.
Added#
nx fmtin Nexium (self/fmt.nx, a command ofself/nx.nx): the formatter, byte for byte the same as the first one on every source in the tree with its layout disturbed, whichcargo testchecks while the Rust one exists. The lexer keeps//comments as tokens on request.nx effects,audit,refcounts,leaks,size,versionanddoctorin Nexium (self/tools.nx,self/size.nx): the reports over the checked program and the section-table reader for ELF64 and COFF binaries, matching the frozen Rust commands on every example except where the Nexium compiler does better (dynandfntypes printed as written, recursive functions showingunbounded_stack).nx docin Nexium (self/doc.nx): the same page as the first generator on every source, apart from a function's effects coming from its own module and generic functions saying so instead of showing one instantiation. The parser keeps each///run's text (Tree.docs).- Packages in Nexium (
self/manifest.nx): thenexium.tomlsubset,nx init,nx add,nx fetchand the lock file; the loader resolvesimport depandimport dep.modulethrough the manifest and scopes a package's own imports to itssrc. nx shipin Nexium (self/ship.nx,self/ship_node.nx,self/installer.nx): the C header, shared and static libraries, the Python package and wheel, the Rust crate, the npm package and the installers, from the export recordscgen.nxwrites as it emits each wrapper. The header and typings carry each function's///comment.nx lspin Nexium (self/lsp.nx): the language server over stdio, with diagnostics, hover, go to definition, completion and rename, and the editor-module loader and IDE queries behind it;cargo testdrives it the way an editor does.nx replin Nexium (self/repl.nx): the interactive session on the compile-time interpreter, which in REPL mode may print, read the console, touch files, the clock and randomness (check.nxrepl_mode,run_repl, values rendered by their types).nxalone at a terminal opens it. The last tool leaves the Rust crate.io.is_terminal(h),os.set_env(name, value)andos.exe_path(); with the last,nxfinds a Zig bundled next to it (<dir>/zig/zigor<dir>/../zig/zig), as the Rust driver did. The driver gives each build its own Zig cache under the output directory whenZIG_LOCAL_CACHE_DIRis not set, as the Rust driver did, since Zig's cache is not safe against severalzig ccstarting at once on Windows.- The seed
bootstrap/nx.cis regenerated whenself/needs a builtin the seed lacks, not only at a release. - The test harness is a Nexium program,
tests/run.nx: it builds the compiler from the seed (when the sources are newer than it), checks the fixed point, and runs the examples, the spec cases, the compile-fail cases,fmt --checkon the tree, the standard library's tests, the GUI, packages,ship, the installer, the REPL and the language server, the cases on several threads.cargo testand the Rust harness retire; CI's test jobs have no Rust toolchain. On Windows a program run throughprocessmay be named with forward slashes. - The stability policy,
docs/stability.md: what a patch, a minor and a major may change from 1.0 on, the deprecation cycle, what is not covered, and the C toolchain policy.nx fix FILE...applies the deprecations the compiler can migrate mechanically; there are none yet. - The platform tiers,
docs/platforms.md: tier 1 is built, tested and released (x86_64 Linux and Windows, Apple Silicon); tier 2 is built and released, not tested (aarch64 Linux, Windows on ARM), cross-compiled from the same C in the release workflow and picked by the install script on an ARM Linux; tier 3 is whatever Zig targets. - Fuzzing,
tests/fuzz.nx: mutated corpus sources through the front end and random bytes through the binary pattern engine (tests/fuzz_patterns.nx); a crash, a signal or a hang is a finding, saved for the report. CI runs it on every push with a fresh seed. - Releases ship the compiler written in Nexium: the release workflow builds it from the C seed on each platform with Zig alone (
ccon macOS), checks the tag againstself/nx.nx, and packages that binary in the archives and the Windows installer. Building from source issh bootstrap/build.sh; no Rust is needed.
Fixed#
if let v = optover a place (a local, a field, an element) bound a bitwise copy of the payload that could then be moved on, so the optional and the binding both freed it. The binding is now a view of the payload, like a loop variable: moving out of it is an error with a.clone()/.?hint, while a binding over an owned temporary (a call result) still takes the payload. The comptime interpreter and the language server had such copies.- Reading standard input through
std.stream.Readerblocked until 64 KiB arrived or the pipe closed, sincefreadfills its whole count; the runtime now reads the descriptor and hands over what the pipe has, so a server on stdin answers each message as it comes.io.read_lineand the reader share one buffer. Standard input is binary on Windows, like the other two streams.
[0.8.0] - 2026-09-20#
Annapurna: the Sickle — the exposed glacier crossing below the summit: the compiler in Nexium is the compiler, and builds from its own C with no Rust.
Changed#
- The compiler written in Nexium is the compiler (decision 90). The first compiler, in Rust, is frozen at 0.7 semantics and moved to
bootstrap/rust/; it still builds and still holds the tools not yet ported (fmt,doc,lsp,ship, packages, the REPL), and leaves at 1.0. A machine with nonxbuilds one frombootstrap/nx.c, the C the compiler emits for itself, with any C compiler and no Rust (bootstrap/build.sh,build.ps1); CI does so on three platforms with no Rust toolchain.cargo testbuilds the compiler from the seed and runs every example, spec case and compile-fail case through it; the oracle diffs that drove the port (nx tokens,sexp,tirandemit-ccompared between the two compilers) retire.
Added#
unbounded_stackis an effect, no longer reserved: a function on a cycle of the call graph (it calls itself, or calls something that calls it back) carries it, because its stack use depends on its input; calls through function values and trait objects acquire it as they acquire every permitted effect.!unbounded_stackis discharged by rewriting the recursion as a loop over an explicit stack (SPEC 9). The first language change made in the compiler in Nexium alone.- The compiler in Nexium renders diagnostics as the tools always have: the message,
--> path:line:col, the source line, a caret, notes under their error, and the count at the end; it printed byte offsets.
Fixed#
- The compiler in Nexium searched no directory for
@cImportheaders and vendored C when the file was named without a path (nx build demo.nxfrom insidegui/); the file's directory is.in that case, as in the Rust compiler.
Removed#
layout(packed)andsoaon structs, and thepoolandstackallocation strategies, are gone from the specification (decision 88):layout(c)andusing arenaare what the language has. The two struct spellings used to be accepted and silently ignored; they are errors now. Region rule R1 is the region rule; the cases it does not cover are listed in SPEC 5.6, and a debug build now fills freed storage with0xDDso a view that outlived its storage does not read the old contents by luck.
[0.7.0] - 2026-09-19#
Annapurna: Camp V — the last camp, at 7,400 m; the summit push starts here: the compiler builds itself.
Added#
self/check.nxchecks closures: parameter and return types from the expected function type, by-value and by-reference captures, inferred return types; and records: constraint checks, compile-time decisions on constant fields,Record.new; and trait objects: vtables,dyncoercions, dynamic calls, effect bounds on function values.examples/generics.nx,examples/tests.nx,examples/tour.nx,examples/records.nx,examples/dyn.nxandstd/thread.nxjoin the body comparison; and binary patterns and construction:examples/binary.nxandexamples/binary_sizes.nxjoin too (46 sources).examples/records.nx: compile-time,.newand run-time constraint checks.examples/binary_sizes.nx: float, signed, little-endian and computed-size segments, a remainder written back out.self/check.nxevaluates at compile time: an interpreter over the typed IR runs constants, globals,comptimeexpressions,comptime testblocks and record constraints;examples/comptime.nx(a CRC table, primes, strings and structs computed at compile time) joins the comparison (50 sources: every example and std module but@cImportusers).- A constant slice whose elements were computed at compile time is stored as a static array; only string literals could back a constant slice.
self/check.nxreports the diagnostics-only passes: declared effect bounds against inferred effects,for parallelbodies mutating shared state, mutable globals in embeddable libraries,ownon copied types and on exported or fn-value functions, moves out of map lookups,matchexhaustiveness, returned views into locals.cargo testruns it over every compile-fail case and expects every message the Rust checker gives.self/cimport.nx, the C header importer in Nexium, and@cImportinself/check.nx: every source in the tree now matches the oracle,examples/cimport.nxand nexium-gui included (54 sources).self/cgen.nx, the C emitter in Nexium: byte-identical tonx emit-con every source in the tree (54, its own 96k-line translation unit included);cargo testdiffs them.- The bootstrap closes:
nx1(the emitter in Nexium, built by the Rust compiler) emits the C of itself,zig ccbuildsnx2from it with no Rust involved, andnx2emits byte-identical C for itself and other programs.cargo testperforms the three stages. tests/spec: the specification's conformance cases, one program per claim SPEC.md makes with its recorded output and exit code, run bycargo testand diffed through every self-hosting stage; sections 2 to 14 (artifacts and the toolchain are covered by the ship tests).self/nx.nx, thenxdriver in Nexium: build, run, test, check, emit-c and tir over the self-hosted pipeline, invoking the C compiler as the Rust driver does; the standard library is embedded in it. It builds itself, and the result builds and runs programs (cargo testchecks that). Nothing past the first compiler needscargo.
Fixed#
Color.Green as u8(a unit enum cast to an integer) emitted a C cast of the whole struct, which the C compiler rejected; it is the tag now.let d: i8 = -128is accepted: a negative literal is one literal, not the negation of 128 (which does not fit an i8).- A labeled block that ends in
break :label valuehas the value's type; it wasnever, and printing the value was rejected. - A
breakinside anorelsedefault, a call argument or any other nested expression now ends awhile true; a non-void function that ended in such a loop was accepted without a return value. - A binary segment sized by an expression (
payload:len*8) is in bits like a constant size; the generated C and the compile-time interpreter scaled it by eight again, so such patterns never matched and such constructions wrote past the intended width. - A
!voidtail expression (amatchwhose arms print, a call) at the end of a function returning!voidis returned; it was reported as unused. %on floats compiles: the generated C applied the integer operator to doubles and the C compiler rejected it; it isfmodnow.- A constant of
List,String,Map,ref classor weak type (or one containing them) is rejected with a hint to store a slice; it used to fail in the C compiler, or worse, be freed by whoever copied it. - Slicing an empty slice (
text[..],"".split(","), an empty binary pattern) added an offset to a null pointer, which is undefined in C: a debug build made by zig 0.14 trapped with "applying zero offset to null pointer". The generated C goes throughnx_padd, which skips the add. if t < 1000 and t > -1000 { t + 1 }: both halves of anandguard narrow the range; the second fact used to replace the first, so the addition was not proven and a!panicsbound on the function failed.- An array indexed by the index of a
for x, i in xsover it is proven in bounds; the proof was skipped for arrays. self/check.nxkept range facts on avaronly until its next assignment was checked, so a guardedt = t - 10was not proven; the facts now hold while the right side is checked, as in the Rust checker.- The generated C no longer collides with macOS's
machheaders, which definets_32and friends as macros: a local namedtsat slot 32 failed to compile on macOS. @cImportretries a preprocessor run that failed without a diagnostic and reports the exit code when it keeps failing, instead of an empty message.self/cimport.nximported hexadecimal float macros (macOS'sMAXFLOAT) that the Rust importer skips, so the two disagreed on<math.h>there; neither imports them now.self/nx.nxlinks native macOS builds with the system compiler, as the Rust driver does (zig 0.14 cannot link against the current Xcode SDK);cargo testbuilds the bootstrap's second stage with that compiler too.- The tree-sitter grammar parses
x orelse return nulland the other jumps afterorelseandcatch, and/little-signedsegment modifiers. List(thread.Worker(Job)): a generic type of another module takes its type arguments from the caller's scope, and is accepted in expression position (List(thread.Worker(Job)).new()). The arguments were looked up in the other module ("cannot find typeJob"), and the expression form was rejected as not a type.
[0.6.1] - 2026-09-19#
Annapurna: Lachenal — patch.
Added#
nx tir: the checked program as S-expressions, the oracle for the self-hosted checker;--sigsprints declarations and signatures only.self/check.nx, the checker in Nexium: module loading, declarations, type interning, signatures (cargo testdiffs--sigsover 41 sources), and function bodies: statements, expressions, calls, builtin methods and namespaces, matches and patterns, casts, coercions, ownership moves and the range analysis. It produces the oracle's exact typed IR for 35 sources, itself among them; generics, closures, trait objects, binary patterns, compile-time calls and C imports remain.
Fixed#
- A C compilation that fails because Zig's shared cache was being written by another
nxat the same time ("failed to check cache") is retried, so parallel builds on Windows no longer fail at random. - The formatter spaces bit-or like the other operators (
a | b, nota| b) and no longer glues-> !List(T) {; closure bars are classified per line so a bit-or inside a closure body is not taken for its closing bar. - A
Stringbuilt for the right side ofand/orproduced C that did not compile (its release was emitted outside the block that declared it). i128range bounds overflowed inside the compiler; ani128literal beyondi64was emitted as undefined C for the minimum;parse_intrejected values above 2^65.- A range fact from an
ifguard (if i == 1 { ... }) outlived its block and could prove a laterxs[i]in range, eliding its bounds check. return xs[i]into a?Tor!Tmoved the element without a clone, and a divergingorelsedefault orcatchhandler marked its operand moved.- Floats print as the shortest text that reads back exactly (
3.141592653589793, not3.1415926535897931). - The tree-sitter parser is regenerated for 0.6.0 (its version is embedded).
[0.6.0] - 2026-09-19#
Annapurna: Camp IV — the syntax settled for 1.0; the parser in Nexium.
Changed#
- Breaking: control flow drops its parentheses and bodies always take braces (decision 87).
if c { },while c { },for x, i in items { },for i in lo..hi step s { },for parallel x in items { }, andif let v = opt { }replaceif (c),while (c),for (items) |x, i|, andif (opt) |v|. The one-statement forms become braced one-liners:if c { return v },let m = if a > b { a } else { b }. A struct literal in a condition needs parentheses (if (Point{ .x = 1 }) == p { }). Match-arm guards arepat if cond =>.catch |e|and closure parameters are unchanged. nx fmtmigrates 0.5 sources to the new syntax as part of formatting;nx fmt --migrate-onlyupgrades the syntax and leaves the layout alone. The migration edits by token span, keeps comments and blank lines, and is idempotent, so running it on a mixed tree is safe.- The formatter puts a space before the body brace of any control head (
if k == Kind.Defer {), where before an uppercase name would have been glued to{as a struct literal. - The language server resolves
forbindings andif letbindings to their declaration. - The tree-sitter grammar, the VS Code and Sublime syntaxes, every example, the standard library, the GUI, the self-hosting sources, and all documentation (including the translations) use the new syntax.
- Moving a
StringorListout of aforloop variable is a compile error (use x.clone()); it silently produced a double free before. A loop variable is a view of the element.
Added#
self/parser.nx: the parser written in Nexium now parses the 0.6 syntax and prints the same tree asnx sexpfor every example, std module, GUI and self-hosting source;cargo testdiffs the two (roadmap phase 4).
Fixed#
unreachableas the last statement of a function returning an error union or struct produced C that did not compile.- A program whose imported module also defines
main(asself/lexer.nxdoes, for running the lexer on its own) took the wrong entry point. nxleft one Zig cache directory behind per process undernx-out/.zig-cache; it is removed when the command finishes, and stale ones are swept.
[0.5.0] - 2026-09-19#
Annapurna: Camp III — other people can build on it: packages, editors, installers.
Added#
- Packages: a
nexium.tomlmanifest with[dependencies]from a git tag ({ git = "...", tag = "..." }) or a directory ({ path = "..." });import deploads the dependency'ssrc/lib.nxandimport dep.moduleitssrc/module.nx; a package's own imports stay inside the package.nx initwrites a manifest,nx addrecords and fetches a dependency,nx fetchclones every git dependency (transitively) intonexium_modules/and writesnexium.lockwith the resolved commits. Seedocs/packages.md. - A tree-sitter grammar (
editors/tree-sitter-nexium) with highlight queries for Neovim, Helix and Zed; it parses every example, std module and self-hosted source without an error node, and CI keeps it that way. - The language server gained go to definition (functions, types, constants, locals, imported and package modules), completion (module members after
alias., fields, methods, variants and errors after., names in scope), and rename (a local within its function, an item across the file). They work from the parsed source, so they answer in files that do not type-check yet. artifact installer:nx shipproduces an installer for a program. On Windows an Inno Setup script (compiled to<Name>-<version>-setup-x64.exewhen Inno Setup 6 is installed) with license page, per-user or all-users install, Start menu entry, optional PATH entry and uninstaller; on Linux and macOS aninstall.shwith--prefixand--uninstallplus a tarball. Listedfilesare copied next to the program.artifact node { name = "pkg" }:nx shipproduces an npm package for an exported library:index.jscalling the shared library through koffi (no build step, no node-gyp),index.d.tstypings,package.json. Slices take typed arrays, arrays or strings; error unions throwNexiumErrorand panics throwNexiumPanicwith the message.
Fixed#
- Parallel builds on Windows could fail inside Zig's own cache ("failed to check cache ... file_open Unexpected") when several
zig ccprocesses started at once; eachnxprocess now gives Zig its own cache under the output directory unlessZIG_LOCAL_CACHE_DIRis set.
[0.4.0] - 2026-09-19#
Annapurna: Camp II — the language talks to the world: sockets, HTTP, threads.
Added#
- Sockets in the runtime:
net.connect,listen,accept,send,recv,close,peer,local,resolve,udp_bind,send_to,recv_from,last_peer; blocking, with per-call timeouts, on Winsock and BSD sockets. New errorsTimeoutandConnectionRefused. std.net:TcpStream(connect with timeout, send, recv, recv_all, peer, bufferedreader()/writer()),TcpListener(bind, accept with timeout, port),UdpSocket(bind, send_to, recv_from),parse_addr,port_of,resolve.std.streamreaders and writers work over sockets.std.http: a client (get,post,requestwith headers; HTTP/1.1, Content-Length and chunked bodies, up to five redirects) and a server (Server.bind,serve,serve_one,Routerwith exact and/*routes,serve_static,Request.param/header, response helperstext,html,json,redirect,not_found). Plainhttp://; TLS is left to@cImport.examples/service.nx: an HTTP service and a client in one program, the phase 2 exit example;examples/errors_more.nxcovers the fixes below.- Threads in the runtime (
thread.start,thread.join,thread.count,sync.mutex_new/lock/unlock/mutex_free,sync.cond_new/wait/signal/broadcast/cond_free) andstd.threadon top:spawnreturns aThread(T, R)whosejoinyields the function's result,runreturns aWorker(T)for functions without one,Channel(T)(send,recv,try_recv,close),Mutex(T)(lockreturns*mut T,unlock). A panic inside a thread is re-raised byjoin. Starting a thread carries thenondeterministicandshared_mutableeffects. ownis accepted on parameters of methods in genericimplblocks.process.exec(argv, stdin, cwd)runs a program with stdin fed, a working directory, and stdout/stderr captured (process.last_stdout,process.last_stderr);std.processwraps it asrun,run_with,shellreturning anOutputwithcode,stdout,stderr.
Fixed#
- Free functions with the same name in two imported modules (
fs.copyandstream.copy) collided in the generated C. - Returning a caught error value (
catch |e| { return e }) from a function returning!Tproduced a bare error id instead of an error union. - An untyped integer literal now coerces into
!T(return 7in a function returning!i32). - The formatter kept the space in
-> http.Response {(a dotted type before a block is not a struct literal).
[0.3.0] - 2026-09-19#
Annapurna: Camp I — the first camp on the mountain: the tools' standard library.
Added#
std.fs:exists,is_file,is_dir,size,modified,read,read_lines,write,append,copy,list(sorted),make_dir,make_dirs,remove,remove_all,rename,walk,cwd,temp_dir,temp_path, and the path helpersjoin,parent,base_name,stem,extension,with_extension,normalize,is_absolute. Under it, newioprimitives in the runtime:append_file,file_kind,file_size,file_modified,make_dir,remove_file,remove_dir,rename,list_dir,cwd,temp_dir; they also work at the REPL.std.time:DateTime(utc,local,with_offset,now_utc,now_local,date,parse_iso,to_ms,weekday,day_of_year,iso,formatwith%Y %m %d %H %M %S %3 %z %a %b %j),Duration(seconds...days,between,since,textsuch as1h 02m),Stopwatchon the monotonic clock,is_leap,days_in_month. Under it,time.utc_offset(ms)in the runtime (0 at the REPL).std.regex: a Pike VM (no backtracking, linear time) with classes,\d \w \s \b, anchors, groups and(?:...), alternation, greedy and lazy repeats including{n,m};compile,find,find_at,find_all,is_match,replace_allwith$1references,split, andMatch.group.- A local that was moved out can be assigned again; the assignment re-initializes it instead of being reported as a use after move.
for (a..b step s) |i|walks a range with a step; a negative step counts down (the loop variable must be signed).while (c) { } else { }runs the else block when the condition turns false, but not after abreak.matchexhaustiveness now uses the full matrix algorithm, so tuples of enums, nested optionals and enum payloads are checked precisely instead of demanding a catch-all arm.nx test FILE --filter NAMEruns the tests whose names contain NAME.std.text: UTF-8 by code point (decode_at,chars,char_count,char_at,slice,truncate,reverse,encode,is_valid), terminalwidth(wide and zero-width aware), and case mapping for ASCII, Latin-1, Latin Extended-A, Greek and Cyrillic (to_upper,to_lower,eq_ignore_case).std.testing:approx,expect_approx,is_err,expect_err,expect_error,expect_contains,expect_lines(names the first differing line) and file snapshots (snapshot,snapshot_in;NX_UPDATE_SNAPSHOTS=1rewrites them).- Embedded std modules can import each other;
erroris a type name (the anonymous error set), andownis accepted on generic parameters. nx testno longer runs the tests of imported std modules.std.stream: bufferedReader(open,stdin,read_line,read,read_all) andWriter(open,append,stdout,stderr,write,write_line,flush,close) pluscopy, over new runtime file handles (io.open,io.read,io.write,io.flush,io.close; handles 1 to 3 are the standard streams).os.environ()lists the environment;args.env_map()turns it into aMap(String, String).nx test --verboseprints timings and the tests a filter skipped;nx doc std.fsandnx test std.regexaccept an embedded module by name.examples/tool.nx: a log scanner (walk a tree, parse timestamps, filter by a date window and a regex, tally by level) in 142 lines, the phase 1 exit example of the roadmap; runs onexamples/data/logsby default.
Fixed#
- A struct, tuple or enum literal that read a local in one field and moved it in a later field saw the already-zeroed value; field values are now materialized in source order.
[0.2.1] - 2026-09-19#
Annapurna: Herzog — patch.
Added#
SPEC.md, the language specification as implemented, andROADMAP.md.nx repl, andnxwith no arguments at a terminal: an interactive session on the compiler's interpreter, with bindings kept across lines, the real diagnostics, I/O, and:load. The Windows installer adds a Start menu entry that opens it, and offers to launch it when setup finishes.- The compile-time interpreter now evaluates
Map, the mutatingList,Stringand slice methods (insert,remove,sort,split,trim,parse_int, ...),expect_eq,assert, UTF-8 helpers and the reference class operations, and pointers stay valid across calls, sostd.jsonand the other std modules run at the prompt. A statement the interpreter cannot evaluate is reported with the position of the failing expression.
Fixed#
- Building a program by its bare file name (
nx run app.nxfrom inside its directory) dropped the C sources declared inartifact link: the empty parent directory produced a lone-Ithat swallowed the next argument. opt.?,opt orelse dandtry reson a local holding an owning value now move the local: it is no longer dropped a second time at scope end (this crashed GUI programs on exit), and a later use is reported as a use after move.
[0.2.0] - 2026-09-19#
Annapurna: Base Camp — where the expedition is staged: the installer, the spec, the roadmap.
Added#
ownparameters:fn f(own s: String)takes ownership of its argument (moved at the call site, dropped or moved on by the callee).orelse return vandcatch |e| return v: jumps on the right oforelseandcatch.String.push_byte(b)appends one raw byte.- Integer and float literals coerce into
?T. examples/own.nxand five compile-fail cases for theownrules.- The standard library in Nexium:
import std.strings,std.lists,std.bytes,std.num,std.json,std.args(96 functions with tests), embedded in the compiler;docs/std.mdlists them;examples/stdlib.nxuses them. - Tuple types as type arguments:
List((A, B)). - A Windows installer (
nexium-<version>-setup-x64.exe): license, overview, per-user or all-users, components (bundled Zig, std and examples, docs, VS Code extension), PATH and.nxfile-type tasks,nx doctoron finish. - An install script for macOS and Linux (
installers/install.sh) that verifies checksums and sets up a C compiler. nx doctor, andnxfinds a Zig bundled next to itself (NX_ZIGtoo).- Releases carry
SHA256SUMS.txt, per-file checksums, and install instructions in the notes; the tarballs include examples, std, and docs. - Recursive types through
List:enum Json { Arr(List(Json)) }. - Matching through a pointer (
match p.*) binds owning payloads by reference, somatch v.* { .Arr(items) => items.append(x) }mutates in place. *Stringand*List(T)coerce to[]u8and[]T;nullcoerces into a nested optional such as!?T.
Changed#
- Moves are tracked per branch: a value moved in one
ifbranch ormatcharm stays usable in the others. - An unbraced
iforelsebody is one statement, soif (c) x = 1works. self/lexer.nxmoves token text withowninstead of cloning it.
Fixed#
- A branch that diverges (
if (c) return x) no longer marks what it moved as moved afterwards. - Two modules each defining a type of the same name (
Parserinstd.jsonandstd.args) collided in the generated C; type names now carry their module. A user function named like a runtime identifier (fn string) no longer collides either. nx fmtspacing after-> List(T), after a closing closure bar, and between anifcondition and a parenthesized body.
[0.1.0] - 2026-09-18#
Annapurna: Miristi Khola — the gorge the 1950 expedition spent weeks finding a way through; the approach.
First public release.
Added#
- The
nxcompiler:build,run,test,check,effects,audit,ship,emit-c,parse. - Language: structs, records with
whereconstraints, enums with payloads,ref classwith reference counting andweak, distinct types, traits with default methods, generic functions and types throughcomptime T: type, closures with explicit captures, error unions withtry/catch,defer/errdefer, optionals, labeled blocks and loops, the pipe operator, binary pattern matching and construction, compile-time evaluation of constants,testblocks. - Effects:
allocates,refcounts,blocks,shared_mutable,nondeterministic,panics,ffi, inferred for every function and enforced against negative bounds, with diagnostics naming the site that introduces an effect. - Standard library builtins:
List,String,Map, slices, formatting,math,io,os,time,random,mem. - Artifacts:
cabi(static library, shared library, C header) andpython(ctypes package and wheel) from one source tree withnx ship;clibinaries. - Embedded mode: reachable mutable globals are rejected when an embeddable artifact is declared (S2); panics never cross the export boundary (S3).
- Examples under
examples/, compile-fail cases undertests/compile_fail/. dyn Traittrait objects with effect bounds,for parallelover a thread pool,using arena { }allocation scopes,comptime test.@cImport("header.h")direct C header import with variadic calls,@cstr, and vendored C throughartifact link { c_sources = [...] }.rustlibartifact: a generated Cargo crate with safe wrappers.- Tools:
nx fmt,nx doc,nx size,nx refcounts,nx leaks,nx lsp. - A conservative region check: returning a view into a local is an error.
process.run(argv) -> !i32,nx tokens, andself/lexer.nx: the first stage of the self-hosted compiler, verified against the Rust lexer incargo test.- Logo and banner under
assets/;docs/architecture.mdexplains how the compiler works. gui/: nexium-gui, an immediate-mode GUI written in Nexium (software rasterizer, bitmap font, buttons, checkboxes, sliders, text fields) over a 200-line C platform layer; Win32 window backend, offscreen rendering everywhere.artifact linkacceptslibs_windows,libs_linux,libs_macos; paths in a link artifact resolve relative to the module that declares it.- Editor support: a VS Code extension (grammar,
nx lspclient, run command, packaged as a.vsixon every release) and a Sublime Text syntax. setup-nexiumGitHub Action and a release workflow template for programs written in Nexium (docs/releasing-your-program.md).linguist/: the prepared GitHub Linguist entry, samples, heuristic, and apply script.@cImport: structs with untranslatable fields are opaque types instead of errors, soFILE*works with Apple's libc.- Native macOS builds use the system
cc(zig 0.14 cannot link against the Xcode 26 SDK);NX_CCselects the C compiler without a flag. - Translations under
docs/i18n/: the README in Spanish, Chinese, Japanese, Korean, French, and German; the language reference and architecture tour in Spanish, Chinese, and Japanese.
[Unreleased]: https://github.com/Londopy/nexium/compare/v1.0.2...HEAD [1.0.2]: https://github.com/Londopy/nexium/compare/v1.0.1...v1.0.2 [1.0.1]: https://github.com/Londopy/nexium/compare/v1.0.0...v1.0.1 [1.0.0]: https://github.com/Londopy/nexium/compare/v0.9.0...v1.0.0 [0.9.0]: https://github.com/Londopy/nexium/compare/v0.8.0...v0.9.0 [0.8.0]: https://github.com/Londopy/nexium/compare/v0.7.0...v0.8.0 [0.7.0]: https://github.com/Londopy/nexium/compare/v0.6.1...v0.7.0 [0.6.1]: https://github.com/Londopy/nexium/compare/v0.6.0...v0.6.1 [0.6.0]: https://github.com/Londopy/nexium/compare/v0.5.0...v0.6.0 [0.5.0]: https://github.com/Londopy/nexium/compare/v0.4.0...v0.5.0 [0.4.0]: https://github.com/Londopy/nexium/compare/v0.3.0...v0.4.0 [0.3.0]: https://github.com/Londopy/nexium/compare/v0.2.1...v0.3.0 [0.2.1]: https://github.com/Londopy/nexium/compare/v0.2.0...v0.2.1 [0.2.0]: https://github.com/Londopy/nexium/compare/v0.1.0...v0.2.0 [0.1.0]: https://github.com/Londopy/nexium/releases/tag/v0.1.0