Compile-time intrinsics
Every compiler-known builtin uses the #name(...) sigil: one uniform spelling, distinct from a regular function call. They fall into a few families.
| Family | Intrinsics |
|---|---|
| Typed queries | #size_of::[T](), #align_of::[T](), #addr_of(place), #zero::[T]() |
| Data embedding | #include_bytes("path"), #include_str("path"), #env("NAME") |
| 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. It is unsafe (wrap it in unsafe), because the returned pointer aliases existing storage and the borrow checker does not track its lifetime. Use it when a C function writes through a pointer.
extern fn time(t: *i64) -> i64;
fn now() -> i64 {
let mut t: i64 = 0;
unsafe { 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, there is no turbofish form (E0501), and using it outside unsafe is E0801.
#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 / static mut initializers.
let mut 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 = unsafe { 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.