Repository navigation
Kairo 0.2.0
Warning
This is an alpha. It's the first public build of the self hosted compiler, released so people can try Kairo, break it, and report what broke. The syntax, command-line flags and standard library will change before v1.
Kairo is a statically typed systems programming language with bidirectional C++ interop. You import C and C++ headers directly and use them as if they were Kairo, and C++ code can include what Kairo exports. There are no bindings, wrappers or generated glue to maintain.
Kairo builds native executables on Linux, Windows, macOS, FreeBSD and NetBSD. From any host it cross-compiles to Linux, Windows (MinGW), FreeBSD, NetBSD and WebAssembly with the bundled sysroots; macOS and MSVC targets need your own SDK.
Highlights
- Self-hosted. The compiler is written in Kairo and compiled by a previous release of itself. Building from source now needs a seed release (0.2.0 or newer); see BUILD.md.
- One download, complete toolchain.
kairo,kcc,kldandkfmtship with the runtime and 18 cross-compile sysroots. Clang and LLD run inside the tools, so you don't need a separate C/C++ compiler, linker or SDK for the bundled targets. - 14 host platforms. Linux (x86_64, aarch64, armv7, i686, riscv64), Windows (x86_64, aarch64, armv7, i686), macOS (Apple Silicon and Intel), FreeBSD (x86_64, aarch64) and NetBSD (x86_64).
- C and C++ headers as imports. Import a header and use its functions, classes, templates, operators and iterators directly from Kairo, including the C++ standard library.
- C++ can include Kairo.
kcc, a clang-compatible C/C++ driver, lets a C++ file#include "foo.k"and call into it. kfmt, the Kairo formatter.- Incremental builds.
--cache-dirkeeps an object cache that the tools share; unchanged files skip code generation on the next build. - New value syntax for
if,tryandmatch. A value arm is introduced with=>, and braces after a control keyword always mean a block. Code reads the same way in every position. - Exhaustive
match. Missing cases are compile errors. - Closures, generators, panics,
eval, parameter modes, f-strings, named and default arguments, variadic functions, operator overloading, and generics with specialization. - Readable errors. Every diagnostic has a stable code and an explanation, points at your source with context, and suggests a fix where one is clear. Errors from the C++ compilation step point back at the Kairo line that caused them.
Install
Quick install
Linux, macOS, FreeBSD and NetBSD (any shell):
curl -fsSL https://kairolang.org/install.sh | shWindows (PowerShell 5.1 or 7):
irm https://kairolang.org/install.ps1 | iexBoth are the same script, also attached below as install.sh and install.ps1. It detects your OS and CPU, downloads the matching archive and SHA256SUMS, verifies the checksum before unpacking, and never needs root or administrator rights. On Linux, macOS and the BSDs it installs to ~/.local/share/kairo/0.2.0 and links the tools into ~/.local/bin. On Windows it installs to %LOCALAPPDATA%\Kairo\0.2.0 and adds its bin to your user Path. Set KAIRO_PREFIX to install elsewhere, or KAIRO_TRIPLE to pick a different archive.
Downloads
Linux (the compiler is statically linked against musl and runs on any distribution)
| Architecture | Archive |
|---|---|
| x86_64 | kairo-0.2.0-x86_64-linux-musl.tar.xz |
| aarch64 | kairo-0.2.0-aarch64-linux-musl.tar.xz |
| armv7 | kairo-0.2.0-armv7-linux-musl.tar.xz |
| i686 | kairo-0.2.0-i686-linux-musl.tar.xz |
| riscv64 | kairo-0.2.0-riscv64-linux-musl.tar.xz |
Windows (MinGW builds; no extra runtime needed)
| Architecture | Archive |
|---|---|
| x86_64 | kairo-0.2.0-x86_64-windows-gnu.zip |
| aarch64 | kairo-0.2.0-aarch64-windows-gnu.zip |
| armv7 | kairo-0.2.0-armv7-windows-gnu.zip |
| i686 | kairo-0.2.0-i686-windows-gnu.zip |
macOS
| Architecture | Archive |
|---|---|
| Apple Silicon (arm64), macOS 11.0+ | kairo-0.2.0-arm64-apple-macosx11.0.zip |
| Intel (x86_64), macOS 10.15+ | kairo-0.2.0-x86_64-apple-macosx10.15.zip |
BSD
| Platform | Archive |
|---|---|
| FreeBSD x86_64 | kairo-0.2.0-x86_64-unknown-freebsd.tar.xz |
| FreeBSD aarch64 | kairo-0.2.0-aarch64-unknown-freebsd.tar.xz |
| NetBSD x86_64 | kairo-0.2.0-x86_64-unknown-netbsd.tar.xz |
The i686 builds need an SSE2-class CPU (Pentium 4 or newer), and the ARM builds need ARMv7 or newer.
To install by hand, download an archive and SHA256SUMS, verify it, unpack it anywhere and add its bin folder to your PATH:
sha256sum -c --ignore-missing SHA256SUMS
tar -xJf kairo-0.2.0-x86_64-linux-musl.tar.xz
export PATH="$PWD/kairo-0.2.0-x86_64-linux-musl/bin:$PATH"Keep the folder layout as it is: the tools find their libraries, runtime and sysroots relative to bin.
Archives downloaded with a browser may be blocked on first run on macOS and Windows; see Troubleshooting.
SHA-256 checksums
The same values as the attached SHA256SUMS (which covers the archives; the installers are listed here too). On macOS use shasum -a 256 <file>; on Windows, Get-FileHash -Algorithm SHA256 <file>.
| File | SHA-256 |
|---|---|
kairo-0.2.0-aarch64-linux-musl.tar.xz |
4370035d6fec76908f84f1de6c9bd3510c69e9a119664b6a5bdda05098b65267 |
kairo-0.2.0-aarch64-unknown-freebsd.tar.xz |
3fc4c37ec6ff6fcdea23dd56cf3325ae73a707b4640830b9c62e366b980aa501 |
kairo-0.2.0-aarch64-windows-gnu.zip |
c1b2c5383e7c5be064d4add7c1f5fe24db3d28e05f89d8ce04b3be414542e45d |
kairo-0.2.0-arm64-apple-macosx11.0.zip |
d8786f3bce77f23ad2c40560b5b48315866b4d3bb48ded3b2ab36e9e17bad17a |
kairo-0.2.0-armv7-linux-musl.tar.xz |
e0a74096e268c091f00d01f17c6ecf6efbe83670432614c00d8b1bd5966d0f7e |
kairo-0.2.0-armv7-windows-gnu.zip |
5a5930c857cf6704724a92fcf361dc762d1ac1abb01b8c94f1b20e8e058a770e |
kairo-0.2.0-i686-linux-musl.tar.xz |
1ea27397781f483922062b37094a4c9d985735b63d49cd61410a9d45f11ddc41 |
kairo-0.2.0-i686-windows-gnu.zip |
f8044c7772af2f38602008b15c62cf3f14ba3ff3fc7dd2773820ae691a1de821 |
kairo-0.2.0-riscv64-linux-musl.tar.xz |
d30f991372b2d21918fab1167c2dc52b8bfd864e3e3df08489e051cc33b1c89f |
kairo-0.2.0-x86_64-apple-macosx10.15.zip |
d9a1074177a30a8cb2d12575b4ab08a03f057bc44fc5fd1f8215c6f2a5d9b81a |
kairo-0.2.0-x86_64-linux-musl.tar.xz |
93c4190798267f00e7744222339143ba6910f80eb6f5b57a9d71694181241fa4 |
kairo-0.2.0-x86_64-unknown-freebsd.tar.xz |
d4691c189e28f3365f156310d4db14b03b4c4572e13e6010e4bbe3972d468699 |
kairo-0.2.0-x86_64-unknown-netbsd.tar.xz |
7a39ade94d6d7e2dfbfe7b446267e826af682b2c91c7f2bc38da4e20979e6eb5 |
kairo-0.2.0-x86_64-windows-gnu.zip |
6be5a6a4eff764028e206af18956e317df79157dc39ea716a265b728737b1012 |
What's in the archive
bin/:kairo(the compiler),kcc(a clang-compatible C/C++ driver that can#include "x.k"),kld(the linker driver, for every object format),kfmt(the formatter),llvm-symbolizer,llvm-ar,llvm-ranlibandllvm-lib.lib/: Kairo'sbuiltin,stdandrtlibraries and the clang resource directory.sysroot/: 18 cross-compile sysroots: Linux musl and glibc 2.31 for x86_64, aarch64, i686, riscv64 and armv7; MinGW for x86_64, aarch64, i686 and armv7 Windows; FreeBSD x86_64 and aarch64; NetBSD x86_64; and wasm32-wasi.scripts/:msvc-sysroot.sh/msvc-sysroot.ps1anddarwin-sysroot.sh, which build MSVC and macOS sysroots from your own SDK. Apple's and Microsoft's SDKs are never shipped.share/kairo/runtimes/: the LLVM runtime sources those scripts compile.LICENSE,NOTICE,LICENSES/andTHIRD-PARTY-NOTICES.
Language
Control flow
if,tryandmatchcan be used as expressions, with=>introducing each value arm.- Arms can sit on one line or be split across lines however you like.
matchcovers enums, integers, ranges, tuples, negative literals and alternatives, and is checked for exhaustiveness.forloops with an empty initializer (for ; i < n; i += 1).- The
switchstatement is gone; usematch.
Functions
- Default arguments, named arguments and keyword-only parameters.
- Variadic functions: inside the body, a variadic parameter is a slice you can index and iterate.
- Expression-bodied functions.
- Arguments are always evaluated left to right, in the order written.
- Parameter modes:
@inoutfor mutable references and@movefor ownership transfer. Using a moved-from value is a compile error. - Copy and move constructors are declared explicitly with
@copyand@move. - The destructor is spelled
fn op finally(self). - Closures, with every capture mode, can be stored, passed, returned and nested. A closure that captures nothing can be passed where a C function pointer is expected.
- Generators: a function declared
-> yield Tproduces values lazily. - Panics:
panic,try/catch(as a statement and as an expression) andfinally, with each function's panic set inferred and checked. - Functions declared inside a function body.
@no_unwindmarks functions that never throw.- Purity inference is on by default: a function that keeps the promise is treated as
constor@purewithout being marked (-fno-infer-purityturns it off).
Types
- Classes, structs, unions, enums and interfaces, with extensions on any type, including built-in ones. Classes, structs and enums can also be declared inside a function body.
- Generics with explicit and partial specialization,
implandderivesbounds,requiresclauses, and value arguments. Generic bodies are checked by Kairo; duck-typed generics are off by default. - Built-in containers: vectors, slices, sets, maps and fixed-size arrays. Arrays and vectors convert to slices implicitly.
- Integers from 8 to 512 bits. Floats include
f16,bf16,f32,f64andf128. The 80-bitf80is available on targets whoselong doubleis x87 extended precision, and anywhere else you get a clear error explaining why. - Nullable types with null-safe access and
??. constcorrectness throughout. Const and non-const overloads behave the way they do in C++.- Compile-time evaluation with
evalconstants,eval fnandeval for.
Operators
- Overload operators as members, as free functions, or through extensions.
!=is derived automatically from==, and<,<=,>,>=from<=>.[]returns an assignable location, sox[i] = vworks on your own types.- Conversion operators, which can optionally be made implicit.
- A class with
op ()can be called like a function. ^^for exponentiation.asnow binds looser than prefix operators, so casts of negative numbers and addresses do what they look like.
Strings
- Raw strings and triple-quoted multi-line strings.
- F-strings support fill, alignment, sign, width, precision, alternate form, zero-padding, number bases, debug formatting and self-documenting
{x=}. Format specs are checked at compile time. - Adjacent string literals join into one.
stringowns its memory: it is copied and freed correctly, and==compares two strings.
Loops and ranges
- Closed, half-open and open-ended ranges.
for ... inworks over ranges, arrays, slices, vectors, sets, maps (for (k, v) in m), generators, your own types (by definingop in), and C++ containers.
Modules and imports
- Plain, aliased, selective and wildcard imports.
import self::ximports the file beside this one, andimport module::ximports from the root of the current module.- Re-exporting through
pubandprotimports. - Visibility levels
pub,protandpriv. A declaration with no visibility written isprivto its file, orprotto its module when amodule.ksits at or above the file. - A module can be split across several files.
- A
module fooline in amodule.knames the library, whatever its folder is called. -I diradds a Kairo search directory and a C/C++ include directory at once.- The standard library is imported explicitly, for example
import std::io. Built-in types are always available.
Macros
- Macros with parameters, defaults and variadic arguments.
- Nested macro calls expand correctly, and real recursion is caught with a clear error.
- An error inside a macro expansion points at your call site.
C and C++ interop
- Import C headers with
ffi "c"and C++ headers withffi "c++". C headers get C linkage automatically. - Functions, overloads, default arguments, operators, class templates, nested types, iterators and range-based loops over C++ containers all work.
- C++
stdis reached ascxx::std. Plainstdis always Kairo's standard library. - A C++
T¶meter is an@inoutparameter in Kairo, and the call site passes&x. @cxx::newand@cxx::deleteallocate and free with C++newanddelete.- Raw C and C++ pointers come in as
unsafepointers, so it's always clear where Kairo's safety guarantees end. size_tand its relatives map tousizeandisize.- Compiler builtins such as
__builtin_popcountare available directly. - C and C++ source files can be passed alongside
.kfiles and are compiled and linked in the same command. - A C++ or Objective-C++ file compiled with
kcccan#include "foo.k"and use itspubdeclarations. --emit-headers=<dir>writes C++ headers for your Kairo modules, so C++ projects can use them.
Tools
kairo, the compiler.kcc, a drop-in forclang/clang++that also understands Kairo: it compiles C, C++ and Kairo inputs, and C++ inputs can#includeKairo files.kld, the linker driver, for ELF, Mach-O, PE/COFF and WebAssembly.kfmt, the formatter:kfmt --check,--writeor--stdouton files or directories.- An incremental object cache: with
--cache-dir, unchanged files skip code generation on the next build, and the cache is shared across the tools.--force-recompileignores it. - A pinned progress line while building (
--progress=auto|always|never|lines;linesprints machine-readable progress events).
Platforms and targets
- Hosts: Linux (x86_64, aarch64, armv7, i686, riscv64), Windows (x86_64, aarch64, armv7, i686), macOS (Apple Silicon and Intel), FreeBSD (x86_64, aarch64) and NetBSD (x86_64).
- Cross-compilation: pass
--targetand the matching bundled sysroot is used automatically. Bundled targets: Linux (glibc 2.31 and musl), Windows (MinGW), FreeBSD, NetBSD and WebAssembly (WASI). - On Linux, programs build against glibc 2.31 by default, so they run on any distribution with glibc 2.31 or newer. A
<arch>-linux-musltarget builds a static executable. - macOS and MSVC targets: Apple's and Microsoft's SDKs are not shipped. On a Mac, native builds use the installed Command Line Tools SDK. To cross-compile to macOS or MSVC, build the sysroot once from your own SDK with the scripts in
scripts/;kairothen finds it with no flags. --freestandingcompiles without a C library, CRT or prelude, for bare-metal and kernel code.- Invalid targets and CPU names are rejected up front. Use
--print-cpusto see what a target supports. - The compiler can output executables, shared libraries, object files, assembly, LLVM IR or bitcode.
Diagnostics
- Every error and warning has a stable code and a written explanation (
kairo --explain <CODE>). - Errors show the surrounding source lines and label every span involved.
- Fix-its are suggested where the fix is unambiguous.
--error-format=basicprints one line per diagnostic, with fix-its in a machine-applicable form for editors and scripts.--error-format=jsonis also available. - Warnings can be silenced, promoted to errors, or forbidden per code.
- Access control is checked by Kairo itself: using a private member, a
privconstructor or aprotdeclaration from outside its scope is a Kairo error with a fix-it. - A mistyped flag gets a suggestion for the closest valid one.
- Bad input paths, unreadable files and directories are reported cleanly instead of crashing.
- If the compiler does hit an internal error, it reports the line of your code that triggered it.
Performance
- Semantic analysis and code generation run in parallel (
--threads,--sema-threads,--codegen-threads). - C++ headers shared by several files are parsed once per build.
- Unchanged files are not recompiled when a
--cache-diris reused. --time-passesand--time-traceshow where compile time goes.
## Coming from Stage 0
This release includes breaking changes. Every removed form is a compile error, and many of those errors carry a fix-it that rewrites the code for you (for example braced value arms, op delete and import module foo).
- Source files use the
.kextension instead of.kro. - Value arms are written with
=>. Braced value arms are an error, and the fix-it rewrites them. switchis replaced bymatch.ref!andmref!parameters become@inoutand@move.- A C++
T¶meter is@inout, and the call site needs&x. - The destructor is
fn op finally(self); the fix-it renamesop delete. Thedelete pexpression is removed: use@destroy p, or@cxx::delete pfor memory from C++new. asbinds looser than prefix operators.b""byte strings are removed, andr""is now a true raw string.- The
\x27and\x22escape workarounds are no longer needed. - The standard library is no longer imported implicitly.
- Top-level declarations are
priv(orprotunder amodule.k) unless markedpub. import module Xis removed; writeimport X,import self::Xorimport module::X.-I name=pathis removed; name a library with amoduleline in itsmodule.k.- Duck-typed generics are off: a generic body may use only what its bounds allow.
- Raw C pointers are
unsafepointers. concat!is renamedpaste!, and variadic macro parameters are written...name.===,.*and->*can no longer be overloaded.- A trailing
conston a method is rejected; useconst selfinstead. - Default arguments belong on a method's declaration, not on its out-of-line definition.
--out-diris renamed--cache-dir.
Fixed issues
- #103
stringowned heap memory but had no destructor or copy, so every f-string leaked. - #99
string == stringhad no operator (SC066E). - #109 Module pinning: a
module fooline in amodule.know names the library, rather than its folder name or the-Ipath. - #102 Several
-Iheader directories all namedincludefailed with R018E "duplicate module root". - #93 Access control was checked only by the C++ step. Private members,
privconstructors and same-module access now get Kairo diagnostics. - #92 A field assigned in a constructor was not initialized by it, so a member with no default constructor failed to compile.
- #94 A struct literal evaluated its initializers in declaration order instead of the order written.
- #98 A payload enum's declared underlying type (
derives u8) was ignored; the tag was always 32 bits. - #108 Any
unioncaused an internal compiler error during code generation. - #88
emplace_back(x)on acxx::std::vectorfound no viable overload. - #87 An imported container of a move-only element was treated as copyable.
- #90 Compound assignment, subscripts, expression bodies and generic code copied a move-only value instead of moving it.
- #91 A type nested in a generic class, named through an instance (
L<i32>::P), lost the instance's arguments. - #73 Concrete generic arguments in a type-position path (
Foo<i32>::Inner) were treated as dependent. - #85 An enum over a concrete generic instance (
Box<i32>) failed with SC116E. - #83
.Variant == xfailed with SC123E when the dotted variant was the left operand. - #74
Selfwas reported as "used outside of a type body" inside a generic or structuralextend. - #80 Integer literal type inference contradicted the documented
i32default. - #97 An invalid cast argument produced a second, misleading SC063E error.
- #68 Generic specialization syntax wasn't parsed: the
<>marker was rejected as an empty parameter list (S049E). - #66 Closure expressions weren't parsed:
fnwas rejected in expression position. - #65 Chained access dropped trailing member access after a call or subscript, and brace-initialization on qualified paths bound to the wrong thing.
- #110 The operator-overloading docs no longer list
.*and->*as overloadable.
Plus many other crash and correctness fixes.
Known limitations
for ... inover a Kairostringisn't supported yet (SC116E). Vectors, sets, maps, slices, arrays and ranges work.x.finally()is reserved and not implemented until 0.3 (SC116E).<=>on two primitive values doesn't yield a usableOrderingyet. User types with their ownop <=>work.byte & bytehas no operator yet.- Indexing a slice (
Slice[i]) or astringis not bounds-checked. Vector indexing is. - On Linux, statically linked programs print no stack frames when they crash.
await,spawnand thethreadexpression are not compiled yet:spawnandthreadstop with an internal compiler error, andawaitis rejected (SC086E).- Virtual interfaces (calls through
*Iface) are not compiled yet. - Generic closures (
fn <T>(x: T)) are rejected (SC116E). - Some mistakes with move-only values, such as passing a
constmove-only value by value (#89) or passing a non-transferable value by value (#84), are reported by the C++ step (CL001E) rather than by Kairo. unsafe { expr }in value position is mistyped (SC028E) (#96).-fsanitizeis not supported yet (#101).@packedand@align(N)work, but@pack(N),@bitfield(N)and@voidedaren't implemented yet (#105).f80literals are not always correctly rounded, and very long fractions are dropped (#79).- A discarded function result gets no warning, and a discarded
x + 1is warned about twice, once by Kairo and once by the C++ step (#100). - An f-string of a plain struct prints its address rather than its fields (#107).
- Kairo's memory-safety system (Tether) is not part of this release. Using a moved-from value is already a compile error, but full memory safety for safe code isn't promised yet.
- Some generic code is checked less strictly than it will be in v1.
- The
kairo sysrootgenerator isn't in this release; the prebuilt sysroots ship instead. Targets without a bundled sysroot need--sysrootor--freestanding. - The MSVC and macOS sysroot scripts have been tested on Linux only (MSVC from an xwin splat, macOS against an SDK-shaped tree), not yet on real Windows or macOS hosts.
- The test suite runs on x86_64 Linux only.
- The standard library is minimal.
The full, current list is on the Implementation Status page.
Troubleshooting
macOS: "cannot verify this app is free of malware" when running a downloaded release binary. Release binaries aren't notarized yet. If you downloaded the archive with a browser, clear the quarantine flag on the unpacked folder (the curl installer doesn't need this):
xattr -dr com.apple.quarantine ./kairo-0.2.0-arm64-apple-macosx11.0Windows: Defender or SmartScreen flags the downloaded binary. Release binaries aren't code-signed yet, so SmartScreen shows "Windows protected your PC" on first run. Click More info -> Run anyway. If Defender quarantines the archive outright, unblock it before extracting:
Unblock-File -Path .\kairo-0.2.0-x86_64-windows-gnu.zipThe PowerShell installer (irm https://kairolang.org/install.ps1 | iex) downloads without the browser's mark of the web, so it usually avoids both prompts.
kairo: command not found after installing. The installer links the tools into ~/.local/bin (on Windows it adds the install's bin folder to your user PATH). Open a new terminal, or add ~/.local/bin to PATH if your shell doesn't include it.
error [P008E]: no sysroot found for target ... for a *-apple-macosx or *-windows-msvc target. Apple's and Microsoft's SDKs can't be redistributed, so those sysroots don't ship. Build one from your own SDK with the scripts in the archive's scripts/ folder (darwin-sysroot.sh, msvc-sysroot.sh / msvc-sysroot.ps1); Kairo finds the result with no flags.
Feedback
This is exactly what an alpha is for. Please file issues using the templates. If the compiler reports an internal error, paste the full output and the smallest file that reproduces it.
Report security issues privately, as described in SECURITY.md: through GitHub's private vulnerability reporting, or by email to security@kairolang.org.
License
Kairo is licensed under Apache-2.0 WITH AdditionRef-Kairo-Runtime-exception.
- Programs you compile with Kairo, and the code the compiler generates into them, are yours to license under any terms.
- mimalloc's MIT notice is embedded in every compiled program automatically, so you don't need to ship it separately.
- The language specification under
Compiler/Spec/is licensed CC BY 4.0. - Third-party components are listed in
THIRD-PARTY-NOTICESandLICENSES/in each archive. - Contributions require signing the CLA (v2).
- Use of the Kairo name and logo is covered by TRADEMARKS.md.