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")
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.
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.
Related
- source.md for generated module syntax.
- metaprogramming.md for practical usage.
- tooling.md for
ny fmt --metaprog.