Macros & Compile-Time Evaluation
Overview
Nitpick provides three mechanisms for compile-time code generation and evaluation:
- AST macros — Template-based code generation invoked with
name!(args) - Compile-time evaluation —
comptime(expr)for constant folding at compile time - Derive macros — Auto-generate trait implementations (see Derive Macros)
AST Macros
Declaration
Macros use macro:name = (params) { body; }; syntax, consistent with func:name:
macro:double_it = (x) {
x + x;
};
Invocation
Invoke a macro with name!(args):
func:main = int32() {
int32:val = double_it!(5i32);
// val == 10 (expanded to: 5i32 + 5i32)
exit val;
};
func:failsafe = int32(tbb32:err) {
exit 1;
};
Multiple Parameters
macro:add3 = (a, b, c) {
a + b + c;
};
func:main = int32() {
int32:result = add3!(1i32, 2i32, 3i32);
// result == 6
exit result;
};
func:failsafe = int32(tbb32:err) {
exit 1;
};
How It Works
- The parser sees
macro:name = (params) { body; };and stores the AST template - When
name!(args)is encountered, arguments are substituted into the template - Expansion happens at parse time — the macro body becomes inline code
- Type checking runs on the expanded code, not the macro definition
Key Differences from C Macros
| Feature | C Preprocessor | Nitpick Macros |
|---|---|---|
| Expansion level | Text substitution | AST-level substitution |
| Type safety | None (text) | Full (post-expansion type check) |
| Hygiene | None | Scoped to expansion site |
# operator |
Stringify | Pin (borrow checker) |
## operator |
Token paste | Does not exist |
| Syntax | #define NAME(x) |
macro:name = (x) { }; |
Important: Nitpick's
#operator is the pin operator for the borrow checker, NOT a stringify operator.
Compile-Time Evaluation
comptime Expression
Evaluate an expression at compile time:
func:main = int32() {
int32:size = comptime(4 * 1024);
// size == 4096, computed at compile time
exit 0;
};
func:failsafe = int32(tbb32:err) {
exit 1;
};
comptime Function
Mark an entire function for compile-time evaluation:
comptime func:factorial = int64(int64:n) {
if (n <= 1i64) {
pass 1i64;
}
pass n * raw factorial(n - 1i64);
};
func:main = int32() {
int64:val = comptime(raw factorial(10i64));
// val == 3628800, computed entirely at compile time
exit 0;
};
func:failsafe = int32(tbb32:err) {
exit 1;
};
pub comptime Function
Export a compile-time function for use in other modules:
pub comptime func:align_up = int64(int64:val, int64:align) {
pass ((val + align - 1i64) / align) * align;
};
comptime Block
Execute a block of statements at compile time:
func:main = int32() {
comptime {
// All statements here execute at compile time
int32:x = 10;
int32:y = x * 2;
};
exit 0;
};
func:failsafe = int32(tbb32:err) {
exit 1;
};
Derive Macros
Auto-generate trait implementations for structs or enums using #[derive(...)]:
#[derive(Eq, Ord, Clone, ToString, Debug, Hash)]
struct:Point = {
int32:x;
int32:y;
};
func:main = int32() {
Point:a = Point{ x: 1, y: 2 };
Point:b = Point{ x: 3, y: 4 };
bool:equal = raw a.eq(b);
string:repr = raw a.to_string();
exit 0;
};
func:failsafe = int32(tbb32:err) {
exit 1;
};
derive also works on enums (plain and tagged):
#[derive(Display)]
enum:Msg = { Hello(int32) }; // tagged enum
#[derive(Eq)]
enum:Season = { Spring, Summer, Autumn, Winter };
Msg:m = Msg.Hello(42i32);
string:s = `&{m}`; // "Hello(42)"
Season:a = Season.Summer;
Season:b = Season.Winter;
bool:same = raw a.eq(b); // false — different discriminants
For tagged enums, derive(Eq) compares discriminants only (payload not included).
Available Derive Traits
| Trait | Method | Applies to | Description |
|---|---|---|---|
Eq |
eq(other) |
struct, enum | Field-by-field equality (struct); discriminant equality (enum) |
Ord |
less_than(other) |
struct | Lexicographic comparison |
Clone |
clone() |
struct | Shallow copy |
ToString |
to_string() |
struct | String representation |
Display |
(template literal) | struct, enum | Human-readable display — "Variant(payload)" for tagged enums |
Debug |
debug() |
struct | Debug output with type info |
Hash |
hash() |
struct | FNV-1a hash |
Derived methods return
Result<T>— userawto unwrap the value.
Attribute Macros
Attributes modify declarations at compile time:
#[inline]
func:hot_path = int32(int32:x) {
pass x * 2;
};
#[noinline]
func:cold_path = int32(int32:x) {
pass x / 2;
};
#[align(64)]
struct:CacheLine = {
int64:data;
};
Available Attributes
| Attribute | Target | Description |
|---|---|---|
#[inline] |
Functions | Suggest inlining |
#[noinline] |
Functions | Prevent inlining |
#[align(N)] |
Structs/Vars | Set byte alignment |
#[derive(...)] |
Structs | Generate trait impls |
#[gpu_kernel] |
Functions | Mark as GPU kernel |
#[gpu_device] |
Functions | GPU device function |
#[comptime] |
Functions | Compile-time evaluation |
See Also
- MACRO_AUTHORING_GUIDE.md — Full derive and attribute reference
- Traits — Trait system documentation
- Generics — Generic functions and turbofish