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

CodeMeaning
0Success
1Failure (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:

  1. read_file(path)
  2. parser::parse(&src) → AST
  3. check_module_path — D78 path/module enforcement
  4. types::check_module → type-check + effect inference
  5. 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:

  1. parse + path-check + type-check
  2. types::annotate_map_literals — annotate [k: v] literals with inferred K/V
  3. desugar::desugar_module — desugar map literals into with_capacity + @insert
  4. interp::Interpreter::new()load_modulerun_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_teststests: 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]
FlagDefaultDescription
FILE.nv file.
-o OUTPUT<name>.c next to sourceOutput .c path.
--no-annotate-sourceoff (annotations enabled)Skip /* SRC: ... */ comments — for compact output.
--no-lintoff (lint enabled)Disable lint passes (export-fail-untyped, etc.).

Pipeline:

  1. parse + path-check + type-check
  2. annotate_map_literals + desugar_module
  3. types::infer_effects — D28 effect inference for private fn
  4. lints::lint_module (if lint enabled)
  5. CEmitter::new() + set_source_for_annotations(src)
  6. set_proven_contracts — selective contract stripping (Plan 33.3, true zero-cost)
  7. emit_module → C source + warnings
  8. 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]
FlagDefaultDescription
--root PATH. (CWD)Repository root (containing std/runtime/).
--checkoffCompare only; exit 1 on diff (CI / pre-commit guard).

Workflow for adding a new runtime fn (e.g. f64.@cbrt):

  1. Add RuntimeFn { ... } to src/codegen/runtime_registry.rs.
  2. Implement in nova_rt/<module>.c (or a libc wrapper).
  3. Regenerate: nova regen-runtime (or nova-codegen emit-runtime-stubs --root .).
  4. 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]
FlagDefaultDescription
FILE.nv test file.
--modedevdev or release.
--toolchainautoauto, clang, msvc, gcc.
--vcvars PATHauto via vswherePath to vcvars64.bat (Windows).
--clang PATHauto detectPath to clang.exe.
--cg-include PATH<cwd>/compiler-codegenPath to compiler-codegen/ for nova_rt/ includes.
--rt-dir PATH<cwd>/compiler-codegen/nova_rtRuntime sources directory.
--tmp-dir PATH$TEMP/nova_tests or $TMPDIR/nova_testsTmp directory for .c/.exe/.obj.
--display NAMEbasenameOverride display name in output.
--keep-artifactsoffKeep artifacts after run.
--timeout SECS60Per-test timeout.
--gcboehmboehm 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]
FlagDefaultDescription
--tests-dir PATHnova_testsTest corpus root.
--stdlib-dir PATHstdstd/ root (if --include-stdlib).
--include-stdliboffInclude std/* files.
--filter SUBSTRFilter by display name substring.
--modedevSee test-build.
--toolchainauto
--vcvars, --clangauto
--cg-include, --rt-dirderived from CWD
--tmp-dir$TEMP/nova_tests or equivalent
--keep-artifactsoff
--timeout60Per-test timeout.
--jobs N0 (= num_cpus)Parallel workers.
--formattexttext, json, tap.
-v, --verboseoffOutput for PASS tests.
-q, --quietoffFAIL + summary only.
--results-file PATHWrite last-results.json.
--rerun-failedoffRe-run only failed/timeout from results file.
--retries N0Retry on transient AV/race failures (CI default 2).
--gcboehmSee 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

VariableEffect
NOVA_NO_CACHE=1Disable SMT contracts cache (Plan 33.3).
NOVA_PERF_TIMER=1Enable __PERF__ markers in compiler (per-pass timing).
NOVA_MONO_DEPTH=NMonomorphization-instantiation depth limit (default 500).
NOVA_DEBUG_MONO=1Verbose debug print of mono instances.
NOVA_SMT_BACKEND=trivial|z3Override the SMT backend for contracts.
NOVA_CACHE_DIR=PATHOverride the SMT proof cache directory (default <cwd>/target/).
NOVA_CLANG=PATHOverride clang.exe for test-build/test-all.
NOVA_GCC=PATHOverride gcc path.
NOVA_VCVARS=PATHOverride vcvars64.bat (Windows MSVC).
NOVA_MARCH_NATIVE=1Enable -march=native for release builds (non-portable).
NOVA_GC_LIB_DIR=PATHOverride libgc.a / gc.lib directory (Boehm).
NOVA_GC_INCLUDE_DIR=PATHOverride include path for gc.h.
VCPKG_ROOT=PATHvcpkg root (for libuv / libz3 auto-resolve).
CC=nameFallback C compiler (POSIX).
NOVA_VERSION=N.M.KCurrent version for deprecation diagnostics (Plan 45).
NOVA_FEATURES=f1,f2Cfg feature set (Plan 42.12).
NOVA_TARGET_OS=nameOverride target_os for cfg resolve.
TEMP (Windows)Tmp directory for build/test artifacts.
TMPDIR (Unix)Same.
PATHUsed to locate clang/gcc/cl/vswhere.
ProgramFiles(x86)Used to find vswhere for MSVC auto-detect.

Cargo Features

FeatureDescription
(default)TrivialBackend SMT (reflexive ensures), no external dependencies.
z3-backendLinks 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:

ModuleWhat
argbindNamed/positional arg binding (Plan 46 / D102).
astAST types: Module, Item, Expr, Stmt, Pattern, …
callnormCall-site normalization for named params.
codegenC backend: CEmitter::emit_module, runtime_registry::all, …
desugarDesugars map literals, for-in, and other syntax sugar.
diagStructured diagnostics (Diagnostic, Span, byte_to_line_col).
docPlan 45 — DocModel, renderers, MCP server.
importsPlan 35 R31 — cross-file resolver (resolve_imports_inline).
interpTreewalk interpreter (Interpreter::new / load_module / run_main / run_tests).
lexerTokenization: lex(&src) -> Vec<Token>.
lintsD-rule based lints (lint_module).
manifestnova.toml + D78 path/module enforcement.
parserRecursive-descent parser (parse(&src) -> Result<Module, Diagnostic>).
perf_timerNOVA_PERF_TIMER instrumentation.
test_runnerTest discovery + parallel execution + toolchain detect.
typesType-checker + effect inference (check_module, infer_effects, annotate_map_literals).
verifySMT 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 + visited sets).
  • 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_pathsVec<PathBuf>, alphabetical sort for deterministic builds.
  • *_test.nv peers 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.nv convention 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:

FileWhat
alloc.c (or alloc_boehm.c, alloc_rc.c)Allocator (Boehm GC default since Plan 27).
effects.cHandler stack (D61), nova_interrupt / nova_interrupt_ptr.
fibers.cShim over minicoro.h + structured concurrency (Plan 44.5).

Header-only:

HeaderWhat
array.hNovaArray_<T>, NovaOpt_<T> auto-gen helpers.
cast.hD54 narrow casts (saturation, wrap-around semantics).
effects.hNovaThrowKind, nova_throw_cancel, Handler[E, IRT] API.
fibers.hnova_spawn, nova_supervised, nova_cancel_*, M:N runtime.
channels.hChannel[T] mpsc (Plan 44.1), select waiter.
sync.hC11 atomics + mutex for channel hardening.
minicoro.hVendored stackful coroutines (version-pinned, do not patch).
nova_rt.hSingle 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).