Internal component. For day-to-day use, prefer the nova CLI. nova-codegen is the entry point for IDE integration, CI, direct codegen debugging, and is consumed by nova-cli as a path dependency.
Install & Build
# Debug build
cargo build --manifest-path compiler-codegen/Cargo.toml
# Release (bootstrap keeps opt-level=0, no size optimization)
cargo build --release --manifest-path compiler-codegen/Cargo.toml
# With Z3 backend for contracts (Plan 33.1)
cargo build --release --manifest-path compiler-codegen/Cargo.toml --features z3-backend
# Rust-level unit tests for the compiler
cargo test --manifest-path compiler-codegen/Cargo.toml
Output: compiler-codegen/target/{debug,release}/nova-codegen[.exe]
The main thread is spawned with a 64 MiB stack — AST traversals are mutually recursive (expr ↔ block ↔ stmt), and the type checker / SCC purity inference / SMT encoder need deep stacks on large modules. The default 1 MiB Windows stack is insufficient.
Minimal dependencies: clap, anyhow. Builds with an empty lockfile on any stable Rust 1.85+.
Exit Codes
| Code | Meaning |
|---|---|
0 | Success |
1 | Failure (parse fail, type-check fail, codegen fail, runtime fail, tests failed) |
Unlike nova, nova-codegen uses binary exit codes (0/1) — no separation between usage error and diagnostic failure.
nova-codegen check
Type-check a file without running it.
nova-codegen check FILE
Pipeline:
read_file(path)parser::parse(&src)→ ASTcheck_module_path— D78 path/module enforcementtypes::check_module→ type-check + effect inference- Prints
ok: {} parsed and checked
Errors are rendered via Diagnostic::render with line:col and source underline.
nova-codegen run
Type-check and interpret (calls fn main).
nova-codegen run FILE
Pipeline:
- parse + path-check + type-check
types::annotate_map_literals— annotate[k: v]literals with inferred K/Vdesugar::desugar_module— desugar map literals intowith_capacity+@insertinterp::Interpreter::new()→load_module→run_main
The treewalk interpreter has parity with the C backend — same effects, handlers, structured concurrency, contracts, defer, channels.
nova-codegen test
Run test "..." { ... } blocks in a file via the interpreter.
nova-codegen test FILE
Pipeline: parse → path-check → annotate_map_literals → desugar → interp::run_tests → tests: N passed, N failed.
Exit 1 if at least one test failed; prints names of failed tests.
This is the interpreter-mode test runner (fast, no C pipeline). For codegen pipeline checks use test-build or test-all.
nova-codegen compile
Compile a .nv file to .c (no linking).
nova-codegen compile FILE [-o OUTPUT] [--no-annotate-source] [--no-lint]
| Flag | Default | Description |
|---|---|---|
FILE | — | .nv file. |
-o OUTPUT | <name>.c next to source | Output .c path. |
--no-annotate-source | off (annotations enabled) | Skip /* SRC: ... */ comments — for compact output. |
--no-lint | off (lint enabled) | Disable lint passes (export-fail-untyped, etc.). |
Pipeline:
- parse + path-check + type-check
annotate_map_literals+desugar_moduletypes::infer_effects— D28 effect inference for private fnlints::lint_module(if lint enabled)CEmitter::new()+set_source_for_annotations(src)set_proven_contracts— selective contract stripping (Plan 33.3, true zero-cost)emit_module→ C source + warnings- Write to output path
SRC annotations (/* SRC: ... */) map each C line to the originating Nova source. Enabled by default; useful for .c-level debugging.
nova-codegen emit-runtime-stubs
Regenerate std/runtime/string.nv and std/runtime/math.nv from runtime_registry.rs (Plan 13).
nova-codegen emit-runtime-stubs [--root PATH] [--check]
| Flag | Default | Description |
|---|---|---|
--root PATH | . (CWD) | Repository root (containing std/runtime/). |
--check | off | Compare only; exit 1 on diff (CI / pre-commit guard). |
Workflow for adding a new runtime fn (e.g. f64.@cbrt):
- Add
RuntimeFn { ... }tosrc/codegen/runtime_registry.rs. - Implement in
nova_rt/<module>.c(or a libc wrapper). - Regenerate:
nova regen-runtime(ornova-codegen emit-runtime-stubs --root .). - Commit all three (registry +
.c+.nv).
--check normalizes line endings (CRLF → LF) before comparison. The generated files are guarded by nova regen-runtime --check in CI — do not edit by hand.
nova-codegen dump-runtime
Print the runtime-functions registry (Plan 13) for auditing registry vs actual C signatures in nova_rt/.
nova-codegen dump-runtime
Nova runtime registry: N function(s) total.
=== <module> (N fns) ===
<receiver> [mut] <.|@><name>(<params>) -> <return-ty> [c: <c-name>]
...
. = static fn (Type.fn). @ = instance method (obj.@fn).
nova-codegen test-build
Cross-platform per-file test runner (Plan 24): compile one .nv to .exe and check EXPECT markers (D89).
nova-codegen test-build FILE [--mode dev|release] [--toolchain auto|clang|msvc|gcc]
[--vcvars PATH] [--clang PATH]
[--cg-include PATH] [--rt-dir PATH] [--tmp-dir PATH]
[--display NAME] [--keep-artifacts]
[--timeout SECS] [--gc boehm|malloc]
| Flag | Default | Description |
|---|---|---|
FILE | — | .nv test file. |
--mode | dev | dev or release. |
--toolchain | auto | auto, clang, msvc, gcc. |
--vcvars PATH | auto via vswhere | Path to vcvars64.bat (Windows). |
--clang PATH | auto detect | Path to clang.exe. |
--cg-include PATH | <cwd>/compiler-codegen | Path to compiler-codegen/ for nova_rt/ includes. |
--rt-dir PATH | <cwd>/compiler-codegen/nova_rt | Runtime sources directory. |
--tmp-dir PATH | $TEMP/nova_tests or $TMPDIR/nova_tests | Tmp directory for .c/.exe/.obj. |
--display NAME | basename | Override display name in output. |
--keep-artifacts | off | Keep artifacts after run. |
--timeout SECS | 60 | Per-test timeout. |
--gc | boehm | boehm or malloc. |
Pipeline: detect toolchain → detect/build libuv → compile .nv → .c → .exe → run → check EXPECT markers.
Output format: <STATUS:14> <display> # <detail>
EXPECT markers (D89):
// EXPECT: <line>— exact stdout line match// EXPECT_STDERR: <line>— stderr match// EXPECT_COMPILE_ERROR: <substring>— must fail to compile// EXPECT_RUNTIME_ERROR: <substring>— panic with substring// REQUIRES_SMT_BACKEND— skip if SMT unavailable
nova-codegen test-all
Batch test runner (Plan 24): recursive walk of all .nv under --tests-dir. Powers nova test.
nova-codegen test-all [--tests-dir PATH] [--stdlib-dir PATH] [--include-stdlib]
[--filter SUBSTR] [--mode dev|release]
[--toolchain auto|clang|msvc|gcc]
[--vcvars PATH] [--clang PATH]
[--cg-include PATH] [--rt-dir PATH] [--tmp-dir PATH]
[--keep-artifacts] [--timeout SECS] [--jobs N]
[--format text|json|tap] [-v|-q]
[--results-file PATH] [--rerun-failed]
[--retries N] [--gc boehm|malloc]
| Flag | Default | Description |
|---|---|---|
--tests-dir PATH | nova_tests | Test corpus root. |
--stdlib-dir PATH | std | std/ root (if --include-stdlib). |
--include-stdlib | off | Include std/* files. |
--filter SUBSTR | — | Filter by display name substring. |
--mode | dev | See test-build. |
--toolchain | auto | |
--vcvars, --clang | auto | |
--cg-include, --rt-dir | derived from CWD | |
--tmp-dir | $TEMP/nova_tests or equivalent | |
--keep-artifacts | off | |
--timeout | 60 | Per-test timeout. |
--jobs N | 0 (= num_cpus) | Parallel workers. |
--format | text | text, json, tap. |
-v, --verbose | off | Output for PASS tests. |
-q, --quiet | off | FAIL + summary only. |
--results-file PATH | — | Write last-results.json. |
--rerun-failed | off | Re-run only failed/timeout from results file. |
--retries N | 0 | Retry on transient AV/race failures (CI default 2). |
--gc | boehm | See test-build. |
Informational messages go to stderr (toolchain, libuv status). Per-test events and summary go to stdout.
Note: --list, --filter-from, --shuffle, --skip, --mono-depth are only supported via nova test.
Environment Variables
| Variable | Effect |
|---|---|
NOVA_NO_CACHE=1 | Disable SMT contracts cache (Plan 33.3). |
NOVA_PERF_TIMER=1 | Enable __PERF__ markers in compiler (per-pass timing). |
NOVA_MONO_DEPTH=N | Monomorphization-instantiation depth limit (default 500). |
NOVA_DEBUG_MONO=1 | Verbose debug print of mono instances. |
NOVA_SMT_BACKEND=trivial|z3 | Override the SMT backend for contracts. |
NOVA_CACHE_DIR=PATH | Override the SMT proof cache directory (default <cwd>/target/). |
NOVA_CLANG=PATH | Override clang.exe for test-build/test-all. |
NOVA_GCC=PATH | Override gcc path. |
NOVA_VCVARS=PATH | Override vcvars64.bat (Windows MSVC). |
NOVA_MARCH_NATIVE=1 | Enable -march=native for release builds (non-portable). |
NOVA_GC_LIB_DIR=PATH | Override libgc.a / gc.lib directory (Boehm). |
NOVA_GC_INCLUDE_DIR=PATH | Override include path for gc.h. |
VCPKG_ROOT=PATH | vcpkg root (for libuv / libz3 auto-resolve). |
CC=name | Fallback C compiler (POSIX). |
NOVA_VERSION=N.M.K | Current version for deprecation diagnostics (Plan 45). |
NOVA_FEATURES=f1,f2 | Cfg feature set (Plan 42.12). |
NOVA_TARGET_OS=name | Override target_os for cfg resolve. |
TEMP (Windows) | Tmp directory for build/test artifacts. |
TMPDIR (Unix) | Same. |
PATH | Used to locate clang/gcc/cl/vswhere. |
ProgramFiles(x86) | Used to find vswhere for MSVC auto-detect. |
Cargo Features
| Feature | Description |
|---|---|
| (default) | TrivialBackend SMT (reflexive ensures), no external dependencies. |
z3-backend | Links libz3 via vcpkg (Plan 33.1). FFI bindings are in-tree in src/verify/backend/z3_ffi.rs — no z3/z3-sys crates. Linkage controlled by build.rs + vcpkg.json. |
# Build with Z3:
cargo build --release --features z3-backend
# Run tests with Z3:
NOVA_SMT_BACKEND=z3 nova test
Library API (nova_codegen)
nova-cli uses nova-codegen as a path dependency and consumes the library API directly (no subprocess). Public modules from lib.rs:
| Module | What |
|---|---|
argbind | Named/positional arg binding (Plan 46 / D102). |
ast | AST types: Module, Item, Expr, Stmt, Pattern, … |
callnorm | Call-site normalization for named params. |
codegen | C backend: CEmitter::emit_module, runtime_registry::all, … |
desugar | Desugars map literals, for-in, and other syntax sugar. |
diag | Structured diagnostics (Diagnostic, Span, byte_to_line_col). |
doc | Plan 45 — DocModel, renderers, MCP server. |
imports | Plan 35 R31 — cross-file resolver (resolve_imports_inline). |
interp | Treewalk interpreter (Interpreter::new / load_module / run_main / run_tests). |
lexer | Tokenization: lex(&src) -> Vec<Token>. |
lints | D-rule based lints (lint_module). |
manifest | nova.toml + D78 path/module enforcement. |
parser | Recursive-descent parser (parse(&src) -> Result<Module, Diagnostic>). |
perf_timer | NOVA_PERF_TIMER instrumentation. |
test_runner | Test discovery + parallel execution + toolchain detect. |
types | Type-checker + effect inference (check_module, infer_effects, annotate_map_literals). |
verify | SMT contracts integration (TrivialBackend, Z3Backend under feature). |
Re-exports: Diagnostic, Span directly from nova_codegen::*.
Internal Architecture
src/
lexer/ tokenization
parser/ recursive-descent parser
ast/ AST types
types/ type checker + effect inference + lints
interp/ treewalk interpreter
codegen/ C backend
emit_c.rs main codegen (~20k LOC)
runtime_registry.rs source-of-truth for std/runtime/*.nv stubs
imports.rs Plan 35 R31: cross-file resolver
diag/ structured diagnostics (with FileId for cross-file)
manifest.rs nova.toml + D78 path enforcement
lints.rs D-rule based lints
test_runner.rs test discovery + parallel execution
verify/ contracts SMT (TrivialBackend + Z3 optional)
doc/ Plan 45 nova doc (parser, renderer, MCP)
desugar.rs desugaring passes
argbind.rs named-params binding (Plan 46)
callnorm.rs call-site normalization
perf_timer.rs NOVA_PERF_TIMER markers
lib.rs re-exports
main.rs CLI dispatch (~664 LOC)
Cross-file Resolver (Plan 35 R31)
imports::resolve_imports_inline — shared between nova check, nova build, nova test:
- DFS with cycle detection (
in_progress+visitedsets). - Selective import
X.{A, B}(syntax; bootstrap MVP does not yet enforce the filter). export import X.{A}re-export.- Auto-import of
std/prelude.nv(R27).
Folder-modules (Plan 42)
A module can be a single-file X.nv or a folder X/ with peers (Go-style):
manifest::resolve_module_paths→Vec<PathBuf>, alphabetical sort for deterministic builds.*_test.nvpeers are filtered when!include_test_peers.X.nv+X/coexisting with a direct.nv→ ambiguous error.internal/<…>path protection — import only from parent's descendants._module.nvconvention for module-level#forbid/#cfg/#doc.
D78 Path/Module Enforcement
If a file lives inside a package (nova.toml in parent dirs), the compiler verifies that module X.Y.Z matches the filesystem path. Standalone .nv files without nova.toml pass through.
Effect Inference (D28)
types::infer_effects injects Fail into the effect row of private functions if the body contains throw and Fail is not declared explicitly. Public functions — explicit only (D28: "public API must not implicitly throw").
Plan 33.3 Contract Stripping (Ф.9.9)
CEmitter::set_proven_contracts selectively strips proven contract bodies in codegen (true zero-cost, even in debug). SMT-proven requires/ensures/invariant are not emitted as runtime assertions.
Runtime (nova_rt/)
C sources linked into every .exe. Minimum 3 .c files are always linked:
| File | What |
|---|---|
alloc.c (or alloc_boehm.c, alloc_rc.c) | Allocator (Boehm GC default since Plan 27). |
effects.c | Handler stack (D61), nova_interrupt / nova_interrupt_ptr. |
fibers.c | Shim over minicoro.h + structured concurrency (Plan 44.5). |
Header-only:
| Header | What |
|---|---|
array.h | NovaArray_<T>, NovaOpt_<T> auto-gen helpers. |
cast.h | D54 narrow casts (saturation, wrap-around semantics). |
effects.h | NovaThrowKind, nova_throw_cancel, Handler[E, IRT] API. |
fibers.h | nova_spawn, nova_supervised, nova_cancel_*, M:N runtime. |
channels.h | Channel[T] mpsc (Plan 44.1), select waiter. |
sync.h | C11 atomics + mutex for channel hardening. |
minicoro.h | Vendored stackful coroutines (version-pinned, do not patch). |
nova_rt.h | Single include — nova_str_cmp/lt/le/gt/ge byte-wise compare, etc. |
Build scripts in compiler-codegen/: build_c.bat / build_c.ps1 / build_c.sh, vcpkg.json / vcpkg_installed/ (vendored libuv + libz3), build.rs (feature-flag wiring).