AHA! Lang 1.4.1–1.4.3: Complete Structs, 384 Tests
Three consecutive releases bring structs from literals to function parameters and return values — with 384 automated tests.
August 16, 2026
AHA! Lang 1.4.1–1.4.3: Complete Structs
Three consecutive releases — 1.4.1, 1.4.2, 1.4.3 — complete the first pillar of the Phase 2 roadmap: structs fully working at runtime. From literal codegen, to field mutation, and finally structs as function parameters and return values.
1.4.1 — Struct Codegen
struct Point { x, y } now produces real LLVM struct types:
struct Point { x, y }
let p = Point { x: 3, y: 4 };
let total = p.x + p.y; // 7- Literals
Point { x: 1, y: 2 }are built via LLVMinsertvalue - Field access
p.xreads viaextractvalue - Typed fields:
name: string, age: intare honored at runtime —stringfields use{i8*, i64}layout,intfields usei64 - Types are checked at compile time: assigning a string to an
intfield is an error - Field access preserves the declared type, so
len(p.name)andp.first + p.lastwork
1.4.2 — Field Mutation
p.x = value becomes a true lvalue:
p.x = 9; // load struct → insertvalue → store back
p.name = "baru"; // string fields can be mutated too- The parser now treats
=as an infix operator —AssignmentExpressionhas a generictarget, opening the door to array assignmentarr[0] = xin the future - Fixed:
Assignmissing from the precedence table - 373 tests passing
1.4.3 — Struct as Parameter & Return Value
Functions now accept structs by value and return them:
fn sum(p) { p.x + p.y }
sum(Point { x: 40, y: 2 }); // 42 — literal passed directly as an argument
fn make(x, y) { Point { x: x, y: y } }
sum(make(20, 22)); // 42 — chained: return value flows straight in
fn shifted(p) { p.x = p.x + 100; p }
let q = shifted(p); // mutation only touches the local copysum(make(20, 22))— a struct return value flows directly into another function- String fields work inside functions too:
len(p.name)+p.age - The pre-pass type inference now recognizes
StructLiteralandFieldAccess, with a fixpoint so parameter types stabilize even when call sites appear later in the source
Quality: 417 Automated Tests
| Release | Tests | Feature |
|---|---|---|
| 1.4.0 | 84 | Type system, strings, loops |
| 1.4.1 | 363 | Struct codegen |
| 1.4.2 | 373 | Field mutation |
| 1.4.3 | 384 | Struct param/return |
| 1.4.4 | 404 | Type annotations & inference |
| 1.4.5 | 417 | Generic functions |
All verified via CI on the development branch — cargo check, cargo test --test-threads=1, cargo build --release — and the results are deterministic.
New in 1.4.5: Generic Functions
fn pick<T>(a: T, b: T) -> T — functions with generic type parameters, with the concrete type inferred from each call site:
fn id<T>(x: T) -> T { x }
fn pick<T>(a: T, b: T) -> T { if a > b { a } else { b } }
fn first<A, B>(a: A, b: B) -> A { a }
pick(3, 7); // 7 — instantiated as pick_Int
len(id("hello")); // 5 — instantiated as id_StringEach unique (name, concrete types) combination becomes a separate LLVM function at compile time — monomorphization with zero runtime cost. Return types can be annotated (-> T, -> int), and generic functions can call other generic functions.
Stabilize First, Then Move Step by Step
With F1 (structs), F2 (type annotations), and F3 (generics) stable, the roadmap continues to F4 — Module System & Package Manager (aha install). Resource lifetimes (F5) remain frozen until the foundation is stable — per the principle stabilize first, then move step by step.
Follow the development on the GitHub repo and check the compiler-verified Syntax Reference.