Compare commits

...

2 Commits

Author SHA1 Message Date
hl-valdemar 0706188b98 stlib arraylist 2026-07-12 16:59:46 +02:00
hl-valdemar b0c716537e bug fixes 2026-07-12 13:04:20 +02:00
25 changed files with 1441 additions and 122 deletions
+5 -2
View File
@@ -59,6 +59,7 @@ roadmap and milestone history.
- explicit comptime type parameters such as `max func($T type, a, b T) T`, specialized by type and omitted from the runtime ABI
- 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
- 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
@@ -74,7 +75,8 @@ roadmap and milestone history.
### standard packages
- `std/mem` allocator contract with a context pointer plus shared `AllocatorVTable`, raw byte operations `raw_alloc` / `raw_realloc` / `raw_free`, fallible typed `alloc(T, allocator, count)`, and typed `free(T, allocator, memory)`; failed nonzero raw reallocation preserves the original allocation, while zero size frees it
- `std/mem` 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
### compiler behavior
@@ -91,7 +93,8 @@ roadmap and milestone history.
- 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 helpers, arenas, pools, build-mode heap policy, and escaping-allocation diagnostics
- arenas, pools, build-mode heap policy, and escaping-allocation diagnostics
- recursive type factories, type reflection, inferred type arguments, and type-producing unions/enums
- broader Zig-style pointer/result casts beyond V1 `ptr_cast(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
+2
View File
@@ -179,8 +179,10 @@ Current prototype features:
- Demand-monomorphized Brolang and C-ABI functions
- Integer and type comptime parameters (`func($N usize) [N]u8`, `func($T type, value T) T`) specialized by comptime argument
- Forced typed comptime expressions (`$sum(1, 2)`, `$Point { x = 1, y = 2 }`) and comptime value blocks (`${ yield 4 }`)
- Zig-style comptime type factories returning anonymous native structs (`Box func($T type) type`, used as `Box(i32)`)
- 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
- Native function pointer values and types (`*func(...) R`, `*func(...) R ! E`, `?*func(...) R`)
- Typed allocation/reallocation through `std/mem` and generic dynamic arrays through `std/arraylist`
- Bodyless concrete C function declarations with exact external symbol names
- Bodyless manual and imported C variadic declarations with default argument promotions
- Ordered linking of additional C sources, objects, archives, and libraries
+28 -3
View File
@@ -732,12 +732,37 @@
- deferred: build graph / steps / caching, multiple artifacts, computed paths
(needs string building), struct field defaults to drop `&[]` on empty lists
29. basic `std/arraylist` implementation using the new `std/mem` typed allocation
29. fix bugs (implemented)
- `return _` was already the supported empty return for void functions; the original
bare-`return` report was stale
- catch value blocks may end by returning from the function instead of yielding when
every path exits
- implicit-conversion diagnostics render source-level composite and named types instead
of internal `<type N>` ids
- aliases resolve transparently in value contexts, including composed enum/union sums
- final open-constant defaults feed one last inference fixpoint before stale
specializations are pruned
30. disallow arbitrary integer division
30. Zig-style type factories and basic `std/arraylist` (implemented; v1)
- comptime-only functions may return `type`; anonymous `struct { ... }` expressions and
factory calls such as `ArrayList(i32)` resolve to cached nominal concrete types
- factory parameters use the existing explicit `$T type` / integer comptime parameters;
normal comptime control flow and helper factory calls are supported
- type-factory calls work in signatures, nested types, struct literals, and type builtins;
runtime materialization and recursive specializations are diagnosed
- `std/mem` adds typed `empty` and failure-preserving `realloc`, including overflow,
zero-count, zero-sized-type, and alignment handling
- `std/arraylist.ArrayList(T)` exposes `items`, `capacity`, and `allocator`, with fallible
reserve/append, roughly 1.5x growth from 8, clear-without-free, and reusable deinit
- deferred: recursive factories, reflection, inferred type arguments, type-producing
unions/enums, pop/insert/remove/shrink/clone container operations
31. threading generic/polymorphic type information everywhere (init, deinit, etc.) might be annoying and verbose. consider whether generic structs could fit nicely to avoid this.
32. disallow arbitrary integer division
- take inspiration from zig
- see also below for a word on unchecked casts
- the user should be explicit about what they mean with integer division (e.g. `div`, `rem`)
- the user should be explicit about what they mean with integer division (e.g. `div`, `rem`, `trunc`)
## A word on unchecked casts
+4
View File
@@ -105,6 +105,7 @@ Expr_Kind :: enum u8 {
Try,
Catch,
Function_Literal,
Anonymous_Struct_Type,
}
Expr :: struct {
@@ -294,6 +295,7 @@ Module :: struct {
unsupported: [dynamic]Unsupported,
c_trampolines: [dynamic]Trampoline,
strings: [dynamic]string,
type_fields: [dynamic]types.Field,
type_store: types.Store,
allocator: mem.Allocator,
}
@@ -312,6 +314,7 @@ init_module :: proc(allocator := context.allocator) -> Module {
module.unsupported.allocator = allocator
module.c_trampolines.allocator = allocator
module.strings.allocator = allocator
module.type_fields.allocator = allocator
return module
}
@@ -362,5 +365,6 @@ destroy_module :: proc(module: ^Module) {
delete(module.unsupported)
delete(module.c_trampolines)
delete(module.strings)
delete(module.type_fields)
types.destroy_store(&module.type_store)
}
+291 -26
View File
@@ -130,6 +130,19 @@ Import_Index_Entry :: struct {
id: ast.Import_Id,
}
Type_Factory_Entry :: struct {
template: ast.Function_Id,
values: []Comptime_Value,
result: types.Type,
resolving: bool,
}
Generated_Type_Entry :: struct {
expr: ast.Expr_Id,
values: []Comptime_Value,
result: types.Type,
}
Checker :: struct {
ast_module: ^ast.Module,
diagnostics: ^source.Diagnostics,
@@ -169,6 +182,8 @@ Checker :: struct {
current_result: types.Type,
current_build_ctx: ^Build_Ctx,
current_comptime_values: []Comptime_Value,
type_factories: [dynamic]Type_Factory_Entry,
generated_types: [dynamic]Generated_Type_Entry,
target: target.Target,
allocator: mem.Allocator,
}
@@ -265,14 +280,102 @@ record_unused_locals :: proc(
}
}
// type_label renders a type for a diagnostic, resolving a named type (enum/union/struct/
// distinct) to its declared source name; primitives and unnamed types fall back to
// `types.name` (which prints `<type N>` for anonymous nodes).
type_label :: proc(checker: ^Checker, value: types.Type) -> string {
if node, ok := types.node(&checker.module.types, value); ok && node.name != 0 {
return symbol_text(checker, symbol.Id(node.name))
write_type_label :: proc(checker: ^Checker, builder: ^strings.Builder, value: types.Type) {
store := &checker.module.types
item, ok := types.node(store, value)
if !ok {
strings.write_string(builder, types.name(value))
return
}
return types.name(value)
if item.name != 0 {
strings.write_string(builder, symbol_text(checker, symbol.Id(item.name)))
return
}
switch item.kind {
case .Array:
strings.write_byte(builder, '[')
if item.inferred_count {
strings.write_byte(builder, '_')
} else {
fmt.sbprintf(builder, "%d", item.count)
}
if item.has_sentinel {
fmt.sbprintf(builder, ";%d", item.sentinel)
}
strings.write_byte(builder, ']')
if item.mutable {
strings.write_string(builder, "mut ")
}
write_type_label(checker, builder, item.child)
case .Pointer:
if item.has_sentinel {
fmt.sbprintf(builder, "[*;%d]", item.sentinel)
} else {
strings.write_byte(builder, '*' if item.many else '@')
}
if item.mutable {
strings.write_string(builder, "mut ")
}
write_type_label(checker, builder, item.child)
case .Slice:
if item.has_sentinel {
fmt.sbprintf(builder, "[;%d]", item.sentinel)
} else {
strings.write_string(builder, "[]")
}
if item.mutable {
strings.write_string(builder, "mut ")
}
write_type_label(checker, builder, item.child)
case .Range:
strings.write_string(builder, "range(")
write_type_label(checker, builder, item.child)
strings.write_byte(builder, ')')
case .Optional:
strings.write_byte(builder, '?')
write_type_label(checker, builder, item.child)
case .Function:
strings.write_string(builder, "c_func(" if item.c_abi else "func(")
for param, index in types.params_for(store, value) {
if index > 0 {
strings.write_string(builder, ", ")
}
write_type_label(checker, builder, param.type)
}
if item.variadic {
if item.field_count > 0 {
strings.write_string(builder, ", ")
}
strings.write_string(builder, "...")
}
strings.write_string(builder, ") ")
write_type_label(checker, builder, item.child)
case .Fallible:
write_type_label(checker, builder, item.child)
strings.write_string(builder, " ! ")
write_type_label(checker, builder, item.extra)
case .Type_Call:
strings.write_string(builder, "<type factory call>")
case .Struct:
strings.write_string(builder, "struct")
case .Union:
strings.write_string(builder, "union")
case .Enum:
strings.write_string(builder, "enum")
case .Alias, .Distinct, .Named:
write_type_label(checker, builder, item.child)
case .Invalid, .Void, .Anyopaque, .Int_Constraint, .Float_Constraint, .Range_Constraint, .Scalar:
strings.write_string(builder, types.name(value))
}
}
// Render dynamic types using source syntax so diagnostics never expose internal
// type-store ids such as `<type 230>`.
type_label :: proc(checker: ^Checker, value: types.Type) -> string {
builder := strings.builder_make(context.temp_allocator)
write_type_label(checker, &builder, value)
return strings.to_string(builder)
}
is_ptr_cast_call :: proc(checker: ^Checker, expr: ast.Expr) -> bool {
@@ -460,6 +563,8 @@ type_from_syntax :: proc(
store := &checker.module.types
changed := false
#partial switch item.kind {
case .Alias:
return type_from_syntax(checker, item.child, pkg, file, depth+1)
case .Array:
child := type_from_syntax(checker, item.child, pkg, file, depth+1)
changed = changed || child != item.child
@@ -509,6 +614,8 @@ type_from_syntax :: proc(
if params_changed || result != item.child {
return types.function(store, resolved_params, result, item.c_abi, item.variadic)
}
case .Type_Call:
return resolve_type_factory_call(checker, ast.Expr_Id(item.count_expr), pkg, file)
}
if changed {
return types.intern(store, item)
@@ -1042,10 +1149,133 @@ resolve_type_argument :: proc(
value := types.find_named(&checker.module.types, u32(target_pkg), u32(expr.name), file=u32(expr_lookup_file(expr, file)))
value = types.resolve_alias(value, &checker.module.types)
return value, types.is_valid(value)
case .Call:
value := resolve_type_factory_call(checker, expr_id, pkg, file)
return value, types.is_valid(value)
}
return types.INVALID, false
}
clone_comptime_values :: proc(values: []Comptime_Value, allocator: mem.Allocator) -> []Comptime_Value {
result := make([]Comptime_Value, len(values), allocator)
copy(result, values)
return result
}
resolve_generated_struct_type :: proc(checker: ^Checker, expr_id: ast.Expr_Id, pkg: ast.Package_Id, file: ast.File_Id) -> types.Type {
for entry in checker.generated_types {
if entry.expr == expr_id && comptime_values_equal(entry.values, checker.current_comptime_values) {
return entry.result
}
}
if expr_id == ast.INVALID_EXPR || int(expr_id) >= len(checker.ast_module.exprs) {
return types.INVALID
}
expr := checker.ast_module.exprs[expr_id]
field_start := int(u32(expr.integer>>32))
field_count := int(u32(expr.integer))
if field_start < 0 || field_count < 0 || field_start+field_count > len(checker.ast_module.type_fields) {
return types.INVALID
}
template_fields := checker.ast_module.type_fields[field_start:field_start+field_count]
fields := make([]types.Field, len(template_fields), checker.allocator)
defer delete(fields, checker.allocator)
for field, index in template_fields {
resolved := type_from_syntax(checker, field.type, pkg, file)
if !is_runtime_type(checker, resolved) || types.is_void(resolved) {
source.addf(checker.diagnostics, expr.span, "anonymous struct field '%s' requires a concrete runtime type, got %s", symbol_text(checker, symbol.Id(field.name)), type_label(checker, resolved))
return types.INVALID
}
fields[index] = types.Field{name=field.name, type=resolved}
}
result := types.struct_generated(&checker.module.types, fields)
append(&checker.generated_types, Generated_Type_Entry{
expr=expr_id,
values=clone_comptime_values(checker.current_comptime_values, checker.allocator),
result=result,
})
return result
}
resolve_type_factory_call :: proc(checker: ^Checker, expr_id: ast.Expr_Id, pkg: ast.Package_Id, file: ast.File_Id) -> types.Type {
if expr_id == ast.INVALID_EXPR || int(expr_id) >= len(checker.ast_module.exprs) {
return types.INVALID
}
expr := checker.ast_module.exprs[expr_id]
if expr.kind != .Call || expr.left != ast.INVALID_EXPR {
source.add(checker.diagnostics, expr.span, "type position requires a direct type-factory call")
return types.INVALID
}
target_pkg, available := expr_package(checker, expr, pkg, file, true)
if !available {
return types.INVALID
}
template := find_template(checker, expr.name, target_pkg, expr_lookup_file(expr, file))
if template == ast.INVALID_FUNCTION || int(template) >= len(checker.ast_module.functions) {
source.addf(checker.diagnostics, expr.span, "unknown type factory '%s'", symbol_text(checker, expr.name))
return types.INVALID
}
function := checker.ast_module.functions[template]
if !is_type_metatype_syntax(checker, function.result) || types.is_valid(function.error) {
source.addf(checker.diagnostics, expr.span, "function '%s' does not return a type", symbol_text(checker, expr.name))
return types.INVALID
}
for param in function.params {
if !param.comptime_value {
source.addf(checker.diagnostics, param.span, "type-factory parameter '%s' must be comptime", symbol_text(checker, param.name))
return types.INVALID
}
}
if !valid_call_arity(function, len(expr.args)) {
source.addf(checker.diagnostics, expr.span, "type factory '%s' expects %d arguments, got %d", symbol_text(checker, expr.name), len(function.params), len(expr.args))
return types.INVALID
}
values, ok := collect_comptime_values(checker, function, expr.args, pkg, file, true, checker.current_comptime_values)
defer delete(values, checker.allocator)
if !ok {
return types.INVALID
}
// A generic function's declaration is validated before it has a specialization.
// Leave calls containing its unresolved type parameters pending until then.
for value in values {
if value.kind != .Type {
continue
}
if item, item_ok := types.node(&checker.module.types, value.type); item_ok && item.kind == .Named && !item.declared {
return types.INVALID
}
}
for &entry in checker.type_factories {
if entry.template != template || !comptime_values_equal(entry.values, values) {
continue
}
if entry.resolving {
source.addf(checker.diagnostics, expr.span, "recursive type-factory specialization of '%s'", symbol_text(checker, expr.name))
return types.INVALID
}
return entry.result
}
entry_index := len(checker.type_factories)
append(&checker.type_factories, Type_Factory_Entry{
template=template,
values=clone_comptime_values(values, checker.allocator),
result=types.INVALID,
resolving=true,
})
state := ct_state_make(checker, pkg, file)
value, flow, eval_ok := ct_eval_call_expr(&state, expr, function.result, 0)
result := types.INVALID
if eval_ok && flow.kind == .Normal && value != INVALID_CT_VALUE && int(value) < len(state.values) && state.values[value].kind == .Type {
result = types.Type(state.values[value].index)
} else if state.diagnostic == source.INVALID_DIAGNOSTIC {
source.addf(checker.diagnostics, expr.span, "type factory '%s' did not return a type", symbol_text(checker, expr.name))
}
ct_state_destroy(&state)
checker.type_factories[entry_index].result = result
checker.type_factories[entry_index].resolving = false
return result
}
collect_comptime_values :: proc(
checker: ^Checker,
function: ast.Function,
@@ -1274,7 +1504,7 @@ mark_expr_imports_used :: proc(checker: ^Checker, expr_id: ast.Expr_Id, file: as
}
case .Add, .Sub, .Mul, .Div, .Index, .Orelse, .Eq, .Ne, .Lt, .Le, .Gt, .Ge, .And, .Or, .Range:
append(&stack, expr.left, expr.right)
case .Invalid, .Integer, .Float, .String, .Bool, .None, .Undefined, .Type, .Name:
case .Invalid, .Integer, .Float, .String, .Bool, .None, .Undefined, .Type, .Name, .Anonymous_Struct_Type:
}
}
}
@@ -2085,6 +2315,9 @@ infer_expr :: proc(
case .Type:
last = types.INVALID
_ = pop(&stack)
case .Anonymous_Struct_Type:
last = types.INVALID
_ = pop(&stack)
case .Integer:
last = types.I64
if expr.integer <= 0x7fff_ffff_ffff_ffff {
@@ -3246,6 +3479,7 @@ infer_all :: proc(checker: ^Checker) {
ensure_spec(checker, main_template, nil)
}
defaults_applied := false
for {
changed := false
checker.global_demands_dirty = false
@@ -3334,20 +3568,28 @@ infer_all :: proc(checker: ^Checker) {
changed = true
}
if !changed {
break
}
}
// Any open constant that no use ever demanded now takes its default: an integer
// constant the smallest signed type that holds its value, a float constant f64.
if !defaults_applied {
defaults_applied = true
defaulted := false
// No authoritative demand can still arrive. Assign final defaults, then
// continue the same fixpoint so dependent globals/specs observe them.
for global, index in checker.ast_module.globals {
if global.external || is_runtime_type(checker, checker.global_types[index]) {
continue
}
if checker.global_open_const[index] {
checker.global_types[index] = types.smallest_signed_for_literal(i64(checker.global_const_value[index]))
defaulted = true
} else if checker.global_open_float[index] {
checker.global_types[index] = types.F64
defaulted = true
}
}
if defaulted {
continue
}
}
break
}
}
}
@@ -3604,8 +3846,8 @@ coerce_expr :: proc(
checker.diagnostics,
span,
"cannot implicitly convert %s to %s",
types.name(actual),
types.name(expected),
type_label(checker, actual),
type_label(checker, expected),
)
return invalid_hir_expr(checker, span, id, expected)
}
@@ -4533,7 +4775,7 @@ build_compound_expr :: proc(
}
handler: [dynamic]hir.Stmt_Id
handler.allocator = checker.allocator
fallback, _ = build_value_source(ctx, &handler, expr.body, success, expr.span)
fallback, _ = build_value_source(ctx, &handler, expr.body, success, expr.span, allow_exit=true)
body = handler[:]
resize(ctx.locals, capture_start)
}
@@ -4692,7 +4934,10 @@ build_compound_expr :: proc(
})
case .Struct_Literal:
struct_type := types.INVALID
if symbol.is_valid(expr.name) {
if expr.left != ast.INVALID_EXPR {
struct_type, _ = resolve_type_argument(checker, expr.left, pkg, file)
struct_type = types.resolve_alias(struct_type, store)
} else if symbol.is_valid(expr.name) {
target_pkg, available := expr_package(checker, expr, pkg, file, true)
struct_type = types.find_named(store, u32(target_pkg), u32(expr.name), file=u32(expr_lookup_file(expr, file))) if available else types.INVALID
struct_type = types.resolve_alias(struct_type, store)
@@ -4880,7 +5125,7 @@ build_expr :: proc(
template := ast.Function_Id(u32(expr.integer))
last = build_function_value(checker, template, expr.span, frame.expected)
_ = pop(&stack)
case .Type:
case .Type, .Anonymous_Struct_Type:
id := source.add(checker.diagnostics, expr.span, "type is not a runtime value")
last = invalid_hir_expr(checker, expr.span, id)
_ = pop(&stack)
@@ -5197,6 +5442,12 @@ build_expr :: proc(
continue
}
function := checker.ast_module.functions[template]
if is_type_metatype_syntax(checker, function.result) {
id := source.addf(checker.diagnostics, expr.span, "type factory '%s' is only valid in type position", symbol_text(checker, expr.name))
last = invalid_hir_expr(checker, expr.span, id)
_ = pop(&stack)
continue
}
if !valid_call_arity(function, len(expr.args)) {
message := "function '%s' expects at least %d arguments, got %d" if function.variadic else
"function '%s' expects %d arguments, got %d"
@@ -6683,18 +6934,17 @@ build_block :: proc(
}
// build_value_block builds a `{ ... yield v }` value block whose final statement
// must be a `yield`: it builds the leading statements inline (their own scope and
// defers), evaluates the yield expression in that scope, then capturing the value
// first, like a function return runs the block's defers and closes the scope. The
// resulting `value`/`value_type` are spliced into the enclosing declaration or
// assignment. `expected` is the binding's type (INVALID for an untyped `::`, where
// the yield's natural type is taken). Statements are appended to `body`.
// must be a `yield`. Catch handlers may instead exit on every path, in which case
// `allow_exit` leaves the fallback expression invalid. Otherwise it builds the leading
// statements inline, evaluates the yield in their scope, then captures the value before
// running defers. `expected` is the binding's type (INVALID for an untyped `::`).
build_value_block :: proc(
ctx: ^Build_Ctx,
body: ^[dynamic]hir.Stmt_Id,
body_stmts: []ast.Stmt_Id,
expected: types.Type,
span: source.Span,
allow_exit := false,
) -> (value: hir.Expr_Id, value_type: types.Type) {
checker := ctx.checker
n := len(body_stmts)
@@ -6705,6 +6955,10 @@ build_value_block :: proc(
for s in inner {
append(body, s)
}
if allow_exit && all_paths_exit(&checker.module, inner) {
delete(inner, checker.allocator)
return hir.INVALID_EXPR, expected
}
delete(inner, checker.allocator)
id := source.add(checker.diagnostics, span, "a value block must end with an explicit 'yield'")
ctx.problematic^ = true
@@ -6782,6 +7036,7 @@ build_value_source :: proc(
span: source.Span,
label := symbol.INVALID,
value_control_flow := false,
allow_exit := false,
) -> (value: hir.Expr_Id, value_type: types.Type) {
checker := ctx.checker
if symbol.is_valid(label) {
@@ -6797,7 +7052,7 @@ build_value_source :: proc(
return build_value_match(ctx, body, body_stmts[0], expected, span)
}
}
return build_value_block(ctx, body, body_stmts, expected, span)
return build_value_block(ctx, body, body_stmts, expected, span, allow_exit)
}
// new_value_slot allocates a fresh, un-nameable mutable local to hold a value-if/loop
@@ -8720,6 +8975,8 @@ check :: proc(
checker.build_stack.allocator = allocator
checker.cycle_stack.allocator = allocator
checker.anon_globals.allocator = allocator
checker.type_factories.allocator = allocator
checker.generated_types.allocator = allocator
build_symbol_indexes(&checker)
checker.global_types = make([]types.Type, len(ast_module.globals), allocator)
checker.global_demands = make([]types.Type, len(ast_module.globals), allocator)
@@ -8763,6 +9020,14 @@ check :: proc(
delete(checker.infer_stack)
delete(checker.build_stack)
delete(checker.cycle_stack)
for entry in checker.type_factories {
delete(entry.values, allocator)
}
for entry in checker.generated_types {
delete(entry.values, allocator)
}
delete(checker.type_factories)
delete(checker.generated_types)
}
for function, index in ast_module.functions {
+19 -3
View File
@@ -215,6 +215,7 @@ Ct_Value_Kind :: enum u8 {
Pointer,
Slice,
Function,
Type,
None,
Optional_Some,
Fallible,
@@ -333,6 +334,9 @@ ct_state_make :: proc(
if value.kind == .Integer {
id := ct_add_value(&state, Ct_Value{kind=.Integer, type=value.type, integer=value.value})
ct_bind_value(&state, value.name, value.type, id, false)
} else if value.kind == .Type {
id := ct_add_value(&state, Ct_Value{kind=.Type, type=types.INVALID, index=u64(value.type)})
ct_bind_value(&state, value.name, types.INVALID, id, false)
}
}
return state
@@ -532,6 +536,9 @@ ct_coerce_value :: proc(state: ^Ct_State, id: Ct_Value_Id, expected: types.Type,
return id, true
}
value := state.values[id]
if value.kind == .Type && is_type_metatype_syntax(state.checker, expected) {
return id, true
}
if types.equal(value.type, expected) {
return id, true
}
@@ -914,7 +921,7 @@ ct_eval_expr :: proc(
id := ct_add_value(state, Ct_Value{kind=.Integer, type=value.type, integer=value.value})
return id, ct_flow(.Normal), true
}
return INVALID_CT_VALUE, ct_flow(.Normal), ct_failf(state, .Not_Comptime, expr.span, "type parameter '%s' is not a runtime value", symbol_text(checker, expr.name))
return ct_add_value(state, Ct_Value{kind=.Type, type=types.INVALID, index=u64(value.type)}), ct_flow(.Normal), true
}
} else if find_import(checker, state.file, expr.qualifier) == ast.INVALID_IMPORT {
if index, ok := ct_find_binding_index(state, expr.qualifier); ok {
@@ -971,6 +978,12 @@ ct_eval_expr :: proc(
return ct_eval_array_expr(state, expr, expected, depth+1)
case .Struct_Literal:
return ct_eval_struct_expr(state, expr, expected, depth+1)
case .Type:
resolved := type_from_syntax(checker, expr.type, state.pkg, state.file)
return ct_add_value(state, Ct_Value{kind=.Type, type=types.INVALID, index=u64(resolved)}), ct_flow(.Normal), types.is_valid(resolved)
case .Anonymous_Struct_Type:
resolved := resolve_generated_struct_type(checker, expr_id, state.pkg, state.file)
return ct_add_value(state, Ct_Value{kind=.Type, type=types.INVALID, index=u64(resolved)}), ct_flow(.Normal), types.is_valid(resolved)
case .Enum_Literal:
return ct_eval_enum_literal(state, expr, expected, depth+1)
case .None:
@@ -1150,7 +1163,7 @@ ct_eval_expr :: proc(
return value, ct_flow(.Normal), true
case .Slice:
return ct_eval_slice_expr(state, expr, depth+1)
case .Type, .Undefined, .Keyed:
case .Undefined, .Keyed:
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, expr.span, "expression cannot be evaluated at comptime")
}
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, expr.span, "expression cannot be evaluated at comptime")
@@ -1216,7 +1229,10 @@ ct_eval_struct_expr :: proc(state: ^Ct_State, expr: ast.Expr, expected: types.Ty
checker := state.checker
store := &checker.module.types
struct_type := types.INVALID
if symbol.is_valid(expr.name) {
if expr.left != ast.INVALID_EXPR {
struct_type, _ = resolve_type_argument(checker, expr.left, state.pkg, state.file)
struct_type = types.resolve_alias(struct_type, store)
} else if symbol.is_valid(expr.name) {
target_pkg, available := expr_package(checker, expr, state.pkg, state.file, false)
struct_type = types.find_named(store, u32(target_pkg), u32(expr.name), file=u32(expr_lookup_file(expr, state.file))) if available else types.INVALID
struct_type = types.resolve_alias(struct_type, store)
+2 -1
View File
@@ -132,7 +132,8 @@ Expr :: struct {
integer: i64,
args: []Expr_Id,
// `Catch` block handlers use `body` for the handler statements and `target`
// for the optional captured error local.
// for the optional captured error local. A missing `right` means the handler
// exits on every path and therefore has no fallback value.
body: []Stmt_Id,
target: Ref,
left: Expr_Id,
+3
View File
@@ -1402,6 +1402,9 @@ canonicalize_types :: proc(module: ^ast.Module, allocator: mem.Allocator) {
for &statement in module.statements {
statement.type = canonical_type(module, statement.type, mapping, visiting)
}
for &field in module.type_fields {
field.type = canonical_type(module, field.type, mapping, visiting)
}
for index := 0; index < original_count; index += 1 {
_ = canonical_type(module, types.DYNAMIC_START+types.Type(index), mapping, visiting)
}
+12 -2
View File
@@ -396,11 +396,14 @@ lower_compound_expr :: proc(state: ^State, expr_id: hir.Expr_Id) -> ir.Instructi
success_lbl := fresh_label(state)
error_lbl := fresh_label(state)
merge_lbl := fresh_label(state)
slot := append_instruction(state, ir.Instruction{
slot := ir.INVALID_INSTRUCTION
if !types.is_void(success) {
slot = append_instruction(state, ir.Instruction{
op=.Alloca, span=expr.span, type=success,
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
}
append_instruction(state, ir.Instruction{
op=.Cond_Br, span=expr.span, type=types.VOID,
integer=success_lbl, target=ir.Ref(u32(error_lbl)), a=ok,
@@ -466,17 +469,21 @@ lower_compound_expr :: proc(state: ^State, expr_id: hir.Expr_Id) -> ir.Instructi
}
lower_statements(state, expr.body)
}
if expr.right != hir.INVALID_EXPR {
fallback := lower_nested_expr(state, expr.right)
if !types.is_void(success) {
append_instruction(state, ir.Instruction{
op=.Store, span=expr.span, type=success,
target=ir.INVALID_REF, a=slot, b=fallback, diagnostic=source.INVALID_DIAGNOSTIC,
})
}
append_instruction(state, ir.Instruction{
op=.Br, span=expr.span, type=types.VOID, integer=merge_lbl,
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
}
}
append_instruction(state, ir.Instruction{
op=.Label, span=expr.span, type=types.VOID, integer=success_lbl,
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
@@ -503,11 +510,14 @@ lower_compound_expr :: proc(state: ^State, expr_id: hir.Expr_Id) -> ir.Instructi
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
append_instruction(state, ir.Instruction{
merge := append_instruction(state, ir.Instruction{
op=.Label, span=expr.span, type=types.VOID, integer=merge_lbl,
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
if types.is_void(success) {
return merge
}
return append_instruction(state, ir.Instruction{
op=.Load, span=expr.span, type=success,
target=ir.INVALID_REF, a=slot, b=ir.INVALID_INSTRUCTION,
+48 -2
View File
@@ -378,7 +378,7 @@ parse_type_atom :: proc(parser: ^Parser) -> ast.Type_Syntax {
}
name = advance(parser)
}
return types.named(
named := types.named(
&parser.module.type_store,
u32(parser.pkg),
u32(name.symbol),
@@ -386,6 +386,14 @@ parse_type_atom :: proc(parser: ^Parser) -> ast.Type_Syntax {
u32(parser.file),
!symbol.is_valid(qualifier) && file_hidden_name(parser, name),
)
if current(parser).kind == .Left_Paren {
call := parse_call(parser, qualifier, first, name, 0)
return types.intern(&parser.module.type_store, types.Node{
kind=.Type_Call,
count_expr=u32(call),
})
}
return named
}
source.add(parser.diagnostics, tok.span, "expected a type")
return types.INVALID
@@ -589,6 +597,29 @@ parse_struct_literal :: proc(
})
}
parse_anonymous_struct_type_expr :: proc(parser: ^Parser) -> ast.Expr_Id {
start := advance(parser)
fields: [dynamic]types.Field
fields.allocator = parser.module.allocator
if !parse_record_body(parser, &fields, "expected '{' after anonymous struct type") {
delete(fields)
return invalid_expr(parser, start.span, "invalid anonymous struct type")
}
end := previous(parser)
field_start := u32(len(parser.module.type_fields))
field_count := u32(len(fields))
append(&parser.module.type_fields, ..fields[:])
delete(fields)
return add_expr(parser, ast.Expr{
kind=.Anonymous_Struct_Type,
span=span_from(start.span, end.span),
integer=u64(field_start)<<32 | u64(field_count),
left=ast.INVALID_EXPR,
right=ast.INVALID_EXPR,
diagnostic=source.INVALID_DIAGNOSTIC,
})
}
parse_integer_magnitude :: proc(text: string) -> (u64, bool) {
value: u64
for byte in transmute([]byte)text {
@@ -755,6 +786,8 @@ parse_primary :: proc(parser: ^Parser, nesting: int) -> ast.Expr_Id {
})
case .Keyword_Func:
return parse_function_literal(parser)
case .Keyword_Struct:
return parse_anonymous_struct_type_expr(parser)
case .Left_Bracket:
if starts_declared_type(parser) {
start := tok
@@ -842,7 +875,20 @@ parse_primary :: proc(parser: ^Parser, nesting: int) -> ast.Expr_Id {
name = advance(parser)
}
if current(parser).kind == .Left_Paren {
return parse_call(parser, qualifier, first, name, nesting)
call := parse_call(parser, qualifier, first, name, nesting)
if current(parser).kind == .Left_Brace && !(parser.no_struct_literal && parser.delimiter_depth == 0) {
left_brace := advance(parser)
args, right_brace := parse_keyed_initializers(parser, left_brace, nesting, "expected '}' after struct literal")
return add_expr(parser, ast.Expr{
kind=.Struct_Literal,
span=span_from(parser.module.exprs[call].span, right_brace.span),
args=args,
left=call,
right=ast.INVALID_EXPR,
diagnostic=source.INVALID_DIAGNOSTIC,
})
}
return call
}
if current(parser).kind == .Left_Brace && !(parser.no_struct_literal && parser.delimiter_depth == 0) {
return parse_struct_literal(parser, qualifier, first, name, nesting)
+16
View File
@@ -75,6 +75,7 @@ Kind :: enum u8 {
Struct,
Union,
Fallible,
Type_Call,
}
Node :: struct {
@@ -359,6 +360,21 @@ struct_anonymous :: proc(store: ^Store, fields: []Field) -> Type {
})
}
// Generated structs are nominal per comptime type-expression specialization.
// The checker owns canonicalization; this routine deliberately creates a fresh node.
struct_generated :: proc(store: ^Store, fields: []Field) -> Type {
start := u32(len(store.fields))
append(&store.fields, ..fields)
id := DYNAMIC_START+Type(len(store.nodes))
append(&store.nodes, Node{
kind=.Struct,
field_start=start,
field_count=u32(len(fields)),
declared=true,
})
return id
}
variant_id :: proc(store: ^Store, name: u32, payload: Type) -> (u16, bool) {
for variant in store.variants {
if variant.name == name && variant.payload == payload {
+144 -35
View File
@@ -3961,6 +3961,77 @@ main func() i32 {
testing.expect_value(t, compiler_core.compile_package(directory, output), 1)
}
@(test)
terminating_catch_block_compiles_and_runs :: proc(t: ^testing.T) {
text := `Failure :: enum {
bad
}
may_fail func(fail bool) i32 ! Failure {
if (fail) return .bad
return 1
}
recover func(fail bool) i32 {
value :: may_fail(fail) catch |_| {
return 40
}
return value + 1
}
main func() i32 {
return recover(false) + recover(true) - 42
}
`
directory := "/tmp/brolang-test-terminating-catch"
main_path := "/tmp/brolang-test-terminating-catch/main.bro"
output := "/tmp/brolang-test-terminating-catch-output"
_ = os2.remove_all(directory)
defer _ = os2.remove_all(directory)
defer _ = os.remove(output)
testing.expect(t, os.make_directory(directory) == nil)
testing.expect(t, os.write_entire_file(main_path, transmute([]byte)text))
testing.expect_value(t, compiler_core.compile_package(directory, output), 0)
state := run_executable(output)
testing.expect_value(t, state.exit_code, 0)
}
@(test)
conversion_diagnostics_render_source_types :: proc(t: ^testing.T) {
text := `Allocator :: struct {
marker i32
}
take func(allocator Allocator, memory []mut u8) void {}
main func() void {
allocator Allocator = Allocator { marker = 0 }
data [1]mut u8 = [0]
take(data[..], allocator)
}
`
source_file := source.Source{path="type_labels.bro", text=text}
diagnostics := source.init_diagnostics(&source_file)
defer source.destroy_diagnostics(&diagnostics)
symbols := symbol.init_table()
defer symbol.destroy_table(&symbols)
stream := lexer.lex(&source_file, &diagnostics, &symbols)
defer delete(stream.items)
ast_module := parser.parse(&stream, &source_file, &diagnostics)
defer ast.destroy_module(&ast_module)
hir_module := checker.check(&ast_module, &diagnostics, &symbols)
defer hir.destroy_module(&hir_module)
slice_to_allocator := false
allocator_to_slice := false
internal_type_id := false
for diagnostic in diagnostics.items {
slice_to_allocator = slice_to_allocator ||
strings.contains(diagnostic.message, "cannot implicitly convert []mut u8 to Allocator")
allocator_to_slice = allocator_to_slice ||
strings.contains(diagnostic.message, "cannot implicitly convert Allocator to []mut u8")
internal_type_id = internal_type_id || strings.contains(diagnostic.message, "<type ")
}
testing.expect(t, slice_to_allocator)
testing.expect(t, allocator_to_slice)
testing.expect(t, !internal_type_id)
}
@(test)
contextual_payload_variants_compile :: proc(t: ^testing.T) {
text := `DetailError :: union(enum) {
@@ -4941,26 +5012,6 @@ folded_constant_addition_compiles_and_runs :: proc(t: ^testing.T) {
testing.expect_value(t, state.exit_code, 0)
}
@(test)
unused_invalid_global_does_not_trap :: proc(t: ^testing.T) {
output := "/tmp/brolang-test-invalid-unused"
defer _ = os.remove(output)
status := compiler_core.compile_package("examples/programs/invalid_unused_global", output)
testing.expect_value(t, status, 1)
state := run_executable(output)
testing.expect_value(t, state.exit_code, 0)
}
@(test)
used_invalid_global_traps :: proc(t: ^testing.T) {
output := "/tmp/brolang-test-invalid-used"
defer _ = os.remove(output)
status := compiler_core.compile_package("examples/programs/invalid_used_global", output)
testing.expect_value(t, status, 1)
state := run_executable(output)
testing.expect(t, !state.success)
}
@(test)
main_int_is_constrained_to_i32 :: proc(t: ^testing.T) {
output := "/tmp/brolang-test-main-int"
@@ -4981,16 +5032,6 @@ main_i32_returns_directly :: proc(t: ^testing.T) {
testing.expect_value(t, state.exit_code, 4)
}
@(test)
transitive_problematic_global_is_deferred :: proc(t: ^testing.T) {
output := "/tmp/brolang-test-transitive-unused"
defer _ = os.remove(output)
status := compiler_core.compile_package("examples/programs/invalid_transitive_unused_global", output)
testing.expect_value(t, status, 1)
state := run_executable(output)
testing.expect_value(t, state.exit_code, 0)
}
@(test)
checked_addition_traps_on_overflow :: proc(t: ^testing.T) {
output := "/tmp/brolang-test-overflow"
@@ -6159,13 +6200,16 @@ main func() void {}
hir_module := checker.check(&ast_module, &diagnostics, &symbols)
defer hir.destroy_module(&hir_module)
found_signed_range := 0
found_expression_range := 0
found_literal_range := 0
found_u64_range := false
for diagnostic in diagnostics.items {
found_signed_range += 1 if strings.contains(diagnostic.message, "exceeds signed i64 range") else 0
found_expression_range += 1 if strings.contains(diagnostic.message, "exceeds signed i64 range") else 0
found_literal_range += 1 if strings.contains(diagnostic.message, "does not fit in i64") else 0
found_u64_range = found_u64_range || strings.contains(diagnostic.message, "magnitude does not fit in u64")
}
testing.expect_value(t, found_signed_range, 4)
testing.expect_value(t, found_expression_range, 2)
testing.expect_value(t, found_literal_range, 2)
testing.expect(t, found_u64_range)
}
@@ -6479,7 +6523,7 @@ main func() void {
for global in hir_module.globals {
name := symbol.resolve(&symbols, global.name)
if name == "counter" {
found_counter = global.writable && !global.is_static && types.equal(global.type, types.I8)
found_counter = global.writable && !global.is_static && types.equal(global.type, types.I32)
} else if name == "ratio" {
found_ratio = global.writable && !global.is_static && types.equal(global.type, types.F64)
} else if name == "span" {
@@ -6494,7 +6538,7 @@ main func() void {
testing.expect(t, found_span)
testing.expect(t, found_values)
testing.expect(t, strings.contains(llvm_text, "internal global"))
testing.expect(t, !strings.contains(llvm_text, "internal constant i8 0"))
testing.expect(t, !strings.contains(llvm_text, "internal constant i32 0"))
}
@(test)
@@ -6946,6 +6990,71 @@ comptime_type_params_compile_and_run :: proc(t: ^testing.T) {
testing.expect_value(t, state.exit_code, 0)
}
@(test)
type_factories_compile_and_run :: proc(t: ^testing.T) {
output := "/tmp/brolang-test-type-factory"
defer _ = os.remove(output)
status := compiler_core.compile_package("examples/programs/type_factory", output)
testing.expect_value(t, status, 0)
state := run_executable(output)
testing.expect_value(t, state.exit_code, 0)
}
@(test)
arraylist_compiles_and_runs :: proc(t: ^testing.T) {
output := "/tmp/brolang-test-arraylist"
defer _ = os.remove(output)
status := compiler_core.compile_package("examples/programs/arraylist", output, nil, target.DEFAULT, cimport.Options{}, ".")
testing.expect_value(t, status, 0)
state := run_executable(output)
testing.expect_value(t, state.exit_code, 0)
}
@(test)
type_factory_rejects_runtime_parameters_and_recursion :: proc(t: ^testing.T) {
texts := []string{
`Bad func($T type, n usize) type {
return struct { value [n]T }
}
main func() void { value Bad(i32, 4) = undefined; _ = &value }
`,
`Loop func($T type) type {
return struct { next @Loop(T) }
}
main func() void { value Loop(i32) = undefined; _ = &value }
`,
`Box func($T type) type {
return struct { value T }
}
main func() void { _ = Box(i32) }
`,
`Bad func($T type) type {
return 1
}
main func() void { value Bad(i32) = undefined; _ = &value }
`,
}
wanted := []string{"must be comptime", "recursive type-factory specialization", "only valid in type position", "cannot implicitly convert"}
for text, index in texts {
source_file := source.Source{path="test.bro", text=text}
diagnostics := source.init_diagnostics(&source_file)
symbols := symbol.init_table()
stream := lexer.lex(&source_file, &diagnostics, &symbols)
ast_module := parser.parse(&stream, &source_file, &diagnostics)
hir_module := checker.check(&ast_module, &diagnostics, &symbols)
found := false
for diagnostic in diagnostics.items {
found = found || strings.contains(diagnostic.message, wanted[index])
}
testing.expect(t, found)
hir.destroy_module(&hir_module)
ast.destroy_module(&ast_module)
delete(stream.items)
symbol.destroy_table(&symbols)
source.destroy_diagnostics(&diagnostics)
}
}
@(test)
comptime_eval_compile_and_run :: proc(t: ^testing.T) {
output := "/tmp/brolang-test-comptime-eval"
+77
View File
@@ -0,0 +1,77 @@
arraylist :: import "@std/arraylist"
mem :: import "@std/mem"
_fail_alloc func(_ ?*mut anyopaque, _ usize, _ usize) ?*mut u8 {
return none
}
_fail_realloc func(_ ?*mut anyopaque, _ ?*mut u8, _ usize, _ usize, _ usize) ?*mut u8 {
return none
}
_fail_free func(_ ?*mut anyopaque, _ ?*mut u8, _ usize, _ usize) void {}
_fail_vtable mem.AllocatorVTable :: mem.AllocatorVTable {
alloc = _fail_alloc,
realloc = _fail_realloc,
free = _fail_free,
}
_fail_allocator mem.Allocator :: mem.Allocator {
context = none,
vtable = &_fail_vtable,
}
_noop func() void {}
run func() i32 ! mem.AllocError {
values arraylist.ArrayList(i32) = arraylist.init(i32, mem.c_allocator)
defer arraylist.deinit(i32, &values)
if (values.items.len != 0 or values.capacity != 0) return 1
i usize = 0
while i < 20 : i += 1 {
arraylist.append(i32, &values, i32(i)) catch |_| {
return .out_of_memory
}
}
if (values.items.len != 20 or values.capacity < 20) return 2
if (values.items[0] != 0 or values.items[19] != 19) return 3
values.items[3] = 33
if (values.items[3] != 33) return 4
arraylist.reserve(i32, &values, 50) catch |_| {
return .out_of_memory
}
if (values.capacity < 50 or values.items.len != 20 or values.items[19] != 19) return 5
capacity usize :: values.capacity
arraylist.clear(i32, &values)
if (values.items.len != 0 or values.capacity != capacity) return 6
arraylist.append(i32, &values, 7) catch |_| {
return .out_of_memory
}
if (values.items.len != 1 or values.items[0] != 7 or values.capacity != capacity) return 7
empty_values arraylist.ArrayList([0]u8) = arraylist.init([0]u8, mem.c_allocator)
defer arraylist.deinit([0]u8, &empty_values)
zero [0]u8 :: []
arraylist.append([0]u8, &empty_values, zero) catch |_| {
return .out_of_memory
}
if (empty_values.items.len != 1) return 8
failed arraylist.ArrayList(i32) = arraylist.init(i32, _fail_allocator)
failed_as_expected bool = false
arraylist.append(i32, &failed, 1) catch |_| {
failed_as_expected = true
yield _noop()
}
if (failed_as_expected == false or failed.items.len != 0 or failed.capacity != 0) return 9
arraylist.deinit(i32, &failed)
return 0
}
main func() i32 {
return run() catch 100
}
@@ -1,11 +0,0 @@
bad int = 4
read_bad func() int {
return bad
}
derived int :: read_bad()
main func() void {
_ = 1
}
@@ -1,5 +0,0 @@
bad int = 4
main func() void {
_ = 1
}
@@ -1,5 +0,0 @@
bad int = 4
main func() void {
_ = bad
}
@@ -92,6 +92,14 @@ typed_allocator_test func() i32 {
typed[0] = 10
typed[3] = 20
if (typed[0] + typed[3] != 30) return 39
typed = mem.realloc(i32, mem.c_allocator, typed, 8) catch |_| {
return 41
}
if (typed.len != 8 or typed[0] != 10 or typed[3] != 20) return 42
typed = mem.realloc(i32, mem.c_allocator, typed, 2) catch |_| {
return 43
}
if (typed.len != 2 or typed[0] != 10) return 44
return 0
}
+44
View File
@@ -0,0 +1,44 @@
Box func($T type) type {
return struct {
value T
}
}
Buffer func($T type, $N usize) type {
if N == 0 {
return struct {
values [0]T
}
}
return struct {
values [N]T
}
}
BoxAlias func($T type) type {
return Box(T)
}
LocalAlias func($T type) type {
chosen :: T
return chosen
}
make_box func($T type, value T) Box(T) {
return Box(T) { value = value }
}
main func() i32 {
box Box(i32) :: make_box(i32, 42)
if (box.value != 42) return 1
aliased BoxAlias(i32) :: box
if (aliased.value != 42) return 3
local_alias LocalAlias(i32) :: 42
if (local_alias != 42) return 6
pointer @Box(i32) :: &box
if (pointer.value != 42) return 4
buffer Buffer(u8, 4) :: Buffer(u8, 4) { values = [1, 2, 3, 4] }
if (buffer.values.len != 4) return 2
if (size_of(Buffer(u8, 4)) != 4) return 5
return 0
}
+67
View File
@@ -0,0 +1,67 @@
mem :: import "@std/mem"
ArrayList func($T type) type {
return struct {
items []mut T
capacity usize
allocator mem.Allocator
}
}
init func($T type, allocator mem.Allocator) ArrayList(T) {
return ArrayList(T) {
items = mem.empty(T),
capacity = 0,
allocator = allocator,
}
}
deinit func($T type, list @mut ArrayList(T)) void {
allocation []mut T :: list.items.ptr[..list.capacity]
mem.free(T, list.allocator, allocation)
list.items = mem.empty(T)
list.capacity = 0
}
reserve func($T type, list @mut ArrayList(T), minimum_capacity usize) void ! mem.AllocError {
if minimum_capacity <= list.capacity {
return _
}
new_capacity usize = 8
if list.capacity >= 8 {
half usize :: list.capacity / 2
if list.capacity > max_value(usize) - half {
new_capacity = minimum_capacity
} else {
new_capacity = list.capacity + half
}
}
if new_capacity < minimum_capacity {
new_capacity = minimum_capacity
}
length usize :: list.items.len
allocation []mut T :: list.items.ptr[..list.capacity]
grown []mut T :: mem.realloc(T, list.allocator, allocation, new_capacity) catch |_| {
return .out_of_memory
}
list.items = grown.ptr[..length]
list.capacity = new_capacity
return _
}
append func($T type, list @mut ArrayList(T), value T) void ! mem.AllocError {
length usize :: list.items.len
if length == max_value(usize) {
return .out_of_memory
}
try reserve(T, list, length + 1)
list.items = list.items.ptr[..length + 1]
list.items[length] = value
return _
}
clear func($T type, list @mut ArrayList(T)) void {
list.items = list.items.ptr[..0]
}
+47 -6
View File
@@ -1,9 +1,7 @@
c :: import "@ffi/c"
AllocatorVTable :: struct {
alloc @func(context ?*mut anyopaque, size usize, alignment usize) ?*mut u8
realloc @func(context ?*mut anyopaque, memory ?*mut u8, old_size usize, new_size usize, alignment usize) ?*mut u8
free @func(context ?*mut anyopaque, memory ?*mut u8, size usize, alignment usize) void
AllocError :: enum {
out_of_memory
}
Allocator :: struct {
@@ -11,8 +9,10 @@ Allocator :: struct {
vtable @AllocatorVTable
}
AllocError :: enum {
out_of_memory
AllocatorVTable :: struct {
alloc @func(context ?*mut anyopaque, size usize, alignment usize) ?*mut u8
realloc @func(context ?*mut anyopaque, memory ?*mut u8, old_size usize, new_size usize, alignment usize) ?*mut u8
free @func(context ?*mut anyopaque, memory ?*mut u8, size usize, alignment usize) void
}
raw_alloc func(allocator Allocator, size usize, alignment usize) ?*mut u8 {
@@ -34,6 +34,10 @@ _empty_slice func($T type, count usize) []mut T {
return pointer[..count]
}
empty func($T type) []mut T {
return _empty_slice(T, 0)
}
alloc func($T type, allocator Allocator, count usize) []mut T ! AllocError {
if count == 0 {
return _empty_slice(T, 0)
@@ -55,6 +59,43 @@ alloc func($T type, allocator Allocator, count usize) []mut T ! AllocError {
return .out_of_memory
}
realloc func($T type, allocator Allocator, memory []mut T, new_count usize) []mut T ! AllocError {
if new_count == memory.len {
return memory
}
if new_count == 0 {
free(T, allocator, memory)
return _empty_slice(T, 0)
}
element_size usize :: size_of(T)
if element_size == 0 {
return _empty_slice(T, new_count)
}
if new_count > max_value(usize) / element_size {
return .out_of_memory
}
old_memory ?*mut u8 = none
old_size usize = 0
if memory.len != 0 {
old_memory = ptr_cast(u8, memory.ptr)
old_size = memory.len * element_size
}
resized ?*mut u8 = raw_realloc(
allocator,
old_memory,
old_size,
new_count * element_size,
align_of(T),
)
if resized |bytes| {
pointer *mut T :: ptr_cast(T, bytes)
return pointer[..new_count]
}
return .out_of_memory
}
free func($T type, allocator Allocator, memory []mut T) void {
if memory.len != 0 and size_of(T) != 0 {
raw_free(allocator, ptr_cast(u8, memory.ptr), memory.len * size_of(T), align_of(T))
+420
View File
@@ -0,0 +1,420 @@
# generated by brolang translate-c from stdio.h
# unsupported in bindings: C union '__mbstate_t' has no native spelling
__darwin_pthread_handler_rec :: c_struct {
__routine ?*c_func(_ ?*mut anyopaque) void
__arg ?*mut anyopaque
__next ?*mut __darwin_pthread_handler_rec
}
_opaque_pthread_attr_t :: c_struct {
__sig c_long
__opaque [56]c_char
}
_opaque_pthread_cond_t :: c_struct {
__sig c_long
__opaque [40]c_char
}
_opaque_pthread_condattr_t :: c_struct {
__sig c_long
__opaque [8]c_char
}
_opaque_pthread_mutex_t :: c_struct {
__sig c_long
__opaque [56]c_char
}
_opaque_pthread_mutexattr_t :: c_struct {
__sig c_long
__opaque [8]c_char
}
_opaque_pthread_once_t :: c_struct {
__sig c_long
__opaque [8]c_char
}
_opaque_pthread_rwlock_t :: c_struct {
__sig c_long
__opaque [192]c_char
}
_opaque_pthread_rwlockattr_t :: c_struct {
__sig c_long
__opaque [16]c_char
}
_opaque_pthread_t :: c_struct {
__sig c_long
__cleanup_stack ?*mut __darwin_pthread_handler_rec
__opaque [8176]c_char
}
__sbuf :: c_struct {
_base ?*mut c_uchar
_size c_int
}
__sFILEX :: opaque
__sFILE :: c_struct {
_p ?*mut c_uchar
_r c_int
_w c_int
_flags c_short
_file c_short
_bf __sbuf
_lbfsize c_int
_cookie ?*mut anyopaque
_close ?*c_func(_ ?*mut anyopaque) c_int
_read ?*c_func(_ ?*mut anyopaque, _ ?*mut c_char, _ c_int) c_int
_seek ?*c_func(_ ?*mut anyopaque, _ c_longlong, _ c_int) c_longlong
_write ?*c_func(_ ?*mut anyopaque, _ ?*c_char, _ c_int) c_int
_ub __sbuf
_extra ?*mut __sFILEX
_ur c_int
_ubuf [3]c_uchar
_nbuf [1]c_uchar
_lb __sbuf
_blksize c_int
_offset c_longlong
}
__int8_t :: alias c_schar
__uint8_t :: alias c_uchar
__int16_t :: alias c_short
__uint16_t :: alias c_ushort
__int32_t :: alias c_int
__uint32_t :: alias c_uint
__int64_t :: alias c_longlong
__uint64_t :: alias c_ulonglong
__darwin_intptr_t :: alias c_long
__darwin_natural_t :: alias c_uint
__darwin_ct_rune_t :: alias c_int
__darwin_mbstate_t :: alias __mbstate_t
__darwin_ptrdiff_t :: alias c_long
__darwin_size_t :: alias c_ulong
__darwin_va_list :: alias ?*mut c_char
__darwin_wchar_t :: alias c_int
__darwin_rune_t :: alias c_int
__darwin_wint_t :: alias c_int
__darwin_clock_t :: alias c_ulong
__darwin_socklen_t :: alias c_uint
__darwin_ssize_t :: alias c_long
__darwin_time_t :: alias c_long
__darwin_blkcnt_t :: alias c_longlong
__darwin_blksize_t :: alias c_int
__darwin_dev_t :: alias c_int
__darwin_fsblkcnt_t :: alias c_uint
__darwin_fsfilcnt_t :: alias c_uint
__darwin_gid_t :: alias c_uint
__darwin_id_t :: alias c_uint
__darwin_ino64_t :: alias c_ulonglong
__darwin_ino_t :: alias c_ulonglong
__darwin_mach_port_name_t :: alias c_uint
__darwin_mach_port_t :: alias c_uint
__darwin_mode_t :: alias c_ushort
__darwin_off_t :: alias c_longlong
__darwin_pid_t :: alias c_int
__darwin_sigset_t :: alias c_uint
__darwin_suseconds_t :: alias c_int
__darwin_uid_t :: alias c_uint
__darwin_useconds_t :: alias c_uint
__darwin_uuid_t :: alias [16]c_uchar
__darwin_uuid_string_t :: alias [37]c_char
__darwin_pthread_attr_t :: alias _opaque_pthread_attr_t
__darwin_pthread_cond_t :: alias _opaque_pthread_cond_t
__darwin_pthread_condattr_t :: alias _opaque_pthread_condattr_t
__darwin_pthread_key_t :: alias c_ulong
__darwin_pthread_mutex_t :: alias _opaque_pthread_mutex_t
__darwin_pthread_mutexattr_t :: alias _opaque_pthread_mutexattr_t
__darwin_pthread_once_t :: alias _opaque_pthread_once_t
__darwin_pthread_rwlock_t :: alias _opaque_pthread_rwlock_t
__darwin_pthread_rwlockattr_t :: alias _opaque_pthread_rwlockattr_t
__darwin_pthread_t :: alias ?*mut _opaque_pthread_t
__darwin_nl_item :: alias c_int
__darwin_wctrans_t :: alias c_int
__darwin_wctype_t :: alias c_uint
int8_t :: alias c_schar
int16_t :: alias c_short
int32_t :: alias c_int
int64_t :: alias c_longlong
u_int8_t :: alias c_uchar
u_int16_t :: alias c_ushort
u_int32_t :: alias c_uint
u_int64_t :: alias c_ulonglong
register_t :: alias c_longlong
intptr_t :: alias c_long
uintptr_t :: alias c_ulong
user_addr_t :: alias c_ulonglong
user_size_t :: alias c_ulonglong
user_ssize_t :: alias c_longlong
user_long_t :: alias c_longlong
user_ulong_t :: alias c_ulonglong
user_time_t :: alias c_longlong
user_off_t :: alias c_longlong
syscall_arg_t :: alias c_ulonglong
va_list :: alias ?*mut c_char
size_t :: alias c_ulong
fpos_t :: alias c_longlong
FILE :: alias __sFILE
off_t :: alias c_longlong
ssize_t :: alias c_long
_DARWIN_FEATURE_64_BIT_INODE c_int :: 1
_DARWIN_FEATURE_ONLY_64_BIT_INODE c_int :: 1
_DARWIN_FEATURE_ONLY_VERS_1050 c_int :: 1
_DARWIN_FEATURE_ONLY_UNIX_CONFORMANCE c_int :: 1
_DARWIN_FEATURE_UNIX_CONFORMANCE c_int :: 3
_FORTIFY_SOURCE c_int :: 2
RENAME_SECLUDE c_int :: 1
RENAME_SWAP c_int :: 2
RENAME_EXCL c_int :: 4
RENAME_RESERVED1 c_int :: 8
RENAME_NOFOLLOW_ANY c_int :: 16
RENAME_RESOLVE_BENEATH c_int :: 32
SEEK_SET c_int :: 0
SEEK_CUR c_int :: 1
SEEK_END c_int :: 2
SEEK_HOLE c_int :: 3
SEEK_DATA c_int :: 4
_IOFBF c_int :: 0
_IOLBF c_int :: 1
_IONBF c_int :: 2
BUFSIZ c_int :: 1024
FOPEN_MAX c_int :: 20
FILENAME_MAX c_int :: 1024
L_tmpnam c_int :: 1024
TMP_MAX c_int :: 308915776
L_ctermid c_int :: 1024
_USE_FORTIFY_LEVEL c_int :: 2
renameat c_func(_ c_int, _ ?*c_char, _ c_int, _ ?*c_char) c_int
renamex_np c_func(_ ?*c_char, _ ?*c_char, _ c_uint) c_int
renameatx_np c_func(_ c_int, _ ?*c_char, _ c_int, _ ?*c_char, _ c_uint) c_int
printf c_func(_ ?*c_char, ...) c_int
clearerr c_func(_ ?*mut __sFILE) void
fclose c_func(_ ?*mut __sFILE) c_int
feof c_func(_ ?*mut __sFILE) c_int
ferror c_func(_ ?*mut __sFILE) c_int
fflush c_func(_ ?*mut __sFILE) c_int
fgetc c_func(_ ?*mut __sFILE) c_int
fgetpos c_func(_ ?*mut __sFILE, _ ?*mut c_longlong) c_int
fgets c_func(_ ?*mut c_char, __size c_int, _ ?*mut __sFILE) ?*mut c_char
fopen c_func(__filename ?*c_char, __mode ?*c_char) ?*mut __sFILE
fprintf c_func(_ ?*mut __sFILE, _ ?*c_char, ...) c_int
fputc c_func(_ c_int, _ ?*mut __sFILE) c_int
fputs c_func(_ ?*c_char, _ ?*mut __sFILE) c_int
fread c_func(__ptr ?*mut anyopaque, __size c_ulong, __nitems c_ulong, __stream ?*mut __sFILE) c_ulong
freopen c_func(_ ?*c_char, _ ?*c_char, _ ?*mut __sFILE) ?*mut __sFILE
fscanf c_func(_ ?*mut __sFILE, _ ?*c_char, ...) c_int
fseek c_func(_ ?*mut __sFILE, _ c_long, _ c_int) c_int
fsetpos c_func(_ ?*mut __sFILE, _ ?*c_longlong) c_int
ftell c_func(_ ?*mut __sFILE) c_long
fwrite c_func(__ptr ?*anyopaque, __size c_ulong, __nitems c_ulong, __stream ?*mut __sFILE) c_ulong
getc c_func(_ ?*mut __sFILE) c_int
getchar c_func() c_int
gets c_func(_ ?*mut c_char) ?*mut c_char
perror c_func(_ ?*c_char) void
putc c_func(_ c_int, _ ?*mut __sFILE) c_int
putchar c_func(_ c_int) c_int
puts c_func(_ ?*c_char) c_int
remove c_func(_ ?*c_char) c_int
rename c_func(__old ?*c_char, __new ?*c_char) c_int
rewind c_func(_ ?*mut __sFILE) void
scanf c_func(_ ?*c_char, ...) c_int
setbuf c_func(_ ?*mut __sFILE, _ ?*mut c_char) void
setvbuf c_func(_ ?*mut __sFILE, _ ?*mut c_char, _ c_int, __size c_ulong) c_int
sprintf c_func(_ ?*mut c_char, _ ?*c_char, ...) c_int
sscanf c_func(_ ?*c_char, _ ?*c_char, ...) c_int
tmpfile c_func() ?*mut __sFILE
tmpnam c_func(_ ?*mut c_char) ?*mut c_char
ungetc c_func(_ c_int, _ ?*mut __sFILE) c_int
vfprintf c_func(_ ?*mut __sFILE, _ ?*c_char, _ ?*mut c_char) c_int
vprintf c_func(_ ?*c_char, _ ?*mut c_char) c_int
vsprintf c_func(_ ?*mut c_char, _ ?*c_char, _ ?*mut c_char) c_int
ctermid c_func(_ ?*mut c_char) ?*mut c_char
fdopen c_func(_ c_int, _ ?*c_char) ?*mut __sFILE
fileno c_func(_ ?*mut __sFILE) c_int
pclose c_func(_ ?*mut __sFILE) c_int
popen c_func(_ ?*c_char, _ ?*c_char) ?*mut __sFILE
__srget c_func(_ ?*mut __sFILE) c_int
__svfscanf c_func(_ ?*mut __sFILE, _ ?*c_char, _ ?*mut c_char) c_int
__swbuf c_func(_ c_int, _ ?*mut __sFILE) c_int
__sputc c_func(_c c_int, _p ?*mut __sFILE) c_int
flockfile c_func(_ ?*mut __sFILE) void
ftrylockfile c_func(_ ?*mut __sFILE) c_int
funlockfile c_func(_ ?*mut __sFILE) void
getc_unlocked c_func(_ ?*mut __sFILE) c_int
getchar_unlocked c_func() c_int
putc_unlocked c_func(_ c_int, _ ?*mut __sFILE) c_int
putchar_unlocked c_func(_ c_int) c_int
getw c_func(_ ?*mut __sFILE) c_int
putw c_func(_ c_int, _ ?*mut __sFILE) c_int
tempnam c_func(__dir ?*c_char, __prefix ?*c_char) ?*mut c_char
fseeko c_func(__stream ?*mut __sFILE, __offset c_longlong, __whence c_int) c_int
ftello c_func(__stream ?*mut __sFILE) c_longlong
snprintf c_func(__str ?*mut c_char, __size c_ulong, __format ?*c_char, ...) c_int
vfscanf c_func(__stream ?*mut __sFILE, __format ?*c_char, _ ?*mut c_char) c_int
vscanf c_func(__format ?*c_char, _ ?*mut c_char) c_int
vsnprintf c_func(__str ?*mut c_char, __size c_ulong, __format ?*c_char, _ ?*mut c_char) c_int
vsscanf c_func(__str ?*c_char, __format ?*c_char, _ ?*mut c_char) c_int
dprintf c_func(_ c_int, _ ?*c_char, ...) c_int
vdprintf c_func(_ c_int, _ ?*c_char, _ ?*mut c_char) c_int
getdelim c_func(__linep ?*mut ?*mut c_char, __linecapp ?*mut c_ulong, __delimiter c_int, __stream ?*mut __sFILE) c_long
getline c_func(__linep ?*mut ?*mut c_char, __linecapp ?*mut c_ulong, __stream ?*mut __sFILE) c_long
fmemopen c_func(__buf ?*mut anyopaque, __size c_ulong, __mode ?*c_char) ?*mut __sFILE
open_memstream c_func(__bufp ?*mut ?*mut c_char, __sizep ?*mut c_ulong) ?*mut __sFILE
asprintf c_func(_ ?*mut ?*mut c_char, _ ?*c_char, ...) c_int
ctermid_r c_func(_ ?*mut c_char) ?*mut c_char
fgetln c_func(_ ?*mut __sFILE, __len ?*mut c_ulong) ?*mut c_char
fmtcheck c_func(_ ?*c_char, _ ?*c_char) ?*c_char
fpurge c_func(_ ?*mut __sFILE) c_int
setbuffer c_func(_ ?*mut __sFILE, _ ?*mut c_char, __size c_int) void
setlinebuf c_func(_ ?*mut __sFILE) c_int
vasprintf c_func(_ ?*mut ?*mut c_char, _ ?*c_char, _ ?*mut c_char) c_int
funopen c_func(_ ?*anyopaque, _ ?*c_func(_ ?*mut anyopaque, _ ?*mut c_char, _ c_int) c_int, _ ?*c_func(_ ?*mut anyopaque, _ ?*c_char, _ c_int) c_int, _ ?*c_func(_ ?*mut anyopaque, _ c_longlong, _ c_int) c_longlong, _ ?*c_func(_ ?*mut anyopaque) c_int) ?*mut __sFILE
__snprintf_chk c_func(_ ?*mut c_char, __maxlen c_ulong, _ c_int, _ c_ulong, _ ?*c_char, ...) c_int
__vsnprintf_chk c_func(_ ?*mut c_char, __maxlen c_ulong, _ c_int, _ c_ulong, _ ?*c_char, _ ?*mut c_char) c_int
__sprintf_chk c_func(_ ?*mut c_char, _ c_int, _ c_ulong, _ ?*c_char, ...) c_int
__vsprintf_chk c_func(_ ?*mut c_char, _ c_int, _ c_ulong, _ ?*c_char, _ ?*mut c_char) c_int
# unsupported in bindings: external variable '__stdinp' has no native spelling
# unsupported in bindings: external variable '__stdoutp' has no native spelling
# unsupported in bindings: external variable '__stderrp' has no native spelling
# unsupported in bindings: external variable 'sys_nerr' has no native spelling
# unsupported in bindings: external variable 'sys_errlist' has no native spelling
# unsupported in bindings: _STDIO_H_ — C macro has no replacement value
# unsupported in bindings: _LIBC_BOUNDS_H_ — C macro has no replacement value
# unsupported in bindings: _CDEFS_H_ — C macro has no replacement value
# unsupported in bindings: _LIBC_COUNT — C function-like macros are not supported
# unsupported in bindings: _LIBC_COUNT_OR_NULL — C function-like macros are not supported
# unsupported in bindings: _LIBC_SIZE — C function-like macros are not supported
# unsupported in bindings: _LIBC_SIZE_OR_NULL — C function-like macros are not supported
# unsupported in bindings: _LIBC_ENDED_BY — C function-like macros are not supported
# unsupported in bindings: _LIBC_SINGLE — C macro has no replacement value
# unsupported in bindings: _LIBC_UNSAFE_INDEXABLE — C macro has no replacement value
# unsupported in bindings: _LIBC_CSTR — C macro has no replacement value
# unsupported in bindings: _LIBC_NULL_TERMINATED — C macro has no replacement value
# unsupported in bindings: _LIBC_FLEX_COUNT — C function-like macros are not supported
# unsupported in bindings: _LIBC_SINGLE_BY_DEFAULT — C function-like macros are not supported
# unsupported in bindings: _LIBC_PTRCHECK_REPLACED — C function-like macros are not supported
# unsupported in bindings: _LIBC_FORGE_PTR — C function-like macros are not supported
# unsupported in bindings: MAC_OS_X_VERSION_10_0 — C macro is not a supported constant
# unsupported in bindings: MAC_OS_X_VERSION_10_1 — C macro is not a supported constant
# unsupported in bindings: MAC_OS_X_VERSION_10_2 — C macro is not a supported constant
# unsupported in bindings: MAC_OS_X_VERSION_10_3 — C macro is not a supported constant
# unsupported in bindings: MAC_OS_X_VERSION_10_4 — C macro is not a supported constant
# unsupported in bindings: MAC_OS_X_VERSION_10_5 — C macro is not a supported constant
# unsupported in bindings: MAC_OS_X_VERSION_10_6 — C macro is not a supported constant
# unsupported in bindings: MAC_OS_X_VERSION_10_7 — C macro is not a supported constant
# unsupported in bindings: MAC_OS_X_VERSION_10_8 — C macro is not a supported constant
# unsupported in bindings: MAC_OS_X_VERSION_10_9 — C macro is not a supported constant
# unsupported in bindings: MAC_OS_X_VERSION_10_10 — C macro is not a supported constant
# unsupported in bindings: MAC_OS_X_VERSION_10_10_2 — C macro is not a supported constant
# unsupported in bindings: MAC_OS_X_VERSION_10_10_3 — C macro is not a supported constant
# unsupported in bindings: MAC_OS_X_VERSION_10_11 — C macro is not a supported constant
# unsupported in bindings: MAC_OS_X_VERSION_10_11_2 — C macro is not a supported constant
# unsupported in bindings: MAC_OS_X_VERSION_10_11_3 — C macro is not a supported constant
# unsupported in bindings: MAC_OS_X_VERSION_10_11_4 — C macro is not a supported constant
# unsupported in bindings: MAC_OS_X_VERSION_10_12 — C macro is not a supported constant
# unsupported in bindings: MAC_OS_X_VERSION_10_12_1 — C macro is not a supported constant
# unsupported in bindings: MAC_OS_X_VERSION_10_12_2 — C macro is not a supported constant
# unsupported in bindings: MAC_OS_X_VERSION_10_12_4 — C macro is not a supported constant
# unsupported in bindings: MAC_OS_X_VERSION_10_13 — C macro is not a supported constant
# unsupported in bindings: MAC_OS_X_VERSION_10_13_1 — C macro is not a supported constant
# unsupported in bindings: MAC_OS_X_VERSION_10_13_2 — C macro is not a supported constant
# unsupported in bindings: MAC_OS_X_VERSION_10_13_4 — C macro is not a supported constant
# unsupported in bindings: MAC_OS_X_VERSION_10_14 — C macro is not a supported constant
# unsupported in bindings: MAC_OS_X_VERSION_10_14_1 — C macro is not a supported constant
# unsupported in bindings: MAC_OS_X_VERSION_10_14_4 — C macro is not a supported constant
# unsupported in bindings: MAC_OS_X_VERSION_10_14_5 — C macro is not a supported constant
# unsupported in bindings: MAC_OS_X_VERSION_10_14_6 — C macro is not a supported constant
# unsupported in bindings: MAC_OS_X_VERSION_10_15 — C macro is not a supported constant
# unsupported in bindings: MAC_OS_X_VERSION_10_15_1 — C macro is not a supported constant
# unsupported in bindings: MAC_OS_X_VERSION_10_15_4 — C macro is not a supported constant
# unsupported in bindings: MAC_OS_X_VERSION_10_16 — C macro is not a supported constant
# unsupported in bindings: MAC_OS_VERSION_11_0 — C macro is not a supported constant
# unsupported in bindings: MAC_OS_VERSION_11_1 — C macro is not a supported constant
# unsupported in bindings: MAC_OS_VERSION_11_3 — C macro is not a supported constant
# unsupported in bindings: MAC_OS_VERSION_11_4 — C macro is not a supported constant
# unsupported in bindings: MAC_OS_VERSION_11_5 — C macro is not a supported constant
# unsupported in bindings: MAC_OS_VERSION_11_6 — C macro is not a supported constant
# unsupported in bindings: MAC_OS_VERSION_12_0 — C macro is not a supported constant
# unsupported in bindings: MAC_OS_VERSION_12_1 — C macro is not a supported constant
# unsupported in bindings: MAC_OS_VERSION_12_2 — C macro is not a supported constant
# unsupported in bindings: MAC_OS_VERSION_12_3 — C macro is not a supported constant
# unsupported in bindings: MAC_OS_VERSION_12_4 — C macro is not a supported constant
# unsupported in bindings: MAC_OS_VERSION_12_5 — C macro is not a supported constant
# unsupported in bindings: MAC_OS_VERSION_12_6 — C macro is not a supported constant
# unsupported in bindings: MAC_OS_VERSION_12_7 — C macro is not a supported constant
# unsupported in bindings: MAC_OS_VERSION_13_0 — C macro is not a supported constant
# unsupported in bindings: MAC_OS_VERSION_13_1 — C macro is not a supported constant
# unsupported in bindings: MAC_OS_VERSION_13_2 — C macro is not a supported constant
# unsupported in bindings: MAC_OS_VERSION_13_3 — C macro is not a supported constant
# unsupported in bindings: MAC_OS_VERSION_13_4 — C macro is not a supported constant
# unsupported in bindings: MAC_OS_VERSION_13_5 — C macro is not a supported constant
# unsupported in bindings: MAC_OS_VERSION_13_6 — C macro is not a supported constant
# unsupported in bindings: MAC_OS_VERSION_13_7 — C macro is not a supported constant
# unsupported in bindings: MAC_OS_VERSION_14_0 — C macro is not a supported constant
# unsupported in bindings: MAC_OS_VERSION_14_1 — C macro is not a supported constant
# unsupported in bindings: MAC_OS_VERSION_14_2 — C macro is not a supported constant
# unsupported in bindings: MAC_OS_VERSION_14_3 — C macro is not a supported constant
# unsupported in bindings: MAC_OS_VERSION_14_4 — C macro is not a supported constant
# unsupported in bindings: MAC_OS_VERSION_14_5 — C macro is not a supported constant
# unsupported in bindings: MAC_OS_VERSION_14_6 — C macro is not a supported constant
# unsupported in bindings: MAC_OS_VERSION_14_7 — C macro is not a supported constant
# unsupported in bindings: MAC_OS_VERSION_15_0 — C macro is not a supported constant
# unsupported in bindings: MAC_OS_VERSION_15_1 — C macro is not a supported constant
# unsupported in bindings: MAC_OS_VERSION_15_2 — C macro is not a supported constant
# unsupported in bindings: MAC_OS_VERSION_15_3 — C macro is not a supported constant
# unsupported in bindings: MAC_OS_VERSION_15_4 — C macro is not a supported constant
# unsupported in bindings: MAC_OS_VERSION_15_5 — C macro is not a supported constant
# unsupported in bindings: MAC_OS_VERSION_15_6 — C macro is not a supported constant
# unsupported in bindings: MAC_OS_VERSION_16_0 — C macro is not a supported constant
# unsupported in bindings: MAC_OS_VERSION_26_0 — C macro is not a supported constant
# unsupported in bindings: MAC_OS_VERSION_26_1 — C macro is not a supported constant
# unsupported in bindings: MAC_OS_VERSION_26_2 — C macro is not a supported constant
# unsupported in bindings: MAC_OS_VERSION_26_3 — C macro is not a supported constant
# unsupported in bindings: MAC_OS_VERSION_26_4 — C macro is not a supported constant
# unsupported in bindings: _SYS__TYPES_H_ — C macro has no replacement value
# unsupported in bindings: _BSD_MACHINE__TYPES_H_ — C macro has no replacement value
# unsupported in bindings: _BSD_ARM__TYPES_H_ — C macro has no replacement value
# unsupported in bindings: _SYS__PTHREAD_TYPES_H_ — C macro has no replacement value
# unsupported in bindings: _VA_LIST_T — C macro has no replacement value
# unsupported in bindings: _BSD_MACHINE_TYPES_H_ — C macro has no replacement value
# unsupported in bindings: _ARM_MACHTYPES_H_ — C macro has no replacement value
# unsupported in bindings: _MACHTYPES_H_ — C macro has no replacement value
# unsupported in bindings: _INT8_T — C macro has no replacement value
# unsupported in bindings: _INT16_T — C macro has no replacement value
# unsupported in bindings: _INT32_T — C macro has no replacement value
# unsupported in bindings: _INT64_T — C macro has no replacement value
# unsupported in bindings: _U_INT8_T — C macro has no replacement value
# unsupported in bindings: _U_INT16_T — C macro has no replacement value
# unsupported in bindings: _U_INT32_T — C macro has no replacement value
# unsupported in bindings: _U_INT64_T — C macro has no replacement value
# unsupported in bindings: _INTPTR_T — C macro has no replacement value
# unsupported in bindings: _UINTPTR_T — C macro has no replacement value
# unsupported in bindings: USER_ADDR_NULL — C macro is not a supported constant
# unsupported in bindings: CAST_USER_ADDR_T — C function-like macros are not supported
# unsupported in bindings: _SIZE_T — C macro has no replacement value
# unsupported in bindings: NULL — C macro is not a supported constant
# unsupported in bindings: _SYS_STDIO_H_ — C macro has no replacement value
# unsupported in bindings: _FSTDIO — C macro has no replacement value
# unsupported in bindings: _SEEK_SET_H_ — C macro has no replacement value
# unsupported in bindings: EOF — C macro is not a supported constant
# unsupported in bindings: P_tmpdir — C macro is not a supported constant
# unsupported in bindings: stdin — C macro is not a supported constant
# unsupported in bindings: stdout — C macro is not a supported constant
# unsupported in bindings: stderr — C macro is not a supported constant
# unsupported in bindings: _LIBC_COUNT__L_CTERMID — C macro is not a supported constant
# unsupported in bindings: _CTERMID_H_ — C macro has no replacement value
# unsupported in bindings: getc_unlocked — C function-like macros are not supported
# unsupported in bindings: putc_unlocked — C function-like macros are not supported
# unsupported in bindings: getchar_unlocked — C function-like macros are not supported
# unsupported in bindings: putchar_unlocked — C function-like macros are not supported
# unsupported in bindings: _OFF_T — C macro has no replacement value
# unsupported in bindings: _SSIZE_T — C macro has no replacement value
# unsupported in bindings: fropen — C function-like macros are not supported
# unsupported in bindings: fwopen — C function-like macros are not supported
# unsupported in bindings: feof_unlocked — C function-like macros are not supported
# unsupported in bindings: ferror_unlocked — C function-like macros are not supported
# unsupported in bindings: clearerr_unlocked — C function-like macros are not supported
# unsupported in bindings: fileno_unlocked — C function-like macros are not supported
# unsupported in bindings: _SECURE__STDIO_H_ — C macro has no replacement value
# unsupported in bindings: _SECURE__COMMON_H_ — C macro has no replacement value
# unsupported in bindings: sprintf — C function-like macros are not supported
# unsupported in bindings: vsprintf — C function-like macros are not supported
# unsupported in bindings: snprintf — C function-like macros are not supported
# unsupported in bindings: vsnprintf — C function-like macros are not supported
+4
View File
@@ -0,0 +1,4 @@
malloc c_func(__size c_ulong) ?*mut anyopaque
realloc c_func(__ptr ?*mut anyopaque, __size c_ulong) ?*mut anyopaque
free c_func(_ ?*mut anyopaque) void
posix_memalign c_func(__memptr ?*mut ?*mut anyopaque, __alignment c_ulong, __size c_ulong) c_int
+15
View File
@@ -0,0 +1,15 @@
import "@ffi/c"
import "@std/mem"
main func() void {
allocator :: mem.c_allocator
data :: mem.alloc(u8, allocator, 24) catch |_| {
_ = c.printf("Failed to allocate memory\n")
return _
}
defer mem.free(u8, allocator, data)
data[0] = 'H'
_ = c.printf("data[0] = %s\n", data[0])
}
+17
View File
@@ -0,0 +1,17 @@
# Build configuration surface for `brolang build` (v0).
#
# A project's `build.bro` imports this module and declares a top-level constant
# named `config` of type `BuildConfig`. `brolang build [root]` type-checks
# build.bro, reads the config, and writes root/build/name.
#
# Declarative and literal-only: one executable per build. List fields take an
# address-of an array literal (`&["raylib"]`); empty lists are written `&[]`.
BuildConfig :: struct {
name []u8 # output executable name under root/build
source []u8 # program package directory, relative to build.bro
libraries [][]u8 # library names to link (-l)
lib_paths [][]u8 # library search directories (-L)
includes [][]u8 # C include directories (-I)
defines [][]u8 # C preprocessor defines (name or name=value)
links [][]u8 # extra linker inputs (object/source files, -framework pairs)
}
+147
View File
@@ -0,0 +1,147 @@
c :: import "@ffi/c"
AllocError :: enum {
out_of_memory
}
Allocator :: struct {
context ?*mut anyopaque
vtable @AllocatorVTable
}
AllocatorVTable :: struct {
alloc @func(context ?*mut anyopaque, size usize, alignment usize) ?*mut u8
realloc @func(context ?*mut anyopaque, memory ?*mut u8, old_size usize, new_size usize, alignment usize) ?*mut u8
free @func(context ?*mut anyopaque, memory ?*mut u8, size usize, alignment usize) void
}
raw_alloc func(allocator Allocator, size usize, alignment usize) ?*mut u8 {
return allocator.vtable.alloc(allocator.context, size, alignment)
}
raw_realloc func(allocator Allocator, memory ?*mut u8, old_size usize, new_size usize, alignment usize) ?*mut u8 {
return allocator.vtable.realloc(allocator.context, memory, old_size, new_size, alignment)
}
raw_free func(allocator Allocator, memory ?*mut u8, size usize, alignment usize) void {
allocator.vtable.free(allocator.context, memory, size, alignment)
}
_empty_storage [1]mut u64 = [0]
_empty_slice func($T type, count usize) []mut T {
pointer *mut T :: ptr_cast(T, (&_empty_storage).ptr)
return pointer[..count]
}
alloc func($T type, allocator Allocator, count usize) []mut T ! AllocError {
if count == 0 {
return _empty_slice(T, 0)
}
element_size usize :: size_of(T)
if element_size == 0 {
return _empty_slice(T, count)
}
if count > max_value(usize) / element_size {
return .out_of_memory
}
memory ?*mut u8 = raw_alloc(allocator, count * element_size, align_of(T))
if memory |bytes| {
pointer *mut T :: ptr_cast(T, bytes)
return pointer[..count]
}
return .out_of_memory
}
free func($T type, allocator Allocator, memory []mut T) void {
if memory.len != 0 and size_of(T) != 0 {
raw_free(allocator, ptr_cast(u8, memory.ptr), memory.len * size_of(T), align_of(T))
}
}
_malloc_alignment usize :: 16 # ponytail: aarch64-macos libc malloc alignment assumption.
_power_of_two func(value usize) bool {
if value == 0 {
return false
}
current usize = value
while current > 1 {
half usize = current / 2
if half * 2 != current {
return false
}
current = half
}
return true
}
_c_alloc func(_ ?*mut anyopaque, size usize, alignment usize) ?*mut u8 {
if _power_of_two(alignment) == false {
return none
}
if alignment <= _malloc_alignment {
return ptr_cast(u8, c.malloc(c_ulong(size)))
}
memory [1]mut ?*mut anyopaque = [none]
status c_int = c.posix_memalign((&memory).ptr, c_ulong(alignment), c_ulong(size))
if status != 0 {
return none
}
return ptr_cast(u8, memory[0])
}
_c_realloc func(_ ?*mut anyopaque, memory ?*mut u8, old_size usize, new_size usize, alignment usize) ?*mut u8 {
if _power_of_two(alignment) == false {
return none
}
if new_size == 0 {
c.free(memory)
return none
}
if memory |old_memory| {
if alignment <= _malloc_alignment {
return ptr_cast(u8, c.realloc(old_memory, c_ulong(new_size)))
}
new_memory ?*mut u8 = _c_alloc(none, new_size, alignment)
if new_memory |new_bytes| {
copy_size usize = old_size
if new_size < copy_size {
copy_size = new_size
}
i usize = 0
while i < copy_size : i += 1 {
new_bytes[i] = old_memory[i]
}
c.free(old_memory)
}
return new_memory
}
return _c_alloc(none, new_size, alignment)
}
_c_free func(_ ?*mut anyopaque, memory ?*mut u8, _ usize, _ usize) void {
c.free(memory)
}
_c_vtable AllocatorVTable :: AllocatorVTable {
alloc = _c_alloc,
realloc = _c_realloc,
free = _c_free,
}
c_allocator Allocator :: Allocator {
context = none,
vtable = &_c_vtable,
}