Skip to main content

Design

18 posts

· 11 min read compiler design

Two ways to be generic: monomorphisation, and the day abstract types lowered without one

How Hew lowers generics, told as the evolution it actually was — from stamping out one mangled LLVM function per instantiation, through unifying dispatch under a single call authority, to representing an abstract type parameter directly in MIR so a generic body can lower once even when its type is never made concrete.

· 7 min read compiler design

Abstract type parameters with value witnesses, and a receive ABI that can borrow

Generics that lower without a concrete type yet, an actor receive path that takes a non-owning borrow of the message, and the smaller control-flow pieces — const globals, bare loop, break/continue — I'd been putting off.

· 11 min read compiler design

No GC, no double-free: ownership, copy-on-write strings, and fail-loud traps

How Hew's memory model actually works underneath — single ownership with compiler-inserted drops, strings as copy-on-write refcounted values over a header-aware allocator, and arithmetic that traps with distinct exit codes instead of wrapping silently.

· 8 min read compiler design

String Becomes Copy-on-Write and the Last Legacy Dispatch Path Is Retired

Two days on ownership — retiring the old HashMap/HashSet dual-dispatch, turning `string` into a copy-on-write refcounted value, and adding `&T` borrow markers that fail closed when you try to clone the wrong way.

· 9 min read compiler design

Collections get a real ABI, closures get capture inference, and dispatch goes through one authority

Two days on the substrate under generic data structures — what a layout descriptor actually carries, why the closure checker now infers move vs borrow, and the moment I noticed three different pieces of the compiler each had their own opinion about how a method call lowers.

· 8 min read compiler design

Hardening the Seams Instead of Adding Surface

Two days of making the compiler refuse to lie — fail-closed gates that reject unsupported shapes at compile time, a memory-safety sweep through the runtime, and a `hew check` that finally runs the real pipeline.

· 11 min read compiler design

Talking between nodes: the CBOR wire envelope and a SWIM cluster that gossips

The distributed substrate for v0.5 — replacing a hand-rolled binary wire format with a CBOR envelope described by a CDDL schema, deleting 2,498 lines of wire.rs, and giving the cluster a phi-accrual failure detector that adapts instead of a fixed timeout. Plus cross-node sends that stay type-checked.

· 11 min read compiler design

State as a type: how machine declarations become tagged-union dispatch

The machine construct in detail — what the front end checks before a single instruction is emitted, how a state declaration becomes a tagged-union LLVM layout, and why I'd rather reject an illegal transition at compile time than catch it at runtime. Includes the parts that don't fully work yet.

· 7 min read compiler design

State Machines Reach Codegen and Closures Become First-Class Values

Machine declarations used to type-check and then stop — no runtime behaviour. Now they lower all the way through to native code. Plus pipe-form closures, a canonical pointer spelling, and a TCP-as-Stream bridge.

· 7 min read compiler design

Supervisors That Wire Themselves and Crash Hooks With Real Trap Codes

A supervisor declaration now lowers to a spawn-and-wire bootstrap that returns a typed pid, and a crash hook can finally see the trap code that killed a child. The bottom half is real; the top half is where I am today. Underneath, int and uint are gone and module-qualified calls like math.sqrt resolve.

· 11 min read compiler design

Sends are method calls, crashes carry codes: the actor and supervision model

A walk down the actor stack in v0.5. Why I deleted the `<-` send operator, how a method call lowers to spawn/send/ask through an execution context, and how supervision fires `#[on(crash)]` with the real trap code.

· 10 min read compiler design

WASM parity for free, almost: four targets from one MIR and the seams that weren't free

Native and wasm32 share the same MIR and the same textual LLVM IR — they only diverge at the TargetMachine. That makes most of WASM support genuinely free. This is about the parts that weren't — traps, blocking recv, timers, and a use-after-free that only exists on WASM.

· 8 min read compiler design

Retiring the C++ Backend and Teaching the Compiler to Monomorphise

The day the pipeline became Rust top to bottom — over fifteen hundred C++/MLIR files deleted, and generics finally grew a spine through the IR ladder.

· 9 min read compiler design

Decide in MIR, carry in nothing: why we deleted the MLIR backend before shipping a binary

The backend cutover for v0.5 — the borrowed-String double-free that took six revisions to kill, why each revision grew the C++ walker registry, and how I convinced myself it was safe to delete a 1,568-file MLIR subtree and lower straight from MIR to LLVM IR.

· 8 min read compiler design

Making integers honest and making failure loud: widths, records, and trap-on-overflow

Today the value layer stops being vague. I gave integers real widths and platform-sized isize/usize, wired records all the way through with auto-derived markers, and made arithmetic that overflows trap instead of silently lying.

· 7 min read design compiler

Deleting the Send Operator to Make the Actor Model Honest

First day of the v0.5 rewrite. I tore out the `<-` send operator and the spawn-lambda form, and spent the rest of the day deciding what the actor and machine surface should look like before a single line of it lowers to a binary.

· 5 min read launch design

Launching Hew

Hew puts actor state, supervision, protocol shape, and cleanup into the compiler instead of scattering them across conventions.

· 6 min read compiler design

The Double-Free and the Sized Integers

Two breaking changes in v0.4.0 that were really just cleaning up things I should have gotten right earlier — explicit teardown for resource types, and int uniformity in the stdlib.