Compile-Time Expressions

A compile-time expression is an expression marked with the comptime keyword that MUST be fully evaluated at compile time.

comptime_expr = "comptime" "{" expression "}" ;

The expression inside a comptime block is evaluated during compilation. The following operations are supported within comptime blocks:

  • Integer literals
  • Boolean literals (true, false)
  • Arithmetic operators (+, -, *, /, %)
  • Comparison operators (==, !=, <, <=, >, >=)
  • Logical operators (&&, ||, !)
  • Bitwise operators (&, |, ^, <<, >>)

A comptime expression can be used anywhere an expression is expected. The result of the comptime evaluation replaces the comptime block.

fn main() -> i32 {
    let x: i32 = comptime { 21 * 2 };
    x
}

Comptime Restrictions

It is a compile-time error if an expression inside a comptime block cannot be evaluated at compile time. This includes:

  • References to runtime variables
  • Function calls (except to comptime-evaluable functions in future versions)
  • Operations that would panic at runtime
fn main() -> i32 {
    let x = 10;
    comptime { x + 1 }  // ERROR: x cannot be known at compile time
}

Comptime Parameters

Function parameters can be marked with comptime, requiring the caller to provide a compile-time known value. The parameter's value is available as a compile-time constant within the function body.

parameter = [ "comptime" ] IDENT ":" type ;

Comptime parameters can have any type, including the special type type (see below).

fn multiply(comptime n: i32, value: i32) -> i32 {
    n * value
}

fn main() -> i32 {
    multiply(6, 7)  // n is known at compile time
}

Comptime parameters enable monomorphization: each unique combination of comptime arguments creates a specialized version of the function.

The keyword type is a comptime-only type whose values are types themselves. A parameter of type type must be marked comptime.

fn identity(comptime T: type, x: T) -> T {
    x
}

fn main() -> i32 {
    identity(i32, 42)
}

When a function has a comptime T: type parameter, occurrences of T in parameter types and return types are substituted with the concrete type at each call site.

It is a compile-time error to pass a runtime value to a comptime parameter.

fn double(comptime n: i32) -> i32 { n * 2 }

fn main() -> i32 {
    let x = 21;
    double(x)  // ERROR: comptime parameter requires a compile-time known value
}

Type values cannot exist at runtime. It is a compile-time error to attempt to store a type value in a runtime variable.

fn main() -> i32 {
    let t = comptime { i32 };  // ERROR: type values cannot exist at runtime
    0
}

Anonymous Struct Types

A comptime function that returns type can construct an anonymous struct type using the following syntax:

anon_struct_type = "struct" "{" struct_field { "," struct_field } "}" ;
struct_field = IDENT ":" type ;
fn Point() -> type {
    struct { x: i32, y: i32 }
}

fn main() -> i32 {
    let P = Point();
    let p: P = P { x: 10, y: 32 };
    p.x + p.y
}

Anonymous structs can be parameterized using comptime type parameters:

fn Pair(comptime T: type) -> type {
    struct { first: T, second: T }
}

fn main() -> i32 {
    let IntPair = Pair(i32);
    let p: IntPair = IntPair { first: 20, second: 22 };
    p.first + p.second
}

Two anonymous struct types are structurally equal if and only if they have the same field names in the same order with the same types.

fn make_point1() -> type { struct { x: i32, y: i32 } }
fn make_point2() -> type { struct { x: i32, y: i32 } }

fn main() -> i32 {
    let P1 = make_point1();
    let P2 = make_point2();
    let p1: P1 = P1 { x: 10, y: 20 };
    let p2: P2 = p1;  // OK: P1 and P2 are structurally equal
    p2.x + p2.y
}

Anonymous structs with different field names or different field types are different types and are not assignable to each other.

It is a compile-time error to define an anonymous struct type with no fields.

fn empty() -> type {
    struct { }  // ERROR: empty struct
}