Expression and Call Semantics
Assignment Semantics
The language has two assignment operators:
=means logical value copy.<-means ownership transfer that invalidates a named source binding.
Syntax Snapshot (MVP)
- Function declarations:
fn name(p1: T, p2: &U, p3: V) -> ReturnType { ... }- Return types are required in function signatures.
- Return annotations do not accept ownership sigils; return values are always owned.
- Assignment:
let b = a(read-only view local; source stays valid)let @b = a(owned local initialized froma)let b <- a(read-only view local initialized by move; source is invalid afterward)let @b <- a(owned local initialized by move; source is invalid afterward)let b <- exprandlet @b <- exprare invalid whenexpris not a named placeplace.field = exprupdates a struct field through a named place path
- Array and slice types:
[T; N]is a fixed-size array type[T]is an owned dynamically sized array type
- Array literals:
[e1, e2, ...]is an array literal (Rust-like)[]is an empty array literal and needs array type contextf(<-x)explicit move from named bindingf(rvalue_expr)implicit move from rvalue
- Expression statements:
expr;discards the expression result regardless of type
- Integer arithmetic:
- unary
-xrequiresx: i64and returnsi64 - binary
+,-,*,/,%requirei64operands and returni64 - precedence follows
* / %above+ -, and unary-binds tighter than both
- unary
Copy Assignment (=)
- Source and destination remain valid after assignment.
- Copy assignment is valid only for copyable values.
- Runtime performs an eager owned copy for copyable heap-backed values.
Local Declarations
let x = exprcreates a read-only view local.let &x = exprcreates a mutable reference local.let x <- ycreates a read-only local that owns the moved value fromy.let @x = exprcreates an owned local.- Assigning to
xor borrowing&xrequireslet @xorlet &x = ...; moving<-xstill requireslet @x. - Assigning to
xis valid whenxwas declared withlet &x = .... let x <- yinvalidatesy, butxstill remains read-only after the move.
Read-only parameters and read-only locals do not have identical storage semantics:
- A parameter
x: Tmay read directly from caller-owned storage. - A local
let x <- ybecomes the new read-only owner after the move and does not alias a still-live source binding.
Move Assignment (<-)
<-is only valid when the source is a named binding/place.- Destination receives ownership and source becomes invalid.
<-on rvalues/temporaries is invalid (there is no name to invalidate).- Using moved-from source is a runtime or compile-time error (depending on checker stage).
Arrays and Slices
[T; N]is an owned value and participates in=,<-, and<-xlike other owned values.[T]is an owned runtime-sized array value and is represented as an owned contiguous sequence.- In parameter mode
T,[T]means a read-only slice view. - In parameter mode
&T,[T]means a mutable slice view. - Slice parameters can accept array arguments with element-compatible item types.
@box(expr)allocates a non-null pointer value of type*T.@array(init, len)constructs heap arrays ([T]) by inferringTfrominit.@as(T, x)performs compile-time-safe numeric casts only.@intCast(T, x)performs checked integer casts and traps on overflow.@intToFloat(T, x)converts integer values to floating-point values.@trunc(T, x)allows narrowing integer-to-integer and float-to-float casts.
Checked cast edge cases: @intCast(i8, 120i16) succeeds, while
@intCast(u8, -1i16) and @intCast(i8, 255u16) trap.
Expression-Oriented Semantics
The language is expression-oriented (Rust-style): control-flow constructs and blocks are expressions.
Unit Result
- Unit is implicit in syntax (no required
()literal in MVP). - A block with no tail expression yields unit.
whileyields unit.
if Expression Rules
if (cond) { then } else { other }is an expression.if (cond) { then }(noelse) is allowed only ifthenyields unit.- With
else, both branches must yield compatible result categories.
Pointer and Option Semantics
Pointers are non-null owned handles used for explicit indirection.
- Pointer types are written as
*T. @box(expr)constructs a pointer value of type*Twhenexpr: T.Option[T]is the built-in optional type constructor; useOption[*T]when a boxed value may be absent.*exprdereferences a pointer expression and reads the pointee as a value.- Field/index projection through a pointer base implicitly dereferences as needed (for example
node.next.valuewherenode.next: *Node). - Passing
&bwhenb: *Tborrows the pointee as&T. &*bis rejected.*Tadds exactly one explicit heap indirection whose allocation storesTinline.- The immediate payload
Tcannot itself be another pointer or a built-in heap-handle type such asStringor owned runtime-sized array values.
Pointer and Optional Construction Examples
let @head = Some(@box(Node { value: 1, next: None() }));match (head) { Some(node) => node.value, None() => 0 };
Result Discard Rules
- Any expression statement form
expr;discards the result. let _ = expr,let _ <- expr, andlet @_ <- exprare ordinary bindings to_(not special discard forms).
Iteration Semantics
The language does not expose first-class references, so iterator design avoids storing borrowed references in user-visible iterator objects.
for Forms
for (collection) |x| { ... }- Plain binder
|x|is a read-only view of each element.
- Plain binder
for (collection) |&x| { ... }- Mutable-borrow binder
|&x|follows&Tsemantics (exclusive mutable borrow per iteration).
- Mutable-borrow binder
for (<-collection) |x| { ... }- Consuming traversal form;
<-collectioninvalidates the named collection binding. - Elements are yielded to
xusing the normal plain-binder read-only view semantics.
- Consuming traversal form;
collection may be [T; N] or [T].
Normalization Rule
for (<-collection) |item| { ... }consumescollectionand invalidates its binding.for (collection) |<-item| { ... }is invalid; iteration does not move elements out directly.
Single-Form Principle
- Redundant forms are disallowed to keep one canonical way to express behavior.
<-is valid on the iterable expression only for consuming traversal from a named collection.
Call Semantics
Index and Slice Access
xs[i]reads through a read-only view.xs[a..b]produces a read-only slice view.&xs[i]and&xs[a..b]request mutable borrows.let x = place_exprandlet &x = place_exprcan bind references to fields/indexed items for the binding scope.- Local mutable borrows are checked for overlap: disjoint struct fields may coexist, while index/slice projections on the same root are conservatively treated as overlapping.
- Copyable indexed elements may still be copied into owned locals or other owned contexts when required.
- Plain slice views are not copied into owned values implicitly.
Argument Forms
f(x)- Valid for
T(read-only view) and@T(owned copy).
- Valid for
f(&x)- Valid only when parameter type is
&T.
- Valid only when parameter type is
f(<-x)- Explicit move into an owned parameter (
@T), invalidating named bindingx.
- Explicit move into an owned parameter (
f(rvalue_expr)- For
T, bare rvalues are borrowed for the duration of the call. - For
@T, bare rvalues are passed as owned temporaries.
- For
Parameter Mode Rules
For a parameter declared as T:
- Passing
f(x)borrows a read-only view. - Passing
f(&x)orf(<-x)is invalid. - The callee cannot assign through the parameter binding.
For a parameter declared as &T:
- Passing
f(&x)creates a mutable borrow. - Other argument forms are invalid.
For a parameter declared as @T:
- Passing
f(x)copies into a fresh owned value. - Passing
f(<-x)moves ownership and invalidatesx. - Passing
f(rvalue)moves the temporary into the callee.
<- exists only to invalidate named bindings; temporaries already have no source binding to invalidate.
Copy Strategy
Copyable heap aggregates (String, arrays, records) use eager copy semantics
for = and owned argument copies.
Linear values cannot have aliases other than statically-checked borrows.