16 KiB
language features
This file is the compact current-state ledger. TODO.md remains the detailed
roadmap and milestone history.
IMPLEMENTED
source, declarations, and packages
- newline-terminated statements and
#comments - immutable
::bindings, typed mutable=locals/globals, and_sinks - immutable package globals, mutable runtime globals, function-local mutable locals, and mutable local declarations initialized with
undefined - package-level functions, globals, native type declarations, and
Name :: alias T - directory packages with merged declarations
- file-local relative imports, import aliases, and qualified member access
- transparent declaration aliases with
Name :: alias package.Member; functions/type factories, named types, and globals retain their original declaration or storage identity hidemakes any named top-level declaration file-local; declarations are public by default, leading underscores are ordinary identifier characters, and imports are always file-local- relative
.himports as synthetic C header package namespaces - root
mainvalidation with trap executable recovery for missing or unusable entry points
scalar, aggregate, and pointer types
- exact-width integers,
isize,usize,f32,f64,bool,void,anyopaque, and contextualint,float, andrangeconstraints - target-dependent C scalar primitives from
c_charthroughc_longdouble, kept semantically distinct from native scalars - contextual integer/float/character literals, backward type-demand inference through names and arithmetic, and compile-time folding for numeric constant expressions
- strict numeric conversion by default, widening where valid, C scalar coercions at C boundaries, and explicit scalar keyword casts such as
i32(x)/c_float(x) - compile-time
minval!(T)andmaxval!(T)bounds for concrete native and C integer scalar types - arrays
[N]T, inferred-count arrays[_]T, sentinel arrays[N;S]T, compile-time expression array counts, slices[]T/[;S]T, single-item pointers@T, many-item pointers*T, and sentinel many-item pointers[*;S]T - pointer mutability via
mut, optional pointers as nullable pointers, pointer arithmetic for many-item pointers, postfix dereference^, and trapping optional unwrap? - pointer-to-array
.len, indexing, slicing,.ptron slices and pointers-to-arrays, implicit address-taking for array-variable slices, and pointer/slice sentinel weakening ptrcast!(T, ptr)as a first-pass pointer-child retype that preserves optionality, pointer kind, mutability, and sentinel shape- UTF-8 string literals as immutable pointers to static zero-terminated byte arrays, plus raw backtick multiline strings
- narrow immutable zero-terminated byte pointer/slice conversion to
*c_char/?*c_charwithout generalu8/c_charinterchange - optionals with
none,orelse, postfix?, conditional unwraps, guarded unwraps, and left-to-right short-circuiting multi-unwraps - nominal distinct types with exact backing construction, native enums with optional explicit integer backing and explicit backing-to-scalar casts, contextual enum literals, and imported C enums as target-backed integer aliases
- source-order native structs, opaque nominal records with
Name :: opaque, completec_struct { ... }, keyed record literals, native untagged unions, and native tagged unionsunion(Enum)/union(enum) - void-payload tagged-union variants, anonymous struct payloads, contextual
.variant,.variant{payload}, and.variant{field = value}construction - native sum composition with
A | Bfor unbacked enums and tagged unions, using program-globalu16variant ids - fallible channel types
T ! E, whereEis a native enum/tagged union or supported sum composition;void ! Efunctions complete successfully on fallthrough, and void-successcatchhandlers may fall through withoutyield
native record constraint fields
A direct int, float, or range field in a named native struct or union is a
program-wide constraint, not per-value polymorphism. Before record layout, all reachable keyed
constructors, field assignments, and concrete uses of field reads contribute demands and the field
resolves once to one concrete runtime type. Compatible scalar demands widen normally. Integer
literals remain provisional until inference settles, so a later usize use can resolve an int
field to usize; otherwise literal-only int fields use the widest smallest-signed type required,
and literal-only float fields use f64.
An undemanded field or incompatible demands are errors. This inference applies only to direct
fields of named native records. c_struct fields, nested constraints such as []int, and fields in
anonymous generated records still require concrete runtime types.
keyword member names
Reserved keywords are valid native enum members and tagged-union variants when used in an unambiguous member context:
TokenKind :: enum {
if
else
return
}
Token :: union(TokenKind) {
if i32
else void
return i32
}
conditional func() TokenKind { return TokenKind.if }
fallback func() TokenKind { return .else }
token func() Token { return Token{ if = 1 } }
Keyword variants also work with field access and .variant match patterns; .else: remains
distinct from the else: catch-all arm. No escaping syntax is required. Keywords remain reserved
for ordinary declarations, struct fields, untagged-union fields, and anonymous payload-struct
fields. _ is not a keyword member name.
expressions and control flow
- checked integer
+ - *, unary-, float-only/, IEEE float arithmetic, comparisons,!,and, andor - assignments and compound assignments
+= -= *= /=with single evaluation of complex lvalues;/=is float-only - field access through struct values and pointers, index/slice bounds contextually coerced to
usize, and unsigned narrower index support - boolean
if/else if/elseandforloops with braceless single-statement bodies when the preceding expression is parenthesized or a function call whileloops with optional post-iteration update clausesforloops over ranges, arrays, slices, and pointers-to-arrays with copy captures, pointer captures|@item|, and optionalusizeindex captures;expand forspecializes a comptime aggregate into one checked body per elementbreak,continue, labeledbreak :label, labeledcontinue :label, and labeled plain blocks;break :labelcan cross nested scopes to exit a labeled block- bare block scopes,
defer, and fallible-functionerrdeferwith optional error capture; cleanup is block-scoped and LIFO - bare void
return, same-linereturn value, value blocks, valueif, value loops, valuematch, and strictly value-producingyield value/yield :label value matchstatements/expressions over enums, tagged unions, and scalars, including exhaustiveness checks, payload captures, pointer payload captures, multi-pattern arms, and scalar range patterns- a final
expand |value|:enum arm orexpand |payload[, tag]|:tagged-union arm generates one specialized arm for each variant not covered earlier; enum values and optional tags are comptime-known, while union payloads keep their concrete variant type - fallible
try, fallbackcatch, andcatch |e| { ... }handler blocks - direct
return match ...andyield match ...value-control-flow operands
division
Compiler intrinsics use direct unqualified name!(...) syntax. The ! marks the call as an
intrinsic; it is not part of the identifier. Bare and qualified calls without ! resolve as
ordinary user functions, while qualified bang calls are rejected.
/ and /= accept only floating-point operands. Integer division must state its rounding and
remainder convention with one of these intrinsics:
| Builtin | Result |
|---|---|
divtrunc!(a, b) |
quotient rounded toward zero |
divfloor!(a, b) |
quotient rounded toward negative infinity |
divexact!(a, b) |
truncated quotient; traps unless it divides exactly |
divceil!(a, b) |
quotient rounded toward positive infinity |
rem!(a, b) |
remainder paired with divtrunc!; sign follows a |
mod!(a, b) |
modulus paired with divfloor!; sign follows b |
The operands may be compatible concrete integer or float scalars. Existing literal coercion and numeric widening rules apply, the result has the common operand type, and float quotients are integral-valued floats. These identities hold when representable:
divtrunc!(a, b) * b + rem!(a, b) == a
divfloor!(a, b) * b + mod!(a, b) == a
Negative operands distinguish the operations:
divtrunc!(-5, 3) == -1
divfloor!(-5, 3) == -2
divceil!(-5, 3) == -1
rem!(-5, 3) == -2
mod!(-5, 3) == 1
mod!(5, -3) == -1
All six builtins diagnose a zero denominator at comptime and trap at runtime, including float
zero. Quotient operations also trap for signed minval!(T), -1; rem! and mod! return zero for
that pair. divexact! traps when divtrunc!(a, b) * b == a is false in the operand type, so float
exactness follows floating-point equality. Other float NaN and infinity behavior follows the
underlying IEEE operations. Ordinary float / remains unchecked and therefore preserves IEEE
infinity/NaN behavior.
Only the six bang calls are intrinsic. Bare calls such as divfloor(a, b) and qualified calls such
as math.divfloor(a, b) resolve to ordinary functions.
functions, C interop, and linking
- demand-monomorphized Brolang and C-ABI functions
- integer comptime value parameters such as
make_array func($N usize) [N]u8, specialized by value and omitted from the runtime ABI - explicit comptime type parameters such as
max func($T type, a, b T) T, specialized by type and omitted from the runtime ABI - comptime parameters may appear anywhere, are erased from the runtime ABI, and accept recursively stable booleans, integers, floats, types, immutable bytes, enums, fixed arrays, records/tuples, optionals, and tagged unions; equal structural values share specializations, while pointers, functions, general slices, fallibles, ranges, untagged unions, and undefined values have no stable comptime identity
- comptime parameters may be omitted when uniquely recoverable from runtime arguments, the immediate expected result, or exact type-factory provenance;
_is an explicit inference hole - forced typed comptime expressions such as
$sum(1, 2),$Point { x = 1, y = 2 }, and comptime value blocks such as${ yield 4 } - comptime execution for bodyful Brolang functions with mutable locals, loops,
defer,match,try/catch, pointer/slice storage mutation, pointer captures, and calls through comptime-known function values;undefinedstorage may be initialized at comptime, but remaining poison cannot be observed - comptime type factories such as
Box func($T type) type { return struct { value T } }; calls likeBox(i32)are concrete nominal types and may appear anywhere a type is expected - tuple types are unnamed-field structs (
struct { i32, []u8 }), tuple values use{1, "bro"}/{1,}/{}, and fields use canonical numeric names such as.0 typeinfo!,field!,compile_error!, and semanticexpand forprovide compile-time record and enum reflection and heterogeneous static expansion without runtime metadata; enum reflection exposes declaration-ordered fields, reflected aggregates remain persistent compile-time values, and expand-loopbreak/continuemust be selected entirely at comptimetag!(value)reads a tagged union's active discriminant and folds when the value is comptime-known;tagname!(enum_value)requires a comptime-known enum value and returns its immutable declaration name- bodyful
c_funcdefinitions and bodylessc_funcdeclarations with exact external symbol names - concrete-only C signatures, C variadic declarations/calls, and C default argument promotions
- native function pointer values and types with
@func(...) R, fallible@func(...) R ! E, optional?@func(...) R, and non-variadic native indirect calls - Apple Silicon C ABI lowering for scalars, pointers, fixed-signature plain records/unions, small aggregates, homogeneous float aggregates, and indirect aggregate returns
- imported C typedefs, scalar constants, enum constants, fixed arrays, complete plain structs/unions, C
void*as nullableanyopaquepointers, and pointers to opaque records - imported external C object variables, including mutable variables and immutable object globals
- object-like scalar and plain record/union macro constants
- supported static inline C functions through generated external wrappers
- C function pointer types, imported nullable callback typedefs, concrete
c_funccallback values, and postfix calls through non-null function pointers brolang translate-c <header.h>... [--output-dir <dir>]for native.brobindings from supported C declarations, with package-wide declaration deduplication when writing multiple headersbrolang --translate-c stdio.hfor offline bindings from Zig-bundled standard C headers- ordered linking of additional C sources, objects, archives, library paths, and libraries through compiler CLI options
standard packages
- root
stdre-exportsArrayList(T)while its operations remain instd/arraylist std/memgeneric slice equality, allocator contract with raw byte operations, typedempty/alloc/realloc/free, overflow checks, zero-sized-type support, and failure-preserving reallocationstd/arraylistgenericArrayList(T)with directitemsslice access, explicit capacity, allocator ownership, fallible reserve/append, clear, and deinitstd/ioexplicitIocapabilities, provider-boundReader/Writerhandles, existing-file open/close operations, allocation-freewrite_all, and comptime-expanded writer-firstprint; formatting supports natural{}, byte{s}, decimal{d}, integer{b}/{o}/{x}/{X}, byte-character{c}, scientific float{e}, and{{/}}, with malformed formats and incompatible tuple fields rejected at comptime- entry points are either
main func() ...ormain func(init process.Init) ...;std/process.Initcarries startup capabilities, currently onlyio, while the system provider remains hidden insidestd/io std/debug.printis an allocation-free, failure-ignoring stderr escape hatch independent ofprocess.Init
compiler behavior
- error-tolerant compilation with diagnostics and runtime traps where recovery is possible
- lazy semantic checking of demanded function specializations
- static, eager runtime, mutable runtime, and deferred problematic globals with cycle diagnostics
- demand-driven LLVM declarations for referenced foreign functions
- root
mainmay be parameterless or accept canonical@std/process Init; the generated C entry point obtains the hidden system I/O provider and constructs the init value - replaceable dynamically loaded libclang C-import backend
- C-header import caching by canonical path, target, include paths, and defines
PLANNED / DEFERRED
- native Brolang variadic functions
- exporting Brolang functions to C and broader target-specific C ABI lowering
- non-plain C record layouts such as bitfields, packed records, flexible arrays, qualified fields, and C variadic record arguments
- arenas, pools, build-mode heap policy, and escaping-allocation diagnostics
- recursive type factories, reflection payloads beyond records/enums, and type-producing unions/enums
- broader Zig-style pointer/result casts beyond V1
ptrcast!(T, ptr) - sum-type ABI/layout polish, including dynamic tag-width shrinking, all-void channel collapse, and cross-module global-id determinism
- backed/C enum composition and must-consume fallible linting
- result-to-argument type-demand propagation through function call boundaries
- distinct-type backing operators and reverse explicit conversions
- string concatenation operator