Documentation

ARIA-IR Language Reference

v0.2.0

Module Declaration

Every ARIA-IR program is a module. The module is the top-level container for functions and exports.

(module "name"
  (func ...)
  (export ...))

Functions

Functions are declared with func. Parameters, return types, effects, and intent are all declared explicitly.

(func $name
  (param $x i32)
  (param $y i32)
  (result i32)
  (effects pure)
  (intent "Add two integers")
  (return (add.i32 $x $y)))
$name Mutable binding / function name. Prefixed with $.
%name Immutable binding (SSA). Prefixed with %. Cannot be reassigned.

Type System

ARIA-IR uses explicit, type-suffixed operations. There is no implicit coercion.

i32
i64
f32
f64
bool
str
void

Operations carry their type: add.i32, mul.f64, lt.i32, concat.str.

Effect System

Every function declares its effects. The type checker verifies these declarations at compile time.

pure No side effects. Can be memoized, reordered, or eliminated.
mem Memory effects. Reads or writes mutable state.
io I/O effects. Interacts with the outside world (print, file, network).
div Divergence. May not terminate (loops, recursion).

Intent Annotations

The intent node carries the AI's stated purpose for a function. It is preserved through compilation and available for verification in Layer 4.

(intent "Compute the nth Fibonacci number using recursion")

Intent annotations are not comments. They are first-class nodes in the AST. They travel with the code through the pipeline.

Control Flow

Conditionals

(if (lt.i32 $x 10)
  (then (return $x))
  (else (return (sub.i32 $x 1))))

Loops

(loop $label
  (if (ge.i32 $i $n)
    (then (br $label))
    (else
      ;; loop body
      (set $i (add.i32 $i 1)))))

br breaks out of the named loop.

Bindings

;; Immutable (SSA)
(let %result i32 (add.i32 $x $y))

;; Mutable local
(local mut $counter i32 0)
(set $counter (add.i32 $counter 1))

Exports

Functions must be explicitly exported to be visible outside the module. The $main export is the entry point.

(export $main)
(export $fibonacci)

Module Imports

ARIA-IR supports multi-module programs. A module can import symbols from another file using import. Imported symbols are accessed with qualified names.

(import "math.aria")
(call $math.gcd 48 18)

Path resolution is relative to the importing file. Only symbols declared with (export $name) in the imported module are visible. Circular imports are detected and rejected. The ariac compiler resolves all modules and emits a single C file with module__func prefixed names.

Compile-Time Evaluation

ARIA supports compile-time code generation through a two-pass model. Comptime blocks are compiled via the C backend, executed natively, and their stdout is parsed as ARIA s-expressions and spliced back into the module. Intent verification applies to the post-expansion IR.

comptime

A top-level comptime block contains functions that run at compile time. Their printed output becomes part of the module.

(module "demo"
  (comptime
    (func $main (result i32) (effects io)
      (print "(func $square (param $x i32) (result i32) (effects pure)\n")
      (print "  (return (mul.i32 $x $x)))\n")
      (return 0)))

  (func $main (result i32) (effects io)
    (print "4^2 = %d\n" (call $square 4))
    (return 0)))

The comptime block compiles and executes at compile time. Its output ($square) is spliced into the module before the main compilation pass.

comptime-val

Sugar for computing a single typed value at compile time. Desugars into a full comptime block internally.

(let %answer i32 (comptime-val i32 (mul.i32 6 7)))

Supported types: i32, i64, u32, u64, f32, f64, i8, i16, u8, u16, bool.

Generics via comptime

Comptime functions that take type names as strings and print type-specialized code. This is the generics mechanism. No templates, no special syntax.

(comptime
  (func $gen_max (param $T (ptr i8)) (effects io)
    (print "(func $max_%s (param $a %s) (param $b %s) (result %s) (effects pure)\n" $T $T $T $T)
    (print "  (if (gt.%s $a $b) (then (return $a)) (else (return $b))))\n" $T))

  (func $main (result i32) (effects io)
    (call $gen_max "i32")
    (call $gen_max "f64")
    (return 0)))

Produces $max_i32 and $max_f64 as fully monomorphized functions. Expansion iterates to a fixed point (max 10 passes) to handle comptime that generates more comptime.

Two-pass model

1. Extract comptime blocks from module
2. Wrap in temporary module, compile via C backend
3. Execute native binary, capture stdout
4. Parse stdout as ARIA s-expressions
5. Splice into original module, replacing comptime block
6. Repeat until no comptime blocks remain (fixed point)
7. Parse, type-check, codegen the expanded module

Expression-level comptime is also supported. A (comptime ...) nested inside an expression must produce exactly one form.

C Backend: Effect Attributes

Effect declarations in ARIA-IR are not just documentation. The C backend translates them into GCC/Clang function attributes that enable real compiler optimizations at -O2. This means the effect system does optimization work in the compiled output, not just in the ARIA type checker.

pure
Functions with no pointer parameters emit __attribute__((const)). The return value depends only on the arguments. GCC/Clang can deduplicate calls and eliminate dead calls.
pure + ptrs
Functions with pointer parameters emit __attribute__((pure)). The return value depends on arguments and global state but has no side effects. GCC/Clang can still eliminate redundant calls.
mem
Functions returning a pointer emit __attribute__((malloc)). Tells GCC/Clang the returned pointer does not alias any existing pointer, enabling better alias analysis.

These attributes are GCC and Clang specific. They are ignored by MSVC. The WAT backend preserves effects as ;;effects: comments before each function definition, since WASM has no native effect system.

;; ARIA-IR
(func $add (param $x i32) (param $y i32) (result i32) (effects pure)
  (return (add.i32 $x $y)))

// Generated C
__attribute__((const))
int32_t add(int32_t x, int32_t y) {
    return x + y;
}

Compile-Time Memory Safety

The ariac checker performs static pointer state analysis. Every pointer variable is tracked through four states: alive, freed, null, and owns-allocation. Eight error classes are detected at compile time.

use after free Loading or storing to a pointer after it has been freed.
double free Freeing the same pointer twice.
null dereference Using a pointer initialized to 0 or with no initializer.
memory leak An owned allocation that is never freed, returned, or passed out.
free of non-pointer Attempting to free a variable that is not a pointer type.
alias use-after-free Using an alias of a freed pointer.
pointer arithmetic alias Using an offset pointer after its base is freed.
struct field dangling Loading a struct field whose stored pointer has been freed.

Memory safety analysis is implemented in ariac. The Clojure compiler does not perform pointer state tracking.

Example Programs

The examples/ directory contains complete programs:

fibonacci.aria Recursive and iterative Fibonacci. Demonstrates recursion, loops, intent.
bubble_sort.aria Array sorting. Demonstrates mutable state, nested loops, memory effects.
math_demo.aria Arithmetic operations. Demonstrates type-suffixed ops, pure functions.
float_demo.aria Float arithmetic, temperature conversion, int-to-float cast.
bootstrap_demo.aria Strings as (ptr u8), externs, file I/O, CLI args. Used to verify the self-hosted compiler.
comptime_demo.aria Compile-time code generation. Demonstrates the two-pass comptime model.
comptime_generics.aria Generics via comptime. Type-parameterized function generation.