Mica 5.0.0 Technical Portrait · Chapter 6 of 9 · reading guide

Chapter 1 stated the platform vision in a paragraph. This is the chapter where it becomes mechanism. The bet is simple and contrarian: the ecosystem already exists. It is the entire C ABI world — glibc, POSIX, libm, BLAS, OpenSSL — and Mica is designed to reach every part of it directly, rather than build a second, smaller world on top.


◈ A language for the platform, not above it

Many modern language ecosystems build a second world over the operating system: a second memory manager, a second I/O runtime, a second FFI type system, a second dependency graph. The developer ends up living in that second world and treating the first as a distant country they occasionally visit.

Mica makes the opposite bet. Every C library on the system is a Mica library, described by a JSON contract and callable directly — no wrappers, no glue, no marshaling tax. The system allocator is the default allocator (Chapter 3). The ELF loader is the loader. GDB is the debugger, because Mica binaries carry accurate DWARF v5. A Mica binary and a C binary are equal citizens: they link together, call each other, and share one ABI with no performance tax at the boundary.

WriteLn and ReadLn are not new I/O systems

When a Mica developer writes:

WriteLn("Balance = %d EUR", balance);
ReadLn("%d", n);

there is no Mica I/O runtime underneath. The compiler resolves these calls, per the target’s string encoding, onto the C standard I/O layer: on a UTF-8 target straight to the printf/scanf family, and on the default UTF-32 target through thin UTF-32-aware stdio routines that sit directly on the same C library. The developer writes one call; the compiler selects the encoding-correct path. No new I/O ecosystem, no new runtime, no abstraction to learn.


◈ The contract system

10 embedded JSON contracts · 2,669 contract lines

A JSON contract describes a C library’s types, functions, calling conventions, and ABI layout precisely enough that the compiler can validate calls at compile time — including format strings, argument types, and cross-unit type fingerprints. The developer gets compile-time safety with no runtime overhead and not a line of binding code. The compiler ships embedded contracts for:

ContractContents
stdMica standard I/O: WriteLn, ReadLn, Write, Empty
processProgram arguments: ProgramName, ArgCount, Arg
limitsNumeric bounds: MaxInt8, MinInt32, MaxFloat64, … (data-driven, not hardcoded)
cstdC standard library functions
mathSin, Cos, Sqrt, Pow, … (the same libm)

Three guarantees fall out of the contract model:

  • Type fingerprinting. When two compilation units share a type, the compiler computes a deterministic digest. A mismatch — a changed field, a different layout, a packing difference — is caught at compile time with a clear error, not at runtime with a crash.
  • Format-string analysis. Calling WriteLn("%lld", x) validates the format string against the actual argument types at compile time. A mismatch is a compiler error.
  • Automatic UTF boundary. The contract resolver picks the encoding-correct external symbol for every string-bearing call — the UTF-8 path binds to the C printf/scanf family directly, the UTF-32 path to Mica’s UTF-32-aware stdio routines over the same library — entirely inside the compiler. Porting to a UTF-8-first target needs no source change; the encoding boundary lives in the contracts, not the programs.

◈ The compiler driver

The driver orchestrates the pipeline: it parses CLI options, manages phases, handles multi-file builds, and invokes the GNU toolchain.

FlagPurpose
--compile / -c, --link / -lRun compilation phases / link the final binary
--source / -sInput .mica files (comma-separated)
--build / -bBuild output directory
--optimize / -odebug, release, or checked
--assembly / -asmintel or att
--platform / -pfTarget OS, architecture, encoding (e.g. linux,arm64,utf-8)
--stdlib / -slStandard-library archive override (auto-resolved otherwise)
--external / -extLink profile: static or shared
--export / -expExport intermediate representations
--no-pie / -npDisable position-independent executable

Phased, lock-free concurrency. Within a phase, all compilation units run concurrently; between phases, a barrier ensures (for example) that all files finish parsing before cross-file import resolution begins. The core helper spawns one goroutine per unit and waits on a single WaitGroupno mutex, no channel, no lock — because each goroutine writes only to its own pre-allocated result slot, and shared data (the registries, the contracts) is read-only during parallel execution. The result scales linearly with the number of source files in the parallel phases, with no synchronization overhead beyond the WaitGroup. (The payoff in compile time is Chapter 7.)


◈ The standard library

A single 1,149-line C23 runtime

Mica’s standard library is a single, readable C23 file — the platform principle made literal. It provides:

  • I/O (WriteLn, ReadLn, Write) — delegating to the right UTF-32 or UTF-8 C call.
  • Process (ProgramName, ArgCount, Arg) — argument access without exposing C-style argc/argv in the language entry point.
  • String runtime — UTF-32 handling and descriptor-based strings (pointer + length + capacity).
  • The heap runtime (new in 5.0.0) — the hand-written allocator stubs, the per-owner cleanup-list data structure, and the three allocator plug-ins (hosted calloc/free, the compiler-scheduled region, and the freestanding fixed-arena). This is the runtime side of Chapter 3.
  • Runtime support — static-link traversal for nested functions, and runtime failure reporting with source context for checked arithmetic and nil dereference.

The library compiles to a per-architecture archive, and the compiler auto-resolves the one matching the target architecture. --stdlib overrides this for vendored or alternate runtimes — which is also how a third-party allocator plug-in is selected (Chapter 3).

The PascalCase API. Keywords and library namespaces are lowercase (std, math, process); every callable library symbol is PascalCase (WriteLn, ArgCount, Sin). The rule keeps the source surface self-consistent.


◈ Interop is bidirectional — and table stakes done right

A zero-overhead C ABI is not, on its own, a distinguishing feature: every serious native language (Rust, Zig, D, Ada, Swift, even Free Pascal) calls C at the ABI level. What Mica adds is what crosses the boundary with the call — the compile-time safety analysis (Chapter 3) and the DWARF debug information (Chapter 5) both follow the call into C and back. Disposing a foreign, C-owned pointer is a compile error (provenance is tracked), and a single GDB session steps from Mica into C and back on one stack. That combination — not the ABI itself — is the distinctive part.

The mechanism works in both directions, and the test suite and worked examples prove each.

Mica calls C — the contract direction

This is the direction the rest of this chapter has described: a JSON contract maps a Mica name to a C symbol, declares the shared record types and each parameter’s passing mode (by value or by constant value), and the compiler resolves and validates the call with no binding code. For libraries the program owns, the contract is a mica.external file discovered next to the sources; it carries both external_name_utf8 and external_name_utf32 for every function, so a single source serves a UTF-8 target and a UTF-32 target — the resolver binds the encoding-correct symbol per target, and the --external profile selects static or shared linking. The format_string field drives the same compile-time format-string analysis described above; a %s/%ls mismatch for the target encoding is a compiler error.

C calls Mica — direct, no adapter runtime

The reverse direction needs no special machinery either, because a Mica object is a C object. A Mica library (not a program) is compiled with --output-modifier archive to produce a static .a, or --output-modifier object to produce a shared .so. Every exported function becomes a C-callable symbol named <LibraryName>_<FunctionName> — a library Utilities with a function Fibonacci exports Utilities_Fibonacci. C code declares it extern, links the archive or shared object directly, and calls it — no adapter, no bridge, no runtime in between:

extern int64_t Utilities_Fibonacci(int32_t n);   /* from the Utilities library */
extern Pair    BridgeLib_ShiftPair(Pair valueIn); /* records cross by value */

The CCallsMica example builds the Utilities library and links it into a C program both ways (static and shared); the InteropCMicaRecordRegisterRoundtrip execution test pins the harder case — a record passed by value across the boundary in registers, with the result checked byte-for-byte — proving the ABI classification holds for aggregates in both directions.

The four directions, all under test

  • Mica calling Cprintf, sin, cos, pow.
  • C calling Mica — C code linking a Mica library and calling its <LibraryName>_<FunctionName> exports directly.
  • Mica calling Mica — multi-file projects with cross-unit imports and fingerprint-checked shared types.
  • Mica calling Linux — direct system calls through contracts.

The planned posix and linux contract packs will extend this to the full Linux OS API surface — curated access layers over real syscalls and kernel APIs, not reimplementations (Chapter 9).


◈ Cross-compilation

Mica cross-compiles between Linux/AMD64 and Linux/AArch64 on one host. With the AArch64 GNU cross-toolchain and a QEMU user-mode setup installed on an AMD64 host, --platform linux,arm64,utf-8 is enough to compile, link, and run AArch64 binaries — the matching standard-library archive is auto-resolved. The mirror packages enable the AArch64 → AMD64 direction.


Where to next

You’ve seen that Mica binaries are first-class platform citizens. The obvious next question — and the one this release answers most surprisingly — is how fast are they?

Chapter 7 — Performance · back to the reading guide