Skip to content

Functions ​

Functions are module-level declarations introduced by def. A function's callable contract applies at every call site. It fixes:

  • the parameter names, passing modes, and types
  • the generic parameters and their bounds
  • the return behavior
  • any inferred clone-safety obligations
Aura
def add(left: int32, right: int32) -> int32:
    return left + right

Grammar has the complete declaration grammar. This page defines the matching static and runtime rules.

Signatures And Return Types ​

Every ordinary parameter has an explicit type. The return annotation is optional, and omitting it is exactly the same as writing -> None. An ordinary -> T return is owned. A returned view uses the separate -> view [mut] T from origin form.

A plain triple-quoted string as the first statement of the body is the function's docstring. It documents parameters under an Args: heading and does not run. See Docstrings And API Docs.

Aura
def square(value: int32) -> int32:
    return value * value

def log(message: str):
    print(message)

return expression must have exactly the declared return type. return without an expression has type None and is valid only in a None-returning function. Reaching the end of a None function returns None implicitly.

A function with any other return type must return on every statically reachable path:

Aura
def classify(value: int32) -> str:
    if value < 0:
        return "negative"
    return "non-negative"

A function may return one fixed structural tuple. The return annotation and the returned value both use parentheses. A comma distinguishes a singleton tuple from grouping. For example, def locate() -> (str, int64): may return ("north", 7), and the caller may bind the result with name, number = locate(). Whether a returned tuple copies or moves follows the recursive classification of the whole tuple. See Tuples.

Aura has no implicit numeric widening and no general return coercion. A bare None or a member value in an argument or return position adopts an expected T | None union. Grouping does not discard that context. A function declared -> T | None returns absence with an explicit return None. Static Semantics covers other contextual literal typing and the union equality rule.

Function names share the module item namespace with classes, enums, traits, and imports. The checker rejects duplicate items and any attempt to redefine a maintained builtin function name. Ordinary parameter names must be unique. When a method has a receiver, no other parameter can be named self. In a method declaration, self: Type is rejected, not treated as an ordinary first parameter. Receivers use self, own self, or mut self. See Names And Scopes for the complete namespace rules.

A function is private to its defining module by default. Prefix the declaration with public to make it importable from another module:

Aura
public def double(value: int32) -> int32:
    return value * 2

Visibility controls name access. It does not change the ownership or type rules of the signature.

Parameter Passing Modes ​

The passing mode is part of the function signature:

DeclarationContract at the call boundary
value: TShared access. An implementation may pass copy bits directly without changing the source contract.
value: own TOwned argument. A move value is consumed. A copy value is duplicated.
value: mut TExclusive mutable borrow. The argument must be a mutable place.
Aura
def consume(name: own str):
    print(name)

def length(text: str) -> int64:
    return text.len()

def push_name(names: mut list[str], name: own str):
    names.append(name)

Write the modifier in the declaration, after the colon. A call passes the expression directly, because Aura has no call-site capability prefix:

Aura
mut names = list[str]()
push_name(names, "Ada")

Each argument must have exactly the substituted parameter type.

A call keeps its access to each non-copy method receiver and non-copy argument while it evaluates every later sibling expression. A later sibling may take a shared borrow of the same place, including through an access nested inside another call or expression. It may not mutably borrow or consume an overlapping place. A violation reports AU3002. Ownership And Borrowing specifies the ownership and place rules.

The meaning of a bare parameter is fixed where the function is declared, not separately at each call. An unconstrained generic value: T is therefore a shared borrow, because T is not known to be copyable at the declaration. This choice is declaration-stable: specializing the function later with T = int32 does not turn the parameter into an owned value. Write value: own T when a generic function must consume or return its argument.

Call Binding ​

A call takes positional arguments followed by named arguments:

Aura
def render(name: str, count: int32 = 1):
    print(name)

render("Aura")
render("Aura", 2)
render(name="Aura", count=2)

Binding is deterministic:

  1. positional arguments fill parameters in declaration order
  2. named arguments fill the parameter with the same name
  3. one parameter cannot be filled twice
  4. unknown names and excess positional arguments are rejected
  5. every omitted parameter must have a default
  6. each bound argument must have the parameter's exact substituted type

A positional argument cannot follow a named argument. Parameter and argument lists may span physical lines while their parentheses are open. Each list accepts one trailing comma before its ).

Keyword-Only Parameters ​

A * boundary in a parameter list makes every parameter after it keyword-only. A keyword-only parameter binds by name only:

Aura
def configure(path: str, *, retries: int32 = 2, verbose: bool = false) -> int32:
    if verbose:
        print(path)
    return retries

configure("cfg")
configure("cfg", retries=5)
configure("cfg", verbose=true, retries=1)
  • A positional argument that would reach a keyword-only parameter reports AU2004 with parameter `x` of function `f` is keyword-only.
  • Omitting a required keyword-only parameter reports the missing name.
  • A parameter list may have at most one *. At least one named parameter must follow it. It adds no variadic parameters.
  • The boundary is part of the function's complete callable contract. See Function Values. A trait implementation must place it exactly where the trait declaration does.

Default Arguments ​

A default is permitted on a bare shared or own parameter of a top-level function or class method:

Aura
def greet(name: str = "world"):
    print("hello " + name)

The complete rules are:

  • A mut parameter cannot have a default, whether or not its type is copyable. The default would be a temporary the caller cannot see, so every mutation would be a silently lost write. Require the caller to pass a value, or take the parameter as own T and return the result.
  • A shared-borrow default is permitted. Its temporary lives until the call completes.
  • An own default is permitted. The call consumes its fresh temporary.
  • After the first defaulted positional parameter, every remaining positional parameter must also have a default. Keyword-only parameters bind by name, so a required keyword-only parameter may follow a defaulted one.
  • The default expression must have exactly the declared parameter type.
  • A default expression cannot reference any parameter of the same declaration, including an earlier parameter.
  • A default expression resolves names in its declaring module. It can read that module's constants and imported modules, and a caller's locals never shadow them.
  • Trait method declarations and trait implementation methods cannot declare defaults.

A default is evaluated afresh each time its argument is omitted. It is not a process-global singleton value. A call evaluates its arguments in this order:

  1. Supplied arguments evaluate in call-site source order. Each one finishes before the next supplied expression begins.
  2. Defaults for omitted parameters then evaluate in declaration order.

A copy or move result is captured in its parameter slot. A borrow-mode selection is established without cloning and stays subject to the retained non-copy overlap rules. Later side effects cannot change an argument that is already captured. Binding named values to parameter slots never reorders their evaluation. A supplied argument suppresses its default. See Execution Model.

Named Arguments For Builtins ​

Maintained builtin functions and methods use the same binding rules. Their parameter names come from their API metadata:

Aura
import process

process.run(["/bin/echo", "hi"], stdout=process.pipe(), group=true)
Aura
import net

net.http_request_text_timeout(method="POST", url="http://127.0.0.1:8080/jobs", body="{}", headers={}, timeout=2s)

The module pages and the API Index are authoritative for builtin parameter names, defaults, and return types.

try And Result Returns ​

try is valid only when its operand has type Result[T, E1] and the enclosing function returns Result[U, E2]:

Aura
def parse_total(left: str, right: str) -> Result[int32, str]:
    a = try parse_int32(left)
    b = try parse_int32(right)
    return Result.Ok(a + b)

Result.Ok(value) makes try evaluate to value. Result.Err(error) returns from the enclosing function immediately. E1 must equal E2, or an applicable impl From[E1] for E2 with a from method must be visible. Active with cleanups run during this early return. See Execution Model.

Owned Returns ​

Every ordinary return type annotation describes an owned result:

Aura
class User:
    name: str
    score: int32

def score(user: User) -> int32:
    return user.score

Here the caller receives an ordinary int32 copy. A copy result needs no provenance annotation, because it is an independent owned value.

For a non-copy result, the function must produce ownership. It can do that in four ways:

  • construct a fresh value
  • clone a clone-safe value
  • move from an own parameter
  • call an operation that consumes an owner
Aura
def copy_name(user: User) -> str:
    return user.name.clone()

def into_name(user: own User) -> str:
    return user.name

A bare or mut parameter grants access. It does not give the function ownership of a non-copy value stored behind that access. The checker rejects moving such a value into the result. Use one of the owned-result patterns above instead. See Ownership And Borrowing.

Returned Views ​

A function or method can return non-owning access to one declared receiver or parameter, called its origin:

class User:
    name: str

class Counter:
    value: int64

def name(user: User) -> view str from user:
    return view user.name

def value_mut(counter: mut Counter) -> view mut int64 from counter:
    return view mut counter.value

def bump(value: mut int64):
    value += 1

The signature rules are:

  • The from origin is mandatory and belongs to the signature.
  • The origin is recorded by receiver or parameter slot, across modules and in trait conformance. An implementation may rename its parameter but may not select another slot.
  • A shared result may use a bare or mut origin. A mutable result requires a mut origin.
  • Owned and defaulted parameters cannot be origins.

Every reachable return view must trace to the origin or one of its supported fixed field or tuple projections. Locals, temporaries, enum arm payloads, and newly allocated values cannot escape. Return paths may select different fixed projections of the one declared origin.

At a call site, these rules apply:

  • The origin argument must be an addressable place.
  • A returned view may initialize a matching local view or view mut binding.
  • To view a list element or dictionary entry of a returned view, bind the returned view with view first. view first = rows_of(grid)[0] reports AU3004.
  • A shared result may instead be read directly within one containing expression.
  • A mutable result may instead be reborrowed immediately into a mut call.
  • A returned view cannot initialize an ordinary owned binding or be stored inside an aggregate.

These calls use the declarations above:

user = User(name="Ada")
view current = name(user)
print(current)
print(name(user))

mut counter = Counter(value=0)
view mut editable = value_mut(counter)
editable += 1
print(counter.value)
bump(value_mut(counter))
print(counter.value)

The caller locks the origin conservatively, and execution keeps the exact projection that was selected. Selecting a different root reports AU3010. Structural def(...) -> R types cannot represent returned-view origin metadata, so they do not accept these functions.

Generic Functions ​

Type parameters follow the function name:

Aura
def identity[T](value: own T) -> T:
    return value

Bounds restrict substitutions:

Aura
def describe[T: Greeter](value: T) -> str:
    return value.greet()

def use_both[T: First + Second](value: T) -> int32:
    return value.score()

The checker infers type arguments from the call arguments and from an expected result type when one is available. Explicit specialization fixes them:

Aura
answer = identity[int64](42)

Every type parameter must resolve, every bound must hold, and explicit type arguments must have the declared arity. See Generics And Traits.

A clone-producing operation over an unresolved type parameter does not make the generic declaration invalid. Instead, the checker infers a clone-safety obligation for that parameter. A call discharges the obligation after substitution. A generic caller propagates an unresolved obligation as part of its own callable contract. The requirement also applies when the callable is imported or used as a maintained task target. See Generics And Traits.

Function Values ​

A module-level named function is a value. Its type uses declaration-shaped syntax, def(T1, mut T2, own T3) -> R. The zero-parameter form is def() -> R. Bare parameters are shared. A function type may appear anywhere another complete type may appear. That includes variable and parameter annotations, class fields, return types, alias targets, and collection element types.

Public user-module functions are values, and so are maintained builtin-module functions such as process.pipe. Calling an imported builtin through a value uses the same builtin dispatch and result type as calling its qualified name.

Function values are code pointers. They carry no captured environment, which this page calls thin. They are copy values, so cloning is unnecessary. Copying one, or passing one as an own def(...) -> R parameter, does not invalidate the source binding. Function values also satisfy Transfer.

Inside a function type, | binds more loosely than ->. So def() -> int32 | None is the union of a function type and None, that is (def() -> int32) | None. A function value that returns an optional groups its result type, as in def() -> (int32 | None). The parenthesized single type is grouping, not a tuple.

Callable Contracts ​

A callable type spells a complete contract. Each slot is one of two kinds:

  • An unnamed slot, such as int64, mut Counter, or own str, is positional-only.
  • A named slot, such as value: int64, may also be called by name.

A * boundary makes the named slots after it keyword-only. = ... after a slot promises that the target supplies a default for it. The default expression itself belongs only to the target declaration and is never written in a type.

The contract includes the parameter names, modes, and types, the keyword-only boundary, default availability, and the result type. Two structurally identical signatures with different exposed names or default promises are different contracts.

Aura
type Unary = def(int64) -> int64
type Renderer = def(value: int64, *, prefix: str = ...) -> str

def render(value: int64, *, prefix: str = "value") -> str:
    return f"{prefix}: {value}"

def increment(value: int64) -> int64:
    return value + 1

renderer: Renderer = render
print(renderer(4))
print(renderer(4, prefix="n"))
step: Unary = Unary(increment)
print(step(4))

An inferred local binding takes its target's complete contract: the declaration's names, * boundary, capabilities, and default availability. A call through it accepts the same named arguments and may omit the same parameters as a direct call. An omitted argument evaluates the runtime-selected target's own default expression afresh.

A written destination contract governs calls through that destination. A written destination is an annotation, parameter, field, element type, alias, or return type. Through it, an unnamed slot is called positionally, a named slot may be called by name, and an omitted slot must carry = ....

Ordinary indirect calls evaluate and bind arguments under the selected function's unchanged capability contract. A stored value never takes an argument its contract does not admit.

If an indirect-call default traps, the diagnostic uses the public target name and the precise span of the default expression. Compiler-generated default helpers never appear in the call chain.

Matching Contracts ​

A bare annotation, parameter pass, field or element store, alias binding, return, or branch join must preserve the value's complete callable contract. A difference reports AU2015 and describes the differing slot, kind, or obligation. A written destination does not restrict the value implicitly.

Inference never invents a common contract. Each of these rules reports AU2015 with the differing slot when it fails:

  • Rebinding a local must satisfy the contract the local already has.
  • Without an expected type, the arms of a conditional or match expression must share one complete contract.
  • A container literal's element contract is its annotation, or else its first element's contract. Every later element and insertion must satisfy it.
  • Repeated generic evidence must agree with its first observation.

Adapters ​

A safe restriction requires an explicit adapter. For example, declare type Unary = def(int64) -> int64 and write Unary(double) to hide double's parameter name. An adapter may:

  • hide exposed names
  • drop default availability
  • restrict a positional-or-keyword slot to keyword-only

An adapter cannot rename a slot, invent defaults, or make keyword-only slots positional. It also cannot change parameter types, modes, capabilities, or result guarantees.

Use an adapter to bring each value to a common contract. Calling an alias of a thin def type with one function value or capture-free lambda, such as Unary(increment), yields that alias's contract when the alias admits the value. A generic alias takes its type arguments first, as in Check[int64](is_even). Otherwise it reports AU2015. Thin adapters add no environment. A capturing closure packs into an owned Callable[...] storage type instead. See Closures. Owned Callable[...] and TaskCallable[...] constructors keep their own capture, call-kind, and Transfer admission rules.

Generic Function Values ​

A generic named function must receive explicit type arguments, as in show_int = show[int32], or a concrete expected function type. An expected type can specialize a variable annotation, argument, field, collection element, or parameter default. For example, a generic empty can be used where def() -> (str | None) is required. A generic name with neither source of type arguments does not have one concrete function-value type.

Methods And Returned Views As Values ​

An associated method without self, named as Class.method, is a thin function value carrying the method's complete contract when its class is not generic. A generic method takes explicit type arguments as Class.method[T], or infers them from an expected function type.

Outside call position, receiver.method is a bound method. A bound method is a compiler-synthesized closure over the receiver, specified in Closures. Lambdas and closure capture are specified separately.

A function that returns view [mut] T from name for one of its parameters keeps that contract in its value type, as in def(pair: Pair) -> view str from pair. A method result declared from self has no value type. Closures specifies storage, packing, and calls through such values.

Function Values And Task Starts ​

The ordinary and explicit-stack TaskGroup start methods accept a named function value as their target. They also accept a direct named function or an associated method without self as the target.

Aura
def work(value: int32) -> int32:
    return value * 2

worker = work

with group = TaskGroup():
    task = group.start(worker, 21)

Task capture ownership does not depend on the target function's call application binary interface (ABI). Each argument is first copied or moved into capture storage owned by the task. Then the target's parameter mode applies:

  • An own target parameter consumes its capture.
  • A bare shared parameter accesses that storage for the duration of the child call.
  • A mut target parameter is rejected, because mutable access to detached capture storage has no writeback contract the caller can see.

A stored TaskCallable[...] value is also a valid target. It moves into the start for one child call. An ordinary erased Callable is not a valid target, because its environment was never proven Transfer. That case reports AU3008. See Concurrency.

Decorators ​

A decorator line above a module-level function replaces the function with the result of calling the decorator on it. Write @ and one expression on each line above def.

Aura
type Step = Callable[def(n: int64) -> int64]

def traced(f: own Step) -> Step:
    print("wrapping")
    return Step(lambda [own f] n: f(n) + 1000)

@traced
def fact(n: int64) -> int64:
    if n <= 1:
        return 1
    return n * fact(n - 1)

print(fact(3))

This prints wrapping once, then 10006.

The Decorated Binding ​

The decorated name is a module constant. Its type is Callable[def(...) -> R], built from the function's parameter names, modes, keyword-only boundary, and result type. Owned Callable Storage describes the type. The constant is Shared, so any access can call it. It is not Copy.

A decorator is any expression whose value can be called with that callable and returns the same type. A function name, a call such as @tag("outer"), and a module member such as @tools.traced all qualify. The usual decorator takes f: own Callable[...], so its returned lambda can capture f with [own f].

A decorator whose parameter or result does not match reports AU2002. The message names the type the decorator must take and return.

The body of a decorated function calls the decorated binding when it names itself. In the example, fact(n - 1) runs through traced, so each level adds 1000.

Other modules see only the decorated binding. from tools import fact and tools.fact(3) both call it.

Order ​

Decorator expressions evaluate once, top to bottom. Their results apply bottom to top, so the decorator nearest def wraps first.

Aura
type Step = Callable[def(n: int64) -> int64]
type Wrapper = Callable[def(f: own Step) -> Step]

def tag(label: own str) -> Wrapper:
    print(f"evaluate {label}")
    return Wrapper(lambda [own label] own f: wrap(label.clone(), f))

def wrap(label: own str, f: own Step) -> Step:
    print(f"apply {label}")
    return Step(lambda [own f] n: f(n))

@tag("outer")
@tag("inner")
def run(n: int64) -> int64:
    return n

print(run(1))

This prints evaluate outer, evaluate inner, apply inner, apply outer, then 1.

The decorated constant initializes in declaration order with the module's other constants. A decorator expression that reads the decorated name reports AU2001, because the binding is not initialized yet. A trap inside a decorator stops module initialization, and no later decorator runs. An importing module never runs the decorators again.

Generic Decorators ​

A generic decorator infers its type parameters from each function it decorates. One traced serves every one-parameter function whose parameter is named args:

Aura
type Fn[A, R] = Callable[def(args: A) -> R]

def traced[A, R](f: own Fn[A, R]) -> Fn[A, R]:
    return Fn[A, R](lambda [own f] args: trace_call(f, args))

def trace_call[A, R](f: Fn[A, R], args: A) -> R:
    print("enter")
    return f(args)

@traced
def label(args: str) -> str:
    return f"<{args}>"

@traced
def square(args: int64) -> int64:
    return args * args

print(label("x"))
print(square(3))

Inference runs once per decorator, against the decorated function's exact type. A type parameter stands for a value type, never for a whole signature, so the decorator fixes the number of parameters, their names, and their modes. A function whose parameter has another name reports AU2015. A decorator whose result is a different type reports AU2002.

A decorator factory may be generic too. Its own arguments, such as the attempt count in @retry(3), fix what they can. The function it decorates fixes the rest:

Aura
"""A generic retry decorator, with the attempt count as its argument."""

type Fn[A, R] = Callable[def(args: A) -> R]
type Decorator[A, R] = Callable[def(f: own Fn[A, R]) -> Fn[A, R]]


def retry[A, R, E](attempts: own int32) -> Decorator[A, Result[R, E]]:
    """Builds a decorator that calls the function up to `attempts` times."""
    return Decorator[A, Result[R, E]](lambda [own attempts] own f: retrying(f, attempts))


def retrying[A, R, E](f: own Fn[A, Result[R, E]], attempts: own int32) -> Fn[A, Result[R, E]]:
    return Fn[A, Result[R, E]](lambda [own f, own attempts] args: attempt(f, args, attempts))


def attempt[A, R, E](f: Fn[A, Result[R, E]], args: A, attempts: int32) -> Result[R, E]:
    mut tries: int32 = 1
    while tries < attempts:
        match own f(args):
            case Ok(value):
                return Ok(value)
            case Err(error):
                print(f"attempt {tries} failed")
        tries += 1
    return f(args)


@retry(3)
def parse_port(args: str) -> Result[int64, str]:
    if args == "8080":
        return Ok(8080)
    return Err(f"bad port {args}")


def main():
    match own parse_port("8080"):
        case Ok(port):
            print(port)
        case Err(message):
            print(message)
    match own parse_port("http"):
        case Ok(port):
            print(port)
        case Err(message):
            print(message)

A retry calls the wrapped function again with the same argument, so it needs a shared or Copy parameter. A function that takes own arguments fails at the decorator with AU2002. Its help points to an explicit loop that builds a new argument for each attempt.

Restrictions ​

The checker rejects these with AU2047:

  • a generic decorated function
  • a decorated main
  • a parameter with a default, because a closure has no parameter defaults
  • a decorated function that returns a view
  • a decorator line that is not followed by a module-level def

A decorator inside a function body, or in a class body other than @property, reports AU2005. Decorators on classes, enums, and methods are not implemented.

Tooling ​

Hover on a decorated function shows its decorator lines above the signature. aura doc lists it as a function, with its decorators and docstring. aura fmt keeps decorator lines directly above their definition.

main ​

In the selected entry module, a local function named main is the entrypoint when the module has no executable top-level statements. Its only valid signatures are:

Aura
def main() -> int32:
    return 0
Aura
def main():
    print("done")

main takes no parameters and returns exactly int32 or None. A returned int32 becomes the requested host exit status. None means success. An imported function named main stays an ordinary imported function. A file cannot combine a local main with executable top-level statements.

Execution Model specifies the alternate top-level execution form, evaluation order, cleanup on return, and the 256-call runtime depth limit.

Grammar ​

Grammar is normative for function and method declarations, generic parameters and bounds, receiver and ordinary parameter forms, defaults, owned return annotations, and call arguments. Ordinary functions are module items. Nested function declarations are not accepted. Closures specifies expression lambdas.

Typing Rules ​

  • Every ordinary parameter has one declared type and a declaration-stable passing mode.
  • A call binds positional arguments, then named arguments. It substitutes inferred or explicit generic arguments, enforces bounds and exact types, and fills only legal defaults.
  • A call enforces inferred clone-safety obligations after substitution.
  • Every reachable path of a non-None function returns the declared type.
  • Shared or mutable access never authorizes moving a non-copy value into the result. A non-copy return requires an owned source.

Runtime Semantics ​

  • The callee target is resolved statically.
  • Supplied arguments evaluate left to right. Each result is captured before the side effects of later arguments.
  • Omitted defaults then evaluate afresh in declaration order.
  • A call creates one frame.
  • return transfers its value after the exited cleanups run. try may perform that return early.
  • Entry main maps None to success, and maps an int32 result to the requested host process status.

Ownership And Evaluation Order ​

  • Bare parameters grant shared access. An implementation may pass copy bits directly.
  • own parameters consume their arguments.
  • mut requires one exclusive mutable place and writes through it.
  • Borrowed default temporaries live through the call. Owned defaults are consumed. Mutable-borrow defaults are rejected as guaranteed lost writes.
  • A task start first stores owned captures, then invokes the target under its declared ABI.

Diagnostics ​

Compile-time diagnostics:

CodeMeaning
AU1101Malformed function, method, parameter, return, or call syntax.
AU2001The call target or referenced declaration could not be resolved.
AU2002Signature, function-value capability, parameter, default, return, bound, or entrypoint type mismatch.
AU2004Positional or named argument binding failed.
AU2005Focused guidance for an unavailable callable spelling. This includes a generic method value whose type arguments are neither written nor implied by an expected contract.
AU2015A function value's complete callable contract differs from the one required. The message names the differing slot, kind, or obligation.
AU2999Another callable rejection with no narrower compile-time code.
AU3001A moved argument was used.
AU3002Borrow or alias conflict.
AU3003Mutability violation.
AU3004Invalid parameter, receiver, return, or task-capture ownership mode.
AU3007A call specialization would duplicate non-cloneable random.Rng state, or could not satisfy a callable clone-safety obligation.
AU3008A task start target is an ordinary erased Callable, whose environment was never proven Transfer.
AU3010A returned view has an invalid escape, origin, caller place, kind, or provenance path.

Runtime diagnostics:

CodeMeaning
AU4001Call-depth or general call trap.
AU4002Arithmetic overflow or underflow in a callee.
AU4003Bounds or lookup violation in a callee.
AU4004Zero divisor in a callee.
AU4005Trapping resource or I/O failure in a callee.

A callee's AU4002, AU4003, AU4004, or AU4005 keeps the same typed Aura call frames and task ancestry on mid-level intermediate representation (MIR) execution and on direct-native execution.

Backend Support ​

Ordinary, returned-view, indirect function-value, generic, imported, associated, trait-dispatched, and maintained task-target calls are implemented for MIR execution and for direct native builds. Shared semantic checking and the forced parity matrix require identical call results and primary failures on both. Compiler analysis and the language server use the same resolved signature metadata, including inferred clone-safety obligations.

Limits And Implementation-Defined Behavior ​

Aura has none of these:

  • trait-object function interactions
  • Aura variadic functions
  • overloads
  • nested functions
  • mutable-parameter task targets

Closures specifies expression lambdas. They do not add nested item declarations. Written function types express bare shared, mut, and own parameter contracts.

Runtime calls are limited to 256 nested Aura frames. The host process's exit representation may narrow the requested int32 after it leaves Aura. Function binding and evaluation order are otherwise not implementation-defined.

Status ​

The contracts described above are implemented for functions, methods, generics, capture-free function values, default arguments, named arguments, ordinary owned returns, returned views, inferred clone-safety, task targets, and entrypoints. By-value expression closures are implemented.

The fixture crates/aura-compiler/tests/fixtures/run-pass/explicit_and_default_argument_order.au pins the supplied and default argument evaluation and capture rules on both backends.

Foreign function interface (FFI) v0 provides bodyless, direct-call-only extern "C" def declarations. They are not function values. FFI v0 specifies their restricted signatures.

Design record: supplied and default argument evaluation and capture follow architecture_docs/decisions/0015-explicit-and-default-argument-order.md, which is Accepted. By-value expression closures follow Accepted ADR-0037.

Aura 0.6.0 technical preview. Implementation baseline: bafda1ce8bf8adc108c22b8c5ec2ce409da1e012.