Compile-Time Evaluation
Vex currently splits comptime features into two groups:
#name(...)style compile-time functions and queries$if,$elif,$else,$for,$while, and$constcompile-time control-flow forms
That distinction matters. Earlier docs blurred # and $; the current compiler does not.
Prefix Model
Use this rule of thumb:
- Prefer
#...for reflection, diagnostics, embedding, compile-time math, and bit helpers - Use
$if,$for,$while, and$constfor compile-time branching and expansion - Layout helpers accept both historical
$sizeofand#sizeofstyle spellings, plus the same pattern for alignment
Layout and Reflection
let size_a = #sizeof<i64>()
let size_b = $sizeof<i64>()
let align_a = #alignof<f64>()
let align_b = $alignof<f64>()
let ty_name = $typeName<Vec<i32>>()
let field_count = #fieldCount<User>()
let variant_count = #variantCount<Result<i32, string>>()The compiler also supports field and variant reflection helpers such as:
#fieldNames<T>()#hasField<T>(name)#fieldType<T>(name)#fieldTag<T>(field, key)#hasFieldTag<T>(field, key)#fieldTags<T>(field)#variantNames<E>()#hasVariant<E>(name)#variantDiscriminant<E>(name)#variantHasPayload<E>(name)#variantPayload<E>(name)
#typeInfo<T>()
#typeInfo<T>() is the current structured reflection entry point.
struct User {
id: i32 `json:"user_id" db:"primary_key"`,
name: string `json:"username"`,
}
fn dump_user_fields(u: User) {
$for f in #typeInfo<User>().fields {
$println("field: ", f.name, " type: ", f.type_name)
$for t in f.tags {
$println(" tag ", t.key, " = ", t.value)
}
let value = #getField(u, f)
$println(" value = ", value)
}
}Inside a $for f in #typeInfo<T>().fields loop, the current compiler also supports #setField:
fn rewrite_all_fields() {
let! p = Point { x: 1, y: 2 }
$for f in #typeInfo<Point>().fields {
#setField(p, f, 99)
}
}Compile-Time Control Flow
The parser and codegen currently support all of the following forms:
$if #fieldCount<User>() > 0 {
#warning("User has fields")
} $elif #fieldCount<User>() == 0 {
#warning("User is empty")
} $else {
#compileError("unreachable comptime branch")
}
$for f in #typeInfo<User>().fields {
$println(f.name)
}
$while condition {
break
}
let value = $const {
2 + 2
}$while is part of the language surface today even though older docs barely mentioned it.
Diagnostics and Embedding
#staticAssert(#fieldCount<User>() == 2, "User shape changed")
#warning("legacy path still compiled")
#debugExpr(5 + 3)
let home = #env("HOME")
let config = #includeStr("config.json")
let raw = #includeBytes("blob.bin")
let source = #stringify(User { id: 1, name: "A" })
let ident = #concatIdents(foo, bar)The primary diagnostics and meta helpers currently documented by the compiler are:
#staticAssert#compileError#warning#debugExpr#env#includeStr#includeBytes#concat#stringify#concatIdents
Compile-Time Math and Bit Operations
The current implementation accepts both canonical names and several compatibility aliases.
let pow = #constPow(2, 10)
let abs = #abs(-42)
let min = #min(3, 5)
let max = #max(3, 5)
let clamp = #clamp(15, 0, 10)
let sqrt = #constSqrt(144)
let gcd = #gcd(48, 18)
let lcm = #lcm(4, 6)
let log2 = #log2(256)
let next = #nextPowerOf2(5)
let pop = #bitCount(0b1010101)
let clz = #leadingZeros(16)
let ctz = #trailingZeros(16)
let swapped = #bswap(0x12345678)Accepted aliases in the current compiler include:
#constAbsfor#abs#constMinfor#min#constMaxfor#max#constClampfor#clamp#constLog2for#log2#nextPow2for#nextPowerOf2#popcountfor#bitCount#clzfor#leadingZeros#ctzfor#trailingZeros#reverseBytesfor#bswap
Forcing Evaluation
let a = #eval(10 + 20)
let b = #constEval(50 + 50)#eval evaluates a comptime-capable expression. #constEval is the strict form and produces an error when the expression cannot be folded at compile time.
Defaults and Zeroed Values
let default_i32 = #default<i32>()
let zeroed_user = #zeroed<User>()These helpers exist today and are useful in low-level or generated code, but they should still be used with the same care you would apply to raw initialization elsewhere in the language.
Practical Guidance
- Prefer
#typeInfo<T>()when you need structured field iteration instead of parsing#fieldNames<T>()strings yourself. - Prefer
#staticAssertover comment-based assumptions. - Treat compatibility aliases as compatibility aliases; document the primary spelling you actually want other code to use.
- Keep
$forand$ifbodies simple. They are easiest to reason about when they expand straightforward source.