Skip to content

Attributes and Directives

Michael Kutowski edited this page Nov 1, 2021 · 12 revisions

Attributes

  • @(link_name = <string>)
    This attribute can be attached to variable and procedure declarations inside a foreign block. This specifies what the variable/proc is called in the library.
    Example:
foreign foo {
    @(link_name = "bar")
    testbar :: proc(baz: int) ---
}
  • @(link_prefix = <string>)
    This attribute can be attached to a foreign block to specify a prefix to all names. So if functions are prefixed with ltb_ in the library is you can attach this and not specify that on the procedure on the odin side. Example:
@(link_prefix = "ltb_")
foreign foo {
    testbar :: proc(baz: int) --- // This now refers to ltb_testbar
}
  • @export or @(export=true/false)

  • @(default_calling_convention = <string>)
    This attribute can be attached to a foreign block to specify the default calling convention for all procedures in the block. Example:

@(default_calling_convention = "std")
foreign kernel32 {
    @(link_name="LoadLibraryA") load_library_a  :: proc(c_str: ^u8) -> Hmodule ---
}
  • @(deferred_in=<proc>)
  • @(deferred_out=<proc>)
  • @(deferred_in_out=<proc>)
  • @(deferred_none=<proc>)
    These attributes can be attached to a procedure X which will be called at the end of the calling scope for Xs caller.
    deferred_in will receive the same parameters as the called proc. deferred_out will receive the result of the called proc. deferred_in_out will receive both. deferred_none will receive no parameters.
baz :: proc() {
    fmt.println("In baz")
}

@(deferred_none=baz)
bar :: proc() {
    fmt.println("In bar")
}

foo :: proc() {
    fmt.println("Entered foo")
    bar()
    fmt.println("Leaving foo")
}
// Prints:
// Entered foo
// In bar
// Leaving foo
// In baz
  • @(static)
    Can be applied to a variable to have it keep its' state even when going out of scope. Same as a static local variable in C.
test :: proc() -> int {
    @(static) foo := 0
    foo += 1
    return foo
}

main :: proc() {
    fmt.println(test()) // prints 1
    fmt.println(test()) // prints 2
    fmt.println(test()) // prints 3
}
  • @(thread_local)
    Can be applied to a variable at file scope
@(thread_local) foo: int
  • @(private)
    Prevents a top level element from being exported with the package. In Odin elements beginning with '_' will not be exported. This attribute allows you to explicitly prevent something from being exported without naming it with an '_'.
package foo

_this_is_private :: 0
this_is_not :: 1
@private
this_is_also_private :: 2
  • @(deprecated=<string>)
    Mark a procedure as deprecated. Running odin build/run/check will print out the message for each usage of the deprecated proc.
@(deprecated="'foo' deprecated, use 'bar' instead")
foo :: proc() {
    ...
}
  • @(builtin)
    Marks builtin procs in Odin's "core:runtime" package. Cannot be used in user code.

  • @(require_results)
    Ensures procedure return values are acknowledged.

@(require_results)
foo :: proc() -> bool {
    return true
}

main :: proc() {
    foo() // won't compile
    _ = foo() // Ok
}

Directives

Record Memory Layout

  • #packed
    This tag can be applied to struct. By doing so, the Odin compiler will omit padding and preserve the order of fields in a struct.
  • #raw_union
    This tag can be applied to a struct. Struct's fields will share the same memory space which serves the same functionality as unions in C language. Useful when writing bindings especially.
  • #align
    This tag can be applied to a struct or union. This tag in form #align N specifies the struct's alignment to N bytes.
Foo :: struct #align 4 { 
    b: bool,
}
Bar :: union #align 4 {
    i32,
    u8,
}
  • #no_nil
    This tag can be applied to a union to not allow nil values.
A :: union {int, bool}
B :: union #no_nil {int, bool}
Possible states of A:
{} // nil
{int}
{bool}

Possible states of B:
{int} // default state
{bool}

Control Statements

  • #partial
    By default all cases in an enum have to be covered. This tag allows to use wanted cases
Foo :: enum {
    A,
    B,
    C,
}

test :: proc() {
    bar := Foo.A
    
    // All cases required, removing any would result in an error
    switch bar {
    case .A:
    case .B:
    case .C:
    }
    
    // Partially state wanted cases
    #partial switch bar {
    case .A:
    case .B:
    }
}

Procedure Parameters

  • #no_alias This tag can be applied to a procedure parameter that is a pointer. This is a hint to the compiler that this parameter will not alias other parameters. This is equivalent to C's __restrict.
foo :: proc(#no_alias a, b: ^int) {}
  • #caller_location
    This tag is used as a function's parameter value. In the following function signature,
alloc :: proc(size: int, alignment: int = DEFAULT_ALIGNMENT, loc := #caller_location) -> rawptr

loc is a variable of type Source_Code_Location (see core/runtime/core.odin) that is automatically filled with the location of the line of code calling the function (in this case, the line of code calling alloc).

  • #c_vararg
    Used to interface with vararg functions in foreign procedures.
foreign foo {
    bar :: proc(n: int, #c_vararg args: ..any) ---
}
  • #optional_ok Allows skipping the last return parameter, which needs to be a bool
import "core:fmt"

foo :: proc(x: int) -> (value: int, ok: bool) #optional_ok {
    return x + 1, true
}

main :: proc() {
    for x := 0; x < 11; x = foo(x) {
        fmt.printf("v: %v\n", x)
    }
}

Expressions

  • #type
    This tag doesn't serve a functional purpose in the compiler, this is for telling someone reading the code that the expression is a type. The main case is for showing that a procedure signature without a body is a type and not just missing its body, for example:
foo :: #type proc(foo: string)

bar :: struct {
    gin: foo,
}
  • #bounds_check
  • #no_bounds_check
    #bounds_check and #no_bounds_check are flags that control Odin's built-in bounds checking of arrays and slices. Any statement, block, or function with one of these flags will have their bounds checking turned on or off, depending on the flag provided. Valid uses of these flags include:
proc_without_bounds_check :: proc() #no_bounds_check {
    #bounds_check {
        #no_bounds_check fmt.println(os.args[1])
    }
}

Built-in Procedures

  • #assert
    Assert ran at compile time.
  • #defined
    Checks if an identifier is defined.
n: int
when #defined(n) { fmt.println("true") }
if #defined(int) { fmt.println("true") }
when #defined(nonexistent_proc) == false { fmt.println("proc was not defined") }
  • #file #line #procedure Return the current file path, line number, or procedure name, respectively. Used like a constant value. file_name :: #file
  • #location
    Returns a runtime.Source_Code_Location (see core/runtime/core.odin). Can be called with no parameters for current location, or with a parameter for the location of the variable/proc declaration
foo :: proc() {}

main :: proc() {
    n: int
    fmt.println(#location())
    fmt.println(#location(foo))
    fmt.println(#location(n))
}
  • #load
    Returns a []u8 of file contents at compile time
foo := #load("path/to/file")
fmt.println(string(foo))
  • #load_or
    Returns a []u8 of file contents at compile time, otherwhise default content when the file wasn't found
foo := #load_or("path/to/file", []u8 { 104, 105 })
fmt.println(string(foo))
Clone this wiki locally