diff --git a/LANGUAGE.md b/LANGUAGE.md index 88022fb..4586538 100644 --- a/LANGUAGE.md +++ b/LANGUAGE.md @@ -1,77 +1,90 @@ # language features +This file is the compact current-state ledger. `TODO.md` remains the detailed +roadmap and milestone history. + ## IMPLEMENTED -### source and declarations +### source, declarations, and packages - newline-terminated statements and `#` comments - immutable `::` bindings, mutable function-local `=` bindings, and `_` sinks -- immutable and mutable locals -- package-level globals and functions -- interned identifiers - -### types and expressions - -- exact-width `i8` through `i64`, `u8` through `u64`, `f32`, `f64`, `isize`, `usize`, `bool`, `void`, and inferred integer-constrained `int` -- target-dependent atomic C primitives from `c_char` through `c_longdouble` -- C primitives remain semantically distinct from exact-width Brolang primitives until target lowering -- nominal distinct types with explicit exact-backing construction: `UserID :: distinct u32` and `UserID(42)` -- contextual integer and character literals and constant folding of arithmetic and negation trees -- strict numeric conversions, binary `+ - * /` with checked integer overflow and divide-by-zero traps (floats follow IEEE), and unary negation -- boolean literals, comparisons, unary `!`, and short-circuiting `and` / `or` -- arrays `[N]T`, sentinel arrays `[N;S]T`, single-item pointers `@T`, many-item pointers `*T`, sentinel many-item pointers `[*;S]T`, pointer offsets, slices, and explicit slicing -- immutable UTF-8 string literals typed as pointers to static sentinel arrays: `@[N;0]u8` -- pointer-to-array `.len`, indexing, and slicing without explicit dereference -- arrays expose `.len` but not `.ptr`; slices and pointers-to-arrays expose `.ptr` and preserve sentinel information when available -- information-preserving and information-forgetting pointer-to-array decay and sentinel slice/pointer weakening; array values never implicitly decay -- narrow immutable zero-terminated byte pointer conversion to `*c_char` and `[*;0]c_char`, without general `u8`/`c_char` interchange -- optionals with trapping postfix `?`, `orelse`, and nullable pointer representation -- conditional optional unwrapping with immutable guard/then-block bindings, guarded captures, and left-to-right short-circuiting multi-unwrap: `if first and second |a, b : guard| { ... }` -- source-order native structs, defined or opaque `c_struct`, and keyed record literals -- complete plain imported C structs and unions as runtime values; incomplete or unsupported-layout records remain pointer-only -- C function pointer types as pointer-sized runtime values, including manual `*c_func(...) T` spelling and nullable imported callback typedefs -- postfix pointer dereference, general writable locations, function calls, assignments, compound assignment (`+= -= *= /=`) with single-evaluation lvalues, and returns -- first-class exclusive and inclusive integer ranges: `start..end` and `start..=end` -- boolean `if` statements and `while` loops with optional post-iteration assignment/expression clauses -- `for` loops over ranges, arrays, slices, and pointers-to-arrays, with copy, pointer, and optional `usize` index captures -- `|@item|` pointer captures inherit pointee mutability from the iterable; arrays require an explicit pointer such as `&items` - -### functions and packages - -- demand-monomorphized functions -- bodyful `c_func` definitions using the c calling convention -- bodyless `c_func` declarations with exact, globally unique external symbol names -- concrete-only foreign signatures -- Apple Silicon C ABI scalar, pointer, and fixed-signature plain record/union lowering, including narrow integer extension attributes +- immutable package 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, aliases, and qualified member access -- relative `.h` imports as synthetic package namespaces -- transitive external C function prototypes, typedef chains, C scalars, fixed arrays, complete plain records/unions, and pointers to opaque C records -- imported external C object variables, including writable globals and immutable arrays -- object-like scalar and plain record/union C macro constants +- file-local relative imports, import aliases, and qualified member access +- 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`, 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)` +- arrays `[N]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 +- 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, contextual enum literals, and imported C enums as target-backed integer aliases +- source-order native structs, defined/opaque `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 + +### expressions and control flow + +- checked integer `+ - * /`, unary `-`, divide-by-zero traps, IEEE float arithmetic, comparisons, `!`, `and`, and `or` +- assignments and compound assignments `+= -= *= /=` with single evaluation of complex lvalues +- 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 +- bare block scopes and `defer`, including LIFO flushing on fall-through, `return`, `break`, and `continue` +- value blocks, value `if`, value loops, value `match`, `yield`, and labeled `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 + +### functions, C interop, and linking + +- demand-monomorphized Brolang and C-ABI functions +- 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 +- 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, 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 -- bodyless manual and imported C variadic declarations with target-aware default argument promotions -- passing concrete `c_func` declarations/definitions as C callback values and calling non-null C function pointers with postfix call syntax -- reference-time diagnostics for unsupported imported C declarations +- C function pointer types, imported nullable callback typedefs, concrete `c_func` callback values, and postfix calls through non-null function pointers +- `brolang translate-c ` for native `.bro` bindings from supported C declarations +- ordered linking of additional C sources, objects, archives, library paths, and libraries through compiler CLI options + +### standard packages + +- `std/mem/heap` v1 byte allocation over libc: `alloc(size usize) ?*mut u8` and `free(ptr ?*mut u8)` ### compiler behavior -- error-tolerant compilation with runtime diagnostic traps +- error-tolerant compilation with diagnostics and runtime traps where recovery is possible - lazy semantic checking of demanded function specializations +- static, eager runtime, and deferred problematic globals with cycle diagnostics - demand-driven LLVM declarations for referenced foreign functions -- ordered linking of additional c sources, objects, and libraries - replaceable dynamically loaded libclang C-import backend -- per-compilation C-header import caching by canonical path, target, ordered include paths, and ordered defines -- static, eager runtime, and deferred problematic globals +- C-header import caching by canonical path, target, include paths, and defines -## PLANNED +## PLANNED / DEFERRED -### foreign functions and linking - -- exporting brolang functions to c -- additional target-specific C ABI lowering - -### scalar and compound types - -- tuples and native variadic functions -- C enums and non-plain C record layouts +- comptime polymorphism +- tuples and 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 +- typed heap allocation, allocator parameters, arenas, pools, build-mode heap policy, and escaping-allocation diagnostics +- 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 diff --git a/TODO.md b/TODO.md index 8e31986..a516d78 100644 --- a/TODO.md +++ b/TODO.md @@ -620,10 +620,13 @@ byte slice when possible, and `[;0]u8` slices can decay to immutable `*c_char`/`?*c_char` parameters -25. dynamic heap allocation - - see below for direction - - notes below are too big in scope for a first pass and the language is not mature enough to support it yet - - this first pass should focus on just basic heap allocation, so we have something to work with +25. dynamic heap allocation (implemented; v1) + - `std/mem/heap` is a tiny relative-importable package over libc `malloc`/`free` + - `heap.alloc(size usize) ?*mut u8` returns nullable mutable byte memory; callers use existing optional unwraps + - `heap.free(ptr ?*mut u8) void` forwards to C `free`, including `none` / null + - typed allocation, allocator parameters, arenas/pools, build-mode heap policy, and escaping-allocation diagnostics remain deferred + +26. comptime polymorphism (zig inspired) ## A word on multi-unwrap diff --git a/compiler_tests.odin b/compiler_tests.odin index 38fe31d..ab519e3 100644 --- a/compiler_tests.odin +++ b/compiler_tests.odin @@ -2062,6 +2062,40 @@ milestone_24_rejects_invalid_forms :: proc(t: ^testing.T) { } } +@(test) +milestone_25_heap_compiles_and_runs :: proc(t: ^testing.T) { + output := "/tmp/brolang-test-heap" + defer _ = os.remove(output) + status := compiler_core.compile_package("examples/programs/heap", output) + testing.expect_value(t, status, 0) + state := run_executable(output) + testing.expect_value(t, state.exit_code, 0) +} + +@(test) +milestone_25_heap_emits_libc_alloc_declarations :: proc(t: ^testing.T) { + sources := source.init_store() + defer source.destroy_store(&sources) + diagnostics := source.init_store_diagnostics(&sources) + defer source.destroy_diagnostics(&diagnostics) + symbols := symbol.init_table() + defer symbol.destroy_table(&symbols) + ast_module, loaded := loader.load("examples/programs/heap", &sources, &diagnostics, &symbols) + defer ast.destroy_module(&ast_module) + testing.expect(t, loaded) + + hir_module := checker.check(&ast_module, &diagnostics, &symbols) + defer hir.destroy_module(&hir_module) + ir_module := lower.lower(&hir_module) + defer ir.destroy_module(&ir_module) + llvm_text := llvm.emit(&ir_module, &diagnostics, &symbols) + defer delete(llvm_text) + + testing.expect_value(t, len(diagnostics.items), 0) + testing.expect(t, strings.contains(llvm_text, "declare ptr @malloc(i64)")) + testing.expect(t, strings.contains(llvm_text, "declare void @free(ptr)")) +} + @(test) control_flow_compiles_and_runs :: proc(t: ^testing.T) { output := "/tmp/brolang-test-control-flow" diff --git a/examples/programs/heap/main.bro b/examples/programs/heap/main.bro new file mode 100644 index 0000000..001be61 --- /dev/null +++ b/examples/programs/heap/main.bro @@ -0,0 +1,27 @@ +heap :: import "../../../std/mem/heap" + +printf c_func(fmt *c_char, ...) c_int + +main func() i32 { + memory ?*mut u8 = heap.alloc(4) + defer heap.free(memory) + + if memory |bytes| { + bytes[0] = 10 + bytes[1] = 20 + bytes[2] = bytes[0] + bytes[1] + + _ = printf("bytes[0]: %d\n", bytes[0]) + _ = printf("bytes[1]: %d\n", bytes[1]) + _ = printf("bytes[2]: %d\n", bytes[2]) + _ = printf("bytes[3]: %d\n", bytes[3]) + + if (bytes[2] != 30) { + return 2 + } + + return 0 + } + + return 1 +} diff --git a/std/mem/heap/heap.bro b/std/mem/heap/heap.bro new file mode 100644 index 0000000..445404b --- /dev/null +++ b/std/mem/heap/heap.bro @@ -0,0 +1,6 @@ +malloc c_func(size usize) ?*mut u8 +free c_func(ptr ?*mut u8) void + +alloc func(size usize) ?*mut u8 { + return malloc(size) +}