allocator interface (first pass)
This commit is contained in:
+9
-6
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
@@ -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:
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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{
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
@@ -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
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
malloc c_func(size usize) ?*mut anyopaque
|
||||||
|
free c_func(ptr ?*mut anyopaque) void
|
||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user