205 lines
14 KiB
Markdown
205 lines
14 KiB
Markdown
# 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
|
|
- `hide` makes 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 `.h` imports as synthetic C header package namespaces
|
|
- root `main` validation 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 contextual `int`, `float`, and `range` constraints
|
|
- target-dependent C scalar primitives from `c_char` through `c_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)` and `maxval!(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, `.ptr` on 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_char` without general `u8`/`c_char` interchange
|
|
- 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`, complete `c_struct { ... }`, keyed record literals, native untagged unions, and native tagged unions `union(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 | B` for unbacked enums and tagged unions, using program-global `u16` variant ids
|
|
- fallible channel types `T ! E`, where `E` is a native enum/tagged union or supported sum composition; `void ! E` functions complete successfully on fallthrough, and void-success `catch` handlers may fall through without `yield`
|
|
|
|
#### 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:
|
|
|
|
```bro
|
|
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`, and `or`
|
|
- 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` / `else`, braceless single-statement branches, and optional parenthesized conditions
|
|
- `while` loops with optional post-iteration update clauses
|
|
- `for` loops over ranges, arrays, slices, and pointers-to-arrays with copy captures, pointer captures `|@item|`, and optional `usize` index captures
|
|
- `break`, `continue`, labeled `break :label`, labeled `continue :label`, and labeled plain blocks; `break :label` can cross nested scopes to exit a labeled block
|
|
- bare block scopes, `defer`, and fallible-function `errdefer` with optional error capture; cleanup is block-scoped and LIFO
|
|
- bare void `return`, same-line `return value`, value blocks, value `if`, value loops, value `match`, and strictly value-producing `yield value` / `yield :label value`
|
|
- `match` statements/expressions over enums, tagged unions, and scalars, including exhaustiveness checks, payload captures, pointer payload captures, multi-pattern arms, and scalar range patterns
|
|
- fallible `try`, fallback `catch`, and `catch |e| { ... }` handler blocks
|
|
- direct `return match ...` and `yield 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:
|
|
|
|
```bro
|
|
divtrunc!(a, b) * b + rem!(a, b) == a
|
|
divfloor!(a, b) * b + mod!(a, b) == a
|
|
```
|
|
|
|
Negative operands distinguish the operations:
|
|
|
|
```bro
|
|
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 type/integer/string parameters may appear anywhere, are erased from the runtime ABI, and may be omitted when uniquely recoverable from runtime arguments or the immediate expected result; `_` 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
|
|
- comptime type factories such as `Box func($T type) type { return struct { value T } }`; calls like `Box(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 semantic `inline for` provide compile-time record reflection and heterogeneous static expansion without runtime metadata; reflected aggregates remain persistent compile-time values, and inline-loop `break` / `continue` must be selected entirely at comptime
|
|
- bodyful `c_func` definitions and bodyless `c_func` declarations 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 nullable `anyopaque` pointers, 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_func` callback values, and postfix calls through non-null function pointers
|
|
- `brolang translate-c <header.h>` for native `.bro` bindings from supported C declarations
|
|
- `brolang --translate-c stdio.h` for 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 `std` re-exports `ArrayList(T)` while its operations remain in `std/arraylist`
|
|
- `std/mem` generic slice equality, allocator contract with raw byte operations, typed `empty` / `alloc` / `realloc` / `free`, overflow checks, zero-sized-type support, and failure-preserving reallocation
|
|
- `std/arraylist` generic `ArrayList(T)` with direct `items` slice access, explicit capacity, allocator ownership, fallible reserve/append, clear, and deinit
|
|
- `std/io` explicit `Io` capabilities, `Reader`/`Writer` stream values, allocation-free `write_all`, and comptime-expanded writer-first `print`; formatting currently supports sequential `{s}` / `{d}` plus `{{` / `}}`, with malformed formats and incompatible tuple fields rejected at comptime
|
|
- entry points are either `main func() ...` or `main func(init process.Init) ...`; `std/process.Init` carries startup capabilities, currently only `io`, while the system provider remains hidden inside `std/io`
|
|
- `std/debug.print` is an allocation-free, failure-ignoring stderr escape hatch independent of `process.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 `main` may 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
|
|
|
|
- aggregate comptime parameters and stable aggregate specialization keys
|
|
- 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, 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
|