Contributing to Nexium

Thanks for taking a look. This document covers how to build, test, and propose changes.

Start with docs/architecture.md for a map of the compiler.

Building#

sh bootstrap/build.sh              # the compiler in Nexium from the C seed, no Rust: nx-out/bootstrap/nx2
nx-out/bootstrap/nx2 run tests/run.nx   # every suite through it (the harness is a Nexium program)
nx-out/bootstrap/nx2 run tests/run.nx -- spec fmt   # only these suites

nx needs a C compiler. It looks for zig on your PATH and uses zig cc; pass --cc clang (or --cc gcc) to use another one. The harness rebuilds the compiler when anything under self/, std/, runtime/ or the seed is newer than it, and skips the parts of the ship suite whose tools (cargo, npm) are not installed.

Layout#

pathwhat lives there
self/lexer.nx, self/parser.nxsyntax
self/check.nx, self/cimport.nxname resolution, type checking, monomorphization, effects, the compile-time interpreter, C header import
self/cgen.nxthe C backend
self/nx.nxthe nx driver: build, run, test, check, emit-c, tir
self/fmt.nx, self/doc.nx, self/tools.nx, self/size.nx, self/manifest.nx, self/ship.nx, self/lsp.nx, self/repl.nxthe tools: formatter, docs, reports, packages, ship, the language server, the REPL
bootstrap/the C seed nx.c and the build scripts
runtime/nx_rt.hthe C runtime, embedded into generated code
examples/programs with .expected output, run by the tests
tests/run.nxthe test harness
tests/spec/the specification's conformance cases, one per claim, with recorded output
tests/compile_fail/programs that must be rejected, with // EXPECT: lines

Adding a test#

Where the compiler is#

The compiler is self/ (lexer, parser, checker, C emitter, driver, the tools), written in Nexium; that is where every change goes. A machine that has no nx builds one from bootstrap/nx.c, the C the compiler emits for itself (decision 90). A change to self/*.nx may implement a new feature but may not use it until the seed knows it: regenerate the seed in the same change (nx emit-c self/nx.nx --mode safe > bootstrap/nx.c) when self/ needs a builtin or a form the seed lacks.

Language changes#

The specification is the authority. A proposal to change the language must name which hard constraint in section 3 of nexium-spec.txt it serves, list the alternatives considered, and state the migration cost (spec 17.6). Open an issue with the language label before writing code; small compiler fixes do not need a proposal.

Decisions already taken where the specification was silent are recorded in DECISIONS.md. If you disagree with one, open an issue that references its number.

Known issues#

Open bugs and limitations live in KNOWN_ISSUES.md, each with a repro and the likely fix. Add what you find there; when you fix one, remove its entry, add a Fixed line to the changelog, and land a regression test with it.

Changelog#

CHANGELOG.md follows Keep a Changelog and is validated in CI with patchnotes. Add a line under Unreleased in the right category (Added, Changed, Deprecated, Removed, Fixed, Security) with your change.

Style#

Pull requests#

Keep them focused. Describe what changed and why, link the issue, and make sure nx run tests/run.nx passes on your machine. CI runs on Windows, Linux, and macOS.

Every pull request has to pass, before it can merge:

When every required check has passed, the pull request gets the ready to merge label (and loses it when a new push restarts the checks). The maintainer reviews every change (.github/CODEOWNERS); main only takes what these checks have passed.

AI-assisted contributions#

You may use AI tools to help write a contribution. Three rules, which the CLA makes binding:

An undisclosed AI-assisted contribution is closed when discovered, whatever its quality. Disclosed ones are reviewed like any other: the tests are the bar.

Contributor License Agreement#

Your first pull request gets a comment from the CLA check asking you to accept CLA.md by replying with one sentence. You keep the copyright in your work; the agreement gives the project a license to distribute it under the MIT License (or another OSI-approved license, never a proprietary one) and a patent license for what your contribution necessarily uses, and it records that the work is yours to give, AI-assisted or not. Companies contributing on behalf of employees can open an issue naming the covered accounts instead.

What runs on a push#

The CI workflow (the bootstrap and every harness suite on three platforms, the fuzzer, the sanitizers, the editor files, the lint) skips a push or pull request that changes only prose and pictures (*.md, docs/i18n/, topo/img/, assets/, linguist/). CI (prose) runs instead: it validates CHANGELOG.md and reports the other required checks as nothing to build, so a documentation pull request can merge; Pages builds and deploys the site from the Markdown. A change to a .nx, .c, .h or workflow file runs everything. [skip ci] in a commit message skips every workflow, the site's deploy included, so it is for the rare push that should not be published at all; GitHub reads the marker anywhere in the message, so a message that merely mentions it skips too (spell it out, "the skip marker", when writing about it).

Releasing#

  1. Move the Unreleased entries in CHANGELOG.md under a new ## [x.y.z] - YYYY-MM-DD heading and add its compare link at the bottom. patchnotes validate CHANGELOG.md must pass.
  2. Set the same version and release name in self/nx.nx (VERSION, RELEASE_NAME) and regenerate the seed: nx emit-c self/nx.nx --mode safe > bootstrap/nx.c.
  3. Tag and push: git tag vx.y.z && git push origin vx.y.z. The release workflow refuses a tag that does not match self/nx.nx, builds nx from the seed for Windows, Linux, and macOS, and publishes a GitHub release with the changelog section as its notes.