컴파일 타임 기능
Compile-Time Evaluation (comptime) Feature
Overview
The comptime feature allows Vais code to execute expressions at compile time, enabling powerful metaprogramming capabilities and compile-time optimizations. This is inspired by Zig's comptime feature.
Syntax
Basic comptime Expression
C ARRAY_SIZE = comptime { 4 * 8 }
comptime Block
F calculate_hash()->i64 = comptime {
x := 5381
L i:0..10 {
x = x * 33 + i
}
x
}
comptime Function (Future Feature)
comptime F factorial(n: i64) -> i64 = I n <= 1 { 1 } E { n * @(n - 1) }
Features
Supported Operations
The comptime evaluator supports the following operations:
-
Arithmetic Operations
- Integer:
+,-,*,/,% - Float:
+,-,*,/ - Bitwise:
&,|,^,<<,>>
- Integer:
-
Logical Operations
- Boolean:
&&,||,!
- Boolean:
-
Comparison Operations
<,<=,>,>=,==,!=
-
Control Flow
- Conditionals:
I cond { ... } E { ... } - Ternary:
cond ? then : else - Loops:
L var:range { ... }
- Conditionals:
-
Variables
- Local variable bindings:
x := value - Variable reassignment:
x = new_value
- Local variable bindings:
Restrictions
The following are NOT supported in comptime blocks (to ensure purity):
- I/O operations (file operations, printing, etc.)
- Memory allocation (heap allocations)
- External function calls (except pure, compile-time evaluable functions)
- Mutable global state
- Side effects
Implementation
Architecture
The comptime feature is implemented in several layers:
-
Lexer (
vais-lexer)- Added
comptimekeyword token
- Added
-
AST (
vais-ast)- Added
Expr::Comptime { body: Box<Spanned<Expr>> }variant
- Added
-
Parser (
vais-parser)- Parses
comptime { expr }syntax - Validates block structure
- Parses
-
Evaluator (
vais-types/src/comptime.rs)- Interprets AST at compile time
- Evaluates expressions to concrete values
- Maintains compile-time scope and variable bindings
-
Type Checker (
vais-types)- Evaluates comptime expressions during type checking
- Verifies type consistency
- Returns the type of the evaluated result
-
Code Generator (
vais-codegen)- Replaces comptime expressions with their evaluated constants
- Emits LLVM IR for constant values
Evaluation Process
Source Code → Parse → Type Check (Evaluate comptime) → Codegen (Emit constant)
Example:
F test()->i64 = comptime { 4 * 8 }
- Parser creates:
Expr::Comptime { body: Binary { op: Mul, left: 4, right: 8 } } - Type checker evaluates:
ComptimeValue::Int(32) - Type checker returns:
ResolvedType::I64 - Codegen emits:
32(constant in LLVM IR)
Examples
Example 1: Simple Arithmetic
F array_size()->i64 = comptime { 4 * 8 }
# Evaluates to: F array_size()->i64 = 32
Example 2: Loop Calculation
F compute_hash()->i64 = comptime {
x := 5381
L i:0..10 {
x = x * 33 + i
}
x
}
# Evaluates to a constant computed at compile time
Example 3: Conditional Logic
F get_config()->i64 = comptime {
debug := true
I debug {
100
} E {
50
}
}
# Evaluates to: F get_config()->i64 = 100
Example 4: Factorial
F factorial_five()->i64 = comptime {
n := 5
result := 1
L i:1..=n {
result = result * i
}
result
}
# Evaluates to: F factorial_five()->i64 = 120
Example 5: Power Calculation
F power_of_two()->i64 = comptime {
base := 2
exp := 10
result := 1
L i:0..exp {
result = result * base
}
result
}
# Evaluates to: F power_of_two()->i64 = 1024
Use Cases
1. Constant Array Sizes
C SIZE = comptime { calculate_optimal_size() }
arr: [i64; SIZE]
2. Configuration Values
C MAX_CONNECTIONS = comptime {
I is_production() { 1000 } E { 10 }
}
3. Compile-Time Hashing
C STRING_HASH = comptime { hash("my_constant_string") }
4. Type-Level Computation
C ELEMENT_SIZE = comptime { size_of::<MyType>() }
C ARRAY_ELEMENTS = comptime { BUFFER_SIZE / ELEMENT_SIZE }
Performance Benefits
-
Zero Runtime Cost: Comptime expressions are evaluated during compilation, resulting in zero runtime overhead.
-
Optimization: The compiler can better optimize code with compile-time constants.
-
Compile-Time Validation: Errors in comptime expressions are caught at compile time, not runtime.
-
Code Size Reduction: Eliminates need for runtime calculation code.
Error Handling
Comptime evaluation can fail with the following errors:
- Type Mismatch: Incompatible types in operations
- Division by Zero: Attempting to divide by zero at compile time
- Undefined Variable: Using an undeclared variable
- Overflow: Integer overflow in arithmetic operations
- Non-Pure Operation: Attempting I/O or other impure operations
Example error:
F test()->i64 = comptime {
x := 10
y := 0
x / y # Error: division by zero at compile time
}
Future Enhancements
-
Comptime Functions: Functions marked as
comptimethat can only be called at compile time -
Comptime Reflection: Access to type information at compile time
-
Comptime String Manipulation: String operations in comptime blocks
-
Comptime Type Generation: Generate types based on comptime calculations
-
Comptime Assertions: Static assertions that must hold at compile time
# Future syntax
comptime_assert(SIZE > 0, "Size must be positive")
Comparison with Other Languages
Zig
Vais comptime is inspired by Zig's comptime:
// Zig
const size = comptime calculateSize();
// Vais
C size = comptime { calculateSize() }
C++ constexpr
Similar to C++ constexpr but with a more explicit syntax:
// C++
constexpr int factorial(int n) {
return n <= 1 ? 1 : n * factorial(n - 1);
}
// Vais
comptime F factorial(n: i64) -> i64 = I n <= 1 { 1 } E { n * @(n - 1) }
Rust const fn
Similar to Rust's const fn:
// Rust
const fn add(a: i32, b: i32) -> i32 {
a + b
}
// Vais
comptime F add(a: i64, b: i64) -> i64 = a + b
Testing
Comprehensive test suite in examples/comptime_test.vais covers:
- Simple arithmetic
- Variables and assignments
- Loops and iterations
- Conditionals
- Bitwise operations
- Boolean logic
- Float arithmetic
- Nested expressions
Run tests with:
cargo test --lib -p vais-types comptime