Compile time
Compile-time forms run during compilation and produce tables, matches,
templates, emitted declarations, and generated modules.
Compile-time blocks
comptime { body }
Rules:
- Runs during compilation.
- Returns a value embedded in the program.
- Uses the same resolved imports and aliases as the surrounding source unit.
- Emits only the standard-library helpers reached by the block.
- Can reuse earlier immutable compile-time constants.
- Does not run unrelated top-level user code.
- Does not capture runtime globals.
use std.core
def xs = comptime{ range(4).map(fn(i){ i + 1 }) }
assert_eq(to_str(xs), "[1, 2, 3, 4]", "comptime imports")
use std
def base = comptime{ 2^5 }
def xs = comptime{ range(4).map(fn(i){ i + base }) }
For runtime data, put the value inside the block or bind it first as an
immutable compile-time value.
return expr or a final value-producing expression sets the value of a
compile-time block. A block with only declarations or non-value statements
evaluates to nil.
Tables
comptime table Name {
pattern -> value
_ -> fallback
}
Compile-time tables make static dispatch visible. Use them for lookup
surfaces, generated classifier logic, and branch-free native output.
The compiler also emits a compatibility helper named from the table. For
comptime table KeyMap, call _key_map(key) or _key_map(key, fallback).
Prefer comptime match KeyMap(key, fallback) in new code.
Match helpers
comptime match Name(key, fallback)
comptime match queries a compile-time table by key and returns a fallback
when no arm matches.
Templates
comptime template name(args) {
declarations
}
Templates are hygienic AST templates. Template bodies emit declarations, not
text pasted into the source file.
Emit
comptime emit name(args)
for axis in comptime ["x", "y", "z"] {
emit make_axis_family(axis)
}
emit inserts generated declarations from a template or generator.
Compile-time for iterates a compile-time list and lets the body emit
declarations for each value.
Reflection loops
comptime fields(LayoutName) as f {
emit assert(__layout_offset("LayoutName", f.name) == f.offset, "field")
}
comptime exports(ModuleName) as name {
emit assert(name != "", "export name")
}
fields exposes f.name, f.offset, f.index, and f.type for each layout
field. exports exposes each exported module name as a string.
Diagnostic rules
comptime diagnostic rule bad_layout_store {
when call.name == "store_layout" && !is_literal(call.arg(1))
error "store_layout needs a string literal layout name"
fix "use store_layout(dst, \"LayoutName\", ...)"
}
Diagnostic rules let compile-time code reject a known bad call pattern. The
current rule surface supports call predicates such as call.name,
call.arg(N), and helpers such as is_literal.
Compile-time proofs
Compile-time assertions move safety checks into compilation. The proof type
in types.md provides the carrier for dependent and refinement
facts backed by the same engine.
assert_compile((4 * 11) == 44, "folded arithmetic")
static_assert((3 * 7) == 21, "folded arithmetic")
assert_compile_range(i, 0, 3, "loop index range")
assert_compile_index(xs, i, "list index bounds")
def proof arithmetic = prove((4 * 11) == 44, "arithmetic witness")
static_assert and assert_compile fail compilation when the condition is
known false.
assert_compile_range requires the compiler to prove an integer is within a
closed range. assert_compile_index requires the compiler to prove that an
index is in bounds for the container. range_proven(value, lo, hi) and
index_proven(container, index) expose the same proof engine as compile-time
booleans.
prove(condition[, message]) is the only implicit-free constructor for the
proof type. It rejects false obligations and obligations whose truth is not
known during compilation. A proof has no inspectable payload and currently
witnesses construction success rather than carrying a proposition-indexed
proof term.
Use these checks at safety boundaries: parser tables, byte decoders, crypto
code, native buffers, and loops where an out-of-range value would break memory
or correctness.
--safe-mode uses the same proof engine for compiler-tracked raw memory
accesses. If an allocation size is known, load8/store8 and wider raw
loads/stores require a byte offset proven to stay inside the allocation.
See types.md for proof type, dependent params, and refinement.
Generated modules
module pkg.generated generated from Spec {
native_prefix = "x"
emit make_backend(Contract)
}
Generated modules attach compile-time configuration to a module and emit the
declarations that become part of the module surface.
Boundaries
Compile-time work emits declarations. Generated names stay stable, and emitted
public surfaces stay visible.
Platform selection
Platform guards select source at compile time:
#linux { os_tag = "linux" }
#elif macos { os_tag = "macos" }
#elif windows { os_tag = "windows" }
#else { os_tag = "other" }
#endif
#if(arch() == "x86_64"){ return 64 }
Guard names include OS families and CPU families. comptime{ ... } can call
compile-time facts such as arch(), os(), __os_name(), and __main().
Embedded files
def text = embed("etc/tests/rt/embed.ny")
embed(path) reads a file into the compiled program. Paths are resolved from
the source checkout or active source root used by the compiler.
Syntax extension registry
std.core.syntax exposes the runtime/comptime syntax registry used by the
extension tests:
use std.core.syntax as syntax
mut reg = syntax.new_registry()
reg = syntax.register_macro_in(reg, "double", handler)
syntax.expand_macro_in(reg, "double", [21])
The surface covers process-wide and local registries, macro handlers,
attribute handlers, form construction, deep/fixpoint expansion, registry
clone/merge operations, and deterministic rewrite passes. Missing macro
handlers return nil; missing attribute handlers return the original node.
Result ownership
Heap results cross the comptime boundary by value. Strings, lists, tuples,
ranges, and dictionaries are reconstructed in the receiving program before the
temporary evaluator is destroyed; pointers into evaluator-owned storage are
never exposed. Nested containers are supported with bounded depth and size.
Invalid intermediate or generated evaluator modules are rejected before JIT
execution.
Related
- source.md for generated module syntax.
- metaprogramming.md for practical usage.
- tooling.md for
ny fmt --metaprog.