C+
Systems · View as Markdown

Compile-time intrinsics

Every compiler-known builtin uses the #name(...) sigil: one uniform spelling, distinct from a regular function call. The sigil is mandatory — a bare call to a builtin is a fix-it error — so a compiler-known operation is self-evident at the call site, never confused with a library function. They fall into a few families.

Family Intrinsics
Typed queries #size_of::[T](), #align_of::[T](), #addr_of(place), #addr(p), #zero::[T]()
Data embedding #include_bytes("path"), #include_str("path"), #env("NAME")
Output #println(...) (the single-file print builtin; the library io::println is a normal function and needs no sigil)
FFI / raw #str_ptr(s), #slice_ptr(s), #slice_len(s), #str_from_raw_parts(ptr, len)
Byte order #bswap16/32/64(x), #htons(x), #htonl(x), #ntohs(x), #ntohl(x)
CPU hints #cpu_relax()
ObjC + GPU FFI #selector("name"), #msg_send(recv, "sel", ...) -> RetTy, #compile_shader("file.metal", "msl")

#addr_of(place): address of a place as *T

Returns *T for the type of the addressed place. The returned pointer aliases existing storage and the borrow checker does not track its lifetime, so taking the address is the marker — there is no wrapper. Use it when a C function writes through a pointer.

extern fn time(t: *i64) -> i64;

fn now() -> i64 {
    var t: i64 = 0;
    time(#addr_of(t));
    return t;
}

The argument must be a place expression (an identifier, field access, index, or dereference chain). A call result or arithmetic temporary is rejected, and there is no turbofish form (E0501).

#addr(p): a pointer's address as usize

The loud pointer-to-integer form: it takes a pointer and returns its address as a usize. Use it whenever you need to inspect or compute on a raw address; the intrinsic spelling keeps the conversion visible at the call site.

let n: usize = #addr(p);
let i: i32   = n as i32;

#zero::[T](): an all-zero value

Safe, and useful for C-style aggregate initialization where you fill selected fields afterward. It is also accepted in const / static initializers.

var p: Point = #zero::[Point]();
p.x = 10;

#size_of::[T]() and #align_of::[T]()

Return usize. Safe, with no memory access; LLVM folds the call to a constant at -O1 and above. Substitution propagates through monomorphization, so the value is correct for every instantiation of a generic.

let bytes: usize = #size_of::[T]() *% (n as usize);
let p: *u8       = malloc(bytes);

#include_bytes / #include_str: embed a file at compile time

#include_bytes("path") embeds a file as a *[u8; N] where N is the file length, known at compile time. #include_str("path") does the same but returns a str and requires valid UTF-8. Paths resolve relative to the source file containing the call.

let shader: *[u8; 2048] = #include_bytes("../shaders/double.metallib");
let manifest: str       = #include_str("config.txt");

The bytes live in .rodata. Errors: E0870 (path not found), E0871 (non-literal argument), E0872 (over the 64 MiB limit), and E0875 (invalid UTF-8, for #include_str).

#env("NAME"): read an environment variable at compile time

Returns a str pointing at a .rodata global with the value the compiler saw. Useful for baking build-time config into a binary.

let greeting: str = #env("GREETING");   // resolved at sema time

Errors: E0903 (non-literal argument), E0876 (variable not set when cpc ran). There is no optional form; use a sentinel and check the length at runtime if you need "missing".

#cpu_relax()

A spin-loop CPU hint. It lowers to the platform pause/yield instruction where available, and to nothing elsewhere. Safe, returns ().

ObjC and GPU intrinsics

#selector, #msg_send, and #compile_shader are the load-bearing primitives the appkit and metal bindings sit on. Direct use is rare; consume them through those packages. See FFI for the Objective-C mechanics.