rename intrinsics
This commit is contained in:
+26
-22
@@ -25,11 +25,11 @@ roadmap and milestone history.
|
|||||||
- 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)`
|
||||||
- compile-time `min_value(T)` and `max_value(T)` bounds for concrete native and C integer scalar types
|
- compile-time `minval!(T)` and `maxval!(T)` bounds for concrete native and C integer scalar types
|
||||||
- arrays `[N]T`, inferred-count arrays `[_]T`, sentinel arrays `[N;S]T`, compile-time expression array counts, slices `[]T` / `[;S]T`, single-item pointers `@T`, many-item pointers `*T`, and sentinel many-item pointers `[*;S]T`
|
- 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
|
- `ptrcast!(T, ptr)` as a first-pass pointer-child retype that preserves optionality, pointer kind, mutability, and sentinel shape
|
||||||
- UTF-8 string literals as immutable pointers to static zero-terminated byte arrays, plus raw backtick multiline strings
|
- 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
|
||||||
@@ -98,47 +98,51 @@ fields. `_` is not a keyword member name.
|
|||||||
|
|
||||||
#### division
|
#### division
|
||||||
|
|
||||||
|
Compiler intrinsics use direct unqualified `name!(...)` syntax. The `!` marks the call as an
|
||||||
|
intrinsic; it is not part of the identifier. Bare and qualified calls without `!` resolve as
|
||||||
|
ordinary user functions, while qualified bang calls are rejected.
|
||||||
|
|
||||||
`/` and `/=` accept only floating-point operands. Integer division must state its rounding and
|
`/` and `/=` accept only floating-point operands. Integer division must state its rounding and
|
||||||
remainder convention with one of these unqualified builtins:
|
remainder convention with one of these intrinsics:
|
||||||
|
|
||||||
| Builtin | Result |
|
| Builtin | Result |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| `div_trunc(a, b)` | quotient rounded toward zero |
|
| `divtrunc!(a, b)` | quotient rounded toward zero |
|
||||||
| `div_floor(a, b)` | quotient rounded toward negative infinity |
|
| `divfloor!(a, b)` | quotient rounded toward negative infinity |
|
||||||
| `div_exact(a, b)` | truncated quotient; traps unless it divides exactly |
|
| `divexact!(a, b)` | truncated quotient; traps unless it divides exactly |
|
||||||
| `div_ceil(a, b)` | quotient rounded toward positive infinity |
|
| `divceil!(a, b)` | quotient rounded toward positive infinity |
|
||||||
| `rem(a, b)` | remainder paired with `div_trunc`; sign follows `a` |
|
| `rem!(a, b)` | remainder paired with `divtrunc!`; sign follows `a` |
|
||||||
| `mod(a, b)` | modulus paired with `div_floor`; sign follows `b` |
|
| `mod!(a, b)` | modulus paired with `divfloor!`; sign follows `b` |
|
||||||
|
|
||||||
The operands may be compatible concrete integer or float scalars. Existing literal coercion and
|
The operands may be compatible concrete integer or float scalars. Existing literal coercion and
|
||||||
numeric widening rules apply, the result has the common operand type, and float quotients are
|
numeric widening rules apply, the result has the common operand type, and float quotients are
|
||||||
integral-valued floats. These identities hold when representable:
|
integral-valued floats. These identities hold when representable:
|
||||||
|
|
||||||
```bro
|
```bro
|
||||||
div_trunc(a, b) * b + rem(a, b) == a
|
divtrunc!(a, b) * b + rem!(a, b) == a
|
||||||
div_floor(a, b) * b + mod(a, b) == a
|
divfloor!(a, b) * b + mod!(a, b) == a
|
||||||
```
|
```
|
||||||
|
|
||||||
Negative operands distinguish the operations:
|
Negative operands distinguish the operations:
|
||||||
|
|
||||||
```bro
|
```bro
|
||||||
div_trunc(-5, 3) == -1
|
divtrunc!(-5, 3) == -1
|
||||||
div_floor(-5, 3) == -2
|
divfloor!(-5, 3) == -2
|
||||||
div_ceil(-5, 3) == -1
|
divceil!(-5, 3) == -1
|
||||||
rem(-5, 3) == -2
|
rem!(-5, 3) == -2
|
||||||
mod(-5, 3) == 1
|
mod!(-5, 3) == 1
|
||||||
mod(5, -3) == -1
|
mod!(5, -3) == -1
|
||||||
```
|
```
|
||||||
|
|
||||||
All six builtins diagnose a zero denominator at comptime and trap at runtime, including float
|
All six builtins diagnose a zero denominator at comptime and trap at runtime, including float
|
||||||
zero. Quotient operations also trap for signed `min_value(T), -1`; `rem` and `mod` return zero for
|
zero. Quotient operations also trap for signed `minval!(T), -1`; `rem!` and `mod!` return zero for
|
||||||
that pair. `div_exact` traps when `div_trunc(a, b) * b == a` is false in the operand type, so float
|
that pair. `divexact!` traps when `divtrunc!(a, b) * b == a` is false in the operand type, so float
|
||||||
exactness follows floating-point equality. Other float NaN and infinity behavior follows the
|
exactness follows floating-point equality. Other float NaN and infinity behavior follows the
|
||||||
underlying IEEE operations. Ordinary float `/` remains unchecked and therefore preserves IEEE
|
underlying IEEE operations. Ordinary float `/` remains unchecked and therefore preserves IEEE
|
||||||
infinity/NaN behavior.
|
infinity/NaN behavior.
|
||||||
|
|
||||||
The six spellings are reserved only as direct unqualified calls. A qualified call such as
|
Only the six bang calls are intrinsic. Bare calls such as `divfloor(a, b)` and qualified calls such
|
||||||
`math.div_floor(a, b)` resolves to an ordinary package function.
|
as `math.divfloor(a, b)` resolve to ordinary functions.
|
||||||
|
|
||||||
### functions, C interop, and linking
|
### functions, C interop, and linking
|
||||||
|
|
||||||
@@ -188,7 +192,7 @@ The six spellings are reserved only as direct unqualified calls. A qualified cal
|
|||||||
- 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
|
||||||
- arenas, pools, build-mode heap policy, and escaping-allocation diagnostics
|
- arenas, pools, build-mode heap policy, and escaping-allocation diagnostics
|
||||||
- recursive type factories, type reflection, and type-producing unions/enums
|
- recursive type factories, type reflection, and type-producing unions/enums
|
||||||
- broader Zig-style pointer/result casts beyond V1 `ptr_cast(T, ptr)`
|
- broader Zig-style pointer/result casts beyond V1 `ptrcast!(T, ptr)`
|
||||||
- sum-type ABI/layout polish, including dynamic tag-width shrinking, all-void channel collapse, and cross-module global-id determinism
|
- 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
|
||||||
|
|||||||
@@ -183,7 +183,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`, complete `c_struct`, `opaque`, `anyopaque`, and V1 `ptr_cast(T, ptr)`
|
- Target-dependent atomic `c_*` primitive types, `c_func`, complete `c_struct`, `opaque`, `anyopaque`, and V1 `ptrcast!(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
|
||||||
@@ -203,7 +203,7 @@ Current prototype features:
|
|||||||
- Bodyless manual and imported C variadic declarations with default argument promotions
|
- Bodyless manual and imported C variadic declarations with default argument promotions
|
||||||
- Ordered linking of additional C sources, objects, archives, and libraries
|
- Ordered linking of additional C sources, objects, archives, and libraries
|
||||||
- Checked signed addition and unary negation
|
- Checked signed addition and unary negation
|
||||||
- Float-only `/` plus explicit `div_trunc`, `div_floor`, `div_exact`, `div_ceil`, `rem`, and `mod` scalar builtins
|
- Float-only `/` plus explicit `divtrunc!`, `divfloor!`, `divexact!`, `divceil!`, `rem!`, and `mod!` scalar intrinsics
|
||||||
- Static, eager runtime, mutable runtime, and deferred problematic globals
|
- Static, eager runtime, mutable runtime, and deferred problematic globals
|
||||||
- Runtime diagnostics followed by `llvm.trap`
|
- Runtime diagnostics followed by `llvm.trap`
|
||||||
|
|
||||||
|
|||||||
@@ -173,7 +173,7 @@
|
|||||||
- `Name :: opaque` is the incomplete nominal record spelling; bodyless `c_struct` is invalid
|
- `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
|
- `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`
|
- 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
|
- `ptrcast!(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
|
- 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):
|
||||||
@@ -464,7 +464,7 @@
|
|||||||
active/field `integer`) is sufficient. **no HIR/IR/lowering change** (like 18/19/20); the delta
|
active/field `integer`) is sufficient. **no HIR/IR/lowering change** (like 18/19/20); the delta
|
||||||
is parser + types layout + one checker validation + three LLVM emit sites
|
is parser + types layout + one checker validation + three LLVM emit sites
|
||||||
- runtime layout `{tag, payload-carrier}`: tag at offset 0, payload carrier at
|
- runtime layout `{tag, payload-carrier}`: tag at offset 0, payload carrier at
|
||||||
`payload_offset = round_up(sizeof(tag), payload_align)` (`types.union_payload_offset`, shared by
|
`payload_offset = round_up(sizeof!(tag), payload_align)` (`types.union_payload_offset`, shared by
|
||||||
`size` and the emitter). field access uses byte-offset GEPs, so offsets stay self-consistent
|
`size` and the emitter). field access uses byte-offset GEPs, so offsets stay self-consistent
|
||||||
- construction `T{ variant = value }` reuses the union-literal path and additionally stores the
|
- construction `T{ variant = value }` reuses the union-literal path and additionally stores the
|
||||||
derived tag; payload read `x.variant` reuses field access, reading at the payload offset
|
derived tag; payload read `x.variant` reuses field access, reading at the payload offset
|
||||||
@@ -794,19 +794,19 @@
|
|||||||
32. explicit division family (implemented)
|
32. explicit division family (implemented)
|
||||||
- `/` and `/=` are float-only; every integer use is rejected with guidance toward explicit
|
- `/` and `/=` are float-only; every integer use is rejected with guidance toward explicit
|
||||||
division, including literals, comptime execution, array counts, and compound assignment
|
division, including literals, comptime execution, array counts, and compound assignment
|
||||||
- direct unqualified calls reserve `div_trunc`, `div_floor`, `div_exact`, `div_ceil`, `rem`, and
|
- direct bang calls use `divtrunc!`, `divfloor!`, `divexact!`, `divceil!`, `rem!`, and `mod!`;
|
||||||
`mod`; qualified names remain ordinary package functions
|
bare and qualified names remain ordinary functions
|
||||||
- the builtins accept compatible concrete integer or float scalars, reuse existing literal and
|
- the builtins accept compatible concrete integer or float scalars, reuse existing literal and
|
||||||
widening rules, and return the common operand type (integral-valued floats for quotients)
|
widening rules, and return the common operand type (integral-valued floats for quotients)
|
||||||
- all builtins diagnose zero denominators at comptime and trap at runtime; quotient operations
|
- all builtins diagnose zero denominators at comptime and trap at runtime; quotient operations
|
||||||
also trap on signed `min_value(T) / -1`, while `rem` and `mod` return zero for that pair
|
also trap on signed `minval!(T) / -1`, while `rem!` and `mod!` return zero for that pair
|
||||||
- `div_exact` checks the reconstructed dividend in the operand type; `rem` pairs with truncation
|
- `divexact!` checks the reconstructed dividend in the operand type; `rem!` pairs with truncation
|
||||||
and follows the numerator sign, while `mod` pairs with floor and follows the denominator sign
|
and follows the numerator sign, while `mod!` pairs with floor and follows the denominator sign
|
||||||
- HIR/IR use compact semantic enum tags; integer floor, ceil, and exact lowering reconstructs the
|
- HIR/IR use compact semantic enum tags; integer floor, ceil, and exact lowering reconstructs the
|
||||||
remainder from one quotient so each produces only one hardware-division candidate
|
remainder from one quotient so each produces only one hardware-division candidate
|
||||||
- float lowering uses the typed LLVM trunc/floor/ceil intrinsics, `frem`, and ordered equality;
|
- float lowering uses the typed LLVM trunc/floor/ceil intrinsics, `frem`, and ordered equality;
|
||||||
ordinary float `/` remains the unchecked IEEE infinity/NaN escape hatch
|
ordinary float `/` remains the unchecked IEEE infinity/NaN escape hatch
|
||||||
- migrated `std/mem`, `std/arraylist`, and the compound-assignment example to `div_trunc`
|
- migrated `std/mem`, `std/arraylist`, and the compound-assignment example to `divtrunc!`
|
||||||
|
|
||||||
33. explicit I/O provider (implemented)
|
33. explicit I/O provider (implemented)
|
||||||
- `main` may take one canonical `@std/io Io`; parameterless entry points remain valid
|
- `main` may take one canonical `@std/io Io`; parameterless entry points remain valid
|
||||||
@@ -834,7 +834,8 @@
|
|||||||
- add `debug.print` function making use of `std/io` to print values to the console
|
- add `debug.print` function making use of `std/io` to print values to the console
|
||||||
- this may either require native variadic arguments or a tuple value to like zig's approach (consider pros and cons)
|
- this may either require native variadic arguments or a tuple value to like zig's approach (consider pros and cons)
|
||||||
|
|
||||||
38. consider renaming `ptr_cast` to `ptrcast`
|
38. place every intrinsic behind direct unqualified `name!(...)` syntax, freeing the bare names for
|
||||||
|
user functions (implemented)
|
||||||
|
|
||||||
## A word on unchecked casts
|
## A word on unchecked casts
|
||||||
|
|
||||||
@@ -844,7 +845,7 @@ For casts that bypass safety checks, Honey provides builtin functions:
|
|||||||
| -- | -- | -- |
|
| -- | -- | -- |
|
||||||
| `truncate(x, T)` | Keep low bits, discard rest | Never |
|
| `truncate(x, T)` | Keep low bits, discard rest | Never |
|
||||||
| `bitcast(x, T)` | Reinterpret bits, no cast | Sizes don't match (compile error) |
|
| `bitcast(x, T)` | Reinterpret bits, no cast | Sizes don't match (compile error) |
|
||||||
| `ptrcast(p, T)` | Change pointer type | Gaining mutability (compile error) |
|
| `ptrcast!(p, T)` | Change pointer type | Gaining mutability (compile error) |
|
||||||
|
|
||||||
```honey
|
```honey
|
||||||
# truncation
|
# truncation
|
||||||
@@ -859,10 +860,10 @@ bits := bitcast(f, u32) # IEEE 754 representation
|
|||||||
|
|
||||||
# pointer casts (element type, many ↔ single, pointer ↔ usize)
|
# pointer casts (element type, many ↔ single, pointer ↔ usize)
|
||||||
buf: *u8 = get_buffer()
|
buf: *u8 = get_buffer()
|
||||||
ints := ptrcast(buf, *u32) # element type change
|
ints := ptrcast!(buf, *u32) # element type change
|
||||||
single := ptrcast(buf, @u8) # many → single (restricting)
|
single := ptrcast!(buf, @u8) # many → single (restricting)
|
||||||
addr := ptrcast(buf, usize) # pointer to integer
|
addr := ptrcast!(buf, usize) # pointer to integer
|
||||||
ptr := ptrcast(addr, @u8) # integer to pointer
|
ptr := ptrcast!(addr, @u8) # integer to pointer
|
||||||
```
|
```
|
||||||
|
|
||||||
## A word on multi-unwrap
|
## A word on multi-unwrap
|
||||||
|
|||||||
@@ -120,6 +120,7 @@ Expr :: struct {
|
|||||||
body: []Stmt_Id,
|
body: []Stmt_Id,
|
||||||
diagnostic: source.Diagnostic_Id,
|
diagnostic: source.Diagnostic_Id,
|
||||||
parenthesized: bool,
|
parenthesized: bool,
|
||||||
|
intrinsic: bool,
|
||||||
kind: Expr_Kind,
|
kind: Expr_Kind,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -401,10 +401,11 @@ type_label :: proc(checker: ^Checker, value: types.Type) -> string {
|
|||||||
return strings.to_string(builder)
|
return strings.to_string(builder)
|
||||||
}
|
}
|
||||||
|
|
||||||
is_ptr_cast_call :: proc(checker: ^Checker, expr: ast.Expr) -> bool {
|
is_ptrcast_call :: proc(checker: ^Checker, expr: ast.Expr) -> bool {
|
||||||
return expr.left == ast.INVALID_EXPR &&
|
return expr.intrinsic &&
|
||||||
|
expr.left == ast.INVALID_EXPR &&
|
||||||
!symbol.is_valid(expr.qualifier) &&
|
!symbol.is_valid(expr.qualifier) &&
|
||||||
symbol_text(checker, expr.name) == "ptr_cast"
|
symbol_text(checker, expr.name) == "ptrcast"
|
||||||
}
|
}
|
||||||
|
|
||||||
Type_Builtin :: enum u8 {
|
Type_Builtin :: enum u8 {
|
||||||
@@ -426,14 +427,14 @@ Division_Builtin :: enum u8 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
division_builtin_call :: proc(checker: ^Checker, expr: ast.Expr) -> Division_Builtin {
|
division_builtin_call :: proc(checker: ^Checker, expr: ast.Expr) -> Division_Builtin {
|
||||||
if expr.kind != .Call || expr.left != ast.INVALID_EXPR || symbol.is_valid(expr.qualifier) {
|
if !expr.intrinsic || expr.kind != .Call || expr.left != ast.INVALID_EXPR || symbol.is_valid(expr.qualifier) {
|
||||||
return .None
|
return .None
|
||||||
}
|
}
|
||||||
switch symbol_text(checker, expr.name) {
|
switch symbol_text(checker, expr.name) {
|
||||||
case "div_trunc": return .Trunc
|
case "divtrunc": return .Trunc
|
||||||
case "div_floor": return .Floor
|
case "divfloor": return .Floor
|
||||||
case "div_exact": return .Exact
|
case "divexact": return .Exact
|
||||||
case "div_ceil": return .Ceil
|
case "divceil": return .Ceil
|
||||||
case "rem": return .Rem
|
case "rem": return .Rem
|
||||||
case "mod": return .Mod
|
case "mod": return .Mod
|
||||||
}
|
}
|
||||||
@@ -441,26 +442,26 @@ division_builtin_call :: proc(checker: ^Checker, expr: ast.Expr) -> Division_Bui
|
|||||||
}
|
}
|
||||||
|
|
||||||
type_builtin_call :: proc(checker: ^Checker, expr: ast.Expr) -> Type_Builtin {
|
type_builtin_call :: proc(checker: ^Checker, expr: ast.Expr) -> Type_Builtin {
|
||||||
if expr.kind != .Call || expr.left != ast.INVALID_EXPR || symbol.is_valid(expr.qualifier) {
|
if !expr.intrinsic || expr.kind != .Call || expr.left != ast.INVALID_EXPR || symbol.is_valid(expr.qualifier) {
|
||||||
return .None
|
return .None
|
||||||
}
|
}
|
||||||
name := symbol_text(checker, expr.name)
|
name := symbol_text(checker, expr.name)
|
||||||
if name == "size_of" {
|
if name == "sizeof" {
|
||||||
return .Size_Of
|
return .Size_Of
|
||||||
}
|
}
|
||||||
if name == "align_of" {
|
if name == "alignof" {
|
||||||
return .Align_Of
|
return .Align_Of
|
||||||
}
|
}
|
||||||
if name == "min_value" {
|
if name == "minval" {
|
||||||
return .Min_Value
|
return .Min_Value
|
||||||
}
|
}
|
||||||
if name == "max_value" {
|
if name == "maxval" {
|
||||||
return .Max_Value
|
return .Max_Value
|
||||||
}
|
}
|
||||||
return .None
|
return .None
|
||||||
}
|
}
|
||||||
|
|
||||||
valid_ptr_cast_child :: proc(checker: ^Checker, value: types.Type) -> bool {
|
valid_ptrcast_child :: proc(checker: ^Checker, value: types.Type) -> bool {
|
||||||
return types.is_valid(value) &&
|
return types.is_valid(value) &&
|
||||||
!types.is_void(value) &&
|
!types.is_void(value) &&
|
||||||
!types.is_anyopaque(value) &&
|
!types.is_anyopaque(value) &&
|
||||||
@@ -501,7 +502,7 @@ build_type_builtin :: proc(
|
|||||||
file: ast.File_Id,
|
file: ast.File_Id,
|
||||||
) -> hir.Expr_Id {
|
) -> hir.Expr_Id {
|
||||||
if len(expr.args) != 1 {
|
if len(expr.args) != 1 {
|
||||||
id := source.addf(checker.diagnostics, expr.span, "%s expects 1 argument, got %d", symbol_text(checker, expr.name), len(expr.args))
|
id := source.addf(checker.diagnostics, expr.span, "%s! expects 1 argument, got %d", symbol_text(checker, expr.name), len(expr.args))
|
||||||
return invalid_hir_expr(checker, expr.span, id, types.USIZE)
|
return invalid_hir_expr(checker, expr.span, id, types.USIZE)
|
||||||
}
|
}
|
||||||
target, target_ok := resolve_type_argument(checker, expr.args[0], pkg, file)
|
target, target_ok := resolve_type_argument(checker, expr.args[0], pkg, file)
|
||||||
@@ -640,7 +641,7 @@ type_from_syntax :: proc(
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if constant.kind == .Integer_Division {
|
if constant.kind == .Integer_Division {
|
||||||
source.add(checker.diagnostics, span, "integer '/' is not allowed; use div_trunc, div_floor, div_exact, or div_ceil")
|
source.add(checker.diagnostics, span, "integer '/' is not allowed; use divtrunc!, divfloor!, divexact!, or divceil!")
|
||||||
return types.INVALID
|
return types.INVALID
|
||||||
}
|
}
|
||||||
source.add(checker.diagnostics, span, "array count must be a compile-time integer expression")
|
source.add(checker.diagnostics, span, "array count must be a compile-time integer expression")
|
||||||
@@ -3365,7 +3366,7 @@ infer_expr :: proc(
|
|||||||
_ = pop(&stack)
|
_ = pop(&stack)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if is_ptr_cast_call(checker, expr) {
|
if is_ptrcast_call(checker, expr) {
|
||||||
if len(expr.args) != 2 {
|
if len(expr.args) != 2 {
|
||||||
last = types.INVALID
|
last = types.INVALID
|
||||||
_ = pop(&stack)
|
_ = pop(&stack)
|
||||||
@@ -3374,13 +3375,18 @@ infer_expr :: proc(
|
|||||||
child, child_ok := resolve_type_argument(checker, expr.args[0], pkg, file)
|
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)
|
operand := infer_nested_expr(checker, expr.args[1], locals, pkg, file, demanded, local_types)
|
||||||
result := types.INVALID
|
result := types.INVALID
|
||||||
if child_ok && valid_ptr_cast_child(checker, child) {
|
if child_ok && valid_ptrcast_child(checker, child) {
|
||||||
result, _ = types.replace_pointer_child(&checker.module.types, operand, child)
|
result, _ = types.replace_pointer_child(&checker.module.types, operand, child)
|
||||||
}
|
}
|
||||||
last = result
|
last = result
|
||||||
_ = pop(&stack)
|
_ = pop(&stack)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if expr.intrinsic {
|
||||||
|
last = types.INVALID
|
||||||
|
_ = pop(&stack)
|
||||||
|
continue
|
||||||
|
}
|
||||||
if callee_type, handled := infer_qualified_value_field_type(checker, expr, locals, pkg, file); handled {
|
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)
|
_, function_item, function_type, ok := types.function_pointer(callee_type, &checker.module.types)
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -4957,7 +4963,7 @@ build_constant_expr :: proc(
|
|||||||
return invalid_hir_expr(checker, expr.span, id, recovery_type)
|
return invalid_hir_expr(checker, expr.span, id, recovery_type)
|
||||||
}
|
}
|
||||||
if constant.kind == .Integer_Division {
|
if constant.kind == .Integer_Division {
|
||||||
id := source.add(checker.diagnostics, expr.span, "integer '/' is not allowed; use div_trunc, div_floor, div_exact, or div_ceil")
|
id := source.add(checker.diagnostics, expr.span, "integer '/' is not allowed; use divtrunc!, divfloor!, divexact!, or divceil!")
|
||||||
return invalid_hir_expr(checker, expr.span, id, recovery_type)
|
return invalid_hir_expr(checker, expr.span, id, recovery_type)
|
||||||
}
|
}
|
||||||
if constant.kind == .Overflow ||
|
if constant.kind == .Overflow ||
|
||||||
@@ -5447,7 +5453,7 @@ build_division_builtin :: proc(
|
|||||||
) -> hir.Expr_Id {
|
) -> hir.Expr_Id {
|
||||||
if len(expr.args) != 2 {
|
if len(expr.args) != 2 {
|
||||||
id := source.addf(
|
id := source.addf(
|
||||||
checker.diagnostics, expr.span, "%s expects 2 arguments, got %d",
|
checker.diagnostics, expr.span, "%s! expects 2 arguments, got %d",
|
||||||
symbol_text(checker, expr.name), len(expr.args),
|
symbol_text(checker, expr.name), len(expr.args),
|
||||||
)
|
)
|
||||||
return invalid_hir_expr(checker, expr.span, id)
|
return invalid_hir_expr(checker, expr.span, id)
|
||||||
@@ -6223,7 +6229,7 @@ build_binary_arith :: proc(
|
|||||||
if op == .Div && !types.is_float(result, checker.target) {
|
if op == .Div && !types.is_float(result, checker.target) {
|
||||||
id := source.add(
|
id := source.add(
|
||||||
checker.diagnostics, span,
|
checker.diagnostics, span,
|
||||||
"integer '/' is not allowed; use div_trunc, div_floor, div_exact, or div_ceil",
|
"integer '/' is not allowed; use divtrunc!, divfloor!, divexact!, or divceil!",
|
||||||
)
|
)
|
||||||
return invalid_hir_expr(checker, span, id, result)
|
return invalid_hir_expr(checker, span, id, result)
|
||||||
}
|
}
|
||||||
@@ -6463,22 +6469,22 @@ build_expr :: proc(
|
|||||||
_ = pop(&stack)
|
_ = pop(&stack)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if is_ptr_cast_call(checker, expr) {
|
if is_ptrcast_call(checker, expr) {
|
||||||
if len(expr.args) != 2 {
|
if len(expr.args) != 2 {
|
||||||
id := source.addf(checker.diagnostics, expr.span, "ptr_cast expects 2 arguments, got %d", len(expr.args))
|
id := source.addf(checker.diagnostics, expr.span, "ptrcast! expects 2 arguments, got %d", len(expr.args))
|
||||||
last = invalid_hir_expr(checker, expr.span, id)
|
last = invalid_hir_expr(checker, expr.span, id)
|
||||||
_ = pop(&stack)
|
_ = pop(&stack)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
target, target_ok := resolve_type_argument(checker, expr.args[0], pkg, file)
|
target, target_ok := resolve_type_argument(checker, expr.args[0], pkg, file)
|
||||||
if !target_ok {
|
if !target_ok {
|
||||||
id := source.add(checker.diagnostics, checker.ast_module.exprs[expr.args[0]].span, "ptr_cast target must be a type")
|
id := source.add(checker.diagnostics, checker.ast_module.exprs[expr.args[0]].span, "ptrcast! target must be a type")
|
||||||
last = invalid_hir_expr(checker, expr.span, id)
|
last = invalid_hir_expr(checker, expr.span, id)
|
||||||
_ = pop(&stack)
|
_ = pop(&stack)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if !valid_ptr_cast_child(checker, target) {
|
if !valid_ptrcast_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))
|
id := source.addf(checker.diagnostics, checker.ast_module.exprs[expr.args[0]].span, "ptrcast! target must be a sized runtime object type, got %s", type_label(checker, target))
|
||||||
last = invalid_hir_expr(checker, expr.span, id)
|
last = invalid_hir_expr(checker, expr.span, id)
|
||||||
_ = pop(&stack)
|
_ = pop(&stack)
|
||||||
continue
|
continue
|
||||||
@@ -6488,6 +6494,17 @@ build_expr :: proc(
|
|||||||
append(&stack, Build_Expr_Frame{expr=expr.args[1], expected=types.INVALID, template=ast.INVALID_FUNCTION})
|
append(&stack, Build_Expr_Frame{expr=expr.args[1], expected=types.INVALID, template=ast.INVALID_FUNCTION})
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if expr.intrinsic {
|
||||||
|
id := source.INVALID_DIAGNOSTIC
|
||||||
|
if symbol.is_valid(expr.qualifier) {
|
||||||
|
id = source.add(checker.diagnostics, expr.span, "intrinsic calls must be unqualified")
|
||||||
|
} else {
|
||||||
|
id = source.addf(checker.diagnostics, expr.span, "unknown intrinsic '%s!'", symbol_text(checker, expr.name))
|
||||||
|
}
|
||||||
|
last = invalid_hir_expr(checker, expr.span, id)
|
||||||
|
_ = pop(&stack)
|
||||||
|
continue
|
||||||
|
}
|
||||||
if callee, handled, ok := build_qualified_value_field(checker, expr, locals, global_reads, pkg, file); handled {
|
if callee, handled, ok := build_qualified_value_field(checker, expr, locals, global_reads, pkg, file); handled {
|
||||||
if !ok {
|
if !ok {
|
||||||
last = callee
|
last = callee
|
||||||
@@ -6996,7 +7013,7 @@ build_expr :: proc(
|
|||||||
if frame.stage == 9 {
|
if frame.stage == 9 {
|
||||||
result, ok := types.replace_pointer_child(&checker.module.types, checker.module.exprs[last].type, frame.target_type)
|
result, ok := types.replace_pointer_child(&checker.module.types, checker.module.exprs[last].type, frame.target_type)
|
||||||
if !ok {
|
if !ok {
|
||||||
id := source.add(checker.diagnostics, expr.span, "ptr_cast operand must be a pointer or optional pointer")
|
id := source.add(checker.diagnostics, expr.span, "ptrcast! operand must be a pointer or optional pointer")
|
||||||
last = invalid_hir_expr(checker, expr.span, id)
|
last = invalid_hir_expr(checker, expr.span, id)
|
||||||
} else {
|
} else {
|
||||||
last = add_hir_expr(checker, hir.Expr{
|
last = add_hir_expr(checker, hir.Expr{
|
||||||
|
|||||||
@@ -1862,7 +1862,7 @@ ct_eval_binary :: proc(state: ^Ct_State, op: ast.Expr_Kind, left_id, right_id: C
|
|||||||
if op == .Div {
|
if op == .Div {
|
||||||
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(
|
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(
|
||||||
state, .Integer_Division, span,
|
state, .Integer_Division, span,
|
||||||
"integer '/' is not allowed; use div_trunc, div_floor, div_exact, or div_ceil",
|
"integer '/' is not allowed; use divtrunc!, divfloor!, divexact!, or divceil!",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
value: i128
|
value: i128
|
||||||
@@ -2059,7 +2059,7 @@ ct_eval_call_expr :: proc(state: ^Ct_State, expr: ast.Expr, expected: types.Type
|
|||||||
}
|
}
|
||||||
if builtin := type_builtin_call(checker, expr); builtin != .None {
|
if builtin := type_builtin_call(checker, expr); builtin != .None {
|
||||||
if len(expr.args) != 1 {
|
if len(expr.args) != 1 {
|
||||||
return INVALID_CT_VALUE, ct_flow(.Normal), ct_failf(state, .Not_Comptime, expr.span, "%s expects 1 argument, got %d", symbol_text(checker, expr.name), len(expr.args))
|
return INVALID_CT_VALUE, ct_flow(.Normal), ct_failf(state, .Not_Comptime, expr.span, "%s! expects 1 argument, got %d", symbol_text(checker, expr.name), len(expr.args))
|
||||||
}
|
}
|
||||||
target, target_ok := resolve_type_argument(checker, expr.args[0], state.pkg, state.file)
|
target, target_ok := resolve_type_argument(checker, expr.args[0], state.pkg, state.file)
|
||||||
if !target_ok {
|
if !target_ok {
|
||||||
@@ -2078,6 +2078,12 @@ ct_eval_call_expr :: proc(state: ^Ct_State, expr: ast.Expr, expected: types.Type
|
|||||||
if builtin := division_builtin_call(checker, expr); builtin != .None {
|
if builtin := division_builtin_call(checker, expr); builtin != .None {
|
||||||
return ct_eval_division_call(state, expr, builtin, expected, depth+1)
|
return ct_eval_division_call(state, expr, builtin, expected, depth+1)
|
||||||
}
|
}
|
||||||
|
if expr.intrinsic {
|
||||||
|
if symbol.is_valid(expr.qualifier) {
|
||||||
|
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, expr.span, "intrinsic calls must be unqualified")
|
||||||
|
}
|
||||||
|
return INVALID_CT_VALUE, ct_flow(.Normal), ct_failf(state, .Not_Comptime, expr.span, "unknown intrinsic '%s!'", symbol_text(checker, expr.name))
|
||||||
|
}
|
||||||
target_pkg, available := expr_package(checker, expr, state.pkg, state.file, false)
|
target_pkg, available := expr_package(checker, expr, state.pkg, state.file, false)
|
||||||
if !available {
|
if !available {
|
||||||
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, expr.span, "unavailable function package")
|
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, expr.span, "unavailable function package")
|
||||||
|
|||||||
@@ -399,7 +399,7 @@ parse_type_atom :: proc(parser: ^Parser) -> ast.Type_Syntax {
|
|||||||
!symbol.is_valid(qualifier) && file_hidden_name(parser, name),
|
!symbol.is_valid(qualifier) && file_hidden_name(parser, name),
|
||||||
)
|
)
|
||||||
if current(parser).kind == .Left_Paren {
|
if current(parser).kind == .Left_Paren {
|
||||||
call := parse_call(parser, qualifier, first, name, 0)
|
call := parse_call(parser, qualifier, first, name, 0, false)
|
||||||
return types.intern(&parser.module.type_store, types.Node{
|
return types.intern(&parser.module.type_store, types.Node{
|
||||||
kind=.Type_Call,
|
kind=.Type_Call,
|
||||||
count_expr=u32(call),
|
count_expr=u32(call),
|
||||||
@@ -490,7 +490,7 @@ parse_call_args :: proc(parser: ^Parser, nesting: int) -> ([]ast.Expr_Id, token.
|
|||||||
return args[:], right_paren
|
return args[:], right_paren
|
||||||
}
|
}
|
||||||
|
|
||||||
parse_call :: proc(parser: ^Parser, qualifier: symbol.Id, first, name: token.Token, nesting: int) -> ast.Expr_Id {
|
parse_call :: proc(parser: ^Parser, qualifier: symbol.Id, first, name: token.Token, nesting: int, intrinsic: bool) -> ast.Expr_Id {
|
||||||
if nesting >= MAX_EXPRESSION_NESTING {
|
if nesting >= MAX_EXPRESSION_NESTING {
|
||||||
span := skip_parenthesized(parser)
|
span := skip_parenthesized(parser)
|
||||||
return invalid_expr(parser, span, "expression nesting exceeds 256 levels")
|
return invalid_expr(parser, span, "expression nesting exceeds 256 levels")
|
||||||
@@ -502,6 +502,7 @@ parse_call :: proc(parser: ^Parser, qualifier: symbol.Id, first, name: token.Tok
|
|||||||
qualifier=qualifier,
|
qualifier=qualifier,
|
||||||
name=name.symbol,
|
name=name.symbol,
|
||||||
args=args[:],
|
args=args[:],
|
||||||
|
intrinsic=intrinsic,
|
||||||
left=ast.INVALID_EXPR,
|
left=ast.INVALID_EXPR,
|
||||||
right=ast.INVALID_EXPR,
|
right=ast.INVALID_EXPR,
|
||||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
@@ -885,9 +886,10 @@ parse_primary :: proc(parser: ^Parser, nesting: int) -> ast.Expr_Id {
|
|||||||
qualifier = first.symbol
|
qualifier = first.symbol
|
||||||
name = member
|
name = member
|
||||||
}
|
}
|
||||||
|
_, intrinsic := allow(parser, .Bang)
|
||||||
if current(parser).kind == .Left_Paren {
|
if current(parser).kind == .Left_Paren {
|
||||||
call := parse_call(parser, qualifier, first, name, nesting)
|
call := parse_call(parser, qualifier, first, name, nesting, intrinsic)
|
||||||
if current(parser).kind == .Left_Brace && !(parser.no_struct_literal && parser.delimiter_depth == 0) {
|
if !intrinsic && current(parser).kind == .Left_Brace && !(parser.no_struct_literal && parser.delimiter_depth == 0) {
|
||||||
left_brace := advance(parser)
|
left_brace := advance(parser)
|
||||||
args, right_brace := parse_keyed_initializers(parser, left_brace, nesting, "expected '}' after struct literal")
|
args, right_brace := parse_keyed_initializers(parser, left_brace, nesting, "expected '}' after struct literal")
|
||||||
return add_expr(parser, ast.Expr{
|
return add_expr(parser, ast.Expr{
|
||||||
@@ -901,6 +903,9 @@ parse_primary :: proc(parser: ^Parser, nesting: int) -> ast.Expr_Id {
|
|||||||
}
|
}
|
||||||
return call
|
return call
|
||||||
}
|
}
|
||||||
|
if intrinsic {
|
||||||
|
return invalid_expr(parser, previous(parser).span, "expected '(' after intrinsic name")
|
||||||
|
}
|
||||||
if current(parser).kind == .Left_Brace && !(parser.no_struct_literal && parser.delimiter_depth == 0) {
|
if current(parser).kind == .Left_Brace && !(parser.no_struct_literal && parser.delimiter_depth == 0) {
|
||||||
return parse_struct_literal(parser, qualifier, first, name, nesting)
|
return parse_struct_literal(parser, qualifier, first, name, nesting)
|
||||||
}
|
}
|
||||||
@@ -1156,6 +1161,21 @@ parse_expression_bp :: proc(parser: ^Parser, minimum_binding_power, nesting: int
|
|||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if current(parser).kind == .Bang {
|
||||||
|
marker := advance(parser)
|
||||||
|
if current(parser).kind != .Left_Paren {
|
||||||
|
left = invalid_expr(parser, marker.span, "expected '(' after '!'")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
left_expr := parser.module.exprs[left]
|
||||||
|
call_span := skip_parenthesized(parser)
|
||||||
|
left = invalid_expr(
|
||||||
|
parser,
|
||||||
|
span_from(left_expr.span, call_span),
|
||||||
|
"intrinsic calls require a direct name",
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
}
|
||||||
if current(parser).kind == .Left_Paren {
|
if current(parser).kind == .Left_Paren {
|
||||||
if nesting >= MAX_EXPRESSION_NESTING {
|
if nesting >= MAX_EXPRESSION_NESTING {
|
||||||
span := skip_parenthesized(parser)
|
span := skip_parenthesized(parser)
|
||||||
|
|||||||
+264
-106
@@ -86,6 +86,32 @@ main func() void { _ = value }
|
|||||||
testing.expect_value(t, module.statements[module.functions[0].body[0]].name, sink_symbol)
|
testing.expect_value(t, module.statements[module.functions[0].body[0]].name, sink_symbol)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(test)
|
||||||
|
parser_marks_only_bang_calls_as_intrinsic :: proc(t: ^testing.T) {
|
||||||
|
text := `main func() void {
|
||||||
|
_ = sizeof ! (i32)
|
||||||
|
_ = sizeof(i32)
|
||||||
|
}
|
||||||
|
`
|
||||||
|
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)
|
||||||
|
module := parser.parse(&stream, &source_file, &diagnostics)
|
||||||
|
defer ast.destroy_module(&module)
|
||||||
|
|
||||||
|
marked := module.exprs[module.statements[module.functions[0].body[0]].expr]
|
||||||
|
ordinary := module.exprs[module.statements[module.functions[0].body[1]].expr]
|
||||||
|
testing.expect_value(t, len(diagnostics.items), 0)
|
||||||
|
testing.expect_value(t, marked.kind, ast.Expr_Kind.Call)
|
||||||
|
testing.expect_value(t, ordinary.kind, ast.Expr_Kind.Call)
|
||||||
|
testing.expect(t, marked.intrinsic)
|
||||||
|
testing.expect(t, !ordinary.intrinsic)
|
||||||
|
}
|
||||||
|
|
||||||
@(test)
|
@(test)
|
||||||
compact_ids_reserve_invalid_values_and_preserve_layout :: proc(t: ^testing.T) {
|
compact_ids_reserve_invalid_values_and_preserve_layout :: proc(t: ^testing.T) {
|
||||||
testing.expect_value(t, size_of(source.Span), 12)
|
testing.expect_value(t, size_of(source.Span), 12)
|
||||||
@@ -1372,14 +1398,14 @@ Color :: enum {
|
|||||||
blue
|
blue
|
||||||
}
|
}
|
||||||
UserID :: distinct u32
|
UserID :: distinct u32
|
||||||
SIZE_GLOBAL :: size_of(i32)
|
SIZE_GLOBAL :: sizeof!(i32)
|
||||||
|
|
||||||
needs_usize func(value usize) usize {
|
needs_usize func(value usize) usize {
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer func($T type) [size_of(T)]u8 {
|
buffer func($T type) [sizeof!(T)]u8 {
|
||||||
data [size_of(T)]u8 = undefined
|
data [sizeof!(T)]u8 = undefined
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1387,18 +1413,18 @@ main func() i32 {
|
|||||||
bytes [_]u8 :: buffer(i32)
|
bytes [_]u8 :: buffer(i32)
|
||||||
if (needs_usize(SIZE_GLOBAL) != 4) return 1
|
if (needs_usize(SIZE_GLOBAL) != 4) return 1
|
||||||
if (bytes.len != 4) return 2
|
if (bytes.len != 4) return 2
|
||||||
if (size_of([3]u8) != 3) return 3
|
if (sizeof!([3]u8) != 3) return 3
|
||||||
if (size_of([]u8) != 16) return 4
|
if (sizeof!([]u8) != 16) return 4
|
||||||
if (align_of([]u8) != 8) return 5
|
if (alignof!([]u8) != 8) return 5
|
||||||
if (size_of(*anyopaque) != 8) return 6
|
if (sizeof!(*anyopaque) != 8) return 6
|
||||||
if (size_of(?*i32) != 8) return 7
|
if (sizeof!(?*i32) != 8) return 7
|
||||||
if (size_of(*Opaque) != 8) return 8
|
if (sizeof!(*Opaque) != 8) return 8
|
||||||
if (size_of(Color) != 2) return 9
|
if (sizeof!(Color) != 2) return 9
|
||||||
if (align_of(Color) != 2) return 10
|
if (alignof!(Color) != 2) return 10
|
||||||
if (size_of(Point) != 8) return 11
|
if (sizeof!(Point) != 8) return 11
|
||||||
if (align_of(Point) != 4) return 12
|
if (alignof!(Point) != 4) return 12
|
||||||
if (size_of(UserID) != 4) return 13
|
if (sizeof!(UserID) != 4) return 13
|
||||||
if (align_of(UserID) != 4) return 14
|
if (alignof!(UserID) != 4) return 14
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
@@ -1418,23 +1444,23 @@ integer_bound_builtins_compile_and_run :: proc(t: ^testing.T) {
|
|||||||
directory := "/tmp/brolang-test-integer-bounds"
|
directory := "/tmp/brolang-test-integer-bounds"
|
||||||
main_path := "/tmp/brolang-test-integer-bounds/main.bro"
|
main_path := "/tmp/brolang-test-integer-bounds/main.bro"
|
||||||
output := "/tmp/brolang-test-integer-bounds-output"
|
output := "/tmp/brolang-test-integer-bounds-output"
|
||||||
text := `MAX_U64 u64 :: max_value(u64)
|
text := `MAX_U64 u64 :: maxval!(u64)
|
||||||
|
|
||||||
maximum func($T type) T {
|
maximum func($T type) T {
|
||||||
return max_value(T)
|
return maxval!(T)
|
||||||
}
|
}
|
||||||
|
|
||||||
main func() i32 {
|
main func() i32 {
|
||||||
if (min_value(i8) != -128) return 1
|
if (minval!(i8) != -128) return 1
|
||||||
if (max_value(i8) != 127) return 2
|
if (maxval!(i8) != 127) return 2
|
||||||
if (min_value(u8) != 0) return 3
|
if (minval!(u8) != 0) return 3
|
||||||
if (max_value(u8) != 255) return 4
|
if (maxval!(u8) != 255) return 4
|
||||||
if (min_value(isize) != -9223372036854775808) return 5
|
if (minval!(isize) != -9223372036854775808) return 5
|
||||||
if (max_value(usize) != 18446744073709551615) return 6
|
if (maxval!(usize) != 18446744073709551615) return 6
|
||||||
if (MAX_U64 != 18446744073709551615) return 7
|
if (MAX_U64 != 18446744073709551615) return 7
|
||||||
if (maximum(u16) != 65535) return 8
|
if (maximum(u16) != 65535) return 8
|
||||||
if (min_value(c_int) != -2147483648) return 9
|
if (minval!(c_int) != -2147483648) return 9
|
||||||
if (max_value(c_ulong) != 18446744073709551615) return 10
|
if (maxval!(c_ulong) != 18446744073709551615) return 10
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
@@ -1455,14 +1481,14 @@ integer_bound_builtins_reject_invalid_targets :: proc(t: ^testing.T) {
|
|||||||
Choice :: enum { one }
|
Choice :: enum { one }
|
||||||
|
|
||||||
main func() void {
|
main func() void {
|
||||||
_ = min_value()
|
_ = minval!()
|
||||||
_ = max_value(u8, u16)
|
_ = maxval!(u8, u16)
|
||||||
_ = min_value(1)
|
_ = minval!(1)
|
||||||
_ = max_value(int)
|
_ = maxval!(int)
|
||||||
_ = max_value(f32)
|
_ = maxval!(f32)
|
||||||
_ = max_value(bool)
|
_ = maxval!(bool)
|
||||||
_ = max_value(Named)
|
_ = maxval!(Named)
|
||||||
_ = max_value(Choice)
|
_ = maxval!(Choice)
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
source_file := source.Source{path="test.bro", text=text}
|
source_file := source.Source{path="test.bro", text=text}
|
||||||
@@ -1496,11 +1522,11 @@ layout_builtins_reject_unsized_targets :: proc(t: ^testing.T) {
|
|||||||
Fn :: alias func() void
|
Fn :: alias func() void
|
||||||
|
|
||||||
main func() void {
|
main func() void {
|
||||||
_ = size_of(void)
|
_ = sizeof!(void)
|
||||||
_ = align_of(anyopaque)
|
_ = alignof!(anyopaque)
|
||||||
_ = size_of(Fn)
|
_ = sizeof!(Fn)
|
||||||
_ = size_of(Opaque)
|
_ = sizeof!(Opaque)
|
||||||
_ = align_of(1)
|
_ = alignof!(1)
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
source_file := source.Source{path="test.bro", text=text}
|
source_file := source.Source{path="test.bro", text=text}
|
||||||
@@ -1803,14 +1829,14 @@ main func() void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@(test)
|
@(test)
|
||||||
opaque_anyopaque_and_ptr_cast_compile_and_lower :: proc(t: ^testing.T) {
|
opaque_anyopaque_and_ptrcast_compile_and_lower :: proc(t: ^testing.T) {
|
||||||
text := `Handle :: opaque
|
text := `Handle :: opaque
|
||||||
take func(_ ?*mut anyopaque) void {}
|
take func(_ ?*mut anyopaque) void {}
|
||||||
use_handle func(_ ?@mut Handle) void {}
|
use_handle func(_ ?@mut Handle) void {}
|
||||||
main func() void {
|
main func() void {
|
||||||
values [2]mut u8 = [1, 2]
|
values [2]mut u8 = [1, 2]
|
||||||
raw ?*mut anyopaque = (&values).ptr
|
raw ?*mut anyopaque = (&values).ptr
|
||||||
bytes ?*mut u8 = ptr_cast(u8, raw)
|
bytes ?*mut u8 = ptrcast!(u8, raw)
|
||||||
take(bytes)
|
take(bytes)
|
||||||
if bytes |p| {
|
if bytes |p| {
|
||||||
p[1] = 5
|
p[1] = 5
|
||||||
@@ -1818,7 +1844,7 @@ main func() void {
|
|||||||
|
|
||||||
one u8 = 1
|
one u8 = 1
|
||||||
single ?@mut anyopaque = &one
|
single ?@mut anyopaque = &one
|
||||||
typed ?@mut u8 = ptr_cast(u8, single)
|
typed ?@mut u8 = ptrcast!(u8, single)
|
||||||
if typed |p| {
|
if typed |p| {
|
||||||
p^ = 2
|
p^ = 2
|
||||||
}
|
}
|
||||||
@@ -1858,16 +1884,16 @@ main func() void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@(test)
|
@(test)
|
||||||
anyopaque_by_value_and_invalid_ptr_casts_are_rejected :: proc(t: ^testing.T) {
|
anyopaque_by_value_and_invalid_ptrcasts_are_rejected :: proc(t: ^testing.T) {
|
||||||
text := `Callback :: alias c_func() void
|
text := `Callback :: alias c_func() void
|
||||||
main func() void {
|
main func() void {
|
||||||
raw ?*mut anyopaque = none
|
raw ?*mut anyopaque = none
|
||||||
value anyopaque = undefined
|
value anyopaque = undefined
|
||||||
_ = ptr_cast(void, raw)
|
_ = ptrcast!(void, raw)
|
||||||
_ = ptr_cast(anyopaque, raw)
|
_ = ptrcast!(anyopaque, raw)
|
||||||
_ = ptr_cast(Callback, raw)
|
_ = ptrcast!(Callback, raw)
|
||||||
_ = ptr_cast(u8, 1)
|
_ = ptrcast!(u8, 1)
|
||||||
_ = ptr_cast(1, raw)
|
_ = ptrcast!(1, raw)
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
source_file := source.Source{path="test.bro", text=text}
|
source_file := source.Source{path="test.bro", text=text}
|
||||||
@@ -1888,9 +1914,9 @@ main func() void {
|
|||||||
found_target_type := false
|
found_target_type := false
|
||||||
for diagnostic in diagnostics.items {
|
for diagnostic in diagnostics.items {
|
||||||
found_by_value = found_by_value || strings.contains(diagnostic.message, "could not infer a concrete type")
|
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_target = found_bad_target || strings.contains(diagnostic.message, "ptrcast! 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_bad_operand = found_bad_operand || strings.contains(diagnostic.message, "ptrcast! operand must be a pointer")
|
||||||
found_target_type = found_target_type || strings.contains(diagnostic.message, "ptr_cast target must be a type")
|
found_target_type = found_target_type || strings.contains(diagnostic.message, "ptrcast! target must be a type")
|
||||||
}
|
}
|
||||||
testing.expect(t, found_by_value)
|
testing.expect(t, found_by_value)
|
||||||
testing.expect(t, found_bad_target)
|
testing.expect(t, found_bad_target)
|
||||||
@@ -1898,6 +1924,138 @@ main func() void {
|
|||||||
testing.expect(t, found_target_type)
|
testing.expect(t, found_target_type)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(test)
|
||||||
|
old_intrinsic_spellings_are_not_recognized :: proc(t: ^testing.T) {
|
||||||
|
text := `main func() void {
|
||||||
|
_ = ptr_cast(1, 1)
|
||||||
|
_ = size_of(1)
|
||||||
|
_ = align_of(1)
|
||||||
|
_ = min_value(1)
|
||||||
|
_ = max_value(1)
|
||||||
|
_ = div_trunc(1, 1)
|
||||||
|
_ = div_floor(1, 1)
|
||||||
|
_ = div_exact(1, 1)
|
||||||
|
_ = div_ceil(1, 1)
|
||||||
|
}
|
||||||
|
`
|
||||||
|
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)
|
||||||
|
|
||||||
|
names := [?]string{
|
||||||
|
"ptr_cast", "size_of", "align_of", "min_value", "max_value",
|
||||||
|
"div_trunc", "div_floor", "div_exact", "div_ceil",
|
||||||
|
}
|
||||||
|
found := [len(names)]bool{}
|
||||||
|
for diagnostic in diagnostics.items {
|
||||||
|
if !strings.contains(diagnostic.message, "unresolved function") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for name, index in names {
|
||||||
|
found[index] = found[index] || strings.contains(diagnostic.message, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for value in found {
|
||||||
|
testing.expect(t, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@(test)
|
||||||
|
bare_intrinsic_names_are_available_to_user_functions :: proc(t: ^testing.T) {
|
||||||
|
text := `ptrcast func() i32 { return 1 }
|
||||||
|
sizeof func() i32 { return 2 }
|
||||||
|
alignof func() i32 { return 3 }
|
||||||
|
minval func() i32 { return 4 }
|
||||||
|
maxval func() i32 { return 5 }
|
||||||
|
divtrunc func() i32 { return 6 }
|
||||||
|
divfloor func() i32 { return 7 }
|
||||||
|
divexact func() i32 { return 8 }
|
||||||
|
divceil func() i32 { return 9 }
|
||||||
|
rem func() i32 { return 10 }
|
||||||
|
mod func() i32 { return 11 }
|
||||||
|
main func() i32 {
|
||||||
|
return ptrcast() + sizeof() + alignof() + minval() + maxval() +
|
||||||
|
divtrunc() + divfloor() + divexact() + divceil() + rem() + mod() - 66
|
||||||
|
}
|
||||||
|
`
|
||||||
|
directory := "/tmp/brolang-test-user-intrinsic-names"
|
||||||
|
main_path := "/tmp/brolang-test-user-intrinsic-names/main.bro"
|
||||||
|
output := "/tmp/brolang-test-user-intrinsic-names-output"
|
||||||
|
_ = os2.remove_all(directory)
|
||||||
|
defer _ = os2.remove_all(directory)
|
||||||
|
defer _ = os.remove(output)
|
||||||
|
testing.expect(t, os.make_directory(directory) == nil)
|
||||||
|
testing.expect(t, os.write_entire_file(main_path, transmute([]byte)text))
|
||||||
|
testing.expect_value(t, compiler_core.compile_package(directory, output), 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(test)
|
||||||
|
intrinsic_call_diagnostics_are_precise :: proc(t: ^testing.T) {
|
||||||
|
text := `main func() void {
|
||||||
|
_ = mystery!()
|
||||||
|
_ = math.ptrcast!()
|
||||||
|
}
|
||||||
|
`
|
||||||
|
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_unknown := false
|
||||||
|
found_qualified := false
|
||||||
|
for diagnostic in diagnostics.items {
|
||||||
|
found_unknown = found_unknown || diagnostic.message == "unknown intrinsic 'mystery!'"
|
||||||
|
found_qualified = found_qualified || diagnostic.message == "intrinsic calls must be unqualified"
|
||||||
|
}
|
||||||
|
testing.expect(t, found_unknown)
|
||||||
|
testing.expect(t, found_qualified)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(test)
|
||||||
|
malformed_intrinsic_calls_have_targeted_parse_diagnostics :: proc(t: ^testing.T) {
|
||||||
|
cases := [?]struct {
|
||||||
|
text, message: string,
|
||||||
|
}{
|
||||||
|
{`main func() void { _ = sizeof! }`, "expected '(' after intrinsic name"},
|
||||||
|
{`callback func() void {}
|
||||||
|
main func() void { (callback)!() }
|
||||||
|
`, "intrinsic calls require a direct name"},
|
||||||
|
}
|
||||||
|
for test_case in cases {
|
||||||
|
source_file := source.Source{path="test.bro", text=test_case.text}
|
||||||
|
diagnostics := source.init_diagnostics(&source_file)
|
||||||
|
symbols := symbol.init_table()
|
||||||
|
stream := lexer.lex(&source_file, &diagnostics, &symbols)
|
||||||
|
module := parser.parse(&stream, &source_file, &diagnostics)
|
||||||
|
|
||||||
|
found := false
|
||||||
|
for diagnostic in diagnostics.items {
|
||||||
|
found = found || diagnostic.message == test_case.message
|
||||||
|
}
|
||||||
|
testing.expect(t, found)
|
||||||
|
|
||||||
|
ast.destroy_module(&module)
|
||||||
|
delete(stream.items)
|
||||||
|
symbol.destroy_table(&symbols)
|
||||||
|
source.destroy_diagnostics(&diagnostics)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@(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 {
|
||||||
@@ -6541,7 +6699,7 @@ main func() void {}
|
|||||||
|
|
||||||
@(test)
|
@(test)
|
||||||
constant_division_by_zero_has_a_precise_diagnostic :: proc(t: ^testing.T) {
|
constant_division_by_zero_has_a_precise_diagnostic :: proc(t: ^testing.T) {
|
||||||
text := `value :: div_trunc(5, 0)
|
text := `value :: divtrunc!(5, 0)
|
||||||
main func() void {}
|
main func() void {}
|
||||||
`
|
`
|
||||||
source_file := source.Source{path="test.bro", text=text}
|
source_file := source.Source{path="test.bro", text=text}
|
||||||
@@ -9669,12 +9827,12 @@ compound_assignment_preserves_checked_numeric_operations :: proc(t: ^testing.T)
|
|||||||
signed += 6
|
signed += 6
|
||||||
signed -= 2
|
signed -= 2
|
||||||
signed *= 3
|
signed *= 3
|
||||||
signed = div_trunc(signed, 4)
|
signed = divtrunc!(signed, 4)
|
||||||
unsigned u32 = 24
|
unsigned u32 = 24
|
||||||
unsigned += 6
|
unsigned += 6
|
||||||
unsigned -= 2
|
unsigned -= 2
|
||||||
unsigned *= 3
|
unsigned *= 3
|
||||||
unsigned = div_trunc(unsigned, 4)
|
unsigned = divtrunc!(unsigned, 4)
|
||||||
real f64 = 24.0
|
real f64 = 24.0
|
||||||
real += 6.0
|
real += 6.0
|
||||||
real -= 2.0
|
real -= 2.0
|
||||||
@@ -9838,7 +9996,7 @@ checked_division_and_subtraction_emit_guarded_llvm :: proc(t: ^testing.T) {
|
|||||||
a i32 = 10
|
a i32 = 10
|
||||||
b i32 = 3
|
b i32 = 3
|
||||||
c i32 = a - b
|
c i32 = a - b
|
||||||
return div_trunc(c, b)
|
return divtrunc!(c, b)
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
source_file := source.Source{path="test.bro", text=text}
|
source_file := source.Source{path="test.bro", text=text}
|
||||||
@@ -9933,20 +10091,20 @@ main func() void { _ = $half(4) }`, want="integer '/' is not allowed"},
|
|||||||
|
|
||||||
@(test)
|
@(test)
|
||||||
division_builtins_diagnose_arity_operands_and_comptime_failures :: proc(t: ^testing.T) {
|
division_builtins_diagnose_arity_operands_and_comptime_failures :: proc(t: ^testing.T) {
|
||||||
text := `bad_arity :: div_floor(1)
|
text := `bad_arity :: divfloor!(1)
|
||||||
bad_bool :: rem(true, false)
|
bad_bool :: rem!(true, false)
|
||||||
bad_family :: mod(i32(5), f32(3))
|
bad_family :: mod!(i32(5), f32(3))
|
||||||
zero_trunc :: div_trunc(1, 0)
|
zero_trunc :: divtrunc!(1, 0)
|
||||||
zero_floor :: div_floor(1.0, 0.0)
|
zero_floor :: divfloor!(1.0, 0.0)
|
||||||
zero_exact :: div_exact(1, 0)
|
zero_exact :: divexact!(1, 0)
|
||||||
zero_ceil :: div_ceil(1.0, 0.0)
|
zero_ceil :: divceil!(1.0, 0.0)
|
||||||
zero_rem :: rem(1, 0)
|
zero_rem :: rem!(1, 0)
|
||||||
zero_mod :: mod(1.0, 0.0)
|
zero_mod :: mod!(1.0, 0.0)
|
||||||
inexact :: div_exact(5, 3)
|
inexact :: divexact!(5, 3)
|
||||||
overflow_trunc :: div_trunc(min_value(i32), -1)
|
overflow_trunc :: divtrunc!(minval!(i32), -1)
|
||||||
overflow_floor :: div_floor(min_value(i32), -1)
|
overflow_floor :: divfloor!(minval!(i32), -1)
|
||||||
overflow_exact :: div_exact(min_value(i32), -1)
|
overflow_exact :: divexact!(minval!(i32), -1)
|
||||||
overflow_ceil :: div_ceil(min_value(i32), -1)
|
overflow_ceil :: divceil!(minval!(i32), -1)
|
||||||
main func() void {}
|
main func() void {}
|
||||||
`
|
`
|
||||||
source_file := source.Source{path="test.bro", text=text}
|
source_file := source.Source{path="test.bro", text=text}
|
||||||
@@ -9986,28 +10144,28 @@ division_family_compiles_and_runs_for_integer_and_float_scalars :: proc(t: ^test
|
|||||||
directory := "/tmp/brolang-test-division-family"
|
directory := "/tmp/brolang-test-division-family"
|
||||||
main_path := "/tmp/brolang-test-division-family/main.bro"
|
main_path := "/tmp/brolang-test-division-family/main.bro"
|
||||||
output := "/tmp/brolang-test-division-family-output"
|
output := "/tmp/brolang-test-division-family-output"
|
||||||
text := `COUNT :: div_exact(8, 2)
|
text := `COUNT :: divexact!(8, 2)
|
||||||
items [div_ceil(10, 3)]u8 :: [0, 0, 0, 0]
|
items [divceil!(10, 3)]u8 :: [0, 0, 0, 0]
|
||||||
OPEN :: 5
|
OPEN :: 5
|
||||||
open_ceil i32 :: div_ceil(OPEN, 3)
|
open_ceil i32 :: divceil!(OPEN, 3)
|
||||||
|
|
||||||
check_i32 func(a, b, qt, qf, qc, r, m i32) bool {
|
check_i32 func(a, b, qt, qf, qc, r, m i32) bool {
|
||||||
return div_trunc(a, b) == qt and div_floor(a, b) == qf and
|
return divtrunc!(a, b) == qt and divfloor!(a, b) == qf and
|
||||||
div_ceil(a, b) == qc and rem(a, b) == r and mod(a, b) == m
|
divceil!(a, b) == qc and rem!(a, b) == r and mod!(a, b) == m
|
||||||
}
|
}
|
||||||
|
|
||||||
check_f32 func(a, b, qt, qf, qc, r, m f32) bool {
|
check_f32 func(a, b, qt, qf, qc, r, m f32) bool {
|
||||||
return div_trunc(a, b) == qt and div_floor(a, b) == qf and
|
return divtrunc!(a, b) == qt and divfloor!(a, b) == qf and
|
||||||
div_ceil(a, b) == qc and rem(a, b) == r and mod(a, b) == m
|
divceil!(a, b) == qc and rem!(a, b) == r and mod!(a, b) == m
|
||||||
}
|
}
|
||||||
|
|
||||||
check_f64 func(a, b, qt, qf, qc, r, m f64) bool {
|
check_f64 func(a, b, qt, qf, qc, r, m f64) bool {
|
||||||
return div_trunc(a, b) == qt and div_floor(a, b) == qf and
|
return divtrunc!(a, b) == qt and divfloor!(a, b) == qf and
|
||||||
div_ceil(a, b) == qc and rem(a, b) == r and mod(a, b) == m
|
divceil!(a, b) == qc and rem!(a, b) == r and mod!(a, b) == m
|
||||||
}
|
}
|
||||||
|
|
||||||
edge_rem func(a, b i32) i32 { return rem(a, b) }
|
edge_rem func(a, b i32) i32 { return rem!(a, b) }
|
||||||
edge_mod func(a, b i32) i32 { return mod(a, b) }
|
edge_mod func(a, b i32) i32 { return mod!(a, b) }
|
||||||
|
|
||||||
main func() i32 {
|
main func() i32 {
|
||||||
if COUNT != 4 or items.len != 4 or open_ceil != 2 { return 1 }
|
if COUNT != 4 or items.len != 4 or open_ceil != 2 { return 1 }
|
||||||
@@ -10015,9 +10173,9 @@ main func() i32 {
|
|||||||
if !check_i32(5, -3, -1, -2, -1, 2, -1) { return 3 }
|
if !check_i32(5, -3, -1, -2, -1, 2, -1) { return 3 }
|
||||||
if !check_i32(-5, 3, -1, -2, -1, -2, 1) { return 4 }
|
if !check_i32(-5, 3, -1, -2, -1, -2, 1) { return 4 }
|
||||||
if !check_i32(-5, -3, 1, 1, 2, -2, -2) { return 5 }
|
if !check_i32(-5, -3, 1, 1, 2, -2, -2) { return 5 }
|
||||||
if div_trunc(u32(5), u32(3)) != 1 or div_floor(u32(5), u32(3)) != 1 or
|
if divtrunc!(u32(5), u32(3)) != 1 or divfloor!(u32(5), u32(3)) != 1 or
|
||||||
div_ceil(u32(5), u32(3)) != 2 or rem(u32(5), u32(3)) != 2 or mod(u32(5), u32(3)) != 2 { return 6 }
|
divceil!(u32(5), u32(3)) != 2 or rem!(u32(5), u32(3)) != 2 or mod!(u32(5), u32(3)) != 2 { return 6 }
|
||||||
if div_exact(i32(6), i32(3)) != 2 or div_exact(u32(6), u32(3)) != 2 { return 7 }
|
if divexact!(i32(6), i32(3)) != 2 or divexact!(u32(6), u32(3)) != 2 { return 7 }
|
||||||
if !check_f32(f32(5.0), f32(3.0), f32(1.0), f32(1.0), f32(2.0), f32(2.0), f32(2.0)) or
|
if !check_f32(f32(5.0), f32(3.0), f32(1.0), f32(1.0), f32(2.0), f32(2.0), f32(2.0)) or
|
||||||
!check_f32(f32(5.0), f32(-3.0), f32(-1.0), f32(-2.0), f32(-1.0), f32(2.0), f32(-1.0)) or
|
!check_f32(f32(5.0), f32(-3.0), f32(-1.0), f32(-2.0), f32(-1.0), f32(2.0), f32(-1.0)) or
|
||||||
!check_f32(f32(-5.0), f32(3.0), f32(-1.0), f32(-2.0), f32(-1.0), f32(-2.0), f32(1.0)) or
|
!check_f32(f32(-5.0), f32(3.0), f32(-1.0), f32(-2.0), f32(-1.0), f32(-2.0), f32(1.0)) or
|
||||||
@@ -10026,7 +10184,7 @@ main func() i32 {
|
|||||||
!check_f64(5.0, -3.0, -1.0, -2.0, -1.0, 2.0, -1.0) or
|
!check_f64(5.0, -3.0, -1.0, -2.0, -1.0, 2.0, -1.0) or
|
||||||
!check_f64(-5.0, 3.0, -1.0, -2.0, -1.0, -2.0, 1.0) or
|
!check_f64(-5.0, 3.0, -1.0, -2.0, -1.0, -2.0, 1.0) or
|
||||||
!check_f64(-5.0, -3.0, 1.0, 1.0, 2.0, -2.0, -2.0) { return 9 }
|
!check_f64(-5.0, -3.0, 1.0, 1.0, 2.0, -2.0, -2.0) { return 9 }
|
||||||
if div_exact(f32(6.0), f32(3.0)) != 2.0 or div_exact(f64(6.0), f64(3.0)) != 2.0 { return 10 }
|
if divexact!(f32(6.0), f32(3.0)) != 2.0 or divexact!(f64(6.0), f64(3.0)) != 2.0 { return 10 }
|
||||||
if edge_rem(-2147483648, -1) != 0 or edge_mod(-2147483648, -1) != 0 { return 11 }
|
if edge_rem(-2147483648, -1) != 0 or edge_mod(-2147483648, -1) != 0 { return 11 }
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
@@ -10051,24 +10209,24 @@ division_builtins_trap_for_runtime_zero_overflow_and_inexact_results :: proc(t:
|
|||||||
right: string,
|
right: string,
|
||||||
}
|
}
|
||||||
cases := [?]Case{
|
cases := [?]Case{
|
||||||
{name="div_trunc", type_name="i32", left="1", right="0"},
|
{name="divtrunc", type_name="i32", left="1", right="0"},
|
||||||
{name="div_floor", type_name="f32", left="f32(1.0)", right="f32(0.0)"},
|
{name="divfloor", type_name="f32", left="f32(1.0)", right="f32(0.0)"},
|
||||||
{name="div_exact", type_name="f64", left="1.0", right="0.0"},
|
{name="divexact", type_name="f64", left="1.0", right="0.0"},
|
||||||
{name="div_ceil", type_name="i32", left="1", right="0"},
|
{name="divceil", type_name="i32", left="1", right="0"},
|
||||||
{name="rem", type_name="f32", left="f32(1.0)", right="f32(0.0)"},
|
{name="rem", type_name="f32", left="f32(1.0)", right="f32(0.0)"},
|
||||||
{name="mod", type_name="f64", left="1.0", right="0.0"},
|
{name="mod", type_name="f64", left="1.0", right="0.0"},
|
||||||
{name="div_exact", type_name="i32", left="5", right="3"},
|
{name="divexact", type_name="i32", left="5", right="3"},
|
||||||
{name="div_trunc", type_name="i32", left="-2147483648", right="-1"},
|
{name="divtrunc", type_name="i32", left="-2147483648", right="-1"},
|
||||||
{name="div_floor", type_name="i32", left="-2147483648", right="-1"},
|
{name="divfloor", type_name="i32", left="-2147483648", right="-1"},
|
||||||
{name="div_exact", type_name="i32", left="-2147483648", right="-1"},
|
{name="divexact", type_name="i32", left="-2147483648", right="-1"},
|
||||||
{name="div_ceil", type_name="i32", left="-2147483648", right="-1"},
|
{name="divceil", type_name="i32", left="-2147483648", right="-1"},
|
||||||
}
|
}
|
||||||
for test_case, index in cases {
|
for test_case, index in cases {
|
||||||
directory := fmt.aprintf("/tmp/brolang-test-division-trap-%d", index)
|
directory := fmt.aprintf("/tmp/brolang-test-division-trap-%d", index)
|
||||||
main_path := fmt.aprintf("%s/main.bro", directory)
|
main_path := fmt.aprintf("%s/main.bro", directory)
|
||||||
output := fmt.aprintf("/tmp/brolang-test-division-trap-output-%d", index)
|
output := fmt.aprintf("/tmp/brolang-test-division-trap-output-%d", index)
|
||||||
text := fmt.aprintf(
|
text := fmt.aprintf(
|
||||||
"invoke func(a, b %s) %s {{ return %s(a, b) }}\nmain func() void {{ _ = invoke(%s, %s) }}\n",
|
"invoke func(a, b %s) %s {{ return %s!(a, b) }}\nmain func() void {{ _ = invoke(%s, %s) }}\n",
|
||||||
test_case.type_name, test_case.type_name, test_case.name, test_case.left, test_case.right,
|
test_case.type_name, test_case.type_name, test_case.name, test_case.left, test_case.right,
|
||||||
)
|
)
|
||||||
_ = os2.remove_all(directory)
|
_ = os2.remove_all(directory)
|
||||||
@@ -10096,10 +10254,10 @@ qualified_division_builtin_names_resolve_as_package_functions :: proc(t: ^testin
|
|||||||
math_path := "/tmp/brolang-test-qualified-division/math/math.bro"
|
math_path := "/tmp/brolang-test-qualified-division/math/math.bro"
|
||||||
main_path := "/tmp/brolang-test-qualified-division/app/main.bro"
|
main_path := "/tmp/brolang-test-qualified-division/app/main.bro"
|
||||||
output := "/tmp/brolang-test-qualified-division-output"
|
output := "/tmp/brolang-test-qualified-division-output"
|
||||||
math_text := `div_floor func(a, b i32) i32 { return a + b }
|
math_text := `divfloor func(a, b i32) i32 { return a + b }
|
||||||
`
|
`
|
||||||
main_text := `math :: import "../math"
|
main_text := `math :: import "../math"
|
||||||
main func() i32 { return math.div_floor(20, 22) }
|
main func() i32 { return math.divfloor(20, 22) }
|
||||||
`
|
`
|
||||||
_ = os2.remove_all(directory)
|
_ = os2.remove_all(directory)
|
||||||
defer _ = os2.remove_all(directory)
|
defer _ = os2.remove_all(directory)
|
||||||
@@ -10117,17 +10275,17 @@ main func() i32 { return math.div_floor(20, 22) }
|
|||||||
|
|
||||||
@(test)
|
@(test)
|
||||||
division_builtins_emit_guards_rounding_and_single_integer_divisions :: proc(t: ^testing.T) {
|
division_builtins_emit_guards_rounding_and_single_integer_divisions :: proc(t: ^testing.T) {
|
||||||
text := `floor_i32 func(a, b i32) i32 { return div_floor(a, b) }
|
text := `floor_i32 func(a, b i32) i32 { return divfloor!(a, b) }
|
||||||
ceil_i32 func(a, b i32) i32 { return div_ceil(a, b) }
|
ceil_i32 func(a, b i32) i32 { return divceil!(a, b) }
|
||||||
exact_i32 func(a, b i32) i32 { return div_exact(a, b) }
|
exact_i32 func(a, b i32) i32 { return divexact!(a, b) }
|
||||||
floor_u32 func(a, b u32) u32 { return div_floor(a, b) }
|
floor_u32 func(a, b u32) u32 { return divfloor!(a, b) }
|
||||||
rem_i16 func(a, b i16) i16 { return rem(a, b) }
|
rem_i16 func(a, b i16) i16 { return rem!(a, b) }
|
||||||
mod_i16 func(a, b i16) i16 { return mod(a, b) }
|
mod_i16 func(a, b i16) i16 { return mod!(a, b) }
|
||||||
floor_f32 func(a, b f32) f32 { return div_floor(a, b) }
|
floor_f32 func(a, b f32) f32 { return divfloor!(a, b) }
|
||||||
ceil_f64 func(a, b f64) f64 { return div_ceil(a, b) }
|
ceil_f64 func(a, b f64) f64 { return divceil!(a, b) }
|
||||||
exact_f32 func(a, b f32) f32 { return div_exact(a, b) }
|
exact_f32 func(a, b f32) f32 { return divexact!(a, b) }
|
||||||
rem_f64 func(a, b f64) f64 { return rem(a, b) }
|
rem_f64 func(a, b f64) f64 { return rem!(a, b) }
|
||||||
mod_f32 func(a, b f32) f32 { return mod(a, b) }
|
mod_f32 func(a, b f32) f32 { return mod!(a, b) }
|
||||||
main func() void {
|
main func() void {
|
||||||
_ = floor_i32(5, 3)
|
_ = floor_i32(5, 3)
|
||||||
_ = ceil_i32(5, 3)
|
_ = ceil_i32(5, 3)
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ check_float func() i32 {
|
|||||||
|
|
||||||
check_unsigned func() i32 {
|
check_unsigned func() i32 {
|
||||||
n u32 = 100
|
n u32 = 100
|
||||||
n = div_trunc(n, 7) # 14
|
n = divtrunc!(n, 7) # 14
|
||||||
n -= 4 # 10
|
n -= 4 # 10
|
||||||
if n == 10 {
|
if n == 10 {
|
||||||
return 1
|
return 1
|
||||||
@@ -27,7 +27,7 @@ main func() i32 {
|
|||||||
total += 10 # 10
|
total += 10 # 10
|
||||||
total -= 3 # 7
|
total -= 3 # 7
|
||||||
total *= 4 # 28
|
total *= 4 # 28
|
||||||
total = div_trunc(total, 2) # 14
|
total = divtrunc!(total, 2) # 14
|
||||||
|
|
||||||
# binary operators honour precedence: 14 + (2 * 3) - 4 == 16
|
# binary operators honour precedence: 14 + (2 * 3) - 4 == 16
|
||||||
total = total + 2 * 3 - 4
|
total = total + 2 * 3 - 4
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ mem :: import "@std/mem"
|
|||||||
|
|
||||||
_probe_count func(context ?*mut anyopaque) void {
|
_probe_count func(context ?*mut anyopaque) void {
|
||||||
if context |raw| {
|
if context |raw| {
|
||||||
counts *mut usize :: ptr_cast(usize, raw)
|
counts *mut usize :: ptrcast!(usize, raw)
|
||||||
counts[0] += 1
|
counts[0] += 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -28,7 +28,7 @@ _probe_vtable mem.AllocatorVTable :: mem.AllocatorVTable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
typed_allocator_test func() i32 {
|
typed_allocator_test func() i32 {
|
||||||
if (size_of(mem.Allocator) != 16) return 31
|
if (sizeof!(mem.Allocator) != 16) return 31
|
||||||
|
|
||||||
first_calls [1]mut usize = [0]
|
first_calls [1]mut usize = [0]
|
||||||
second_calls [1]mut usize = [0]
|
second_calls [1]mut usize = [0]
|
||||||
@@ -76,7 +76,7 @@ typed_allocator_test func() i32 {
|
|||||||
}
|
}
|
||||||
if (overflow_fallback_failed) return 37
|
if (overflow_fallback_failed) return 37
|
||||||
overflow_failed bool = false
|
overflow_failed bool = false
|
||||||
_ = mem.alloc(u64, first_allocator, max_value(usize)) catch |_| {
|
_ = mem.alloc(u64, first_allocator, maxval!(usize)) catch |_| {
|
||||||
overflow_failed = true
|
overflow_failed = true
|
||||||
yield overflow_fallback
|
yield overflow_fallback
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,6 +39,6 @@ main func() i32 {
|
|||||||
if (pointer.value != 42) return 4
|
if (pointer.value != 42) return 4
|
||||||
buffer Buffer(u8, 4) :: Buffer(u8, 4) { values = [1, 2, 3, 4] }
|
buffer Buffer(u8, 4) :: Buffer(u8, 4) { values = [1, 2, 3, 4] }
|
||||||
if (buffer.values.len != 4) return 2
|
if (buffer.values.len != 4) return 2
|
||||||
if (size_of(Buffer(u8, 4)) != 4) return 5
|
if (sizeof!(Buffer(u8, 4)) != 4) return 5
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,6 +37,7 @@
|
|||||||
(function_declaration name: (identifier) @function)
|
(function_declaration name: (identifier) @function)
|
||||||
(parameter name: (identifier) @variable.parameter)
|
(parameter name: (identifier) @variable.parameter)
|
||||||
|
|
||||||
|
(intrinsic_call_expression function: (identifier) @function.builtin)
|
||||||
(call_expression function: (expression (identifier) @function))
|
(call_expression function: (expression (identifier) @function))
|
||||||
(call_expression function: (expression (field_expression field: (identifier) @function)))
|
(call_expression function: (expression (field_expression field: (identifier) @function)))
|
||||||
(field_expression field: (identifier) @property)
|
(field_expression field: (identifier) @property)
|
||||||
|
|||||||
@@ -30,8 +30,8 @@ reserve func($T type, list @mut ArrayList(T), minimum_capacity usize) void ! mem
|
|||||||
|
|
||||||
new_capacity usize = 8
|
new_capacity usize = 8
|
||||||
if list.capacity >= 8 {
|
if list.capacity >= 8 {
|
||||||
half usize :: div_trunc(list.capacity, 2)
|
half usize :: divtrunc!(list.capacity, 2)
|
||||||
if list.capacity > max_value(usize) - half {
|
if list.capacity > maxval!(usize) - half {
|
||||||
new_capacity = minimum_capacity
|
new_capacity = minimum_capacity
|
||||||
} else {
|
} else {
|
||||||
new_capacity = list.capacity + half
|
new_capacity = list.capacity + half
|
||||||
@@ -53,7 +53,7 @@ reserve func($T type, list @mut ArrayList(T), minimum_capacity usize) void ! mem
|
|||||||
|
|
||||||
append func($T type, list @mut ArrayList(T), value T) void ! mem.AllocError {
|
append func($T type, list @mut ArrayList(T), value T) void ! mem.AllocError {
|
||||||
length usize :: list.items.len
|
length usize :: list.items.len
|
||||||
if length == max_value(usize) {
|
if length == maxval!(usize) {
|
||||||
return .out_of_memory
|
return .out_of_memory
|
||||||
}
|
}
|
||||||
try reserve(list, length + 1)
|
try reserve(list, length + 1)
|
||||||
|
|||||||
+2
-2
@@ -76,7 +76,7 @@ write_all func(writer Writer, bytes []u8) void ! WriteError {
|
|||||||
|
|
||||||
_system_read func(_ ?*mut anyopaque, stream ReadStream, buffer []mut u8) usize ! ReadError {
|
_system_read func(_ ?*mut anyopaque, stream ReadStream, buffer []mut u8) usize ! ReadError {
|
||||||
request usize = buffer.len
|
request usize = buffer.len
|
||||||
maximum usize :: usize(max_value(c_long))
|
maximum usize :: usize(maxval!(c_long))
|
||||||
if request > maximum {
|
if request > maximum {
|
||||||
request = maximum
|
request = maximum
|
||||||
}
|
}
|
||||||
@@ -94,7 +94,7 @@ _system_read func(_ ?*mut anyopaque, stream ReadStream, buffer []mut u8) usize !
|
|||||||
_system_write func(_ ?*mut anyopaque, stream WriteStream, bytes []u8) usize ! WriteError {
|
_system_write func(_ ?*mut anyopaque, stream WriteStream, bytes []u8) usize ! WriteError {
|
||||||
fd c_int :: c_int(stream)
|
fd c_int :: c_int(stream)
|
||||||
request usize = bytes.len
|
request usize = bytes.len
|
||||||
maximum usize :: usize(max_value(c_long))
|
maximum usize :: usize(maxval!(c_long))
|
||||||
if request > maximum {
|
if request > maximum {
|
||||||
request = maximum
|
request = maximum
|
||||||
}
|
}
|
||||||
|
|||||||
+16
-16
@@ -44,7 +44,7 @@ eql func($T type, left, right []T) bool {
|
|||||||
_empty_storage [1]mut u64 = [0]
|
_empty_storage [1]mut u64 = [0]
|
||||||
|
|
||||||
_empty_slice func($T type, count usize) []mut T {
|
_empty_slice func($T type, count usize) []mut T {
|
||||||
pointer *mut T :: ptr_cast(T, (&_empty_storage).ptr)
|
pointer *mut T :: ptrcast!(T, (&_empty_storage).ptr)
|
||||||
return pointer[..count]
|
return pointer[..count]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,17 +57,17 @@ alloc func($T type, allocator Allocator, count usize) []mut T ! AllocError {
|
|||||||
return _empty_slice(T, 0)
|
return _empty_slice(T, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
element_size usize :: size_of(T)
|
element_size usize :: sizeof!(T)
|
||||||
if element_size == 0 {
|
if element_size == 0 {
|
||||||
return _empty_slice(T, count)
|
return _empty_slice(T, count)
|
||||||
}
|
}
|
||||||
if count > div_trunc(max_value(usize), element_size) {
|
if count > divtrunc!(maxval!(usize), element_size) {
|
||||||
return .out_of_memory
|
return .out_of_memory
|
||||||
}
|
}
|
||||||
|
|
||||||
memory ?*mut u8 = raw_alloc(allocator, count * element_size, align_of(T))
|
memory ?*mut u8 = raw_alloc(allocator, count * element_size, alignof!(T))
|
||||||
if memory |bytes| {
|
if memory |bytes| {
|
||||||
pointer *mut T :: ptr_cast(T, bytes)
|
pointer *mut T :: ptrcast!(T, bytes)
|
||||||
return pointer[..count]
|
return pointer[..count]
|
||||||
}
|
}
|
||||||
return .out_of_memory
|
return .out_of_memory
|
||||||
@@ -82,18 +82,18 @@ realloc func($T type, allocator Allocator, memory []mut T, new_count usize) []mu
|
|||||||
return _empty_slice(T, 0)
|
return _empty_slice(T, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
element_size usize :: size_of(T)
|
element_size usize :: sizeof!(T)
|
||||||
if element_size == 0 {
|
if element_size == 0 {
|
||||||
return _empty_slice(T, new_count)
|
return _empty_slice(T, new_count)
|
||||||
}
|
}
|
||||||
if new_count > div_trunc(max_value(usize), element_size) {
|
if new_count > divtrunc!(maxval!(usize), element_size) {
|
||||||
return .out_of_memory
|
return .out_of_memory
|
||||||
}
|
}
|
||||||
|
|
||||||
old_memory ?*mut u8 = none
|
old_memory ?*mut u8 = none
|
||||||
old_size usize = 0
|
old_size usize = 0
|
||||||
if memory.len != 0 {
|
if memory.len != 0 {
|
||||||
old_memory = ptr_cast(u8, memory.ptr)
|
old_memory = ptrcast!(u8, memory.ptr)
|
||||||
old_size = memory.len * element_size
|
old_size = memory.len * element_size
|
||||||
}
|
}
|
||||||
resized ?*mut u8 = raw_realloc(
|
resized ?*mut u8 = raw_realloc(
|
||||||
@@ -101,18 +101,18 @@ realloc func($T type, allocator Allocator, memory []mut T, new_count usize) []mu
|
|||||||
old_memory,
|
old_memory,
|
||||||
old_size,
|
old_size,
|
||||||
new_count * element_size,
|
new_count * element_size,
|
||||||
align_of(T),
|
alignof!(T),
|
||||||
)
|
)
|
||||||
if resized |bytes| {
|
if resized |bytes| {
|
||||||
pointer *mut T :: ptr_cast(T, bytes)
|
pointer *mut T :: ptrcast!(T, bytes)
|
||||||
return pointer[..new_count]
|
return pointer[..new_count]
|
||||||
}
|
}
|
||||||
return .out_of_memory
|
return .out_of_memory
|
||||||
}
|
}
|
||||||
|
|
||||||
free func($T type, allocator Allocator, memory []mut T) void {
|
free func($T type, allocator Allocator, memory []mut T) void {
|
||||||
if memory.len != 0 and size_of(T) != 0 {
|
if memory.len != 0 and sizeof!(T) != 0 {
|
||||||
raw_free(allocator, ptr_cast(u8, memory.ptr), memory.len * size_of(T), align_of(T))
|
raw_free(allocator, ptrcast!(u8, memory.ptr), memory.len * sizeof!(T), alignof!(T))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,7 +125,7 @@ _power_of_two func(value usize) bool {
|
|||||||
|
|
||||||
current usize = value
|
current usize = value
|
||||||
while current > 1 {
|
while current > 1 {
|
||||||
half usize = div_trunc(current, 2)
|
half usize = divtrunc!(current, 2)
|
||||||
if half * 2 != current {
|
if half * 2 != current {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -141,7 +141,7 @@ _c_alloc func(_ ?*mut anyopaque, size usize, alignment usize) ?*mut u8 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if alignment <= _malloc_alignment {
|
if alignment <= _malloc_alignment {
|
||||||
return ptr_cast(u8, c.malloc(c_ulong(size)))
|
return ptrcast!(u8, c.malloc(c_ulong(size)))
|
||||||
}
|
}
|
||||||
|
|
||||||
memory [1]mut ?*mut anyopaque = [none]
|
memory [1]mut ?*mut anyopaque = [none]
|
||||||
@@ -150,7 +150,7 @@ _c_alloc func(_ ?*mut anyopaque, size usize, alignment usize) ?*mut u8 {
|
|||||||
return none
|
return none
|
||||||
}
|
}
|
||||||
|
|
||||||
return ptr_cast(u8, memory[0])
|
return ptrcast!(u8, memory[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
_c_realloc func(_ ?*mut anyopaque, memory ?*mut u8, old_size usize, new_size usize, alignment usize) ?*mut u8 {
|
_c_realloc func(_ ?*mut anyopaque, memory ?*mut u8, old_size usize, new_size usize, alignment usize) ?*mut u8 {
|
||||||
@@ -165,7 +165,7 @@ _c_realloc func(_ ?*mut anyopaque, memory ?*mut u8, old_size usize, new_size usi
|
|||||||
|
|
||||||
if memory |old_memory| {
|
if memory |old_memory| {
|
||||||
if alignment <= _malloc_alignment {
|
if alignment <= _malloc_alignment {
|
||||||
return ptr_cast(u8, c.realloc(old_memory, c_ulong(new_size)))
|
return ptrcast!(u8, c.realloc(old_memory, c_ulong(new_size)))
|
||||||
}
|
}
|
||||||
|
|
||||||
new_memory ?*mut u8 = _c_alloc(none, new_size, alignment)
|
new_memory ?*mut u8 = _c_alloc(none, new_size, alignment)
|
||||||
|
|||||||
@@ -30,8 +30,8 @@ reserve func($T type, list @mut ArrayList(T), minimum_capacity usize) void ! mem
|
|||||||
|
|
||||||
new_capacity usize = 8
|
new_capacity usize = 8
|
||||||
if list.capacity >= 8 {
|
if list.capacity >= 8 {
|
||||||
half usize :: div_trunc(list.capacity, 2)
|
half usize :: divtrunc!(list.capacity, 2)
|
||||||
if list.capacity > max_value(usize) - half {
|
if list.capacity > maxval!(usize) - half {
|
||||||
new_capacity = minimum_capacity
|
new_capacity = minimum_capacity
|
||||||
} else {
|
} else {
|
||||||
new_capacity = list.capacity + half
|
new_capacity = list.capacity + half
|
||||||
@@ -53,7 +53,7 @@ reserve func($T type, list @mut ArrayList(T), minimum_capacity usize) void ! mem
|
|||||||
|
|
||||||
append func($T type, list @mut ArrayList(T), value T) void ! mem.AllocError {
|
append func($T type, list @mut ArrayList(T), value T) void ! mem.AllocError {
|
||||||
length usize :: list.items.len
|
length usize :: list.items.len
|
||||||
if length == max_value(usize) {
|
if length == maxval!(usize) {
|
||||||
return .out_of_memory
|
return .out_of_memory
|
||||||
}
|
}
|
||||||
try reserve(list, length + 1)
|
try reserve(list, length + 1)
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ write_all func(writer Writer, bytes []u8) void ! WriteError {
|
|||||||
|
|
||||||
_system_read func(_ ?*mut anyopaque, stream ReadStream, buffer []mut u8) usize ! ReadError {
|
_system_read func(_ ?*mut anyopaque, stream ReadStream, buffer []mut u8) usize ! ReadError {
|
||||||
request usize = buffer.len
|
request usize = buffer.len
|
||||||
maximum usize :: usize(max_value(c_long))
|
maximum usize :: usize(maxval!(c_long))
|
||||||
if request > maximum {
|
if request > maximum {
|
||||||
request = maximum
|
request = maximum
|
||||||
}
|
}
|
||||||
@@ -94,7 +94,7 @@ _system_read func(_ ?*mut anyopaque, stream ReadStream, buffer []mut u8) usize !
|
|||||||
_system_write func(_ ?*mut anyopaque, stream WriteStream, bytes []u8) usize ! WriteError {
|
_system_write func(_ ?*mut anyopaque, stream WriteStream, bytes []u8) usize ! WriteError {
|
||||||
fd c_int :: c_int(stream)
|
fd c_int :: c_int(stream)
|
||||||
request usize = bytes.len
|
request usize = bytes.len
|
||||||
maximum usize :: usize(max_value(c_long))
|
maximum usize :: usize(maxval!(c_long))
|
||||||
if request > maximum {
|
if request > maximum {
|
||||||
request = maximum
|
request = maximum
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ eql func($T type, left, right []T) bool {
|
|||||||
_empty_storage [1]mut u64 = [0]
|
_empty_storage [1]mut u64 = [0]
|
||||||
|
|
||||||
_empty_slice func($T type, count usize) []mut T {
|
_empty_slice func($T type, count usize) []mut T {
|
||||||
pointer *mut T :: ptr_cast(T, (&_empty_storage).ptr)
|
pointer *mut T :: ptrcast!(T, (&_empty_storage).ptr)
|
||||||
return pointer[..count]
|
return pointer[..count]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,17 +57,17 @@ alloc func($T type, allocator Allocator, count usize) []mut T ! AllocError {
|
|||||||
return _empty_slice(T, 0)
|
return _empty_slice(T, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
element_size usize :: size_of(T)
|
element_size usize :: sizeof!(T)
|
||||||
if element_size == 0 {
|
if element_size == 0 {
|
||||||
return _empty_slice(T, count)
|
return _empty_slice(T, count)
|
||||||
}
|
}
|
||||||
if count > div_trunc(max_value(usize), element_size) {
|
if count > divtrunc!(maxval!(usize), element_size) {
|
||||||
return .out_of_memory
|
return .out_of_memory
|
||||||
}
|
}
|
||||||
|
|
||||||
memory ?*mut u8 = raw_alloc(allocator, count * element_size, align_of(T))
|
memory ?*mut u8 = raw_alloc(allocator, count * element_size, alignof!(T))
|
||||||
if memory |bytes| {
|
if memory |bytes| {
|
||||||
pointer *mut T :: ptr_cast(T, bytes)
|
pointer *mut T :: ptrcast!(T, bytes)
|
||||||
return pointer[..count]
|
return pointer[..count]
|
||||||
}
|
}
|
||||||
return .out_of_memory
|
return .out_of_memory
|
||||||
@@ -82,18 +82,18 @@ realloc func($T type, allocator Allocator, memory []mut T, new_count usize) []mu
|
|||||||
return _empty_slice(T, 0)
|
return _empty_slice(T, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
element_size usize :: size_of(T)
|
element_size usize :: sizeof!(T)
|
||||||
if element_size == 0 {
|
if element_size == 0 {
|
||||||
return _empty_slice(T, new_count)
|
return _empty_slice(T, new_count)
|
||||||
}
|
}
|
||||||
if new_count > div_trunc(max_value(usize), element_size) {
|
if new_count > divtrunc!(maxval!(usize), element_size) {
|
||||||
return .out_of_memory
|
return .out_of_memory
|
||||||
}
|
}
|
||||||
|
|
||||||
old_memory ?*mut u8 = none
|
old_memory ?*mut u8 = none
|
||||||
old_size usize = 0
|
old_size usize = 0
|
||||||
if memory.len != 0 {
|
if memory.len != 0 {
|
||||||
old_memory = ptr_cast(u8, memory.ptr)
|
old_memory = ptrcast!(u8, memory.ptr)
|
||||||
old_size = memory.len * element_size
|
old_size = memory.len * element_size
|
||||||
}
|
}
|
||||||
resized ?*mut u8 = raw_realloc(
|
resized ?*mut u8 = raw_realloc(
|
||||||
@@ -101,18 +101,18 @@ realloc func($T type, allocator Allocator, memory []mut T, new_count usize) []mu
|
|||||||
old_memory,
|
old_memory,
|
||||||
old_size,
|
old_size,
|
||||||
new_count * element_size,
|
new_count * element_size,
|
||||||
align_of(T),
|
alignof!(T),
|
||||||
)
|
)
|
||||||
if resized |bytes| {
|
if resized |bytes| {
|
||||||
pointer *mut T :: ptr_cast(T, bytes)
|
pointer *mut T :: ptrcast!(T, bytes)
|
||||||
return pointer[..new_count]
|
return pointer[..new_count]
|
||||||
}
|
}
|
||||||
return .out_of_memory
|
return .out_of_memory
|
||||||
}
|
}
|
||||||
|
|
||||||
free func($T type, allocator Allocator, memory []mut T) void {
|
free func($T type, allocator Allocator, memory []mut T) void {
|
||||||
if memory.len != 0 and size_of(T) != 0 {
|
if memory.len != 0 and sizeof!(T) != 0 {
|
||||||
raw_free(allocator, ptr_cast(u8, memory.ptr), memory.len * size_of(T), align_of(T))
|
raw_free(allocator, ptrcast!(u8, memory.ptr), memory.len * sizeof!(T), alignof!(T))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,7 +125,7 @@ _power_of_two func(value usize) bool {
|
|||||||
|
|
||||||
current usize = value
|
current usize = value
|
||||||
while current > 1 {
|
while current > 1 {
|
||||||
half usize = div_trunc(current, 2)
|
half usize = divtrunc!(current, 2)
|
||||||
if half * 2 != current {
|
if half * 2 != current {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -141,7 +141,7 @@ _c_alloc func(_ ?*mut anyopaque, size usize, alignment usize) ?*mut u8 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if alignment <= _malloc_alignment {
|
if alignment <= _malloc_alignment {
|
||||||
return ptr_cast(u8, c.malloc(c_ulong(size)))
|
return ptrcast!(u8, c.malloc(c_ulong(size)))
|
||||||
}
|
}
|
||||||
|
|
||||||
memory [1]mut ?*mut anyopaque = [none]
|
memory [1]mut ?*mut anyopaque = [none]
|
||||||
@@ -150,7 +150,7 @@ _c_alloc func(_ ?*mut anyopaque, size usize, alignment usize) ?*mut u8 {
|
|||||||
return none
|
return none
|
||||||
}
|
}
|
||||||
|
|
||||||
return ptr_cast(u8, memory[0])
|
return ptrcast!(u8, memory[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
_c_realloc func(_ ?*mut anyopaque, memory ?*mut u8, old_size usize, new_size usize, alignment usize) ?*mut u8 {
|
_c_realloc func(_ ?*mut anyopaque, memory ?*mut u8, old_size usize, new_size usize, alignment usize) ?*mut u8 {
|
||||||
@@ -165,7 +165,7 @@ _c_realloc func(_ ?*mut anyopaque, memory ?*mut u8, old_size usize, new_size usi
|
|||||||
|
|
||||||
if memory |old_memory| {
|
if memory |old_memory| {
|
||||||
if alignment <= _malloc_alignment {
|
if alignment <= _malloc_alignment {
|
||||||
return ptr_cast(u8, c.realloc(old_memory, c_ulong(new_size)))
|
return ptrcast!(u8, c.realloc(old_memory, c_ulong(new_size)))
|
||||||
}
|
}
|
||||||
|
|
||||||
new_memory ?*mut u8 = _c_alloc(none, new_size, alignment)
|
new_memory ?*mut u8 = _c_alloc(none, new_size, alignment)
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ raw_free func(allocator Allocator, memory ?*mut u8, size usize, alignment usize)
|
|||||||
_empty_storage [1]mut u64 = [0]
|
_empty_storage [1]mut u64 = [0]
|
||||||
|
|
||||||
_empty_slice func($T type, count usize) []mut T {
|
_empty_slice func($T type, count usize) []mut T {
|
||||||
pointer *mut T :: ptr_cast(T, (&_empty_storage).ptr)
|
pointer *mut T :: ptrcast!(T, (&_empty_storage).ptr)
|
||||||
return pointer[..count]
|
return pointer[..count]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,25 +39,25 @@ alloc func($T type, allocator Allocator, count usize) []mut T ! AllocError {
|
|||||||
return _empty_slice(T, 0)
|
return _empty_slice(T, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
element_size usize :: size_of(T)
|
element_size usize :: sizeof!(T)
|
||||||
if element_size == 0 {
|
if element_size == 0 {
|
||||||
return _empty_slice(T, count)
|
return _empty_slice(T, count)
|
||||||
}
|
}
|
||||||
if count > max_value(usize) / element_size {
|
if count > maxval!(usize) / element_size {
|
||||||
return .out_of_memory
|
return .out_of_memory
|
||||||
}
|
}
|
||||||
|
|
||||||
memory ?*mut u8 = raw_alloc(allocator, count * element_size, align_of(T))
|
memory ?*mut u8 = raw_alloc(allocator, count * element_size, alignof!(T))
|
||||||
if memory |bytes| {
|
if memory |bytes| {
|
||||||
pointer *mut T :: ptr_cast(T, bytes)
|
pointer *mut T :: ptrcast!(T, bytes)
|
||||||
return pointer[..count]
|
return pointer[..count]
|
||||||
}
|
}
|
||||||
return .out_of_memory
|
return .out_of_memory
|
||||||
}
|
}
|
||||||
|
|
||||||
free func($T type, allocator Allocator, memory []mut T) void {
|
free func($T type, allocator Allocator, memory []mut T) void {
|
||||||
if memory.len != 0 and size_of(T) != 0 {
|
if memory.len != 0 and sizeof!(T) != 0 {
|
||||||
raw_free(allocator, ptr_cast(u8, memory.ptr), memory.len * size_of(T), align_of(T))
|
raw_free(allocator, ptrcast!(u8, memory.ptr), memory.len * sizeof!(T), alignof!(T))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,7 +86,7 @@ _c_alloc func(_ ?*mut anyopaque, size usize, alignment usize) ?*mut u8 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if alignment <= _malloc_alignment {
|
if alignment <= _malloc_alignment {
|
||||||
return ptr_cast(u8, c.malloc(c_ulong(size)))
|
return ptrcast!(u8, c.malloc(c_ulong(size)))
|
||||||
}
|
}
|
||||||
|
|
||||||
memory [1]mut ?*mut anyopaque = [none]
|
memory [1]mut ?*mut anyopaque = [none]
|
||||||
@@ -95,7 +95,7 @@ _c_alloc func(_ ?*mut anyopaque, size usize, alignment usize) ?*mut u8 {
|
|||||||
return none
|
return none
|
||||||
}
|
}
|
||||||
|
|
||||||
return ptr_cast(u8, memory[0])
|
return ptrcast!(u8, memory[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
_c_realloc func(_ ?*mut anyopaque, memory ?*mut u8, old_size usize, new_size usize, alignment usize) ?*mut u8 {
|
_c_realloc func(_ ?*mut anyopaque, memory ?*mut u8, old_size usize, new_size usize, alignment usize) ?*mut u8 {
|
||||||
@@ -110,7 +110,7 @@ _c_realloc func(_ ?*mut anyopaque, memory ?*mut u8, old_size usize, new_size usi
|
|||||||
|
|
||||||
if memory |old_memory| {
|
if memory |old_memory| {
|
||||||
if alignment <= _malloc_alignment {
|
if alignment <= _malloc_alignment {
|
||||||
return ptr_cast(u8, c.realloc(old_memory, c_ulong(new_size)))
|
return ptrcast!(u8, c.realloc(old_memory, c_ulong(new_size)))
|
||||||
}
|
}
|
||||||
|
|
||||||
new_memory ?*mut u8 = _c_alloc(none, new_size, alignment)
|
new_memory ?*mut u8 = _c_alloc(none, new_size, alignment)
|
||||||
|
|||||||
@@ -383,6 +383,7 @@ module.exports = grammar({
|
|||||||
$.catch_expression,
|
$.catch_expression,
|
||||||
$.unary_expression,
|
$.unary_expression,
|
||||||
$.field_expression,
|
$.field_expression,
|
||||||
|
$.intrinsic_call_expression,
|
||||||
$.call_expression,
|
$.call_expression,
|
||||||
$.index_expression,
|
$.index_expression,
|
||||||
$.slice_expression,
|
$.slice_expression,
|
||||||
@@ -440,6 +441,12 @@ module.exports = grammar({
|
|||||||
field('field', $.identifier),
|
field('field', $.identifier),
|
||||||
)),
|
)),
|
||||||
|
|
||||||
|
intrinsic_call_expression: $ => prec(PREC.POSTFIX, seq(
|
||||||
|
field('function', $.identifier),
|
||||||
|
field('marker', '!'),
|
||||||
|
field('arguments', $.argument_list),
|
||||||
|
)),
|
||||||
|
|
||||||
call_expression: $ => prec.left(PREC.POSTFIX, seq(
|
call_expression: $ => prec.left(PREC.POSTFIX, seq(
|
||||||
field('function', $.expression),
|
field('function', $.expression),
|
||||||
field('arguments', $.argument_list),
|
field('arguments', $.argument_list),
|
||||||
|
|||||||
@@ -37,6 +37,7 @@
|
|||||||
(function_declaration name: (identifier) @function)
|
(function_declaration name: (identifier) @function)
|
||||||
(parameter name: (identifier) @variable.parameter)
|
(parameter name: (identifier) @variable.parameter)
|
||||||
|
|
||||||
|
(intrinsic_call_expression function: (identifier) @function.builtin)
|
||||||
(call_expression function: (expression (identifier) @function))
|
(call_expression function: (expression (identifier) @function))
|
||||||
(call_expression function: (expression (field_expression field: (identifier) @function)))
|
(call_expression function: (expression (field_expression field: (identifier) @function)))
|
||||||
(field_expression field: (identifier) @property)
|
(field_expression field: (identifier) @property)
|
||||||
|
|||||||
@@ -2696,6 +2696,10 @@
|
|||||||
"type": "SYMBOL",
|
"type": "SYMBOL",
|
||||||
"name": "field_expression"
|
"name": "field_expression"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "SYMBOL",
|
||||||
|
"name": "intrinsic_call_expression"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "SYMBOL",
|
"type": "SYMBOL",
|
||||||
"name": "call_expression"
|
"name": "call_expression"
|
||||||
@@ -3271,6 +3275,39 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"intrinsic_call_expression": {
|
||||||
|
"type": "PREC",
|
||||||
|
"value": 9,
|
||||||
|
"content": {
|
||||||
|
"type": "SEQ",
|
||||||
|
"members": [
|
||||||
|
{
|
||||||
|
"type": "FIELD",
|
||||||
|
"name": "function",
|
||||||
|
"content": {
|
||||||
|
"type": "SYMBOL",
|
||||||
|
"name": "identifier"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "FIELD",
|
||||||
|
"name": "marker",
|
||||||
|
"content": {
|
||||||
|
"type": "STRING",
|
||||||
|
"value": "!"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "FIELD",
|
||||||
|
"name": "arguments",
|
||||||
|
"content": {
|
||||||
|
"type": "SYMBOL",
|
||||||
|
"name": "argument_list"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
"call_expression": {
|
"call_expression": {
|
||||||
"type": "PREC_LEFT",
|
"type": "PREC_LEFT",
|
||||||
"value": 9,
|
"value": 9,
|
||||||
|
|||||||
@@ -730,6 +730,10 @@
|
|||||||
"type": "integer",
|
"type": "integer",
|
||||||
"named": true
|
"named": true
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "intrinsic_call_expression",
|
||||||
|
"named": true
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "multiline_string",
|
"type": "multiline_string",
|
||||||
"named": true
|
"named": true
|
||||||
@@ -1309,6 +1313,42 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "intrinsic_call_expression",
|
||||||
|
"named": true,
|
||||||
|
"fields": {
|
||||||
|
"arguments": {
|
||||||
|
"multiple": false,
|
||||||
|
"required": true,
|
||||||
|
"types": [
|
||||||
|
{
|
||||||
|
"type": "argument_list",
|
||||||
|
"named": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"function": {
|
||||||
|
"multiple": false,
|
||||||
|
"required": true,
|
||||||
|
"types": [
|
||||||
|
{
|
||||||
|
"type": "identifier",
|
||||||
|
"named": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"marker": {
|
||||||
|
"multiple": false,
|
||||||
|
"required": true,
|
||||||
|
"types": [
|
||||||
|
{
|
||||||
|
"type": "!",
|
||||||
|
"named": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "keyed_field_initializer",
|
"type": "keyed_field_initializer",
|
||||||
"named": true,
|
"named": true,
|
||||||
|
|||||||
+107077
-106183
File diff suppressed because it is too large
Load Diff
@@ -96,6 +96,34 @@ sum func(a, b i32) i32 ! Status {
|
|||||||
(enum_literal
|
(enum_literal
|
||||||
(identifier))))))))))))
|
(identifier))))))))))))
|
||||||
|
|
||||||
|
==================
|
||||||
|
Intrinsic calls
|
||||||
|
==================
|
||||||
|
|
||||||
|
main func() void {
|
||||||
|
_ = sizeof ! (i32)
|
||||||
|
}
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
(source_file
|
||||||
|
(function_declaration
|
||||||
|
(identifier)
|
||||||
|
(parameter_list)
|
||||||
|
(type
|
||||||
|
(builtin_type))
|
||||||
|
(block
|
||||||
|
(statement
|
||||||
|
(assignment_statement
|
||||||
|
(expression
|
||||||
|
(sink))
|
||||||
|
(expression
|
||||||
|
(intrinsic_call_expression
|
||||||
|
(identifier)
|
||||||
|
(argument_list
|
||||||
|
(expression
|
||||||
|
(builtin_type))))))))))
|
||||||
|
|
||||||
==================
|
==================
|
||||||
Errdefer
|
Errdefer
|
||||||
==================
|
==================
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
main func() void {
|
||||||
|
_ = sizeof!(i32)
|
||||||
|
# ^^^^^^ function.builtin
|
||||||
|
# ^ operator
|
||||||
|
_ = sizeof(i32)
|
||||||
|
# ^^^^^^ function
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user