Docs/Advanced

Compile-time code

Conditional compilation, target flags, and macros.

On this page

Lines starting with # run while compiling. They pick which code is built for the target, the build flags, or the type a generic function was called with.

Conditional compilation#

#if, #elif, #else and #end keep or drop the lines between them. Code in a dropped branch is not compiled:

Valk
fn main() {
    #if OS == "linux"
    println("Linux")
    #elif OS == "macos"
    println("macOS")
    #else
    println("Windows")
    #end

    #if ARCH == "x64"
    println("x86-64")
    #end
}

#if works inside functions and between declarations, so a whole function or link line can be per platform.

Definitions#

Name Value
OS "linux", "macos" or "win"
ARCH "x64" or "arm64"
TEST true in --test builds
DEBUG true in debug builds (-d)
STATIC true when dependencies are linked statically (--static)
VALK_VERSION The compiler version, such as "0.8.4"
your own Passed with --def

Pass your own with --def, and check whether one is set with is_defined:

Valk
fn main() {
    #if is_defined(TRACE)
    println("tracing on")
    #end
    #if is_defined(LEVEL) && LEVEL == "3"
    println("level 3")
    #end
}
Terminal
valk build src -o app --def TRACE --def LEVEL=3
valk build src -o app --def "TRACE,LEVEL=3"

A definition can also be used as a value, such as println(LEVEL), when the build sets it.

Source location#

Expression Value
__FILE__ Path of the file, relative to its package's source directory
__PATH__ Full path of the file
__DIR__ Directory of the file
__LINE__ Current line, from 1
Valk
fn main() {
    println("at %{__FILE__}:%{__LINE__}")
}

Branch on a type#

In generic code, #if can test the type parameter. Types compare with == and !=, and size_of(T) gives the size in bytes:

Valk
fn describe[T](value: T) String {
    #if T.$is_integer
    return "integer of %{size_of(T)} bytes"
    #elif T == String
    return "text"
    #else
    return "something else"
    #end
}

fn main() {
    println(describe(5))      // integer of 8 bytes
    println(describe("hi"))   // text
    println(describe(1.5))    // something else
}
Check True when T is
T.$is_integer, T.$is_signed, T.$is_float, T.$is_bool That kind of number or a bool
T.$is_enum An enum
T.$is_nullable A ?T type
T.$is_pointer A class, raw pointer or other pointer type
T.$is_raw_pointer ptr or *T
T.$is_gc Managed by the garbage collector
T.$is_void void
T.$is_type_of(String) Exactly that type
T.$has_method(name) A type with a method of that name

Loop over properties#

#loop object value as prop repeats its body for each property of a class or struct value. prop.name is the property name, and prop itself reads the property:

Valk
class User {
    name: String
    age: int
}

fn field_names[T](value: T) Array[String] {
    let names = Array[String]{}
    #loop object value as prop
    names.append(prop.name)
    #end
    return names
}

fn main() {
    let user = User { name: "Ada", age: 36 }
    println(field_names(user).join(", ")) // name, age
}

Stop or report a build#

#error "message" fails the build when its branch is kept. #print value prints a value while compiling:

Valk
fn main() {
    #if OS == "plan9"
    #error "Plan 9 is not supported"
    #end
    #print OS
}

CPU features#

--cpu native lets the whole build use this machine's CPU features, and function flags such as $target_avx2 enable one feature for a single function. See SIMD vectors.