Skip to content

Statements ​

Statements introduce and update bindings, control execution, or evaluate an expression for its effects. This page defines when each statement is legal and what it does when it runs. The exact syntax is normative in Grammar. Compile-time legality is normative in Static Semantics. Runtime sequencing and cleanup are normative in Execution Model.

Statements, Items, And Suites ​

Aura 0.6 has these statements:

  • binding and assignment
  • local view bindings
  • expression statements
  • return
  • assert
  • if / elif / else
  • while and for
  • statement-form match
  • with
  • break, continue, and pass

Class, enum, function, trait, and implementation declarations are items, not statements. Items appear only at module level. Declaration members such as fields, enum variants, and methods appear only in the item bodies that permit them. Aura does not support nested functions, classes, enums, traits, or implementations.

A compound statement header ends with : and NEWLINE. An indented suite follows it. A suite contains one or more statements:

Aura
if ready:
    print("ready")
    record_success()

One-line suites such as if ready: print("ready") are not valid. Blank lines and comment-only lines do not make a suite nonempty. Use pass when a suite needs no operation.

A logical newline ends a statement. A physical newline inside an open delimiter does not end a statement. Aura has no semicolon, and a physical line cannot hold more than one statement.

Bindings And Assignment ​

The first assignment to a simple name introduces a binding:

Aura
name = "aura"
count: int32 = 0

The binding's type is its annotation when one is present. Otherwise it is the initializer's type. After contextual literal inference, the initializer must have exactly that type.

mut makes a new binding assignable and usable as a mutable place:

Aura
def main():
    mut count: int32 = 0
    count = 1
    count += 2

Reassignment requires an existing mutable binding and keeps its type. mut does not make a binding dynamically typed. It also does not make a value mutable through every alias.

from is a contextual identifier. It is a legal binding and assignment target whenever the tokens do not form a from-import:

Aura
def main():
    mut from = "cache"
    from = "network"

Assignment Targets ​

An assignment target starts with a name and may continue through fields or indices:

Aura
point.x = 4.0
values[0] = 9
counts["ready"] = 2
user.profile.name = "Ada"

A place-assignment target cannot contain a call.

A type annotation is allowed only on a simple-name target. mut also belongs only to a new simple-name binding. These forms are invalid:

Aura
# Invalid.
# point.x: float64 = 4.0
# mut point.x = 4.0

Each kind of projected target has its own rules:

  • Field assignment requires a mutable base place and a declared field.
  • List index assignment uses the int64 index domain.
  • Simple dict index assignment requires exactly the dictionary's key type. It replaces the entry for an equal key or inserts a new entry, so an absent key is not an error for simple assignment. It works for every value type. The key and the value are owned storage positions, so each one is consumed when it is non-copy. This matches set(key: own K, value: own V).

A tuple unpack target contains only names and parenthesized name targets, nested to any depth:

left, right = pair
name, (x, y) = record

The right side is evaluated once. Its tuple shape and element types must match the target exactly. A top-level comma marks the statement as an unpack rather than an expression. A tuple value expression itself requires parentheses. Tuple unpacking uses plain =. It does not accept a type annotation, a leading mut, compound assignment, a member leaf, or an index leaf.

Compound Assignment ​

Aura supports the complete arithmetic compound-assignment family +=, -=, *=, /=, %=, and //=:

Aura
count += 1
total *= scale
pages //= page_size

A compound assignment follows these rules:

  • Target. The target must be an existing, mutable, initialized place. The statement selects that place once.
  • Operator. It uses exactly the dispatch of the corresponding binary operator. That includes an applicable user-defined operator trait, for both a root target and a projected target.
  • Copy target. The current value is copied before the right operand is evaluated. The result is stored into the place selected at the start. Side effects in the right operand therefore cannot change the captured left operand or redirect the store.
  • Non-copy target. A non-copy root or projected target stays borrowed while the right operand is evaluated. An overlapping mutable borrow or consumption reports AU3002.
  • Result type. The result must have the target's existing type.

Direct indexed compound assignment requires a copy list element or a copy dict value. The checker rejects a non-copy indexed element. A read-modify-write on it would need either a hidden clone or a destructive move before an operation that may fail. Instead, read the element safely or transfer its ownership explicitly, then write it with simple assignment. For a dict, use get(key) or remove(key) followed by simple assignment.

Runtime overflow and division behavior match the corresponding expression operator. Integer /= is rejected with the same teaching diagnostic as integer /. Use //= for an integer floor quotient. Floating-point /= is true division. //= uses the builtin numeric or Duration rule when one applies. Otherwise it may dispatch through FloorDiv.floor_div.

Assignment Evaluation ​

  • A simple-name or field assignment evaluates its right side before it creates or updates the target.
  • An indexed assignment evaluates the collection place, then the index or key, then the right side. A non-copy collection base stays borrowed through those later inputs. An overlapping mutable borrow or consumption reports AU3002. No hidden deep clone is inserted.
  • A simple dict assignment captures the key before it evaluates and consumes the value. A non-copy key is consumed when it is captured. Side effects in the value therefore cannot change the selected key.
  • Reassigning exactly the moved binding or field reinitializes that place, provided the new value has the required type.
  • A failed checked mutation produces its documented runtime failure or typed result. It does not create a separate language-level contract for partial assignment.

See Ownership And Borrowing for moves, partial field moves, and mutable-place rules.

View Bindings ​

view name = place creates an immutable shared alias. view mut name = place creates a mutable alias that writes through to its source and cannot be rebound. The initializer must resolve to one of these places:

  • a supported addressable root
  • a field path
  • a fixed tuple position
  • a list element or dictionary entry, optionally followed by a field or tuple projection inside the element
  • an existing view

The checker rejects computed temporaries. These examples update the source through a view:

mut pair = (1, 2)
view mut second = pair[1]
second = 7
print(pair)

mut users = [Profile(name="ada", visits=1)]
view mut visits = users[0].visits
visits += 1
print(users[0].visits)

The view's pointee type is inferred. Assigning through a mutable view changes the source. It does not retarget the view.

An element or entry selector is evaluated once, when the view is created. A position outside the list or an absent key fails at that point with AU4003. Rebinding the index variable later does not move the view.

A view borrows its source while it is live. This borrow is called a loan. Static final-use analysis ends a loan as early as control flow safely allows. While the loan is live, the checker rejects overlapping mutation, move, rebind, cleanup, and any other mutable loan. Overlap follows these rules:

  • Views of two different literal positions or keys in one collection are disjoint.
  • A computed selector overlaps every element.
  • Any structural mutation of the collection, such as append, remove, or clear, overlaps every element view.

Scope exits and control-flow exits release active loans before outer cleanup runs.

An assignment whose target projects inside an element or entry, such as users[i].visits = 5 or users[i].visits += 1, writes through a mutable element loan that lasts for the statement. The element is updated in place. It is never copied out and written back.

Expression Statements ​

Any expression may be used as a statement when its value is not needed:

Aura
print("ready")
queue.close()
counter.increment()

The expression is fully evaluated, including its moves, mutations, I/O, and runtime failures. Its value is then discarded. A discarded Result is not propagated implicitly. Use try or match when a failure must affect control flow.

return ​

return is legal only inside a function or method:

Aura
def answer() -> int32:
    return 42

The expression is evaluated before control returns. Its type must equal the declared return type. Bare return produces None and is valid only where None is a valid return:

Aura
def maybe_log(enabled: bool):
    if not enabled:
        return
    print("enabled")

Inside a function declared to return a view, return view [mut] place hands the caller a matching loan derived from the named from origin.

A function that does not return None must return on every statically reachable path. A return runs active with cleanups in reverse nesting order before control reaches the caller.

Conditional Statements ​

An if with zero or more elif branches and an optional else runs at most one suite:

Aura
if value < 0:
    print("negative")
elif value == 0:
    print("zero")
else:
    print("positive")

Conditions must have exactly type bool. Aura does not convert strings, numbers, collections, resources, or classes by truthiness.

Conditions are evaluated in source order until one is true. Only the selected suite runs. The checker analyzes each branch independently. It then merges ownership, partial-move, and initialization state conservatively across the paths that can continue.

A condition that tests a union place with is None or is not None narrows that place inside the selected suite. The narrowing also holds after the statement when the other branch cannot continue. Each elif condition is checked under the facts of every failed condition before it.

while ​

A while statement evaluates its condition before each iteration:

Aura
def main():
    mut attempts = 0
    while attempts < 3:
        attempts += 1

The condition must have type bool. If the condition is false the first time, the body runs zero times. Aura 0.6 has no loop else clause.

A while condition that tests a union place with is None or is not None narrows the place inside the body. A continue in the body after such a test narrows the rest of that iteration. The condition runs before every iteration, so its narrowing holds in the body on every pass. This is true even when the body assigns the place later.

A fact established before the loop survives into the condition and the body only when no iteration can invalidate it. The checker treats the loop header as a fixed point over the loop entry and every backedge, the path from the body back to the header. A body that assigns such a place is therefore checked again without the entry fact.

The checker rejects the first move of a non-copy outer value inside a repeatable loop when that move could make a later iteration invalid. Reinitialize the place on every continuing path, or restructure ownership explicitly.

for Iteration ​

A for statement binds one name, or recursively unpacks one tuple target, for each value from an iterable:

Aura
for value in values:
    print(value)

for name, count in records:
    print(name)
    print(count)

Every target leaf is local to the body and does not escape. A leaf cannot shadow a name already visible in the same scope. A tuple target must match the yielded tuple shape exactly.

As with while, a narrowing fact established before the loop survives into the body only when no iteration can invalidate it. When the body assigns the place, test it again inside the body.

Use for value in own values: when the loop deliberately consumes a list or set and needs owned element bindings. Each owned binding, including every leaf of a tuple target, is a mutable place, so value.append(1) or a mut self method call works on it. The collection moves once, at loop entry, into a source private to the loop. Reinitializing the consumed values binding in the body does not switch or shorten the active iteration.

Maintained iterable forms include:

FormBehavior
for i in range(n):Yields int64 values from zero up to n, excluding n.
for i in range(start, end):Yields int64 values from start up to end, excluding end.
for value in values:Retains the list and yields shared access for non-copy elements.
for value in own values:Consumes the list and yields owned elements.
for value in mut values:Binds each element as a mutable element view. Writes land in the list at once. The iterable place must be mutable.
for value in set:Retains the set and yields shared-borrowed access.
for value in own set:Consumes the set and yields owned elements.
for value in queue:Receives queue items under the scheduler-aware queue iteration contract.
for index, value in enumerate(seq):Yields (int64, element) pairs, counting positions from zero.
for left, right in zip(first, second):Yields one pair per shared position and stops at the shorter sequence.

for value in mut set: is not supported in Aura 0.3.

When an iterable yields tuples, the loop form decides the ownership of each non-copy tuple leaf:

  • Bare, shared collection iteration gives the leaves shared provenance.
  • own collection iteration gives owned leaves.
  • Bare Queue iteration receives an owned item and gives owned leaves.
  • mut iteration with a tuple target is rejected. Bind the tuple to one name and update its positions through it.

Queue Iteration ​

Queue iteration receives values instead of traversing places. Each item arrives owned, and the queue handle is a copy value. So own and mut are rejected for Queue iteration. Use the bare form.

The bare form evaluates and copies the Queue handle once, at loop entry. It does not freeze the source binding. Rebinding the source in the body does not switch later receives to another queue. Queue iteration ends according to the close, cancellation, producer-completion, and task-failure rules in Concurrency.

enumerate And zip ​

enumerate and zip are loop forms known to the compiler, not callable values. They are legal only as the iterable of a for statement. Naming either one anywhere else reports AU2005, and the message names the loop spelling. A user declaration of either name shadows the loop form, so an existing def zip(...) keeps its ordinary call meaning.

Aura
hosts = ["alpha", "beta"]
ports = [80, 443, 8080]

for index, host in enumerate(hosts):
    print(index)

for host, port in zip(hosts, ports):
    print(port)

Both forms read their operands by position, so each operand must be a list[T] or a set[T]. A Range or Queue[T] operand reports AU2002. Both forms use the bare-loop borrow default:

  • An ownership modifier on the loop reports AU3002.
  • Every operand stays shared-borrowed and frozen for the whole loop.
  • A non-copy element binding is a shared borrow and cannot be moved out.

enumerate takes exactly one operand and zip takes exactly two, both positionally. Any other arity, or a named argument, reports AU2004.

zip stops as soon as any operand has no value at the current position. It performs min(len(first), len(second)) iterations and never observes the tail of the longer sequence.

Range Iteration ​

Range iteration accepts only the bare form. Every yielded int64 is an independent copy. mut has no place to write back through, and own has nothing to transfer. Either modifier reports AU3004. The diagnostic explains that ownership modifiers do not apply to these copy values and suggests for item in range(...):.

break And continue ​

break and continue are legal only inside for or while:

Aura
for value in range(10):
    if value == 5:
        break
    if value % 2 == 0:
        continue
    print(value)

break exits the nearest loop. continue starts that loop's next iteration. If either one exits an active with scope, that scope is cleaned up before control transfers.

Match Statements ​

A statement-form match evaluates its scrutinee exactly once and tries its arms in source order. The first matching arm runs:

Aura
match result:
    case Result.Ok(value):
        print(value)
    case Result.Err(message):
        print(message)

Every statement arm contains an indented suite. Inline statement arms such as case Result.Ok(value): print(value) are not valid. Inline arms exist only in match expressions whose arm body is one expression. See Expressions.

A match over an enum or a boolean must be exhaustive unless _ covers the rest. Integer, float, and string literal matches require _, because their value spaces are open. The checker rejects duplicate, unreachable, type-incompatible, and wrong-arity patterns.

The scrutinee's ownership depends on the form:

  • match own value consumes a non-copy scrutinee. A non-copy tuple is consumed as one whole value and unpacked into owned pattern bindings.
  • Bare match value keeps ownership and exposes shared access to enum payloads and tuple leaves.
  • match mut value allows enum-payload mutation and writeback. A tuple pattern is rejected, because the minimal tuple support has no recursive mutable tuple writeback.

See Enums And Pattern Matching for pattern forms.

with And Context Managers ​

with runs a body while one or more context managers are active, and runs each manager's exit exactly once when control leaves the body. A context manager is a value whose type defines how to enter and leave a scope.

Aura
class Tracer:
    name: str

    def __enter__(mut self) -> None:
        print("enter " + self.name)

    def __exit__(mut self) -> None:
        print("exit " + self.name)

def main():
    with Tracer(name="outer") as outer, inner = Tracer(name="inner"):
        print("body")

This program prints enter outer, enter inner, body, exit inner, and exit outer.

Items ​

Each item has one of two equivalent forms, name = expression or expression as name. Both forms evaluate and consume the expression, then bind name in the body.

A header may list several items separated by commas. Items enter left to right, and each later item can use the bindings of earlier items. Exits run in reverse order. A header with a trailing comma, a missing as name, or a missing item is rejected with AU1101.

A failure while an item is constructed or entered runs the exits of the items already entered. The failing item's own exit does not run, because it is not yet registered.

The Manager Protocol ​

A user class is a context manager when it declares the following methods:

MethodAccepted signaturesRole
__enter__def __enter__(mut self) -> None or def __enter__(mut self) -> view [mut] T from selfOptional. Runs after the item expression succeeds and before the body.
__exit__def __exit__(mut self) -> None or def __exit__(mut self) -> Result[None, E]Runs once when control leaves the body.
closedef close(mut self) -> NoneThe exit of a class that has no __exit__.

A class with __exit__ uses it and ignores any close method. Generic classes follow the same rules after their type arguments are applied. Any other shape is rejected with AU2017.

Builtin resources define their own cleanup and have no entry. These include fs.File, the net socket, listener, and HTTP types, process.Child, process.Pipe, process.Supervisor, and TaskGroup:

Aura
with file = try fs.open("data.txt"):
    text = try file.read_all()
    print(text)

Entry Views ​

When __enter__ returns a view, the item name binds that view, not the manager. The manager stays in a hidden binding that the body cannot name. The view is shared or mutable as __enter__ declares:

Aura
class Lock:
    values: list[int64]

    def __enter__(mut self) -> view mut list[int64] from self:
        return view mut self.values

    def __exit__(mut self) -> None:
        print(len(self.values))

def main():
    with Lock(values=[]) as values:
        values.append(1)
        values.append(2)

The view ends before the exit runs. A view cannot escape the body. Returning it or storing it is rejected with AU3010. A manager without an entry view is an ordinary named binding in the body. Mutating it while a view into it is live is rejected with AU3002.

Exits That Return Result ​

An __exit__ that returns Result[None, E] acts like try on the normal paths out of the body: fallthrough, return, break, and continue. Ok continues. Err returns from the enclosing function with the error, after the outer managers exit. The enclosing function must return Result[_, F], where F is E or has a From conversion from E. Otherwise the checker reports AU2017.

Aura
class Flush:
    pending: int64

    def __exit__(mut self) -> Result[None, str]:
        if self.pending > 0:
            return Result.Err("unflushed writes")
        return Result.Ok(None)

def save() -> Result[int64, str]:
    with sink = Flush(pending=1):
        print("writing")
    return Result.Ok(1)

def main():
    match save():
        case Result.Ok(count):
            print(count)
        case Result.Err(error):
            print(error)

When the body is already leaving with an error, the exit's error does not replace it:

  • Under try propagation, the body's error is returned and the exit's Err is dropped.
  • Under a trap, the trap stays primary. The exit's Err is added to the trap diagnostic as a note.
  • Under cancellation, the exit's Err is dropped and the task ends cancelled.

When Exits Run ​

Each registered exit runs exactly once when control leaves the body by:

  • normal fallthrough
  • return
  • break or continue that exits the scope
  • try error propagation
  • a maintained Aura runtime failure
  • task cancellation

An exit runs while cancellation is still pending, so a cancellable wait inside an exit observes it. Nested exits run in reverse registration order. If the body is already failing and an exit traps, the body's diagnostic stays primary.

Scheduler abandonment runs no __exit__ or close. See Execution Model.

aura run and native builds share this contract. aura run executes the compiler's mid-level intermediate representation (MIR) through the maintained MIR runtime. Native builds use the maintained native execution paths. Backend parity tests enforce the common contract.

See examples/resources/context_managers.au for a runnable program.

assert ​

An assertion checks an invariant. It either continues or stops the program with an unrecoverable runtime diagnostic, called a trap:

assert ready
assert response_code == 200, "expected a successful response"

An assertion follows these rules:

  • The condition must have exactly type bool. The optional message must have exactly type str.
  • The condition is evaluated exactly once. A true condition falls through without evaluating the message.
  • A false condition evaluates the message exactly once and traps with AU4001. With no message, the failure text is exactly assertion failed. Otherwise the supplied str is kept exactly, including an empty or whitespace-only value.
  • The diagnostic points to the assert keyword.
  • A trap raised while evaluating the condition or the message happens first and stays primary.
  • A failed assertion runs active with cleanups. The assertion stays primary if cleanup also fails.

For static analysis, an assertion falls through normally. It does not refine the type or possible values of any later expression. The compiler does not strip assertions in any build mode.

Assertions are valid executable top-level statements in a script entry module. The usual rule against combining top-level execution with a local main still applies.

See Assertions for the complete contract and an executable example.

pass ​

pass does nothing and introduces no binding:

Aura
def placeholder():
    pass

It must be on its own logical line. Use it for an intentionally empty function, method, class, trait, implementation, or control-flow suite. An enum body still needs at least one variant, and pass does not count as a variant.

Module Constants, Imports, And Execution ​

Imports are module elements, not executable statements. Aura accepts these forms:

Aura
import util.math
from util.math import double, triple
import agents.telemetry as telemetry
from agents.telemetry import record as record_event, Event
  • Import paths are dot-separated identifiers.
  • A module import may bind the whole module under a local alias.
  • Each name in a from-import may have its own local name. Renamed and direct imports may appear together.
  • A renamed import introduces only its local name into the importing module.

Aliases are static local names for resolved modules and declarations. They do not change visibility, nominal identity, trait implementations, initialization storage, or the package path used for resolution.

A from-import list may be wrapped in parentheses, span lines, and end with a trailing comma. Aura does not accept wildcard imports or relative-dot imports. Packages defines import resolution and visibility.

An immutable binding at module level is a module constant:

Aura
message = "hello"
public retry_limit: int64 = 3

def main():
    print(message)

The initializer is required. The checker rejects mut module storage and later assignment. Constants may coexist with a local main. Constants in reachable dependencies initialize before the entry runs. Names And Scopes defines the full scope, order, visibility, and ownership rules.

An entry module may also contain executable top-level statements:

print(message)

These statements run in their stored source order, after the reachable module constants are ready. An entry module with executable top-level statements cannot define a local main. The top-level statements of an imported module do not run as a side effect of the import.

A top-level mut name = value statement declares name as a local of the entry script. A later name = value, or a compound assignment such as name += value, reassigns that same local:

Aura
mut count = 0
count = count + 1
count += 1
print(count)

A bare top-level binding of a new name is always a module constant, wherever it appears among the entry statements. It cannot read a top-level script local, because constants initialize before the entry runs. To keep the computation in the entry script, declare the new binding with mut. Otherwise, move the work into main.

Functions and Execution Model define the accepted main signatures and process exit behavior.

Contextual Legality Summary ​

A statement that parses is not legal in every context:

  • return requires a function or method.
  • break and continue require an enclosing loop.
  • Reassignment and compound assignment require an existing mutable place.
  • Member and index assignment require a mutable base and cannot declare a type or use mut.
  • Conditions require bool, not truthiness.
  • Assertion conditions require bool, and assertion messages require str.
  • Match arms must satisfy the compatibility, reachability, and exhaustiveness rules.
  • with requires a context manager and keeps its exit capability intact.
  • Items cannot appear inside suites.
  • Module constants are immutable and cannot use mut or reassignment.
  • Module constants are the bindings above the first top-level statement. They cannot read top-level script locals, which initialize later.
  • An entry module cannot mix executable top-level statements with a local main.

The complete checker rules are normative in Static Semantics. Ownership effects are normative in Ownership And Borrowing.

Grammar ​

Grammar is normative for the simple and compound statement productions, suite indentation, binding and assignment targets, loop modifiers, match arms, and with forms. Statements end at a NEWLINE. Aura has no semicolon-separated statements and no inline compound statements.

Typing Rules ​

  • A binding infers or checks one type. Reassignment keeps that type.
  • Conditions have exactly type bool.
  • Return values match the enclosing signature.
  • The iterable determines the loop binding contract.
  • Match patterns must be compatible, reachable, and exhaustive where required.
  • with accepts only a context manager. An exit that returns Result[None, E] requires a compatible Result return type in the enclosing function.
  • Assertion conditions have exactly type bool, and assertion messages have exactly type str. An assertion does not refine later control flow.
  • is None and is not None conditions refine a stable union place on the paths they select, as described in Static Semantics.
  • Contextual legality is checked after parsing.

Runtime Semantics ​

  • Statements run in source order within the selected suite.
  • Simple-name and field assignment evaluate the right side before writing the target.
  • Indexed assignment evaluates its collection and its index or key before the right side. A simple dict assignment captures its owned key before any side effects of the value.
  • Compound assignment uses the corresponding binary dispatch and stores into the target it selected once. A copy target is captured before the right side. A non-copy root or projected target stays borrowed across the right side.
  • Direct indexed compound assignment reads only a copy element. It traps with AU4003 when a dictionary key is absent.
  • A conditional runs at most one branch.
  • A loop tests its condition, or receives a value, before each body.
  • A match evaluates its scrutinee once.
  • with registers an exit only after the item expression and its __enter__ succeed. Items enter left to right and exit right to left.
  • An assertion evaluates its condition once. It skips the message on success and evaluates the message once before failing.
  • A control transfer runs every exited cleanup in reverse registration order.

Ownership And Evaluation Order ​

  • A binding owns, copies, or borrows its initializer, depending on type and context.
  • own list and set iteration consumes the collection once into a loop-private source.
  • Bare collection iteration retains its selected place and freezes it.
  • Queue iteration copies the handle once and receives items that are already owned.
  • Simple dict indexed assignment consumes non-copy keys and values into owned storage.
  • Direct list and dict indexed compound assignment is limited to copy elements.
  • Assignment to a place invalidates conflicting borrows and reinitializes the written place.
  • Branch and loop analysis conservatively keeps any move that may reach a continuing path. No control-flow join restores ownership implicitly.

Diagnostics ​

Compile-time diagnostics:

CodeMeaning
AU1101Malformed statement or suite syntax.
AU2001Unresolved name or target.
AU2002Expected-type, condition, iteration, match, return, or assignment mismatch.
AU2003Unsupported compound-assignment operator.
AU2004Call or target argument binding failed.
AU2005Unsupported syntax or feature for a Python-shaped statement.
AU2013A union match is missing a type arm, duplicates one, or cannot reach one.
AU2014A member use through a place whose None-test narrowing was ended by an assignment, a mutable match, or a call with mutable access.
AU2017A with item is not a context manager, or its exit Result does not fit the enclosing function.
AU2999An exhaustiveness, contextual-legality, or unsupported-statement rejection with no narrower code.
AU3001Use of a moved place.
AU3002Borrow conflict. This includes a later access that mutably borrows or consumes an overlapping, retained non-copy target of a compound or indexed assignment.
AU3003An immutable target was used mutably.
AU3004Invalid loop, parameter, or ownership mode.
AU3005A non-copy direct indexed read.
AU3006A non-copy indexed compound assignment.

Runtime diagnostics:

CodeMeaning
AU4001General statement trap.
AU4002Numeric range, overflow, or underflow failure.
AU4003Bounds or lookup violation.
AU4004Zero divisor.
AU4005Trapping resource or I/O failure. This includes a cleanup failure when no earlier body failure stays primary.

A failed assertion reports AU4001 with assertion failed or the exact custom message, and points to its keyword.

Backend Support ​

Every implemented statement form uses the same checker and MIR lowering for MIR execution and for direct native generation. The backend-parity suite forces cleanup, loop, match, task, and runtime-trap behavior to agree. Where direct lowering is unsupported, it is contained rather than silently given different semantics.

Limits And Implementation-Defined Behavior ​

  • A suite requires a real statement.
  • Loops have no else clause.
  • Statement match arms cannot be inline.
  • A statement can span physical lines only inside an open (, [, or {. Backslash continuation is not available.
  • Items cannot nest inside suites.
  • Range iteration yields copy int64 values and accepts only the bare form, as described above.

No statement evaluation order is implementation-defined.

Status ​

Bindings, assignments, expression statements, return, assert, conditionals, loops, match, scoped cleanup, pass, imports, and top-level execution in entry modules are implemented as described. Tuple targets for assignment and loops are implemented.

Class and collection destructuring, loop else, exception statements, yield, raise, async, and nested declarations are not available. try is an expression over Result.

Design record: the loop ownership modes are those accepted in ADR-0006. One-time iterable selection is the accepted ADR-0017 rule. Tuple assignment and loop targets follow Accepted ADR-0026.

Aura 0.6.0 technical preview. Implementation baseline: 4d7e076de9ff6e2e056cdec10881d4bf421358de.