Mica proves at compile time what other systems languages leave to the debugger: heap lifetimes, data-race freedom, and array bounds — with no garbage collector, no borrow checker, and no lifetime annotations.

Mica is a statically typed systems language with an optimizing native backend. It compiles to Linux x86_64 and AArch64 ELF binaries with DWARF v5 debug information, links directly against any C library on the system in both directions, and carries no runtime you did not ask for.

It is its own language. The structured-programming discipline it takes seriously — readable declarations, explicit types, no hidden control flow — puts it in the tradition of Pascal and Ada, and engineers from that background will find the shape familiar. But Mica is not a dialect of either, and it is not a Pascal implementation: the type system, the memory model, the failure channel and the concurrency model are its own, and several constructs those languages provide are deliberately refused. See refusals as design.

At a glance

Current version6.7.0
TargetsLinux x86_64 (x86-64-v3) · Linux AArch64 (ARMv8.0-A)
Code generationOwn backend, end to end — no LLVM, no GCC backend
Debug informationDWARF v5, gdb-tested
Implementation165,186 lines of Go across 663 files, zero dependencies
Test corpus1,389 programs, 8,436 declared test cases
Cross-architecture1,378 programs run on both architectures
LicenceMCL-1.0 — free for any use, including commercial; not open source

A first look

program FailsChannelBasic;

imp
    WriteLn : std;

type
    ParseError = error (BadDigit, Overflow, EmptyInput);

var
    n : int64;

function ParseDigit(c : int32) : int64 fails ParseError;
begin
    if c < 0 then
        fail EmptyInput;
    if c > 9 then
        fail BadDigit;
    ParseDigit := c as int64;
end;

function ParsePair(a : int32, b : int32) : int64 fails ParseError;
var
    left : int64;
    right : int64;
begin
    left := ParseDigit(a) on fail leave;
    right := ParseDigit(b) on fail leave;
    ParsePair := left * 10 + right;
end;

begin
    n := ParsePair(4, 2) on fail use -1;
    WriteLn("%lld", n);                     { 42 }

    n := ParsePair(4, 77) on fail use -1;
    WriteLn("%lld", n);                     { -1 }
end.

Errors that a caller is expected to handle travel on a dedicated channel declared in the signature. Bugs do not: they are a separate mechanism that terminates deterministically. The two never share a path. See the failure channel.

What makes it different

The full list is the capability catalog. These six are the ones we would lead with in a technical evaluation.

Sharing has to be written down

Concurrency is a visible block with a structural join, and every touch of shared state must be spelled synchronized. Touch a global or an ancestor activation’s cell without the mark — directly, or through a call several frames down — and it is a compile error, not a lint. Taking a bare address of a shared cell inside the region is rejected too.

The practical effect is that unmarked code is not shared code, so a reviewer’s search space collapses. Deadlock freedom follows from construction: owners form a chain, and locks are acquired in chain order.

The same source compiles for a single carrier or for many, with no source change ever, and a single-carrier build produces byte-identical output to the sequential one. → Compile-time data-race freedom

Heap lifetimes proven by flow analysis

Every allocation creates an obligation attached to the allocation value. Wherever the compiler can follow the pointer, it must see that obligation discharged on every path, and an undischarged cell is a build error.

The part we care most about is the honest half. Where a pointer’s flow escapes what the analysis can follow, Mica does not guess — the site degrades to manually-verified and the compiler tells you what it could not prove, rather than demanding an annotation. That is a zero-false-positive posture a borrow checker structurally cannot offer. → Heap lifetime proof

The intermediate language never names malloc; it emits allocator intrinsics. Which deployment class those bind to — hosted, or a freestanding fixed-arena with no OS heap at all — is decided when you link, and code generation is byte-identical across both, down to matching object-file hashes. Regions are scheduled by the compiler per allocation site from the lifetime it proved, and run inside either class. One source tree serves a Linux server and a statically budgeted embedded target. → Allocator as a link decision

Determinism as a shipped guarantee

Parallel linear algebra is bit-identical to serial at every carrier count, because work is partitioned by whole output rows and never within an accumulation. Parallel reductions are refused rather than silently provided. Floating-point contraction decides identically on both backends, so a program produces bit-identical results across architectures at a fixed optimization set. → Determinism

The C boundary is a typed surface

An imported C function is a failing function. Its error convention — negative errno, NULL, or a sentinel value — is declared once in a contract and lifted at the call site into the language’s failure channel. No -1, no NULL, no MAP_FAILED, and no errno in user code. Curated contract packs cover the C standard library, POSIX, and Linux. → The C boundary

Zero dependencies, one vendor

165,186 lines of pure Go, an empty dependency set, and the GNU assembler and linker as the only external tools. Every phase — scanner, parser, type system, semantic analysis, intermediate language, inlining, SSA optimization, register allocation, two native backends, ELF and DWARF emission — is ours. Adopting Mica means trusting one vendor, not a dependency tree. → Zero-dependency implementation

Where to go next

Figures as of 2026-07-22, compiler 6.7.0.