Skip to content

Kairo 0.2.0

Choose a tag to compare

@Ze7111 Ze7111 released this 09 Nov 07:11
· 48 commits to main since this release
Immutable release. Only release title and notes can be modified.
8ba98f5

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, kld and kfmt ship 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-dir keeps an object cache that the tools share; unchanged files skip code generation on the next build.
  • New value syntax for if, try and match. 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 | sh

Windows (PowerShell 5.1 or 7):

irm https://kairolang.org/install.ps1 | iex

Both 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-ranlib and llvm-lib.
  • lib/: Kairo's builtin, std and rt libraries 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.ps1 and darwin-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/ and THIRD-PARTY-NOTICES.

Language

Control flow

  • if, try and match can be used as expressions, with => introducing each value arm.
  • Arms can sit on one line or be split across lines however you like.
  • match covers enums, integers, ranges, tuples, negative literals and alternatives, and is checked for exhaustiveness.
  • for loops with an empty initializer (for ; i < n; i += 1).
  • The switch statement is gone; use match.

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: @inout for mutable references and @move for ownership transfer. Using a moved-from value is a compile error.
  • Copy and move constructors are declared explicitly with @copy and @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 T produces values lazily.
  • Panics: panic, try/catch (as a statement and as an expression) and finally, with each function's panic set inferred and checked.
  • Functions declared inside a function body.
  • @no_unwind marks functions that never throw.
  • Purity inference is on by default: a function that keeps the promise is treated as const or @pure without being marked (-fno-infer-purity turns 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, impl and derives bounds, requires clauses, 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, f64 and f128. The 80-bit f80 is available on targets whose long double is x87 extended precision, and anywhere else you get a clear error explaining why.
  • Nullable types with null-safe access and ??.
  • const correctness throughout. Const and non-const overloads behave the way they do in C++.
  • Compile-time evaluation with eval constants, eval fn and eval for.

Operators

  • Overload operators as members, as free functions, or through extensions.
  • != is derived automatically from ==, and <, <=, >, >= from <=>.
  • [] returns an assignable location, so x[i] = v works on your own types.
  • Conversion operators, which can optionally be made implicit.
  • A class with op () can be called like a function.
  • ^^ for exponentiation.
  • as now 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.
  • string owns its memory: it is copied and freed correctly, and == compares two strings.

Loops and ranges

  • Closed, half-open and open-ended ranges.
  • for ... in works over ranges, arrays, slices, vectors, sets, maps (for (k, v) in m), generators, your own types (by defining op in), and C++ containers.

Modules and imports

  • Plain, aliased, selective and wildcard imports.
  • import self::x imports the file beside this one, and import module::x imports from the root of the current module.
  • Re-exporting through pub and prot imports.
  • Visibility levels pub, prot and priv. A declaration with no visibility written is priv to its file, or prot to its module when a module.k sits at or above the file.
  • A module can be split across several files.
  • A module foo line in a module.k names the library, whatever its folder is called.
  • -I dir adds 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 with ffi "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++ std is reached as cxx::std. Plain std is always Kairo's standard library.
  • A C++ T& parameter is an @inout parameter in Kairo, and the call site passes &x.
  • @cxx::new and @cxx::delete allocate and free with C++ new and delete.
  • Raw C and C++ pointers come in as unsafe pointers, so it's always clear where Kairo's safety guarantees end.
  • size_t and its relatives map to usize and isize.
  • Compiler builtins such as __builtin_popcount are available directly.
  • C and C++ source files can be passed alongside .k files and are compiled and linked in the same command.
  • A C++ or Objective-C++ file compiled with kcc can #include "foo.k" and use its pub declarations.
  • --emit-headers=<dir> writes C++ headers for your Kairo modules, so C++ projects can use them.

Tools

  • kairo, the compiler.
  • kcc, a drop-in for clang / clang++ that also understands Kairo: it compiles C, C++ and Kairo inputs, and C++ inputs can #include Kairo files.
  • kld, the linker driver, for ELF, Mach-O, PE/COFF and WebAssembly.
  • kfmt, the formatter: kfmt --check, --write or --stdout on 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-recompile ignores it.
  • A pinned progress line while building (--progress=auto|always|never|lines; lines prints 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 --target and 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-musl target 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/; kairo then finds it with no flags.
  • --freestanding compiles without a C library, CRT or prelude, for bare-metal and kernel code.
  • Invalid targets and CPU names are rejected up front. Use --print-cpus to 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=basic prints one line per diagnostic, with fix-its in a machine-applicable form for editors and scripts. --error-format=json is 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 priv constructor or a prot declaration 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-dir is reused.
  • --time-passes and --time-trace show 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 .k extension instead of .kro.
  • Value arms are written with =>. Braced value arms are an error, and the fix-it rewrites them.
  • switch is replaced by match.
  • ref! and mref! parameters become @inout and @move.
  • A C++ T& parameter is @inout, and the call site needs &x.
  • The destructor is fn op finally(self); the fix-it renames op delete. The delete p expression is removed: use @destroy p, or @cxx::delete p for memory from C++ new.
  • as binds looser than prefix operators.
  • b"" byte strings are removed, and r"" is now a true raw string.
  • The \x27 and \x22 escape workarounds are no longer needed.
  • The standard library is no longer imported implicitly.
  • Top-level declarations are priv (or prot under a module.k) unless marked pub.
  • import module X is removed; write import X, import self::X or import module::X.
  • -I name=path is removed; name a library with a module line in its module.k.
  • Duck-typed generics are off: a generic body may use only what its bounds allow.
  • Raw C pointers are unsafe pointers.
  • concat! is renamed paste!, and variadic macro parameters are written ...name.
  • ===, .* and ->* can no longer be overloaded.
  • A trailing const on a method is rejected; use const self instead.
  • Default arguments belong on a method's declaration, not on its out-of-line definition.
  • --out-dir is renamed --cache-dir.

Fixed issues

  • #103 string owned heap memory but had no destructor or copy, so every f-string leaked.
  • #99 string == string had no operator (SC066E).
  • #109 Module pinning: a module foo line in a module.k now names the library, rather than its folder name or the -I path.
  • #102 Several -I header directories all named include failed with R018E "duplicate module root".
  • #93 Access control was checked only by the C++ step. Private members, priv constructors 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 union caused an internal compiler error during code generation.
  • #88 emplace_back(x) on a cxx::std::vector found 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 == x failed with SC123E when the dotted variant was the left operand.
  • #74 Self was reported as "used outside of a type body" inside a generic or structural extend.
  • #80 Integer literal type inference contradicted the documented i32 default.
  • #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: fn was 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 ... in over a Kairo string isn'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 usable Ordering yet. User types with their own op <=> work.
  • byte & byte has no operator yet.
  • Indexing a slice (Slice[i]) or a string is not bounds-checked. Vector indexing is.
  • On Linux, statically linked programs print no stack frames when they crash.
  • await, spawn and the thread expression are not compiled yet: spawn and thread stop with an internal compiler error, and await is 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 const move-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).
  • -fsanitize is not supported yet (#101).
  • @packed and @align(N) work, but @pack(N), @bitfield(N) and @voided aren't implemented yet (#105).
  • f80 literals are not always correctly rounded, and very long fractions are dropped (#79).
  • A discarded function result gets no warning, and a discarded x + 1 is 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 sysroot generator isn't in this release; the prebuilt sysroots ship instead. Targets without a bundled sysroot need --sysroot or --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.0

Windows: 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.zip

The 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-NOTICES and LICENSES/ in each archive.
  • Contributions require signing the CLA (v2).
  • Use of the Kairo name and logo is covered by TRADEMARKS.md.