allocator interface (first pass)

This commit is contained in:
2026-07-06 21:24:49 +02:00
parent 7ce3917c15
commit 95c61311ca
24 changed files with 833 additions and 161 deletions
+9 -6
View File
@@ -18,18 +18,19 @@ roadmap and milestone history.
### scalar, aggregate, and pointer types ### scalar, aggregate, and pointer types
- exact-width integers, `isize`, `usize`, `f32`, `f64`, `bool`, `void`, and contextual `int`, `float`, and `range` constraints - exact-width integers, `isize`, `usize`, `f32`, `f64`, `bool`, `void`, `anyopaque`, and contextual `int`, `float`, and `range` constraints
- target-dependent C scalar primitives from `c_char` through `c_longdouble`, kept semantically distinct from native scalars - 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 - 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)` - 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`, inferred-count arrays `[_]T`, sentinel arrays `[N;S]T`, compile-time expression array counts, slices `[]T` / `[;S]T`, single-item pointers `@T`, many-item pointers `*T`, and sentinel many-item pointers `[*;S]T` - arrays `[N]T`, inferred-count arrays `[_]T`, sentinel arrays `[N;S]T`, compile-time expression array counts, slices `[]T` / `[;S]T`, single-item pointers `@T`, many-item pointers `*T`, and sentinel many-item pointers `[*;S]T`
- pointer mutability via `mut`, optional pointers as nullable pointers, pointer arithmetic for many-item pointers, postfix dereference `^`, and trapping optional unwrap `?` - pointer 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 - 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
- `ptr_cast(T, ptr)` as a first-pass pointer-child retype that preserves optionality, pointer kind, mutability, and sentinel shape
- UTF-8 string literals as immutable pointers to static zero-terminated byte arrays, plus raw backtick multiline strings - 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 - 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 - 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 - 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)` - source-order native structs, opaque nominal records with `Name :: opaque`, complete `c_struct { ... }`, keyed record literals, native untagged unions, and native tagged unions `union(Enum)` / `union(enum)`
- void-payload tagged-union variants, anonymous struct payloads, contextual `.variant`, `.variant{payload}`, and `.variant{field = value}` construction - 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 - 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 - fallible channel types `T ! E`, where `E` is a native enum/tagged union or supported sum composition
@@ -58,9 +59,9 @@ roadmap and milestone history.
- 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 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
- bodyful `c_func` definitions and bodyless `c_func` declarations with exact external symbol names - 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 - 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 - native function pointer values and types with `@func(...) R`, fallible `@func(...) R ! E`, optional `?@func(...) R`, and non-variadic native indirect calls
- Apple Silicon C ABI lowering for scalars, pointers, fixed-signature plain records/unions, small aggregates, homogeneous float aggregates, and indirect aggregate returns - 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 C typedefs, scalar constants, enum constants, fixed arrays, complete plain structs/unions, C `void*` as nullable `anyopaque` pointers, and pointers to opaque records
- imported external C object variables, including mutable variables and immutable object globals - imported external C object variables, including mutable variables and immutable object globals
- object-like scalar and plain record/union macro constants - object-like scalar and plain record/union macro constants
- supported static inline C functions through generated external wrappers - supported static inline C functions through generated external wrappers
@@ -71,7 +72,8 @@ roadmap and milestone history.
### standard packages ### standard packages
- `std/mem/heap` v1 byte allocation over libc: `alloc(size usize) ?*mut u8` and `free(ptr ?*mut u8)` - `std/mem` allocator contract over byte allocation: `Allocator` with `?*mut anyopaque` context, `heap`, `alloc(allocator, size, alignment) ?*mut u8`, and `free(allocator, ptr, size, alignment)`
- `std/mem/heap` legacy compatibility wrappers: `alloc(size usize) ?*mut u8` and `free(ptr ?*mut u8)`
### compiler behavior ### compiler behavior
@@ -88,7 +90,8 @@ roadmap and milestone history.
- tuples and native Brolang variadic functions - tuples and native Brolang variadic functions
- exporting Brolang functions to C and broader target-specific C ABI lowering - 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 - 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 - typed heap allocation helpers, arenas, pools, build-mode heap policy, and escaping-allocation diagnostics
- 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 - 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 - backed/C enum composition and must-consume fallible linting
- result-to-argument type-demand propagation through function call boundaries - result-to-argument type-demand propagation through function call boundaries
+4 -3
View File
@@ -71,7 +71,8 @@ main func() void {
Header imports expose supported external functions, typedefs, C scalars, fixed Header imports expose supported external functions, typedefs, C scalars, fixed
arrays, complete plain structs and unions, function pointer typedefs, and arrays, complete plain structs and unions, function pointer typedefs, and
pointers to opaque records. Plain records can be constructed with keyed pointers to opaque records. C `void*` imports as nullable `anyopaque` pointers.
Plain records can be constructed with keyed
literals, accessed by field, and passed or returned by value through fixed C literals, accessed by field, and passed or returned by value through fixed C
signatures on `aarch64-macos`. Unsupported or incomplete records remain signatures on `aarch64-macos`. Unsupported or incomplete records remain
pointer-only. Header imports never add linker inputs; implementations must still pointer-only. Header imports never add linker inputs; implementations must still
@@ -154,7 +155,7 @@ declares them. Imports beginning with `@` resolve from the project root:
```bro ```bro
import "../math" import "../math"
other_math :: import "../math" other_math :: import "../math"
heap :: import "@std/mem/heap" mem :: import "@std/mem"
value :: math.sum(other_math.value, 1) value :: math.sum(other_math.value, 1)
``` ```
@@ -165,7 +166,7 @@ Current prototype features:
- `#` comments - `#` comments
- Immutable `::` bindings, typed mutable `=` locals/globals, and `_` sinks - Immutable `::` bindings, typed mutable `=` locals/globals, and `_` sinks
- Exact-width signed/unsigned integers, `f32`, `f64`, `isize`, `usize`, and loose integer-constrained `int` - Exact-width signed/unsigned integers, `f32`, `f64`, `isize`, `usize`, and loose integer-constrained `int`
- Target-dependent atomic `c_*` primitive types, `c_func`, and defined or opaque `c_struct` - Target-dependent atomic `c_*` primitive types, `c_func`, complete `c_struct`, `opaque`, `anyopaque`, and V1 `ptr_cast(T, ptr)`
- Arrays, sentinel arrays, single-item pointers, many-item pointers, sentinel many-item pointers, slices, sentinel slices, strings, character literals, optionals, and native structs - Arrays, sentinel arrays, single-item pointers, many-item pointers, sentinel many-item pointers, slices, sentinel slices, strings, character literals, optionals, and native structs
- String literals as immutable pointers to static zero-terminated byte arrays - String literals as immutable pointers to static zero-terminated byte arrays
- Pointer-preserving `.ptr`/`.len`, pointer-to-array indexing and slicing, postfix pointer dereference and optional unwrap, and keyed struct literals - Pointer-preserving `.ptr`/`.len`, pointer-to-array indexing and slicing, postfix pointer dereference and optional unwrap, and keyed struct literals
+83 -59
View File
@@ -11,7 +11,7 @@
1. interop type foundation (implemented) 1. interop type foundation (implemented)
- unsigned integers, floats, and target-dependent c scalar types - unsigned integers, floats, and target-dependent c scalar types
- atomic `c_*` primitive types remain distinct until target-aware lowering - atomic `c_*` primitive types remain distinct until target-aware lowering
- `c_func` and pointer-only `c_struct`; `c` remains an ordinary identifier - `c_func`, complete `c_struct`, and pointer-only `opaque`; `c` remains an ordinary identifier
- keep binding mutability (`::` / `=`) separate from element or pointee mutability (`mut`) - keep binding mutability (`::` / `=`) separate from element or pointee mutability (`mut`)
- arrays and indexing - arrays and indexing
- `[N]T`: array with `N` logical elements - `[N]T`: array with `N` logical elements
@@ -28,9 +28,9 @@
- character literals - character literals
- optionals with trapping unwrap and fallback operations - optionals with trapping unwrap and fallback operations
- native structs with compiler-controlled layout - native structs with compiler-controlled layout
- pointer-only `c_struct` support with target c layout - complete `c_struct` support with target c layout and pointer-only `opaque` records
- `Some :: c_struct { ... }`: defined c-layout struct - `Some :: c_struct { ... }`: defined c-layout struct
- `Some :: c_struct`: opaque c-layout struct - `Some :: opaque`: incomplete nominal record
- passing c structs by value was deferred until milestone 4.1 - passing c structs by value was deferred until milestone 4.1
2. restricted c header imports (implemented) 2. restricted c header imports (implemented)
@@ -164,12 +164,19 @@
- added a native type-alias declaration `Name :: alias T` (parser/lexer/token surface; the - added a native type-alias declaration `Name :: alias T` (parser/lexer/token surface; the
`types.define_alias` / `.Alias` machinery already existed) so C typedefs and callback `types.define_alias` / `.Alias` machinery already existed) so C typedefs and callback
typedefs round-trip typedefs round-trip
- emits functions, complete/opaque structs (collapsing `typedef struct {...} Foo`), - emits functions, complete structs and opaque records (collapsing `typedef struct {...} Foo`),
typedef aliases, and scalar/aggregate/enum-member constants typedef aliases, and scalar/aggregate/enum-member constants
- C unions, external variables, static-inline functions, and unsupported declarations have - C unions, external variables, static-inline functions, and unsupported declarations have
no hand-writable spelling and are emitted as `# unsupported in bindings:` comments no hand-writable spelling and are emitted as `# unsupported in bindings:` comments
(functions that reference an un-spellable union therefore keep a dangling reference) (functions that reference an un-spellable union therefore keep a dangling reference)
11.1. opaque, anyopaque, and pointer casts (implemented; v1)
- `Name :: opaque` is the incomplete nominal record spelling; bodyless `c_struct` is invalid
- `anyopaque` is the erased object type used behind pointers for C `void*` and allocator contexts
- C `void` function results remain `void`; C `void*` / `const void*` import and render as `?*mut anyopaque` / `?*anyopaque`
- `ptr_cast(T, ptr)` preserves pointer shape and only changes the child type in v1
- future direction: generalize toward Zig-style arbitrary pointer-result casts once casts have a broader result-type story
12. `undefined` as inspired by zig (implemented): 12. `undefined` as inspired by zig (implemented):
- allow mutable local declarations with `undefined` - allow mutable local declarations with `undefined`
- undefined values are assigned a poison value (0xaa...) - undefined values are assigned a poison value (0xaa...)
@@ -620,10 +627,11 @@
parameters parameters
25. dynamic heap allocation (implemented; v1) 25. dynamic heap allocation (implemented; v1)
- `std/mem/heap` is a tiny relative-importable package over libc `malloc`/`free` - `std/mem` exposes a plain-data `Allocator` contract with `?*mut anyopaque` context, `alloc`, and `free` `@func` pointers
- `heap.alloc(size usize) ?*mut u8` returns nullable mutable byte memory; callers use existing optional unwraps - `mem.heap` is the default libc-backed allocator; `mem.alloc(mem.heap, size, alignment)` returns nullable mutable byte memory
- `heap.free(ptr ?*mut u8) void` forwards to C `free`, including `none` / null - `mem.free(mem.heap, ptr, size, alignment)` frees with the same allocator; size/alignment are part of the contract, though libc ignores them in v1
- typed allocation, allocator parameters, arenas/pools, build-mode heap policy, and escaping-allocation diagnostics remain deferred - `std/mem/heap` remains as legacy compatibility wrappers over `std/mem`
- typed allocation helpers, arenas/pools, build-mode heap policy, and escaping-allocation diagnostics remain deferred
26. import from project "root" (implemented) 26. import from project "root" (implemented)
- imports beginning with `@` resolve from the project root - imports beginning with `@` resolve from the project root
@@ -705,7 +713,7 @@
- comptime storage pointers/slices cannot materialize as runtime memory; escaped - comptime storage pointers/slices cannot materialize as runtime memory; escaped
dead storage is rejected dead storage is rejected
- bare concrete non-comptime function names are values; native function pointer - bare concrete non-comptime function names are values; native function pointer
types use `*func(...) R` and fallible `*func(...) R ! E` types use `@func(...) R` and fallible `@func(...) R ! E`
- comptime-known native/bodyful `c_func` values can be called; bodyless/imported - comptime-known native/bodyful `c_func` values can be called; bodyless/imported
callbacks remain runtime-only callbacks remain runtime-only
- native function pointers are non-variadic v1; C variadic function pointers stay - native function pointers are non-variadic v1; C variadic function pointers stay
@@ -1323,8 +1331,25 @@ total :: $sum_loop(4) # implemented: mutable locals/loops/def
## A word on memory allocation ## A word on memory allocation
(NOTE THAT SYNTAX MAY NOT MATCH BROLANG EXACTLY AND SHOULD BE TAKEN WITH A GRAIN OF SALT - INSPIRATION ONLY) Current v1 is intentionally byte-oriented and plain data:
(FURTHER, EXAMPLES ASSUME ARGUMENTS WITH DEFAULT VALUES AND COMPTIME POLYMORPHISM IN THE FORM OF GENERIC TYPE PARAMETERS - MONOMORPHISED)
```
mem :: import "@std/mem"
bytes := mem.alloc(mem.heap, 128, 1)
defer mem.free(mem.heap, bytes, 128, 1)
```
`@std/mem` is the preferred allocator API. `@std/mem/heap` remains as legacy compatibility wrappers over the same heap allocator:
```
heap :: import "@std/mem/heap"
bytes := heap.alloc(128)
defer heap.free(bytes)
```
Typed allocation helpers, arenas, pools, build-mode heap policy, and escaping-allocation diagnostics are future work. Older examples below are design sketches where noted, not committed syntax.
Memory allocation in Brolang is designed to be **explicit but not verbose**. We reject the dogma that global state is inherently evil — allocators are a cross-cutting concern that nearly every function needs, making them a perfect candidate for sensible defaults. Memory allocation in Brolang is designed to be **explicit but not verbose**. We reject the dogma that global state is inherently evil — allocators are a cross-cutting concern that nearly every function needs, making them a perfect candidate for sensible defaults.
@@ -1345,26 +1370,26 @@ Memory allocation in Brolang is designed to be **explicit but not verbose**. We
### The Default Heap Allocator ### The Default Heap Allocator
Brolang provides a **thread-local global heap allocator** that is: Brolang provides a default heap allocator value:
* Determined at compile time by build mode * Available as `mem.heap`
* Passed explicitly to `mem.alloc` and `mem.free`
* **Immutable at runtime** — cannot be reconfigured * **Immutable at runtime** — cannot be reconfigured
``` ```
import "std/mem/heap" mem :: import "@std/mem"
process func(input []u8) u64 { process func(input []u8) u64 {
# heap used for internal temporary work — does not escape temp ?*mut u8 = mem.alloc(mem.heap, input.len * 2, 1)
temp := heap.alloc(u8, size: input.len * 2) defer mem.free(mem.heap, temp, input.len * 2, 1)
defer heap.free(temp)
# ... work with temp ... # ... work with temp ...
return compute_hash(temp) # only the result escapes, not the allocation return compute_hash(temp)
} }
``` ```
The behavior of `heap` depends on build mode: Future build modes can choose different implementations behind `mem.heap` without changing the allocator contract:
| Build Mode | Allocator Behavior | | Build Mode | Allocator Behavior |
| -- | -- | | -- | -- |
@@ -1372,7 +1397,7 @@ The behavior of `heap` depends on build mode:
| Release | Fast allocator, zero overhead | | Release | Fast allocator, zero overhead |
| ReleaseSafe | Bounds-checking allocator | | ReleaseSafe | Bounds-checking allocator |
This is configured at compile time. You cannot change which allocator `heap` uses at runtime. This is intentional — it prevents bugs where memory allocated with one allocator is freed with another. That policy is configured at compile time. You cannot change which allocator `mem.heap` uses at runtime. This is intentional — it prevents bugs where memory allocated with one allocator is freed with another.
### The Escaping Allocation Rule ### The Escaping Allocation Rule
@@ -1381,42 +1406,41 @@ This is configured at compile time. You cannot change which allocator `heap` use
This rule makes ownership transfer visible at the function signature level. The caller never needs to read the function's implementation to know whether heap cleanup is involved: This rule makes ownership transfer visible at the function signature level. The caller never needs to read the function's implementation to know whether heap cleanup is involved:
``` ```
import "std/mem" mem :: import "@std/mem"
import "std/mem/heap"
# Allocation escapes via return value — requires allocator # Allocation escapes via return value — requires allocator
duplicate func(input []u8, allocator @mem.Allocator) []u8 { duplicate func(input []u8, allocator mem.Allocator) ?*mut u8 {
result := allocator.alloc(u8, size: input.len) result ?*mut u8 = mem.alloc(allocator, input.len, 1)
mem.copy(result, input) mem.copy(result, input)
return result # caller manages this memory return result # caller manages this memory
} }
# Allocation escapes via mutable parameter — requires allocator # Allocation escapes via mutable parameter — requires allocator
init func(obj: @mut MyStruct, allocator: @mem.Allocator) void { init func(obj @mut MyStruct, allocator mem.Allocator) void {
obj.buffer = allocator.alloc(u8, size: 100) obj.buffer = mem.alloc(allocator, 100, 1)
# caller now knows heap memory was written into obj # caller now knows heap memory was written into obj
} }
# No allocation escapes — no allocator needed # No allocation escapes — no allocator needed
process func(input: []u8) u64 { process func(input []u8) u64 {
temp := heap.alloc(u8, size: input.len) temp ?*mut u8 = mem.alloc(mem.heap, input.len, 1)
defer heap.free(temp) defer mem.free(mem.heap, temp, input.len, 1)
# ... work with temp ... # ... work with temp ...
return compute_hash(temp) return compute_hash(temp)
} }
# No heap allocation at all — no allocator needed # No heap allocation at all — no allocator needed
reset func(obj: @mut MyStruct) void { reset func(obj @mut MyStruct) void {
obj.count = 0 obj.count = 0
} }
main func() void { main func() void {
data := duplicate("hello", heap) data := duplicate("hello", mem.heap)
defer heap.free(data) defer mem.free(mem.heap, data, 5, 1)
mut obj := MyStruct{ ... } mut obj := MyStruct{ ... }
init(&obj, heap) init(&obj, mem.heap)
defer heap.free(obj.buffer) defer mem.free(mem.heap, obj.buffer, 100, 1)
} }
``` ```
@@ -1424,9 +1448,9 @@ main func() void {
* Without the rule, a function like `init(obj: @mut MyStruct) void` is ambiguous — did it heap-allocate into `obj`, or just set some fields to stack/static data? The caller has no way to know without reading the implementation. * Without the rule, a function like `init(obj: @mut MyStruct) void` is ambiguous — did it heap-allocate into `obj`, or just set some fields to stack/static data? The caller has no way to know without reading the implementation.
* With the rule, the allocator parameter is a clear signal: "this function produces heap memory that outlives its scope, and you are responsible for cleaning it up." * With the rule, the allocator parameter is a clear signal: "this function produces heap memory that outlives its scope, and you are responsible for cleaning it up."
* Internal allocations (temporary buffers, scratch space) use `heap` directly and are freed before the function returns. No allocator parameter needed, no burden on the caller. * Internal allocations (temporary buffers, scratch space) use `mem.heap` directly and are freed before the function returns. No allocator parameter needed, no burden on the caller.
**The compiler enforces this rule.** If a function heap-allocates memory that escapes without accepting an allocator parameter, the compiler emits an error. The compiler should eventually enforce this rule. If a function heap-allocates memory that escapes without accepting an allocator parameter, the compiler should emit an error.
### Why Immutable Defaults? ### Why Immutable Defaults?
@@ -1437,16 +1461,16 @@ Consider what would happen if you could reconfigure the default allocator:
mem.heap_set(my_custom_heap) mem.heap_set(my_custom_heap)
# Somewhere else in the codebase... # Somewhere else in the codebase...
data := heap.alloc(u8, size: 100) data := mem.alloc(mem.heap, 100, 1)
# Later, someone changes it again... # Later, someone changes it again...
mem.heap_set(different_heap) mem.heap_set(different_heap)
# Now who frees `data`? With which allocator? # Now who frees `data`? With which allocator?
heap.free(data) # 💥 Wrong allocator - undefined behavior! mem.free(mem.heap, data, 100, 1) # wrong allocator - undefined behavior
``` ```
This is "action at a distance" — the behavior of `heap.free()` depends on what some unrelated code did earlier. By making `heap` immutable, Brolang guarantees: This is "action at a distance" — the behavior of `mem.free(mem.heap, ...)` depends on what some unrelated code did earlier. By making `mem.heap` immutable, Brolang guarantees:
**Whatever you allocate with, you free with.** **Whatever you allocate with, you free with.**
@@ -1454,15 +1478,16 @@ This is "action at a distance" — the behavior of `heap.free()` depends on what
For specialized needs, you create explicit allocator instances. These are not global — you manage their lifetime and pass them where needed. For specialized needs, you create explicit allocator instances. These are not global — you manage their lifetime and pass them where needed.
The examples in this section are future typed API sketches. The v1 allocator contract is still `mem.Allocator` plus byte-oriented `mem.alloc`/`mem.free`.
**Arena Allocator**: Fast bump allocation, bulk deallocation: **Arena Allocator**: Fast bump allocation, bulk deallocation:
``` ```
import "std/mem" mem :: import "@std/mem"
import "std/mem/heap"
process_file func(path: []u8, allocator: @mem.Allocator) !Data { process_file func(path []u8, allocator mem.Allocator) !Data {
# arena manages its own backing memory via heap # arena manages its own backing memory via heap
arena := mem.Arena.init(heap, capacity: mem.megabytes(1)) arena := mem.Arena.init(mem.heap, capacity: mem.megabytes(1))
defer arena.deinit() defer arena.deinit()
# all temporary allocations from arena (fast bump allocation) # all temporary allocations from arena (fast bump allocation)
@@ -1484,14 +1509,13 @@ process_file func(path: []u8, allocator: @mem.Allocator) !Data {
**Pool Allocator**: O(1) fixed-size allocation, no fragmentation: **Pool Allocator**: O(1) fixed-size allocation, no fragmentation:
``` ```
import "std/mem" mem :: import "@std/mem"
import "std/mem/heap"
EntitySystem :: struct { EntitySystem :: struct {
pool: mem.Pool(Entity), pool: mem.Pool(Entity),
} }
init_entities func(allocator: @mem.Allocator) EntitySystem { init_entities func(allocator mem.Allocator) EntitySystem {
return EntitySystem{ return EntitySystem{
pool = mem.Pool(Entity).init(allocator, capacity: 10_000), pool = mem.Pool(Entity).init(allocator, capacity: 10_000),
} }
@@ -1511,23 +1535,23 @@ despawn func(sys: @mut EntitySystem, entity: @Entity) void {
As described in the escaping allocation rule, when a function heap-allocates memory that escapes its scope, it must accept an allocator parameter. The caller decides which allocator to use: As described in the escaping allocation rule, when a function heap-allocates memory that escapes its scope, it must accept an allocator parameter. The caller decides which allocator to use:
``` ```
import "std/mem" mem :: import "@std/mem"
# Function that uses caller's allocator # Function that uses caller's allocator
parse func(input: []u8, allocator: @mem.Allocator) !ParseResult { parse func(input []u8, allocator mem.Allocator) !ParseResult {
buffer := allocator.alloc(u8, size: input.len) buffer := mem.alloc(allocator, input.len, 1)
defer allocator.free(buffer) defer mem.free(allocator, buffer, input.len, 1)
# ... parse into buffer ... # ... parse into buffer ...
result := allocator.alloc(ParseResult) # size defaults to 1 result := mem.alloc(allocator, parse_result_size, parse_result_alignment)
return result return result
} }
# Caller decides which allocator to use # Caller decides which allocator to use
main func() void { main func() void {
# use an arena for this parsing work # use an arena for this parsing work
arena := mem.Arena.init(heap, capacity: mem.kilobytes(64)) arena := mem.Arena.init(mem.heap, capacity: mem.kilobytes(64))
defer arena.deinit() defer arena.deinit()
result := parse(input, &arena) catch |err| { result := parse(input, &arena) catch |err| {
# handle error # handle error
@@ -1546,15 +1570,15 @@ main func() void {
| What | How | When to Use | | What | How | When to Use |
| -- | -- | -- | | -- | -- | -- |
| `heap.alloc(T, size: n)` | Thread-local global | General purpose, 90% of cases | | `mem.alloc(mem.heap, n, a)` | Preferred heap allocator | General purpose byte allocation |
| `heap.create(T)` | Thread-local global | Allocate single item | | `mem.free(mem.heap, ptr, n, a)` | Preferred heap allocator | Free byte allocation with original size/alignment |
| `allocator.alloc(T, size: n)` | Caller-provided | Escaping allocations (returned or written to caller's data) | | `heap.alloc(n)` / `heap.free(ptr)` | Legacy wrapper | Compatibility with older `@std/mem/heap` code |
| `arena.alloc(T, size: n)` | Explicit instance | Temporary/scoped work, bulk free | | `mem.alloc(allocator, n, a)` | Caller-provided allocator | Escaping allocations (returned or written to caller's data) |
| `pool.alloc()` | Explicit instance | Many same-sized objects, O(1) | | typed helpers / arenas / pools | Future APIs | Higher-level allocation patterns |
Note that `heap` satisfies the `Allocator` interface, so callers can pass `heap` as the allocator argument when they don't need a specialized allocator — which is most of the time. Note that `mem.heap` is a `mem.Allocator`, so callers can pass it as the allocator argument when they don't need a specialized allocator — which is most of the time.
**The golden rule:** Allocate and free with the same allocator. The type system helps enforce this — memory from `heap` can only be freed with `heap`, memory from your arena can only be freed with that arena. The escaping allocation rule ensures the caller always knows which allocator was used. **The golden rule:** Allocate and free with the same allocator. In v1 this is explicit in the call sites; future diagnostics should use the escaping allocation rule to ensure the caller always knows which allocator was used.
### Compared to Other Languages ### Compared to Other Languages
+1 -1
View File
@@ -290,7 +290,7 @@ unwrap_coercions :: proc(m: ^hir.Module, id: hir.Expr_Id) -> hir.Expr_Id {
cur := id cur := id
for cur != hir.INVALID_EXPR && int(cur) < len(m.exprs) { for cur != hir.INVALID_EXPR && int(cur) < len(m.exprs) {
#partial switch m.exprs[cur].kind { #partial switch m.exprs[cur].kind {
case .Retype, .Weaken_Slice, .Weaken_Pointer, .Decay_Array_Pointer, .Slice_Ptr, case .Retype, .Pointer_Cast, .Weaken_Slice, .Weaken_Pointer, .Decay_Array_Pointer, .Slice_Ptr,
.Widen, .Sum_Widen, .Optional_Some, .C_Coerce, .Scalar_Cast: .Widen, .Sum_Widen, .Optional_Some, .C_Coerce, .Scalar_Cast:
cur = m.exprs[cur].left cur = m.exprs[cur].left
case: case:
+234 -2
View File
@@ -180,6 +180,21 @@ type_label :: proc(checker: ^Checker, value: types.Type) -> string {
return types.name(value) return types.name(value)
} }
is_ptr_cast_call :: proc(checker: ^Checker, expr: ast.Expr) -> bool {
return expr.left == ast.INVALID_EXPR &&
!symbol.is_valid(expr.qualifier) &&
symbol_text(checker, expr.name) == "ptr_cast"
}
valid_ptr_cast_child :: proc(checker: ^Checker, value: types.Type) -> bool {
return types.is_valid(value) &&
!types.is_void(value) &&
!types.is_anyopaque(value) &&
!types.is_function(value, &checker.module.types) &&
(types.is_runtime_value(value, &checker.module.types) ||
types.is_opaque_struct(value, &checker.module.types))
}
is_type_metatype_syntax :: proc(checker: ^Checker, value: ast.Type_Syntax) -> bool { is_type_metatype_syntax :: proc(checker: ^Checker, value: ast.Type_Syntax) -> bool {
item, ok := types.node(&checker.module.types, value) item, ok := types.node(&checker.module.types, value)
return ok && item.name == u32(checker.type_symbol) && item.qualifier == 0 return ok && item.name == u32(checker.type_symbol) && item.qualifier == 0
@@ -958,7 +973,7 @@ function_pointer_type_for_template :: proc(
defer delete(params, checker.allocator) defer delete(params, checker.allocator)
function := checker.ast_module.functions[template] function := checker.ast_module.functions[template]
function_type := types.function(&checker.module.types, params, result, function.c_abi, function.variadic) function_type := types.function(&checker.module.types, params, result, function.c_abi, function.variadic)
pointer_type := types.pointer(&checker.module.types, function_type, false, true) pointer_type := types.pointer(&checker.module.types, function_type, false, false)
spec := INVALID_SPEC spec := INVALID_SPEC
if demanded == nil { if demanded == nil {
if demand_spec { if demand_spec {
@@ -1970,6 +1985,42 @@ infer_expr :: proc(
} }
continue continue
} }
if is_ptr_cast_call(checker, expr) {
if len(expr.args) != 2 {
last = types.INVALID
_ = pop(&stack)
continue
}
child, child_ok := resolve_type_argument(checker, expr.args[0], pkg, file)
operand := infer_nested_expr(checker, expr.args[1], locals, pkg, file, demanded, local_types)
result := types.INVALID
if child_ok && valid_ptr_cast_child(checker, child) {
result, _ = types.replace_pointer_child(&checker.module.types, operand, child)
}
last = result
_ = pop(&stack)
continue
}
if callee_type, handled := infer_qualified_value_field_type(checker, expr, locals, pkg, file); handled {
_, function_item, function_type, ok := types.function_pointer(callee_type, &checker.module.types)
if !ok {
last = types.INVALID
_ = pop(&stack)
continue
}
stack[frame_index].left = function_type
stack[frame_index].args = make([]types.Type, len(expr.args), checker.allocator)
stack[frame_index].stage = 6
if len(expr.args) > 0 {
append(&stack, Infer_Frame{expr=expr.args[0], template=ast.INVALID_FUNCTION})
} else if valid_callable_arity(function_item, 0) {
last = function_item.child
delete(stack[frame_index].args, checker.allocator)
stack[frame_index].args = nil
_ = pop(&stack)
}
continue
}
target_pkg, available := expr_package(checker, expr, pkg, file) target_pkg, available := expr_package(checker, expr, pkg, file)
template := ast.INVALID_FUNCTION template := ast.INVALID_FUNCTION
if available { if available {
@@ -3477,6 +3528,126 @@ find_struct_field :: proc(checker: ^Checker, struct_type: types.Type, name: symb
return 0, {}, false return 0, {}, false
} }
field_type_from_value :: proc(checker: ^Checker, expr: ast.Expr, base_type: types.Type) -> types.Type {
store := &checker.module.types
field_name := symbol_text(checker, expr.name)
item, has_item := types.container(base_type, store)
if has_item && (item.kind == .Array || item.kind == .Slice) {
if field_name == "len" {
return types.USIZE
}
if field_name == "ptr" &&
(item.kind == .Slice || types.is_pointer(base_type, store)) {
return container_pointer_type(store, item)
}
}
value_type := base_type
if types.is_pointer(value_type, store) {
value_type = types.child_type(value_type, store)
}
_, field, ok := find_struct_field(checker, value_type, expr.name)
return field.type if ok else types.INVALID
}
infer_qualified_value_field_type :: proc(
checker: ^Checker,
expr: ast.Expr,
locals: []Infer_Local,
pkg: ast.Package_Id,
file: ast.File_Id,
) -> (types.Type, bool) {
if !symbol.is_valid(expr.qualifier) ||
find_import(checker, file, expr.qualifier) != ast.INVALID_IMPORT {
return types.INVALID, false
}
base_type := find_infer_local(locals, expr.qualifier)
if !types.is_valid(base_type) {
if global := find_global(checker, expr.qualifier, pkg); global != ast.INVALID_GLOBAL {
base_type = checker.global_types[global]
}
}
if !types.is_valid(base_type) {
return types.INVALID, false
}
return field_type_from_value(checker, expr, base_type), true
}
build_field_from_value :: proc(
checker: ^Checker,
expr: ast.Expr,
base: hir.Expr_Id,
base_type: types.Type,
) -> (hir.Expr_Id, bool) {
store := &checker.module.types
field_name := symbol_text(checker, expr.name)
item, has_item := types.container(base_type, store)
if has_item && (item.kind == .Array || item.kind == .Slice) {
if field_name == "len" {
return add_hir_expr(checker, hir.Expr{
kind=.Length, span=expr.span, type=types.USIZE, left=base,
target=hir.INVALID_REF, right=hir.INVALID_EXPR, diagnostic=source.INVALID_DIAGNOSTIC,
}), true
}
if field_name == "ptr" &&
(item.kind == .Slice || types.is_pointer(base_type, store)) {
return add_hir_expr(checker, hir.Expr{
kind=.Slice_Ptr, span=expr.span,
type=container_pointer_type(store, item), left=base,
target=hir.INVALID_REF, right=hir.INVALID_EXPR, diagnostic=source.INVALID_DIAGNOSTIC,
}), true
}
if field_name == "ptr" && item.kind == .Array {
id := source.add(checker.diagnostics, expr.span, "arrays do not expose '.ptr'; take their address first")
return invalid_hir_expr(checker, expr.span, id), false
}
}
value_type := base_type
if types.is_pointer(value_type, store) {
value_type = types.child_type(value_type, store)
}
index, field, ok := find_struct_field(checker, value_type, expr.name)
if !ok {
id := source.addf(checker.diagnostics, expr.span, "unknown struct field '%s'", symbol_text(checker, expr.name))
return invalid_hir_expr(checker, expr.span, id), false
}
if types.is_void(field.type) {
id := source.addf(checker.diagnostics, expr.span, "variant '%s' has no payload to read", symbol_text(checker, expr.name))
return invalid_hir_expr(checker, expr.span, id), false
}
return add_hir_expr(checker, hir.Expr{
kind=.Field, span=expr.span, type=field.type, integer=i64(index), left=base,
target=hir.INVALID_REF, right=hir.INVALID_EXPR, diagnostic=source.INVALID_DIAGNOSTIC,
}), true
}
build_qualified_value_field :: proc(
checker: ^Checker,
expr: ast.Expr,
locals: []Build_Local,
global_reads: ^[dynamic]hir.Global_Id,
pkg: ast.Package_Id,
file: ast.File_Id,
) -> (hir.Expr_Id, bool, bool) {
if !symbol.is_valid(expr.qualifier) ||
find_import(checker, file, expr.qualifier, true) != ast.INVALID_IMPORT {
return hir.INVALID_EXPR, false, false
}
if local, ok := find_build_local(locals, expr.qualifier); ok {
base := add_hir_expr(checker, hir.Expr{
kind=.Local, span=expr.span, type=local.type, target=hir.local_ref(local.id),
left=hir.INVALID_EXPR, right=hir.INVALID_EXPR, diagnostic=source.INVALID_DIAGNOSTIC,
})
value, ok := build_field_from_value(checker, expr, base, local.type)
return value, true, ok
}
if global := find_global(checker, expr.qualifier, pkg); global != ast.INVALID_GLOBAL {
base := build_global_reference(checker, global, expr.span, global_reads)
value, ok := build_field_from_value(checker, expr, base, checker.global_types[global])
return value, true, ok
}
return hir.INVALID_EXPR, false, false
}
find_enum_member :: proc(checker: ^Checker, enum_type: types.Type, name: symbol.Id) -> (types.Enum_Member, bool) { find_enum_member :: proc(checker: ^Checker, enum_type: types.Type, name: symbol.Id) -> (types.Enum_Member, bool) {
for member in types.enum_members_for(&checker.module.types, enum_type) { for member in types.enum_members_for(&checker.module.types, enum_type) {
if member.name == u32(name) { if member.name == u32(name) {
@@ -3581,7 +3752,15 @@ build_function_value :: proc(
} }
defer delete(params, checker.allocator) defer delete(params, checker.allocator)
function_type := types.function(&checker.module.types, params, result, function.c_abi, function.variadic) function_type := types.function(&checker.module.types, params, result, function.c_abi, function.variadic)
pointer_type := types.pointer(&checker.module.types, function_type, false, true) pointer_type := types.pointer(&checker.module.types, function_type, false, false)
expected_pointer := expected
if types.is_optional(expected_pointer, &checker.module.types) {
expected_pointer = types.child_type(expected_pointer, &checker.module.types)
}
if _, _, expected_function, ok := types.function_pointer(expected_pointer, &checker.module.types); ok &&
types.equal(expected_function, function_type) {
pointer_type = expected_pointer
}
spec := find_spec(checker, template, params) spec := find_spec(checker, template, params)
if spec == INVALID_SPEC { if spec == INVALID_SPEC {
id := source.addf( id := source.addf(
@@ -4553,6 +4732,41 @@ build_expr :: proc(
append(&stack, Build_Expr_Frame{expr=expr.left, expected=types.INVALID, template=ast.INVALID_FUNCTION}) append(&stack, Build_Expr_Frame{expr=expr.left, expected=types.INVALID, template=ast.INVALID_FUNCTION})
continue continue
} }
if is_ptr_cast_call(checker, expr) {
if len(expr.args) != 2 {
id := source.addf(checker.diagnostics, expr.span, "ptr_cast expects 2 arguments, got %d", len(expr.args))
last = invalid_hir_expr(checker, expr.span, id)
_ = pop(&stack)
continue
}
target, target_ok := resolve_type_argument(checker, expr.args[0], pkg, file)
if !target_ok {
id := source.add(checker.diagnostics, checker.ast_module.exprs[expr.args[0]].span, "ptr_cast target must be a type")
last = invalid_hir_expr(checker, expr.span, id)
_ = pop(&stack)
continue
}
if !valid_ptr_cast_child(checker, target) {
id := source.addf(checker.diagnostics, checker.ast_module.exprs[expr.args[0]].span, "ptr_cast target must be a sized runtime object type, got %s", type_label(checker, target))
last = invalid_hir_expr(checker, expr.span, id)
_ = pop(&stack)
continue
}
stack[frame_index].target_type = target
stack[frame_index].stage = 9
append(&stack, Build_Expr_Frame{expr=expr.args[1], expected=types.INVALID, template=ast.INVALID_FUNCTION})
continue
}
if callee, handled, ok := build_qualified_value_field(checker, expr, locals, global_reads, pkg, file); handled {
if !ok {
last = callee
_ = pop(&stack)
continue
}
stack[frame_index].stage = 6
last = callee
continue
}
target_pkg, available := expr_package(checker, expr, pkg, file, true) target_pkg, available := expr_package(checker, expr, pkg, file, true)
if !available { if !available {
id := add_package_resolution_diagnostic(checker, expr, file) id := add_package_resolution_diagnostic(checker, expr, file)
@@ -4992,6 +5206,24 @@ build_expr :: proc(
} }
_ = pop(&stack) _ = pop(&stack)
} }
if frame.stage == 9 {
result, ok := types.replace_pointer_child(&checker.module.types, checker.module.exprs[last].type, frame.target_type)
if !ok {
id := source.add(checker.diagnostics, expr.span, "ptr_cast operand must be a pointer or optional pointer")
last = invalid_hir_expr(checker, expr.span, id)
} else {
last = add_hir_expr(checker, hir.Expr{
kind=.Pointer_Cast,
span=expr.span,
type=result,
left=last,
target=hir.INVALID_REF,
right=hir.INVALID_EXPR,
diagnostic=source.INVALID_DIAGNOSTIC,
})
}
_ = pop(&stack)
}
} }
return last return last
} }
+1
View File
@@ -103,6 +103,7 @@ Expr_Kind :: enum u8 {
C_Vararg_Promote, C_Vararg_Promote,
Retype, Retype,
Scalar_Cast, Scalar_Cast,
Pointer_Cast,
Weaken_Pointer, Weaken_Pointer,
Weaken_Slice, Weaken_Slice,
Decay_Array_Pointer, Decay_Array_Pointer,
+1
View File
@@ -100,6 +100,7 @@ Opcode :: enum u8 {
C_Vararg_Promote, C_Vararg_Promote,
Retype, Retype,
Scalar_Cast, Scalar_Cast,
Pointer_Cast,
Weaken_Pointer, Weaken_Pointer,
Weaken_Slice, Weaken_Slice,
Decay_Array_Pointer, Decay_Array_Pointer,
+2
View File
@@ -18,6 +18,7 @@ keyword_kind :: proc(text: string) -> token.Kind {
case "c_func": return .Keyword_C_Func case "c_func": return .Keyword_C_Func
case "struct": return .Keyword_Struct case "struct": return .Keyword_Struct
case "c_struct": return .Keyword_C_Struct case "c_struct": return .Keyword_C_Struct
case "opaque": return .Keyword_Opaque
case "union": return .Keyword_Union case "union": return .Keyword_Union
case "enum": return .Keyword_Enum case "enum": return .Keyword_Enum
case "distinct": return .Keyword_Distinct case "distinct": return .Keyword_Distinct
@@ -44,6 +45,7 @@ keyword_kind :: proc(text: string) -> token.Kind {
case "true": return .Keyword_True case "true": return .Keyword_True
case "false": return .Keyword_False case "false": return .Keyword_False
case "void": return .Keyword_Void case "void": return .Keyword_Void
case "anyopaque": return .Keyword_Anyopaque
case "bool": return .Keyword_Bool case "bool": return .Keyword_Bool
case "int": return .Keyword_Int case "int": return .Keyword_Int
case "float": return .Keyword_Float case "float": return .Keyword_Float
+8 -1
View File
@@ -256,7 +256,7 @@ valid_value :: proc(
.Load_Global, .Function_Address, .Address_Of, .Load, .Union_Tag, .Slice, .Length, .Slice_Ptr, .Load_Global, .Function_Address, .Address_Of, .Load, .Union_Tag, .Slice, .Length, .Slice_Ptr,
.Fallible_Error, .Extract, .Select, .Unwrap, .Fallible_Error, .Extract, .Select, .Unwrap,
.Optional_Is_Some, .Optional_Value, .Orelse, .Optional_Is_Some, .Optional_Value, .Orelse,
.Widen, .Sum_Widen, .C_Coerce, .C_Vararg_Promote, .Retype, .Scalar_Cast, .Weaken_Pointer, .Weaken_Slice, .Decay_Array_Pointer, .Widen, .Sum_Widen, .C_Coerce, .C_Vararg_Promote, .Retype, .Scalar_Cast, .Pointer_Cast, .Weaken_Pointer, .Weaken_Slice, .Decay_Array_Pointer,
.Neg_Checked, .Add_Checked, .Sub_Checked, .Mul_Checked, .Div_Checked, .Pointer_Add, .Not, .Compare, .Call: .Neg_Checked, .Add_Checked, .Sub_Checked, .Mul_Checked, .Div_Checked, .Pointer_Add, .Not, .Compare, .Call:
return true return true
case .Address_Global, .Alloca, .Index_Address, .Field_Address, .Orelse_Begin, case .Address_Global, .Alloca, .Index_Address, .Field_Address, .Orelse_Begin,
@@ -1511,6 +1511,13 @@ emit_instruction_stream :: proc(
continue continue
} }
fmt.sbprintf(&emitter.builder, " %%v%d = select i1 true, ptr %%v%d, ptr null\n", instruction_index, instruction.a) fmt.sbprintf(&emitter.builder, " %%v%d = select i1 true, ptr %%v%d, ptr null\n", instruction_index, instruction.a)
case .Pointer_Cast:
if !valid_instruction(instructions, instruction.a) ||
!types.same_pointer_shape(instructions[instruction.a].type, instruction.type, &emitter.module.types) {
emit_recovery_value(emitter, instruction_index, instruction, "invalid pointer cast operand")
continue
}
fmt.sbprintf(&emitter.builder, " %%v%d = select i1 true, ptr %%v%d, ptr null\n", instruction_index, instruction.a)
case .Weaken_Slice: case .Weaken_Slice:
if !valid_instruction(instructions, instruction.a) || if !valid_instruction(instructions, instruction.a) ||
!types.can_weaken_slice(instructions[instruction.a].type, instruction.type, &emitter.module.types) { !types.can_weaken_slice(instructions[instruction.a].type, instruction.type, &emitter.module.types) {
+4 -1
View File
@@ -173,6 +173,9 @@ translate_c_type :: proc(
case .C_Longdouble: translated = types.C_LONGDOUBLE case .C_Longdouble: translated = types.C_LONGDOUBLE
case .Pointer: case .Pointer:
child := translate_c_type(state, result, item.child, pkg, record_mapping, type_mapping) child := translate_c_type(state, result, item.child, pkg, record_mapping, type_mapping)
if child == types.VOID {
child = types.ANYOPAQUE
}
if types.is_valid(child) { if types.is_valid(child) {
pointer := types.pointer(&state.module.type_store, child, item.mutable, true) pointer := types.pointer(&state.module.type_store, child, item.mutable, true)
translated = types.optional(&state.module.type_store, pointer) translated = types.optional(&state.module.type_store, pointer)
@@ -964,7 +967,7 @@ load_header :: proc(state: ^State, path: string, import_span: source.Span) -> as
name = fmt.tprintf("__c_record_%d", len(state.record_types)) name = fmt.tprintf("__c_record_%d", len(state.record_types))
} }
record_type = types.named(&state.module.type_store, u32(pkg_id), u32(symbol.intern(state.symbols, name))) record_type = types.named(&state.module.type_store, u32(pkg_id), u32(symbol.intern(state.symbols, name)))
_ = types.define_record(&state.module.type_store, record_type, nil, true, true, record.kind == .Union) _ = types.define_record(&state.module.type_store, record_type, nil, false, true, record.kind == .Union)
append(&state.record_identities, strings.clone(record.identity, state.allocator)) append(&state.record_identities, strings.clone(record.identity, state.allocator))
append(&state.record_types, record_type) append(&state.record_types, record_type)
} }
+2 -1
View File
@@ -665,7 +665,7 @@ lower_expr :: proc(state: ^State, expr_id: hir.Expr_Id) -> ir.Instruction_Id {
}) })
} }
_ = pop(&stack) _ = pop(&stack)
case .Widen, .Sum_Widen, .C_Coerce, .C_Vararg_Promote, .Retype, .Scalar_Cast, .Weaken_Pointer, .Weaken_Slice, .Decay_Array_Pointer: case .Widen, .Sum_Widen, .C_Coerce, .C_Vararg_Promote, .Retype, .Scalar_Cast, .Pointer_Cast, .Weaken_Pointer, .Weaken_Slice, .Decay_Array_Pointer:
stack[frame_index].stage = 1 stack[frame_index].stage = 1
append(&stack, Lower_Expr_Frame{expr=expr.left}) append(&stack, Lower_Expr_Frame{expr=expr.left})
case .Negate: case .Negate:
@@ -727,6 +727,7 @@ lower_expr :: proc(state: ^State, expr_id: hir.Expr_Id) -> ir.Instruction_Id {
case .C_Vararg_Promote: op = .C_Vararg_Promote case .C_Vararg_Promote: op = .C_Vararg_Promote
case .Retype: op = .Retype case .Retype: op = .Retype
case .Scalar_Cast: op = .Scalar_Cast case .Scalar_Cast: op = .Scalar_Cast
case .Pointer_Cast: op = .Pointer_Cast
case: op = .Widen case: op = .Widen
} }
last = append_instruction(state, ir.Instruction{ last = append_instruction(state, ir.Instruction{
+29 -2
View File
@@ -113,7 +113,8 @@ is_type_token :: proc(kind: token.Kind) -> bool {
.Keyword_C_Short, .Keyword_C_Ushort, .Keyword_C_Int, .Keyword_C_Uint, .Keyword_C_Short, .Keyword_C_Ushort, .Keyword_C_Int, .Keyword_C_Uint,
.Keyword_C_Long, .Keyword_C_Ulong, .Keyword_C_Longlong, .Keyword_C_Ulonglong, .Keyword_C_Long, .Keyword_C_Ulong, .Keyword_C_Longlong, .Keyword_C_Ulonglong,
.Keyword_C_Float, .Keyword_C_Double, .Keyword_C_Longdouble, .Keyword_C_Float, .Keyword_C_Double, .Keyword_C_Longdouble,
.Keyword_Void, .Keyword_Bool, .Keyword_Func, .Keyword_C_Func, .Identifier, .Question, .At, .Star, .Left_Bracket: .Keyword_Void, .Keyword_Anyopaque, .Keyword_Bool, .Keyword_Func, .Keyword_C_Func,
.Identifier, .Question, .At, .Star, .Left_Bracket:
return true return true
} }
return false return false
@@ -326,6 +327,9 @@ parse_type_atom :: proc(parser: ^Parser) -> ast.Type_Syntax {
case .Keyword_Void: case .Keyword_Void:
advance(parser) advance(parser)
return types.VOID return types.VOID
case .Keyword_Anyopaque:
advance(parser)
return types.ANYOPAQUE
case .Keyword_Bool: case .Keyword_Bool:
advance(parser) advance(parser)
return types.BOOL return types.BOOL
@@ -600,7 +604,7 @@ parse_integer_magnitude :: proc(text: string) -> (u64, bool) {
parse_primary :: proc(parser: ^Parser, nesting: int) -> ast.Expr_Id { parse_primary :: proc(parser: ^Parser, nesting: int) -> ast.Expr_Id {
tok := current(parser) tok := current(parser)
#partial switch tok.kind { #partial switch tok.kind {
case .Keyword_Int, .Keyword_Float, .Keyword_Range, .Keyword_Void, .Keyword_Bool: case .Keyword_Int, .Keyword_Float, .Keyword_Range, .Keyword_Void, .Keyword_Anyopaque, .Keyword_Bool:
start := tok start := tok
target := parse_type_atom(parser) target := parse_type_atom(parser)
return add_expr(parser, ast.Expr{ return add_expr(parser, ast.Expr{
@@ -2214,6 +2218,13 @@ parse_struct :: proc(parser: ^Parser, name: token.Token, c_layout: bool, is_unio
ended_by_newline := current(parser).kind == .Newline ended_by_newline := current(parser).kind == .Newline
skip_newlines(parser) skip_newlines(parser)
if current(parser).kind != .Left_Brace { if current(parser).kind != .Left_Brace {
if c_layout {
source.add(parser.diagnostics, start.span, "c_struct declarations require a body; use 'opaque' for incomplete types")
if !ended_by_newline {
_ = finish_statement(parser)
}
return
}
if !c_layout { if !c_layout {
source.add( source.add(
parser.diagnostics, start.span, parser.diagnostics, start.span,
@@ -2242,6 +2253,18 @@ parse_struct :: proc(parser: ^Parser, name: token.Token, c_layout: bool, is_unio
_ = finish_statement(parser) _ = finish_statement(parser)
} }
parse_opaque :: proc(parser: ^Parser, name: token.Token) {
start := advance(parser)
id := types.named(&parser.module.type_store, u32(parser.pkg), u32(name.symbol))
if !types.define_record(&parser.module.type_store, id, nil, false, true, false) {
source.addf(parser.diagnostics, name.span, "duplicate type declaration '%s'", token_text(parser, name))
}
if current(parser).kind == .Left_Brace {
source.add(parser.diagnostics, start.span, "opaque declarations do not have a body")
}
_ = finish_statement(parser)
}
// synthesize_union_tag builds the anonymous runtime discriminant enum for a tagged // synthesize_union_tag builds the anonymous runtime discriminant enum for a tagged
// union: one member per variant, valued by the program-global (name, payload-type) // union: one member per variant, valued by the program-global (name, payload-type)
// ID. The declared tag enum, if any, remains only the validation surface. // ID. The declared tag enum, if any, remains only the validation surface.
@@ -2573,6 +2596,10 @@ parse_top_level :: proc(parser: ^Parser) {
parse_struct(parser, name, current(parser).kind == .Keyword_C_Struct) parse_struct(parser, name, current(parser).kind == .Keyword_C_Struct)
return return
} }
if operator.kind == .Colon_Colon && current(parser).kind == .Keyword_Opaque {
parse_opaque(parser, name)
return
}
if operator.kind == .Colon_Colon && current(parser).kind == .Keyword_Union { if operator.kind == .Colon_Colon && current(parser).kind == .Keyword_Union {
parse_struct(parser, name, false, is_union=true) parse_struct(parser, name, false, is_union=true)
return return
+2
View File
@@ -54,6 +54,7 @@ Kind :: enum u8 {
Keyword_C_Func, Keyword_C_Func,
Keyword_Struct, Keyword_Struct,
Keyword_C_Struct, Keyword_C_Struct,
Keyword_Opaque,
Keyword_Union, Keyword_Union,
Keyword_Enum, Keyword_Enum,
Keyword_Distinct, Keyword_Distinct,
@@ -80,6 +81,7 @@ Kind :: enum u8 {
Keyword_True, Keyword_True,
Keyword_False, Keyword_False,
Keyword_Void, Keyword_Void,
Keyword_Anyopaque,
Keyword_Bool, Keyword_Bool,
Keyword_Int, Keyword_Int,
Keyword_Float, Keyword_Float,
+6 -2
View File
@@ -99,7 +99,11 @@ render_type :: proc(b: ^strings.Builder, result: ^cimport.Result, id: cimport.Ty
if item.mutable { if item.mutable {
strings.write_string(b, "mut ") strings.write_string(b, "mut ")
} }
render_type(b, result, item.child, record_names) if int(item.child) >= 0 && int(item.child) < len(result.types) && result.types[item.child].kind == .Void {
strings.write_string(b, "anyopaque")
} else {
render_type(b, result, item.child, record_names)
}
case .Array: case .Array:
fmt.sbprintf(b, "[%d]", item.count) fmt.sbprintf(b, "[%d]", item.count)
render_type(b, result, item.child, record_names) render_type(b, result, item.child, record_names)
@@ -157,7 +161,7 @@ emit_records :: proc(b: ^strings.Builder, result: ^cimport.Result, record_names:
} }
if !record.complete || len(record.reason) > 0 { if !record.complete || len(record.reason) > 0 {
// opaque / pointer-only struct // opaque / pointer-only struct
fmt.sbprintf(b, "%s :: c_struct\n", name) fmt.sbprintf(b, "%s :: opaque\n", name)
wrote = true wrote = true
continue continue
} }
+66 -3
View File
@@ -43,6 +43,7 @@ C_LONGDOUBLE :: Type(28)
BOOL :: Type(29) BOOL :: Type(29)
FLOAT :: Type(30) FLOAT :: Type(30)
RANGE :: Type(31) RANGE :: Type(31)
ANYOPAQUE :: Type(32)
DYNAMIC_START :: Type(64) DYNAMIC_START :: Type(64)
@@ -56,6 +57,7 @@ Numeric_Category :: enum u8 {
Kind :: enum u8 { Kind :: enum u8 {
Invalid, Invalid,
Void, Void,
Anyopaque,
Int_Constraint, Int_Constraint,
Float_Constraint, Float_Constraint,
Range_Constraint, Range_Constraint,
@@ -555,6 +557,8 @@ kind :: proc(value: Type, store: ^Store = nil) -> Kind {
return .Invalid return .Invalid
case VOID: case VOID:
return .Void return .Void
case ANYOPAQUE:
return .Anyopaque
case INT: case INT:
return .Int_Constraint return .Int_Constraint
case FLOAT: case FLOAT:
@@ -595,6 +599,10 @@ is_void :: proc(value: Type) -> bool {
return value == VOID return value == VOID
} }
is_anyopaque :: proc(value: Type) -> bool {
return value == ANYOPAQUE
}
is_bool :: proc(value: Type) -> bool { is_bool :: proc(value: Type) -> bool {
return value == BOOL return value == BOOL
} }
@@ -915,8 +923,19 @@ is_runtime_value :: proc(value: Type, store: ^Store, depth := 0) -> bool {
if value_kind == .Scalar || value_kind == .Pointer { if value_kind == .Scalar || value_kind == .Pointer {
return true return true
} }
if value_kind == .Slice || value_kind == .Array || value_kind == .Range || value_kind == .Optional { if value_kind == .Slice || value_kind == .Array || value_kind == .Range {
return !contains_c_struct_by_value(value, store) item, ok := node(store, value)
return ok && is_runtime_value(item.child, store, depth+1) && !contains_c_struct_by_value(value, store)
}
if value_kind == .Optional {
item, ok := node(store, value)
if !ok {
return false
}
if is_pointer(item.child, store) {
return true
}
return is_runtime_value(item.child, store, depth+1) && !contains_c_struct_by_value(value, store)
} }
if value_kind == .Struct || value_kind == .Union { if value_kind == .Struct || value_kind == .Union {
item, ok := node(store, value) item, ok := node(store, value)
@@ -1204,6 +1223,42 @@ function_pointer :: proc(value: Type, store: ^Store) -> (pointer_item, function_
return pointer_node, function_node, pointer_node.child, true return pointer_node, function_node, pointer_node.child, true
} }
replace_pointer_child :: proc(store: ^Store, value, child: Type) -> (Type, bool) {
item, ok := node(store, value)
if !ok {
return INVALID, false
}
if item.kind == .Optional {
replaced, replaced_ok := replace_pointer_child(store, item.child, child)
if !replaced_ok || !is_pointer(replaced, store) {
return INVALID, false
}
return optional(store, replaced), true
}
if item.kind != .Pointer {
return INVALID, false
}
item.child = child
return intern(store, item), true
}
same_pointer_shape :: proc(left, right: Type, store: ^Store) -> bool {
left_item, left_ok := node(store, left)
right_item, right_ok := node(store, right)
if !left_ok || !right_ok {
return false
}
if left_item.kind == .Optional || right_item.kind == .Optional {
return left_item.kind == .Optional && right_item.kind == .Optional &&
same_pointer_shape(left_item.child, right_item.child, store)
}
return left_item.kind == .Pointer && right_item.kind == .Pointer &&
left_item.many == right_item.many &&
left_item.mutable == right_item.mutable &&
left_item.has_sentinel == right_item.has_sentinel &&
(!left_item.has_sentinel || left_item.sentinel == right_item.sentinel)
}
is_c_struct :: proc(value: Type, store: ^Store) -> bool { is_c_struct :: proc(value: Type, store: ^Store) -> bool {
item, ok := node(store, value) item, ok := node(store, value)
return ok && (item.kind == .Struct || item.kind == .Union) && item.c_layout return ok && (item.kind == .Struct || item.kind == .Union) && item.c_layout
@@ -1318,6 +1373,10 @@ with_array_count :: proc(store: ^Store, value: Type, count: u64) -> Type {
can_weaken_pointer :: proc(from, to: Type, store: ^Store) -> bool { can_weaken_pointer :: proc(from, to: Type, store: ^Store) -> bool {
from_node, from_ok := node(store, from) from_node, from_ok := node(store, from)
to_node, to_ok := node(store, to) to_node, to_ok := node(store, to)
if from_ok && to_ok && (from_node.kind == .Optional || to_node.kind == .Optional) {
return from_node.kind == .Optional && to_node.kind == .Optional &&
can_weaken_pointer(from_node.child, to_node.child, store)
}
if !from_ok || !to_ok || from_node.kind != .Pointer || to_node.kind != .Pointer || if !from_ok || !to_ok || from_node.kind != .Pointer || to_node.kind != .Pointer ||
from_node.many != to_node.many || (to_node.mutable && !from_node.mutable) { from_node.many != to_node.many || (to_node.mutable && !from_node.mutable) {
return false return false
@@ -1327,9 +1386,12 @@ can_weaken_pointer :: proc(from, to: Type, store: ^Store) -> bool {
return false return false
} }
same_child := from_node.child == to_node.child same_child := from_node.child == to_node.child
anyopaque_erasure := to_node.child == ANYOPAQUE &&
(is_runtime_value(from_node.child, store) ||
is_opaque_struct(from_node.child, store))
c_string := from_node.many && from_node.child == U8 && to_node.child == C_CHAR && c_string := from_node.many && from_node.child == U8 && to_node.child == C_CHAR &&
from_node.has_sentinel && from_node.sentinel == 0 && !to_node.mutable from_node.has_sentinel && from_node.sentinel == 0 && !to_node.mutable
return same_child || c_string return same_child || anyopaque_erasure || c_string
} }
can_weaken_slice :: proc(from, to: Type, store: ^Store) -> bool { can_weaken_slice :: proc(from, to: Type, store: ^Store) -> bool {
@@ -1594,6 +1656,7 @@ name :: proc(value: Type) -> string {
switch value { switch value {
case INVALID: return "<invalid>" case INVALID: return "<invalid>"
case VOID: return "void" case VOID: return "void"
case ANYOPAQUE: return "anyopaque"
case BOOL: return "bool" case BOOL: return "bool"
case INT: return "int" case INT: return "int"
case FLOAT: return "float" case FLOAT: return "float"
+256 -9
View File
@@ -298,7 +298,7 @@ parser_accepts_native_function_pointer_types :: proc(t: ^testing.T) {
text := `Error :: enum { text := `Error :: enum {
bad bad
} }
take func(callback ?*func(value i32) i32, fallible *func() i32 ! Error) void take func(callback ?@func(value i32) i32, fallible @func() i32 ! Error) void
main func() void {} main func() void {}
` `
source_file := source.Source{path="test.bro", text=text} source_file := source.Source{path="test.bro", text=text}
@@ -320,12 +320,12 @@ main func() void {}
fallible, fallible_ok := types.node(&module.type_store, fallible_function.child) fallible, fallible_ok := types.node(&module.type_store, fallible_function.child)
testing.expect_value(t, len(diagnostics.items), 0) testing.expect_value(t, len(diagnostics.items), 0)
testing.expect(t, optional_ok && optional.kind == .Optional) testing.expect(t, optional_ok && optional.kind == .Optional)
testing.expect(t, pointer_ok && pointer.kind == .Pointer && pointer.many && !pointer.mutable) testing.expect(t, pointer_ok && pointer.kind == .Pointer && !pointer.many && !pointer.mutable)
testing.expect(t, function_ok && function.kind == .Function && !function.c_abi && !function.variadic) testing.expect(t, function_ok && function.kind == .Function && !function.c_abi && !function.variadic)
testing.expect(t, function.child == types.I32) testing.expect(t, function.child == types.I32)
testing.expect_value(t, len(params), 1) testing.expect_value(t, len(params), 1)
testing.expect(t, params[0].type == types.I32) testing.expect(t, params[0].type == types.I32)
testing.expect(t, fallible_pointer_ok && fallible_pointer.kind == .Pointer) testing.expect(t, fallible_pointer_ok && fallible_pointer.kind == .Pointer && !fallible_pointer.many)
testing.expect(t, fallible_function_ok && fallible_function.kind == .Function && !fallible_function.c_abi) testing.expect(t, fallible_function_ok && fallible_function.kind == .Function && !fallible_function.c_abi)
testing.expect(t, fallible_ok && fallible.kind == .Fallible && fallible.child == types.I32) testing.expect(t, fallible_ok && fallible.kind == .Fallible && fallible.child == types.I32)
} }
@@ -1526,11 +1526,12 @@ variadicness_is_part_of_c_function_signature_compatibility :: proc(t: ^testing.T
} }
@(test) @(test)
c_structs_are_by_value_and_may_be_opaque :: proc(t: ^testing.T) { c_structs_are_by_value_and_bodyless_c_struct_uses_opaque :: proc(t: ^testing.T) {
text := `Defined :: c_struct { text := `Defined :: c_struct {
value c_int value c_int
} }
Opaque :: c_struct Opaque :: opaque
Bodyless :: c_struct
Empty :: c_struct {} Empty :: c_struct {}
Bad :: c_struct { Bad :: c_struct {
values []i32 values []i32
@@ -1555,18 +1556,117 @@ main func() void {
defer hir.destroy_module(&hir_module) defer hir.destroy_module(&hir_module)
found_opaque := false found_opaque := false
found_bodyless := false
found_bad_layout := false found_bad_layout := false
found_empty := false found_empty := false
for diagnostic in diagnostics.items { for diagnostic in diagnostics.items {
found_opaque = found_opaque || strings.contains(diagnostic.message, "cannot be passed by value") found_opaque = found_opaque || strings.contains(diagnostic.message, "cannot be passed by value")
found_bodyless = found_bodyless || strings.contains(diagnostic.message, "use 'opaque'")
found_bad_layout = found_bad_layout || strings.contains(diagnostic.message, "C-layout-compatible") found_bad_layout = found_bad_layout || strings.contains(diagnostic.message, "C-layout-compatible")
found_empty = found_empty || strings.contains(diagnostic.message, "at least one field") found_empty = found_empty || strings.contains(diagnostic.message, "at least one field")
} }
testing.expect(t, found_opaque) testing.expect(t, found_opaque)
testing.expect(t, found_bodyless)
testing.expect(t, found_bad_layout) testing.expect(t, found_bad_layout)
testing.expect(t, found_empty) testing.expect(t, found_empty)
} }
@(test)
opaque_anyopaque_and_ptr_cast_compile_and_lower :: proc(t: ^testing.T) {
text := `Handle :: opaque
take func(value ?*mut anyopaque) void {}
use_handle func(handle ?@mut Handle) void {}
main func() void {
values [2]mut u8 = [1, 2]
raw ?*mut anyopaque = (&values).ptr
bytes ?*mut u8 = ptr_cast(u8, raw)
take(bytes)
if bytes |p| {
p[1] = 5
}
one u8 = 1
single ?@mut anyopaque = &one
typed ?@mut u8 = ptr_cast(u8, single)
if typed |p| {
p^ = 2
}
handle ?@mut Handle = none
use_handle(handle)
}
`
source_file := source.Source{path="test.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)
ir_module := lower.lower(&hir_module)
defer ir.destroy_module(&ir_module)
hir_casts := 0
for expr in hir_module.exprs {
hir_casts += 1 if expr.kind == .Pointer_Cast else 0
}
ir_casts := 0
for function in ir_module.functions {
for instruction in function.instructions {
ir_casts += 1 if instruction.op == .Pointer_Cast else 0
}
}
testing.expect_value(t, len(diagnostics.items), 0)
testing.expect_value(t, hir_casts, 2)
testing.expect_value(t, ir_casts, 2)
}
@(test)
anyopaque_by_value_and_invalid_ptr_casts_are_rejected :: proc(t: ^testing.T) {
text := `Callback :: alias c_func() void
main func() void {
raw ?*mut anyopaque = none
value anyopaque = undefined
_ = ptr_cast(void, raw)
_ = ptr_cast(anyopaque, raw)
_ = ptr_cast(Callback, raw)
_ = ptr_cast(u8, 1)
_ = ptr_cast(1, raw)
}
`
source_file := source.Source{path="test.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)
found_by_value := false
found_bad_target := false
found_bad_operand := false
found_target_type := false
for diagnostic in diagnostics.items {
found_by_value = found_by_value || strings.contains(diagnostic.message, "could not infer a concrete type")
found_bad_target = found_bad_target || strings.contains(diagnostic.message, "ptr_cast target must be a sized runtime object type")
found_bad_operand = found_bad_operand || strings.contains(diagnostic.message, "ptr_cast operand must be a pointer")
found_target_type = found_target_type || strings.contains(diagnostic.message, "ptr_cast target must be a type")
}
testing.expect(t, found_by_value)
testing.expect(t, found_bad_target)
testing.expect(t, found_bad_operand)
testing.expect(t, found_target_type)
}
@(test) @(test)
aarch64_c_record_abi_classifies_fixed_parameters_and_results :: proc(t: ^testing.T) { aarch64_c_record_abi_classifies_fixed_parameters_and_results :: proc(t: ^testing.T) {
text := `Small :: c_struct { text := `Small :: c_struct {
@@ -2448,7 +2548,7 @@ main func() void {
@(test) @(test)
native_function_pointer_type_restrictions_are_diagnosed :: proc(t: ^testing.T) { native_function_pointer_type_restrictions_are_diagnosed :: proc(t: ^testing.T) {
text := `main func() void { text := `main func() void {
callback *func(...) void = undefined callback @func(...) void = undefined
} }
` `
source_file := source.Source{path="test.bro", text=text} source_file := source.Source{path="test.bro", text=text}
@@ -2812,6 +2912,140 @@ milestone_24_rejects_invalid_forms :: proc(t: ^testing.T) {
} }
} }
@(test)
field_function_pointer_calls_lower_as_indirect_calls :: proc(t: ^testing.T) {
text := `Callbacks :: struct {
call @func(value i32) i32
value i32
}
plus_one func(value i32) i32 {
return value + 1
}
run func(callbacks Callbacks) i32 {
return callbacks.call(callbacks.value)
}
main func() i32 {
callbacks Callbacks = Callbacks { call = plus_one, value = 41 }
return run(callbacks) - 42
}
`
source_file := source.Source{path="test.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)
ir_module := lower.lower(&hir_module)
defer ir.destroy_module(&ir_module)
indirect_calls := 0
for expr in hir_module.exprs {
if expr.kind == .Call && expr.left != hir.INVALID_EXPR {
indirect_calls += 1
}
}
testing.expect_value(t, len(diagnostics.items), 0)
testing.expect(t, len(ir_module.functions) > 0)
testing.expect(t, indirect_calls > 0)
}
@(test)
field_function_pointer_calls_reject_non_callable_fields :: proc(t: ^testing.T) {
text := `Box :: struct {
value i32
}
main func() void {
box Box = Box { value = 1 }
box.value()
}
`
source_file := source.Source{path="test.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)
found := false
for diagnostic in diagnostics.items {
found = found || strings.contains(diagnostic.message, "call target is not a function pointer")
}
testing.expect(t, found)
}
@(test)
allocator_contract_compiles_and_runs :: proc(t: ^testing.T) {
output := "/tmp/brolang-test-mem-allocator"
defer _ = os.remove(output)
status := compiler_core.compile_package("examples/programs/mem_allocator", 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)
allocator_contract_heap_global_lowers :: 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/mem_allocator", &sources, &diagnostics, &symbols, context.allocator, context.allocator, cimport.Options{}, target.DEFAULT, ".")
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)
found_heap := false
found_anyopaque_context := false
found_alloc_callback := false
found_free_callback := false
for global in hir_module.globals {
if symbol.resolve(&symbols, global.name) != "heap" {
continue
}
found_heap = true
for field in types.fields_for(&hir_module.types, global.type) {
name := symbol.resolve(&symbols, symbol.Id(field.name))
callback_pointer, _, _, callable := types.function_pointer(field.type, &hir_module.types)
if name == "context" {
optional_item, optional_ok := types.node(&hir_module.types, field.type)
if optional_ok && optional_item.kind == .Optional {
pointer_item, pointer_ok := types.node(&hir_module.types, optional_item.child)
found_anyopaque_context = pointer_ok &&
pointer_item.kind == .Pointer &&
pointer_item.mutable &&
pointer_item.many &&
pointer_item.child == types.ANYOPAQUE
}
}
found_alloc_callback = found_alloc_callback || name == "alloc" && callable && !callback_pointer.many
found_free_callback = found_free_callback || name == "free" && callable && !callback_pointer.many
}
}
testing.expect_value(t, len(diagnostics.items), 0)
testing.expect(t, len(ir_module.functions) > 0)
testing.expect(t, found_heap)
testing.expect(t, found_anyopaque_context)
testing.expect(t, found_alloc_callback)
testing.expect(t, found_free_callback)
}
@(test) @(test)
milestone_25_heap_compiles_and_runs :: proc(t: ^testing.T) { milestone_25_heap_compiles_and_runs :: proc(t: ^testing.T) {
output := "/tmp/brolang-test-heap" output := "/tmp/brolang-test-heap"
@@ -8571,7 +8805,7 @@ main func() i32 {
@(test) @(test)
distinct_types_reject_implicit_conversions_operators_and_invalid_backings :: proc(t: ^testing.T) { distinct_types_reject_implicit_conversions_operators_and_invalid_backings :: proc(t: ^testing.T) {
text := `Opaque :: c_struct text := `Opaque :: opaque
UserID :: distinct u32 UserID :: distinct u32
OtherID :: distinct u32 OtherID :: distinct u32
BadInt :: distinct int BadInt :: distinct int
@@ -8881,13 +9115,16 @@ translate_c_emits_native_bindings_and_round_trips :: proc(t: ^testing.T) {
result := cimport.init_result(context.allocator) result := cimport.init_result(context.allocator)
// type table: [0]=c_int, [1]=c_ulong, [2]=Record(Pair), // type table: [0]=c_int, [1]=c_ulong, [2]=Record(Pair),
// [3]=Function(c_int)->c_int, [4]=Pointer->Function (callback) // [3]=Function(c_int)->c_int, [4]=Pointer->Function (callback),
// [5]=void, [6]=Pointer->void
append(&result.types, cimport.Type{kind = .C_Int, child = cimport.INVALID_TYPE}) append(&result.types, cimport.Type{kind = .C_Int, child = cimport.INVALID_TYPE})
append(&result.types, cimport.Type{kind = .C_Ulong, child = cimport.INVALID_TYPE}) append(&result.types, cimport.Type{kind = .C_Ulong, child = cimport.INVALID_TYPE})
append(&result.types, cimport.Type{kind = .Record, record = 0, child = cimport.INVALID_TYPE}) append(&result.types, cimport.Type{kind = .Record, record = 0, child = cimport.INVALID_TYPE})
func_params := []cimport.Type_Id{cimport.Type_Id(0)} func_params := []cimport.Type_Id{cimport.Type_Id(0)}
append(&result.types, cimport.Type{kind = .Function, params = func_params, child = cimport.Type_Id(0)}) append(&result.types, cimport.Type{kind = .Function, params = func_params, child = cimport.Type_Id(0)})
append(&result.types, cimport.Type{kind = .Pointer, child = cimport.Type_Id(3)}) append(&result.types, cimport.Type{kind = .Pointer, child = cimport.Type_Id(3)})
append(&result.types, cimport.Type{kind = .Void, child = cimport.INVALID_TYPE})
append(&result.types, cimport.Type{kind = .Pointer, child = cimport.Type_Id(5), mutable = true})
// record 0: Pair { left c_int; right c_int } // record 0: Pair { left c_int; right c_int }
pair_fields: [dynamic]cimport.Field pair_fields: [dynamic]cimport.Field
@@ -8900,13 +9137,20 @@ translate_c_emits_native_bindings_and_round_trips :: proc(t: ^testing.T) {
append(&choice_fields, cimport.Field{name = "tag", type = cimport.Type_Id(0)}) append(&choice_fields, cimport.Field{name = "tag", type = cimport.Type_Id(0)})
append(&result.records, cimport.Record{name = "Choice", fields = choice_fields, kind = .Union, complete = true}) append(&result.records, cimport.Record{name = "Choice", fields = choice_fields, kind = .Union, complete = true})
// typedef aliases: a scalar and a callback function pointer // record 2: incomplete struct -> opaque
append(&result.records, cimport.Record{name = "Handle", kind = .Struct, complete = false})
// typedef aliases: a scalar, a callback function pointer, and a C void pointer
append(&result.aliases, cimport.Alias{name = "Size", type = cimport.Type_Id(1)}) append(&result.aliases, cimport.Alias{name = "Size", type = cimport.Type_Id(1)})
append(&result.aliases, cimport.Alias{name = "Mapper", type = cimport.Type_Id(4)}) append(&result.aliases, cimport.Alias{name = "Mapper", type = cimport.Type_Id(4)})
append(&result.aliases, cimport.Alias{name = "RawPtr", type = cimport.Type_Id(6)})
add_params := []cimport.Type_Id{cimport.Type_Id(0), cimport.Type_Id(0)} add_params := []cimport.Type_Id{cimport.Type_Id(0), cimport.Type_Id(0)}
add_param_names := []string{"a", "b"} add_param_names := []string{"a", "b"}
append(&result.functions, cimport.Function{name = "imported_add", params = add_params, param_names = add_param_names, result = cimport.Type_Id(0)}) append(&result.functions, cimport.Function{name = "imported_add", params = add_params, param_names = add_param_names, result = cimport.Type_Id(0)})
raw_params := []cimport.Type_Id{cimport.Type_Id(6)}
raw_param_names := []string{"ptr"}
append(&result.functions, cimport.Function{name = "consume_raw", params = raw_params, param_names = raw_param_names, result = cimport.Type_Id(5)})
append(&result.macros, cimport.Macro_Constant{ append(&result.macros, cimport.Macro_Constant{
name = "MAX_LEN", name = "MAX_LEN",
@@ -8938,8 +9182,11 @@ translate_c_emits_native_bindings_and_round_trips :: proc(t: ^testing.T) {
testing.expect(t, strings.contains(output, "Size :: alias c_ulong")) testing.expect(t, strings.contains(output, "Size :: alias c_ulong"))
// Function-pointer types carry no parameter names, so the callback renders `_`. // Function-pointer types carry no parameter names, so the callback renders `_`.
testing.expect(t, strings.contains(output, "Mapper :: alias ?*c_func(_ c_int) c_int")) testing.expect(t, strings.contains(output, "Mapper :: alias ?*c_func(_ c_int) c_int"))
testing.expect(t, strings.contains(output, "RawPtr :: alias ?*mut anyopaque"))
testing.expect(t, strings.contains(output, "Handle :: opaque"))
// Real C parameter names are used when present. // Real C parameter names are used when present.
testing.expect(t, strings.contains(output, "imported_add c_func(a c_int, b c_int) c_int")) testing.expect(t, strings.contains(output, "imported_add c_func(a c_int, b c_int) c_int"))
testing.expect(t, strings.contains(output, "consume_raw c_func(ptr ?*mut anyopaque) void"))
testing.expect(t, strings.contains(output, "MAX_LEN c_int :: 256")) testing.expect(t, strings.contains(output, "MAX_LEN c_int :: 256"))
testing.expect(t, strings.contains(output, "# unsupported in bindings: C union 'Choice'")) testing.expect(t, strings.contains(output, "# unsupported in bindings: C union 'Choice'"))
testing.expect(t, strings.contains(output, "# unsupported in bindings: external variable 'some_global'")) testing.expect(t, strings.contains(output, "# unsupported in bindings: external variable 'some_global'"))
+27 -27
View File
@@ -46,7 +46,7 @@ Rectangle :: c_struct {
height c_float height c_float
} }
Image :: c_struct { Image :: c_struct {
data ?*mut void data ?*mut anyopaque
width c_int width c_int
height c_int height c_int
mipmaps c_int mipmaps c_int
@@ -179,10 +179,10 @@ Wave :: c_struct {
sampleRate c_uint sampleRate c_uint
sampleSize c_uint sampleSize c_uint
channels c_uint channels c_uint
data ?*mut void data ?*mut anyopaque
} }
rAudioBuffer :: c_struct rAudioBuffer :: opaque
rAudioProcessor :: c_struct rAudioProcessor :: opaque
AudioStream :: c_struct { AudioStream :: c_struct {
buffer ?*mut rAudioBuffer buffer ?*mut rAudioBuffer
processor ?*mut rAudioProcessor processor ?*mut rAudioProcessor
@@ -199,7 +199,7 @@ Music :: c_struct {
frameCount c_uint frameCount c_uint
looping bool looping bool
ctxType c_int ctxType c_int
ctxData ?*mut void ctxData ?*mut anyopaque
} }
VrDeviceInfo :: c_struct { VrDeviceInfo :: c_struct {
hResolution c_int hResolution c_int
@@ -268,10 +268,10 @@ CameraProjection :: alias c_uint
NPatchLayout :: alias c_uint NPatchLayout :: alias c_uint
TraceLogCallback :: alias ?*c_func(_ c_int, _ ?*c_char, _ ?*mut c_char) void TraceLogCallback :: alias ?*c_func(_ c_int, _ ?*c_char, _ ?*mut c_char) void
LoadFileDataCallback :: alias ?*c_func(_ ?*c_char, _ ?*mut c_int) ?*mut c_uchar LoadFileDataCallback :: alias ?*c_func(_ ?*c_char, _ ?*mut c_int) ?*mut c_uchar
SaveFileDataCallback :: alias ?*c_func(_ ?*c_char, _ ?*mut void, _ c_int) bool SaveFileDataCallback :: alias ?*c_func(_ ?*c_char, _ ?*mut anyopaque, _ c_int) bool
LoadFileTextCallback :: alias ?*c_func(_ ?*c_char) ?*mut c_char LoadFileTextCallback :: alias ?*c_func(_ ?*c_char) ?*mut c_char
SaveFileTextCallback :: alias ?*c_func(_ ?*c_char, _ ?*mut c_char) bool SaveFileTextCallback :: alias ?*c_func(_ ?*c_char, _ ?*mut c_char) bool
AudioCallback :: alias ?*c_func(_ ?*mut void, _ c_uint) void AudioCallback :: alias ?*c_func(_ ?*mut anyopaque, _ c_uint) void
RAYLIB_VERSION_MAJOR c_int :: 5 RAYLIB_VERSION_MAJOR c_int :: 5
RAYLIB_VERSION_MINOR c_int :: 5 RAYLIB_VERSION_MINOR c_int :: 5
@@ -634,7 +634,7 @@ SetWindowMaxSize c_func(width c_int, height c_int) void
SetWindowSize c_func(width c_int, height c_int) void SetWindowSize c_func(width c_int, height c_int) void
SetWindowOpacity c_func(opacity c_float) void SetWindowOpacity c_func(opacity c_float) void
SetWindowFocused c_func() void SetWindowFocused c_func() void
GetWindowHandle c_func() ?*mut void GetWindowHandle c_func() ?*mut anyopaque
GetScreenWidth c_func() c_int GetScreenWidth c_func() c_int
GetScreenHeight c_func() c_int GetScreenHeight c_func() c_int
GetRenderWidth c_func() c_int GetRenderWidth c_func() c_int
@@ -685,8 +685,8 @@ LoadShaderFromMemory c_func(vsCode ?*c_char, fsCode ?*c_char) Shader
IsShaderValid c_func(shader Shader) bool IsShaderValid c_func(shader Shader) bool
GetShaderLocation c_func(shader Shader, uniformName ?*c_char) c_int GetShaderLocation c_func(shader Shader, uniformName ?*c_char) c_int
GetShaderLocationAttrib c_func(shader Shader, attribName ?*c_char) c_int GetShaderLocationAttrib c_func(shader Shader, attribName ?*c_char) c_int
SetShaderValue c_func(shader Shader, locIndex c_int, value ?*void, uniformType c_int) void SetShaderValue c_func(shader Shader, locIndex c_int, value ?*anyopaque, uniformType c_int) void
SetShaderValueV c_func(shader Shader, locIndex c_int, value ?*void, uniformType c_int, count c_int) void SetShaderValueV c_func(shader Shader, locIndex c_int, value ?*anyopaque, uniformType c_int, count c_int) void
SetShaderValueMatrix c_func(shader Shader, locIndex c_int, mat Matrix) void SetShaderValueMatrix c_func(shader Shader, locIndex c_int, mat Matrix) void
SetShaderValueTexture c_func(shader Shader, locIndex c_int, texture Texture) void SetShaderValueTexture c_func(shader Shader, locIndex c_int, texture Texture) void
UnloadShader c_func(shader Shader) void UnloadShader c_func(shader Shader) void
@@ -714,17 +714,17 @@ SetConfigFlags c_func(flags c_uint) void
OpenURL c_func(url ?*c_char) void OpenURL c_func(url ?*c_char) void
TraceLog c_func(logLevel c_int, text ?*c_char, ...) void TraceLog c_func(logLevel c_int, text ?*c_char, ...) void
SetTraceLogLevel c_func(logLevel c_int) void SetTraceLogLevel c_func(logLevel c_int) void
MemAlloc c_func(size c_uint) ?*mut void MemAlloc c_func(size c_uint) ?*mut anyopaque
MemRealloc c_func(ptr ?*mut void, size c_uint) ?*mut void MemRealloc c_func(ptr ?*mut anyopaque, size c_uint) ?*mut anyopaque
MemFree c_func(ptr ?*mut void) void MemFree c_func(ptr ?*mut anyopaque) void
SetTraceLogCallback c_func(callback ?*c_func(_ c_int, _ ?*c_char, _ ?*mut c_char) void) void SetTraceLogCallback c_func(callback ?*c_func(_ c_int, _ ?*c_char, _ ?*mut c_char) void) void
SetLoadFileDataCallback c_func(callback ?*c_func(_ ?*c_char, _ ?*mut c_int) ?*mut c_uchar) void SetLoadFileDataCallback c_func(callback ?*c_func(_ ?*c_char, _ ?*mut c_int) ?*mut c_uchar) void
SetSaveFileDataCallback c_func(callback ?*c_func(_ ?*c_char, _ ?*mut void, _ c_int) bool) void SetSaveFileDataCallback c_func(callback ?*c_func(_ ?*c_char, _ ?*mut anyopaque, _ c_int) bool) void
SetLoadFileTextCallback c_func(callback ?*c_func(_ ?*c_char) ?*mut c_char) void SetLoadFileTextCallback c_func(callback ?*c_func(_ ?*c_char) ?*mut c_char) void
SetSaveFileTextCallback c_func(callback ?*c_func(_ ?*c_char, _ ?*mut c_char) bool) void SetSaveFileTextCallback c_func(callback ?*c_func(_ ?*c_char, _ ?*mut c_char) bool) void
LoadFileData c_func(fileName ?*c_char, dataSize ?*mut c_int) ?*mut c_uchar LoadFileData c_func(fileName ?*c_char, dataSize ?*mut c_int) ?*mut c_uchar
UnloadFileData c_func(data ?*mut c_uchar) void UnloadFileData c_func(data ?*mut c_uchar) void
SaveFileData c_func(fileName ?*c_char, data ?*mut void, dataSize c_int) bool SaveFileData c_func(fileName ?*c_char, data ?*mut anyopaque, dataSize c_int) bool
ExportDataAsCode c_func(data ?*c_uchar, dataSize c_int, fileName ?*c_char) bool ExportDataAsCode c_func(data ?*c_uchar, dataSize c_int, fileName ?*c_char) bool
LoadFileText c_func(fileName ?*c_char) ?*mut c_char LoadFileText c_func(fileName ?*c_char) ?*mut c_char
UnloadFileText c_func(text ?*mut c_char) void UnloadFileText c_func(text ?*mut c_char) void
@@ -967,8 +967,8 @@ IsTextureValid c_func(texture Texture) bool
UnloadTexture c_func(texture Texture) void UnloadTexture c_func(texture Texture) void
IsRenderTextureValid c_func(target RenderTexture) bool IsRenderTextureValid c_func(target RenderTexture) bool
UnloadRenderTexture c_func(target RenderTexture) void UnloadRenderTexture c_func(target RenderTexture) void
UpdateTexture c_func(texture Texture, pixels ?*void) void UpdateTexture c_func(texture Texture, pixels ?*anyopaque) void
UpdateTextureRec c_func(texture Texture, rec Rectangle, pixels ?*void) void UpdateTextureRec c_func(texture Texture, rec Rectangle, pixels ?*anyopaque) void
GenTextureMipmaps c_func(texture ?*mut Texture) void GenTextureMipmaps c_func(texture ?*mut Texture) void
SetTextureFilter c_func(texture Texture, filter c_int) void SetTextureFilter c_func(texture Texture, filter c_int) void
SetTextureWrap c_func(texture Texture, wrap c_int) void SetTextureWrap c_func(texture Texture, wrap c_int) void
@@ -992,8 +992,8 @@ ColorAlpha c_func(color Color, alpha c_float) Color
ColorAlphaBlend c_func(dst Color, src Color, tint Color) Color ColorAlphaBlend c_func(dst Color, src Color, tint Color) Color
ColorLerp c_func(color1 Color, color2 Color, factor c_float) Color ColorLerp c_func(color1 Color, color2 Color, factor c_float) Color
GetColor c_func(hexValue c_uint) Color GetColor c_func(hexValue c_uint) Color
GetPixelColor c_func(srcPtr ?*mut void, format c_int) Color GetPixelColor c_func(srcPtr ?*mut anyopaque, format c_int) Color
SetPixelColor c_func(dstPtr ?*mut void, color Color, format c_int) void SetPixelColor c_func(dstPtr ?*mut anyopaque, color Color, format c_int) void
GetPixelDataSize c_func(width c_int, height c_int, format c_int) c_int GetPixelDataSize c_func(width c_int, height c_int, format c_int) c_int
GetFontDefault c_func() Font GetFontDefault c_func() Font
LoadFont c_func(fileName ?*c_char) Font LoadFont c_func(fileName ?*c_char) Font
@@ -1082,7 +1082,7 @@ DrawBillboard c_func(camera Camera3D, texture Texture, position Vector3, scale c
DrawBillboardRec c_func(camera Camera3D, texture Texture, source Rectangle, position Vector3, size Vector2, tint Color) void DrawBillboardRec c_func(camera Camera3D, texture Texture, source Rectangle, position Vector3, size Vector2, tint Color) void
DrawBillboardPro c_func(camera Camera3D, texture Texture, source Rectangle, position Vector3, up Vector3, size Vector2, origin Vector2, rotation c_float, tint Color) void DrawBillboardPro c_func(camera Camera3D, texture Texture, source Rectangle, position Vector3, up Vector3, size Vector2, origin Vector2, rotation c_float, tint Color) void
UploadMesh c_func(mesh ?*mut Mesh, dynamic bool) void UploadMesh c_func(mesh ?*mut Mesh, dynamic bool) void
UpdateMeshBuffer c_func(mesh Mesh, index c_int, data ?*void, dataSize c_int, offset c_int) void UpdateMeshBuffer c_func(mesh Mesh, index c_int, data ?*anyopaque, dataSize c_int, offset c_int) void
UnloadMesh c_func(mesh Mesh) void UnloadMesh c_func(mesh Mesh) void
DrawMesh c_func(mesh Mesh, material Material, transform Matrix) void DrawMesh c_func(mesh Mesh, material Material, transform Matrix) void
DrawMeshInstanced c_func(mesh Mesh, material Material, transforms ?*Matrix, instances c_int) void DrawMeshInstanced c_func(mesh Mesh, material Material, transforms ?*Matrix, instances c_int) void
@@ -1133,7 +1133,7 @@ LoadSound c_func(fileName ?*c_char) Sound
LoadSoundFromWave c_func(wave Wave) Sound LoadSoundFromWave c_func(wave Wave) Sound
LoadSoundAlias c_func(source Sound) Sound LoadSoundAlias c_func(source Sound) Sound
IsSoundValid c_func(sound Sound) bool IsSoundValid c_func(sound Sound) bool
UpdateSound c_func(sound Sound, data ?*void, sampleCount c_int) void UpdateSound c_func(sound Sound, data ?*anyopaque, sampleCount c_int) void
UnloadWave c_func(wave Wave) void UnloadWave c_func(wave Wave) void
UnloadSound c_func(sound Sound) void UnloadSound c_func(sound Sound) void
UnloadSoundAlias c_func(_ Sound) void UnloadSoundAlias c_func(_ Sound) void
@@ -1171,7 +1171,7 @@ GetMusicTimePlayed c_func(music Music) c_float
LoadAudioStream c_func(sampleRate c_uint, sampleSize c_uint, channels c_uint) AudioStream LoadAudioStream c_func(sampleRate c_uint, sampleSize c_uint, channels c_uint) AudioStream
IsAudioStreamValid c_func(stream AudioStream) bool IsAudioStreamValid c_func(stream AudioStream) bool
UnloadAudioStream c_func(stream AudioStream) void UnloadAudioStream c_func(stream AudioStream) void
UpdateAudioStream c_func(stream AudioStream, data ?*void, frameCount c_int) void UpdateAudioStream c_func(stream AudioStream, data ?*anyopaque, frameCount c_int) void
IsAudioStreamProcessed c_func(stream AudioStream) bool IsAudioStreamProcessed c_func(stream AudioStream) bool
PlayAudioStream c_func(stream AudioStream) void PlayAudioStream c_func(stream AudioStream) void
PauseAudioStream c_func(stream AudioStream) void PauseAudioStream c_func(stream AudioStream) void
@@ -1182,11 +1182,11 @@ SetAudioStreamVolume c_func(stream AudioStream, volume c_float) void
SetAudioStreamPitch c_func(stream AudioStream, pitch c_float) void SetAudioStreamPitch c_func(stream AudioStream, pitch c_float) void
SetAudioStreamPan c_func(stream AudioStream, pan c_float) void SetAudioStreamPan c_func(stream AudioStream, pan c_float) void
SetAudioStreamBufferSizeDefault c_func(size c_int) void SetAudioStreamBufferSizeDefault c_func(size c_int) void
SetAudioStreamCallback c_func(stream AudioStream, callback ?*c_func(_ ?*mut void, _ c_uint) void) void SetAudioStreamCallback c_func(stream AudioStream, callback ?*c_func(_ ?*mut anyopaque, _ c_uint) void) void
AttachAudioStreamProcessor c_func(stream AudioStream, processor ?*c_func(_ ?*mut void, _ c_uint) void) void AttachAudioStreamProcessor c_func(stream AudioStream, processor ?*c_func(_ ?*mut anyopaque, _ c_uint) void) void
DetachAudioStreamProcessor c_func(stream AudioStream, processor ?*c_func(_ ?*mut void, _ c_uint) void) void DetachAudioStreamProcessor c_func(stream AudioStream, processor ?*c_func(_ ?*mut anyopaque, _ c_uint) void) void
AttachAudioMixedProcessor c_func(processor ?*c_func(_ ?*mut void, _ c_uint) void) void AttachAudioMixedProcessor c_func(processor ?*c_func(_ ?*mut anyopaque, _ c_uint) void) void
DetachAudioMixedProcessor c_func(processor ?*c_func(_ ?*mut void, _ c_uint) void) void DetachAudioMixedProcessor c_func(processor ?*c_func(_ ?*mut anyopaque, _ c_uint) void) void
# unsupported in bindings: RAYLIB_H — C macro has no replacement value # unsupported in bindings: RAYLIB_H — C macro has no replacement value
# unsupported in bindings: _VA_LIST — C macro has no replacement value # unsupported in bindings: _VA_LIST — C macro has no replacement value
+2 -2
View File
@@ -78,11 +78,11 @@ increment func(value i32) i32 {
return value + 1 return value + 1
} }
call_native func(callback *func(value i32) i32, value i32) i32 { call_native func(callback @func(value i32) i32, value i32) i32 {
return callback(value) return callback(value)
} }
call_fallible func(callback *func(flag bool) i32 ! Error, flag bool) i32 ! Error { call_fallible func(callback @func(flag bool) i32 ! Error, flag bool) i32 ! Error {
return try callback(flag) return try callback(flag)
} }
+20
View File
@@ -0,0 +1,20 @@
mem :: import "@std/mem"
main func() i32 {
memory ?*mut u8 = mem.alloc(mem.heap, 4, 1)
defer mem.free(mem.heap, memory, 4, 1)
if memory |bytes| {
bytes[0] = 10
bytes[1] = 20
bytes[2] = bytes[0] + bytes[1]
if (bytes[2] != 30) {
return 2
}
return 0
}
return 1
}
+12 -12
View File
@@ -2,8 +2,8 @@
# unsupported in bindings: C union '__mbstate_t' has no native spelling # unsupported in bindings: C union '__mbstate_t' has no native spelling
__darwin_pthread_handler_rec :: c_struct { __darwin_pthread_handler_rec :: c_struct {
__routine ?*c_func(_ ?*mut void) void __routine ?*c_func(_ ?*mut anyopaque) void
__arg ?*mut void __arg ?*mut anyopaque
__next ?*mut __darwin_pthread_handler_rec __next ?*mut __darwin_pthread_handler_rec
} }
_opaque_pthread_attr_t :: c_struct { _opaque_pthread_attr_t :: c_struct {
@@ -47,7 +47,7 @@ __sbuf :: c_struct {
_base ?*mut c_uchar _base ?*mut c_uchar
_size c_int _size c_int
} }
__sFILEX :: c_struct __sFILEX :: opaque
__sFILE :: c_struct { __sFILE :: c_struct {
_p ?*mut c_uchar _p ?*mut c_uchar
_r c_int _r c_int
@@ -56,11 +56,11 @@ __sFILE :: c_struct {
_file c_short _file c_short
_bf __sbuf _bf __sbuf
_lbfsize c_int _lbfsize c_int
_cookie ?*mut void _cookie ?*mut anyopaque
_close ?*c_func(_ ?*mut void) c_int _close ?*c_func(_ ?*mut anyopaque) c_int
_read ?*c_func(_ ?*mut void, _ ?*mut c_char, _ c_int) c_int _read ?*c_func(_ ?*mut anyopaque, _ ?*mut c_char, _ c_int) c_int
_seek ?*c_func(_ ?*mut void, _ c_longlong, _ c_int) c_longlong _seek ?*c_func(_ ?*mut anyopaque, _ c_longlong, _ c_int) c_longlong
_write ?*c_func(_ ?*mut void, _ ?*c_char, _ c_int) c_int _write ?*c_func(_ ?*mut anyopaque, _ ?*c_char, _ c_int) c_int
_ub __sbuf _ub __sbuf
_extra ?*mut __sFILEX _extra ?*mut __sFILEX
_ur c_int _ur c_int
@@ -196,13 +196,13 @@ fopen c_func(__filename ?*c_char, __mode ?*c_char) ?*mut __sFILE
fprintf c_func(_ ?*mut __sFILE, _ ?*c_char, ...) c_int fprintf c_func(_ ?*mut __sFILE, _ ?*c_char, ...) c_int
fputc c_func(_ c_int, _ ?*mut __sFILE) c_int fputc c_func(_ c_int, _ ?*mut __sFILE) c_int
fputs c_func(_ ?*c_char, _ ?*mut __sFILE) c_int fputs c_func(_ ?*c_char, _ ?*mut __sFILE) c_int
fread c_func(__ptr ?*mut void, __size c_ulong, __nitems c_ulong, __stream ?*mut __sFILE) c_ulong 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 freopen c_func(_ ?*c_char, _ ?*c_char, _ ?*mut __sFILE) ?*mut __sFILE
fscanf c_func(_ ?*mut __sFILE, _ ?*c_char, ...) c_int fscanf c_func(_ ?*mut __sFILE, _ ?*c_char, ...) c_int
fseek c_func(_ ?*mut __sFILE, _ c_long, _ c_int) c_int fseek c_func(_ ?*mut __sFILE, _ c_long, _ c_int) c_int
fsetpos c_func(_ ?*mut __sFILE, _ ?*c_longlong) c_int fsetpos c_func(_ ?*mut __sFILE, _ ?*c_longlong) c_int
ftell c_func(_ ?*mut __sFILE) c_long ftell c_func(_ ?*mut __sFILE) c_long
fwrite c_func(__ptr ?*void, __size c_ulong, __nitems c_ulong, __stream ?*mut __sFILE) c_ulong fwrite c_func(__ptr ?*anyopaque, __size c_ulong, __nitems c_ulong, __stream ?*mut __sFILE) c_ulong
getc c_func(_ ?*mut __sFILE) c_int getc c_func(_ ?*mut __sFILE) c_int
getchar c_func() c_int getchar c_func() c_int
gets c_func(_ ?*mut c_char) ?*mut c_char gets c_func(_ ?*mut c_char) ?*mut c_char
@@ -254,7 +254,7 @@ dprintf c_func(_ c_int, _ ?*c_char, ...) c_int
vdprintf c_func(_ c_int, _ ?*c_char, _ ?*mut 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 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 getline c_func(__linep ?*mut ?*mut c_char, __linecapp ?*mut c_ulong, __stream ?*mut __sFILE) c_long
fmemopen c_func(__buf ?*mut void, __size c_ulong, __mode ?*c_char) ?*mut __sFILE 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 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 asprintf c_func(_ ?*mut ?*mut c_char, _ ?*c_char, ...) c_int
ctermid_r c_func(_ ?*mut c_char) ?*mut c_char ctermid_r c_func(_ ?*mut c_char) ?*mut c_char
@@ -264,7 +264,7 @@ fpurge c_func(_ ?*mut __sFILE) c_int
setbuffer c_func(_ ?*mut __sFILE, _ ?*mut c_char, __size c_int) void setbuffer c_func(_ ?*mut __sFILE, _ ?*mut c_char, __size c_int) void
setlinebuf c_func(_ ?*mut __sFILE) c_int setlinebuf c_func(_ ?*mut __sFILE) c_int
vasprintf c_func(_ ?*mut ?*mut c_char, _ ?*c_char, _ ?*mut c_char) c_int vasprintf c_func(_ ?*mut ?*mut c_char, _ ?*c_char, _ ?*mut c_char) c_int
funopen c_func(_ ?*void, _ ?*c_func(_ ?*mut void, _ ?*mut c_char, _ c_int) c_int, _ ?*c_func(_ ?*mut void, _ ?*c_char, _ c_int) c_int, _ ?*c_func(_ ?*mut void, _ c_longlong, _ c_int) c_longlong, _ ?*c_func(_ ?*mut void) c_int) ?*mut __sFILE 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 __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 __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 __sprintf_chk c_func(_ ?*mut c_char, _ c_int, _ c_ulong, _ ?*c_char, ...) c_int
+2
View File
@@ -0,0 +1,2 @@
malloc c_func(size usize) ?*mut anyopaque
free c_func(ptr ?*mut anyopaque) void
+6 -3
View File
@@ -1,6 +1,9 @@
malloc c_func(size usize) ?*mut u8 mem :: import ".."
free c_func(ptr ?*mut u8) void
alloc func(size usize) ?*mut u8 { alloc func(size usize) ?*mut u8 {
return malloc(size) return mem.alloc(mem.heap, size, 1)
}
free func(memory ?*mut u8) void {
mem.free(mem.heap, memory, 0, 1)
} }
+29
View File
@@ -0,0 +1,29 @@
c :: import "@ffi/c"
Allocator :: struct {
context ?*mut anyopaque
alloc @func(context ?*mut anyopaque, size usize, alignment usize) ?*mut u8
free @func(context ?*mut anyopaque, memory ?*mut u8, size usize, alignment usize) void
}
heap Allocator :: Allocator {
context = none,
alloc = heap_alloc,
free = heap_free,
}
heap_alloc func(context ?*mut anyopaque, size usize, alignment usize) ?*mut u8 {
return ptr_cast(u8, c.malloc(size))
}
heap_free func(context ?*mut anyopaque, memory ?*mut u8, size usize, alignment usize) void {
c.free(memory)
}
alloc func(allocator Allocator, size usize, alignment usize) ?*mut u8 {
return allocator.alloc(allocator.context, size, alignment)
}
free func(allocator Allocator, memory ?*mut u8, size usize, alignment usize) void {
allocator.free(allocator.context, memory, size, alignment)
}
+27 -27
View File
@@ -46,7 +46,7 @@ Rectangle :: c_struct {
height c_float height c_float
} }
Image :: c_struct { Image :: c_struct {
data ?*mut void data ?*mut anyopaque
width c_int width c_int
height c_int height c_int
mipmaps c_int mipmaps c_int
@@ -179,10 +179,10 @@ Wave :: c_struct {
sampleRate c_uint sampleRate c_uint
sampleSize c_uint sampleSize c_uint
channels c_uint channels c_uint
data ?*mut void data ?*mut anyopaque
} }
rAudioBuffer :: c_struct rAudioBuffer :: opaque
rAudioProcessor :: c_struct rAudioProcessor :: opaque
AudioStream :: c_struct { AudioStream :: c_struct {
buffer ?*mut rAudioBuffer buffer ?*mut rAudioBuffer
processor ?*mut rAudioProcessor processor ?*mut rAudioProcessor
@@ -199,7 +199,7 @@ Music :: c_struct {
frameCount c_uint frameCount c_uint
looping bool looping bool
ctxType c_int ctxType c_int
ctxData ?*mut void ctxData ?*mut anyopaque
} }
VrDeviceInfo :: c_struct { VrDeviceInfo :: c_struct {
hResolution c_int hResolution c_int
@@ -268,10 +268,10 @@ CameraProjection :: alias c_uint
NPatchLayout :: alias c_uint NPatchLayout :: alias c_uint
TraceLogCallback :: alias ?*c_func(_ c_int, _ ?*c_char, _ ?*mut c_char) void TraceLogCallback :: alias ?*c_func(_ c_int, _ ?*c_char, _ ?*mut c_char) void
LoadFileDataCallback :: alias ?*c_func(_ ?*c_char, _ ?*mut c_int) ?*mut c_uchar LoadFileDataCallback :: alias ?*c_func(_ ?*c_char, _ ?*mut c_int) ?*mut c_uchar
SaveFileDataCallback :: alias ?*c_func(_ ?*c_char, _ ?*mut void, _ c_int) bool SaveFileDataCallback :: alias ?*c_func(_ ?*c_char, _ ?*mut anyopaque, _ c_int) bool
LoadFileTextCallback :: alias ?*c_func(_ ?*c_char) ?*mut c_char LoadFileTextCallback :: alias ?*c_func(_ ?*c_char) ?*mut c_char
SaveFileTextCallback :: alias ?*c_func(_ ?*c_char, _ ?*mut c_char) bool SaveFileTextCallback :: alias ?*c_func(_ ?*c_char, _ ?*mut c_char) bool
AudioCallback :: alias ?*c_func(_ ?*mut void, _ c_uint) void AudioCallback :: alias ?*c_func(_ ?*mut anyopaque, _ c_uint) void
RAYLIB_VERSION_MAJOR c_int :: 5 RAYLIB_VERSION_MAJOR c_int :: 5
RAYLIB_VERSION_MINOR c_int :: 5 RAYLIB_VERSION_MINOR c_int :: 5
@@ -634,7 +634,7 @@ SetWindowMaxSize c_func(width c_int, height c_int) void
SetWindowSize c_func(width c_int, height c_int) void SetWindowSize c_func(width c_int, height c_int) void
SetWindowOpacity c_func(opacity c_float) void SetWindowOpacity c_func(opacity c_float) void
SetWindowFocused c_func() void SetWindowFocused c_func() void
GetWindowHandle c_func() ?*mut void GetWindowHandle c_func() ?*mut anyopaque
GetScreenWidth c_func() c_int GetScreenWidth c_func() c_int
GetScreenHeight c_func() c_int GetScreenHeight c_func() c_int
GetRenderWidth c_func() c_int GetRenderWidth c_func() c_int
@@ -685,8 +685,8 @@ LoadShaderFromMemory c_func(vsCode ?*c_char, fsCode ?*c_char) Shader
IsShaderValid c_func(shader Shader) bool IsShaderValid c_func(shader Shader) bool
GetShaderLocation c_func(shader Shader, uniformName ?*c_char) c_int GetShaderLocation c_func(shader Shader, uniformName ?*c_char) c_int
GetShaderLocationAttrib c_func(shader Shader, attribName ?*c_char) c_int GetShaderLocationAttrib c_func(shader Shader, attribName ?*c_char) c_int
SetShaderValue c_func(shader Shader, locIndex c_int, value ?*void, uniformType c_int) void SetShaderValue c_func(shader Shader, locIndex c_int, value ?*anyopaque, uniformType c_int) void
SetShaderValueV c_func(shader Shader, locIndex c_int, value ?*void, uniformType c_int, count c_int) void SetShaderValueV c_func(shader Shader, locIndex c_int, value ?*anyopaque, uniformType c_int, count c_int) void
SetShaderValueMatrix c_func(shader Shader, locIndex c_int, mat Matrix) void SetShaderValueMatrix c_func(shader Shader, locIndex c_int, mat Matrix) void
SetShaderValueTexture c_func(shader Shader, locIndex c_int, texture Texture) void SetShaderValueTexture c_func(shader Shader, locIndex c_int, texture Texture) void
UnloadShader c_func(shader Shader) void UnloadShader c_func(shader Shader) void
@@ -714,17 +714,17 @@ SetConfigFlags c_func(flags c_uint) void
OpenURL c_func(url ?*c_char) void OpenURL c_func(url ?*c_char) void
TraceLog c_func(logLevel c_int, text ?*c_char, ...) void TraceLog c_func(logLevel c_int, text ?*c_char, ...) void
SetTraceLogLevel c_func(logLevel c_int) void SetTraceLogLevel c_func(logLevel c_int) void
MemAlloc c_func(size c_uint) ?*mut void MemAlloc c_func(size c_uint) ?*mut anyopaque
MemRealloc c_func(ptr ?*mut void, size c_uint) ?*mut void MemRealloc c_func(ptr ?*mut anyopaque, size c_uint) ?*mut anyopaque
MemFree c_func(ptr ?*mut void) void MemFree c_func(ptr ?*mut anyopaque) void
SetTraceLogCallback c_func(callback ?*c_func(_ c_int, _ ?*c_char, _ ?*mut c_char) void) void SetTraceLogCallback c_func(callback ?*c_func(_ c_int, _ ?*c_char, _ ?*mut c_char) void) void
SetLoadFileDataCallback c_func(callback ?*c_func(_ ?*c_char, _ ?*mut c_int) ?*mut c_uchar) void SetLoadFileDataCallback c_func(callback ?*c_func(_ ?*c_char, _ ?*mut c_int) ?*mut c_uchar) void
SetSaveFileDataCallback c_func(callback ?*c_func(_ ?*c_char, _ ?*mut void, _ c_int) bool) void SetSaveFileDataCallback c_func(callback ?*c_func(_ ?*c_char, _ ?*mut anyopaque, _ c_int) bool) void
SetLoadFileTextCallback c_func(callback ?*c_func(_ ?*c_char) ?*mut c_char) void SetLoadFileTextCallback c_func(callback ?*c_func(_ ?*c_char) ?*mut c_char) void
SetSaveFileTextCallback c_func(callback ?*c_func(_ ?*c_char, _ ?*mut c_char) bool) void SetSaveFileTextCallback c_func(callback ?*c_func(_ ?*c_char, _ ?*mut c_char) bool) void
LoadFileData c_func(fileName ?*c_char, dataSize ?*mut c_int) ?*mut c_uchar LoadFileData c_func(fileName ?*c_char, dataSize ?*mut c_int) ?*mut c_uchar
UnloadFileData c_func(data ?*mut c_uchar) void UnloadFileData c_func(data ?*mut c_uchar) void
SaveFileData c_func(fileName ?*c_char, data ?*mut void, dataSize c_int) bool SaveFileData c_func(fileName ?*c_char, data ?*mut anyopaque, dataSize c_int) bool
ExportDataAsCode c_func(data ?*c_uchar, dataSize c_int, fileName ?*c_char) bool ExportDataAsCode c_func(data ?*c_uchar, dataSize c_int, fileName ?*c_char) bool
LoadFileText c_func(fileName ?*c_char) ?*mut c_char LoadFileText c_func(fileName ?*c_char) ?*mut c_char
UnloadFileText c_func(text ?*mut c_char) void UnloadFileText c_func(text ?*mut c_char) void
@@ -967,8 +967,8 @@ IsTextureValid c_func(texture Texture) bool
UnloadTexture c_func(texture Texture) void UnloadTexture c_func(texture Texture) void
IsRenderTextureValid c_func(target RenderTexture) bool IsRenderTextureValid c_func(target RenderTexture) bool
UnloadRenderTexture c_func(target RenderTexture) void UnloadRenderTexture c_func(target RenderTexture) void
UpdateTexture c_func(texture Texture, pixels ?*void) void UpdateTexture c_func(texture Texture, pixels ?*anyopaque) void
UpdateTextureRec c_func(texture Texture, rec Rectangle, pixels ?*void) void UpdateTextureRec c_func(texture Texture, rec Rectangle, pixels ?*anyopaque) void
GenTextureMipmaps c_func(texture ?*mut Texture) void GenTextureMipmaps c_func(texture ?*mut Texture) void
SetTextureFilter c_func(texture Texture, filter c_int) void SetTextureFilter c_func(texture Texture, filter c_int) void
SetTextureWrap c_func(texture Texture, wrap c_int) void SetTextureWrap c_func(texture Texture, wrap c_int) void
@@ -992,8 +992,8 @@ ColorAlpha c_func(color Color, alpha c_float) Color
ColorAlphaBlend c_func(dst Color, src Color, tint Color) Color ColorAlphaBlend c_func(dst Color, src Color, tint Color) Color
ColorLerp c_func(color1 Color, color2 Color, factor c_float) Color ColorLerp c_func(color1 Color, color2 Color, factor c_float) Color
GetColor c_func(hexValue c_uint) Color GetColor c_func(hexValue c_uint) Color
GetPixelColor c_func(srcPtr ?*mut void, format c_int) Color GetPixelColor c_func(srcPtr ?*mut anyopaque, format c_int) Color
SetPixelColor c_func(dstPtr ?*mut void, color Color, format c_int) void SetPixelColor c_func(dstPtr ?*mut anyopaque, color Color, format c_int) void
GetPixelDataSize c_func(width c_int, height c_int, format c_int) c_int GetPixelDataSize c_func(width c_int, height c_int, format c_int) c_int
GetFontDefault c_func() Font GetFontDefault c_func() Font
LoadFont c_func(fileName ?*c_char) Font LoadFont c_func(fileName ?*c_char) Font
@@ -1082,7 +1082,7 @@ DrawBillboard c_func(camera Camera3D, texture Texture, position Vector3, scale c
DrawBillboardRec c_func(camera Camera3D, texture Texture, source Rectangle, position Vector3, size Vector2, tint Color) void DrawBillboardRec c_func(camera Camera3D, texture Texture, source Rectangle, position Vector3, size Vector2, tint Color) void
DrawBillboardPro c_func(camera Camera3D, texture Texture, source Rectangle, position Vector3, up Vector3, size Vector2, origin Vector2, rotation c_float, tint Color) void DrawBillboardPro c_func(camera Camera3D, texture Texture, source Rectangle, position Vector3, up Vector3, size Vector2, origin Vector2, rotation c_float, tint Color) void
UploadMesh c_func(mesh ?*mut Mesh, dynamic bool) void UploadMesh c_func(mesh ?*mut Mesh, dynamic bool) void
UpdateMeshBuffer c_func(mesh Mesh, index c_int, data ?*void, dataSize c_int, offset c_int) void UpdateMeshBuffer c_func(mesh Mesh, index c_int, data ?*anyopaque, dataSize c_int, offset c_int) void
UnloadMesh c_func(mesh Mesh) void UnloadMesh c_func(mesh Mesh) void
DrawMesh c_func(mesh Mesh, material Material, transform Matrix) void DrawMesh c_func(mesh Mesh, material Material, transform Matrix) void
DrawMeshInstanced c_func(mesh Mesh, material Material, transforms ?*Matrix, instances c_int) void DrawMeshInstanced c_func(mesh Mesh, material Material, transforms ?*Matrix, instances c_int) void
@@ -1133,7 +1133,7 @@ LoadSound c_func(fileName ?*c_char) Sound
LoadSoundFromWave c_func(wave Wave) Sound LoadSoundFromWave c_func(wave Wave) Sound
LoadSoundAlias c_func(source Sound) Sound LoadSoundAlias c_func(source Sound) Sound
IsSoundValid c_func(sound Sound) bool IsSoundValid c_func(sound Sound) bool
UpdateSound c_func(sound Sound, data ?*void, sampleCount c_int) void UpdateSound c_func(sound Sound, data ?*anyopaque, sampleCount c_int) void
UnloadWave c_func(wave Wave) void UnloadWave c_func(wave Wave) void
UnloadSound c_func(sound Sound) void UnloadSound c_func(sound Sound) void
UnloadSoundAlias c_func(_ Sound) void UnloadSoundAlias c_func(_ Sound) void
@@ -1171,7 +1171,7 @@ GetMusicTimePlayed c_func(music Music) c_float
LoadAudioStream c_func(sampleRate c_uint, sampleSize c_uint, channels c_uint) AudioStream LoadAudioStream c_func(sampleRate c_uint, sampleSize c_uint, channels c_uint) AudioStream
IsAudioStreamValid c_func(stream AudioStream) bool IsAudioStreamValid c_func(stream AudioStream) bool
UnloadAudioStream c_func(stream AudioStream) void UnloadAudioStream c_func(stream AudioStream) void
UpdateAudioStream c_func(stream AudioStream, data ?*void, frameCount c_int) void UpdateAudioStream c_func(stream AudioStream, data ?*anyopaque, frameCount c_int) void
IsAudioStreamProcessed c_func(stream AudioStream) bool IsAudioStreamProcessed c_func(stream AudioStream) bool
PlayAudioStream c_func(stream AudioStream) void PlayAudioStream c_func(stream AudioStream) void
PauseAudioStream c_func(stream AudioStream) void PauseAudioStream c_func(stream AudioStream) void
@@ -1182,11 +1182,11 @@ SetAudioStreamVolume c_func(stream AudioStream, volume c_float) void
SetAudioStreamPitch c_func(stream AudioStream, pitch c_float) void SetAudioStreamPitch c_func(stream AudioStream, pitch c_float) void
SetAudioStreamPan c_func(stream AudioStream, pan c_float) void SetAudioStreamPan c_func(stream AudioStream, pan c_float) void
SetAudioStreamBufferSizeDefault c_func(size c_int) void SetAudioStreamBufferSizeDefault c_func(size c_int) void
SetAudioStreamCallback c_func(stream AudioStream, callback ?*c_func(_ ?*mut void, _ c_uint) void) void SetAudioStreamCallback c_func(stream AudioStream, callback ?*c_func(_ ?*mut anyopaque, _ c_uint) void) void
AttachAudioStreamProcessor c_func(stream AudioStream, processor ?*c_func(_ ?*mut void, _ c_uint) void) void AttachAudioStreamProcessor c_func(stream AudioStream, processor ?*c_func(_ ?*mut anyopaque, _ c_uint) void) void
DetachAudioStreamProcessor c_func(stream AudioStream, processor ?*c_func(_ ?*mut void, _ c_uint) void) void DetachAudioStreamProcessor c_func(stream AudioStream, processor ?*c_func(_ ?*mut anyopaque, _ c_uint) void) void
AttachAudioMixedProcessor c_func(processor ?*c_func(_ ?*mut void, _ c_uint) void) void AttachAudioMixedProcessor c_func(processor ?*c_func(_ ?*mut anyopaque, _ c_uint) void) void
DetachAudioMixedProcessor c_func(processor ?*c_func(_ ?*mut void, _ c_uint) void) void DetachAudioMixedProcessor c_func(processor ?*c_func(_ ?*mut anyopaque, _ c_uint) void) void
# unsupported in bindings: RAYLIB_H — C macro has no replacement value # unsupported in bindings: RAYLIB_H — C macro has no replacement value
# unsupported in bindings: _VA_LIST — C macro has no replacement value # unsupported in bindings: _VA_LIST — C macro has no replacement value