Ruta graveolens  ·  notes from a language experiment  ·  cultivated since 2025

Builtins

A builtin is a compiler-provided construct prefixed with @.

builtin = "@" IDENT "(" [ builtin_args ] ")" ;
builtin_args = builtin_arg { "," builtin_arg } ;
builtin_arg = IDENT | expression | type ;

The @ prefix distinguishes builtins from user-defined constructs.

Tokenization of builtins is uniform: @ is its own token and the builtin name is an ordinary identifier token. No builtin name — including import — forms a fused single token, and the name is interned like any other identifier.

Using an unknown builtin name is a compile-time error.

Builtin Kinds

There are two kinds of builtins, distinguished by their syntactic position:

KindPositionPurposeExamples
IntrinsicExpressionProduces a value@dbg, @size_of, @align_of
DirectiveBefore item/statementModifies compiler behavior@allow, @copy, @repr

An intrinsic builtin appears where an expression is expected and evaluates to a value. See Intrinsic Expressions for details.

A directive builtin appears before an item or statement and modifies how the compiler processes that construct. See Directives for details.

Directives

A directive is a builtin that modifies the behavior of the immediately following item or statement.

directive = "@" IDENT "(" [ directive_args ] ")" ;
directive_args = directive_arg { "," directive_arg } ;
directive_arg = IDENT ;

A directive MUST be immediately followed by an item or statement. A directive at the end of a file or block without a following construct is a compile-time error.

@allow

The @allow directive suppresses specific compiler warnings for the following item or statement.

@allow accepts one or more warning names as arguments.

The following warning names are recognized:

Warning NameDescription
unused_variableVariable is declared but never used
unused_functionFunction is declared but never called
unreachable_codeCode that can never be executed
unreachable_patternMatch arm pattern that can never match

Using an unrecognized warning name in @allow is a compile-time error.

Suppressing Unused Variable Warnings

When @allow(unused_variable) precedes a let statement, no unused variable warning is emitted for that binding.

fn main() -> i32 {
    @allow(unused_variable)
    let x = 42;  // no warning, even though x is unused
    0
}

When @allow(unused_variable) precedes a function definition, no unused variable warnings are emitted for any bindings within that function.

@allow(unused_variable)
fn example() -> i32 {
    let a = 1;  // no warning
    let b = 2;  // no warning
    0
}

Suppressing Unused Function Warnings

When @allow(unused_function) precedes a function definition, no unused function warning is emitted for that function.

@allow(unused_function)
fn helper() {
    // This function is never called, but no warning is emitted
}

fn main() -> i32 {
    0
}

Suppressing Unreachable Code Warnings

When @allow(unreachable_code) precedes a function definition, no unreachable code warnings are emitted for code within that function.

@allow(unreachable_code)
fn example() -> i32 {
    return 0;
    let x = 42;  // unreachable, but no warning
    x
}

Multiple Warnings

Multiple warning names MAY be specified in a single @allow directive, separated by commas.

@allow(unused_variable, unreachable_code)
fn example() -> i32 {
    let x = 1;
    return 0;
    let y = 2;
    0
}

Relationship to Underscore Prefix

The underscore prefix convention (e.g., _unused) and @allow(unused_variable) are both valid ways to suppress unused variable warnings. The underscore prefix is more concise for individual variables; @allow is useful when suppressing warnings for an entire function or when the variable name should not have an underscore prefix.

fn main() -> i32 {
    let _x = 42;                    // underscore prefix suppresses warning

    @allow(unused_variable)
    let important_name = 42;        // @allow preserves the meaningful name

    0
}

@copy

The @copy directive marks a struct type as a Copy type.

@copy MUST appear immediately before a struct definition.

@copy takes no arguments.

@copy
struct Point { x: i32, y: i32 }

fn main() -> i32 {
    let p = Point { x: 1, y: 2 };
    let q = p;  // p is copied, not moved
    p.x + q.x   // both are valid
}

See Move Semantics for the full semantics of @copy structs.

@repr

The @repr directive is a representation guarantee marker for a struct type. @repr(c) guarantees the struct's object representation follows the selected compilation target's default C data model (the platform psABI): its size, alignment, field order, field offsets, and tail padding equal the corresponding C aggregate's.

@repr MUST appear immediately before a struct definition, and is parameterized: it takes exactly one representation argument. Only c is accepted; any other argument is a compile-time error. The parameterized form reserves room for future representations without a re-spelling.

@repr(c) is gated behind the c_ffi preview feature (ADR-0064): the marker is meaningful only at a C FFI boundary, so it requires --preview c_ffi.

A @repr(c) struct MUST be FFI-eligible: it is not empty, and every field is a C-compatible scalar, a raw pointer, a fixed array of eligible elements, or a nested @repr(c) struct. An empty struct, an enum field, an aggregate field that is not itself @repr(c), and a linear or destructor-bearing field are each rejected — the representation is never guessed. A pointer field crosses without the pointee's layout, so a pointer to an opaque or incomplete type is eligible.

Because Rue's sole memory representation (compact layout) already matches the C aggregate rule for the supported field subset, @repr(c) changes no bytes today: it is a layout no-op whose value is the forward guarantee against future native-layout evolution, and the anchor of the FFI-safety predicates.

@repr(c)
struct Point { x: i32, y: i32 }

extern "C" {
    fn translate(p: ptr const Point, dx: i32) -> i32;
}

See ADR-0064 for the full C FFI boundary contract, the three FFI-safety predicates, and the staged migration plan.