Draft / Experimental: Zith documentation describes a language under active development. Check Implementation Status before relying on a feature.
Last updated: 2026-08-06 (documentation architecture refresh).
This document is the single source of truth for what the compiler supports today. Status reflects actual compiler behaviour at baseline a5f3716. Each feature was verified by running build/zithc check against a standalone test file, with source inspection where a status depends on internal structure; status reflects actual compiler behaviour, not spec intent.
| Label | Meaning |
|---|---|
| Working | Accepted by parser and sema. Lowers through HIR to LLVM codegen. |
| Check only | Passes zithc check but semantics are incidental (parsed as Name / Binary). No dedicated AST node, HIR, or codegen. |
| Parse skipped | Declaration accepted; body entirely skipped by skipDelimited('{', '}'). No semantics. |
| Parse error | The parser itself rejects this construct. Does not reach sema. |
| Spec only | No compiler implementation. |
| Stub | CLI subcommand exists but returns "not implemented yet". |
| Stage | Status | Notes | ||
|---|---|---|---|---|
| Lexer | Working | Hand-written, character-at-a-time. Longest-first maximal munch for all multi-char operators. && and ` |
are rejected with a dedicated error pointing to and / or` |
|
| Parser | Working | Recursive-descent. Function decls, expressions, imports. | ||
| Formatter | Working | Round-trip stable for all 16 ExprKind nodes including Index, OptionalProp, Field, Arrow, and StructLiteral |
||
| Import resolution | Working | import, from, export, alias, type |
||
| Name resolution | Working | Scope-chained lookupBinding. Per-scope DuplicateDecl. |
||
| Type checking | Working | All ExprKind nodes. Optional/null validation. Index bounds. |
||
| Generic instantiation | Partial | <T, U> parameter lists parse and type-check on declarations; calling a generic function still fails in the comptime solver with "generic parameter T has no concrete type". T: Trait constraints parse but are not enforced |
||
| Comptime / Solve | Partial | Present in the documented target pipeline before ownership proof; some current frontend lowering still needs to stop erasing resource information before NRA | ||
| NTA / NRA | In progress | Pre-HIR residual-fact boundary is implemented: semantic facts are accumulated and consumed before final lowering; the alive/dead/lent state machine and full diagnostics remain | ||
| HIR lowering | Working | Covers all working features; stable boundary is sema -> comptime/solve -> NTA/NRA -> HIR, and residual ownership facts attach to side tables without introducing ownership HIR nodes |
||
| LLVM codegen | Working | x86-64 and WebAssembly targets | ||
| Cache | Partial | Object caching works; .zirl format not yet used |
| Feature | Status | Notes |
|---|---|---|
fn |
Working | Parameters, return type, body. The return type is written fn f(x: T): R or fn f(x: T) -> R; both spellings parse. Overloading by parameter count and types (F-33); linkage names are qualified as <module>.<Owner>.<name>(<params>), except extern fn and main |
generic parameter lists <T, U> |
Working (parse + typing) | Accepted on fn, struct, type alias, enum, union and trait declarations. Inside the declaration each parameter resolves as an opaque type. Instantiation is not solved: both identity<i32>(42) and inferred identity(42) report E3001 "generic parameter T has no concrete type". T: Trait constraints parse but are not enforced |
flow fn |
Working | Parsed and lowers; marker/dock/jump not exhaustively tested |
raw fn |
Working | Parsed and lowers |
const fn |
Parse error | const is a binding keyword; const fn f() parses as const binding named fn, not a const function |
extern fn |
Working | C ABI interop |
let, var, const, global |
Working | All binding forms. const means immutable, not comptime |
| Feature | Status | Notes |
|---|---|---|
bool, char |
Working | |
i8–i128, u8–u128 |
Working | Arithmetic between matching widths only; no implicit promotion |
f32, f64 |
Working | Same-width arithmetic only |
?T (optional) |
Working | null → ?T and T → ?T coercions; ? postfix propagation with operand/return validation. null is rejected for non-optional *T |
T! (failable) |
Working | Declared type; lowered through HIR |
*T (pointer) |
Working | Non-nullable: null requires ?T. p deref, &x addr-of, and -> arrow all work. void is rejected (use raw opaque). Pointers imported from C are ?T, checked with is null; a ?T is still accepted unchecked where T is expected |
raw opaque |
Working | Dedicated TypeExprKind::Opaque, lowered to pointer-to-void. Castable to and from any *T via as. Bare opaque (the tagged reference type) is still unimplemented and reports unknown type |
[N]T (array), []T (slice) |
Working | Indexing on arrays, slices, and pointers lowers through HIR/LLVM |
dyn Trait |
Parse error | Type parser does not handle dyn |
struct, component, enum, union |
Working | Declarations parse and resolve |
trait, interface |
Working | Declarations parse and resolve |
implement T as Trait {} |
Working | Method bodies lowered |
type alias |
Working | |
memory qualifiers (mut, lend, view, unique, share, belong) |
Working (parse + types) | Accepted as type prefixes anywhere a type is written; carried in the type table as TypeKind::Qualified; writing through a view binding reports E4004. HIR/codegen strip the qualifier, and residual ownership facts are produced by the pre-HIR NTA/NRA boundary (F-34, partial F-14) |
| Feature | Status | Notes | ||
|---|---|---|---|---|
literals (42, 0xFF, 0c17, 0b101, 3.14, true, false, null, strings, chars) |
Working | Explicit radix prefixes (0x hex, 0c octal, 0b binary) are typed and lowered to their value; a literal wider than 64 bits reports E0004. Digit separators (1_000) are unsupported. C-like escapes decoded in string and char literals; unknown escapes report E0001 |
||
unary -, not |
Working | |||
unary ~ |
Working | Bitwise NOT; integer operand only, lowers to HirUnaryOp::BitNot |
||
binary + - * / % == != < > <= >= |
Working | |||
bitwise &. ` |
. ^.` |
Working | Spec spellings keep the .. Both operands must be integers of the same type; share HirBinaryOp::And/Or/Xor with the and/or/xor keywords |
|
assignment = |
Working | Right-associative, yields a value | ||
compound assignment += -= *= /= %= <<= >>= &= ` |
= ^=` |
Working | Desugared in the parser to Assign(Binary(base)), so they yield a value like = and inherit its coercion and view checks. The bitwise compounds drop the . of their base spelling. No new HIR node |
|
&&, ` |
` | Parse error | Lexed as single tokens purely to report a dedicated error pointing at and / or; exactly one diagnostic, no cascade |
|
field access x.field |
Working | Dot access on struct values | ||
dereference *p |
Working | Pointer dereference via unary * |
||
address-of &x |
Working | Address-of via unary & |
||
-> chain operator |
Working | Arrow access on struct pointers (p->field) |
||
index a[i] |
Working | On arrays, slices, pointers; rejects non-indexable types | ||
? postfix propagation |
Working | Requires optional operand in optional-returning function | ||
as cast |
Working | Dedicated ExprKind::Cast -> HirCast -> LLVM conversion. Numeric pairs plus raw opaque <-> *T (classifyCast); pointer-to-pointer between concrete pointees, integer/pointer mixes and user-defined casts stay rejected. No narrowing overflow check |
||
is null |
Working | Dedicated ExprKind::IsNull. Requires an optional operand; ?*T uses the nullptr niche, ?T reads the discriminant |
||
is <type> |
Parse error | Only is null is supported; any other operand reports a dedicated diagnostic |
||
range 1..5 |
Check only | Parsed as binary ..; no dedicated sema |
||
struct literal Foo { x: 1, y: 2 } |
Working | Struct literal with named fields via {} syntax |
||
@sizeOf, @offsetOf, @alignOf |
Working | @ parses in expression position. @sizeOf(T) accepts any complete type and types as u64; @offsetOf(S, field) and @alignOf(S) are struct-only and type as i32. @sizeOf(void) reports E3001 ("requires a complete type") |
| Feature | Status | Notes |
|---|---|---|
if / else / else if |
Working | |
while |
Deprecated | Still lowers correctly, but emits W1008 suggesting for (cond) { } |
break, continue |
Working | |
return (void and typed) |
Working | |
for (cond) { }, for { } |
Working | Conditional and infinite loop forms lower to the same CFG as while |
for (init; cond; step) { } |
Working | Three clauses separated by semicolons. init and step are both optional; continue still runs the step before the next test |
for (x in xs) |
Parse error | The iterator form is recognised and reported as not implemented yet |
when / match pattern match |
Working | Arms are written (pattern) ~> body, comma-separated; match is a parser synonym for when. Equality, boolean and range (1..3) patterns lower through HIR to codegen. (_) is the default arm and must come last; a value-producing when without a default reports non-exhaustive. Covered by the runtime test test_when_expression_runtime |
marker / jump |
Working | Block-style go-to: marker declares a labeled block, jump transfers control to it. dock not implemented |
dock |
Parse error | Not implemented yet |
| Feature | Status | Notes |
|---|---|---|
prefix, suffix, infix, nop decls |
Parse skipped | Body skipped via skipDelimited |
context declarations |
Parse skipped | Body skipped |
use statements |
Parse skipped | Body skipped |
macro / raw macro declarations and @name(...) calls |
Working | Normal macros rename template-local bindings hygienically and resolve other template names through the call-site scope (globals/imports visible when not shadowed). raw macro splices literally into the call-site scope and names resolve there before module/global fallback. Templates are not analysed as code; resolution is keyed by node id |
tag macro calls |
Working | <Section ...> ... </Section>; named attributes via attributes.name; statement-position only |
| word call expressions | Parse error | No parser support |
| word sequence expressions | Parse error | No parser support |
| Feature | Status | Notes |
|---|---|---|
import, from, export |
Working | Import resolution with correct paths |
alias |
Working | |
pub, mod |
Working | |
mod(..), mod(N) |
Not verified | Parser accepts; sema behaviour unknown |
| C header imports | Working (common C) | libclang only; variadic functions, array-decayed parameters, va_list, and function-pointer parameters supported. Single unsupported decls are skipped and recorded in skippedFunctions; macros, globals, bitfields, packed/anonymous records and flexible arrays remain unimported. Struct-by-value ABI is not verified |
| Feature | Spec chapter |
|---|---|
| NRA ownership analysis (alive/dead/lent state machine; qualifiers themselves are implemented) | 07-memory-model.md |
comptime evaluation |
11-comptime.md |
const fn evaluation |
11-comptime.md |
fail / with / catch / must / throw |
08-error-handling.md |
Assets (ZithProject.toml asset paths) |
12-assets.md |
.zirl binary format |
01-overview (§1.5) |
@appendField, @removeField, @appendMethod |
11-comptime.md |
dyn dispatch |
14-polymorphism.md |
| Surface | Current behaviour | Notes |
|---|---|---|
async fn |
Parse skipped | Legacy parser affordance only. Concurrency is being documented as stdlib/runtime APIs, not a function kind |
yield |
Reserved token | Not a core statement |
spawn, await |
Reserved tokens | Not core operators or statements; no frontend/HIR contract depends on them |
| Command | Status | Notes |
|---|---|---|
zithc build |
Working | Links an executable into target/ by default; --emit obj/ir/asm/hir stop earlier; --cache-stats prints object-cache hit/miss counts |
zithc run |
Working | Compiles + executes in one step; the program's stdout/stderr is forwarded to zithc's stdout, compiler diagnostics stay on stderr |
zithc check |
Working | Type-checks without emitting. Errors forwarded from frontend snapshot |
zithc fmt |
Working | Round-trip tested for Index and OptionalProp |
zithc create <name> |
Working | |
zithc clean |
Working | |
zithc execute <file> |
Working | |
zithc test <path> |
Working | Discovers and runs test files under the given path |
zithc repl |
Stub | |
zithc deps list |
Working | Reads ZithProject.toml and lists declared dependencies |
zithc deps add, deps remove |
Stub | |
zithc docs |
Working | Generates documentation from source |
Codes are grouped by pipeline stage. E0000 remains the generic user-reported diagnostic.
| Range | Stage | Codes |
|---|---|---|
| 0001-0005 | Lexical | E0001 UnknownToken, E0002 UnclosedString, E0003 InvalidEscape, E0004 InvalidIntLiteral, E0005 UnclosedComment |
| 1001-1008 | Parse | E1001 ExpectedExpr, E1002 ExpectedSemicolon, E1003 UnclosedParen, E1004 ExpectedIdent, E1005 InvalidImportDepth, E1006 ImportError, E1007 TopLevelLetNotAllowed, W1008 DeprecatedSyntax (while -> for (cond)) |
| 2001-2010 | Semantic | E2001 UndefinedIdent, E2002 DuplicateDecl, E2003 WrongArity, E2004 UnusedDecl, E2005 NotNamespace, E2006 NoMember, E2007 NoMatchingFn, E2008 AmbiguousCall, E2009 NotImplemented, E2010 UnsupportedSyntax |
| 2011-2020 | Macro | E2011 MacroUnknown, E2012 MacroArity, E2013 MacroArgKind, E2014 MacroRecursion, E2015 MacroDuplicate, E2016 MacroRawValue, E2017 MacroTagValue, E2018 MacroTagMismatch, E2019 MacroAttrUnknown, E2020 MacroAttrNotAllowed |
| 3001-3008 | Types | E3001 TypeMismatch, E3002 CannotInfer, E3003 InvalidCast, E3004 CyclicType, E3005 NullDerefUnproven, E3006 CoercionFailure, E3007 WidthMismatch, E3008 OptionalViolation |
| 4001-4004 | NRA / ownership | E4001 UseAfterMove, E4002 BorrowConflict, E4003 DoubleBorrow, E4004 WriteThroughView — only E4004 is emitted today |
| 5001-5002 | Lowering | E5001 InvalidIR, E5002 Unreachable |
| 10001-10004 | Runtime | R10001 IndexOutOfBounds, R10002 DivisionByZero, R10003 NullDeref, R10004 Panic |
All statuses above were verified against the binary built from commit a5f3716, by running zithc check on standalone files per feature and by inspecting the source where a status depends on internal structure (pipeline boundaries, linkage naming, diagnostic ranges).
Recorded deliberately; each item is a follow-up, not an unknown.
| Item | Notes |
|---|---|
Formatter re-prints for (cond) as while |
for reuses ExprKind::While; a distinct node is needed to round-trip the spelling |
| No overflow check on narrowing conversions | Neither as nor numeric-literal adaptation validates that the value fits the target |
Unchecked ?T -> T coercion |
Every C pointer is ?T, but without flow-sensitive narrowing it is accepted unchecked where T is expected. Isolated in PerModuleSema::allowsUncheckedNullablePointer; delete it when narrowing lands |
No flow-sensitive narrowing after is null |
p->field on a ?*T requires NonNull proof from if (p is null) { } else { p->field } or for (not (p is null)). Error code E3005 |
is limited to is null |
Union/type narrowing is not addressed |
for iterator form unimplemented |
for (x in xs) is reported as an error rather than parsed; the 3-clause form works |
| User-defined casts | To be added as a new branch in classifyCast |
| No C struct-by-value ABI | struct parameters/results import as named foreign types, but there is no verified ABI and no Zith-visible layout, so constructing/passing records to C remains unsupported |
.. lexes per character |
Its precedence() is -1 and the when-case range pattern depends on the two . tokens. Every other multi-char operator is munched longest-first as one token and wired through the parser, sema and formatter |
++ / -- |
Not implemented; no increment/decrement operators exist |
| Ownership proof still happens after premature lowering in places | The stable order is sema -> comptime/solve -> NTA/NRA -> HIR; residual facts are now attached before final lowering, while some paths still need the full NRA proof before emitting their final form |
When a feature moves from one status to another, update this table and re-verify.