Compare commits
72 Commits
0eeacc2e37
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 378eb637c6 | |||
| c2343b54bb | |||
| fa53ca2219 | |||
| ba6052eef3 | |||
| 5244bfbf1b | |||
| 8785912b73 | |||
| a7a32894f3 | |||
| 572ffe7d07 | |||
| 6688822de2 | |||
| 729488e702 | |||
| e0f1d2d7cb | |||
| 081a3fd5df | |||
| 88b94197c9 | |||
| aadce94b42 | |||
| 84b88f6127 | |||
| 9e79d6692b | |||
| c92723bc14 | |||
| f25f76adff | |||
| b9526b5f06 | |||
| 91aa601464 | |||
| f267e8c3cb | |||
| 304880a9aa | |||
| a8593c00f9 | |||
| 96275121be | |||
| 8b50eb7606 | |||
| 21ff291788 | |||
| ec5880e757 | |||
| 07e89e23e1 | |||
| 0d04925b3a | |||
| 63f000dd42 | |||
| c389c19a81 | |||
| 8e153fa84e | |||
| 2c2a310e6d | |||
| 09571ffeb9 | |||
| 9c6215776e | |||
| 5f343ad2d3 | |||
| 17508ff751 | |||
| 402871ef7a | |||
| 1619ea98a3 | |||
| dd00af7731 | |||
| 297f2e3078 | |||
| ad4802a270 | |||
| 05aa7d084a | |||
| ec36b6b861 | |||
| deab47e75e | |||
| 10abba54a5 | |||
| 59bbb197e0 | |||
| 944648fce6 | |||
| 2e09864adf | |||
| 95f90cc306 | |||
| c7e3162ecb | |||
| f9448363e4 | |||
| 9eb7476522 | |||
| e889a99e55 | |||
| 85693e57e1 | |||
| 9f433af724 | |||
| ef91f13e7b | |||
| b09787029d | |||
| cedc63b28b | |||
| 866e28adb8 | |||
| 97f1c06057 | |||
| 1f25e6cd1d | |||
| 3cc750b3b2 | |||
| 7de0b7f268 | |||
| c4fa8e930f | |||
| 1165cfb7c0 | |||
| 0b2055d64b | |||
| 267947e79d | |||
| 5157cf3bcc | |||
| 471896b48a | |||
| eac5b32738 | |||
| 0b0b00a050 |
@@ -1,3 +1,2 @@
|
|||||||
/build/
|
/build/
|
||||||
/grammars/
|
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|||||||
+157
-57
@@ -8,49 +8,81 @@ roadmap and milestone history.
|
|||||||
### source, declarations, and packages
|
### source, declarations, and packages
|
||||||
|
|
||||||
- newline-terminated statements and `#` comments
|
- newline-terminated statements and `#` comments
|
||||||
- immutable `::` bindings, typed mutable `=` locals/globals, and `_` sinks
|
- immutable `name :: value` / `name Type :: value` bindings and mutable `name := value` / `name Type := value` bindings
|
||||||
- immutable package globals, mutable runtime globals, function-local mutable locals, and mutable local declarations initialized with `undefined`
|
- `=` is assignment, including `_ = value` sinks; keyed record initializers and named struct field defaults also use `=`
|
||||||
|
- immutable package globals, mutable runtime globals, function-local mutable locals, and mutable declarations initialized with `undefined`
|
||||||
- package-level functions, globals, native type declarations, and `Name :: alias T`
|
- package-level functions, globals, native type declarations, and `Name :: alias T`
|
||||||
- directory packages with merged declarations
|
- directory packages with merged declarations
|
||||||
- file-local relative imports, import aliases, and qualified member access
|
- file-local relative imports, import aliases, and qualified member access
|
||||||
- transparent declaration aliases with `Name :: alias package.Member`; functions/type factories,
|
- transparent declaration aliases with `Name :: alias package.Member`; functions/type factories,
|
||||||
named types, and globals retain their original declaration or storage identity
|
named types, and globals retain their original declaration or storage identity
|
||||||
- native top-level declarations beginning with `_` are visible only within their source file; locals, fields, parameters, and C declarations are unaffected
|
- bare `@hide` and explicit `@hide:package` make a named top-level declaration package-local;
|
||||||
|
`@hide:file` makes it file-local, and any qualifier may precede its declaration on a separate
|
||||||
|
line; the qualifier words remain valid identifiers, declarations are public by default, and imports are always file-local
|
||||||
- relative `.h` imports as synthetic C header package namespaces
|
- relative `.h` imports as synthetic C header package namespaces
|
||||||
|
- native `name test { ... }` declarations with fallible-void results inferred from `testing.Error`
|
||||||
|
and errors propagated by `try`, plus anonymous
|
||||||
|
transitive `test import "..."` discovery used only by test builds
|
||||||
- root `main` validation with trap executable recovery for missing or unusable entry points
|
- root `main` validation with trap executable recovery for missing or unusable entry points
|
||||||
|
|
||||||
### scalar, aggregate, and pointer types
|
### scalar, aggregate, and pointer types
|
||||||
|
|
||||||
- exact-width integers, `isize`, `usize`, `f32`, `f64`, `bool`, `void`, `anyopaque`, and contextual `int`, `float`, and `range` constraints
|
- exact-width integers, concrete pointer-sized `isize` / `usize`, `f32`, `f64`, `bool`, `void`, `noreturn`, and `anyopaque`; `noreturn` is a bottom type valid as a native function result and coerces to any expected value type; contextual `int` accepts the whole integer family, while `uint` accepts only unsigned native and target-classified C integers
|
||||||
- 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/bitwise expressions, and typed compile-time evaluation 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 casts through keywords or transparent aliases, such as `i32(x)`, `c_float(x)`, or `StringId(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, C, and scalar-backed distinct integer types; the result retains `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`
|
- 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
|
- unsafe `constcast!(value)` for restoring mutability to pointers, optional pointers, and slices without changing their child type or shape
|
||||||
|
- UTF-8 string literals as immutable pointers to static zero-terminated byte arrays, supporting `\\`, `\"`, `\n`, `\r`, `\t`, `\0`, and `\xNN` escapes, 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 `null`, `orelse`, postfix `?`, conditional `if`/`while` unwraps, guarded unwraps, and left-to-right short-circuiting multi-unwraps
|
||||||
- nominal distinct types with exact backing construction, native enums with optional explicit integer backing and explicit backing-to-scalar casts, contextual enum literals, and imported C enums as target-backed integer aliases
|
- nominal distinct types with explicit scalar backing conversion during construction and explicit scalar backing extraction, native enums with optional explicit integer backing and explicit backing-to-scalar casts, contextual enum literals, and imported C enums as target-backed integer aliases
|
||||||
- source-order native structs, opaque nominal records with `Name :: opaque`, complete `c_struct { ... }`, keyed record literals, native untagged unions, and native tagged unions `union(Enum)` / `union(enum)`
|
- compiler-reordered native structs with fields laid out by decreasing alignment (declaration order breaks ties and remains the reflection/diagnostic order), opaque nominal records with `Name :: opaque`, complete source-order `c_struct { ... }`, keyed record literals, native untagged unions, and native tagged unions `union(Enum)` / `union(enum)`
|
||||||
|
- named native struct fields may declare defaults with `field T = expression`; keyed literals use defaults for omitted fields and explicit initializers override them
|
||||||
- void-payload tagged-union variants, anonymous struct payloads, contextual `.variant`, `.variant{payload}`, and `.variant{field = value}` construction
|
- void-payload tagged-union variants, anonymous struct payloads, contextual `.variant`, `.variant{payload}`, and `.variant{field = value}` construction
|
||||||
- native sum composition with `A | B` for unbacked enums and tagged unions, using program-global `u16` variant ids
|
- native sum composition with `A | B` for unbacked enums and tagged unions, optionally grouped as `(A | B)`, using program-global `u16` variant ids
|
||||||
- fallible channel types `T ! E`, where `E` is a native enum/tagged union or supported sum composition
|
- fallible channel types `T ! E`, where `E` is a native enum, native struct, tagged union, or supported sum composition; `void ! E` functions complete successfully on fallthrough, and void-success `catch` handlers may fall through without `yield`
|
||||||
|
- bodyful local functions and root `main` may write `T!` to infer a specialization-local error channel from propagated `try` expressions and concretely typed error returns; inference composes only existing named error types, never synthesizes variants, and requires at least one inferred error
|
||||||
|
|
||||||
|
#### distinct types
|
||||||
|
|
||||||
|
`Name :: distinct T` creates a nominal identity and reuses `T`'s runtime representation. When
|
||||||
|
`T` is a concrete numeric scalar, construction first applies the corresponding explicit scalar
|
||||||
|
cast, so `UserID(index)` is sufficient for `UserID :: distinct u32` even when `index` is `usize`.
|
||||||
|
There is still no implicit conversion in either direction. Construction with a non-scalar or
|
||||||
|
distinct immediate backing requires that exact backing type. An explicit scalar cast extracts one
|
||||||
|
layer: `u32(id)` works for `UserID`, while nested distinct values must be peeled one declared layer
|
||||||
|
at a time.
|
||||||
|
|
||||||
|
Scalar-backed distinct values support the operations of their representation while preserving the
|
||||||
|
nominal result type: checked integer `+`, `-`, `*`, unary `-`, bitwise operators, shifts,
|
||||||
|
comparisons, and compound assignments; float arithmetic, unary `-`, comparisons, and compound
|
||||||
|
assignments; and boolean equality/inequality. Integer literals and float literals are contextual.
|
||||||
|
Typed backing values and separate distinct identities remain incompatible in ordinary operations;
|
||||||
|
an explicit constructor is required to cross that boundary. Distinct integers require an explicit
|
||||||
|
`usize` cast for indices and slice bounds; `minval!` / `maxval!` return the distinct type.
|
||||||
|
Runtime and comptime behavior match.
|
||||||
|
|
||||||
|
`typeinfo!(Distinct).backing` reports the immediate declared backing. Standard formatting peels
|
||||||
|
distinct layers recursively, so all scalar format verbs behave like the final scalar backing.
|
||||||
|
|
||||||
#### native record constraint fields
|
#### native record constraint fields
|
||||||
|
|
||||||
A direct `int`, `float`, or `range` field in a named native struct or union is a
|
A direct `int`, `uint`, `float`, or `range` field in a named native struct or union is a
|
||||||
program-wide constraint, not per-value polymorphism. Before record layout, all reachable keyed
|
program-wide constraint, not per-value polymorphism. Before record layout, all reachable keyed
|
||||||
constructors, field assignments, and concrete uses of field reads contribute demands and the field
|
constructors, field assignments, and concrete uses of field reads contribute demands and the field
|
||||||
resolves once to one concrete runtime type. Compatible scalar demands widen normally. Integer
|
resolves once to one concrete runtime type. Compatible scalar demands widen normally. Integer
|
||||||
literals remain provisional until inference settles, so a later `usize` use can resolve an `int`
|
literals remain provisional until inference settles, so a later `usize` use can resolve an `int`
|
||||||
field to `usize`; otherwise literal-only `int` fields use the widest smallest-signed type required,
|
field to `usize`; otherwise literal-only `int` fields use the widest smallest-signed type required,
|
||||||
and literal-only `float` fields use `f64`.
|
literal-only `uint` fields use the widest smallest-unsigned type required, and literal-only `float`
|
||||||
|
fields use `f64`.
|
||||||
|
|
||||||
An undemanded field or incompatible demands are errors. This inference applies only to direct
|
An undemanded field or incompatible demands are errors. This inference applies only to direct
|
||||||
fields of named native records. `c_struct` fields, nested constraints such as `[]int`, and fields in
|
fields of named native records. `c_struct` fields, nested constraints such as `[]int` / `[]uint`, and fields in
|
||||||
anonymous generated records still require concrete runtime types.
|
anonymous generated records still require concrete runtime types.
|
||||||
|
|
||||||
#### keyword member names
|
#### keyword member names
|
||||||
@@ -84,82 +116,146 @@ fields. `_` is not a keyword member name.
|
|||||||
### expressions and control flow
|
### expressions and control flow
|
||||||
|
|
||||||
- checked integer `+ - *`, unary `-`, float-only `/`, IEEE float arithmetic, comparisons, `!`, `and`, and `or`
|
- checked integer `+ - *`, unary `-`, float-only `/`, IEEE float arithmetic, comparisons, `!`, `and`, and `or`
|
||||||
- assignments and compound assignments `+= -= *= /=` with single evaluation of complex lvalues; `/=` is float-only
|
- Zig-style integer bitwise complement `~`, binary `&`, `|`, `xor`, shifts `<<` / `>>`, and saturating left shift `<<|`; postfix `^` remains pointer dereference
|
||||||
- field access through struct values and pointers, index/slice bounds contextually coerced to `usize`, and unsigned narrower index support
|
- assignments and compound assignments `+= -= *= /= &= |= xor= <<= >>= <<|=` with single evaluation of complex lvalues; `/=` is float-only and `xor=` is contiguous
|
||||||
- boolean `if` / `else if` / `else`, braceless single-statement branches, and optional parenthesized conditions
|
- field access through struct values and pointers, exact `usize` indices and slice bounds, and contextual integer constants in those positions
|
||||||
- `while` loops with optional post-iteration update clauses
|
- boolean `if` / `else if` / `else` and `for` loops with braceless single-statement bodies when the preceding expression is parenthesized or a function call
|
||||||
- `for` loops over ranges, arrays, slices, and pointers-to-arrays with copy captures, pointer captures `|@item|`, and optional `usize` index captures
|
- `while` loops with conditional unwrap captures and guards plus optional post-iteration update clauses
|
||||||
- `break`, `continue`, labeled `break :label`, labeled `continue :label`, and labeled plain blocks
|
- `for` loops over ranges, arrays, slices, and pointers-to-arrays with copy captures, pointer captures `|@item|`, and optional `usize` index captures; `inline for` specializes a comptime aggregate into one checked body per element
|
||||||
|
- `break`, `continue`, labeled `break :label`, labeled `continue :label`, and labeled plain blocks; `break :label` can cross nested scopes to exit a labeled block
|
||||||
- bare block scopes, `defer`, and fallible-function `errdefer` with optional error capture; cleanup is block-scoped and LIFO
|
- bare block scopes, `defer`, and fallible-function `errdefer` with optional error capture; cleanup is block-scoped and LIFO
|
||||||
- value blocks, value `if`, value loops, value `match`, `yield`, and labeled `yield :label value`
|
- always-trapping `unreachable`, a `noreturn` expression that diagnoses use during comptime evaluation and terminates the current runtime path
|
||||||
|
- bare void `return`, same-line `return value`, value blocks, value `if` with implicit single-expression branches, value loops, value `match`, and strictly value-producing `yield value` / `yield :label value`
|
||||||
- `match` statements/expressions over enums, tagged unions, and scalars, including exhaustiveness checks, payload captures, pointer payload captures, multi-pattern arms, and scalar range patterns
|
- `match` statements/expressions over enums, tagged unions, and scalars, including exhaustiveness checks, payload captures, pointer payload captures, multi-pattern arms, and scalar range patterns
|
||||||
- fallible `try`, fallback `catch`, and `catch |e| { ... }` handler blocks
|
- a final `inline |value|:` enum arm or `inline |payload[, tag]|:` tagged-union arm generates one specialized arm for each variant not covered earlier; enum values and optional tags are comptime-known, while union payloads keep their concrete variant type
|
||||||
|
- fallible `try` and uniform `catch [|e|] value_source` fallbacks; captures work with ordinary expressions, value blocks, and value-producing `if` / loops / `match`
|
||||||
- direct `return match ...` and `yield match ...` value-control-flow operands
|
- direct `return match ...` and `yield match ...` value-control-flow operands
|
||||||
|
|
||||||
|
#### bitwise operations
|
||||||
|
|
||||||
|
Bitwise operands must be concrete integers. `~` preserves its operand type. `&`, `xor`, and `|`
|
||||||
|
use the ordinary common-integer widening rules; incompatible fixed integer families remain errors.
|
||||||
|
Shifts preserve the left operand type and require a concrete unsigned count. `>>` is arithmetic for
|
||||||
|
signed integers and logical for unsigned integers.
|
||||||
|
|
||||||
|
Ordinary `<<` and `>>` reject compile-time-known counts at least as large as the left type's bit
|
||||||
|
width and trap for such runtime counts. `<<` discards shifted-out bits. Saturating `<<|` permits any
|
||||||
|
unsigned count: zero remains zero, unsigned nonzero values clamp to the type maximum, and signed
|
||||||
|
values clamp to the minimum or maximum according to their sign.
|
||||||
|
|
||||||
|
Binary precedence, from tightest to loosest, is:
|
||||||
|
|
||||||
|
```text
|
||||||
|
* /
|
||||||
|
+ -
|
||||||
|
<< >> <<|
|
||||||
|
& xor |
|
||||||
|
== != < > <= >=
|
||||||
|
and
|
||||||
|
or
|
||||||
|
```
|
||||||
|
|
||||||
|
Each level is left-associative. Because `|` also delimits `if`, `while`, and `for` captures, a
|
||||||
|
bitwise-OR header expression must be parenthesized before a capture list, for example
|
||||||
|
`while (flags | mask) |value| { ... }`.
|
||||||
|
|
||||||
#### 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 these six division bang calls select integer-division behavior. Bare calls such as
|
||||||
`math.div_floor(a, b)` resolves to an ordinary package function.
|
`divfloor(a, b)` and qualified calls such as `math.divfloor(a, b)` resolve to ordinary functions.
|
||||||
|
|
||||||
|
#### typed memory operations
|
||||||
|
|
||||||
|
`memcopy!(destination, source)` and `memset!(destination, value)` are available at runtime and
|
||||||
|
comptime. A destination must be a mutable slice or mutable pointer-to-array. A `memcopy!` source
|
||||||
|
may be a slice or pointer-to-array; many-item pointers must first be sliced. Array pointers are
|
||||||
|
treated as regions containing their explicit logical elements, including a sentinel only when it
|
||||||
|
is part of that array region.
|
||||||
|
|
||||||
|
`memcopy!` requires the same element type after alias resolution and the same element count. Its
|
||||||
|
non-empty regions must not overlap. Comptime calls diagnose unequal lengths and overlap; runtime
|
||||||
|
calls trap for either condition or if the element count cannot be converted to a byte count.
|
||||||
|
Zero-sized elements still require equal counts. Empty copies are no-ops and may name the same
|
||||||
|
region.
|
||||||
|
|
||||||
|
`memset!` coerces `value` to the destination element type. Use `memset!(destination, 0)` to zero a
|
||||||
|
region; there is no separate `memzero!`, and `memset!` does not promise secure zeroing. Copying or
|
||||||
|
filling with `undefined` transfers undefined state without reading it. Each operand is evaluated
|
||||||
|
exactly once. Bare functions named `memcopy` or `memset` remain ordinary user functions.
|
||||||
|
|
||||||
### functions, C interop, and linking
|
### functions, C interop, and linking
|
||||||
|
|
||||||
- demand-monomorphized Brolang and C-ABI functions
|
- demand-monomorphized Brolang and C-ABI functions
|
||||||
- integer comptime value parameters such as `make_array func($N usize) [N]u8`, specialized by value and omitted from the runtime ABI
|
- integer comptime value parameters such as `make_array func($N usize) [N]u8`, specialized by value and omitted from the runtime ABI
|
||||||
- explicit comptime type parameters such as `max func($T type, a, b T) T`, specialized by type and omitted from the runtime ABI
|
- explicit comptime type parameters such as `max func($T type, a, b T) T`, specialized by type and omitted from the runtime ABI
|
||||||
- leading comptime type/integer parameters may be omitted when uniquely recoverable from runtime argument types or the immediate expected result; explicit calls remain valid
|
- later comptime value parameters may depend on earlier type parameters, as in `factory func($T type, $default T) type`
|
||||||
- comptime parameters must form one leading prefix before all runtime parameters
|
- comptime parameters may appear anywhere, are erased from the runtime ABI, and accept recursively stable booleans, integers, floats, types, immutable bytes, enums, fixed arrays, records/tuples, optionals, tagged unions, and bare function identities; equal structural values and aliases of one function declaration share specializations, while distinct declarations remain distinct and pointers, general slices, fallibles, ranges, untagged unions, and undefined values have no stable comptime identity
|
||||||
|
- comptime parameters may be omitted when uniquely recoverable from runtime arguments, the immediate expected result, or exact type-factory provenance; `_` is an explicit inference hole
|
||||||
|
- direct bodyful value calls with no runtime parameters, including parameterless and all-`$` functions, are evaluated at comptime when their resolved result can materialize; otherwise they retain their zero-argument runtime specialization, while a reached `compile_error!` remains a diagnostic
|
||||||
- forced typed comptime expressions such as `$sum(1, 2)`, `$Point { x = 1, y = 2 }`, and comptime value blocks such as `${ yield 4 }`
|
- forced typed comptime expressions such as `$sum(1, 2)`, `$Point { x = 1, y = 2 }`, and comptime value blocks such as `${ yield 4 }`
|
||||||
- comptime execution for bodyful Brolang functions with mutable locals, loops, `defer`, `match`, `try`/`catch`, pointer/slice storage mutation, pointer captures, and calls through comptime-known function values
|
- comptime execution for bodyful Brolang functions with mutable locals, loops, `defer`, `match`, `try`/`catch`, exact type `==`/`!=`, pointer/slice storage mutation, pointer captures, and calls through comptime-known function values; `undefined` storage may be initialized at comptime, but remaining poison cannot be observed
|
||||||
- comptime type factories such as `Box func($T type) type { return struct { value T } }`; calls like `Box(i32)` are concrete nominal types and may appear anywhere a type is expected
|
- comptime type factories such as `Box func($T type) type { return struct { value T } }`; calls like `Box(i32)` are concrete nominal types and may appear anywhere a type is expected
|
||||||
|
- `struct_type!(layout, names, types, defaults)` constructs a nominal record type from comptime fixed arrays or tuples and is valid in every type position; layout is `.auto` or `.c`, bare `null` means a required field, and `some!(null)` installs an optional `null` default
|
||||||
|
- `some!(value)` explicitly constructs the present branch of an expected optional, including nested optionals where `some!(null)` differs from outer `null`
|
||||||
|
- tuple types are unnamed-field structs (`struct { i32, []u8 }`), tuple values use `{1, "bro"}` / `{1,}` / `{}`, and fields use canonical numeric names such as `.0`
|
||||||
|
- anonymous keyed records use `{x = 1, name = "bro"}`; without context their declaration-ordered names and inferred value types form a structurally interned record type, while a record context applies that type's coercions and field defaults; `{}` remains an empty tuple without context and constructs an empty contextual record when a record is expected
|
||||||
|
- `typeinfo!`, `field!`, `compile_error!`, and semantic `inline for` provide compile-time record and enum reflection and heterogeneous static expansion without runtime metadata; enum reflection exposes declaration-ordered fields, reflected aggregates remain persistent compile-time values, and inline-loop `break` / `continue` must be selected entirely at comptime
|
||||||
|
- `tag!(value)` reads a tagged union's active discriminant and folds when the value is comptime-known; `tagname!(enum_value)` requires a comptime-known enum value and returns its immutable declaration name
|
||||||
- bodyful `c_func` definitions and bodyless `c_func` declarations with exact external symbol names
|
- bodyful `c_func` definitions and bodyless `c_func` declarations with exact external symbol names
|
||||||
- concrete-only C signatures, C variadic declarations/calls, and C default argument promotions
|
- concrete-only C signatures, C variadic declarations/calls, and C default argument promotions
|
||||||
- native function pointer values and types with `@func(...) R`, fallible `@func(...) R ! E`, optional `?@func(...) R`, and non-variadic native indirect calls
|
- bare `func(...) R` and `c_func(...) R` values are comptime-only declaration identities; arrays, native records, optionals, and tagged unions containing one are also comptime-only and cannot enter runtime storage, ordinary ABI parameters/results, runtime globals, or C-layout records
|
||||||
|
- native function pointer values and types use `@func(...) R`, fallible `@func(...) R ! E`, and optional `?@func(...) R`; bare native identities implicitly materialize compatible pointers when a runtime pointer context requires one, but pointers never convert back to bare identities
|
||||||
|
- statically known bare identities and comptime-known pointers lower calls directly; native indirect calls remain available for runtime-selected non-variadic pointers
|
||||||
- Apple Silicon C ABI lowering for scalars, pointers, fixed-signature plain records/unions, small aggregates, homogeneous float aggregates, and indirect aggregate returns
|
- Apple Silicon C ABI lowering for scalars, pointers, fixed-signature plain records/unions, small aggregates, homogeneous float aggregates, and indirect aggregate returns
|
||||||
- imported C typedefs, scalar constants, enum constants, fixed arrays, complete plain structs/unions, C `void*` as nullable `anyopaque` pointers, and pointers to opaque records
|
- imported C typedefs, scalar constants, enum constants, fixed arrays, complete plain structs/unions, C `void*` as nullable `anyopaque` pointers, and pointers to opaque records
|
||||||
- imported external C object variables, including mutable variables and immutable object globals
|
- imported external C object variables, including mutable variables and immutable object globals
|
||||||
- object-like scalar and plain record/union macro constants
|
- object-like scalar and plain record/union macro constants
|
||||||
- supported static inline C functions through generated external wrappers
|
- supported static inline C functions through generated external wrappers
|
||||||
- C function pointer types, imported nullable callback typedefs, concrete `c_func` callback values, and postfix calls through non-null function pointers
|
- C function pointer types, imported nullable callback typedefs, bare `c_func` identities with one-way pointer materialization, and postfix calls through non-null function pointers
|
||||||
- `brolang translate-c <header.h>` for native `.bro` bindings from supported C declarations
|
- `brolang translate-c <header.h>... [--output-dir <dir>]` for native `.bro` bindings from supported C declarations, with package-wide declaration deduplication when writing multiple headers
|
||||||
- `brolang --translate-c stdio.h` for offline bindings from Zig-bundled standard C headers
|
- `brolang --translate-c stdio.h` for offline bindings from Zig-bundled standard C headers
|
||||||
- ordered linking of additional C sources, objects, archives, library paths, and libraries through compiler CLI options
|
- ordered linking of additional C sources, objects, archives, library paths, and libraries through compiler CLI options
|
||||||
|
|
||||||
@@ -168,7 +264,12 @@ The six spellings are reserved only as direct unqualified calls. A qualified cal
|
|||||||
- root `std` re-exports `ArrayList(T)` while its operations remain in `std/arraylist`
|
- root `std` re-exports `ArrayList(T)` while its operations remain in `std/arraylist`
|
||||||
- `std/mem` generic slice equality, allocator contract with raw byte operations, typed `empty` / `alloc` / `realloc` / `free`, overflow checks, zero-sized-type support, and failure-preserving reallocation
|
- `std/mem` generic slice equality, allocator contract with raw byte operations, typed `empty` / `alloc` / `realloc` / `free`, overflow checks, zero-sized-type support, and failure-preserving reallocation
|
||||||
- `std/arraylist` generic `ArrayList(T)` with direct `items` slice access, explicit capacity, allocator ownership, fallible reserve/append, clear, and deinit
|
- `std/arraylist` generic `ArrayList(T)` with direct `items` slice access, explicit capacity, allocator ownership, fallible reserve/append, clear, and deinit
|
||||||
- `std/io` explicit `Io` capabilities, `Reader`/`Writer` stream values, one-shot `read`/`write`, and allocation-free `write_all`
|
- `std/meta` reflection records plus `EnumFieldStruct(E, Field, default ?Field)`, implemented with `struct_type!`; it produces a record with one field per native enum member in declaration order, where outer `null` means no field default
|
||||||
|
- `std/io` explicit `Io` capabilities, provider-bound `Reader`/`Writer` handles, existing-file open/close operations, allocation-free `write_all`, and comptime-expanded writer-first `print`; formatting supports natural `{}`, byte `{s}`, decimal `{d}`, integer `{b}` / `{o}` / `{x}` / `{X}`, byte-character `{c}`, scientific float `{e}`, recursively scalar-backed distinct values, and `{{` / `}}`, with malformed formats and incompatible tuple fields rejected at comptime
|
||||||
|
- entry points are either `main func() ...` or `main func(init process.Init) ...`; their success channel is `void`, `i32`, or `int` and may have an error channel; an unhandled entry error exits with status 1. `std/process.Init` carries startup capabilities, currently only `io`, while the system provider remains hidden inside `std/io`
|
||||||
|
- `std/debug.print` is an allocation-free, failure-ignoring stderr escape hatch independent of `process.Init`
|
||||||
|
- `std/testing` supplies fallible `expect`, expected-first `expect_equal`, and exact compile-time `expect_type`; direct calls through
|
||||||
|
an alias of exactly `@std/testing` receive compiler-injected source locations
|
||||||
|
|
||||||
### compiler behavior
|
### compiler behavior
|
||||||
|
|
||||||
@@ -176,21 +277,20 @@ The six spellings are reserved only as direct unqualified calls. A qualified cal
|
|||||||
- lazy semantic checking of demanded function specializations
|
- lazy semantic checking of demanded function specializations
|
||||||
- static, eager runtime, mutable runtime, and deferred problematic globals with cycle diagnostics
|
- static, eager runtime, mutable runtime, and deferred problematic globals with cycle diagnostics
|
||||||
- demand-driven LLVM declarations for referenced foreign functions
|
- demand-driven LLVM declarations for referenced foreign functions
|
||||||
- root `main` may be parameterless or accept the canonical `@std/io Io`; the injected form is called through a synthesized no-argument C entry point
|
- root `main` may be parameterless or accept canonical `@std/process Init`; the generated C entry point obtains the hidden system I/O provider and constructs the init value
|
||||||
|
- `brolang test [root]` reuses `build.bro`, discovers only explicit test-import edges, skips the
|
||||||
|
application entry point, and runs tests sequentially while continuing after assertion failures
|
||||||
- replaceable dynamically loaded libclang C-import backend
|
- replaceable dynamically loaded libclang C-import backend
|
||||||
- C-header import caching by canonical path, target, include paths, and defines
|
- C-header import caching by canonical path, target, include paths, and defines
|
||||||
|
|
||||||
## PLANNED / DEFERRED
|
## PLANNED / DEFERRED
|
||||||
|
|
||||||
- aggregate comptime parameters and stable aggregate specialization keys
|
|
||||||
- tuples and native Brolang variadic functions
|
|
||||||
- exporting Brolang functions to C and broader target-specific C ABI lowering
|
- exporting Brolang functions to C and broader target-specific C ABI lowering
|
||||||
- non-plain C record layouts such as bitfields, packed records, flexible arrays, qualified fields, and C variadic record arguments
|
- non-plain C record layouts such as bitfields, packed records, flexible arrays, qualified fields, and C variadic record arguments
|
||||||
- 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, reflection payloads beyond records/enums, 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
|
||||||
- distinct-type backing operators and reverse explicit conversions
|
|
||||||
- string concatenation operator
|
- string concatenation operator
|
||||||
|
|||||||
@@ -9,17 +9,15 @@ odin build . -out:build/brolang
|
|||||||
./build/prototype
|
./build/prototype
|
||||||
```
|
```
|
||||||
|
|
||||||
Programs may receive the system I/O capability explicitly. Readers and writers
|
Programs may receive the system I/O capability explicitly. Standard-stream
|
||||||
pair that implementation with a stream; `main func() ...` remains valid.
|
helpers bind the provider, handle, and callback; `main func() ...` remains valid.
|
||||||
|
|
||||||
```bro
|
```bro
|
||||||
io :: import "@std/io"
|
io :: import "@std/io"
|
||||||
|
process :: import "@std/process"
|
||||||
|
|
||||||
main func(system io.Io) void {
|
main func(init process.Init) void {
|
||||||
io.write_all(io.Writer {
|
io.print(io.stdout(init.io), "hello {s} {d}\n", {"bro", 37}) catch |_| {
|
||||||
impl = system,
|
|
||||||
stream = .stdout,
|
|
||||||
}, "hello\n") catch |_| {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -50,9 +48,10 @@ Instead of passing these on the command line, a project can describe its build
|
|||||||
in Brolang itself. `brolang new <project>` creates a project with local `std`
|
in Brolang itself. `brolang new <project>` creates a project with local `std`
|
||||||
and `ffi` copies; `brolang init` does the same for the current directory without
|
and `ffi` copies; `brolang init` does the same for the current directory without
|
||||||
overwriting existing files. `brolang build [root]` reads a `config` constant
|
overwriting existing files. `brolang build [root]` reads a `config` constant
|
||||||
from `root/build.bro` and compiles the program package it names. Without
|
from exactly one of `root/build.bro` or `root/build.hon` and compiles the
|
||||||
`root`, it searches the current directory and parents for the nearest
|
program package it names. Without `root`, it searches the current directory
|
||||||
`build.bro`. Build outputs are written to `root/build/<name>`.
|
and parents for the nearest build file. Build outputs are written to
|
||||||
|
`root/build/<name>`.
|
||||||
|
|
||||||
```bro
|
```bro
|
||||||
b :: import "@std/build"
|
b :: import "@std/build"
|
||||||
@@ -69,12 +68,34 @@ config :: b.BuildConfig{
|
|||||||
```
|
```
|
||||||
|
|
||||||
`name` is a plain executable name, and `source` is the program package relative
|
`name` is a plain executable name, and `source` is the program package relative
|
||||||
to `build.bro`. The list fields map to the matching C options (`libraries` →
|
to the build file. The list fields map to the matching C options (`libraries` →
|
||||||
`-l`, `lib_paths` → `-L`, `includes` → `-I`, `defines` → C defines, `links` →
|
`-l`, `lib_paths` → `-L`, `includes` → `-I`, `defines` → C defines, `links` →
|
||||||
linker inputs) and, like those flags, their paths are relative to the invocation
|
linker inputs) and, like those flags, their paths are relative to the invocation
|
||||||
directory. Lists take the address of an array literal; empty lists are written
|
directory. Lists take the address of an array literal; empty lists are written
|
||||||
`&[]`. See `examples/build/` for runnable projects.
|
`&[]`. See `examples/build/` for runnable projects.
|
||||||
|
|
||||||
|
Projects can declare tests directly and run them with `brolang test [root]`.
|
||||||
|
The command reads the same build file—exactly one of `build.bro` or
|
||||||
|
`build.hon`—writes `build/<name>-test`, and reuses its C link inputs,
|
||||||
|
libraries, include paths, and defines.
|
||||||
|
|
||||||
|
```bro
|
||||||
|
math :: import "../math"
|
||||||
|
testing :: import "@std/testing"
|
||||||
|
|
||||||
|
test import "../math"
|
||||||
|
|
||||||
|
addition test {
|
||||||
|
try testing.expect(math.add(20, 22) == 42)
|
||||||
|
try testing.expect_equal(42, math.add(20, 22))
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`test import` discovers tests transitively without creating a namespace;
|
||||||
|
calling package code still requires an ordinary import. Ordinary imports do
|
||||||
|
not discover dependency tests. Assertions report their source location, a
|
||||||
|
failure ends only the current test, and the runner continues with the suite.
|
||||||
|
|
||||||
Relative `.h` imports create synthetic package namespaces backed by libclang:
|
Relative `.h` imports create synthetic package namespaces backed by libclang:
|
||||||
|
|
||||||
```bro
|
```bro
|
||||||
@@ -95,7 +116,12 @@ pointer-only. Header imports never add linker inputs; implementations must still
|
|||||||
be supplied explicitly with the C-prefixed linking options. Set
|
be supplied explicitly with the C-prefixed linking options. Set
|
||||||
`BROLANG_LIBCLANG_PATH` when libclang is not installed in a standard location.
|
`BROLANG_LIBCLANG_PATH` when libclang is not installed in a standard location.
|
||||||
For offline bindings, `brolang --translate-c stdio.h` resolves standard C
|
For offline bindings, `brolang --translate-c stdio.h` resolves standard C
|
||||||
headers through the Zig libc headers used by the backend.
|
headers through the Zig libc headers used by the backend. Multiple headers can
|
||||||
|
be generated into one deduplicated package:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
brolang --translate-c stdio.h stdlib.h unistd.h fcntl.h errno.h --output-dir ffi/c
|
||||||
|
```
|
||||||
|
|
||||||
```bro
|
```bro
|
||||||
native :: import "../include/native.h"
|
native :: import "../include/native.h"
|
||||||
@@ -120,15 +146,20 @@ call_mapper func(mapper native.Imported_Mapper) c_int {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Native Brolang function pointer values use `*func(...) R`, with fallible
|
Native Brolang function pointer values use `@func(...) R`, with fallible
|
||||||
channels written on the result:
|
channels written on the result:
|
||||||
|
|
||||||
```bro
|
```bro
|
||||||
call func(callback *func(value i32) i32, value i32) i32 {
|
call func(callback @func(value i32) i32, value i32) i32 {
|
||||||
return callback(value)
|
return callback(value)
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Bare `func(...) R` and `c_func(...) R` values are comptime-only declaration
|
||||||
|
identities. They implicitly materialize compatible pointers in runtime pointer
|
||||||
|
contexts; pointers do not convert back to bare identities. Aggregates containing
|
||||||
|
bare identities are likewise comptime-only.
|
||||||
|
|
||||||
Bodyless manual and imported C functions may be variadic:
|
Bodyless manual and imported C functions may be variadic:
|
||||||
|
|
||||||
```bro
|
```bro
|
||||||
@@ -177,33 +208,51 @@ mem :: import "@std/mem"
|
|||||||
value :: math.sum(other_math.value, 1)
|
value :: math.sum(other_math.value, 1)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Top-level declarations are public by default. Prefix a declaration with bare `@hide`
|
||||||
|
to make it package-local, or spell the scope explicitly with `@hide:package` or
|
||||||
|
`@hide:file`. The qualifier is contextual, so `hide`, `package`, and `file` remain
|
||||||
|
available as identifiers. Imports are always file-local and cannot use visibility
|
||||||
|
qualifiers or be re-exported:
|
||||||
|
|
||||||
|
```bro
|
||||||
|
@hide
|
||||||
|
shared_helper func() i32 { return 42 }
|
||||||
|
|
||||||
|
@hide:file
|
||||||
|
implementation_detail func() i32 { return shared_helper() }
|
||||||
|
```
|
||||||
|
|
||||||
Current prototype features:
|
Current prototype features:
|
||||||
|
|
||||||
- Newline-terminated, multiline statements; `}` may terminate a block's final statement
|
- Newline-terminated, multiline statements; `}` may terminate a block's final statement
|
||||||
- `#` comments
|
- `#` comments
|
||||||
- Immutable `::` bindings, typed mutable `=` locals/globals, and `_` sinks
|
- Immutable inferred/typed `::` bindings, mutable inferred/typed `:=` 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
|
||||||
- Contextual integer constants and compile-time folding of addition and unary negation trees
|
- Contextual integer constants and typed compile-time evaluation of arithmetic and Zig-style bitwise expressions
|
||||||
|
- Integer `~`, `&`, `|`, `xor`, guarded `<<` / `>>`, saturating `<<|`, and their compound assignments; postfix `^` remains pointer dereference
|
||||||
|
- Scalar-backed nominal `distinct` types with same-identity runtime/comptime operators, explicit backing extraction casts, integer bounds, reflection, and recursive standard formatting
|
||||||
- Directory packages with merged declarations and file-local relative imports
|
- Directory packages with merged declarations and file-local relative imports
|
||||||
- Relative C header imports as synthetic package namespaces
|
- Relative C header imports as synthetic package namespaces
|
||||||
- Plain imported C structs/unions, fixed arrays, and C function pointer typedefs, including keyed literals, field access, callbacks, and Apple Silicon by-value ABI lowering
|
- Plain imported C structs/unions, fixed arrays, and C function pointer typedefs, including keyed literals, field access, callbacks, and Apple Silicon by-value ABI lowering
|
||||||
- Qualified imported globals and functions with package-aware symbol mangling
|
- Qualified imported globals and functions with package-aware symbol mangling
|
||||||
- Demand-monomorphized Brolang and C-ABI functions
|
- Demand-monomorphized Brolang and C-ABI functions
|
||||||
- Integer and type comptime parameters (`func($N usize) [N]u8`, `func($T type, value T) T`) specialized by explicit or uniquely inferred leading comptime arguments
|
- Recursively stable comptime values—including booleans, integers, floats, types, immutable bytes, enums, fixed arrays, records/tuples, optionals, tagged unions, and bare function identities—may be interleaved with runtime parameters, are erased from the ABI, and specialize from explicit arguments, declaration identity, or exact inference provenance
|
||||||
- Forced typed comptime expressions (`$sum(1, 2)`, `$Point { x = 1, y = 2 }`) and comptime value blocks (`${ yield 4 }`)
|
- Forced typed comptime expressions (`$sum(1, 2)`, `$Point { x = 1, y = 2 }`) and comptime value blocks (`${ yield 4 }`)
|
||||||
- Zig-style comptime type factories returning anonymous native structs (`Box func($T type) type`, used as `Box(i32)`)
|
- Zig-style comptime type factories returning anonymous native structs (`Box func($T type) type`, used as `Box(i32)`)
|
||||||
- Comptime execution for bodyful Brolang functions with mutable locals, loops, `defer`/`errdefer`, `match`, `try`/`catch`, pointer/slice storage mutation, pointer captures, and calls through comptime-known function values
|
- Comptime execution for bodyful Brolang functions with mutable locals, loops, `defer`/`errdefer`, `match`, `try`/`catch`, pointer/slice storage mutation, pointer captures, and calls through comptime-known function values
|
||||||
- Native function pointer values and types (`*func(...) R`, `*func(...) R ! E`, `?*func(...) R`)
|
- Comptime-only native and C function identities (`func(...) R`, `c_func(...) R`) with structural comptime-only propagation through aggregates
|
||||||
|
- Native function pointer values and types (`@func(...) R`, `@func(...) R ! E`, `?@func(...) R`) with implicit bare-to-pointer materialization
|
||||||
- Typed allocation/reallocation through `std/mem` and generic dynamic arrays through `std/arraylist`
|
- Typed allocation/reallocation through `std/mem` and generic dynamic arrays through `std/arraylist`
|
||||||
- Bodyless concrete C function declarations with exact external symbol names
|
- Bodyless concrete C function declarations with exact external symbol names
|
||||||
- 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
|
||||||
|
- Runtime/comptime typed `memcopy!` and `memset!` over slices and pointers-to-arrays, with checked lengths and overlap
|
||||||
- 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`
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
- unsigned integers, floats, and target-dependent c scalar types
|
- unsigned integers, floats, and target-dependent c scalar types
|
||||||
- atomic `c_*` primitive types remain distinct until target-aware lowering
|
- atomic `c_*` primitive types remain distinct until target-aware lowering
|
||||||
- `c_func`, complete `c_struct`, and pointer-only `opaque`; `c` remains an ordinary identifier
|
- `c_func`, complete `c_struct`, and pointer-only `opaque`; `c` remains an ordinary identifier
|
||||||
- keep binding mutability (`::` / `=`) separate from element or pointee mutability (`mut`)
|
- keep binding mutability (`::` / `:=`) separate from element or pointee mutability (`mut`)
|
||||||
- arrays and indexing
|
- arrays and indexing
|
||||||
- `[N]T`: array with `N` logical elements
|
- `[N]T`: array with `N` logical elements
|
||||||
- `[N;S]T`: array with `N` logical elements followed by sentinel `S`
|
- `[N;S]T`: array with `N` logical elements followed by sentinel `S`
|
||||||
@@ -93,11 +93,11 @@
|
|||||||
- if statements (implemented). example: `if condition { ... } else if { ... } else { ... }`
|
- if statements (implemented). example: `if condition { ... } else if { ... } else { ... }`
|
||||||
- conditions must be `bool`; block-scoped locals do not escape their blocks
|
- conditions must be `bool`; block-scoped locals do not escape their blocks
|
||||||
- lowered through new `Label` / `Br` / `Cond_Br` IR opcodes (alloca-backed locals, no phi nodes)
|
- lowered through new `Label` / `Br` / `Cond_Br` IR opcodes (alloca-backed locals, no phi nodes)
|
||||||
- conditional unwrapping for optionals (`?T`) (implemented): `if val |v| { ... } else { ... }` - unwrap `val` into `v` if it is not `none`
|
- conditional unwrapping for optionals (`?T`) (implemented): `if val |v| { ... } else { ... }` - unwrap `val` into `v` if it is not `null`
|
||||||
- single immutable binding scoped to the then-block; `v` not visible in `else` or after the `if`
|
- single immutable binding scoped to the then-block; `v` not visible in `else` or after the `if`
|
||||||
- `|` lexes as a new `Pipe` token; the `.If` reuses AST `name` / HIR `local` to carry the binding (no new statement kind)
|
- `|` lexes as a new `Pipe` token; the `.If` reuses AST `name` / HIR `local` to carry the binding (no new statement kind)
|
||||||
- new `Optional_Is_Some` / `Optional_Value` IR opcodes (the `Unwrap` presence-test + extract, minus the trap)
|
- new `Optional_Is_Some` / `Optional_Value` IR opcodes (the `Unwrap` presence-test + extract, minus the trap)
|
||||||
- conditional unwrapping with guard clause (implemented): `if val |v : v >= 10| { ... } else { ... }` - enter the then-block when `val` is not `none` and the guard is true
|
- conditional unwrapping with guard clause (implemented): `if val |v : v >= 10| { ... } else { ... }` - enter the then-block when `val` is not `null` and the guard is true
|
||||||
- multi-unwrap (implemented; see section below)
|
- multi-unwrap (implemented; see section below)
|
||||||
- while loops (implemented; operates on boolean conditions). examples:
|
- while loops (implemented; operates on boolean conditions). examples:
|
||||||
- `while condition { ... }` - iterate while the condition is true
|
- `while condition { ... }` - iterate while the condition is true
|
||||||
@@ -139,10 +139,13 @@
|
|||||||
|
|
||||||
8. distinct types (implemented; see below)
|
8. distinct types (implemented; see below)
|
||||||
- nominal declarations preserve identity across packages and reuse the backing runtime representation
|
- nominal declarations preserve identity across packages and reuse the backing runtime representation
|
||||||
- construction uses `Type(value)` with exactly one value of the exact backing type
|
- construction of a numeric scalar-backed distinct type applies the backing's explicit scalar
|
||||||
- no implicit conversion to or from the backing type
|
cast before wrapping; non-scalar and nested-distinct backings still require the exact immediate type
|
||||||
- backing-type operators and reverse explicit conversions remain deferred
|
- no implicit conversion crosses the nominal boundary or mixes separate distinct declarations
|
||||||
- concrete runtime backing types are supported; unresolved, `int`, `void`, function, and opaque backings are rejected
|
- explicit scalar casts extract one declared distinct layer at a time
|
||||||
|
- scalar-backed values support matching runtime/comptime arithmetic, bitwise, shift, comparison,
|
||||||
|
compound-assignment, bounds, indexing, reflection, and standard formatting behavior
|
||||||
|
- unresolved, `int`, `void`, function, and opaque backings remain invalid runtime declarations
|
||||||
|
|
||||||
9. allow pointer field access pass-through (implemented)
|
9. allow pointer field access pass-through (implemented)
|
||||||
- having a pointer (`ptr`) to a struct, we should allow access through `ptr.field` as opposed to mandating `ptr^.field`
|
- having a pointer (`ptr`) to a struct, we should allow access through `ptr.field` as opposed to mandating `ptr^.field`
|
||||||
@@ -173,15 +176,15 @@
|
|||||||
- `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):
|
||||||
- allow mutable local declarations with `undefined`
|
- allow mutable local and global declarations with `undefined`
|
||||||
- undefined values are assigned a poison value (0xaa...)
|
- undefined values are assigned a poison value (0xaa...)
|
||||||
- allows for something like:
|
- allows for something like:
|
||||||
```
|
```
|
||||||
a int = undefined
|
a int := undefined
|
||||||
if (condition) {
|
if (condition) {
|
||||||
a = 42
|
a = 42
|
||||||
} else {
|
} else {
|
||||||
@@ -189,7 +192,7 @@
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
- disallow: `b :: undefined` since assigning undefined to something that can't change defeats the purpose
|
- disallow: `b :: undefined` since assigning undefined to something that can't change defeats the purpose
|
||||||
- disallow assigning `undefined` after declaration; use optionals and `none` for values that intentionally move back to an empty state
|
- disallow assigning `undefined` after declaration; use optionals and `null` for values that intentionally move back to an empty state
|
||||||
|
|
||||||
13. introduce `float` and `range` type constraints (the `int` family generalized) (implemented)
|
13. introduce `float` and `range` type constraints (the `int` family generalized) (implemented)
|
||||||
- `float` resolves a local binding to any float scalar (`f32`/`f64`) via static analysis;
|
- `float` resolves a local binding to any float scalar (`f32`/`f64`) via static analysis;
|
||||||
@@ -252,15 +255,15 @@
|
|||||||
c :: b + 3 # c is constrained to `i32`
|
c :: b + 3 # c is constrained to `i32`
|
||||||
```
|
```
|
||||||
|
|
||||||
16. for if statements, allow `if (cond) one-line statement` or `if some_func(some_arg) one-line statement` (instead of forcing either `if (cond) { block }` or `if cond { block }`) (implemented)
|
16. allow brace-less single-statement `if` and `for` bodies (implemented)
|
||||||
- if statements without a bracketed body must wrap the condition in parentheses UNLESS it's a function call
|
- brace-less bodies must wrap the condition or iterable in parentheses UNLESS it's a function call
|
||||||
- brace-less single-statement bodies apply to the then-body, the `else`-body, and the
|
- brace-less single-statement bodies apply to the then-body, the `else`-body, and the
|
||||||
unwrap/guard forms (`if v |x| stmt`); each branch is independent, so braced and
|
unwrap/guard forms (`if (v) |x| stmt`); each branch is independent, so braced and
|
||||||
brace-less branches mix freely
|
brace-less branches mix freely
|
||||||
- the parenthesize-or-call rule constrains only the then-branch condition; `else` and the
|
- the parenthesize-or-call rule constrains `if` then-branch conditions (including unwraps)
|
||||||
unwrap `|...|` already delimit, so they need no parentheses
|
and `for` iterables; `else` bodies have no preceding expression to constrain
|
||||||
- the brace-less statement may sit on the line after the condition
|
- the brace-less statement may sit on the following line
|
||||||
- parser-only change (`parse_branch_body` in `compiler/parser/parser.odin`): a brace-less
|
- parser-only change (`parse_control_body` in `compiler/parser/parser.odin`): a brace-less
|
||||||
body is just a 1-element statement slice, so the checker and codegen are unchanged
|
body is just a 1-element statement slice, so the checker and codegen are unchanged
|
||||||
|
|
||||||
17. multi-line strings (implemented; see below)
|
17. multi-line strings (implemented; see below)
|
||||||
@@ -320,7 +323,7 @@
|
|||||||
- a `{ ... }` on the right of a declaration or assignment is a *value block*: its final
|
- a `{ ... }` on the right of a declaration or assignment is a *value block*: its final
|
||||||
statement must be `yield <expr>`, which supplies the block's value (the block analogue
|
statement must be `yield <expr>`, which supplies the block's value (the block analogue
|
||||||
of `return`). Supported: `x :: { ...; yield v }` (untyped — the local takes the yield's
|
of `return`). Supported: `x :: { ...; yield v }` (untyped — the local takes the yield's
|
||||||
natural type), `x T = { ... }` (coerces to `T`), and `target = { ... }` (coerces to the
|
natural type), `x T := { ... }` (coerces to `T`), and `target = { ... }` (coerces to the
|
||||||
target's type, including complex targets like `a[i] = { ... }`)
|
target's type, including complex targets like `a[i] = { ... }`)
|
||||||
- the yielded value is captured *before* the block's defers run (a defer that mutates a
|
- the yielded value is captured *before* the block's defers run (a defer that mutates a
|
||||||
block local can't change what is yielded), reusing the `return` spill-to-temp pattern
|
block local can't change what is yielded), reusing the `return` spill-to-temp pattern
|
||||||
@@ -351,9 +354,9 @@
|
|||||||
`target = if …` are supported too
|
`target = if …` are supported too
|
||||||
- value-loop: a labeled body `for/while … blk: { … }` whose early exits are
|
- value-loop: a labeled body `for/while … blk: { … }` whose early exits are
|
||||||
`yield :blk x` and whose body ends in an unlabeled fall-through `yield` (the value
|
`yield :blk x` and whose body ends in an unlabeled fall-through `yield` (the value
|
||||||
when the loop completes). The `{T, none}` yields resolve the result to `?T`
|
when the loop completes). The `{T, null}` yields resolve the result to `?T`
|
||||||
(a pure-AST `none`-scan picks optionality; the first concrete yield fixes the element
|
(a pure-AST `null`-scan picks optionality; the first concrete yield fixes the element
|
||||||
type). E.g. `active_ent_idx :: for 0..10 |i| blk: { if (cond) yield :blk i; yield none }`
|
type). E.g. `active_ent_idx :: for 0..10 |i| blk: { if (cond) yield :blk i; yield null }`
|
||||||
resolves to `?usize`
|
resolves to `?usize`
|
||||||
- new `blk:` / `yield :blk` label surface adds one `label` field to the AST `Stmt`; no new
|
- new `blk:` / `yield :blk` label surface adds one `label` field to the AST `Stmt`; no new
|
||||||
token (`blk:` is `Identifier Colon`, `:blk` is `Colon Identifier`). The parser carries a
|
token (`blk:` is `Identifier Colon`, `:blk` is `Colon Identifier`). The parser carries a
|
||||||
@@ -370,7 +373,7 @@
|
|||||||
loop. The TODO "BAD" loops (unlabeled yield from inside an `if`, an unbound labeled loop)
|
loop. The TODO "BAD" loops (unlabeled yield from inside an `if`, an unbound labeled loop)
|
||||||
fall out of these naturally
|
fall out of these naturally
|
||||||
- follow-ups: a branch that early-`return`s instead of yielding, unwrap-`if` as a value
|
- follow-ups: a branch that early-`return`s instead of yielding, unwrap-`if` as a value
|
||||||
source, and `none`-before-concrete typing in untyped loops are done in 20.6; labeled value
|
source, and `null`-before-concrete typing in untyped loops are done in 20.6; labeled value
|
||||||
blocks and `yield`/`break` to an outer loop are done in 20.7
|
blocks and `yield`/`break` to an outer loop are done in 20.7
|
||||||
|
|
||||||
20.6 value if/loop follow-ups (implemented; checker-only)
|
20.6 value if/loop follow-ups (implemented; checker-only)
|
||||||
@@ -383,16 +386,16 @@
|
|||||||
guard), each branch assigning the slot; the HIR `.If` carries the unwraps, which the existing
|
guard), each branch assigning the slot; the HIR `.If` carries the unwraps, which the existing
|
||||||
lowering already handles. (The simple "unwrap or fallback" case is just `orelse` —
|
lowering already handles. (The simple "unwrap or fallback" case is just `orelse` —
|
||||||
`name :: opt orelse d` — already a plain expression.)
|
`name :: opt orelse d` — already a plain expression.)
|
||||||
- untyped value loops pre-type their element from the first concrete (non-`none`) yield
|
- untyped value loops pre-type their element from the first concrete (non-`null`) yield
|
||||||
regardless of source order (a capture-scoped probe build, `value_loop_element_type`), so a
|
regardless of source order (a capture-scoped probe build, `value_loop_element_type`), so a
|
||||||
`none` yielded before any concrete value still resolves the result to `?T`
|
`null` yielded before any concrete value still resolves the result to `?T`
|
||||||
- still checker-only; no HIR/lowering change
|
- still checker-only; no HIR/lowering change
|
||||||
|
|
||||||
20.7 labels — value blocks + yield/break to an outer loop (implemented; first lowering change)
|
20.7 labels — value blocks + yield/break to an outer loop (implemented; first lowering change)
|
||||||
- `x :: blk: { …; yield :blk v }` — a labeled value *block* (the disambiguated form of "an
|
- `x :: blk: { …; yield :blk v }` — a labeled value *block* (the disambiguated form of "an
|
||||||
if/loop at the end of a block"; an unlabeled trailing if/loop stays ambiguous and is not a
|
if/loop at the end of a block"; an unlabeled trailing if/loop stays ambiguous and is not a
|
||||||
value source). `yield :blk v` exits the block with a value; every path must yield. Carries
|
value source). `yield :blk v` exits the block with a value; every path must yield. Carries
|
||||||
the same `{T, none}` → `?T` typing, defer-capture, and reassignment forms as value loops
|
the same `{T, null}` → `?T` typing, defer-capture, and reassignment forms as value loops
|
||||||
- `yield :outer v` to an enclosing (non-innermost) value loop/block, plus plain `break :L` /
|
- `yield :outer v` to an enclosing (non-innermost) value loop/block, plus plain `break :L` /
|
||||||
`continue :L` to an enclosing labeled loop
|
`continue :L` to an enclosing labeled loop
|
||||||
- a label now names a first-class exit target: `label` added to the HIR `Stmt` (on
|
- a label now names a first-class exit target: `label` added to the HIR `Stmt` (on
|
||||||
@@ -406,10 +409,10 @@
|
|||||||
`.Block` break target; not a loop, so unlabeled `break`/`continue` and `continue :blk` skip
|
`.Block` break target; not a loop, so unlabeled `break`/`continue` and `continue :blk` skip
|
||||||
it). The checker tracks a parallel `loop_is_loop` stack so labeled `break` reaches a loop or
|
it). The checker tracks a parallel `loop_is_loop` stack so labeled `break` reaches a loop or
|
||||||
block while `continue` and unlabeled `break`/`continue` reach only the innermost loop
|
block while `continue` and unlabeled `break`/`continue` reach only the innermost loop
|
||||||
- untyped block `none`-before-concrete typing now builds the block's leading (yield-free)
|
- untyped block `null`-before-concrete typing now builds the block's leading (yield-free)
|
||||||
statements first (a throwaway probe), so a first concrete `yield :blk` that references a
|
statements first (a throwaway probe), so a first concrete `yield :blk` that references a
|
||||||
block local still resolves the result to `?T`
|
block local still resolves the result to `?T`
|
||||||
- deferred (`// ponytail:`): the same `none`-before-concrete typing in an untyped block (or
|
- deferred (`// ponytail:`): the same `null`-before-concrete typing in an untyped block (or
|
||||||
loop) whose concrete yield references a local declared *past* the first yield (annotate)
|
loop) whose concrete yield references a local declared *past* the first yield (annotate)
|
||||||
|
|
||||||
21. unions and tagged unions (implemented; first pass — native untagged unions only; see below)
|
21. unions and tagged unions (implemented; first pass — native untagged unions only; see below)
|
||||||
@@ -464,7 +467,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
|
||||||
@@ -627,7 +630,7 @@
|
|||||||
parameters
|
parameters
|
||||||
|
|
||||||
25. dynamic heap allocation (implemented; v1)
|
25. dynamic heap allocation (implemented; v1)
|
||||||
- `std/mem` exposes a plain-data `Allocator` contract with `?*mut anyopaque` context and `alloc`, `realloc`, and `free` `@func` pointers
|
- `std/mem` exposes a plain-data `Allocator` contract with `?@mut anyopaque` context and `alloc`, `realloc`, and `free` `@func` pointers
|
||||||
- `mem.c_allocator` is the libc-backed allocator; `mem.alloc(mem.c_allocator, size, alignment)` returns nullable mutable byte memory
|
- `mem.c_allocator` is the libc-backed allocator; `mem.alloc(mem.c_allocator, size, alignment)` returns nullable mutable byte memory
|
||||||
- `mem.free(mem.c_allocator, ptr, size, alignment)` frees with the same allocator; `malloc` handles default-aligned requests and `posix_memalign` handles larger power-of-two alignments
|
- `mem.free(mem.c_allocator, ptr, size, alignment)` frees with the same allocator; `malloc` handles default-aligned requests and `posix_memalign` handles larger power-of-two alignments
|
||||||
- `mem.realloc` preserves alignment and the original allocation on failure; zero size frees, and over-aligned blocks use allocate/copy/free
|
- `mem.realloc` preserves alignment and the original allocation on failure; zero size frees, and over-aligned blocks use allocate/copy/free
|
||||||
@@ -686,16 +689,17 @@
|
|||||||
evaluation; runtime-dependent values remain invalid in comptime contexts
|
evaluation; runtime-dependent values remain invalid in comptime contexts
|
||||||
- runtime-only behavior is rejected in comptime: external/bodyless `c_func`,
|
- runtime-only behavior is rejected in comptime: external/bodyless `c_func`,
|
||||||
writable globals, and materializing comptime storage pointers/slices as runtime memory
|
writable globals, and materializing comptime storage pointers/slices as runtime memory
|
||||||
- v1 keeps integer-only `$N` specialization keys; aggregate comptime parameters
|
- milestone 39 extends specialization keys from integer/type/string values to
|
||||||
and stable aggregate serialization are deferred
|
recursively stable values while keeping runtime ABI erasure unchanged
|
||||||
|
|
||||||
27.8 source-defined mutable runtime globals (implemented)
|
27.8 source-defined mutable runtime globals (implemented)
|
||||||
- allow mutable global declarations in Brolang source for process-global runtime
|
- allow mutable global declarations in Brolang source for process-global runtime
|
||||||
state, matching the writable-global support already needed for imported C globals
|
state, matching the writable-global support already needed for imported C globals
|
||||||
- require source type syntax and an initializer; constraints (`int`/`float`/`range`)
|
- require an initializer and infer or explicitly declare a concrete runtime storage type;
|
||||||
and inferred array counts may resolve through the existing inference fixpoint, but
|
constraints (`int`/`float`/`range`) and inferred array counts resolve through the existing
|
||||||
the final type must be concrete runtime storage
|
inference fixpoint
|
||||||
- emit source-defined mutable globals as writable globals, not constants
|
- emit source-defined mutable globals as writable globals, not constants
|
||||||
|
- allow `undefined` initializers for runtime storage initialized explicitly by a function
|
||||||
- allow assignment, address-taking, field/index mutation, and pointer passing under
|
- allow assignment, address-taking, field/index mutation, and pointer passing under
|
||||||
the same mutability rules as other writable locations
|
the same mutability rules as other writable locations
|
||||||
- keep mutable globals invalid in comptime evaluation; `$global_var` and writes from
|
- keep mutable globals invalid in comptime evaluation; `$global_var` and writes from
|
||||||
@@ -712,8 +716,10 @@
|
|||||||
places, slicing, `.len`, `.ptr`, pointer captures, and pointer-param aliasing
|
places, slicing, `.len`, `.ptr`, pointer captures, and pointer-param aliasing
|
||||||
- comptime storage pointers/slices cannot materialize as runtime memory; escaped
|
- comptime storage pointers/slices cannot materialize as runtime memory; escaped
|
||||||
dead storage is rejected
|
dead storage is rejected
|
||||||
- bare concrete non-comptime function names are values; native function pointer
|
- bare concrete non-comptime function names are comptime-only declaration identities;
|
||||||
types use `@func(...) R` and fallible `@func(...) R ! E`
|
native function pointer types use `@func(...) R` and fallible `@func(...) R ! E`
|
||||||
|
- bare identities implicitly materialize compatible runtime pointers, never the reverse;
|
||||||
|
aggregates containing bare identities remain comptime-only
|
||||||
- comptime-known native/bodyful `c_func` values can be called; bodyless/imported
|
- comptime-known native/bodyful `c_func` values can be called; bodyless/imported
|
||||||
callbacks remain runtime-only
|
callbacks remain runtime-only
|
||||||
- native function pointers are non-variadic v1; C variadic function pointers stay
|
- native function pointers are non-variadic v1; C variadic function pointers stay
|
||||||
@@ -729,12 +735,12 @@
|
|||||||
- enabled `&<array literal>` (Zig's `&.{...}`): the literal is promoted to an
|
- enabled `&<array literal>` (Zig's `&.{...}`): the literal is promoted to an
|
||||||
anonymous global whose address decays to a slice, so list fields like
|
anonymous global whose address decays to a slice, so list fields like
|
||||||
`libraries = &["raylib"]` work; empty lists are `&[]`
|
`libraries = &["raylib"]` work; empty lists are `&[]`
|
||||||
- deferred: build graph / steps / caching, multiple artifacts, computed paths
|
- deferred: build graph / steps / caching, multiple artifacts, and computed paths
|
||||||
(needs string building), struct field defaults to drop `&[]` on empty lists
|
(needs string building); milestone 44 later removed explicit `&[]` build-config fields
|
||||||
|
|
||||||
29. fix bugs (implemented)
|
29. fix bugs (implemented)
|
||||||
- `return _` was already the supported empty return for void functions; the original
|
- bare `return` is the empty return for void functions; `yield` always requires a
|
||||||
bare-`return` report was stale
|
same-line, non-void value because `break` handles valueless scope exits
|
||||||
- catch value blocks may end by returning from the function instead of yielding when
|
- catch value blocks may end by returning from the function instead of yielding when
|
||||||
every path exits
|
every path exits
|
||||||
- implicit-conversion diagnostics render source-level composite and named types instead
|
- implicit-conversion diagnostics render source-level composite and named types instead
|
||||||
@@ -768,7 +774,7 @@
|
|||||||
improving layout or specialization
|
improving layout or specialization
|
||||||
- the smallest fitting feature is call-local inference of omitted comptime type arguments:
|
- the smallest fitting feature is call-local inference of omitted comptime type arguments:
|
||||||
```
|
```
|
||||||
values ArrayList(i32) = arraylist.init(mem.c_allocator)
|
values ArrayList(i32) := arraylist.init(mem.c_allocator)
|
||||||
defer arraylist.deinit(&values)
|
defer arraylist.deinit(&values)
|
||||||
try arraylist.append(&values, 42)
|
try arraylist.append(&values, 42)
|
||||||
```
|
```
|
||||||
@@ -777,56 +783,187 @@
|
|||||||
- keep functions package-scoped and keep the explicit form valid; this preserves simple name
|
- keep functions package-scoped and keep the explicit form valid; this preserves simple name
|
||||||
resolution and gives ambiguous calls an escape hatch
|
resolution and gives ambiguous calls an escape hatch
|
||||||
|
|
||||||
31.5. inferred leading comptime parameters (implemented)
|
31.5. inferred comptime parameters (implemented)
|
||||||
- a native call may omit its complete leading `$T type` / integer comptime prefix when every
|
- comptime parameters may appear anywhere and are erased while preserving runtime parameter order
|
||||||
|
- a native call may omit `$T type`, integer, or immutable byte-string comptime arguments when every
|
||||||
value is uniquely recoverable from runtime argument types and/or the immediate expected result
|
value is uniquely recoverable from runtime argument types and/or the immediate expected result
|
||||||
- inference structurally matches direct type parameters, pointers/slices/arrays/optionals/
|
- inference structurally matches direct type parameters, pointers/slices/arrays/optionals/
|
||||||
fallibles/functions, direct array counts, and canonical generated type-factory provenance;
|
fallibles/functions, direct array counts, and canonical generated type-factory provenance;
|
||||||
forwarding/non-invertible factories keep the explicit spelling
|
forwarding/non-invertible factories keep the explicit spelling
|
||||||
- concrete evidence is exact; contextual numeric constants are weak evidence and are rebuilt with
|
- concrete evidence is exact; contextual numeric constants are weak evidence and are rebuilt with
|
||||||
the resolved parameter type before ordinary coercion
|
the resolved parameter type before ordinary coercion
|
||||||
- calls are all-explicit or all-inferred (no partial prefix omission); unconstrained/conflicting
|
- `_` explicitly leaves one comptime argument to inference; exactly one complete argument mapping
|
||||||
values diagnose with the explicit call as the escape hatch
|
must succeed, with missing and ambiguous mappings diagnosed
|
||||||
- comptime parameters must form one leading prefix before every runtime parameter
|
|
||||||
- the existing specialization/HIR/LLVM ABI is unchanged; `std/mem` and `std/arraylist` now use the
|
- the existing specialization/HIR/LLVM ABI is unchanged; `std/mem` and `std/arraylist` now use the
|
||||||
inferred form where their arguments or result provide enough information
|
inferred form where their arguments or result provide enough information
|
||||||
|
|
||||||
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/process Init`; parameterless entry points remain valid
|
||||||
- the compiler supplies a file-hidden macOS provider through an external no-argument C wrapper
|
- the compiler supplies a `hide system` macOS provider and constructs `Init` in the external C wrapper
|
||||||
- readers and writers pair an explicit provider with `stdin`, `stdout`, or `stderr`
|
- readers and writers bind provider context, a generalized handle, and a direct callback
|
||||||
|
- standard-stream helpers and existing-file operations use the injected provider; files retain it
|
||||||
- `read` and `write` validate provider counts; `write_all` handles partial writes and no progress
|
- `read` and `write` validate provider counts; `write_all` handles partial writes and no progress
|
||||||
- the system provider uses unbuffered libc `read`/`write`, retries interruption, and allocates nothing
|
- the system provider uses unbuffered POSIX file descriptors, retries interrupted open/read/write,
|
||||||
|
and allocates nothing
|
||||||
|
|
||||||
34. package declaration aliases and root `std.ArrayList` (implemented)
|
34. package declaration aliases and root `std.ArrayList` (implemented)
|
||||||
- bare qualified aliases use `Name :: alias package.Member` without adding a keyword
|
- bare qualified aliases use `Name :: alias package.Member` without adding a keyword
|
||||||
- functions/type factories, named types, and globals transparently retain the target identity;
|
- functions/type factories, named types, and globals transparently retain the target identity;
|
||||||
mutable global aliases therefore share the original storage
|
mutable global aliases therefore share the original storage
|
||||||
- aliases resolve transitively at load time, consume their file-local import, preserve leading-
|
- aliases resolve transitively at load time, consume their file-local import, preserve explicit
|
||||||
underscore visibility, and diagnose missing, hidden, unavailable, ambiguous, cyclic, or
|
`hide` visibility, and diagnose missing, hidden, unavailable, ambiguous, cyclic, or
|
||||||
conflicting targets
|
conflicting targets
|
||||||
- root `std` re-exports only `ArrayList(T)` for now; operations remain under `std/arraylist`
|
- root `std` re-exports only `ArrayList(T)` for now; operations remain under `std/arraylist`
|
||||||
|
|
||||||
35. syntax highlighting (tree-sitter) updates (implemented)
|
|
||||||
- pointer sigils are highlighted as operators
|
36. transitive package namespaces (spike completed; no language change)
|
||||||
- type-factory calls in type positions and struct literals are highlighted as functions
|
- imports remain file-local implementation details, including explicitly named imports such as
|
||||||
|
`rl :: import "@vendor/raylib"`; naming an import only chooses its local qualifier
|
||||||
|
- packages expose declarations, not their imports, so imported namespaces never become public or
|
||||||
|
transitively reachable package members
|
||||||
|
- callers import each package they use directly; subdirectory layout does not create namespaces
|
||||||
|
- this keeps package lookup shallow and deterministic and avoids overloading import aliases with
|
||||||
|
declaration visibility
|
||||||
|
|
||||||
|
36.5. explicit `hide` package-local declarations (implemented)
|
||||||
|
- `hide name ...` gives any named top-level function, global, native/C record, union, enum,
|
||||||
|
opaque/distinct type, or declaration/type alias package-local visibility across sibling files
|
||||||
|
- declarations remain public by default; a leading underscore is an ordinary identifier and `_`
|
||||||
|
remains the write-only sink
|
||||||
|
- hidden declarations occupy the package namespace, collide with sibling declarations of the
|
||||||
|
same name, and remain absent from imported package namespaces
|
||||||
|
- `hide` is reserved for named top-level declarations and is rejected on imports, locals,
|
||||||
|
parameters, fields, and anonymous declarations
|
||||||
|
|
||||||
|
37. tuples, reflection, process init, and printing (implemented)
|
||||||
|
- tuples are unnamed-field structs with structural anonymous values, nominal named declarations,
|
||||||
|
brace literals, numeric fields, and no runtime metadata
|
||||||
|
- `@std/meta`, `typeinfo!`, `field!`, `compile_error!`, specialization-time branches, and semantic
|
||||||
|
`inline for` use checker-owned persistent compile-time values for aggregate-first reflection and
|
||||||
|
heterogeneous static expansion; inline-loop control is recursively resolved at comptime
|
||||||
|
- interleaved comptime parameters use semantic candidate resolution, immutable byte values specialize
|
||||||
|
by contents, and all comptime parameters remain erased from the runtime ABI
|
||||||
|
- `io.print(writer, format, args)` validates and expands `{s}` / `{d}` formatting at comptime,
|
||||||
|
is implemented in ordinary `@std/io` code, performs no allocation or runtime parsing, and
|
||||||
|
propagates the first write error
|
||||||
|
- `debug.print` reuses formatting through an independent stderr backend, ignores failures, and adds
|
||||||
|
no newline
|
||||||
|
- entrypoint validation accepts only parameterless `main` or canonical `main(process.Init)`, validates
|
||||||
|
the hidden `@std/io.system` provider, and injects the provider through the generated C entrypoint;
|
||||||
|
native variadics and additional startup data remain deferred
|
||||||
|
|
||||||
|
38. place every intrinsic behind direct unqualified `name!(...)` syntax, freeing the bare names for
|
||||||
|
user functions (implemented)
|
||||||
|
|
||||||
|
39. stable comptime values and richer formatting (implemented)
|
||||||
|
- comptime parameters accept booleans, integers, floats, types, immutable bytes,
|
||||||
|
enums, fixed arrays, records/tuples, optionals, and tagged unions recursively
|
||||||
|
- canonical specialization keys include deterministic type identity, exact float bits,
|
||||||
|
byte contents, ordered aggregate children, optional state, and active union variants;
|
||||||
|
FNV-1a fingerprints accelerate lookup while exact key comparison handles collisions
|
||||||
|
- equal structural values reuse specializations and stable emitted names, distinct values
|
||||||
|
specialize separately, aggregate inference uses exact type-factory provenance, and all
|
||||||
|
comptime parameters remain erased from the runtime ABI
|
||||||
|
- undefined values, pointers, general slices, fallibles, ranges, and untagged
|
||||||
|
unions diagnose that they have no stable comptime identity
|
||||||
|
- `@std/meta.TypeInfo.enum` carries declaration-ordered `EnumInfo.fields`, enabling enum
|
||||||
|
formatting through `field!` without runtime reflection metadata
|
||||||
|
- `io.print` and `debug.print` retain their APIs and expand `{}`, `{s}`, `{d}`, `{b}`,
|
||||||
|
`{o}`, `{x}`, `{X}`, `{c}`, and `{e}` at comptime; `{{` and `}}` remain escapes
|
||||||
|
- integer output uses one base-aware 65-byte stack buffer; float output uses fixed-buffer
|
||||||
|
libc `snprintf` with 32-bit and 64-bit general/scientific precision and propagates failure
|
||||||
|
|
||||||
|
40. compile-time `inline` (implemented)
|
||||||
|
- expansion-oriented `expand for` is strictly renamed to `inline for`; `expand` is no longer a keyword
|
||||||
|
- final inlined enum and tagged-union match arms generate checker-local specialized arms only
|
||||||
|
for variants not covered by preceding explicit arms
|
||||||
|
- generated enum values and union tags are static bindings, heterogeneous payloads retain their
|
||||||
|
concrete types, and `void` payloads support value and pointer captures without runtime storage
|
||||||
|
- `tag!` reads or folds a tagged union discriminant, while `tagname!` turns a comptime-known enum
|
||||||
|
value into its immutable field-name string
|
||||||
|
|
||||||
|
41. native test framework (implemented; v1)
|
||||||
|
- `name test { ... }` declares an implicit `void ! testing.Error` test omitted from executable builds
|
||||||
|
- anonymous `test import` edges discover dependency tests transitively; ordinary imports remain
|
||||||
|
separate namespaces and never discover tests
|
||||||
|
- direct `@std/testing` `expect` and expected-first `expect_equal` calls inject their source locations
|
||||||
|
- `brolang test` reuses `build.bro`, runs tests sequentially, continues after assertion failures,
|
||||||
|
prints a summary, and returns failure when a test fails or traps
|
||||||
|
- filtering, skipping, fixtures, snapshots, parallelism, isolation, allocators, and more assertion
|
||||||
|
families remain deferred
|
||||||
|
|
||||||
|
42. unsigned integer constraint (implemented)
|
||||||
|
- `uint` is the unsigned subset constraint while `int` remains the whole integer family
|
||||||
|
- literal-only values choose the smallest fitting `u8`, `u16`, `u32`, or `u64`
|
||||||
|
- native unsigned scalars and target-classified unsigned C scalars satisfy the constraint
|
||||||
|
- `isize` and `usize` remain concrete pointer-sized types
|
||||||
|
|
||||||
|
43. comptime function parameters (implemented)
|
||||||
|
- bare native and C function identities, function literals, and comptime-only aggregates
|
||||||
|
containing them specialize by declaration identity rather than runtime address
|
||||||
|
- repeated declarations reuse specializations, distinct declarations specialize separately, and
|
||||||
|
function-valued parameters remain erased from the runtime ABI
|
||||||
|
- statically known callback invocations lower to direct calls; runtime-selected function pointers
|
||||||
|
remain indirect, and bodyless C declarations remain runtime-only during comptime execution
|
||||||
|
- runtime pointer contexts implicitly materialize a bare identity; pointer-to-identity conversion is
|
||||||
|
rejected, and bare-containing aggregates cannot enter runtime storage or ABI/C layouts
|
||||||
|
|
||||||
|
44. struct field defaults on declaration (implemented)
|
||||||
|
- named native structs accept `field T = expression`; keyed construction evaluates defaults
|
||||||
|
for omitted fields while explicit initializers override them
|
||||||
|
- defaults resolve names in the declaration file, participate in record constraint inference,
|
||||||
|
and work during runtime and comptime construction
|
||||||
|
- `@std/build.BuildConfig` uses defaults for optional list fields
|
||||||
|
- fields without defaults remain required; C-layout records, unions, tuples, and anonymous
|
||||||
|
generated structs do not accept defaults
|
||||||
|
|
||||||
|
45. typed memory intrinsics (implemented)
|
||||||
|
- `memcopy!` copies equal-length, non-overlapping slices or pointers-to-arrays with identical
|
||||||
|
element types; comptime diagnoses invalid regions and runtime guards length, size, and overlap
|
||||||
|
- `memset!` fills a mutable region with a value coerced to its element type; bytes lower to LLVM
|
||||||
|
memset and wider values use typed stores
|
||||||
|
- both operations evaluate operands once, preserve undefined state without observing it, and work
|
||||||
|
identically during comptime evaluation; zeroing is `memset!(destination, 0)`
|
||||||
|
- `std/mem` aligned reallocation uses `memcopy!`
|
||||||
|
|
||||||
|
46. fix `EnumFieldStruct` in `std/meta` (implemented)
|
||||||
|
- comptime local declarations resolve type syntax in the active interpreter state, preserving match captures
|
||||||
|
- `EnumFieldStruct` sizes its working arrays directly from the comptime `.enum |info|` capture
|
||||||
|
|
||||||
|
47. rename optional `none` to `null` (implemented)
|
||||||
|
|
||||||
|
48. add `noreturn` and `unreachable` (implemented)
|
||||||
|
- `noreturn` is the native bottom type, permitted as a function result but rejected for storage,
|
||||||
|
parameters, record fields, and the C ABI
|
||||||
|
- `noreturn` expressions coerce to any expected value type and terminate path analysis
|
||||||
|
- `unreachable` always traps at runtime and reports an error during comptime evaluation
|
||||||
|
|
||||||
|
49. unify catch fallback value sources (implemented)
|
||||||
|
- `expr catch [|error|] value_source` uses the same ordinary expression, value block, and
|
||||||
|
value-producing control-flow forms with or without an error capture
|
||||||
|
- void fallthrough and diverging `noreturn` fallbacks remain valid
|
||||||
|
|
||||||
|
50. add `constcast!` and immutable deallocation (implemented)
|
||||||
|
- `constcast!` restores mutability only for pointers, optional pointers, and slices while
|
||||||
|
preserving child type, pointer kind, optionality, length, and sentinel shape
|
||||||
|
- `std/mem.free` accepts immutable slices and restores mutability only at the allocator boundary
|
||||||
|
|
||||||
## A word on unchecked casts
|
## A word on unchecked casts
|
||||||
|
|
||||||
@@ -836,7 +973,8 @@ 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!(T, p)` | Change a pointer's child type while preserving its shape | Invalid child or non-pointer operand (compile error) |
|
||||||
|
| `constcast!(p)` | Restore pointer or slice mutability | Non-pointer/slice operand (compile error) |
|
||||||
|
|
||||||
```honey
|
```honey
|
||||||
# truncation
|
# truncation
|
||||||
@@ -849,23 +987,21 @@ m := bitcast(n, u32) # m == 0xFFFFFFFF (same bits)
|
|||||||
f: f32 = 3.14
|
f: f32 = 3.14
|
||||||
bits := bitcast(f, u32) # IEEE 754 representation
|
bits := bitcast(f, u32) # IEEE 754 representation
|
||||||
|
|
||||||
# pointer casts (element type, many ↔ single, pointer ↔ usize)
|
# pointer casts
|
||||||
buf: *u8 = get_buffer()
|
buf *u8 := get_buffer()
|
||||||
ints := ptrcast(buf, *u32) # element type change
|
ints *u32 := ptrcast!(u32, buf) # element type change, same pointer shape
|
||||||
single := ptrcast(buf, @u8) # many → single (restricting)
|
writable *mut u8 := constcast!(buf) # explicit unsafe mutability restoration
|
||||||
addr := ptrcast(buf, usize) # pointer to integer
|
|
||||||
ptr := ptrcast(addr, @u8) # integer to pointer
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## A word on multi-unwrap
|
## A word on multi-unwrap
|
||||||
|
|
||||||
Unwrap multiple optionals with `and`. This **short-circuits**: if the first optional is none, subsequent expressions are not evaluated.
|
Unwrap multiple optionals with `and`. This **short-circuits**: if the first optional is null, subsequent expressions are not evaluated.
|
||||||
|
|
||||||
```
|
```
|
||||||
name: ?[]u8 = get_name()
|
name: ?[]u8 = get_name()
|
||||||
age: ?u8 = get_age()
|
age: ?u8 = get_age()
|
||||||
if name and age |n, a| {
|
if name and age |n, a| {
|
||||||
# both n and a are guaranteed non-none here
|
# both n and a are guaranteed non-null here
|
||||||
print("{s} is {d} years old", n, a)
|
print("{s} is {d} years old", n, a)
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -894,7 +1030,7 @@ The `and` in multi-unwrap short-circuits left-to-right:
|
|||||||
|
|
||||||
```
|
```
|
||||||
if get_name() and get_hat() |n, h| {
|
if get_name() and get_hat() |n, h| {
|
||||||
# get_hat() is only called if get_name() returned non-none
|
# get_hat() is only called if get_name() returned non-null
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -925,16 +1061,30 @@ For-loop captures are immutable and scoped to the loop body. Sequence index capt
|
|||||||
|
|
||||||
## A word on distinct types
|
## A word on distinct types
|
||||||
|
|
||||||
Distinct types are considered distinct from their backing type. They do not implicitly coerce to their backing type.
|
Distinct declarations are nominal even when they share a backing type. A constructor for a
|
||||||
|
numeric scalar-backed distinct type first performs the backing's explicit scalar cast, while
|
||||||
|
implicit conversion remains forbidden in either direction. Explicit scalar casts extract one
|
||||||
|
layer:
|
||||||
|
|
||||||
```
|
```bro
|
||||||
# distinct type
|
|
||||||
UserID :: distinct u32
|
UserID :: distinct u32
|
||||||
|
OuterID :: distinct UserID
|
||||||
# instantiate distinct type
|
index usize := 42
|
||||||
my_id UserID :: UserID(42) # value must have the exact backing type
|
id UserID :: UserID(index)
|
||||||
|
raw u32 :: u32(id)
|
||||||
|
outer OuterID :: OuterID(id)
|
||||||
|
inner UserID :: UserID(outer)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Scalar-backed distinct values retain their nominal type across the operations supported by the
|
||||||
|
backing scalar. Integer forms support checked arithmetic, bitwise operations, shifts, comparisons,
|
||||||
|
compound assignments, indexing, slicing, and `minval!` / `maxval!`; float forms support arithmetic
|
||||||
|
and comparisons; boolean forms support equality and inequality. Separate distinct identities and
|
||||||
|
typed backing operands never mix implicitly or in ordinary operations; crossing between numeric
|
||||||
|
representations requires an explicit constructor or scalar cast. Runtime and
|
||||||
|
comptime rules are identical. Reflection reports the immediate backing, while standard formatting
|
||||||
|
recursively follows nested distinct backings to the final scalar.
|
||||||
|
|
||||||
## A word on enums
|
## A word on enums
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -1071,7 +1221,7 @@ data :: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# match arms
|
# match arms
|
||||||
label []u8 = match p {
|
label []u8 := match p {
|
||||||
.high: "HIGH", # single expression: implicit yield
|
.high: "HIGH", # single expression: implicit yield
|
||||||
.low: {
|
.low: {
|
||||||
log("low priority")
|
log("low priority")
|
||||||
@@ -1080,7 +1230,7 @@ label []u8 = match p {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# catch handlers (planned; block form deferred in milestone 23 v1)
|
# catch handlers (planned; block form deferred in milestone 23 v1)
|
||||||
data []u8 = read(path) catch |e| {
|
data []u8 := read(path) catch |e| {
|
||||||
log(e)
|
log(e)
|
||||||
yield fallback_data # block: explicit yield
|
yield fallback_data # block: explicit yield
|
||||||
}
|
}
|
||||||
@@ -1101,7 +1251,7 @@ result :: if a {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# yielding to a variable
|
# yielding to a variable
|
||||||
result int = if a {
|
result int := if a {
|
||||||
yield 1
|
yield 1
|
||||||
} else if b {
|
} else if b {
|
||||||
yield 2
|
yield 2
|
||||||
@@ -1123,7 +1273,7 @@ Yielding is also possible from loops with the same constraint.
|
|||||||
# get active entity
|
# get active entity
|
||||||
active_ent_idx :: for 0..10 |i| blk: {
|
active_ent_idx :: for 0..10 |i| blk: {
|
||||||
if is_active(some_entity, i) yield :blk i
|
if is_active(some_entity, i) yield :blk i
|
||||||
yield none # fall-through: no active ent was found (this should imply a return type matching both the index value and `none`, meaning it should resolve to an optional in this case)
|
yield null # fall-through: no active ent was found (this should imply a return type matching both the index value and `null`, meaning it should resolve to an optional in this case)
|
||||||
|
|
||||||
# note that in this case, we have to use the `blk` label to yield from the correct scope.
|
# note that in this case, we have to use the `blk` label to yield from the correct scope.
|
||||||
# otherwise, the yield should return directly from the if-statement's scope (which would be incorrect in this case).
|
# otherwise, the yield should return directly from the if-statement's scope (which would be incorrect in this case).
|
||||||
@@ -1132,13 +1282,13 @@ active_ent_idx :: for 0..10 |i| blk: {
|
|||||||
# BAD: yield returned from if-statement, but no name binds it: should miscompile similar to unused return values from functions.
|
# BAD: yield returned from if-statement, but no name binds it: should miscompile similar to unused return values from functions.
|
||||||
active_ent_idx :: for 0..10 |i| {
|
active_ent_idx :: for 0..10 |i| {
|
||||||
if is_active(some_entity, i) yield i # bad
|
if is_active(some_entity, i) yield i # bad
|
||||||
yield none
|
yield null
|
||||||
}
|
}
|
||||||
|
|
||||||
# BAD: likewise for loops
|
# BAD: likewise for loops
|
||||||
for 0..10 |i| blk: { # bad, no name binds returned value
|
for 0..10 |i| blk: { # bad, no name binds returned value
|
||||||
if is_active(some_entity, i) yield :blk i
|
if is_active(some_entity, i) yield :blk i
|
||||||
yield none
|
yield null
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -1331,7 +1481,7 @@ Fallible functions use ordinary `return` for both channels. If the returned expr
|
|||||||
|
|
||||||
```
|
```
|
||||||
parse_section func(p: @mut Parser) void ! ParseError {
|
parse_section func(p: @mut Parser) void ! ParseError {
|
||||||
start_line Line = p.line
|
start_line Line := p.line
|
||||||
p.advance()
|
p.advance()
|
||||||
|
|
||||||
# ... parsing logic ...
|
# ... parsing logic ...
|
||||||
@@ -1346,7 +1496,7 @@ Since errors are just union values, you can also construct them separately:
|
|||||||
|
|
||||||
```
|
```
|
||||||
# Construct error value (it's just a union)
|
# Construct error value (it's just a union)
|
||||||
e ParseError = .timeout{500}
|
e ParseError := .timeout{500}
|
||||||
|
|
||||||
# Return it via error channel later
|
# Return it via error channel later
|
||||||
return e
|
return e
|
||||||
@@ -1496,7 +1646,7 @@ Brolang provides a libc-backed allocator value:
|
|||||||
mem :: import "@std/mem"
|
mem :: import "@std/mem"
|
||||||
|
|
||||||
process func(input []u8) u64 {
|
process func(input []u8) u64 {
|
||||||
temp ?*mut u8 = mem.alloc(mem.c_allocator, input.len * 2, 1)
|
temp ?*mut u8 := mem.alloc(mem.c_allocator, input.len * 2, 1)
|
||||||
defer mem.free(mem.c_allocator, temp, input.len * 2, 1)
|
defer mem.free(mem.c_allocator, temp, input.len * 2, 1)
|
||||||
|
|
||||||
# ... work with temp ...
|
# ... work with temp ...
|
||||||
@@ -1526,7 +1676,7 @@ mem :: import "@std/mem"
|
|||||||
|
|
||||||
# Allocation escapes via return value — requires allocator
|
# Allocation escapes via return value — requires allocator
|
||||||
duplicate func(input []u8, allocator mem.Allocator) ?*mut u8 {
|
duplicate func(input []u8, allocator mem.Allocator) ?*mut u8 {
|
||||||
result ?*mut u8 = mem.alloc(allocator, input.len, 1)
|
result ?*mut u8 := mem.alloc(allocator, input.len, 1)
|
||||||
mem.copy(result, input)
|
mem.copy(result, input)
|
||||||
return result # caller manages this memory
|
return result # caller manages this memory
|
||||||
}
|
}
|
||||||
@@ -1539,7 +1689,7 @@ init func(obj @mut MyStruct, allocator mem.Allocator) void {
|
|||||||
|
|
||||||
# No allocation escapes — no allocator needed
|
# No allocation escapes — no allocator needed
|
||||||
process func(input []u8) u64 {
|
process func(input []u8) u64 {
|
||||||
temp ?*mut u8 = mem.alloc(mem.c_allocator, input.len, 1)
|
temp ?*mut u8 := mem.alloc(mem.c_allocator, input.len, 1)
|
||||||
defer mem.free(mem.c_allocator, temp, input.len, 1)
|
defer mem.free(mem.c_allocator, temp, input.len, 1)
|
||||||
# ... work with temp ...
|
# ... work with temp ...
|
||||||
return compute_hash(temp)
|
return compute_hash(temp)
|
||||||
|
|||||||
+82
-13
@@ -63,6 +63,14 @@ index :: proc(id: $T, invalid: T, count: int) -> (int, bool) {
|
|||||||
|
|
||||||
Type_Syntax :: types.Type
|
Type_Syntax :: types.Type
|
||||||
|
|
||||||
|
Type_Use :: struct {
|
||||||
|
type: Type_Syntax,
|
||||||
|
span: source.Span,
|
||||||
|
pkg: Package_Id,
|
||||||
|
file: File_Id,
|
||||||
|
diagnostic: source.Diagnostic_Id,
|
||||||
|
}
|
||||||
|
|
||||||
Expr_Kind :: enum u8 {
|
Expr_Kind :: enum u8 {
|
||||||
Invalid,
|
Invalid,
|
||||||
Integer,
|
Integer,
|
||||||
@@ -70,8 +78,10 @@ Expr_Kind :: enum u8 {
|
|||||||
String,
|
String,
|
||||||
Bool,
|
Bool,
|
||||||
Array,
|
Array,
|
||||||
None,
|
Null,
|
||||||
|
Unreachable,
|
||||||
Undefined,
|
Undefined,
|
||||||
|
Inference_Hole,
|
||||||
Type,
|
Type,
|
||||||
Name,
|
Name,
|
||||||
Enum_Literal,
|
Enum_Literal,
|
||||||
@@ -88,10 +98,17 @@ Expr_Kind :: enum u8 {
|
|||||||
Comptime,
|
Comptime,
|
||||||
Negate,
|
Negate,
|
||||||
Not,
|
Not,
|
||||||
|
Bit_Not,
|
||||||
Add,
|
Add,
|
||||||
Sub,
|
Sub,
|
||||||
Mul,
|
Mul,
|
||||||
Div,
|
Div,
|
||||||
|
Bit_And,
|
||||||
|
Bit_Or,
|
||||||
|
Bit_Xor,
|
||||||
|
Shift_Left,
|
||||||
|
Shift_Right,
|
||||||
|
Shift_Left_Saturating,
|
||||||
Eq,
|
Eq,
|
||||||
Ne,
|
Ne,
|
||||||
Lt,
|
Lt,
|
||||||
@@ -120,6 +137,8 @@ Expr :: struct {
|
|||||||
body: []Stmt_Id,
|
body: []Stmt_Id,
|
||||||
diagnostic: source.Diagnostic_Id,
|
diagnostic: source.Diagnostic_Id,
|
||||||
parenthesized: bool,
|
parenthesized: bool,
|
||||||
|
intrinsic: bool,
|
||||||
|
tuple: bool,
|
||||||
kind: Expr_Kind,
|
kind: Expr_Kind,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,6 +173,12 @@ Assignment_Op :: enum u8 {
|
|||||||
Sub,
|
Sub,
|
||||||
Mul,
|
Mul,
|
||||||
Div,
|
Div,
|
||||||
|
Bit_And,
|
||||||
|
Bit_Or,
|
||||||
|
Bit_Xor,
|
||||||
|
Shift_Left,
|
||||||
|
Shift_Right,
|
||||||
|
Shift_Left_Saturating,
|
||||||
}
|
}
|
||||||
|
|
||||||
Stmt :: struct {
|
Stmt :: struct {
|
||||||
@@ -169,20 +194,20 @@ Stmt :: struct {
|
|||||||
immutable: bool,
|
immutable: bool,
|
||||||
value_control_flow: bool,
|
value_control_flow: bool,
|
||||||
pointer_capture: bool,
|
pointer_capture: bool,
|
||||||
|
expand: bool,
|
||||||
error_only: bool,
|
error_only: bool,
|
||||||
// Assignments store the lvalue in `target`, the right-hand side in `expr`,
|
// Assignments store the lvalue in `target`, the right-hand side in `expr`,
|
||||||
// and the source operator in `assignment_op`. `Set` is ordinary `=`;
|
// and the source operator in `assignment_op`. `Set` is ordinary `=`;
|
||||||
// the arithmetic variants are `+=`, `-=`, `*=`, and `/=`.
|
// the remaining variants preserve their corresponding compound operator.
|
||||||
assignment_op: Assignment_Op,
|
assignment_op: Assignment_Op,
|
||||||
target: Expr_Id,
|
target: Expr_Id,
|
||||||
expr: Expr_Id,
|
expr: Expr_Id,
|
||||||
// `If` statements use `expr` as the condition, `captures` as optional
|
// `If` and `While` statements use `expr` as the condition, `captures` as
|
||||||
// unwrap binding names, `guard` as the optional post-unwrap boolean
|
// optional unwrap binding names, and `guard` as the optional post-unwrap
|
||||||
// condition, `body` as the then-block, and `else_body` as the else-block.
|
// boolean condition. `If` uses `body` as the then-block and `else_body` as
|
||||||
// An `else if` chain is represented as an `else_body` holding a single
|
// the else-block; an `else if` chain is represented as an `else_body`
|
||||||
// nested `If` statement.
|
// holding a single nested `If` statement. `While` uses `body` as the loop
|
||||||
// `While` statements use `expr` as the condition, `body` as the loop body,
|
// body and `update` as the optional post-iteration statement.
|
||||||
// and `update` as the optional post-iteration statement.
|
|
||||||
// `For` statements use `expr` as the iterable, `name` as the item capture,
|
// `For` statements use `expr` as the iterable, `name` as the item capture,
|
||||||
// `index_name` as the optional index capture, and `pointer_capture` to
|
// `index_name` as the optional index capture, and `pointer_capture` to
|
||||||
// distinguish `|@item|` from copy capture.
|
// distinguish `|@item|` from copy capture.
|
||||||
@@ -195,7 +220,8 @@ Stmt :: struct {
|
|||||||
// list (empty marks the `else` arm; more than one is a multi-pattern arm),
|
// list (empty marks the `else` arm; more than one is a multi-pattern arm),
|
||||||
// `captures` for the optional payload capture (0 or 1 name, tagged-union variants
|
// `captures` for the optional payload capture (0 or 1 name, tagged-union variants
|
||||||
// only) with `pointer_capture` distinguishing `|@cap|` from `|cap|`, and `body`
|
// only) with `pointer_capture` distinguishing `|@cap|` from `|cap|`, and `body`
|
||||||
// as the arm body.
|
// as the arm body. An `inline` arm has `expand` set, no patterns, and one or two
|
||||||
|
// captures for its specialized value/payload and optional tagged-union tag.
|
||||||
captures: []symbol.Id,
|
captures: []symbol.Id,
|
||||||
// `Match_Arm` pattern list; empty ⇒ the `else` arm.
|
// `Match_Arm` pattern list; empty ⇒ the `else` arm.
|
||||||
patterns: []Expr_Id,
|
patterns: []Expr_Id,
|
||||||
@@ -214,12 +240,15 @@ Function :: struct {
|
|||||||
c_abi: bool,
|
c_abi: bool,
|
||||||
imported: bool,
|
imported: bool,
|
||||||
generated: bool,
|
generated: bool,
|
||||||
file_hidden: bool,
|
analysis_root: bool,
|
||||||
|
test: bool,
|
||||||
|
visibility: types.Visibility,
|
||||||
has_body: bool,
|
has_body: bool,
|
||||||
variadic: bool,
|
variadic: bool,
|
||||||
params: []Param,
|
params: []Param,
|
||||||
result: Type_Syntax,
|
result: Type_Syntax,
|
||||||
error: Type_Syntax,
|
error: Type_Syntax,
|
||||||
|
infer_error: bool,
|
||||||
body: []Stmt_Id,
|
body: []Stmt_Id,
|
||||||
link_name: string,
|
link_name: string,
|
||||||
unsupported_reason: string,
|
unsupported_reason: string,
|
||||||
@@ -236,11 +265,24 @@ Global :: struct {
|
|||||||
immutable: bool,
|
immutable: bool,
|
||||||
external: bool,
|
external: bool,
|
||||||
writable: bool,
|
writable: bool,
|
||||||
file_hidden: bool,
|
visibility: types.Visibility,
|
||||||
expr: Expr_Id,
|
expr: Expr_Id,
|
||||||
diagnostic: source.Diagnostic_Id,
|
diagnostic: source.Diagnostic_Id,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Enum_Value :: struct {
|
||||||
|
expr: Expr_Id,
|
||||||
|
span: source.Span,
|
||||||
|
explicit: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
Enum_Declaration :: struct {
|
||||||
|
type: types.Type,
|
||||||
|
pkg: Package_Id,
|
||||||
|
file: File_Id,
|
||||||
|
values: []Enum_Value,
|
||||||
|
}
|
||||||
|
|
||||||
Import :: struct {
|
Import :: struct {
|
||||||
span: source.Span,
|
span: source.Span,
|
||||||
alias: symbol.Id,
|
alias: symbol.Id,
|
||||||
@@ -250,6 +292,7 @@ Import :: struct {
|
|||||||
target: Package_Id,
|
target: Package_Id,
|
||||||
valid: bool,
|
valid: bool,
|
||||||
used: bool,
|
used: bool,
|
||||||
|
test_only: bool,
|
||||||
diagnostic: source.Diagnostic_Id,
|
diagnostic: source.Diagnostic_Id,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -270,11 +313,19 @@ Declaration_Alias :: struct {
|
|||||||
target_pkg: Package_Id,
|
target_pkg: Package_Id,
|
||||||
target: u32,
|
target: u32,
|
||||||
kind: Declaration_Alias_Kind,
|
kind: Declaration_Alias_Kind,
|
||||||
file_hidden: bool,
|
visibility: types.Visibility,
|
||||||
valid: bool,
|
valid: bool,
|
||||||
diagnostic: source.Diagnostic_Id,
|
diagnostic: source.Diagnostic_Id,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Struct_Field_Default :: struct {
|
||||||
|
record: Type_Syntax,
|
||||||
|
field: symbol.Id,
|
||||||
|
expr: Expr_Id,
|
||||||
|
pkg: Package_Id,
|
||||||
|
file: File_Id,
|
||||||
|
}
|
||||||
|
|
||||||
File :: struct {
|
File :: struct {
|
||||||
source: source.Source_Id,
|
source: source.Source_Id,
|
||||||
pkg: Package_Id,
|
pkg: Package_Id,
|
||||||
@@ -284,9 +335,15 @@ Package :: struct {
|
|||||||
path: string,
|
path: string,
|
||||||
name: symbol.Id,
|
name: symbol.Id,
|
||||||
available: bool,
|
available: bool,
|
||||||
|
test: bool,
|
||||||
kind: Package_Kind,
|
kind: Package_Kind,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Compile_Mode :: enum u8 {
|
||||||
|
Executable,
|
||||||
|
Test,
|
||||||
|
}
|
||||||
|
|
||||||
Package_Kind :: enum u8 {
|
Package_Kind :: enum u8 {
|
||||||
Native,
|
Native,
|
||||||
C_Header,
|
C_Header,
|
||||||
@@ -313,6 +370,7 @@ Module :: struct {
|
|||||||
statements: [dynamic]Stmt,
|
statements: [dynamic]Stmt,
|
||||||
functions: [dynamic]Function,
|
functions: [dynamic]Function,
|
||||||
globals: [dynamic]Global,
|
globals: [dynamic]Global,
|
||||||
|
enum_declarations: [dynamic]Enum_Declaration,
|
||||||
imports: [dynamic]Import,
|
imports: [dynamic]Import,
|
||||||
aliases: [dynamic]Declaration_Alias,
|
aliases: [dynamic]Declaration_Alias,
|
||||||
files: [dynamic]File,
|
files: [dynamic]File,
|
||||||
@@ -321,6 +379,8 @@ Module :: struct {
|
|||||||
c_trampolines: [dynamic]Trampoline,
|
c_trampolines: [dynamic]Trampoline,
|
||||||
strings: [dynamic]string,
|
strings: [dynamic]string,
|
||||||
type_fields: [dynamic]types.Field,
|
type_fields: [dynamic]types.Field,
|
||||||
|
struct_field_defaults: [dynamic]Struct_Field_Default,
|
||||||
|
type_uses: [dynamic]Type_Use,
|
||||||
type_store: types.Store,
|
type_store: types.Store,
|
||||||
allocator: mem.Allocator,
|
allocator: mem.Allocator,
|
||||||
}
|
}
|
||||||
@@ -333,6 +393,7 @@ init_module :: proc(allocator := context.allocator) -> Module {
|
|||||||
module.statements.allocator = allocator
|
module.statements.allocator = allocator
|
||||||
module.functions.allocator = allocator
|
module.functions.allocator = allocator
|
||||||
module.globals.allocator = allocator
|
module.globals.allocator = allocator
|
||||||
|
module.enum_declarations.allocator = allocator
|
||||||
module.imports.allocator = allocator
|
module.imports.allocator = allocator
|
||||||
module.aliases.allocator = allocator
|
module.aliases.allocator = allocator
|
||||||
module.files.allocator = allocator
|
module.files.allocator = allocator
|
||||||
@@ -341,6 +402,8 @@ init_module :: proc(allocator := context.allocator) -> Module {
|
|||||||
module.c_trampolines.allocator = allocator
|
module.c_trampolines.allocator = allocator
|
||||||
module.strings.allocator = allocator
|
module.strings.allocator = allocator
|
||||||
module.type_fields.allocator = allocator
|
module.type_fields.allocator = allocator
|
||||||
|
module.struct_field_defaults.allocator = allocator
|
||||||
|
module.type_uses.allocator = allocator
|
||||||
return module
|
return module
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -367,6 +430,9 @@ destroy_module :: proc(module: ^Module) {
|
|||||||
for global in module.globals {
|
for global in module.globals {
|
||||||
delete(global.link_name, module.allocator)
|
delete(global.link_name, module.allocator)
|
||||||
}
|
}
|
||||||
|
for declaration in module.enum_declarations {
|
||||||
|
delete(declaration.values, module.allocator)
|
||||||
|
}
|
||||||
for pkg in module.packages {
|
for pkg in module.packages {
|
||||||
delete(pkg.path, module.allocator)
|
delete(pkg.path, module.allocator)
|
||||||
}
|
}
|
||||||
@@ -385,6 +451,7 @@ destroy_module :: proc(module: ^Module) {
|
|||||||
delete(module.statements)
|
delete(module.statements)
|
||||||
delete(module.functions)
|
delete(module.functions)
|
||||||
delete(module.globals)
|
delete(module.globals)
|
||||||
|
delete(module.enum_declarations)
|
||||||
delete(module.imports)
|
delete(module.imports)
|
||||||
delete(module.aliases)
|
delete(module.aliases)
|
||||||
delete(module.files)
|
delete(module.files)
|
||||||
@@ -393,5 +460,7 @@ destroy_module :: proc(module: ^Module) {
|
|||||||
delete(module.c_trampolines)
|
delete(module.c_trampolines)
|
||||||
delete(module.strings)
|
delete(module.strings)
|
||||||
delete(module.type_fields)
|
delete(module.type_fields)
|
||||||
|
delete(module.struct_field_defaults)
|
||||||
|
delete(module.type_uses)
|
||||||
types.destroy_store(&module.type_store)
|
types.destroy_store(&module.type_store)
|
||||||
}
|
}
|
||||||
|
|||||||
+92
-45
@@ -43,26 +43,7 @@ destroy_build_config :: proc(cfg: ^BuildConfig) {
|
|||||||
delete(cfg.c_options.defines)
|
delete(cfg.c_options.defines)
|
||||||
}
|
}
|
||||||
|
|
||||||
// run_build implements `brolang build [root]`: it loads and type-checks
|
load_build_config :: proc(project_root: string) -> (BuildConfig, bool) {
|
||||||
// `root/build.bro`, reads its `config` constant, and compiles the program
|
|
||||||
// package the config names. build.bro is only checked (never lowered/emitted),
|
|
||||||
// so the config is read straight from the HIR.
|
|
||||||
run_build :: proc(root: string) -> int {
|
|
||||||
project_root := root
|
|
||||||
owns_project_root := false
|
|
||||||
if len(project_root) == 0 {
|
|
||||||
found_root, found := find_build_root()
|
|
||||||
if !found {
|
|
||||||
fmt.eprintln("brolang build: could not find build.bro in the current directory or any parent")
|
|
||||||
return 2
|
|
||||||
}
|
|
||||||
project_root = found_root
|
|
||||||
owns_project_root = true
|
|
||||||
}
|
|
||||||
defer if owns_project_root {
|
|
||||||
delete(project_root)
|
|
||||||
}
|
|
||||||
|
|
||||||
sources := source.init_store()
|
sources := source.init_store()
|
||||||
defer source.destroy_store(&sources)
|
defer source.destroy_store(&sources)
|
||||||
diagnostics := source.init_store_diagnostics(&sources)
|
diagnostics := source.init_store_diagnostics(&sources)
|
||||||
@@ -73,7 +54,7 @@ run_build :: proc(root: string) -> int {
|
|||||||
arena: vmem.Arena
|
arena: vmem.Arena
|
||||||
if err := vmem.arena_init_growing(&arena); err != nil {
|
if err := vmem.arena_init_growing(&arena); err != nil {
|
||||||
fmt.eprintln("failed to initialize build arena:", err)
|
fmt.eprintln("failed to initialize build arena:", err)
|
||||||
return 2
|
return {}, false
|
||||||
}
|
}
|
||||||
defer vmem.arena_destroy(&arena)
|
defer vmem.arena_destroy(&arena)
|
||||||
a := vmem.arena_allocator(&arena)
|
a := vmem.arena_allocator(&arena)
|
||||||
@@ -82,18 +63,40 @@ run_build :: proc(root: string) -> int {
|
|||||||
if !loaded {
|
if !loaded {
|
||||||
source.print_all(&diagnostics)
|
source.print_all(&diagnostics)
|
||||||
fmt.eprintln("failed to load build root:", project_root)
|
fmt.eprintln("failed to load build root:", project_root)
|
||||||
return 2
|
return {}, false
|
||||||
}
|
}
|
||||||
// check needs no `main`: it synthesizes a trap main and emits one benign
|
// check needs no `main`: it synthesizes a trap main and emits one benign
|
||||||
// "missing main" diagnostic, which is expected for build.bro. Suppress that
|
// "missing main" diagnostic, which is expected for a build config. Suppress
|
||||||
// one but surface any real errors in build.bro (and fail on them).
|
// that one but surface any real errors in the build config (and fail on them).
|
||||||
hir_module := checker.check(&ast_module, &diagnostics, &symbols, target.DEFAULT, a)
|
hir_module := checker.check(&ast_module, &diagnostics, &symbols, target.DEFAULT, a)
|
||||||
if build_bro_has_errors(&diagnostics) {
|
if build_config_has_errors(&diagnostics) {
|
||||||
source.print_all(&diagnostics)
|
source.print_all(&diagnostics)
|
||||||
|
return {}, false
|
||||||
|
}
|
||||||
|
return extract_build_config(&hir_module, &symbols)
|
||||||
|
}
|
||||||
|
|
||||||
|
project_root_for_command :: proc(root, command: string) -> (string, bool, bool) {
|
||||||
|
if len(root) > 0 {
|
||||||
|
return root, false, true
|
||||||
|
}
|
||||||
|
project_root, found := find_build_root()
|
||||||
|
if !found {
|
||||||
|
fmt.eprintfln("brolang %s: could not find build.bro or build.hon in the current directory or any parent", command)
|
||||||
|
return "", false, false
|
||||||
|
}
|
||||||
|
return project_root, true, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// run_build implements `brolang build [root]`: it reads the build config and
|
||||||
|
// compiles the configured program package.
|
||||||
|
run_build :: proc(root: string) -> int {
|
||||||
|
project_root, owned, found := project_root_for_command(root, "build")
|
||||||
|
if !found {
|
||||||
return 2
|
return 2
|
||||||
}
|
}
|
||||||
|
defer if owned {delete(project_root)}
|
||||||
cfg, ok := extract_build_config(&hir_module, &symbols)
|
cfg, ok := load_build_config(project_root)
|
||||||
if !ok {
|
if !ok {
|
||||||
return 2
|
return 2
|
||||||
}
|
}
|
||||||
@@ -109,6 +112,48 @@ run_build :: proc(root: string) -> int {
|
|||||||
return compile_package(program, output, cfg.link_arguments, target.DEFAULT, cfg.c_options, project_root)
|
return compile_package(program, output, cfg.link_arguments, target.DEFAULT, cfg.c_options, project_root)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
run_tests :: proc(root: string) -> int {
|
||||||
|
project_root, owned, found := project_root_for_command(root, "test")
|
||||||
|
if !found {
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
defer if owned {delete(project_root)}
|
||||||
|
cfg, ok := load_build_config(project_root)
|
||||||
|
if !ok {
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
defer destroy_build_config(&cfg)
|
||||||
|
|
||||||
|
program := filepath.join({project_root, cfg.source_dir})
|
||||||
|
defer delete(program)
|
||||||
|
test_name := fmt.aprintf("%s-test", cfg.output_name)
|
||||||
|
defer delete(test_name)
|
||||||
|
output, output_ok := build_output_path(project_root, test_name)
|
||||||
|
if !output_ok {
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
defer delete(output)
|
||||||
|
status := compile_package(
|
||||||
|
program, output, cfg.link_arguments, target.DEFAULT, cfg.c_options, project_root, .Test,
|
||||||
|
)
|
||||||
|
if status != 0 {
|
||||||
|
return status
|
||||||
|
}
|
||||||
|
state, stdout, stderr, err := os2.process_exec(
|
||||||
|
os2.Process_Desc{command=[]string{output}},
|
||||||
|
context.allocator,
|
||||||
|
)
|
||||||
|
defer delete(stdout)
|
||||||
|
defer delete(stderr)
|
||||||
|
if len(stdout) > 0 {fmt.print(string(stdout))}
|
||||||
|
if len(stderr) > 0 {fmt.eprint(string(stderr))}
|
||||||
|
if err != nil {
|
||||||
|
fmt.eprintln("failed to run test executable:", err)
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
return 0 if state.exit_code == 0 else 1
|
||||||
|
}
|
||||||
|
|
||||||
valid_output_name :: proc(name: string) -> bool {
|
valid_output_name :: proc(name: string) -> bool {
|
||||||
return len(name) > 0 && name != "." && name != ".." &&
|
return len(name) > 0 && name != "." && name != ".." &&
|
||||||
!strings.contains(name, "/") && !strings.contains(name, "\\")
|
!strings.contains(name, "/") && !strings.contains(name, "\\")
|
||||||
@@ -116,7 +161,7 @@ valid_output_name :: proc(name: string) -> bool {
|
|||||||
|
|
||||||
build_output_path :: proc(project_root, name: string, allocator := context.allocator) -> (string, bool) {
|
build_output_path :: proc(project_root, name: string, allocator := context.allocator) -> (string, bool) {
|
||||||
if !valid_output_name(name) {
|
if !valid_output_name(name) {
|
||||||
fmt.eprintfln("build.bro: config 'name' must be a plain executable name, got '%s'", name)
|
fmt.eprintfln("build config: config 'name' must be a plain executable name, got '%s'", name)
|
||||||
return "", false
|
return "", false
|
||||||
}
|
}
|
||||||
build_dir, dir_error := filepath.join({project_root, "build"}, allocator)
|
build_dir, dir_error := filepath.join({project_root, "build"}, allocator)
|
||||||
@@ -157,15 +202,17 @@ find_build_root_from :: proc(start: string, allocator := context.allocator) -> (
|
|||||||
current = strings.clone(start, allocator)
|
current = strings.clone(start, allocator)
|
||||||
}
|
}
|
||||||
for {
|
for {
|
||||||
build_path, build_error := filepath.join({current, "build.bro"}, allocator)
|
for build_file in ([?]string{"build.bro", "build.hon"}) {
|
||||||
if build_error != nil {
|
build_path, build_error := filepath.join({current, build_file}, allocator)
|
||||||
delete(current, allocator)
|
if build_error != nil {
|
||||||
return "", false
|
delete(current, allocator)
|
||||||
}
|
return "", false
|
||||||
found := os.exists(build_path)
|
}
|
||||||
delete(build_path, allocator)
|
found := os.exists(build_path)
|
||||||
if found {
|
delete(build_path, allocator)
|
||||||
return current, true
|
if found {
|
||||||
|
return current, true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if current == "/" {
|
if current == "/" {
|
||||||
delete(current, allocator)
|
delete(current, allocator)
|
||||||
@@ -182,10 +229,10 @@ find_build_root_from :: proc(start: string, allocator := context.allocator) -> (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// build_bro_has_errors reports whether checking build.bro produced any diagnostic
|
// build_config_has_errors reports whether checking a build config produced any
|
||||||
// other than the benign "missing or unusable main function" (build.bro has no
|
// diagnostic other than the benign "missing or unusable main function" (build
|
||||||
// main by design; that one is emitted with an empty span).
|
// configs have no main by design; that one is emitted with an empty span).
|
||||||
build_bro_has_errors :: proc(diagnostics: ^source.Diagnostics) -> bool {
|
build_config_has_errors :: proc(diagnostics: ^source.Diagnostics) -> bool {
|
||||||
for item in diagnostics.items {
|
for item in diagnostics.items {
|
||||||
if item.span == (source.Span{}) && item.message == "missing or unusable main function" {
|
if item.span == (source.Span{}) && item.message == "missing or unusable main function" {
|
||||||
continue
|
continue
|
||||||
@@ -210,12 +257,12 @@ extract_build_config :: proc(m: ^hir.Module, symbols: ^symbol.Table) -> (BuildCo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !found {
|
if !found {
|
||||||
fmt.eprintln("build.bro: missing top-level 'config' constant")
|
fmt.eprintln("build config: missing top-level 'config' constant")
|
||||||
return {}, false
|
return {}, false
|
||||||
}
|
}
|
||||||
root := unwrap_coercions(m, config_expr)
|
root := unwrap_coercions(m, config_expr)
|
||||||
if root == hir.INVALID_EXPR || m.exprs[root].kind != .Struct {
|
if root == hir.INVALID_EXPR || m.exprs[root].kind != .Struct {
|
||||||
fmt.eprintln("build.bro: 'config' must be a BuildConfig{...} literal")
|
fmt.eprintln("build config: 'config' must be a BuildConfig{...} literal")
|
||||||
return {}, false
|
return {}, false
|
||||||
}
|
}
|
||||||
args := m.exprs[root].args
|
args := m.exprs[root].args
|
||||||
@@ -277,7 +324,7 @@ extract_build_config :: proc(m: ^hir.Module, symbols: ^symbol.Table) -> (BuildCo
|
|||||||
cfg.c_options.defines = defines[:]
|
cfg.c_options.defines = defines[:]
|
||||||
|
|
||||||
if len(cfg.output_name) == 0 || len(cfg.source_dir) == 0 {
|
if len(cfg.output_name) == 0 || len(cfg.source_dir) == 0 {
|
||||||
fmt.eprintln("build.bro: config requires non-empty 'name' and 'source'")
|
fmt.eprintln("build config: config requires non-empty 'name' and 'source'")
|
||||||
destroy_build_config(&cfg)
|
destroy_build_config(&cfg)
|
||||||
return {}, false
|
return {}, false
|
||||||
}
|
}
|
||||||
@@ -291,7 +338,7 @@ unwrap_coercions :: proc(m: ^hir.Module, id: hir.Expr_Id) -> hir.Expr_Id {
|
|||||||
for cur != hir.INVALID_EXPR && int(cur) < len(m.exprs) {
|
for cur != hir.INVALID_EXPR && int(cur) < len(m.exprs) {
|
||||||
#partial switch m.exprs[cur].kind {
|
#partial switch m.exprs[cur].kind {
|
||||||
case .Retype, .Pointer_Cast, .Weaken_Slice, .Weaken_Pointer, .Decay_Array_Pointer, .Slice_Ptr,
|
case .Retype, .Pointer_Cast, .Weaken_Slice, .Weaken_Pointer, .Decay_Array_Pointer, .Slice_Ptr,
|
||||||
.Widen, .Sum_Widen, .Optional_Some, .C_Coerce, .Scalar_Cast:
|
.Widen, .Sum_Widen, .Sum_Project, .Optional_Some, .C_Coerce, .Scalar_Cast:
|
||||||
cur = m.exprs[cur].left
|
cur = m.exprs[cur].left
|
||||||
case:
|
case:
|
||||||
return cur
|
return cur
|
||||||
|
|||||||
+5644
-981
File diff suppressed because it is too large
Load Diff
+3226
-264
File diff suppressed because it is too large
Load Diff
@@ -502,7 +502,7 @@ translate_type :: proc(ctx: ^Context, value: CXType, preferred_record_name := ""
|
|||||||
params: [dynamic]Type_Id
|
params: [dynamic]Type_Id
|
||||||
params.allocator = ctx.allocator
|
params.allocator = ctx.allocator
|
||||||
for index in 0..<count {
|
for index in 0..<count {
|
||||||
param := translate_type(ctx, ctx.api.get_arg_type(value, u32(index)), "", depth+1)
|
param := translate_parameter_type(ctx, ctx.api.get_arg_type(value, u32(index)), depth+1)
|
||||||
append(¶ms, param)
|
append(¶ms, param)
|
||||||
if param == INVALID_TYPE {
|
if param == INVALID_TYPE {
|
||||||
delete(params)
|
delete(params)
|
||||||
@@ -537,6 +537,30 @@ translate_type :: proc(ctx: ^Context, value: CXType, preferred_record_name := ""
|
|||||||
return INVALID_TYPE
|
return INVALID_TYPE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
translate_parameter_type :: proc(ctx: ^Context, value: CXType, depth := 0) -> Type_Id {
|
||||||
|
if depth > 64 {
|
||||||
|
return INVALID_TYPE
|
||||||
|
}
|
||||||
|
array := value
|
||||||
|
if value.kind != CXType_ConstantArray && value.kind != CXType_IncompleteArray {
|
||||||
|
canonical := ctx.api.get_canonical_type(value)
|
||||||
|
if canonical.kind != CXType_ConstantArray && canonical.kind != CXType_IncompleteArray {
|
||||||
|
return translate_type(ctx, value, "", depth+1)
|
||||||
|
}
|
||||||
|
array = canonical
|
||||||
|
}
|
||||||
|
element := ctx.api.get_array_element_type(array)
|
||||||
|
child := translate_type(ctx, element, "", depth+1)
|
||||||
|
if child == INVALID_TYPE {
|
||||||
|
return INVALID_TYPE
|
||||||
|
}
|
||||||
|
return add_type(ctx, Type{
|
||||||
|
kind=.Pointer,
|
||||||
|
child=child,
|
||||||
|
mutable=ctx.api.is_const_qualified_type(element) == 0,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
has_named :: proc(items: []Unsupported, name: string) -> bool {
|
has_named :: proc(items: []Unsupported, name: string) -> bool {
|
||||||
for item in items {
|
for item in items {
|
||||||
if item.name == name {
|
if item.name == name {
|
||||||
@@ -1385,7 +1409,7 @@ visit_cursor :: proc "c"(cursor, parent: CXCursor, client_data: rawptr) -> i32 {
|
|||||||
reason = "function declaration has no prototype"
|
reason = "function declaration has no prototype"
|
||||||
} else {
|
} else {
|
||||||
for index in 0..<count {
|
for index in 0..<count {
|
||||||
param := translate_type(ctx, ctx.api.get_arg_type(function_type, u32(index)))
|
param := translate_parameter_type(ctx, ctx.api.get_arg_type(function_type, u32(index)))
|
||||||
append(¶ms, param)
|
append(¶ms, param)
|
||||||
if param == INVALID_TYPE && len(reason) == 0 {
|
if param == INVALID_TYPE && len(reason) == 0 {
|
||||||
reason = "function parameter type is not supported"
|
reason = "function parameter type is not supported"
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package compiler
|
package compiler
|
||||||
|
|
||||||
|
import "./ast"
|
||||||
import "./backend"
|
import "./backend"
|
||||||
import "./cimport"
|
import "./cimport"
|
||||||
import "./checker"
|
import "./checker"
|
||||||
@@ -28,12 +29,22 @@ write_escaped_c_string :: proc(builder: ^strings.Builder, value: string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
has_errors :: proc(diagnostics: ^source.Diagnostics) -> bool {
|
||||||
|
for diagnostic in diagnostics.items {
|
||||||
|
if diagnostic.severity == .Error {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
compile_package :: proc(
|
compile_package :: proc(
|
||||||
input_path, output_path: string,
|
input_path, output_path: string,
|
||||||
link_arguments: []linker.Argument = nil,
|
link_arguments: []linker.Argument = nil,
|
||||||
selected := target.DEFAULT,
|
selected := target.DEFAULT,
|
||||||
c_options := cimport.Options{},
|
c_options := cimport.Options{},
|
||||||
project_root := "",
|
project_root := "",
|
||||||
|
mode := ast.Compile_Mode.Executable,
|
||||||
) -> int {
|
) -> int {
|
||||||
sources := source.init_store()
|
sources := source.init_store()
|
||||||
defer source.destroy_store(&sources)
|
defer source.destroy_store(&sources)
|
||||||
@@ -77,12 +88,18 @@ compile_package :: proc(
|
|||||||
c_options,
|
c_options,
|
||||||
selected,
|
selected,
|
||||||
project_root if len(project_root) > 0 else input_path,
|
project_root if len(project_root) > 0 else input_path,
|
||||||
|
mode,
|
||||||
)
|
)
|
||||||
if !loaded {
|
if !loaded {
|
||||||
source.print_all(&diagnostics)
|
source.print_all(&diagnostics)
|
||||||
fmt.eprintln("failed to load root package directory:", input_path)
|
fmt.eprintln("failed to load root package directory:", input_path)
|
||||||
return 2
|
return 2
|
||||||
}
|
}
|
||||||
|
effective_root := project_root if len(project_root) > 0 else input_path
|
||||||
|
if !prepare_tests(&ast_module, &sources, &diagnostics, &symbols, mode, effective_root) {
|
||||||
|
source.print_all(&diagnostics)
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
// Generated C trampolines (for `static inline` imports) must be compiled and
|
// Generated C trampolines (for `static inline` imports) must be compiled and
|
||||||
// linked with the program. Write them out and add the source as a link input
|
// linked with the program. Write them out and add the source as a link input
|
||||||
@@ -134,6 +151,10 @@ compile_package :: proc(
|
|||||||
vmem.arena_free_all(&lexer_arena)
|
vmem.arena_free_all(&lexer_arena)
|
||||||
hir_module := checker.check(&ast_module, &diagnostics, &symbols, selected, vmem.arena_allocator(&checker_arena))
|
hir_module := checker.check(&ast_module, &diagnostics, &symbols, selected, vmem.arena_allocator(&checker_arena))
|
||||||
vmem.arena_free_all(&parser_arena)
|
vmem.arena_free_all(&parser_arena)
|
||||||
|
if has_errors(&diagnostics) {
|
||||||
|
source.print_all(&diagnostics)
|
||||||
|
return 1
|
||||||
|
}
|
||||||
ir_module := lower.lower(&hir_module, vmem.arena_allocator(&lower_arena))
|
ir_module := lower.lower(&hir_module, vmem.arena_allocator(&lower_arena))
|
||||||
vmem.arena_free_all(&checker_arena)
|
vmem.arena_free_all(&checker_arena)
|
||||||
opt.run(&ir_module)
|
opt.run(&ir_module)
|
||||||
@@ -141,6 +162,10 @@ compile_package :: proc(
|
|||||||
llvm_text := llvm.emit(&ir_module, &diagnostics, &symbols)
|
llvm_text := llvm.emit(&ir_module, &diagnostics, &symbols)
|
||||||
defer delete(llvm_text)
|
defer delete(llvm_text)
|
||||||
vmem.arena_free_all(&lower_arena)
|
vmem.arena_free_all(&lower_arena)
|
||||||
|
if has_errors(&diagnostics) {
|
||||||
|
source.print_all(&diagnostics)
|
||||||
|
return 1
|
||||||
|
}
|
||||||
llvm_path := fmt.tprintf("%s.brolang-%d.ll", output_path, os2.get_pid())
|
llvm_path := fmt.tprintf("%s.brolang-%d.ll", output_path, os2.get_pid())
|
||||||
defer _ = os.remove(llvm_path)
|
defer _ = os.remove(llvm_path)
|
||||||
if err := os.write_entire_file_or_err(llvm_path, transmute([]byte)llvm_text); err != nil {
|
if err := os.write_entire_file_or_err(llvm_path, transmute([]byte)llvm_text); err != nil {
|
||||||
|
|||||||
+36
-11
@@ -74,13 +74,16 @@ Linkage :: enum u8 {
|
|||||||
|
|
||||||
Expr_Kind :: enum u8 {
|
Expr_Kind :: enum u8 {
|
||||||
Invalid,
|
Invalid,
|
||||||
|
Void,
|
||||||
Integer,
|
Integer,
|
||||||
Float,
|
Float,
|
||||||
String,
|
String,
|
||||||
Bool,
|
Bool,
|
||||||
|
Undefined,
|
||||||
Array,
|
Array,
|
||||||
Struct,
|
Struct,
|
||||||
None,
|
Null,
|
||||||
|
Unreachable,
|
||||||
Optional_Some,
|
Optional_Some,
|
||||||
Local,
|
Local,
|
||||||
Global,
|
Global,
|
||||||
@@ -99,16 +102,19 @@ Expr_Kind :: enum u8 {
|
|||||||
Catch,
|
Catch,
|
||||||
Widen,
|
Widen,
|
||||||
Sum_Widen,
|
Sum_Widen,
|
||||||
|
Sum_Project,
|
||||||
C_Coerce,
|
C_Coerce,
|
||||||
C_Vararg_Promote,
|
C_Vararg_Promote,
|
||||||
Retype,
|
Retype,
|
||||||
Scalar_Cast,
|
Scalar_Cast,
|
||||||
Pointer_Cast,
|
Pointer_Cast,
|
||||||
|
Const_Cast,
|
||||||
Weaken_Pointer,
|
Weaken_Pointer,
|
||||||
Weaken_Slice,
|
Weaken_Slice,
|
||||||
Decay_Array_Pointer,
|
Decay_Array_Pointer,
|
||||||
Negate,
|
Negate,
|
||||||
Not,
|
Not,
|
||||||
|
Bit_Not,
|
||||||
Add,
|
Add,
|
||||||
Sub,
|
Sub,
|
||||||
Mul,
|
Mul,
|
||||||
@@ -120,6 +126,12 @@ Expr_Kind :: enum u8 {
|
|||||||
Rem,
|
Rem,
|
||||||
Mod,
|
Mod,
|
||||||
Pointer_Add,
|
Pointer_Add,
|
||||||
|
Bit_And,
|
||||||
|
Bit_Or,
|
||||||
|
Bit_Xor,
|
||||||
|
Shift_Left,
|
||||||
|
Shift_Right,
|
||||||
|
Shift_Left_Saturating,
|
||||||
Eq,
|
Eq,
|
||||||
Ne,
|
Ne,
|
||||||
Lt,
|
Lt,
|
||||||
@@ -129,9 +141,15 @@ Expr_Kind :: enum u8 {
|
|||||||
And,
|
And,
|
||||||
Or,
|
Or,
|
||||||
Range,
|
Range,
|
||||||
|
Mem_Copy,
|
||||||
|
Mem_Set,
|
||||||
Call,
|
Call,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CATCH_EXPRESSION :: i64(0)
|
||||||
|
CATCH_BLOCK :: i64(1)
|
||||||
|
CATCH_VOID_FALLTHROUGH :: i64(2)
|
||||||
|
|
||||||
Expr :: struct {
|
Expr :: struct {
|
||||||
span: source.Span,
|
span: source.Span,
|
||||||
type: types.Type,
|
type: types.Type,
|
||||||
@@ -140,9 +158,9 @@ Expr :: struct {
|
|||||||
// encoded as Expr_Id because it otherwise has no args.
|
// encoded as Expr_Id because it otherwise has no args.
|
||||||
args: []Expr_Id,
|
args: []Expr_Id,
|
||||||
// `Catch` block handlers use `body` for the handler statements and `target`
|
// `Catch` block handlers use `body` for the handler statements and `target`
|
||||||
// for the optional captured error local. A missing `right` means the handler
|
// for the optional captured error local. `integer` selects the catch mode;
|
||||||
// exits on every path and therefore has no fallback value. `Try` uses `body`
|
// a missing `right` means the block exits or completes with void. `Try` uses
|
||||||
// for active error-exit cleanup.
|
// `body` for active error-exit cleanup.
|
||||||
body: []Stmt_Id,
|
body: []Stmt_Id,
|
||||||
target: Ref,
|
target: Ref,
|
||||||
left: Expr_Id,
|
left: Expr_Id,
|
||||||
@@ -187,6 +205,12 @@ Assignment_Op :: enum u8 {
|
|||||||
Mul,
|
Mul,
|
||||||
Div,
|
Div,
|
||||||
Pointer_Add,
|
Pointer_Add,
|
||||||
|
Bit_And,
|
||||||
|
Bit_Or,
|
||||||
|
Bit_Xor,
|
||||||
|
Shift_Left,
|
||||||
|
Shift_Right,
|
||||||
|
Shift_Left_Saturating,
|
||||||
}
|
}
|
||||||
|
|
||||||
Stmt :: struct {
|
Stmt :: struct {
|
||||||
@@ -201,16 +225,16 @@ Stmt :: struct {
|
|||||||
expr: Expr_Id,
|
expr: Expr_Id,
|
||||||
iterator_type: types.Type,
|
iterator_type: types.Type,
|
||||||
pointer_capture: bool,
|
pointer_capture: bool,
|
||||||
// Assignments carry their operation explicitly. Arithmetic operations lower
|
// Assignments carry their operation explicitly. Compound operations lower
|
||||||
// by computing the target address once, loading its current value, applying
|
// by computing the target address once, loading its current value, applying
|
||||||
// the operation to `expr`, and storing through the original address.
|
// the operation to `expr`, and storing through the original address.
|
||||||
assignment_op: Assignment_Op,
|
assignment_op: Assignment_Op,
|
||||||
// Boolean `If` statements use `expr` as the condition. Conditional unwraps
|
// Boolean `If` and `While` statements use `expr` as the condition.
|
||||||
// use `unwraps` for the ordered optional expressions and capture locals, and
|
// Conditional unwraps use `unwraps` for the ordered optional expressions
|
||||||
// `guard` for the optional boolean checked after every unwrap succeeds.
|
// and capture locals, and `guard` for the optional boolean checked after
|
||||||
// Both forms use `then_body`/`else_body` as the branch statement lists.
|
// every unwrap succeeds. `If` uses `then_body`/`else_body` as its branches.
|
||||||
// `While` statements use `expr` as the condition, `then_body` as the loop
|
// `While` uses `then_body` as its loop body and `update` as the optional
|
||||||
// body, and `update` as the optional post-iteration statement.
|
// post-iteration statement.
|
||||||
// `For` statements use `expr` as the iterable, `local` as the item capture,
|
// `For` statements use `expr` as the iterable, `local` as the item capture,
|
||||||
// `index_local` as the optional sequence index, and `iterator_type` as the
|
// `index_local` as the optional sequence index, and `iterator_type` as the
|
||||||
// normalized many-item pointer type for sequence iteration.
|
// normalized many-item pointer type for sequence iteration.
|
||||||
@@ -247,6 +271,7 @@ Global :: struct {
|
|||||||
expr: Expr_Id,
|
expr: Expr_Id,
|
||||||
static_value: i64,
|
static_value: i64,
|
||||||
is_static: bool,
|
is_static: bool,
|
||||||
|
eager: bool,
|
||||||
external: bool,
|
external: bool,
|
||||||
writable: bool,
|
writable: bool,
|
||||||
dependencies: [dynamic]Global_Id,
|
dependencies: [dynamic]Global_Id,
|
||||||
|
|||||||
+14
-1
@@ -68,9 +68,10 @@ Linkage :: enum u8 {
|
|||||||
Opcode :: enum u8 {
|
Opcode :: enum u8 {
|
||||||
Param,
|
Param,
|
||||||
Const,
|
Const,
|
||||||
|
Poison,
|
||||||
String,
|
String,
|
||||||
Aggregate,
|
Aggregate,
|
||||||
None,
|
Null,
|
||||||
Optional_Some,
|
Optional_Some,
|
||||||
Load_Global,
|
Load_Global,
|
||||||
Function_Address,
|
Function_Address,
|
||||||
@@ -96,11 +97,13 @@ Opcode :: enum u8 {
|
|||||||
Orelse,
|
Orelse,
|
||||||
Widen,
|
Widen,
|
||||||
Sum_Widen,
|
Sum_Widen,
|
||||||
|
Sum_Project,
|
||||||
C_Coerce,
|
C_Coerce,
|
||||||
C_Vararg_Promote,
|
C_Vararg_Promote,
|
||||||
Retype,
|
Retype,
|
||||||
Scalar_Cast,
|
Scalar_Cast,
|
||||||
Pointer_Cast,
|
Pointer_Cast,
|
||||||
|
Const_Cast,
|
||||||
Weaken_Pointer,
|
Weaken_Pointer,
|
||||||
Weaken_Slice,
|
Weaken_Slice,
|
||||||
Decay_Array_Pointer,
|
Decay_Array_Pointer,
|
||||||
@@ -117,7 +120,16 @@ Opcode :: enum u8 {
|
|||||||
Mod_Checked,
|
Mod_Checked,
|
||||||
Pointer_Add,
|
Pointer_Add,
|
||||||
Not,
|
Not,
|
||||||
|
Bit_Not,
|
||||||
|
Bit_And,
|
||||||
|
Bit_Or,
|
||||||
|
Bit_Xor,
|
||||||
|
Shift_Left,
|
||||||
|
Shift_Right,
|
||||||
|
Shift_Left_Saturating,
|
||||||
Compare,
|
Compare,
|
||||||
|
Mem_Copy,
|
||||||
|
Mem_Set,
|
||||||
Label,
|
Label,
|
||||||
Br,
|
Br,
|
||||||
Cond_Br,
|
Cond_Br,
|
||||||
@@ -167,6 +179,7 @@ Global :: struct {
|
|||||||
link_name: string,
|
link_name: string,
|
||||||
type: types.Type,
|
type: types.Type,
|
||||||
is_static: bool,
|
is_static: bool,
|
||||||
|
eager: bool,
|
||||||
external: bool,
|
external: bool,
|
||||||
writable: bool,
|
writable: bool,
|
||||||
static_value: i64,
|
static_value: i64,
|
||||||
|
|||||||
+82
-13
@@ -8,13 +8,19 @@ is_identifier_start :: proc(value: byte) -> bool {
|
|||||||
return value == '_' || value >= 'a' && value <= 'z' || value >= 'A' && value <= 'Z'
|
return value == '_' || value >= 'a' && value <= 'z' || value >= 'A' && value <= 'Z'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
is_hex_digit :: proc(value: byte) -> bool {
|
||||||
|
return value >= '0' && value <= '9' || value >= 'a' && value <= 'f' || value >= 'A' && value <= 'F'
|
||||||
|
}
|
||||||
|
|
||||||
is_identifier_continue :: proc(value: byte) -> bool {
|
is_identifier_continue :: proc(value: byte) -> bool {
|
||||||
return is_identifier_start(value) || value >= '0' && value <= '9'
|
return is_identifier_start(value) || value >= '0' && value <= '9'
|
||||||
}
|
}
|
||||||
|
|
||||||
keyword_kind :: proc(text: string) -> token.Kind {
|
keyword_kind :: proc(text: string) -> token.Kind {
|
||||||
switch text {
|
switch text {
|
||||||
|
case "test": return .Keyword_Test
|
||||||
case "func": return .Keyword_Func
|
case "func": return .Keyword_Func
|
||||||
|
case "proc": return .Keyword_Func
|
||||||
case "c_func": return .Keyword_C_Func
|
case "c_func": return .Keyword_C_Func
|
||||||
case "struct": return .Keyword_Struct
|
case "struct": return .Keyword_Struct
|
||||||
case "c_struct": return .Keyword_C_Struct
|
case "c_struct": return .Keyword_C_Struct
|
||||||
@@ -28,14 +34,16 @@ keyword_kind :: proc(text: string) -> token.Kind {
|
|||||||
case "try": return .Keyword_Try
|
case "try": return .Keyword_Try
|
||||||
case "catch": return .Keyword_Catch
|
case "catch": return .Keyword_Catch
|
||||||
case "mut": return .Keyword_Mut
|
case "mut": return .Keyword_Mut
|
||||||
case "none": return .Keyword_None
|
case "null": return .Keyword_Null
|
||||||
case "undefined": return .Keyword_Undefined
|
case "undefined": return .Keyword_Undefined
|
||||||
case "orelse": return .Keyword_Orelse
|
case "orelse": return .Keyword_Orelse
|
||||||
case "and": return .Keyword_And
|
case "and": return .Keyword_And
|
||||||
case "or": return .Keyword_Or
|
case "or": return .Keyword_Or
|
||||||
|
case "xor": return .Keyword_Xor
|
||||||
case "if": return .Keyword_If
|
case "if": return .Keyword_If
|
||||||
case "while": return .Keyword_While
|
case "while": return .Keyword_While
|
||||||
case "for": return .Keyword_For
|
case "for": return .Keyword_For
|
||||||
|
case "inline": return .Keyword_Inline
|
||||||
case "break": return .Keyword_Break
|
case "break": return .Keyword_Break
|
||||||
case "continue": return .Keyword_Continue
|
case "continue": return .Keyword_Continue
|
||||||
case "defer": return .Keyword_Defer
|
case "defer": return .Keyword_Defer
|
||||||
@@ -46,9 +54,12 @@ keyword_kind :: proc(text: string) -> token.Kind {
|
|||||||
case "true": return .Keyword_True
|
case "true": return .Keyword_True
|
||||||
case "false": return .Keyword_False
|
case "false": return .Keyword_False
|
||||||
case "void": return .Keyword_Void
|
case "void": return .Keyword_Void
|
||||||
|
case "noreturn": return .Keyword_Noreturn
|
||||||
case "anyopaque": return .Keyword_Anyopaque
|
case "anyopaque": return .Keyword_Anyopaque
|
||||||
|
case "unreachable": return .Keyword_Unreachable
|
||||||
case "bool": return .Keyword_Bool
|
case "bool": return .Keyword_Bool
|
||||||
case "int": return .Keyword_Int
|
case "int": return .Keyword_Int
|
||||||
|
case "uint": return .Keyword_Uint
|
||||||
case "float": return .Keyword_Float
|
case "float": return .Keyword_Float
|
||||||
case "range": return .Keyword_Range
|
case "range": return .Keyword_Range
|
||||||
case "i8": return .Keyword_I8
|
case "i8": return .Keyword_I8
|
||||||
@@ -125,9 +136,16 @@ lex :: proc(
|
|||||||
case ':':
|
case ':':
|
||||||
start := cursor
|
start := cursor
|
||||||
cursor += 1
|
cursor += 1
|
||||||
if cursor < len(bytes) && bytes[cursor] == ':' {
|
if cursor < len(bytes) {
|
||||||
cursor += 1
|
if bytes[cursor] == ':' {
|
||||||
append_token(&stream, source_file, .Colon_Colon, start, cursor)
|
cursor += 1
|
||||||
|
append_token(&stream, source_file, .Colon_Colon, start, cursor)
|
||||||
|
} else if bytes[cursor] == '=' {
|
||||||
|
cursor += 1
|
||||||
|
append_token(&stream, source_file, .Colon_Equal, start, cursor)
|
||||||
|
} else {
|
||||||
|
append_token(&stream, source_file, .Colon, start, cursor)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
append_token(&stream, source_file, .Colon, start, cursor)
|
append_token(&stream, source_file, .Colon, start, cursor)
|
||||||
}
|
}
|
||||||
@@ -152,7 +170,23 @@ lex :: proc(
|
|||||||
case '<':
|
case '<':
|
||||||
start := cursor
|
start := cursor
|
||||||
cursor += 1
|
cursor += 1
|
||||||
if cursor < len(bytes) && bytes[cursor] == '=' {
|
if cursor < len(bytes) && bytes[cursor] == '<' {
|
||||||
|
cursor += 1
|
||||||
|
if cursor < len(bytes) && bytes[cursor] == '|' {
|
||||||
|
cursor += 1
|
||||||
|
if cursor < len(bytes) && bytes[cursor] == '=' {
|
||||||
|
cursor += 1
|
||||||
|
append_token(&stream, source_file, .Less_Less_Pipe_Equal, start, cursor)
|
||||||
|
} else {
|
||||||
|
append_token(&stream, source_file, .Less_Less_Pipe, start, cursor)
|
||||||
|
}
|
||||||
|
} else if cursor < len(bytes) && bytes[cursor] == '=' {
|
||||||
|
cursor += 1
|
||||||
|
append_token(&stream, source_file, .Less_Less_Equal, start, cursor)
|
||||||
|
} else {
|
||||||
|
append_token(&stream, source_file, .Less_Less, start, cursor)
|
||||||
|
}
|
||||||
|
} else if cursor < len(bytes) && bytes[cursor] == '=' {
|
||||||
cursor += 1
|
cursor += 1
|
||||||
append_token(&stream, source_file, .Less_Equal, start, cursor)
|
append_token(&stream, source_file, .Less_Equal, start, cursor)
|
||||||
} else {
|
} else {
|
||||||
@@ -161,7 +195,15 @@ lex :: proc(
|
|||||||
case '>':
|
case '>':
|
||||||
start := cursor
|
start := cursor
|
||||||
cursor += 1
|
cursor += 1
|
||||||
if cursor < len(bytes) && bytes[cursor] == '=' {
|
if cursor < len(bytes) && bytes[cursor] == '>' {
|
||||||
|
cursor += 1
|
||||||
|
if cursor < len(bytes) && bytes[cursor] == '=' {
|
||||||
|
cursor += 1
|
||||||
|
append_token(&stream, source_file, .Greater_Greater_Equal, start, cursor)
|
||||||
|
} else {
|
||||||
|
append_token(&stream, source_file, .Greater_Greater, start, cursor)
|
||||||
|
}
|
||||||
|
} else if cursor < len(bytes) && bytes[cursor] == '=' {
|
||||||
cursor += 1
|
cursor += 1
|
||||||
append_token(&stream, source_file, .Greater_Equal, start, cursor)
|
append_token(&stream, source_file, .Greater_Equal, start, cursor)
|
||||||
} else {
|
} else {
|
||||||
@@ -227,11 +269,20 @@ lex :: proc(
|
|||||||
append_token(&stream, source_file, .Slash, start, cursor)
|
append_token(&stream, source_file, .Slash, start, cursor)
|
||||||
}
|
}
|
||||||
case '&':
|
case '&':
|
||||||
append_token(&stream, source_file, .Ampersand, cursor, cursor+1)
|
start := cursor
|
||||||
cursor += 1
|
cursor += 1
|
||||||
|
if cursor < len(bytes) && bytes[cursor] == '=' {
|
||||||
|
cursor += 1
|
||||||
|
append_token(&stream, source_file, .Ampersand_Equal, start, cursor)
|
||||||
|
} else {
|
||||||
|
append_token(&stream, source_file, .Ampersand, start, cursor)
|
||||||
|
}
|
||||||
case '^':
|
case '^':
|
||||||
append_token(&stream, source_file, .Caret, cursor, cursor+1)
|
append_token(&stream, source_file, .Caret, cursor, cursor+1)
|
||||||
cursor += 1
|
cursor += 1
|
||||||
|
case '~':
|
||||||
|
append_token(&stream, source_file, .Tilde, cursor, cursor+1)
|
||||||
|
cursor += 1
|
||||||
case '?':
|
case '?':
|
||||||
append_token(&stream, source_file, .Question, cursor, cursor+1)
|
append_token(&stream, source_file, .Question, cursor, cursor+1)
|
||||||
cursor += 1
|
cursor += 1
|
||||||
@@ -257,8 +308,14 @@ lex :: proc(
|
|||||||
append_token(&stream, source_file, .Comma, cursor, cursor+1)
|
append_token(&stream, source_file, .Comma, cursor, cursor+1)
|
||||||
cursor += 1
|
cursor += 1
|
||||||
case '|':
|
case '|':
|
||||||
append_token(&stream, source_file, .Pipe, cursor, cursor+1)
|
start := cursor
|
||||||
cursor += 1
|
cursor += 1
|
||||||
|
if cursor < len(bytes) && bytes[cursor] == '=' {
|
||||||
|
cursor += 1
|
||||||
|
append_token(&stream, source_file, .Pipe_Equal, start, cursor)
|
||||||
|
} else {
|
||||||
|
append_token(&stream, source_file, .Pipe, start, cursor)
|
||||||
|
}
|
||||||
case '"':
|
case '"':
|
||||||
start := cursor
|
start := cursor
|
||||||
cursor += 1
|
cursor += 1
|
||||||
@@ -266,13 +323,21 @@ lex :: proc(
|
|||||||
for cursor < len(bytes) && bytes[cursor] != '"' && bytes[cursor] != '\n' {
|
for cursor < len(bytes) && bytes[cursor] != '"' && bytes[cursor] != '\n' {
|
||||||
if bytes[cursor] == '\\' {
|
if bytes[cursor] == '\\' {
|
||||||
cursor += 1
|
cursor += 1
|
||||||
if cursor >= len(bytes) ||
|
valid_escape := cursor < len(bytes) &&
|
||||||
(bytes[cursor] != '\\' && bytes[cursor] != '"' && bytes[cursor] != 'n' &&
|
(bytes[cursor] == '\\' || bytes[cursor] == '"' || bytes[cursor] == 'n' ||
|
||||||
bytes[cursor] != 'r' && bytes[cursor] != 't' && bytes[cursor] != '0') {
|
bytes[cursor] == 'r' || bytes[cursor] == 't' || bytes[cursor] == '0')
|
||||||
|
if cursor < len(bytes) && bytes[cursor] == 'x' {
|
||||||
|
valid_escape = cursor+2 < len(bytes) &&
|
||||||
|
is_hex_digit(bytes[cursor+1]) && is_hex_digit(bytes[cursor+2])
|
||||||
|
if valid_escape {
|
||||||
|
cursor += 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !valid_escape {
|
||||||
source.add(
|
source.add(
|
||||||
diagnostics,
|
diagnostics,
|
||||||
source.Span{file=source_file.id, start=source.Offset(max(cursor-1, start)), end=source.Offset(min(cursor+1, len(bytes)))},
|
source.Span{file=source_file.id, start=source.Offset(max(cursor-1, start)), end=source.Offset(min(cursor+3, len(bytes)))},
|
||||||
"strings only support '\\\\', '\\\"', '\\n', '\\r', '\\t', and '\\0' escapes",
|
"strings only support '\\\\', '\\\"', '\\n', '\\r', '\\t', '\\0', and '\\xNN' escapes",
|
||||||
)
|
)
|
||||||
valid = false
|
valid = false
|
||||||
}
|
}
|
||||||
@@ -366,6 +431,10 @@ lex :: proc(
|
|||||||
}
|
}
|
||||||
text := source_file.text[start:cursor]
|
text := source_file.text[start:cursor]
|
||||||
kind := keyword_kind(text)
|
kind := keyword_kind(text)
|
||||||
|
if kind == .Keyword_Xor && cursor < len(bytes) && bytes[cursor] == '=' {
|
||||||
|
cursor += 1
|
||||||
|
kind = .Xor_Equal
|
||||||
|
}
|
||||||
id := symbol.INVALID
|
id := symbol.INVALID
|
||||||
if kind == .Identifier || kind == .Underscore {
|
if kind == .Identifier || kind == .Underscore {
|
||||||
id = symbol.intern(symbols, text)
|
id = symbol.intern(symbols, text)
|
||||||
|
|||||||
+419
-48
@@ -142,6 +142,11 @@ llvm_type :: proc(value: types.Type, store: ^types.Store = nil) -> string {
|
|||||||
if types.is_void(resolved) {
|
if types.is_void(resolved) {
|
||||||
return "void"
|
return "void"
|
||||||
}
|
}
|
||||||
|
// `noreturn` has no value representation. Keep malformed storage recoverable with
|
||||||
|
// a byte carrier; function and call result positions map it to LLVM `void` below.
|
||||||
|
if types.is_noreturn(resolved) {
|
||||||
|
return "i8"
|
||||||
|
}
|
||||||
if types.is_bool(resolved) {
|
if types.is_bool(resolved) {
|
||||||
return "i1"
|
return "i1"
|
||||||
}
|
}
|
||||||
@@ -183,6 +188,9 @@ function_result_type :: proc(function: ir.Function, store: ^types.Store) -> stri
|
|||||||
if function.is_main {
|
if function.is_main {
|
||||||
return "i32"
|
return "i32"
|
||||||
}
|
}
|
||||||
|
if types.is_noreturn(function.result) {
|
||||||
|
return "void"
|
||||||
|
}
|
||||||
if function.calling_convention == .C {
|
if function.calling_convention == .C {
|
||||||
return c_abi_result_type(function.result, store)
|
return c_abi_result_type(function.result, store)
|
||||||
}
|
}
|
||||||
@@ -240,6 +248,29 @@ valid_instruction :: proc(instructions: []ir.Instruction, instruction_id: ir.Ins
|
|||||||
return instruction_id != ir.INVALID_INSTRUCTION && int(instruction_id) < len(instructions)
|
return instruction_id != ir.INVALID_INSTRUCTION && int(instruction_id) < len(instructions)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
memory_region :: proc(value: types.Type, store: ^types.Store) -> (
|
||||||
|
child, array_type: types.Type,
|
||||||
|
count: u64,
|
||||||
|
mutable, is_slice, ok: bool,
|
||||||
|
) {
|
||||||
|
resolved := types.resolve_alias(value, store)
|
||||||
|
item, item_ok := types.node(store, resolved)
|
||||||
|
if !item_ok {
|
||||||
|
return types.INVALID, types.INVALID, 0, false, false, false
|
||||||
|
}
|
||||||
|
if item.kind == .Slice {
|
||||||
|
return item.child, types.INVALID, 0, item.mutable, true, true
|
||||||
|
}
|
||||||
|
if item.kind == .Pointer && !item.many {
|
||||||
|
array_type = types.resolve_alias(item.child, store)
|
||||||
|
array, array_ok := types.node(store, array_type)
|
||||||
|
if array_ok && array.kind == .Array {
|
||||||
|
return array.child, array_type, array.count, item.mutable && array.mutable, false, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return types.INVALID, types.INVALID, 0, false, false, false
|
||||||
|
}
|
||||||
|
|
||||||
valid_value :: proc(
|
valid_value :: proc(
|
||||||
instructions: []ir.Instruction,
|
instructions: []ir.Instruction,
|
||||||
value_id: ir.Instruction_Id,
|
value_id: ir.Instruction_Id,
|
||||||
@@ -252,18 +283,19 @@ valid_value :: proc(
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
switch instructions[value_id].op {
|
switch instructions[value_id].op {
|
||||||
case .Param, .Const, .String, .Aggregate, .None, .Optional_Some,
|
case .Param, .Const, .Poison, .String, .Aggregate, .Null, .Optional_Some,
|
||||||
.Load_Global, .Function_Address, .Address_Of, .Load, .Union_Tag, .Slice, .Length, .Slice_Ptr,
|
.Load_Global, .Function_Address, .Address_Of, .Load, .Union_Tag, .Slice, .Length, .Slice_Ptr,
|
||||||
.Fallible_Error, .Extract, .Select, .Unwrap,
|
.Fallible_Error, .Extract, .Select, .Unwrap,
|
||||||
.Optional_Is_Some, .Optional_Value, .Orelse,
|
.Optional_Is_Some, .Optional_Value, .Orelse,
|
||||||
.Widen, .Sum_Widen, .C_Coerce, .C_Vararg_Promote, .Retype, .Scalar_Cast, .Pointer_Cast, .Weaken_Pointer, .Weaken_Slice, .Decay_Array_Pointer,
|
.Widen, .Sum_Widen, .Sum_Project, .C_Coerce, .C_Vararg_Promote, .Retype, .Scalar_Cast, .Pointer_Cast, .Const_Cast, .Weaken_Pointer, .Weaken_Slice, .Decay_Array_Pointer,
|
||||||
.Neg_Checked, .Add_Checked, .Sub_Checked, .Mul_Checked, .Div_Checked,
|
.Neg_Checked, .Add_Checked, .Sub_Checked, .Mul_Checked, .Div_Checked,
|
||||||
.Div_Trunc_Checked, .Div_Floor_Checked, .Div_Exact_Checked, .Div_Ceil_Checked,
|
.Div_Trunc_Checked, .Div_Floor_Checked, .Div_Exact_Checked, .Div_Ceil_Checked,
|
||||||
.Rem_Checked, .Mod_Checked,
|
.Rem_Checked, .Mod_Checked,
|
||||||
.Pointer_Add, .Not, .Compare, .Call:
|
.Pointer_Add, .Not, .Bit_Not, .Bit_And, .Bit_Or, .Bit_Xor,
|
||||||
|
.Shift_Left, .Shift_Right, .Shift_Left_Saturating, .Compare, .Call:
|
||||||
return true
|
return true
|
||||||
case .Address_Global, .Alloca, .Index_Address, .Field_Address, .Orelse_Begin,
|
case .Address_Global, .Alloca, .Index_Address, .Field_Address, .Orelse_Begin,
|
||||||
.Store, .Fill, .Trap, .Label, .Br, .Cond_Br, .Return, .Return_Void:
|
.Store, .Fill, .Mem_Copy, .Mem_Set, .Trap, .Label, .Br, .Cond_Br, .Return, .Return_Void:
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
@@ -332,6 +364,8 @@ write_operand :: proc(
|
|||||||
value := instructions[value_id]
|
value := instructions[value_id]
|
||||||
if value.op == .Const {
|
if value.op == .Const {
|
||||||
write_constant(builder, value.integer, expected, store)
|
write_constant(builder, value.integer, expected, store)
|
||||||
|
} else if value.op == .Poison {
|
||||||
|
strings.write_string(builder, "poison")
|
||||||
} else {
|
} else {
|
||||||
fmt.sbprintf(builder, "%%v%d", value_id)
|
fmt.sbprintf(builder, "%%v%d", value_id)
|
||||||
}
|
}
|
||||||
@@ -383,8 +417,9 @@ emit_recovery_value :: proc(emitter: ^Emitter, instruction_id: int, instruction:
|
|||||||
message := diagnostic_message(emitter, instruction.diagnostic, instruction.span, fallback)
|
message := diagnostic_message(emitter, instruction.diagnostic, instruction.span, fallback)
|
||||||
emit_trap_call(emitter, message)
|
emit_trap_call(emitter, message)
|
||||||
if types.is_runtime_value(instruction.type, &emitter.module.types) {
|
if types.is_runtime_value(instruction.type, &emitter.module.types) {
|
||||||
if !types.is_float(instruction.type, emitter.module.target) {
|
representation := types.runtime_representation(instruction.type, &emitter.module.types)
|
||||||
if !types.is_concrete_scalar(instruction.type) {
|
if !types.is_float(representation, emitter.module.target) {
|
||||||
|
if !types.is_concrete_scalar(representation) {
|
||||||
fmt.sbprintf(
|
fmt.sbprintf(
|
||||||
&emitter.builder,
|
&emitter.builder,
|
||||||
" %%v%d = freeze %s zeroinitializer\n",
|
" %%v%d = freeze %s zeroinitializer\n",
|
||||||
@@ -549,8 +584,9 @@ emit_checked_arithmetic :: proc(
|
|||||||
float_op: string,
|
float_op: string,
|
||||||
overflow_message: string,
|
overflow_message: string,
|
||||||
) {
|
) {
|
||||||
|
representation := types.runtime_representation(instruction.type, &emitter.module.types)
|
||||||
type_name := llvm_type(instruction.type, &emitter.module.types)
|
type_name := llvm_type(instruction.type, &emitter.module.types)
|
||||||
if types.is_float(instruction.type, emitter.module.target) {
|
if types.is_float(representation, emitter.module.target) {
|
||||||
fmt.sbprintf(&emitter.builder, " %%v%d = %s %s ", instruction_index, float_op, type_name)
|
fmt.sbprintf(&emitter.builder, " %%v%d = %s %s ", instruction_index, float_op, type_name)
|
||||||
write_operand(&emitter.builder, instructions, instruction.a, instruction.type, &emitter.module.types)
|
write_operand(&emitter.builder, instructions, instruction.a, instruction.type, &emitter.module.types)
|
||||||
strings.write_string(&emitter.builder, ", ")
|
strings.write_string(&emitter.builder, ", ")
|
||||||
@@ -558,7 +594,7 @@ emit_checked_arithmetic :: proc(
|
|||||||
strings.write_string(&emitter.builder, "\n")
|
strings.write_string(&emitter.builder, "\n")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
prefix := "u" if types.is_unsigned(instruction.type, emitter.module.target) else "s"
|
prefix := "u" if types.is_unsigned(representation, emitter.module.target) else "s"
|
||||||
fmt.sbprintf(&emitter.builder, " %%pair%d = call ", instruction_index)
|
fmt.sbprintf(&emitter.builder, " %%pair%d = call ", instruction_index)
|
||||||
strings.write_string(&emitter.builder, "{ ")
|
strings.write_string(&emitter.builder, "{ ")
|
||||||
fmt.sbprintf(&emitter.builder, "%s, i1 } @llvm.%s%s.with.overflow.%s(%s ", type_name, prefix, mnemonic, type_name, type_name)
|
fmt.sbprintf(&emitter.builder, "%s, i1 } @llvm.%s%s.with.overflow.%s(%s ", type_name, prefix, mnemonic, type_name, type_name)
|
||||||
@@ -591,8 +627,9 @@ emit_division_zero_guard :: proc(
|
|||||||
instruction_index: int,
|
instruction_index: int,
|
||||||
instruction: ir.Instruction,
|
instruction: ir.Instruction,
|
||||||
) {
|
) {
|
||||||
|
representation := types.runtime_representation(instruction.type, &emitter.module.types)
|
||||||
type_name := llvm_type(instruction.type, &emitter.module.types)
|
type_name := llvm_type(instruction.type, &emitter.module.types)
|
||||||
if types.is_float(instruction.type, emitter.module.target) {
|
if types.is_float(representation, emitter.module.target) {
|
||||||
fmt.sbprintf(&emitter.builder, " %%divzero%d = fcmp oeq %s ", instruction_index, type_name)
|
fmt.sbprintf(&emitter.builder, " %%divzero%d = fcmp oeq %s ", instruction_index, type_name)
|
||||||
write_operand(&emitter.builder, instructions, instruction.b, instruction.type, &emitter.module.types)
|
write_operand(&emitter.builder, instructions, instruction.b, instruction.type, &emitter.module.types)
|
||||||
strings.write_string(&emitter.builder, ", 0.000000e+00\n")
|
strings.write_string(&emitter.builder, ", 0.000000e+00\n")
|
||||||
@@ -621,7 +658,8 @@ emit_division_overflow_guard :: proc(
|
|||||||
instruction: ir.Instruction,
|
instruction: ir.Instruction,
|
||||||
) {
|
) {
|
||||||
type_name := llvm_type(instruction.type, &emitter.module.types)
|
type_name := llvm_type(instruction.type, &emitter.module.types)
|
||||||
min_value := -(i128(1) << u32(types.bits(instruction.type, emitter.module.target)-1))
|
representation := types.runtime_representation(instruction.type, &emitter.module.types)
|
||||||
|
min_value := -(i128(1) << u32(types.bits(representation, emitter.module.target)-1))
|
||||||
fmt.sbprintf(&emitter.builder, " %%divminlo%d = icmp eq %s ", instruction_index, type_name)
|
fmt.sbprintf(&emitter.builder, " %%divminlo%d = icmp eq %s ", instruction_index, type_name)
|
||||||
write_operand(&emitter.builder, instructions, instruction.a, instruction.type, &emitter.module.types)
|
write_operand(&emitter.builder, instructions, instruction.a, instruction.type, &emitter.module.types)
|
||||||
fmt.sbprintf(&emitter.builder, ", %d\n %%divminhi%d = icmp eq %s ", min_value, instruction_index, type_name)
|
fmt.sbprintf(&emitter.builder, ", %d\n %%divminhi%d = icmp eq %s ", min_value, instruction_index, type_name)
|
||||||
@@ -649,7 +687,8 @@ emit_float_division_builtin :: proc(
|
|||||||
instruction: ir.Instruction,
|
instruction: ir.Instruction,
|
||||||
) {
|
) {
|
||||||
type_name := llvm_type(instruction.type, &emitter.module.types)
|
type_name := llvm_type(instruction.type, &emitter.module.types)
|
||||||
suffix := "f32" if types.bits(instruction.type, emitter.module.target) == 32 else "f64"
|
representation := types.runtime_representation(instruction.type, &emitter.module.types)
|
||||||
|
suffix := "f32" if types.bits(representation, emitter.module.target) == 32 else "f64"
|
||||||
|
|
||||||
if instruction.op == .Rem_Checked || instruction.op == .Mod_Checked {
|
if instruction.op == .Rem_Checked || instruction.op == .Mod_Checked {
|
||||||
name := fmt.tprintf("%%v%d", instruction_index) if instruction.op == .Rem_Checked else fmt.tprintf("%%rawrem%d", instruction_index)
|
name := fmt.tprintf("%%v%d", instruction_index) if instruction.op == .Rem_Checked else fmt.tprintf("%%rawrem%d", instruction_index)
|
||||||
@@ -706,7 +745,8 @@ emit_integer_remainder_builtin :: proc(
|
|||||||
instruction: ir.Instruction,
|
instruction: ir.Instruction,
|
||||||
) {
|
) {
|
||||||
type_name := llvm_type(instruction.type, &emitter.module.types)
|
type_name := llvm_type(instruction.type, &emitter.module.types)
|
||||||
signed := types.is_signed(instruction.type, emitter.module.target)
|
representation := types.runtime_representation(instruction.type, &emitter.module.types)
|
||||||
|
signed := types.is_signed(representation, emitter.module.target)
|
||||||
raw_name := fmt.tprintf("%%v%d", instruction_index) if instruction.op == .Rem_Checked || !signed else fmt.tprintf("%%rawrem%d", instruction_index)
|
raw_name := fmt.tprintf("%%v%d", instruction_index) if instruction.op == .Rem_Checked || !signed else fmt.tprintf("%%rawrem%d", instruction_index)
|
||||||
if !signed {
|
if !signed {
|
||||||
fmt.sbprintf(&emitter.builder, " %s = urem %s ", raw_name, type_name)
|
fmt.sbprintf(&emitter.builder, " %s = urem %s ", raw_name, type_name)
|
||||||
@@ -715,7 +755,7 @@ emit_integer_remainder_builtin :: proc(
|
|||||||
write_operand(&emitter.builder, instructions, instruction.b, instruction.type, &emitter.module.types)
|
write_operand(&emitter.builder, instructions, instruction.b, instruction.type, &emitter.module.types)
|
||||||
strings.write_string(&emitter.builder, "\n")
|
strings.write_string(&emitter.builder, "\n")
|
||||||
} else {
|
} else {
|
||||||
min_value := -(i128(1) << u32(types.bits(instruction.type, emitter.module.target)-1))
|
min_value := -(i128(1) << u32(types.bits(representation, emitter.module.target)-1))
|
||||||
fmt.sbprintf(&emitter.builder, " %%remminlo%d = icmp eq %s ", instruction_index, type_name)
|
fmt.sbprintf(&emitter.builder, " %%remminlo%d = icmp eq %s ", instruction_index, type_name)
|
||||||
write_operand(&emitter.builder, instructions, instruction.a, instruction.type, &emitter.module.types)
|
write_operand(&emitter.builder, instructions, instruction.a, instruction.type, &emitter.module.types)
|
||||||
fmt.sbprintf(&emitter.builder, ", %d\n %%remminhi%d = icmp eq %s ", min_value, instruction_index, type_name)
|
fmt.sbprintf(&emitter.builder, ", %d\n %%remminhi%d = icmp eq %s ", min_value, instruction_index, type_name)
|
||||||
@@ -748,7 +788,8 @@ emit_integer_quotient_builtin :: proc(
|
|||||||
instruction: ir.Instruction,
|
instruction: ir.Instruction,
|
||||||
) {
|
) {
|
||||||
type_name := llvm_type(instruction.type, &emitter.module.types)
|
type_name := llvm_type(instruction.type, &emitter.module.types)
|
||||||
signed := types.is_signed(instruction.type, emitter.module.target)
|
representation := types.runtime_representation(instruction.type, &emitter.module.types)
|
||||||
|
signed := types.is_signed(representation, emitter.module.target)
|
||||||
operation := "sdiv" if signed else "udiv"
|
operation := "sdiv" if signed else "udiv"
|
||||||
name := fmt.tprintf("%%v%d", instruction_index) if instruction.op == .Div_Trunc_Checked else fmt.tprintf("%%divq%d", instruction_index)
|
name := fmt.tprintf("%%v%d", instruction_index) if instruction.op == .Div_Trunc_Checked else fmt.tprintf("%%divq%d", instruction_index)
|
||||||
fmt.sbprintf(&emitter.builder, " %s = %s %s ", name, operation, type_name)
|
fmt.sbprintf(&emitter.builder, " %s = %s %s ", name, operation, type_name)
|
||||||
@@ -798,13 +839,14 @@ emit_division_builtin :: proc(
|
|||||||
instruction: ir.Instruction,
|
instruction: ir.Instruction,
|
||||||
) {
|
) {
|
||||||
emit_division_zero_guard(emitter, instructions, instruction_index, instruction)
|
emit_division_zero_guard(emitter, instructions, instruction_index, instruction)
|
||||||
if types.is_float(instruction.type, emitter.module.target) {
|
representation := types.runtime_representation(instruction.type, &emitter.module.types)
|
||||||
|
if types.is_float(representation, emitter.module.target) {
|
||||||
emit_float_division_builtin(emitter, instructions, instruction_index, instruction)
|
emit_float_division_builtin(emitter, instructions, instruction_index, instruction)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
quotient := instruction.op == .Div_Trunc_Checked || instruction.op == .Div_Floor_Checked ||
|
quotient := instruction.op == .Div_Trunc_Checked || instruction.op == .Div_Floor_Checked ||
|
||||||
instruction.op == .Div_Exact_Checked || instruction.op == .Div_Ceil_Checked
|
instruction.op == .Div_Exact_Checked || instruction.op == .Div_Ceil_Checked
|
||||||
if quotient && types.is_signed(instruction.type, emitter.module.target) {
|
if quotient && types.is_signed(representation, emitter.module.target) {
|
||||||
emit_division_overflow_guard(emitter, instructions, instruction_index, instruction)
|
emit_division_overflow_guard(emitter, instructions, instruction_index, instruction)
|
||||||
}
|
}
|
||||||
if quotient {
|
if quotient {
|
||||||
@@ -814,6 +856,95 @@ emit_division_builtin :: proc(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
emit_shift :: proc(
|
||||||
|
emitter: ^Emitter,
|
||||||
|
instructions: []ir.Instruction,
|
||||||
|
instruction_index: int,
|
||||||
|
instruction: ir.Instruction,
|
||||||
|
) {
|
||||||
|
count_type := types.INVALID
|
||||||
|
if valid_instruction(instructions, instruction.b) {
|
||||||
|
count_type = instructions[instruction.b].type
|
||||||
|
}
|
||||||
|
representation := types.runtime_representation(instruction.type, &emitter.module.types)
|
||||||
|
if !types.is_concrete_integer(representation) ||
|
||||||
|
!valid_value(instructions, instruction.a, instruction.type, &emitter.module.types) ||
|
||||||
|
!types.is_concrete_integer(count_type) || !types.is_unsigned(count_type, emitter.module.target) ||
|
||||||
|
!valid_value(instructions, instruction.b, count_type, &emitter.module.types) {
|
||||||
|
emit_recovery_value(emitter, instruction_index, instruction, "invalid shift operands")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
type_name := llvm_type(instruction.type, &emitter.module.types)
|
||||||
|
bits := types.bits(representation, emitter.module.target)
|
||||||
|
count_bits := types.bits(count_type, emitter.module.target)
|
||||||
|
if count_bits < 64 {
|
||||||
|
fmt.sbprintf(&emitter.builder, " %%shift_count64_%d = zext %s ", instruction_index, llvm_type(count_type, &emitter.module.types))
|
||||||
|
write_operand(&emitter.builder, instructions, instruction.b, count_type, &emitter.module.types)
|
||||||
|
strings.write_string(&emitter.builder, " to i64\n")
|
||||||
|
} else {
|
||||||
|
fmt.sbprintf(&emitter.builder, " %%shift_count64_%d = select i1 true, i64 ", instruction_index)
|
||||||
|
write_operand(&emitter.builder, instructions, instruction.b, count_type, &emitter.module.types)
|
||||||
|
strings.write_string(&emitter.builder, ", i64 0\n")
|
||||||
|
}
|
||||||
|
fmt.sbprintf(&emitter.builder, " %%shift_in_range%d = icmp ult i64 %%shift_count64_%d, %d\n", instruction_index, instruction_index, bits)
|
||||||
|
|
||||||
|
if instruction.op != .Shift_Left_Saturating {
|
||||||
|
fmt.sbprintf(
|
||||||
|
&emitter.builder,
|
||||||
|
" br i1 %%shift_in_range%d, label %%shift_continue%d, label %%shift_trap%d\nshift_trap%d:\n",
|
||||||
|
instruction_index, instruction_index, instruction_index, instruction_index,
|
||||||
|
)
|
||||||
|
message := diagnostic_message(emitter, source.INVALID_DIAGNOSTIC, instruction.span, "shift count exceeds integer width")
|
||||||
|
emit_trap_call(emitter, message)
|
||||||
|
fmt.sbprintf(&emitter.builder, " unreachable\nshift_continue%d:\n", instruction_index)
|
||||||
|
if bits < 64 {
|
||||||
|
fmt.sbprintf(&emitter.builder, " %%shift_count%d = trunc i64 %%shift_count64_%d to %s\n", instruction_index, instruction_index, type_name)
|
||||||
|
}
|
||||||
|
operation := "shl"
|
||||||
|
if instruction.op == .Shift_Right {
|
||||||
|
operation = "ashr" if types.is_signed(representation, emitter.module.target) else "lshr"
|
||||||
|
}
|
||||||
|
fmt.sbprintf(&emitter.builder, " %%v%d = %s %s ", instruction_index, operation, type_name)
|
||||||
|
write_operand(&emitter.builder, instructions, instruction.a, instruction.type, &emitter.module.types)
|
||||||
|
if bits < 64 {
|
||||||
|
fmt.sbprintf(&emitter.builder, ", %%shift_count%d\n", instruction_index)
|
||||||
|
} else {
|
||||||
|
fmt.sbprintf(&emitter.builder, ", %%shift_count64_%d\n", instruction_index)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// LLVM's saturating shift intrinsics produce poison for oversized counts.
|
||||||
|
// Select zero before narrowing, then explicitly select the Zig endpoint.
|
||||||
|
fmt.sbprintf(&emitter.builder, " %%shift_safe64_%d = select i1 %%shift_in_range%d, i64 %%shift_count64_%d, i64 0\n", instruction_index, instruction_index, instruction_index)
|
||||||
|
if bits < 64 {
|
||||||
|
fmt.sbprintf(&emitter.builder, " %%shift_safe%d = trunc i64 %%shift_safe64_%d to %s\n", instruction_index, instruction_index, type_name)
|
||||||
|
}
|
||||||
|
signed := types.is_signed(representation, emitter.module.target)
|
||||||
|
intrinsic := "sshl" if signed else "ushl"
|
||||||
|
fmt.sbprintf(&emitter.builder, " %%shift_saturated%d = call %s @llvm.%s.sat.%s(%s ", instruction_index, type_name, intrinsic, type_name, type_name)
|
||||||
|
write_operand(&emitter.builder, instructions, instruction.a, instruction.type, &emitter.module.types)
|
||||||
|
if bits < 64 {
|
||||||
|
fmt.sbprintf(&emitter.builder, ", %s %%shift_safe%d)\n", type_name, instruction_index)
|
||||||
|
} else {
|
||||||
|
fmt.sbprintf(&emitter.builder, ", i64 %%shift_safe64_%d)\n", instruction_index)
|
||||||
|
}
|
||||||
|
fmt.sbprintf(&emitter.builder, " %%shift_nonzero%d = icmp ne %s ", instruction_index, type_name)
|
||||||
|
write_operand(&emitter.builder, instructions, instruction.a, instruction.type, &emitter.module.types)
|
||||||
|
strings.write_string(&emitter.builder, ", 0\n")
|
||||||
|
if signed {
|
||||||
|
minimum := -(i128(1) << u32(bits-1))
|
||||||
|
maximum := (i128(1) << u32(bits-1))-1
|
||||||
|
fmt.sbprintf(&emitter.builder, " %%shift_negative%d = icmp slt %s ", instruction_index, type_name)
|
||||||
|
write_operand(&emitter.builder, instructions, instruction.a, instruction.type, &emitter.module.types)
|
||||||
|
fmt.sbprintf(&emitter.builder, ", 0\n %%shift_nonzero_endpoint%d = select i1 %%shift_negative%d, %s %d, %s %d\n", instruction_index, instruction_index, type_name, minimum, type_name, maximum)
|
||||||
|
fmt.sbprintf(&emitter.builder, " %%shift_endpoint%d = select i1 %%shift_nonzero%d, %s %%shift_nonzero_endpoint%d, %s 0\n", instruction_index, instruction_index, type_name, instruction_index, type_name)
|
||||||
|
} else {
|
||||||
|
fmt.sbprintf(&emitter.builder, " %%shift_endpoint%d = select i1 %%shift_nonzero%d, %s -1, %s 0\n", instruction_index, instruction_index, type_name, type_name)
|
||||||
|
}
|
||||||
|
fmt.sbprintf(&emitter.builder, " %%v%d = select i1 %%shift_in_range%d, %s %%shift_saturated%d, %s %%shift_endpoint%d\n", instruction_index, instruction_index, type_name, instruction_index, type_name, instruction_index)
|
||||||
|
}
|
||||||
|
|
||||||
emit_instruction_stream :: proc(
|
emit_instruction_stream :: proc(
|
||||||
emitter: ^Emitter,
|
emitter: ^Emitter,
|
||||||
instructions: []ir.Instruction,
|
instructions: []ir.Instruction,
|
||||||
@@ -836,7 +967,7 @@ emit_instruction_stream :: proc(
|
|||||||
after_terminator = false
|
after_terminator = false
|
||||||
}
|
}
|
||||||
switch instruction.op {
|
switch instruction.op {
|
||||||
case .Param, .Const:
|
case .Param, .Const, .Poison:
|
||||||
case .String:
|
case .String:
|
||||||
string_id := int(instruction.integer)
|
string_id := int(instruction.integer)
|
||||||
_, array, pointer_ok := types.array_pointer(instruction.type, &emitter.module.types)
|
_, array, pointer_ok := types.array_pointer(instruction.type, &emitter.module.types)
|
||||||
@@ -919,6 +1050,16 @@ emit_instruction_stream :: proc(
|
|||||||
fmt.sbprintf(&emitter.builder, " %%fallible_payload%d = getelementptr i8, ptr %%fallible_slot%d, i64 %d\n", instruction_index, instruction_index, payload_offset)
|
fmt.sbprintf(&emitter.builder, " %%fallible_payload%d = getelementptr i8, ptr %%fallible_slot%d, i64 %d\n", instruction_index, instruction_index, payload_offset)
|
||||||
fmt.sbprintf(&emitter.builder, " call void @llvm.memcpy.p0.p0.i64(ptr %%fallible_payload%d, ptr %%fallible_error_payload%d, i64 %d, i1 false)\n", instruction_index, instruction_index, error_payload_size)
|
fmt.sbprintf(&emitter.builder, " call void @llvm.memcpy.p0.p0.i64(ptr %%fallible_payload%d, ptr %%fallible_error_payload%d, i64 %d, i1 false)\n", instruction_index, instruction_index, error_payload_size)
|
||||||
}
|
}
|
||||||
|
} else if types.is_struct(error_type, &emitter.module.types) {
|
||||||
|
if !valid_value(instructions, instruction.args[0], error_type, &emitter.module.types) {
|
||||||
|
emit_recovery_value(emitter, instruction_index, instruction, "invalid fallible struct error operand")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fmt.sbprintf(&emitter.builder, " store i16 1, ptr %%fallible_slot%d\n", instruction_index)
|
||||||
|
fmt.sbprintf(&emitter.builder, " %%fallible_payload%d = getelementptr i8, ptr %%fallible_slot%d, i64 %d\n", instruction_index, instruction_index, payload_offset)
|
||||||
|
fmt.sbprintf(&emitter.builder, " store %s ", llvm_type(error_type, &emitter.module.types))
|
||||||
|
write_operand(&emitter.builder, instructions, instruction.args[0], error_type, &emitter.module.types)
|
||||||
|
fmt.sbprintf(&emitter.builder, ", ptr %%fallible_payload%d\n", instruction_index)
|
||||||
}
|
}
|
||||||
fmt.sbprintf(&emitter.builder, " %%v%d = load %s, ptr %%fallible_slot%d\n", instruction_index, type_name, instruction_index)
|
fmt.sbprintf(&emitter.builder, " %%v%d = load %s, ptr %%fallible_slot%d\n", instruction_index, type_name, instruction_index)
|
||||||
continue
|
continue
|
||||||
@@ -976,6 +1117,15 @@ emit_instruction_stream :: proc(
|
|||||||
} else if item.kind == .Range && arg_index == 2 {
|
} else if item.kind == .Range && arg_index == 2 {
|
||||||
element_type = types.BOOL
|
element_type = types.BOOL
|
||||||
}
|
}
|
||||||
|
aggregate_index := arg_index
|
||||||
|
if item.kind == .Struct {
|
||||||
|
aggregate_index = types.physical_field_index(
|
||||||
|
&emitter.module.types,
|
||||||
|
instruction.type,
|
||||||
|
arg_index,
|
||||||
|
emitter.module.target,
|
||||||
|
)
|
||||||
|
}
|
||||||
final := arg_index == total-1
|
final := arg_index == total-1
|
||||||
if final {
|
if final {
|
||||||
fmt.sbprintf(&emitter.builder, " %%v%d = insertvalue %s ", instruction_index, type_name)
|
fmt.sbprintf(&emitter.builder, " %%v%d = insertvalue %s ", instruction_index, type_name)
|
||||||
@@ -993,12 +1143,12 @@ emit_instruction_stream :: proc(
|
|||||||
} else {
|
} else {
|
||||||
write_constant(&emitter.builder, i64(item.sentinel), element_type, &emitter.module.types)
|
write_constant(&emitter.builder, i64(item.sentinel), element_type, &emitter.module.types)
|
||||||
}
|
}
|
||||||
fmt.sbprintf(&emitter.builder, ", %d\n", arg_index)
|
fmt.sbprintf(&emitter.builder, ", %d\n", aggregate_index)
|
||||||
}
|
}
|
||||||
case .None:
|
case .Null:
|
||||||
item, ok := types.node(&emitter.module.types, instruction.type)
|
item, ok := types.node(&emitter.module.types, instruction.type)
|
||||||
if !ok || item.kind != .Optional {
|
if !ok || item.kind != .Optional {
|
||||||
emit_recovery_value(emitter, instruction_index, instruction, "invalid optional none")
|
emit_recovery_value(emitter, instruction_index, instruction, "invalid optional null")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if types.is_pointer(item.child, &emitter.module.types) {
|
if types.is_pointer(item.child, &emitter.module.types) {
|
||||||
@@ -1103,7 +1253,9 @@ emit_instruction_stream :: proc(
|
|||||||
fmt.sbprintf(
|
fmt.sbprintf(
|
||||||
&emitter.builder,
|
&emitter.builder,
|
||||||
" %%v%d = getelementptr %s, ptr %%v%d, i64 0\n",
|
" %%v%d = getelementptr %s, ptr %%v%d, i64 0\n",
|
||||||
instruction_index, llvm_type(child, &emitter.module.types), instruction.a,
|
instruction_index,
|
||||||
|
"i8" if types.is_void(child) else llvm_type(child, &emitter.module.types),
|
||||||
|
instruction.a,
|
||||||
)
|
)
|
||||||
case .Alloca:
|
case .Alloca:
|
||||||
if !types.is_runtime_value(instruction.type, &emitter.module.types) {
|
if !types.is_runtime_value(instruction.type, &emitter.module.types) {
|
||||||
@@ -1191,13 +1343,19 @@ emit_instruction_stream :: proc(
|
|||||||
emit_recovery_value(emitter, instruction_index, instruction, "invalid field reference")
|
emit_recovery_value(emitter, instruction_index, instruction, "invalid field reference")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
physical_index := types.physical_field_index(
|
||||||
|
&emitter.module.types,
|
||||||
|
base_type,
|
||||||
|
field_index,
|
||||||
|
emitter.module.target,
|
||||||
|
)
|
||||||
if types.is_union(base_type, &emitter.module.types) {
|
if types.is_union(base_type, &emitter.module.types) {
|
||||||
fmt.sbprintf(&emitter.builder, " %%v%d = getelementptr i8, ptr %%v%d, i64 %d\n", instruction_index, instruction.a, types.union_payload_offset(base_type, &emitter.module.types, emitter.module.target))
|
fmt.sbprintf(&emitter.builder, " %%v%d = getelementptr i8, ptr %%v%d, i64 %d\n", instruction_index, instruction.a, types.union_payload_offset(base_type, &emitter.module.types, emitter.module.target))
|
||||||
} else {
|
} else {
|
||||||
fmt.sbprintf(
|
fmt.sbprintf(
|
||||||
&emitter.builder,
|
&emitter.builder,
|
||||||
" %%v%d = getelementptr %s, ptr %%v%d, i32 0, i32 %d\n",
|
" %%v%d = getelementptr %s, ptr %%v%d, i32 0, i32 %d\n",
|
||||||
instruction_index, llvm_type(base_type, &emitter.module.types), instruction.a, field_index,
|
instruction_index, llvm_type(base_type, &emitter.module.types), instruction.a, physical_index,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
case .Load:
|
case .Load:
|
||||||
@@ -1256,6 +1414,12 @@ emit_instruction_stream :: proc(
|
|||||||
fmt.sbprintf(&emitter.builder, " %%v%d = load %s, ptr %%fallible_error_slot%d\n", instruction_index, type_name, instruction_index)
|
fmt.sbprintf(&emitter.builder, " %%v%d = load %s, ptr %%fallible_error_slot%d\n", instruction_index, type_name, instruction_index)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if types.is_struct(error_type, &emitter.module.types) {
|
||||||
|
source_offset := types.fallible_payload_offset(channel_type, &emitter.module.types, emitter.module.target)
|
||||||
|
fmt.sbprintf(&emitter.builder, " %%fallible_error_source%d = getelementptr i8, ptr %%v%d, i64 %d\n", instruction_index, instruction.a, source_offset)
|
||||||
|
fmt.sbprintf(&emitter.builder, " %%v%d = load %s, ptr %%fallible_error_source%d\n", instruction_index, llvm_type(error_type, &emitter.module.types), instruction_index)
|
||||||
|
continue
|
||||||
|
}
|
||||||
emit_recovery_value(emitter, instruction_index, instruction, "unsupported fallible error type")
|
emit_recovery_value(emitter, instruction_index, instruction, "unsupported fallible error type")
|
||||||
case .Store:
|
case .Store:
|
||||||
if !valid_address(instructions, instruction.a, instruction.type, &emitter.module.types) ||
|
if !valid_address(instructions, instruction.a, instruction.type, &emitter.module.types) ||
|
||||||
@@ -1277,6 +1441,109 @@ emit_instruction_stream :: proc(
|
|||||||
instruction.a,
|
instruction.a,
|
||||||
types.size(instruction.type, &emitter.module.types, emitter.module.target),
|
types.size(instruction.type, &emitter.module.types, emitter.module.target),
|
||||||
)
|
)
|
||||||
|
case .Mem_Copy:
|
||||||
|
if !valid_instruction(instructions, instruction.a) || !valid_instruction(instructions, instruction.b) {
|
||||||
|
emit_recovery_value(emitter, instruction_index, instruction, "invalid memcopy operands")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
destination_type := instructions[instruction.a].type
|
||||||
|
source_type := instructions[instruction.b].type
|
||||||
|
destination_child, destination_array, destination_count, destination_mutable, destination_is_slice, destination_ok := memory_region(destination_type, &emitter.module.types)
|
||||||
|
source_child, source_array, source_count, _, source_is_slice, source_ok := memory_region(source_type, &emitter.module.types)
|
||||||
|
if !destination_ok || !destination_mutable || !source_ok ||
|
||||||
|
!types.equal(types.resolve_alias(destination_child, &emitter.module.types), types.resolve_alias(source_child, &emitter.module.types)) ||
|
||||||
|
!types.equal(types.resolve_alias(destination_child, &emitter.module.types), types.resolve_alias(instruction.type, &emitter.module.types)) {
|
||||||
|
emit_recovery_value(emitter, instruction_index, instruction, "invalid memcopy region types")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if destination_is_slice {
|
||||||
|
fmt.sbprintf(&emitter.builder, " %%memcopy_dst%d = extractvalue %s %%v%d, 0\n", instruction_index, llvm_type(destination_type, &emitter.module.types), instruction.a)
|
||||||
|
fmt.sbprintf(&emitter.builder, " %%memcopy_dst_len%d = extractvalue %s %%v%d, 1\n", instruction_index, llvm_type(destination_type, &emitter.module.types), instruction.a)
|
||||||
|
} else {
|
||||||
|
fmt.sbprintf(&emitter.builder, " %%memcopy_dst%d = getelementptr %s, ptr %%v%d, i64 0, i64 0\n", instruction_index, llvm_type(destination_array, &emitter.module.types), instruction.a)
|
||||||
|
fmt.sbprintf(&emitter.builder, " %%memcopy_dst_len%d = add i64 0, %d\n", instruction_index, destination_count)
|
||||||
|
}
|
||||||
|
if source_is_slice {
|
||||||
|
fmt.sbprintf(&emitter.builder, " %%memcopy_src%d = extractvalue %s %%v%d, 0\n", instruction_index, llvm_type(source_type, &emitter.module.types), instruction.b)
|
||||||
|
fmt.sbprintf(&emitter.builder, " %%memcopy_src_len%d = extractvalue %s %%v%d, 1\n", instruction_index, llvm_type(source_type, &emitter.module.types), instruction.b)
|
||||||
|
} else {
|
||||||
|
fmt.sbprintf(&emitter.builder, " %%memcopy_src%d = getelementptr %s, ptr %%v%d, i64 0, i64 0\n", instruction_index, llvm_type(source_array, &emitter.module.types), instruction.b)
|
||||||
|
fmt.sbprintf(&emitter.builder, " %%memcopy_src_len%d = add i64 0, %d\n", instruction_index, source_count)
|
||||||
|
}
|
||||||
|
fmt.sbprintf(&emitter.builder, " %%memcopy_len_ok%d = icmp eq i64 %%memcopy_dst_len%d, %%memcopy_src_len%d\n", instruction_index, instruction_index, instruction_index)
|
||||||
|
fmt.sbprintf(&emitter.builder, " br i1 %%memcopy_len_ok%d, label %%memcopy_size_check%d, label %%memcopy_len_trap%d\nmemcopy_len_trap%d:\n", instruction_index, instruction_index, instruction_index, instruction_index)
|
||||||
|
message := diagnostic_message(emitter, source.INVALID_DIAGNOSTIC, instruction.span, "memcopy! source and destination lengths differ")
|
||||||
|
emit_trap_call(emitter, message)
|
||||||
|
fmt.sbprintf(&emitter.builder, " unreachable\nmemcopy_size_check%d:\n", instruction_index)
|
||||||
|
element_size := types.size(instruction.type, &emitter.module.types, emitter.module.target)
|
||||||
|
if element_size == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
max_count := u64(0xffff_ffff_ffff_ffff)/element_size
|
||||||
|
fmt.sbprintf(&emitter.builder, " %%memcopy_size_ok%d = icmp ule i64 %%memcopy_dst_len%d, %d\n", instruction_index, instruction_index, max_count)
|
||||||
|
fmt.sbprintf(&emitter.builder, " br i1 %%memcopy_size_ok%d, label %%memcopy_overlap_check%d, label %%memcopy_size_trap%d\nmemcopy_size_trap%d:\n", instruction_index, instruction_index, instruction_index, instruction_index)
|
||||||
|
message = diagnostic_message(emitter, source.INVALID_DIAGNOSTIC, instruction.span, "memory operation size overflow")
|
||||||
|
emit_trap_call(emitter, message)
|
||||||
|
fmt.sbprintf(&emitter.builder, " unreachable\nmemcopy_overlap_check%d:\n", instruction_index)
|
||||||
|
fmt.sbprintf(&emitter.builder, " %%memcopy_bytes%d = mul i64 %%memcopy_dst_len%d, %d\n", instruction_index, instruction_index, element_size)
|
||||||
|
fmt.sbprintf(&emitter.builder, " %%memcopy_dst_end%d = getelementptr i8, ptr %%memcopy_dst%d, i64 %%memcopy_bytes%d\n", instruction_index, instruction_index, instruction_index)
|
||||||
|
fmt.sbprintf(&emitter.builder, " %%memcopy_src_end%d = getelementptr i8, ptr %%memcopy_src%d, i64 %%memcopy_bytes%d\n", instruction_index, instruction_index, instruction_index)
|
||||||
|
fmt.sbprintf(&emitter.builder, " %%memcopy_before%d = icmp ule ptr %%memcopy_dst_end%d, %%memcopy_src%d\n", instruction_index, instruction_index, instruction_index)
|
||||||
|
fmt.sbprintf(&emitter.builder, " %%memcopy_after%d = icmp ule ptr %%memcopy_src_end%d, %%memcopy_dst%d\n", instruction_index, instruction_index, instruction_index)
|
||||||
|
fmt.sbprintf(&emitter.builder, " %%memcopy_disjoint%d = or i1 %%memcopy_before%d, %%memcopy_after%d\n", instruction_index, instruction_index, instruction_index)
|
||||||
|
fmt.sbprintf(&emitter.builder, " %%memcopy_empty%d = icmp eq i64 %%memcopy_bytes%d, 0\n", instruction_index, instruction_index)
|
||||||
|
fmt.sbprintf(&emitter.builder, " %%memcopy_ok%d = or i1 %%memcopy_empty%d, %%memcopy_disjoint%d\n", instruction_index, instruction_index, instruction_index)
|
||||||
|
fmt.sbprintf(&emitter.builder, " br i1 %%memcopy_ok%d, label %%memcopy_continue%d, label %%memcopy_overlap_trap%d\nmemcopy_overlap_trap%d:\n", instruction_index, instruction_index, instruction_index, instruction_index)
|
||||||
|
message = diagnostic_message(emitter, source.INVALID_DIAGNOSTIC, instruction.span, "memcopy! source and destination overlap")
|
||||||
|
emit_trap_call(emitter, message)
|
||||||
|
fmt.sbprintf(&emitter.builder, " unreachable\nmemcopy_continue%d:\n", instruction_index)
|
||||||
|
fmt.sbprintf(&emitter.builder, " call void @llvm.memcpy.p0.p0.i64(ptr %%memcopy_dst%d, ptr %%memcopy_src%d, i64 %%memcopy_bytes%d, i1 false)\n", instruction_index, instruction_index, instruction_index)
|
||||||
|
case .Mem_Set:
|
||||||
|
if !valid_instruction(instructions, instruction.a) || !valid_value(instructions, instruction.b, instruction.type, &emitter.module.types) {
|
||||||
|
emit_recovery_value(emitter, instruction_index, instruction, "invalid memset operands")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
destination_type := instructions[instruction.a].type
|
||||||
|
destination_child, destination_array, destination_count, destination_mutable, destination_is_slice, destination_ok := memory_region(destination_type, &emitter.module.types)
|
||||||
|
if !destination_ok || !destination_mutable ||
|
||||||
|
!types.equal(types.resolve_alias(destination_child, &emitter.module.types), types.resolve_alias(instruction.type, &emitter.module.types)) {
|
||||||
|
emit_recovery_value(emitter, instruction_index, instruction, "invalid memset destination")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if destination_is_slice {
|
||||||
|
fmt.sbprintf(&emitter.builder, " %%memset_dst%d = extractvalue %s %%v%d, 0\n", instruction_index, llvm_type(destination_type, &emitter.module.types), instruction.a)
|
||||||
|
fmt.sbprintf(&emitter.builder, " %%memset_len%d = extractvalue %s %%v%d, 1\n", instruction_index, llvm_type(destination_type, &emitter.module.types), instruction.a)
|
||||||
|
} else {
|
||||||
|
fmt.sbprintf(&emitter.builder, " %%memset_dst%d = getelementptr %s, ptr %%v%d, i64 0, i64 0\n", instruction_index, llvm_type(destination_array, &emitter.module.types), instruction.a)
|
||||||
|
fmt.sbprintf(&emitter.builder, " %%memset_len%d = add i64 0, %d\n", instruction_index, destination_count)
|
||||||
|
}
|
||||||
|
element_size := types.size(instruction.type, &emitter.module.types, emitter.module.target)
|
||||||
|
if element_size == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
max_count := u64(0xffff_ffff_ffff_ffff)/element_size
|
||||||
|
fmt.sbprintf(&emitter.builder, " %%memset_size_ok%d = icmp ule i64 %%memset_len%d, %d\n", instruction_index, instruction_index, max_count)
|
||||||
|
fmt.sbprintf(&emitter.builder, " br i1 %%memset_size_ok%d, label %%memset_start%d, label %%memset_size_trap%d\nmemset_size_trap%d:\n", instruction_index, instruction_index, instruction_index, instruction_index)
|
||||||
|
message := diagnostic_message(emitter, source.INVALID_DIAGNOSTIC, instruction.span, "memory operation size overflow")
|
||||||
|
emit_trap_call(emitter, message)
|
||||||
|
fmt.sbprintf(&emitter.builder, " unreachable\nmemset_start%d:\n", instruction_index)
|
||||||
|
representation := types.runtime_representation(instruction.type, &emitter.module.types)
|
||||||
|
if types.is_concrete_integer(representation) && types.bits(representation, emitter.module.target) == 8 {
|
||||||
|
fmt.sbprintf(&emitter.builder, " %%memset_bytes%d = mul i64 %%memset_len%d, %d\n", instruction_index, instruction_index, element_size)
|
||||||
|
fmt.sbprintf(&emitter.builder, " call void @llvm.memset.p0.i64(ptr %%memset_dst%d, i8 ", instruction_index)
|
||||||
|
write_operand(&emitter.builder, instructions, instruction.b, instruction.type, &emitter.module.types)
|
||||||
|
fmt.sbprintf(&emitter.builder, ", i64 %%memset_bytes%d, i1 false)\n", instruction_index)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fmt.sbprintf(&emitter.builder, " %%memset_index_slot%d = alloca i64\n store i64 0, ptr %%memset_index_slot%d\n br label %%memset_loop%d\nmemset_loop%d:\n", instruction_index, instruction_index, instruction_index, instruction_index)
|
||||||
|
fmt.sbprintf(&emitter.builder, " %%memset_index%d = load i64, ptr %%memset_index_slot%d\n", instruction_index, instruction_index)
|
||||||
|
fmt.sbprintf(&emitter.builder, " %%memset_more%d = icmp ult i64 %%memset_index%d, %%memset_len%d\n", instruction_index, instruction_index, instruction_index)
|
||||||
|
fmt.sbprintf(&emitter.builder, " br i1 %%memset_more%d, label %%memset_body%d, label %%memset_done%d\nmemset_body%d:\n", instruction_index, instruction_index, instruction_index, instruction_index)
|
||||||
|
fmt.sbprintf(&emitter.builder, " %%memset_element%d = getelementptr %s, ptr %%memset_dst%d, i64 %%memset_index%d\n", instruction_index, llvm_type(instruction.type, &emitter.module.types), instruction_index, instruction_index)
|
||||||
|
fmt.sbprintf(&emitter.builder, " store %s ", llvm_type(instruction.type, &emitter.module.types))
|
||||||
|
write_operand(&emitter.builder, instructions, instruction.b, instruction.type, &emitter.module.types)
|
||||||
|
fmt.sbprintf(&emitter.builder, ", ptr %%memset_element%d\n", instruction_index)
|
||||||
|
fmt.sbprintf(&emitter.builder, " %%memset_next%d = add i64 %%memset_index%d, 1\n store i64 %%memset_next%d, ptr %%memset_index_slot%d\n br label %%memset_loop%d\nmemset_done%d:\n", instruction_index, instruction_index, instruction_index, instruction_index, instruction_index, instruction_index)
|
||||||
case .Slice:
|
case .Slice:
|
||||||
if !valid_instruction(instructions, instruction.a) {
|
if !valid_instruction(instructions, instruction.a) {
|
||||||
emit_recovery_value(emitter, instruction_index, instruction, "invalid slice container")
|
emit_recovery_value(emitter, instruction_index, instruction, "invalid slice container")
|
||||||
@@ -1437,7 +1704,7 @@ emit_instruction_stream :: proc(
|
|||||||
fmt.sbprintf(&emitter.builder, " %%optional_ok%d = extractvalue %s %%v%d, 0\n", instruction_index, llvm_type(optional_type, &emitter.module.types), instruction.a)
|
fmt.sbprintf(&emitter.builder, " %%optional_ok%d = extractvalue %s %%v%d, 0\n", instruction_index, llvm_type(optional_type, &emitter.module.types), instruction.a)
|
||||||
}
|
}
|
||||||
fmt.sbprintf(&emitter.builder, " br i1 %%optional_ok%d, label %%optional_continue%d, label %%optional_trap%d\noptional_trap%d:\n", instruction_index, instruction_index, instruction_index, instruction_index)
|
fmt.sbprintf(&emitter.builder, " br i1 %%optional_ok%d, label %%optional_continue%d, label %%optional_trap%d\noptional_trap%d:\n", instruction_index, instruction_index, instruction_index, instruction_index)
|
||||||
message := diagnostic_message(emitter, source.INVALID_DIAGNOSTIC, instruction.span, "attempted to unwrap none")
|
message := diagnostic_message(emitter, source.INVALID_DIAGNOSTIC, instruction.span, "attempted to unwrap null")
|
||||||
emit_trap_call(emitter, message)
|
emit_trap_call(emitter, message)
|
||||||
fmt.sbprintf(&emitter.builder, " unreachable\noptional_continue%d:\n", instruction_index)
|
fmt.sbprintf(&emitter.builder, " unreachable\noptional_continue%d:\n", instruction_index)
|
||||||
if types.is_pointer(item.child, &emitter.module.types) {
|
if types.is_pointer(item.child, &emitter.module.types) {
|
||||||
@@ -1521,10 +1788,14 @@ emit_instruction_stream :: proc(
|
|||||||
fmt.sbprintf(&emitter.builder, " %%v%d = %s %s ", instruction_index, operation, llvm_type(from_type, &emitter.module.types))
|
fmt.sbprintf(&emitter.builder, " %%v%d = %s %s ", instruction_index, operation, llvm_type(from_type, &emitter.module.types))
|
||||||
write_operand(&emitter.builder, instructions, instruction.a, from_type, &emitter.module.types)
|
write_operand(&emitter.builder, instructions, instruction.a, from_type, &emitter.module.types)
|
||||||
fmt.sbprintf(&emitter.builder, " to %s\n", llvm_type(instruction.type, &emitter.module.types))
|
fmt.sbprintf(&emitter.builder, " to %s\n", llvm_type(instruction.type, &emitter.module.types))
|
||||||
case .Sum_Widen:
|
case .Sum_Widen, .Sum_Project:
|
||||||
if !valid_instruction(instructions, instruction.a) ||
|
project := instruction.op == .Sum_Project
|
||||||
!types.can_sum_widen(instructions[instruction.a].type, instruction.type, &emitter.module.types) {
|
valid_conversion := valid_instruction(instructions, instruction.a) &&
|
||||||
emit_recovery_value(emitter, instruction_index, instruction, "invalid sum widening operand")
|
(types.can_sum_project(instructions[instruction.a].type, instruction.type, &emitter.module.types) if project else
|
||||||
|
types.can_sum_widen(instructions[instruction.a].type, instruction.type, &emitter.module.types))
|
||||||
|
if !valid_conversion {
|
||||||
|
emit_recovery_value(emitter, instruction_index, instruction,
|
||||||
|
"invalid sum projection operand" if project else "invalid sum widening operand")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
from_type := instructions[instruction.a].type
|
from_type := instructions[instruction.a].type
|
||||||
@@ -1572,6 +1843,9 @@ emit_instruction_stream :: proc(
|
|||||||
from_payload_offset := types.union_payload_offset(from_type, &emitter.module.types, emitter.module.target)
|
from_payload_offset := types.union_payload_offset(from_type, &emitter.module.types, emitter.module.target)
|
||||||
to_payload_offset := types.union_payload_offset(instruction.type, &emitter.module.types, emitter.module.target)
|
to_payload_offset := types.union_payload_offset(instruction.type, &emitter.module.types, emitter.module.target)
|
||||||
payload_size := types.sum_payload_size(from_type, &emitter.module.types, emitter.module.target)
|
payload_size := types.sum_payload_size(from_type, &emitter.module.types, emitter.module.target)
|
||||||
|
if project {
|
||||||
|
payload_size = min(payload_size, types.sum_payload_size(instruction.type, &emitter.module.types, emitter.module.target))
|
||||||
|
}
|
||||||
if payload_size > 0 {
|
if payload_size > 0 {
|
||||||
fmt.sbprintf(&emitter.builder, " %%sum_from_payload%d = getelementptr i8, ptr %%sum_from_slot%d, i64 %d\n", instruction_index, instruction_index, from_payload_offset)
|
fmt.sbprintf(&emitter.builder, " %%sum_from_payload%d = getelementptr i8, ptr %%sum_from_slot%d, i64 %d\n", instruction_index, instruction_index, from_payload_offset)
|
||||||
fmt.sbprintf(&emitter.builder, " %%sum_to_payload%d = getelementptr i8, ptr %%sum_to_slot%d, i64 %d\n", instruction_index, instruction_index, to_payload_offset)
|
fmt.sbprintf(&emitter.builder, " %%sum_to_payload%d = getelementptr i8, ptr %%sum_to_slot%d, i64 %d\n", instruction_index, instruction_index, to_payload_offset)
|
||||||
@@ -1580,7 +1854,8 @@ emit_instruction_stream :: proc(
|
|||||||
fmt.sbprintf(&emitter.builder, " %%v%d = load %s, ptr %%sum_to_slot%d\n", instruction_index, to_name, instruction_index)
|
fmt.sbprintf(&emitter.builder, " %%v%d = load %s, ptr %%sum_to_slot%d\n", instruction_index, to_name, instruction_index)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
emit_recovery_value(emitter, instruction_index, instruction, "unsupported sum widening operand")
|
emit_recovery_value(emitter, instruction_index, instruction,
|
||||||
|
"unsupported sum projection operand" if project else "unsupported sum widening operand")
|
||||||
case .C_Coerce:
|
case .C_Coerce:
|
||||||
if !valid_instruction(instructions, instruction.a) ||
|
if !valid_instruction(instructions, instruction.a) ||
|
||||||
!(types.can_coerce_c_integer(instructions[instruction.a].type, instruction.type, emitter.module.target) ||
|
!(types.can_coerce_c_integer(instructions[instruction.a].type, instruction.type, emitter.module.target) ||
|
||||||
@@ -1633,8 +1908,8 @@ emit_instruction_stream :: proc(
|
|||||||
fmt.sbprintf(&emitter.builder, " to %s\n", llvm_type(instruction.type, &emitter.module.types))
|
fmt.sbprintf(&emitter.builder, " to %s\n", llvm_type(instruction.type, &emitter.module.types))
|
||||||
case .Retype:
|
case .Retype:
|
||||||
if !valid_instruction(instructions, instruction.a) ||
|
if !valid_instruction(instructions, instruction.a) ||
|
||||||
!types.can_construct_distinct(instructions[instruction.a].type, instruction.type, &emitter.module.types) {
|
!types.can_retype_distinct(instructions[instruction.a].type, instruction.type, &emitter.module.types) {
|
||||||
emit_recovery_value(emitter, instruction_index, instruction, "invalid distinct type construction")
|
emit_recovery_value(emitter, instruction_index, instruction, "invalid distinct retype")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
type_name := llvm_type(instruction.type, &emitter.module.types)
|
type_name := llvm_type(instruction.type, &emitter.module.types)
|
||||||
@@ -1656,7 +1931,8 @@ emit_instruction_stream :: proc(
|
|||||||
from_repr := types.runtime_representation(from_type, &emitter.module.types)
|
from_repr := types.runtime_representation(from_type, &emitter.module.types)
|
||||||
from_item, from_item_ok := types.node(&emitter.module.types, from_type)
|
from_item, from_item_ok := types.node(&emitter.module.types, from_type)
|
||||||
explicit_enum := from_item_ok && from_item.kind == .Enum && from_item.explicit_backing
|
explicit_enum := from_item_ok && from_item.kind == .Enum && from_item.explicit_backing
|
||||||
valid_from := (types.is_concrete_scalar(from_type) || explicit_enum) &&
|
_, distinct_scalar := types.distinct_scalar_backing(from_type, &emitter.module.types)
|
||||||
|
valid_from := (types.is_concrete_scalar(from_type) || explicit_enum || distinct_scalar) &&
|
||||||
types.is_concrete_scalar(from_repr) && !types.is_bool(from_repr)
|
types.is_concrete_scalar(from_repr) && !types.is_bool(from_repr)
|
||||||
if !valid_from || !types.is_concrete_scalar(instruction.type) || types.is_bool(instruction.type) {
|
if !valid_from || !types.is_concrete_scalar(instruction.type) || types.is_bool(instruction.type) {
|
||||||
emit_recovery_value(emitter, instruction_index, instruction, "invalid scalar cast operand")
|
emit_recovery_value(emitter, instruction_index, instruction, "invalid scalar cast operand")
|
||||||
@@ -1703,6 +1979,16 @@ emit_instruction_stream :: proc(
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
fmt.sbprintf(&emitter.builder, " %%v%d = select i1 true, ptr %%v%d, ptr null\n", instruction_index, instruction.a)
|
fmt.sbprintf(&emitter.builder, " %%v%d = select i1 true, ptr %%v%d, ptr null\n", instruction_index, instruction.a)
|
||||||
|
case .Const_Cast:
|
||||||
|
if !valid_instruction(instructions, instruction.a) ||
|
||||||
|
!types.same_constcast_shape(instructions[instruction.a].type, instruction.type, &emitter.module.types) {
|
||||||
|
emit_recovery_value(emitter, instruction_index, instruction, "invalid const cast operand")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
type_name := llvm_type(instruction.type, &emitter.module.types)
|
||||||
|
fmt.sbprintf(&emitter.builder, " %%v%d = select i1 true, %s ", instruction_index, type_name)
|
||||||
|
write_operand(&emitter.builder, instructions, instruction.a, instructions[instruction.a].type, &emitter.module.types)
|
||||||
|
fmt.sbprintf(&emitter.builder, ", %s zeroinitializer\n", type_name)
|
||||||
case .Weaken_Slice:
|
case .Weaken_Slice:
|
||||||
if !valid_instruction(instructions, instruction.a) ||
|
if !valid_instruction(instructions, instruction.a) ||
|
||||||
!types.can_weaken_slice(instructions[instruction.a].type, instruction.type, &emitter.module.types) {
|
!types.can_weaken_slice(instructions[instruction.a].type, instruction.type, &emitter.module.types) {
|
||||||
@@ -1728,12 +2014,15 @@ emit_instruction_stream :: proc(
|
|||||||
fmt.sbprintf(&emitter.builder, " %%v%d = insertvalue %s %%decay_slice%d, i64 %d, 1\n", instruction_index, type_name, instruction_index, array.count)
|
fmt.sbprintf(&emitter.builder, " %%v%d = insertvalue %s %%decay_slice%d, i64 %d, 1\n", instruction_index, type_name, instruction_index, array.count)
|
||||||
}
|
}
|
||||||
case .Neg_Checked:
|
case .Neg_Checked:
|
||||||
if !valid_value(instructions, instruction.a, instruction.type, &emitter.module.types) {
|
representation := types.runtime_representation(instruction.type, &emitter.module.types)
|
||||||
|
if !valid_value(instructions, instruction.a, instruction.type, &emitter.module.types) ||
|
||||||
|
(!types.is_signed(representation, emitter.module.target) &&
|
||||||
|
!types.is_float(representation, emitter.module.target)) {
|
||||||
emit_recovery_value(emitter, instruction_index, instruction, "invalid negation operand")
|
emit_recovery_value(emitter, instruction_index, instruction, "invalid negation operand")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
type_name := llvm_type(instruction.type, &emitter.module.types)
|
type_name := llvm_type(instruction.type, &emitter.module.types)
|
||||||
if types.is_float(instruction.type, emitter.module.target) {
|
if types.is_float(representation, emitter.module.target) {
|
||||||
fmt.sbprintf(&emitter.builder, " %%v%d = fneg %s ", instruction_index, type_name)
|
fmt.sbprintf(&emitter.builder, " %%v%d = fneg %s ", instruction_index, type_name)
|
||||||
write_operand(&emitter.builder, instructions, instruction.a, instruction.type, &emitter.module.types)
|
write_operand(&emitter.builder, instructions, instruction.a, instruction.type, &emitter.module.types)
|
||||||
strings.write_string(&emitter.builder, "\n")
|
strings.write_string(&emitter.builder, "\n")
|
||||||
@@ -1783,19 +2072,21 @@ emit_instruction_stream :: proc(
|
|||||||
}
|
}
|
||||||
emit_checked_arithmetic(emitter, instructions, instruction_index, instruction, "mul", "fmul", "integer multiplication overflow")
|
emit_checked_arithmetic(emitter, instructions, instruction_index, instruction, "mul", "fmul", "integer multiplication overflow")
|
||||||
case .Div_Checked:
|
case .Div_Checked:
|
||||||
|
representation := types.runtime_representation(instruction.type, &emitter.module.types)
|
||||||
if !valid_value(instructions, instruction.a, instruction.type, &emitter.module.types) ||
|
if !valid_value(instructions, instruction.a, instruction.type, &emitter.module.types) ||
|
||||||
!valid_value(instructions, instruction.b, instruction.type, &emitter.module.types) ||
|
!valid_value(instructions, instruction.b, instruction.type, &emitter.module.types) ||
|
||||||
!types.is_float(instruction.type, emitter.module.target) {
|
!types.is_float(representation, emitter.module.target) {
|
||||||
emit_recovery_value(emitter, instruction_index, instruction, "invalid division operand")
|
emit_recovery_value(emitter, instruction_index, instruction, "invalid division operand")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
emit_checked_arithmetic(emitter, instructions, instruction_index, instruction, "div", "fdiv", "")
|
emit_checked_arithmetic(emitter, instructions, instruction_index, instruction, "div", "fdiv", "")
|
||||||
case .Div_Trunc_Checked, .Div_Floor_Checked, .Div_Exact_Checked, .Div_Ceil_Checked,
|
case .Div_Trunc_Checked, .Div_Floor_Checked, .Div_Exact_Checked, .Div_Ceil_Checked,
|
||||||
.Rem_Checked, .Mod_Checked:
|
.Rem_Checked, .Mod_Checked:
|
||||||
|
representation := types.runtime_representation(instruction.type, &emitter.module.types)
|
||||||
if !valid_value(instructions, instruction.a, instruction.type, &emitter.module.types) ||
|
if !valid_value(instructions, instruction.a, instruction.type, &emitter.module.types) ||
|
||||||
!valid_value(instructions, instruction.b, instruction.type, &emitter.module.types) ||
|
!valid_value(instructions, instruction.b, instruction.type, &emitter.module.types) ||
|
||||||
(!types.is_concrete_integer(instruction.type) &&
|
(!types.is_concrete_integer(representation) &&
|
||||||
!types.is_float(instruction.type, emitter.module.target)) {
|
!types.is_float(representation, emitter.module.target)) {
|
||||||
emit_recovery_value(emitter, instruction_index, instruction, "invalid division builtin operands")
|
emit_recovery_value(emitter, instruction_index, instruction, "invalid division builtin operands")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -1816,6 +2107,38 @@ emit_instruction_stream :: proc(
|
|||||||
fmt.sbprintf(&emitter.builder, " %%v%d = getelementptr %s, ptr %%v%d, i64 ", instruction_index, llvm_type(result_item.child, &emitter.module.types), instruction.a)
|
fmt.sbprintf(&emitter.builder, " %%v%d = getelementptr %s, ptr %%v%d, i64 ", instruction_index, llvm_type(result_item.child, &emitter.module.types), instruction.a)
|
||||||
write_operand(&emitter.builder, instructions, instruction.b, types.USIZE, &emitter.module.types)
|
write_operand(&emitter.builder, instructions, instruction.b, types.USIZE, &emitter.module.types)
|
||||||
strings.write_string(&emitter.builder, "\n")
|
strings.write_string(&emitter.builder, "\n")
|
||||||
|
case .Bit_Not:
|
||||||
|
representation := types.runtime_representation(instruction.type, &emitter.module.types)
|
||||||
|
if !types.is_concrete_integer(representation) ||
|
||||||
|
!valid_value(instructions, instruction.a, instruction.type, &emitter.module.types) {
|
||||||
|
emit_recovery_value(emitter, instruction_index, instruction, "invalid bitwise complement operand")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
type_name := llvm_type(instruction.type, &emitter.module.types)
|
||||||
|
fmt.sbprintf(&emitter.builder, " %%v%d = xor %s ", instruction_index, type_name)
|
||||||
|
write_operand(&emitter.builder, instructions, instruction.a, instruction.type, &emitter.module.types)
|
||||||
|
strings.write_string(&emitter.builder, ", -1\n")
|
||||||
|
case .Bit_And, .Bit_Or, .Bit_Xor:
|
||||||
|
representation := types.runtime_representation(instruction.type, &emitter.module.types)
|
||||||
|
if !types.is_concrete_integer(representation) ||
|
||||||
|
!valid_value(instructions, instruction.a, instruction.type, &emitter.module.types) ||
|
||||||
|
!valid_value(instructions, instruction.b, instruction.type, &emitter.module.types) {
|
||||||
|
emit_recovery_value(emitter, instruction_index, instruction, "invalid bitwise operands")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
operation := "and"
|
||||||
|
#partial switch instruction.op {
|
||||||
|
case .Bit_Or: operation = "or"
|
||||||
|
case .Bit_Xor: operation = "xor"
|
||||||
|
}
|
||||||
|
type_name := llvm_type(instruction.type, &emitter.module.types)
|
||||||
|
fmt.sbprintf(&emitter.builder, " %%v%d = %s %s ", instruction_index, operation, type_name)
|
||||||
|
write_operand(&emitter.builder, instructions, instruction.a, instruction.type, &emitter.module.types)
|
||||||
|
strings.write_string(&emitter.builder, ", ")
|
||||||
|
write_operand(&emitter.builder, instructions, instruction.b, instruction.type, &emitter.module.types)
|
||||||
|
strings.write_string(&emitter.builder, "\n")
|
||||||
|
case .Shift_Left, .Shift_Right, .Shift_Left_Saturating:
|
||||||
|
emit_shift(emitter, instructions, instruction_index, instruction)
|
||||||
case .Call:
|
case .Call:
|
||||||
function_id := ir.as_function(instruction.target)
|
function_id := ir.as_function(instruction.target)
|
||||||
if function_id == ir.INVALID_FUNCTION {
|
if function_id == ir.INVALID_FUNCTION {
|
||||||
@@ -1872,7 +2195,7 @@ emit_instruction_stream :: proc(
|
|||||||
strings.write_string(&emitter.builder, " ")
|
strings.write_string(&emitter.builder, " ")
|
||||||
} else if result_abi.kind != .None && result_abi.kind != .Homogeneous_Float {
|
} else if result_abi.kind != .None && result_abi.kind != .Homogeneous_Float {
|
||||||
fmt.sbprintf(&emitter.builder, " %%abi_result%d = ", instruction_index)
|
fmt.sbprintf(&emitter.builder, " %%abi_result%d = ", instruction_index)
|
||||||
} else if !types.is_void(instruction.type) {
|
} else if !types.is_void(instruction.type) && !types.is_noreturn(instruction.type) {
|
||||||
fmt.sbprintf(&emitter.builder, " %%v%d = ", instruction_index)
|
fmt.sbprintf(&emitter.builder, " %%v%d = ", instruction_index)
|
||||||
} else {
|
} else {
|
||||||
strings.write_string(&emitter.builder, " ")
|
strings.write_string(&emitter.builder, " ")
|
||||||
@@ -1888,7 +2211,10 @@ emit_instruction_stream :: proc(
|
|||||||
}
|
}
|
||||||
strings.write_string(&emitter.builder, c_abi_result_type(function_item.child, &emitter.module.types))
|
strings.write_string(&emitter.builder, c_abi_result_type(function_item.child, &emitter.module.types))
|
||||||
} else {
|
} else {
|
||||||
strings.write_string(&emitter.builder, llvm_type(function_item.child, &emitter.module.types))
|
strings.write_string(
|
||||||
|
&emitter.builder,
|
||||||
|
"void" if types.is_noreturn(function_item.child) else llvm_type(function_item.child, &emitter.module.types),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
if function_item.variadic {
|
if function_item.variadic {
|
||||||
strings.write_string(&emitter.builder, " (")
|
strings.write_string(&emitter.builder, " (")
|
||||||
@@ -1938,6 +2264,10 @@ emit_instruction_stream :: proc(
|
|||||||
wrote_arg = true
|
wrote_arg = true
|
||||||
}
|
}
|
||||||
strings.write_string(&emitter.builder, ")\n")
|
strings.write_string(&emitter.builder, ")\n")
|
||||||
|
if types.is_noreturn(instruction.type) {
|
||||||
|
strings.write_string(&emitter.builder, " unreachable\n")
|
||||||
|
after_terminator = true
|
||||||
|
}
|
||||||
if result_abi.kind == .Indirect {
|
if result_abi.kind == .Indirect {
|
||||||
fmt.sbprintf(&emitter.builder, " %%v%d = load %s, ptr %%abi_result_slot%d\n", instruction_index, llvm_type(function_item.child, &emitter.module.types), instruction_index)
|
fmt.sbprintf(&emitter.builder, " %%v%d = load %s, ptr %%abi_result_slot%d\n", instruction_index, llvm_type(function_item.child, &emitter.module.types), instruction_index)
|
||||||
} else if result_abi.kind != .None && result_abi.kind != .Homogeneous_Float {
|
} else if result_abi.kind != .None && result_abi.kind != .Homogeneous_Float {
|
||||||
@@ -1999,7 +2329,7 @@ emit_instruction_stream :: proc(
|
|||||||
strings.write_string(&emitter.builder, " ")
|
strings.write_string(&emitter.builder, " ")
|
||||||
} else if result_abi.kind != .None && result_abi.kind != .Homogeneous_Float {
|
} else if result_abi.kind != .None && result_abi.kind != .Homogeneous_Float {
|
||||||
fmt.sbprintf(&emitter.builder, " %%abi_result%d = ", instruction_index)
|
fmt.sbprintf(&emitter.builder, " %%abi_result%d = ", instruction_index)
|
||||||
} else if !types.is_void(instruction.type) {
|
} else if !types.is_void(instruction.type) && !types.is_noreturn(instruction.type) {
|
||||||
fmt.sbprintf(&emitter.builder, " %%v%d = ", instruction_index)
|
fmt.sbprintf(&emitter.builder, " %%v%d = ", instruction_index)
|
||||||
} else {
|
} else {
|
||||||
strings.write_string(&emitter.builder, " ")
|
strings.write_string(&emitter.builder, " ")
|
||||||
@@ -2055,6 +2385,10 @@ emit_instruction_stream :: proc(
|
|||||||
wrote_arg = true
|
wrote_arg = true
|
||||||
}
|
}
|
||||||
strings.write_string(&emitter.builder, ")\n")
|
strings.write_string(&emitter.builder, ")\n")
|
||||||
|
if types.is_noreturn(instruction.type) {
|
||||||
|
strings.write_string(&emitter.builder, " unreachable\n")
|
||||||
|
after_terminator = true
|
||||||
|
}
|
||||||
if result_abi.kind == .Indirect {
|
if result_abi.kind == .Indirect {
|
||||||
fmt.sbprintf(&emitter.builder, " %%v%d = load %s, ptr %%abi_result_slot%d\n", instruction_index, llvm_type(target.result, &emitter.module.types), instruction_index)
|
fmt.sbprintf(&emitter.builder, " %%v%d = load %s, ptr %%abi_result_slot%d\n", instruction_index, llvm_type(target.result, &emitter.module.types), instruction_index)
|
||||||
} else if result_abi.kind != .None && result_abi.kind != .Homogeneous_Float {
|
} else if result_abi.kind != .None && result_abi.kind != .Homogeneous_Float {
|
||||||
@@ -2081,10 +2415,11 @@ emit_instruction_stream :: proc(
|
|||||||
}
|
}
|
||||||
predicate := ir.Compare_Predicate(instruction.integer)
|
predicate := ir.Compare_Predicate(instruction.integer)
|
||||||
type_name := llvm_type(operand_type, &emitter.module.types)
|
type_name := llvm_type(operand_type, &emitter.module.types)
|
||||||
if types.is_float(operand_type, emitter.module.target) {
|
representation := types.runtime_representation(operand_type, &emitter.module.types)
|
||||||
|
if types.is_float(representation, emitter.module.target) {
|
||||||
fmt.sbprintf(&emitter.builder, " %%v%d = fcmp %s %s ", instruction_index, float_predicate(predicate), type_name)
|
fmt.sbprintf(&emitter.builder, " %%v%d = fcmp %s %s ", instruction_index, float_predicate(predicate), type_name)
|
||||||
} else {
|
} else {
|
||||||
fmt.sbprintf(&emitter.builder, " %%v%d = icmp %s %s ", instruction_index, integer_predicate(predicate, types.is_signed(operand_type, emitter.module.target)), type_name)
|
fmt.sbprintf(&emitter.builder, " %%v%d = icmp %s %s ", instruction_index, integer_predicate(predicate, types.is_signed(representation, emitter.module.target)), type_name)
|
||||||
}
|
}
|
||||||
write_operand(&emitter.builder, instructions, instruction.a, operand_type, &emitter.module.types)
|
write_operand(&emitter.builder, instructions, instruction.a, operand_type, &emitter.module.types)
|
||||||
strings.write_string(&emitter.builder, ", ")
|
strings.write_string(&emitter.builder, ", ")
|
||||||
@@ -2106,8 +2441,11 @@ emit_instruction_stream :: proc(
|
|||||||
fmt.sbprintf(&emitter.builder, ", label %%bro_block_%d, label %%bro_block_%d\n", instruction.integer, u32(instruction.target))
|
fmt.sbprintf(&emitter.builder, ", label %%bro_block_%d, label %%bro_block_%d\n", instruction.integer, u32(instruction.target))
|
||||||
after_terminator = true
|
after_terminator = true
|
||||||
case .Trap:
|
case .Trap:
|
||||||
message := diagnostic_message(emitter, instruction.diagnostic, instruction.span, "invalid recovered source")
|
fallback := "reached unreachable code" if instruction.integer != 0 else "invalid recovered source"
|
||||||
|
message := diagnostic_message(emitter, instruction.diagnostic, instruction.span, fallback)
|
||||||
emit_trap_call(emitter, message)
|
emit_trap_call(emitter, message)
|
||||||
|
strings.write_string(&emitter.builder, " unreachable\n")
|
||||||
|
after_terminator = true
|
||||||
case .Return:
|
case .Return:
|
||||||
if global_initializer {
|
if global_initializer {
|
||||||
return_value = instruction.a
|
return_value = instruction.a
|
||||||
@@ -2270,12 +2608,30 @@ emit_types :: proc(emitter: ^Emitter) {
|
|||||||
strings.write_string(&emitter.builder, " }\n")
|
strings.write_string(&emitter.builder, " }\n")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
fields := types.fields_for(&emitter.module.types, id)
|
||||||
strings.write_string(&emitter.builder, "{ ")
|
strings.write_string(&emitter.builder, "{ ")
|
||||||
for field, field_index in types.fields_for(&emitter.module.types, id) {
|
previous_index := -1
|
||||||
if field_index > 0 {
|
for physical_index in 0..<len(fields) {
|
||||||
|
logical_index := -1
|
||||||
|
for _, candidate_index in fields {
|
||||||
|
if previous_index >= 0 &&
|
||||||
|
!types.field_layout_precedes(
|
||||||
|
&emitter.module.types, id, previous_index, candidate_index, emitter.module.target,
|
||||||
|
) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if logical_index < 0 ||
|
||||||
|
types.field_layout_precedes(
|
||||||
|
&emitter.module.types, id, candidate_index, logical_index, emitter.module.target,
|
||||||
|
) {
|
||||||
|
logical_index = candidate_index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if physical_index > 0 {
|
||||||
strings.write_string(&emitter.builder, ", ")
|
strings.write_string(&emitter.builder, ", ")
|
||||||
}
|
}
|
||||||
strings.write_string(&emitter.builder, llvm_type(field.type, &emitter.module.types))
|
strings.write_string(&emitter.builder, llvm_type(fields[logical_index].type, &emitter.module.types))
|
||||||
|
previous_index = logical_index
|
||||||
}
|
}
|
||||||
strings.write_string(&emitter.builder, " }\n")
|
strings.write_string(&emitter.builder, " }\n")
|
||||||
}
|
}
|
||||||
@@ -2353,7 +2709,12 @@ emit_constructor :: proc(emitter: ^Emitter) {
|
|||||||
)
|
)
|
||||||
strings.write_string(&emitter.builder, "define internal void @bro.init() {\nentry:\n")
|
strings.write_string(&emitter.builder, "define internal void @bro.init() {\nentry:\n")
|
||||||
for global, global_id in emitter.module.globals {
|
for global, global_id in emitter.module.globals {
|
||||||
if !global.is_static && !global.external && !global.problematic {
|
if global.eager && !global.is_static && !global.external && !global.problematic {
|
||||||
|
fmt.sbprintf(&emitter.builder, " %%g%d = call %s @bro.get.%d()\n", global_id, llvm_type(global.type, &emitter.module.types), global_id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for global, global_id in emitter.module.globals {
|
||||||
|
if !global.eager && !global.is_static && !global.external && !global.problematic {
|
||||||
fmt.sbprintf(&emitter.builder, " %%g%d = call %s @bro.get.%d()\n", global_id, llvm_type(global.type, &emitter.module.types), global_id)
|
fmt.sbprintf(&emitter.builder, " %%g%d = call %s @bro.get.%d()\n", global_id, llvm_type(global.type, &emitter.module.types), global_id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2432,10 +2793,18 @@ emit_functions :: proc(emitter: ^Emitter) {
|
|||||||
strings.write_string(&emitter.builder, "...")
|
strings.write_string(&emitter.builder, "...")
|
||||||
}
|
}
|
||||||
if function.implementation == .Declaration {
|
if function.implementation == .Declaration {
|
||||||
strings.write_string(&emitter.builder, ")\n\n")
|
strings.write_string(&emitter.builder, ")")
|
||||||
|
if types.is_noreturn(function.result) {
|
||||||
|
strings.write_string(&emitter.builder, " noreturn")
|
||||||
|
}
|
||||||
|
strings.write_string(&emitter.builder, "\n\n")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
strings.write_string(&emitter.builder, ") {\nentry:\n")
|
strings.write_string(&emitter.builder, ")")
|
||||||
|
if types.is_noreturn(function.result) {
|
||||||
|
strings.write_string(&emitter.builder, " noreturn")
|
||||||
|
}
|
||||||
|
strings.write_string(&emitter.builder, " {\nentry:\n")
|
||||||
emit_entry_allocas(emitter, function.instructions)
|
emit_entry_allocas(emitter, function.instructions)
|
||||||
if function.calling_convention == .C {
|
if function.calling_convention == .C {
|
||||||
for param_type, index in function.param_types {
|
for param_type, index in function.param_types {
|
||||||
@@ -2486,6 +2855,8 @@ emit_declarations :: proc(emitter: ^Emitter) {
|
|||||||
strings.write_string(&emitter.builder, ".with.overflow.i")
|
strings.write_string(&emitter.builder, ".with.overflow.i")
|
||||||
fmt.sbprintf(&emitter.builder, "%d(i%d, i%d)\n", bits, bits, bits)
|
fmt.sbprintf(&emitter.builder, "%d(i%d, i%d)\n", bits, bits, bits)
|
||||||
}
|
}
|
||||||
|
fmt.sbprintf(&emitter.builder, "declare i%d @llvm.sshl.sat.i%d(i%d, i%d)\n", bits, bits, bits, bits)
|
||||||
|
fmt.sbprintf(&emitter.builder, "declare i%d @llvm.ushl.sat.i%d(i%d, i%d)\n", bits, bits, bits, bits)
|
||||||
}
|
}
|
||||||
strings.write_string(
|
strings.write_string(
|
||||||
&emitter.builder,
|
&emitter.builder,
|
||||||
|
|||||||
+336
-45
@@ -26,6 +26,7 @@ State :: struct {
|
|||||||
c_options: cimport.Options,
|
c_options: cimport.Options,
|
||||||
selected: target.Target,
|
selected: target.Target,
|
||||||
project_root: string,
|
project_root: string,
|
||||||
|
mode: ast.Compile_Mode,
|
||||||
record_identities: [dynamic]string,
|
record_identities: [dynamic]string,
|
||||||
record_types: [dynamic]types.Type,
|
record_types: [dynamic]types.Type,
|
||||||
root_failed: bool,
|
root_failed: bool,
|
||||||
@@ -436,7 +437,7 @@ add_macro_zero_expr :: proc(
|
|||||||
return ast.INVALID_EXPR, false
|
return ast.INVALID_EXPR, false
|
||||||
}
|
}
|
||||||
return add_import_expr(state, ast.Expr{
|
return add_import_expr(state, ast.Expr{
|
||||||
kind=.None, span=span,
|
kind=.Null, span=span,
|
||||||
left=ast.INVALID_EXPR, right=ast.INVALID_EXPR,
|
left=ast.INVALID_EXPR, right=ast.INVALID_EXPR,
|
||||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
}), true
|
}), true
|
||||||
@@ -679,7 +680,7 @@ add_converted_macro_value_expr :: proc(
|
|||||||
}, span), true
|
}, span), true
|
||||||
case .Null:
|
case .Null:
|
||||||
return add_import_expr(state, ast.Expr{
|
return add_import_expr(state, ast.Expr{
|
||||||
kind=.None, span=span,
|
kind=.Null, span=span,
|
||||||
left=ast.INVALID_EXPR, right=ast.INVALID_EXPR,
|
left=ast.INVALID_EXPR, right=ast.INVALID_EXPR,
|
||||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
}), true
|
}), true
|
||||||
@@ -1130,7 +1131,42 @@ load_header :: proc(state: ^State, path: string, import_span: source.Span) -> as
|
|||||||
return pkg_id
|
return pkg_id
|
||||||
}
|
}
|
||||||
|
|
||||||
load_package :: proc(state: ^State, path: string, import_span: source.Span, is_root := false) -> ast.Package_Id {
|
resolve_package_imports :: proc(state: ^State, pkg_id: ast.Package_Id) {
|
||||||
|
if int(pkg_id) >= len(state.module.packages) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
canonical := state.module.packages[pkg_id].path
|
||||||
|
import_count := len(state.module.imports)
|
||||||
|
for import_id in 0..<import_count {
|
||||||
|
import_item := state.module.imports[import_id]
|
||||||
|
if import_item.pkg != pkg_id || import_item.target != ast.INVALID_PACKAGE ||
|
||||||
|
import_item.test_only && (state.mode != .Test || !state.module.packages[pkg_id].test) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if filepath.is_abs(import_item.path) {
|
||||||
|
state.module.imports[import_id].diagnostic = source.add(state.diagnostics, import_item.span, "absolute import paths are invalid")
|
||||||
|
state.module.imports[import_id].valid = false
|
||||||
|
state.module.imports[import_id].target = add_placeholder(state, import_item.path)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
target_path, target_ok := resolve_import_path(state, canonical, import_item.path)
|
||||||
|
target := load_header(state, target_path, import_item.span) if filepath.ext(import_item.path) == ".h" else
|
||||||
|
load_package(state, target_path, import_item.span, include_tests=import_item.test_only)
|
||||||
|
state.module.imports[import_id].target = target
|
||||||
|
if !target_ok || target == ast.INVALID_PACKAGE || !state.module.packages[target].available {
|
||||||
|
state.module.imports[import_id].valid = false
|
||||||
|
}
|
||||||
|
delete(target_path, state.allocator)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
load_package :: proc(
|
||||||
|
state: ^State,
|
||||||
|
path: string,
|
||||||
|
import_span: source.Span,
|
||||||
|
is_root := false,
|
||||||
|
include_tests := false,
|
||||||
|
) -> ast.Package_Id {
|
||||||
canonical, ok := filepath.abs(path, state.allocator)
|
canonical, ok := filepath.abs(path, state.allocator)
|
||||||
if !ok || !os.is_dir(path) {
|
if !ok || !os.is_dir(path) {
|
||||||
if is_root {
|
if is_root {
|
||||||
@@ -1152,6 +1188,10 @@ load_package :: proc(state: ^State, path: string, import_span: source.Span, is_r
|
|||||||
return id
|
return id
|
||||||
}
|
}
|
||||||
if existing := find_package(state, canonical); existing != ast.INVALID_PACKAGE {
|
if existing := find_package(state, canonical); existing != ast.INVALID_PACKAGE {
|
||||||
|
if include_tests && !state.module.packages[existing].test {
|
||||||
|
state.module.packages[existing].test = true
|
||||||
|
resolve_package_imports(state, existing)
|
||||||
|
}
|
||||||
delete(canonical, state.allocator)
|
delete(canonical, state.allocator)
|
||||||
return existing
|
return existing
|
||||||
}
|
}
|
||||||
@@ -1161,6 +1201,7 @@ load_package :: proc(state: ^State, path: string, import_span: source.Span, is_r
|
|||||||
path=canonical,
|
path=canonical,
|
||||||
name=symbol.intern(state.symbols, filepath.base(canonical)),
|
name=symbol.intern(state.symbols, filepath.base(canonical)),
|
||||||
available=true,
|
available=true,
|
||||||
|
test=state.mode == .Test && is_root || include_tests,
|
||||||
})
|
})
|
||||||
files, files_ok := read_package_files(state, canonical)
|
files, files_ok := read_package_files(state, canonical)
|
||||||
if !files_ok {
|
if !files_ok {
|
||||||
@@ -1204,37 +1245,33 @@ load_package :: proc(state: ^State, path: string, import_span: source.Span, is_r
|
|||||||
}
|
}
|
||||||
os.file_info_slice_delete(files, state.allocator)
|
os.file_info_slice_delete(files, state.allocator)
|
||||||
|
|
||||||
import_count := len(state.module.imports)
|
resolve_package_imports(state, pkg_id)
|
||||||
for import_id in 0..<import_count {
|
|
||||||
import_item := state.module.imports[import_id]
|
|
||||||
if import_item.pkg != pkg_id || import_item.target != ast.INVALID_PACKAGE {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if filepath.is_abs(import_item.path) {
|
|
||||||
state.module.imports[import_id].diagnostic = source.add(state.diagnostics, import_item.span, "absolute import paths are invalid")
|
|
||||||
state.module.imports[import_id].valid = false
|
|
||||||
state.module.imports[import_id].target = add_placeholder(state, import_item.path)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
target_path, target_ok := resolve_import_path(state, canonical, import_item.path)
|
|
||||||
target := load_header(state, target_path, import_item.span) if filepath.ext(import_item.path) == ".h" else load_package(state, target_path, import_item.span)
|
|
||||||
state.module.imports[import_id].target = target
|
|
||||||
if !target_ok || target == ast.INVALID_PACKAGE || !state.module.packages[target].available {
|
|
||||||
state.module.imports[import_id].valid = false
|
|
||||||
}
|
|
||||||
delete(target_path, state.allocator)
|
|
||||||
}
|
|
||||||
return pkg_id
|
return pkg_id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declaration_scopes_overlap :: proc(
|
||||||
|
left_visibility: types.Visibility,
|
||||||
|
left_file: ast.File_Id,
|
||||||
|
right_visibility: types.Visibility,
|
||||||
|
right_file: ast.File_Id,
|
||||||
|
) -> bool {
|
||||||
|
return left_visibility != .File || right_visibility != .File || left_file == right_file
|
||||||
|
}
|
||||||
|
|
||||||
|
declaration_visible_in_file :: proc(visibility: types.Visibility, declaration_file, file: ast.File_Id) -> bool {
|
||||||
|
return visibility != .File || declaration_file == file
|
||||||
|
}
|
||||||
|
|
||||||
declaration_conflicts :: proc(module: ^ast.Module, pkg: ast.Package_Id, file: ast.File_Id, name: symbol.Id) -> bool {
|
declaration_conflicts :: proc(module: ^ast.Module, pkg: ast.Package_Id, file: ast.File_Id, name: symbol.Id) -> bool {
|
||||||
for function in module.functions {
|
for function in module.functions {
|
||||||
if function.pkg == pkg && function.name == name && (!function.file_hidden || function.file == file) {
|
if function.pkg == pkg && function.name == name &&
|
||||||
|
declaration_visible_in_file(function.visibility, function.file, file) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for global in module.globals {
|
for global in module.globals {
|
||||||
if global.pkg == pkg && global.name == name && (!global.file_hidden || global.file == file) {
|
if global.pkg == pkg && global.name == name &&
|
||||||
|
declaration_visible_in_file(global.visibility, global.file, file) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1248,6 +1285,9 @@ declaration_conflicts :: proc(module: ^ast.Module, pkg: ast.Package_Id, file: as
|
|||||||
|
|
||||||
validate_imports :: proc(state: ^State) {
|
validate_imports :: proc(state: ^State) {
|
||||||
for import_item, import_id in state.module.imports {
|
for import_item, import_id in state.module.imports {
|
||||||
|
if import_item.test_only {
|
||||||
|
continue
|
||||||
|
}
|
||||||
if !symbol.is_valid(import_item.alias) && import_item.target != ast.INVALID_PACKAGE {
|
if !symbol.is_valid(import_item.alias) && import_item.target != ast.INVALID_PACKAGE {
|
||||||
state.module.imports[import_id].alias = state.module.packages[import_item.target].name
|
state.module.imports[import_id].alias = state.module.packages[import_item.target].name
|
||||||
}
|
}
|
||||||
@@ -1287,33 +1327,161 @@ validate_imports :: proc(state: ^State) {
|
|||||||
|
|
||||||
find_type_import :: proc(module: ^ast.Module, file: ast.File_Id, alias: symbol.Id) -> ast.Import_Id {
|
find_type_import :: proc(module: ^ast.Module, file: ast.File_Id, alias: symbol.Id) -> ast.Import_Id {
|
||||||
for import_item, index in module.imports {
|
for import_item, index in module.imports {
|
||||||
if import_item.file == file && import_item.alias == alias {
|
if !import_item.test_only && import_item.file == file && import_item.alias == alias {
|
||||||
return ast.import_id(index)
|
return ast.import_id(index)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ast.INVALID_IMPORT
|
return ast.INVALID_IMPORT
|
||||||
}
|
}
|
||||||
|
|
||||||
alias_declarations_conflict :: proc(left_file: ast.File_Id, left_hidden: bool, right_file: ast.File_Id, right_hidden: bool) -> bool {
|
find_visible_enum_global :: proc(
|
||||||
return left_file == right_file if left_hidden && right_hidden else true
|
module: ^ast.Module,
|
||||||
|
pkg: ast.Package_Id,
|
||||||
|
file: ast.File_Id,
|
||||||
|
name: symbol.Id,
|
||||||
|
public_only := false,
|
||||||
|
) -> ast.Global_Id {
|
||||||
|
for global, index in module.globals {
|
||||||
|
if global.pkg == pkg && global.name == name &&
|
||||||
|
((public_only && global.visibility == .Public) ||
|
||||||
|
(!public_only && declaration_visible_in_file(global.visibility, global.file, file))) {
|
||||||
|
return ast.global_id(index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ast.INVALID_GLOBAL
|
||||||
}
|
}
|
||||||
|
|
||||||
|
eval_enum_global :: proc(
|
||||||
|
state: ^State,
|
||||||
|
id: ast.Global_Id,
|
||||||
|
visiting: []bool,
|
||||||
|
depth: int,
|
||||||
|
) -> (i128, bool) {
|
||||||
|
index := int(id)
|
||||||
|
if id == ast.INVALID_GLOBAL || index < 0 || index >= len(state.module.globals) ||
|
||||||
|
depth > 64 || visiting[index] {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
global := state.module.globals[index]
|
||||||
|
if !global.immutable || global.external || global.expr == ast.INVALID_EXPR {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
if types.is_valid(global.type) && !types.is_concrete_integer(global.type) {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
visiting[index] = true
|
||||||
|
defer visiting[index] = false
|
||||||
|
return eval_enum_constant(state, global.expr, global.pkg, global.file, visiting, depth+1)
|
||||||
|
}
|
||||||
|
|
||||||
|
eval_enum_constant :: proc(
|
||||||
|
state: ^State,
|
||||||
|
id: ast.Expr_Id,
|
||||||
|
pkg: ast.Package_Id,
|
||||||
|
file: ast.File_Id,
|
||||||
|
visiting: []bool,
|
||||||
|
depth: int,
|
||||||
|
) -> (i128, bool) {
|
||||||
|
index := int(id)
|
||||||
|
if id == ast.INVALID_EXPR || index < 0 || index >= len(state.module.exprs) || depth > 64 {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
expr := state.module.exprs[index]
|
||||||
|
#partial switch expr.kind {
|
||||||
|
case .Integer:
|
||||||
|
return i128(expr.integer), true
|
||||||
|
case .Negate:
|
||||||
|
value, ok := eval_enum_constant(state, expr.left, pkg, file, visiting, depth+1)
|
||||||
|
return -value, ok
|
||||||
|
case .Name:
|
||||||
|
if symbol.is_valid(expr.qualifier) {
|
||||||
|
import_id := find_type_import(state.module, file, expr.qualifier)
|
||||||
|
if import_id == ast.INVALID_IMPORT {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
state.module.imports[import_id].used = true
|
||||||
|
import_item := state.module.imports[import_id]
|
||||||
|
if !import_item.valid || import_item.target == ast.INVALID_PACKAGE ||
|
||||||
|
int(import_item.target) >= len(state.module.packages) ||
|
||||||
|
!state.module.packages[import_item.target].available {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
global := find_visible_enum_global(
|
||||||
|
state.module,
|
||||||
|
import_item.target,
|
||||||
|
ast.INVALID_FILE,
|
||||||
|
expr.name,
|
||||||
|
public_only=true,
|
||||||
|
)
|
||||||
|
return eval_enum_global(state, global, visiting, depth+1)
|
||||||
|
}
|
||||||
|
global := find_visible_enum_global(state.module, pkg, file, expr.name)
|
||||||
|
return eval_enum_global(state, global, visiting, depth+1)
|
||||||
|
}
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve_enum_values :: proc(state: ^State) {
|
||||||
|
visiting := make([]bool, len(state.module.globals), state.allocator)
|
||||||
|
defer delete(visiting, state.allocator)
|
||||||
|
for declaration in state.module.enum_declarations {
|
||||||
|
members := types.enum_members_for(&state.module.type_store, declaration.type)
|
||||||
|
if len(members) != len(declaration.values) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
next_value: i128
|
||||||
|
previous: i128
|
||||||
|
has_previous := false
|
||||||
|
previous_known := true
|
||||||
|
for &member, index in members {
|
||||||
|
spec := declaration.values[index]
|
||||||
|
value := next_value
|
||||||
|
known := true
|
||||||
|
if spec.explicit {
|
||||||
|
if spec.expr == ast.INVALID_EXPR {
|
||||||
|
value = member.value
|
||||||
|
} else if resolved, ok := eval_enum_constant(
|
||||||
|
state, spec.expr, declaration.pkg, declaration.file, visiting, 0,
|
||||||
|
); ok {
|
||||||
|
value = resolved
|
||||||
|
} else {
|
||||||
|
source.add(state.diagnostics, spec.span, "enum value must be an immutable integer constant")
|
||||||
|
known = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if known {
|
||||||
|
member.value = value
|
||||||
|
if has_previous && previous_known && value <= previous {
|
||||||
|
source.add(state.diagnostics, spec.span, "enum values must be strictly increasing")
|
||||||
|
}
|
||||||
|
next_value = value+1
|
||||||
|
} else {
|
||||||
|
next_value = member.value+1
|
||||||
|
}
|
||||||
|
previous = value
|
||||||
|
previous_known = known
|
||||||
|
has_previous = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
alias_conflicts_with_declaration :: proc(module: ^ast.Module, alias: ast.Declaration_Alias) -> bool {
|
alias_conflicts_with_declaration :: proc(module: ^ast.Module, alias: ast.Declaration_Alias) -> bool {
|
||||||
for function in module.functions {
|
for function in module.functions {
|
||||||
if function.pkg == alias.pkg && function.name == alias.name &&
|
if function.pkg == alias.pkg && function.name == alias.name &&
|
||||||
alias_declarations_conflict(alias.file, alias.file_hidden, function.file, function.file_hidden) {
|
declaration_scopes_overlap(function.visibility, function.file, alias.visibility, alias.file) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for global in module.globals {
|
for global in module.globals {
|
||||||
if global.pkg == alias.pkg && global.name == alias.name &&
|
if global.pkg == alias.pkg && global.name == alias.name &&
|
||||||
alias_declarations_conflict(alias.file, alias.file_hidden, global.file, global.file_hidden) {
|
declaration_scopes_overlap(global.visibility, global.file, alias.visibility, alias.file) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for item in module.type_store.nodes {
|
for item in module.type_store.nodes {
|
||||||
if item.declared && item.pkg == u32(alias.pkg) && item.name == u32(alias.name) &&
|
if item.declared && item.pkg == u32(alias.pkg) && item.name == u32(alias.name) &&
|
||||||
alias_declarations_conflict(alias.file, alias.file_hidden, ast.File_Id(item.file), item.file_hidden) {
|
declaration_scopes_overlap(item.visibility, ast.File_Id(item.file), alias.visibility, alias.file) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1325,7 +1493,7 @@ direct_alias_target :: proc(module: ^ast.Module, pkg: ast.Package_Id, name: symb
|
|||||||
target: u32
|
target: u32
|
||||||
kinds := 0
|
kinds := 0
|
||||||
for function, index in module.functions {
|
for function, index in module.functions {
|
||||||
if function.pkg == pkg && function.name == name && !function.generated && !function.file_hidden {
|
if function.pkg == pkg && function.name == name && !function.generated && function.visibility == .Public {
|
||||||
kind = .Function
|
kind = .Function
|
||||||
target = u32(ast.function_id(index))
|
target = u32(ast.function_id(index))
|
||||||
kinds += 1
|
kinds += 1
|
||||||
@@ -1333,7 +1501,7 @@ direct_alias_target :: proc(module: ^ast.Module, pkg: ast.Package_Id, name: symb
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for global, index in module.globals {
|
for global, index in module.globals {
|
||||||
if global.pkg == pkg && global.name == name && !global.file_hidden {
|
if global.pkg == pkg && global.name == name && global.visibility == .Public {
|
||||||
kind = .Global
|
kind = .Global
|
||||||
target = u32(ast.global_id(index))
|
target = u32(ast.global_id(index))
|
||||||
kinds += 1
|
kinds += 1
|
||||||
@@ -1350,24 +1518,24 @@ direct_alias_target :: proc(module: ^ast.Module, pkg: ast.Package_Id, name: symb
|
|||||||
return kind, target, kinds
|
return kind, target, kinds
|
||||||
}
|
}
|
||||||
|
|
||||||
hidden_alias_target_exists :: proc(module: ^ast.Module, pkg: ast.Package_Id, name: symbol.Id) -> bool {
|
non_public_alias_target_exists :: proc(module: ^ast.Module, pkg: ast.Package_Id, name: symbol.Id) -> bool {
|
||||||
for function in module.functions {
|
for function in module.functions {
|
||||||
if function.pkg == pkg && function.name == name && function.file_hidden {
|
if function.pkg == pkg && function.name == name && function.visibility != .Public {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for global in module.globals {
|
for global in module.globals {
|
||||||
if global.pkg == pkg && global.name == name && global.file_hidden {
|
if global.pkg == pkg && global.name == name && global.visibility != .Public {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for item in module.type_store.nodes {
|
for item in module.type_store.nodes {
|
||||||
if item.declared && item.pkg == u32(pkg) && item.name == u32(name) && item.file_hidden {
|
if item.declared && item.pkg == u32(pkg) && item.name == u32(name) && item.visibility != .Public {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for alias in module.aliases {
|
for alias in module.aliases {
|
||||||
if alias.valid && alias.pkg == pkg && alias.name == name && alias.file_hidden {
|
if alias.valid && alias.pkg == pkg && alias.name == name && alias.visibility != .Public {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1376,7 +1544,7 @@ hidden_alias_target_exists :: proc(module: ^ast.Module, pkg: ast.Package_Id, nam
|
|||||||
|
|
||||||
find_public_alias :: proc(module: ^ast.Module, pkg: ast.Package_Id, name: symbol.Id) -> int {
|
find_public_alias :: proc(module: ^ast.Module, pkg: ast.Package_Id, name: symbol.Id) -> int {
|
||||||
for alias, index in module.aliases {
|
for alias, index in module.aliases {
|
||||||
if alias.valid && alias.pkg == pkg && alias.name == name && !alias.file_hidden {
|
if alias.valid && alias.pkg == pkg && alias.name == name && alias.visibility == .Public {
|
||||||
return index
|
return index
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1433,11 +1601,11 @@ resolve_declaration_alias :: proc(state: ^State, index: int, states: []u8) -> bo
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if hidden_alias_target_exists(state.module, alias.target_pkg, alias.member) {
|
if non_public_alias_target_exists(state.module, alias.target_pkg, alias.member) {
|
||||||
alias.diagnostic = source.addf(
|
alias.diagnostic = source.addf(
|
||||||
state.diagnostics,
|
state.diagnostics,
|
||||||
alias.span,
|
alias.span,
|
||||||
"package member '%s.%s' is file-hidden",
|
"package member '%s.%s' is not public",
|
||||||
symbol.resolve(state.symbols, alias.qualifier),
|
symbol.resolve(state.symbols, alias.qualifier),
|
||||||
symbol.resolve(state.symbols, alias.member),
|
symbol.resolve(state.symbols, alias.member),
|
||||||
)
|
)
|
||||||
@@ -1464,7 +1632,7 @@ validate_declaration_aliases :: proc(state: ^State) {
|
|||||||
}
|
}
|
||||||
for previous in state.module.aliases[:index] {
|
for previous in state.module.aliases[:index] {
|
||||||
if previous.pkg == alias.pkg && previous.name == alias.name &&
|
if previous.pkg == alias.pkg && previous.name == alias.name &&
|
||||||
alias_declarations_conflict(alias.file, alias.file_hidden, previous.file, previous.file_hidden) {
|
declaration_scopes_overlap(previous.visibility, previous.file, alias.visibility, alias.file) {
|
||||||
alias.diagnostic = source.addf(state.diagnostics, alias.span, "duplicate declaration alias '%s'", name)
|
alias.diagnostic = source.addf(state.diagnostics, alias.span, "duplicate declaration alias '%s'", name)
|
||||||
alias.valid = false
|
alias.valid = false
|
||||||
break
|
break
|
||||||
@@ -1486,7 +1654,7 @@ validate_declaration_aliases :: proc(state: ^State) {
|
|||||||
|
|
||||||
import_id := find_type_import(state.module, alias.file, alias.qualifier)
|
import_id := find_type_import(state.module, alias.file, alias.qualifier)
|
||||||
if import_id == ast.INVALID_IMPORT {
|
if import_id == ast.INVALID_IMPORT {
|
||||||
alias.diagnostic = source.addf(state.diagnostics, alias.span, "unknown package alias '%s'", symbol.resolve(state.symbols, alias.qualifier))
|
alias.diagnostic = source.addf(state.diagnostics, alias.span, "unknown symbol '%s'", symbol.resolve(state.symbols, alias.qualifier))
|
||||||
alias.valid = false
|
alias.valid = false
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -1514,9 +1682,9 @@ validate_declaration_aliases :: proc(state: ^State) {
|
|||||||
u32(alias.pkg),
|
u32(alias.pkg),
|
||||||
u32(alias.name),
|
u32(alias.name),
|
||||||
file=u32(alias.file),
|
file=u32(alias.file),
|
||||||
file_hidden=alias.file_hidden,
|
visibility=alias.visibility,
|
||||||
)
|
)
|
||||||
if !types.define_alias(&state.module.type_store, id, types.Type(alias.target)) {
|
if !types.define_alias(&state.module.type_store, id, types.Type(alias.target), alias.visibility) {
|
||||||
alias.diagnostic = source.addf(state.diagnostics, alias.span, "duplicate type declaration '%s'", symbol.resolve(state.symbols, alias.name))
|
alias.diagnostic = source.addf(state.diagnostics, alias.span, "duplicate type declaration '%s'", symbol.resolve(state.symbols, alias.name))
|
||||||
alias.valid = false
|
alias.valid = false
|
||||||
}
|
}
|
||||||
@@ -1592,6 +1760,19 @@ canonical_type :: proc(
|
|||||||
mapping[index] = resolved
|
mapping[index] = resolved
|
||||||
return resolved
|
return resolved
|
||||||
}
|
}
|
||||||
|
if item.kind == .Sum {
|
||||||
|
left := canonical_type(module, item.child, mapping, visiting)
|
||||||
|
right := canonical_type(module, item.extra, mapping, visiting)
|
||||||
|
if resolved, compose_error := types.compose_sum(&module.type_store, left, right); compose_error == .None {
|
||||||
|
mapping[index] = resolved
|
||||||
|
return resolved
|
||||||
|
}
|
||||||
|
item.child = left
|
||||||
|
item.extra = right
|
||||||
|
resolved := types.intern(&module.type_store, item)
|
||||||
|
mapping[index] = resolved
|
||||||
|
return resolved
|
||||||
|
}
|
||||||
if item.kind == .Function {
|
if item.kind == .Function {
|
||||||
params := make([]types.Type, int(item.field_count), context.temp_allocator)
|
params := make([]types.Type, int(item.field_count), context.temp_allocator)
|
||||||
for param, param_index in types.params_for(&module.type_store, value) {
|
for param, param_index in types.params_for(&module.type_store, value) {
|
||||||
@@ -1613,6 +1794,81 @@ canonical_type :: proc(
|
|||||||
return resolved
|
return resolved
|
||||||
}
|
}
|
||||||
|
|
||||||
|
diagnose_qualified_type_uses :: proc(state: ^State) {
|
||||||
|
module := state.module
|
||||||
|
for &type_use in module.type_uses {
|
||||||
|
item, ok := types.node(&module.type_store, type_use.type)
|
||||||
|
if !ok || item.qualifier == 0 || item.kind != .Named {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
qualifier := symbol.Id(item.qualifier)
|
||||||
|
name := symbol.Id(item.name)
|
||||||
|
import_id := find_type_import(module, type_use.file, qualifier)
|
||||||
|
if import_id == ast.INVALID_IMPORT {
|
||||||
|
type_use.diagnostic = source.addf(
|
||||||
|
state.diagnostics,
|
||||||
|
type_use.span,
|
||||||
|
"unknown symbol '%s'",
|
||||||
|
symbol.resolve(state.symbols, qualifier),
|
||||||
|
)
|
||||||
|
source.set_primary_label(state.diagnostics, type_use.diagnostic, "unknown symbol")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
import_item := module.imports[import_id]
|
||||||
|
if !import_item.valid || import_item.target == ast.INVALID_PACKAGE ||
|
||||||
|
int(import_item.target) >= len(module.packages) || !module.packages[import_item.target].available {
|
||||||
|
type_use.diagnostic = source.addf(
|
||||||
|
state.diagnostics,
|
||||||
|
type_use.span,
|
||||||
|
"unavailable imported package '%s'",
|
||||||
|
symbol.resolve(state.symbols, qualifier),
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !types.is_valid(types.find_named(&module.type_store, u32(import_item.target), u32(name))) {
|
||||||
|
type_use.diagnostic = source.addf(
|
||||||
|
state.diagnostics,
|
||||||
|
type_use.span,
|
||||||
|
"package '%s' has no member '%s'",
|
||||||
|
symbol.resolve(state.symbols, qualifier),
|
||||||
|
symbol.resolve(state.symbols, name),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type_resolution_diagnostic :: proc(module: ^ast.Module, value: types.Type, file: ast.File_Id, depth := 0) -> source.Diagnostic_Id {
|
||||||
|
if depth > 64 {
|
||||||
|
return source.INVALID_DIAGNOSTIC
|
||||||
|
}
|
||||||
|
for type_use in module.type_uses {
|
||||||
|
if type_use.file == file && type_use.type == value && type_use.diagnostic != source.INVALID_DIAGNOSTIC {
|
||||||
|
return type_use.diagnostic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
item, ok := types.node(&module.type_store, value)
|
||||||
|
if !ok {
|
||||||
|
return source.INVALID_DIAGNOSTIC
|
||||||
|
}
|
||||||
|
if diagnostic := type_resolution_diagnostic(module, item.child, file, depth+1);
|
||||||
|
diagnostic != source.INVALID_DIAGNOSTIC {
|
||||||
|
return diagnostic
|
||||||
|
}
|
||||||
|
if diagnostic := type_resolution_diagnostic(module, item.extra, file, depth+1);
|
||||||
|
diagnostic != source.INVALID_DIAGNOSTIC {
|
||||||
|
return diagnostic
|
||||||
|
}
|
||||||
|
if item.kind == .Function {
|
||||||
|
for param in types.params_for(&module.type_store, value) {
|
||||||
|
if diagnostic := type_resolution_diagnostic(module, param.type, file, depth+1);
|
||||||
|
diagnostic != source.INVALID_DIAGNOSTIC {
|
||||||
|
return diagnostic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return source.INVALID_DIAGNOSTIC
|
||||||
|
}
|
||||||
|
|
||||||
canonicalize_types :: proc(module: ^ast.Module, allocator: mem.Allocator) {
|
canonicalize_types :: proc(module: ^ast.Module, allocator: mem.Allocator) {
|
||||||
original_count := len(module.type_store.nodes)
|
original_count := len(module.type_store.nodes)
|
||||||
mapping := make([]types.Type, original_count, allocator)
|
mapping := make([]types.Type, original_count, allocator)
|
||||||
@@ -1620,6 +1876,19 @@ canonicalize_types :: proc(module: ^ast.Module, allocator: mem.Allocator) {
|
|||||||
defer delete(mapping, allocator)
|
defer delete(mapping, allocator)
|
||||||
defer delete(visiting, allocator)
|
defer delete(visiting, allocator)
|
||||||
for &function in module.functions {
|
for &function in module.functions {
|
||||||
|
for param in function.params {
|
||||||
|
if diagnostic := type_resolution_diagnostic(module, param.type, function.file);
|
||||||
|
diagnostic != source.INVALID_DIAGNOSTIC && function.diagnostic == source.INVALID_DIAGNOSTIC {
|
||||||
|
function.diagnostic = diagnostic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
signature_results := [2]types.Type{function.result, function.error}
|
||||||
|
for value in signature_results {
|
||||||
|
if diagnostic := type_resolution_diagnostic(module, value, function.file);
|
||||||
|
diagnostic != source.INVALID_DIAGNOSTIC && function.diagnostic == source.INVALID_DIAGNOSTIC {
|
||||||
|
function.diagnostic = diagnostic
|
||||||
|
}
|
||||||
|
}
|
||||||
for ¶m in function.params {
|
for ¶m in function.params {
|
||||||
param.type = canonical_type(module, param.type, mapping, visiting)
|
param.type = canonical_type(module, param.type, mapping, visiting)
|
||||||
}
|
}
|
||||||
@@ -1627,14 +1896,25 @@ canonicalize_types :: proc(module: ^ast.Module, allocator: mem.Allocator) {
|
|||||||
function.error = canonical_type(module, function.error, mapping, visiting)
|
function.error = canonical_type(module, function.error, mapping, visiting)
|
||||||
}
|
}
|
||||||
for &global in module.globals {
|
for &global in module.globals {
|
||||||
|
if diagnostic := type_resolution_diagnostic(module, global.type, global.file);
|
||||||
|
diagnostic != source.INVALID_DIAGNOSTIC && global.diagnostic == source.INVALID_DIAGNOSTIC {
|
||||||
|
global.diagnostic = diagnostic
|
||||||
|
}
|
||||||
global.type = canonical_type(module, global.type, mapping, visiting)
|
global.type = canonical_type(module, global.type, mapping, visiting)
|
||||||
}
|
}
|
||||||
for &statement in module.statements {
|
for &statement in module.statements {
|
||||||
|
if diagnostic := type_resolution_diagnostic(module, statement.type, ast.File_Id(statement.span.file));
|
||||||
|
diagnostic != source.INVALID_DIAGNOSTIC && statement.diagnostic == source.INVALID_DIAGNOSTIC {
|
||||||
|
statement.diagnostic = diagnostic
|
||||||
|
}
|
||||||
statement.type = canonical_type(module, statement.type, mapping, visiting)
|
statement.type = canonical_type(module, statement.type, mapping, visiting)
|
||||||
}
|
}
|
||||||
for &field in module.type_fields {
|
for &field in module.type_fields {
|
||||||
field.type = canonical_type(module, field.type, mapping, visiting)
|
field.type = canonical_type(module, field.type, mapping, visiting)
|
||||||
}
|
}
|
||||||
|
for &field_default in module.struct_field_defaults {
|
||||||
|
field_default.record = canonical_type(module, field_default.record, mapping, visiting)
|
||||||
|
}
|
||||||
for index := 0; index < original_count; index += 1 {
|
for index := 0; index < original_count; index += 1 {
|
||||||
_ = canonical_type(module, types.DYNAMIC_START+types.Type(index), mapping, visiting)
|
_ = canonical_type(module, types.DYNAMIC_START+types.Type(index), mapping, visiting)
|
||||||
}
|
}
|
||||||
@@ -1653,6 +1933,7 @@ load :: proc(
|
|||||||
c_options := cimport.Options{},
|
c_options := cimport.Options{},
|
||||||
selected := target.DEFAULT,
|
selected := target.DEFAULT,
|
||||||
project_root_path := "",
|
project_root_path := "",
|
||||||
|
mode := ast.Compile_Mode.Executable,
|
||||||
) -> (ast.Module, bool) {
|
) -> (ast.Module, bool) {
|
||||||
module := ast.init_module(allocator)
|
module := ast.init_module(allocator)
|
||||||
project_root_source := project_root_path if len(project_root_path) > 0 else root_path
|
project_root_source := project_root_path if len(project_root_path) > 0 else root_path
|
||||||
@@ -1670,6 +1951,7 @@ load :: proc(
|
|||||||
c_options=c_options,
|
c_options=c_options,
|
||||||
selected=selected,
|
selected=selected,
|
||||||
project_root=project_root,
|
project_root=project_root,
|
||||||
|
mode=mode,
|
||||||
}
|
}
|
||||||
state.record_identities.allocator = allocator
|
state.record_identities.allocator = allocator
|
||||||
state.record_types.allocator = allocator
|
state.record_types.allocator = allocator
|
||||||
@@ -1685,8 +1967,17 @@ load :: proc(
|
|||||||
if root != ast.Package_Id(0) && root != ast.INVALID_PACKAGE {
|
if root != ast.Package_Id(0) && root != ast.INVALID_PACKAGE {
|
||||||
state.root_failed = true
|
state.root_failed = true
|
||||||
}
|
}
|
||||||
|
if mode == .Test {
|
||||||
|
testing_path, testing_error := filepath.join({project_root, "std", "testing"}, allocator)
|
||||||
|
if testing_error == nil {
|
||||||
|
_ = load_package(&state, testing_path, source.Span{})
|
||||||
|
delete(testing_path, allocator)
|
||||||
|
}
|
||||||
|
}
|
||||||
validate_imports(&state)
|
validate_imports(&state)
|
||||||
validate_declaration_aliases(&state)
|
validate_declaration_aliases(&state)
|
||||||
|
resolve_enum_values(&state)
|
||||||
|
diagnose_qualified_type_uses(&state)
|
||||||
canonicalize_types(&module, allocator)
|
canonicalize_types(&module, allocator)
|
||||||
return module, !state.root_failed
|
return module, !state.root_failed
|
||||||
}
|
}
|
||||||
|
|||||||
+373
-141
@@ -244,9 +244,9 @@ lower_compound_expr :: proc(state: ^State, expr_id: hir.Expr_Id) -> ir.Instructi
|
|||||||
b=ir.INVALID_INSTRUCTION,
|
b=ir.INVALID_INSTRUCTION,
|
||||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
})
|
})
|
||||||
case .None:
|
case .Null:
|
||||||
return append_instruction(state, ir.Instruction{
|
return append_instruction(state, ir.Instruction{
|
||||||
op=.None, span=expr.span, type=expr.type,
|
op=.Null, span=expr.span, type=expr.type,
|
||||||
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
|
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
|
||||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
})
|
})
|
||||||
@@ -468,9 +468,8 @@ lower_compound_expr :: proc(state: ^State, expr_id: hir.Expr_Id) -> ir.Instructi
|
|||||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
if expr.integer != 0 {
|
capture := hir.as_local(expr.target)
|
||||||
capture := hir.as_local(expr.target)
|
if capture != hir.INVALID_LOCAL && int(capture) < len(state.func_locals) {
|
||||||
if capture != hir.INVALID_LOCAL && int(capture) < len(state.func_locals) {
|
|
||||||
error_type := state.func_locals[capture].type
|
error_type := state.func_locals[capture].type
|
||||||
error_value := append_instruction(state, ir.Instruction{
|
error_value := append_instruction(state, ir.Instruction{
|
||||||
op=.Fallible_Error, span=expr.span, type=error_type,
|
op=.Fallible_Error, span=expr.span, type=error_type,
|
||||||
@@ -489,8 +488,25 @@ lower_compound_expr :: proc(state: ^State, expr_id: hir.Expr_Id) -> ir.Instructi
|
|||||||
target=ir.INVALID_REF, a=capture_slot, b=error_value,
|
target=ir.INVALID_REF, a=capture_slot, b=error_value,
|
||||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
if expr.integer != hir.CATCH_EXPRESSION {
|
||||||
lower_statements(state, expr.body)
|
lower_statements(state, expr.body)
|
||||||
|
if expr.integer == hir.CATCH_VOID_FALLTHROUGH {
|
||||||
|
append_instruction(state, ir.Instruction{
|
||||||
|
op=.Br, span=expr.span, type=types.VOID, integer=merge_lbl,
|
||||||
|
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
|
||||||
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
|
})
|
||||||
|
} else if expr.right == hir.INVALID_EXPR {
|
||||||
|
// A terminating handler can still end in a synthetic match/if merge label
|
||||||
|
// after its returns. Seal that unreachable continuation before the success
|
||||||
|
// label so LLVM never sees adjacent basic-block labels.
|
||||||
|
append_instruction(state, ir.Instruction{
|
||||||
|
op=.Trap, span=expr.span, type=types.NORETURN, integer=1,
|
||||||
|
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
|
||||||
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if expr.right != hir.INVALID_EXPR {
|
if expr.right != hir.INVALID_EXPR {
|
||||||
fallback := lower_nested_expr(state, expr.right)
|
fallback := lower_nested_expr(state, expr.right)
|
||||||
@@ -554,6 +570,25 @@ lower_compound_expr :: proc(state: ^State, expr_id: hir.Expr_Id) -> ir.Instructi
|
|||||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
})
|
})
|
||||||
case .Eq, .Ne, .Lt, .Le, .Gt, .Ge:
|
case .Eq, .Ne, .Lt, .Le, .Gt, .Ge:
|
||||||
|
left_expr := state.hir_module.exprs[expr.left]
|
||||||
|
right_expr := state.hir_module.exprs[expr.right]
|
||||||
|
if left_expr.kind == .Null || right_expr.kind == .Null {
|
||||||
|
optional_expr := expr.right if left_expr.kind == .Null else expr.left
|
||||||
|
optional := lower_nested_expr(state, optional_expr)
|
||||||
|
present := append_instruction(state, ir.Instruction{
|
||||||
|
op=.Optional_Is_Some, span=expr.span, type=types.BOOL,
|
||||||
|
target=ir.INVALID_REF, a=optional, b=ir.INVALID_INSTRUCTION,
|
||||||
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
|
})
|
||||||
|
if expr.kind == .Ne {
|
||||||
|
return present
|
||||||
|
}
|
||||||
|
return append_instruction(state, ir.Instruction{
|
||||||
|
op=.Not, span=expr.span, type=types.BOOL,
|
||||||
|
target=ir.INVALID_REF, a=present, b=ir.INVALID_INSTRUCTION,
|
||||||
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
|
})
|
||||||
|
}
|
||||||
left := lower_nested_expr(state, expr.left)
|
left := lower_nested_expr(state, expr.left)
|
||||||
right := lower_nested_expr(state, expr.right)
|
right := lower_nested_expr(state, expr.right)
|
||||||
predicate := ir.Compare_Predicate.Eq
|
predicate := ir.Compare_Predicate.Eq
|
||||||
@@ -621,15 +656,38 @@ lower_compound_expr :: proc(state: ^State, expr_id: hir.Expr_Id) -> ir.Instructi
|
|||||||
return append_recovery_value(state, expr.span, expr.type, expr.diagnostic)
|
return append_recovery_value(state, expr.span, expr.type, expr.diagnostic)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
memory_region_element_type :: proc(value: types.Type, store: ^types.Store) -> types.Type {
|
||||||
|
resolved := types.resolve_alias(value, store)
|
||||||
|
item, ok := types.node(store, resolved)
|
||||||
|
if !ok {
|
||||||
|
return types.INVALID
|
||||||
|
}
|
||||||
|
if item.kind == .Slice {
|
||||||
|
return item.child
|
||||||
|
}
|
||||||
|
if item.kind == .Pointer && !item.many {
|
||||||
|
array, array_ok := types.node(store, types.resolve_alias(item.child, store))
|
||||||
|
if array_ok && array.kind == .Array {
|
||||||
|
return array.child
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return types.INVALID
|
||||||
|
}
|
||||||
|
|
||||||
lower_expr :: proc(state: ^State, expr_id: hir.Expr_Id) -> ir.Instruction_Id {
|
lower_expr :: proc(state: ^State, expr_id: hir.Expr_Id) -> ir.Instruction_Id {
|
||||||
stack := state.expr_stack
|
stack := state.expr_stack
|
||||||
|
state.expr_stack = nil
|
||||||
clear_dynamic_array(&stack)
|
clear_dynamic_array(&stack)
|
||||||
defer {
|
defer {
|
||||||
for frame in stack {
|
for frame in stack {
|
||||||
delete(frame.args, state.allocator)
|
delete(frame.args, state.allocator)
|
||||||
}
|
}
|
||||||
clear_dynamic_array(&stack)
|
clear_dynamic_array(&stack)
|
||||||
state.expr_stack = stack
|
if state.expr_stack == nil {
|
||||||
|
state.expr_stack = stack
|
||||||
|
} else {
|
||||||
|
delete(stack)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
append(&stack, Lower_Expr_Frame{expr=expr_id})
|
append(&stack, Lower_Expr_Frame{expr=expr_id})
|
||||||
last := ir.INVALID_INSTRUCTION
|
last := ir.INVALID_INSTRUCTION
|
||||||
@@ -647,13 +705,34 @@ lower_expr :: proc(state: ^State, expr_id: hir.Expr_Id) -> ir.Instruction_Id {
|
|||||||
case .Invalid:
|
case .Invalid:
|
||||||
last = append_recovery_value(state, expr.span, expr.type, expr.diagnostic)
|
last = append_recovery_value(state, expr.span, expr.type, expr.diagnostic)
|
||||||
_ = pop(&stack)
|
_ = pop(&stack)
|
||||||
|
case .Void:
|
||||||
|
last = append_instruction(state, ir.Instruction{
|
||||||
|
op=.Const, span=expr.span, type=types.VOID,
|
||||||
|
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
|
||||||
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
|
})
|
||||||
|
_ = pop(&stack)
|
||||||
case .Integer, .Float, .Bool:
|
case .Integer, .Float, .Bool:
|
||||||
last = append_instruction(state, ir.Instruction{
|
last = append_instruction(state, ir.Instruction{
|
||||||
op=.Const, span=expr.span, type=expr.type, integer=expr.integer,
|
op=.Const, span=expr.span, type=expr.type, integer=expr.integer,
|
||||||
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION, diagnostic=source.INVALID_DIAGNOSTIC,
|
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION, diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
})
|
})
|
||||||
_ = pop(&stack)
|
_ = pop(&stack)
|
||||||
case .String, .Array, .Struct, .Range, .None, .Optional_Some, .Address, .Deref,
|
case .Undefined:
|
||||||
|
last = append_instruction(state, ir.Instruction{
|
||||||
|
op=.Poison, span=expr.span, type=expr.type,
|
||||||
|
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
|
||||||
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
|
})
|
||||||
|
_ = pop(&stack)
|
||||||
|
case .Unreachable:
|
||||||
|
last = append_instruction(state, ir.Instruction{
|
||||||
|
op=.Trap, span=expr.span, type=types.NORETURN, integer=1,
|
||||||
|
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
|
||||||
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
|
})
|
||||||
|
_ = pop(&stack)
|
||||||
|
case .String, .Array, .Struct, .Range, .Null, .Optional_Some, .Address, .Deref,
|
||||||
.Index, .Slice, .Field, .Union_Tag, .Length, .Slice_Ptr, .Unwrap, .Orelse,
|
.Index, .Slice, .Field, .Union_Tag, .Length, .Slice_Ptr, .Unwrap, .Orelse,
|
||||||
.Try, .Catch, .Not, .Eq, .Ne, .Lt, .Le, .Gt, .Ge, .And, .Or:
|
.Try, .Catch, .Not, .Eq, .Ne, .Lt, .Le, .Gt, .Ge, .And, .Or:
|
||||||
last = lower_compound_expr(state, frame.expr)
|
last = lower_compound_expr(state, frame.expr)
|
||||||
@@ -698,14 +777,15 @@ lower_expr :: proc(state: ^State, expr_id: hir.Expr_Id) -> ir.Instruction_Id {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
_ = pop(&stack)
|
_ = pop(&stack)
|
||||||
case .Widen, .Sum_Widen, .C_Coerce, .C_Vararg_Promote, .Retype, .Scalar_Cast, .Pointer_Cast, .Weaken_Pointer, .Weaken_Slice, .Decay_Array_Pointer:
|
case .Widen, .Sum_Widen, .Sum_Project, .C_Coerce, .C_Vararg_Promote, .Retype, .Scalar_Cast, .Pointer_Cast, .Const_Cast, .Weaken_Pointer, .Weaken_Slice, .Decay_Array_Pointer:
|
||||||
stack[frame_index].stage = 1
|
stack[frame_index].stage = 1
|
||||||
append(&stack, Lower_Expr_Frame{expr=expr.left})
|
append(&stack, Lower_Expr_Frame{expr=expr.left})
|
||||||
case .Negate:
|
case .Negate, .Bit_Not:
|
||||||
stack[frame_index].stage = 5
|
stack[frame_index].stage = 5
|
||||||
append(&stack, Lower_Expr_Frame{expr=expr.left})
|
append(&stack, Lower_Expr_Frame{expr=expr.left})
|
||||||
case .Add, .Sub, .Mul, .Div, .Div_Trunc, .Div_Floor, .Div_Exact, .Div_Ceil,
|
case .Add, .Sub, .Mul, .Div, .Div_Trunc, .Div_Floor, .Div_Exact, .Div_Ceil,
|
||||||
.Rem, .Mod, .Pointer_Add:
|
.Rem, .Mod, .Pointer_Add, .Bit_And, .Bit_Or, .Bit_Xor, .Shift_Left,
|
||||||
|
.Shift_Right, .Shift_Left_Saturating, .Mem_Copy, .Mem_Set:
|
||||||
stack[frame_index].stage = 2
|
stack[frame_index].stage = 2
|
||||||
append(&stack, Lower_Expr_Frame{expr=expr.left})
|
append(&stack, Lower_Expr_Frame{expr=expr.left})
|
||||||
case .Call:
|
case .Call:
|
||||||
@@ -743,8 +823,12 @@ lower_expr :: proc(state: ^State, expr_id: hir.Expr_Id) -> ir.Instruction_Id {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if frame.stage == 5 {
|
if frame.stage == 5 {
|
||||||
|
op := ir.Opcode.Neg_Checked
|
||||||
|
if expr.kind == .Bit_Not {
|
||||||
|
op = .Bit_Not
|
||||||
|
}
|
||||||
last = append_instruction(state, ir.Instruction{
|
last = append_instruction(state, ir.Instruction{
|
||||||
op=.Neg_Checked, span=expr.span, type=expr.type, target=ir.INVALID_REF,
|
op=op, span=expr.span, type=expr.type, target=ir.INVALID_REF,
|
||||||
a=last, b=ir.INVALID_INSTRUCTION, diagnostic=source.INVALID_DIAGNOSTIC,
|
a=last, b=ir.INVALID_INSTRUCTION, diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
})
|
})
|
||||||
_ = pop(&stack)
|
_ = pop(&stack)
|
||||||
@@ -754,6 +838,7 @@ lower_expr :: proc(state: ^State, expr_id: hir.Expr_Id) -> ir.Instruction_Id {
|
|||||||
op := ir.Opcode.Widen
|
op := ir.Opcode.Widen
|
||||||
#partial switch expr.kind {
|
#partial switch expr.kind {
|
||||||
case .Sum_Widen: op = .Sum_Widen
|
case .Sum_Widen: op = .Sum_Widen
|
||||||
|
case .Sum_Project: op = .Sum_Project
|
||||||
case .Weaken_Pointer: op = .Weaken_Pointer
|
case .Weaken_Pointer: op = .Weaken_Pointer
|
||||||
case .Weaken_Slice: op = .Weaken_Slice
|
case .Weaken_Slice: op = .Weaken_Slice
|
||||||
case .Decay_Array_Pointer: op = .Decay_Array_Pointer
|
case .Decay_Array_Pointer: op = .Decay_Array_Pointer
|
||||||
@@ -762,6 +847,7 @@ lower_expr :: proc(state: ^State, expr_id: hir.Expr_Id) -> ir.Instruction_Id {
|
|||||||
case .Retype: op = .Retype
|
case .Retype: op = .Retype
|
||||||
case .Scalar_Cast: op = .Scalar_Cast
|
case .Scalar_Cast: op = .Scalar_Cast
|
||||||
case .Pointer_Cast: op = .Pointer_Cast
|
case .Pointer_Cast: op = .Pointer_Cast
|
||||||
|
case .Const_Cast: op = .Const_Cast
|
||||||
case: op = .Widen
|
case: op = .Widen
|
||||||
}
|
}
|
||||||
last = append_instruction(state, ir.Instruction{
|
last = append_instruction(state, ir.Instruction{
|
||||||
@@ -780,6 +866,7 @@ lower_expr :: proc(state: ^State, expr_id: hir.Expr_Id) -> ir.Instruction_Id {
|
|||||||
}
|
}
|
||||||
if frame.stage == 3 {
|
if frame.stage == 3 {
|
||||||
op := ir.Opcode.Add_Checked
|
op := ir.Opcode.Add_Checked
|
||||||
|
result_type := expr.type
|
||||||
#partial switch expr.kind {
|
#partial switch expr.kind {
|
||||||
case .Sub: op = .Sub_Checked
|
case .Sub: op = .Sub_Checked
|
||||||
case .Mul: op = .Mul_Checked
|
case .Mul: op = .Mul_Checked
|
||||||
@@ -791,10 +878,22 @@ lower_expr :: proc(state: ^State, expr_id: hir.Expr_Id) -> ir.Instruction_Id {
|
|||||||
case .Rem: op = .Rem_Checked
|
case .Rem: op = .Rem_Checked
|
||||||
case .Mod: op = .Mod_Checked
|
case .Mod: op = .Mod_Checked
|
||||||
case .Pointer_Add: op = .Pointer_Add
|
case .Pointer_Add: op = .Pointer_Add
|
||||||
|
case .Bit_And: op = .Bit_And
|
||||||
|
case .Bit_Or: op = .Bit_Or
|
||||||
|
case .Bit_Xor: op = .Bit_Xor
|
||||||
|
case .Shift_Left: op = .Shift_Left
|
||||||
|
case .Shift_Right: op = .Shift_Right
|
||||||
|
case .Shift_Left_Saturating: op = .Shift_Left_Saturating
|
||||||
|
case .Mem_Copy:
|
||||||
|
op = .Mem_Copy
|
||||||
|
result_type = memory_region_element_type(state.hir_module.exprs[expr.left].type, &state.hir_module.types)
|
||||||
|
case .Mem_Set:
|
||||||
|
op = .Mem_Set
|
||||||
|
result_type = memory_region_element_type(state.hir_module.exprs[expr.left].type, &state.hir_module.types)
|
||||||
}
|
}
|
||||||
last = append_instruction(state, ir.Instruction{
|
last = append_instruction(state, ir.Instruction{
|
||||||
op=op,
|
op=op,
|
||||||
span=expr.span, type=expr.type, target=ir.INVALID_REF,
|
span=expr.span, type=result_type, target=ir.INVALID_REF,
|
||||||
a=frame.left, b=last, diagnostic=source.INVALID_DIAGNOSTIC,
|
a=frame.left, b=last, diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
})
|
})
|
||||||
_ = pop(&stack)
|
_ = pop(&stack)
|
||||||
@@ -825,6 +924,101 @@ lower_expr :: proc(state: ^State, expr_id: hir.Expr_Id) -> ir.Instruction_Id {
|
|||||||
return last
|
return last
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lower_conditional_unwrap_header :: proc(
|
||||||
|
state: ^State,
|
||||||
|
statement: hir.Stmt,
|
||||||
|
success_lbl, false_lbl: i64,
|
||||||
|
) {
|
||||||
|
for unwrap in statement.unwraps {
|
||||||
|
optional := lower_expr(state, unwrap.expr)
|
||||||
|
present := append_instruction(state, ir.Instruction{
|
||||||
|
op=.Optional_Is_Some,
|
||||||
|
span=statement.span,
|
||||||
|
type=types.BOOL,
|
||||||
|
target=ir.INVALID_REF,
|
||||||
|
a=optional,
|
||||||
|
b=ir.INVALID_INSTRUCTION,
|
||||||
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
|
})
|
||||||
|
next_lbl := fresh_label(state)
|
||||||
|
append_instruction(state, ir.Instruction{
|
||||||
|
op=.Cond_Br,
|
||||||
|
span=statement.span,
|
||||||
|
type=types.VOID,
|
||||||
|
a=present,
|
||||||
|
integer=next_lbl,
|
||||||
|
target=ir.Ref(u32(false_lbl)),
|
||||||
|
b=ir.INVALID_INSTRUCTION,
|
||||||
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
|
})
|
||||||
|
append_instruction(state, ir.Instruction{
|
||||||
|
op=.Label,
|
||||||
|
span=statement.span,
|
||||||
|
type=types.VOID,
|
||||||
|
integer=next_lbl,
|
||||||
|
target=ir.INVALID_REF,
|
||||||
|
a=ir.INVALID_INSTRUCTION,
|
||||||
|
b=ir.INVALID_INSTRUCTION,
|
||||||
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
|
})
|
||||||
|
if unwrap.local != hir.INVALID_LOCAL && int(unwrap.local) < len(state.func_locals) {
|
||||||
|
local := state.func_locals[unwrap.local]
|
||||||
|
slot := append_instruction(state, ir.Instruction{
|
||||||
|
op=.Alloca,
|
||||||
|
span=statement.span,
|
||||||
|
type=local.type,
|
||||||
|
target=ir.local_ref(ir.Local_Id(unwrap.local)),
|
||||||
|
a=ir.INVALID_INSTRUCTION,
|
||||||
|
b=ir.INVALID_INSTRUCTION,
|
||||||
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
|
})
|
||||||
|
state.local_slots[unwrap.local] = slot
|
||||||
|
inner := append_instruction(state, ir.Instruction{
|
||||||
|
op=.Optional_Value,
|
||||||
|
span=statement.span,
|
||||||
|
type=local.type,
|
||||||
|
target=ir.INVALID_REF,
|
||||||
|
a=optional,
|
||||||
|
b=ir.INVALID_INSTRUCTION,
|
||||||
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
|
})
|
||||||
|
append_instruction(state, ir.Instruction{
|
||||||
|
op=.Store,
|
||||||
|
span=statement.span,
|
||||||
|
type=local.type,
|
||||||
|
target=ir.INVALID_REF,
|
||||||
|
a=slot,
|
||||||
|
b=inner,
|
||||||
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if statement.guard != hir.INVALID_EXPR {
|
||||||
|
guard := lower_expr(state, statement.guard)
|
||||||
|
append_instruction(state, ir.Instruction{
|
||||||
|
op=.Cond_Br,
|
||||||
|
span=statement.span,
|
||||||
|
type=types.VOID,
|
||||||
|
a=guard,
|
||||||
|
integer=success_lbl,
|
||||||
|
target=ir.Ref(u32(false_lbl)),
|
||||||
|
b=ir.INVALID_INSTRUCTION,
|
||||||
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
append_instruction(state, ir.Instruction{
|
||||||
|
op=.Br,
|
||||||
|
span=statement.span,
|
||||||
|
type=types.VOID,
|
||||||
|
integer=success_lbl,
|
||||||
|
target=ir.INVALID_REF,
|
||||||
|
a=ir.INVALID_INSTRUCTION,
|
||||||
|
b=ir.INVALID_INSTRUCTION,
|
||||||
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
lower_statements :: proc(state: ^State, statements: []hir.Stmt_Id) {
|
lower_statements :: proc(state: ^State, statements: []hir.Stmt_Id) {
|
||||||
hir_module := state.hir_module
|
hir_module := state.hir_module
|
||||||
for statement_id in statements {
|
for statement_id in statements {
|
||||||
@@ -897,6 +1091,12 @@ lower_statements :: proc(state: ^State, statements: []hir.Stmt_Id) {
|
|||||||
case .Mul: op = .Mul_Checked
|
case .Mul: op = .Mul_Checked
|
||||||
case .Div: op = .Div_Checked
|
case .Div: op = .Div_Checked
|
||||||
case .Pointer_Add: op = .Pointer_Add
|
case .Pointer_Add: op = .Pointer_Add
|
||||||
|
case .Bit_And: op = .Bit_And
|
||||||
|
case .Bit_Or: op = .Bit_Or
|
||||||
|
case .Bit_Xor: op = .Bit_Xor
|
||||||
|
case .Shift_Left: op = .Shift_Left
|
||||||
|
case .Shift_Right: op = .Shift_Right
|
||||||
|
case .Shift_Left_Saturating: op = .Shift_Left_Saturating
|
||||||
}
|
}
|
||||||
value := append_instruction(state, ir.Instruction{
|
value := append_instruction(state, ir.Instruction{
|
||||||
op=op, span=statement.span, type=target_type,
|
op=op, span=statement.span, type=target_type,
|
||||||
@@ -950,6 +1150,9 @@ lower_statements :: proc(state: ^State, statements: []hir.Stmt_Id) {
|
|||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
value := lower_expr(state, statement.expr)
|
value := lower_expr(state, statement.expr)
|
||||||
|
if types.is_noreturn(state.hir_module.exprs[statement.expr].type) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
append_instruction(state, ir.Instruction{
|
append_instruction(state, ir.Instruction{
|
||||||
op=.Return,
|
op=.Return,
|
||||||
span=statement.span,
|
span=statement.span,
|
||||||
@@ -1028,97 +1231,7 @@ lower_statements :: proc(state: ^State, statements: []hir.Stmt_Id) {
|
|||||||
false_target := else_lbl if has_else else merge_lbl
|
false_target := else_lbl if has_else else merge_lbl
|
||||||
|
|
||||||
if len(statement.unwraps) > 0 {
|
if len(statement.unwraps) > 0 {
|
||||||
// Evaluate each optional exactly once, entering the next operand only
|
lower_conditional_unwrap_header(state, statement, then_lbl, false_target)
|
||||||
// after the previous one is present. Capture storage is initialized in
|
|
||||||
// these success blocks so the optional guard can use every binding.
|
|
||||||
for unwrap in statement.unwraps {
|
|
||||||
optional := lower_expr(state, unwrap.expr)
|
|
||||||
present := append_instruction(state, ir.Instruction{
|
|
||||||
op=.Optional_Is_Some,
|
|
||||||
span=statement.span,
|
|
||||||
type=types.BOOL,
|
|
||||||
target=ir.INVALID_REF,
|
|
||||||
a=optional,
|
|
||||||
b=ir.INVALID_INSTRUCTION,
|
|
||||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
|
||||||
})
|
|
||||||
success_lbl := fresh_label(state)
|
|
||||||
append_instruction(state, ir.Instruction{
|
|
||||||
op=.Cond_Br,
|
|
||||||
span=statement.span,
|
|
||||||
type=types.VOID,
|
|
||||||
a=present,
|
|
||||||
integer=success_lbl,
|
|
||||||
target=ir.Ref(u32(false_target)),
|
|
||||||
b=ir.INVALID_INSTRUCTION,
|
|
||||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
|
||||||
})
|
|
||||||
append_instruction(state, ir.Instruction{
|
|
||||||
op=.Label,
|
|
||||||
span=statement.span,
|
|
||||||
type=types.VOID,
|
|
||||||
integer=success_lbl,
|
|
||||||
target=ir.INVALID_REF,
|
|
||||||
a=ir.INVALID_INSTRUCTION,
|
|
||||||
b=ir.INVALID_INSTRUCTION,
|
|
||||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
|
||||||
})
|
|
||||||
if unwrap.local != hir.INVALID_LOCAL && int(unwrap.local) < len(state.func_locals) {
|
|
||||||
local := state.func_locals[unwrap.local]
|
|
||||||
slot := append_instruction(state, ir.Instruction{
|
|
||||||
op=.Alloca,
|
|
||||||
span=statement.span,
|
|
||||||
type=local.type,
|
|
||||||
target=ir.local_ref(ir.Local_Id(unwrap.local)),
|
|
||||||
a=ir.INVALID_INSTRUCTION,
|
|
||||||
b=ir.INVALID_INSTRUCTION,
|
|
||||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
|
||||||
})
|
|
||||||
state.local_slots[unwrap.local] = slot
|
|
||||||
inner := append_instruction(state, ir.Instruction{
|
|
||||||
op=.Optional_Value,
|
|
||||||
span=statement.span,
|
|
||||||
type=local.type,
|
|
||||||
target=ir.INVALID_REF,
|
|
||||||
a=optional,
|
|
||||||
b=ir.INVALID_INSTRUCTION,
|
|
||||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
|
||||||
})
|
|
||||||
append_instruction(state, ir.Instruction{
|
|
||||||
op=.Store,
|
|
||||||
span=statement.span,
|
|
||||||
type=local.type,
|
|
||||||
target=ir.INVALID_REF,
|
|
||||||
a=slot,
|
|
||||||
b=inner,
|
|
||||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if statement.guard != hir.INVALID_EXPR {
|
|
||||||
guard := lower_expr(state, statement.guard)
|
|
||||||
append_instruction(state, ir.Instruction{
|
|
||||||
op=.Cond_Br,
|
|
||||||
span=statement.span,
|
|
||||||
type=types.VOID,
|
|
||||||
a=guard,
|
|
||||||
integer=then_lbl,
|
|
||||||
target=ir.Ref(u32(false_target)),
|
|
||||||
b=ir.INVALID_INSTRUCTION,
|
|
||||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
append_instruction(state, ir.Instruction{
|
|
||||||
op=.Br,
|
|
||||||
span=statement.span,
|
|
||||||
type=types.VOID,
|
|
||||||
integer=then_lbl,
|
|
||||||
target=ir.INVALID_REF,
|
|
||||||
a=ir.INVALID_INSTRUCTION,
|
|
||||||
b=ir.INVALID_INSTRUCTION,
|
|
||||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
cond := lower_expr(state, statement.expr)
|
cond := lower_expr(state, statement.expr)
|
||||||
append_instruction(state, ir.Instruction{
|
append_instruction(state, ir.Instruction{
|
||||||
@@ -1174,12 +1287,16 @@ lower_statements :: proc(state: ^State, statements: []hir.Stmt_Id) {
|
|||||||
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
|
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
|
||||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
})
|
})
|
||||||
condition := lower_expr(state, statement.expr)
|
if len(statement.unwraps) > 0 {
|
||||||
append_instruction(state, ir.Instruction{
|
lower_conditional_unwrap_header(state, statement, body_lbl, exit_lbl)
|
||||||
op=.Cond_Br, span=statement.span, type=types.VOID,
|
} else {
|
||||||
a=condition, integer=body_lbl, target=ir.Ref(u32(exit_lbl)),
|
condition := lower_expr(state, statement.expr)
|
||||||
b=ir.INVALID_INSTRUCTION, diagnostic=source.INVALID_DIAGNOSTIC,
|
append_instruction(state, ir.Instruction{
|
||||||
})
|
op=.Cond_Br, span=statement.span, type=types.VOID,
|
||||||
|
a=condition, integer=body_lbl, target=ir.Ref(u32(exit_lbl)),
|
||||||
|
b=ir.INVALID_INSTRUCTION, diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
|
})
|
||||||
|
}
|
||||||
append_instruction(state, ir.Instruction{
|
append_instruction(state, ir.Instruction{
|
||||||
op=.Label, span=statement.span, type=types.VOID, integer=body_lbl,
|
op=.Label, span=statement.span, type=types.VOID, integer=body_lbl,
|
||||||
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
|
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
|
||||||
@@ -1593,11 +1710,22 @@ lower_body :: proc(hir_module: ^hir.Module, function: hir.Function, allocator: m
|
|||||||
}
|
}
|
||||||
|
|
||||||
lower_statements(&state, function.body)
|
lower_statements(&state, function.body)
|
||||||
if len(state.instructions) == 0 ||
|
last_terminates := false
|
||||||
(state.instructions[len(state.instructions)-1].op != .Return &&
|
if len(state.instructions) > 0 {
|
||||||
state.instructions[len(state.instructions)-1].op != .Return_Void) {
|
last_instruction := state.instructions[len(state.instructions)-1]
|
||||||
|
last_terminates = last_instruction.op == .Return || last_instruction.op == .Return_Void ||
|
||||||
|
last_instruction.op == .Trap ||
|
||||||
|
last_instruction.op == .Call && types.is_noreturn(last_instruction.type)
|
||||||
|
}
|
||||||
|
if !last_terminates {
|
||||||
if types.is_void(function.result) {
|
if types.is_void(function.result) {
|
||||||
append_instruction(&state, ir.Instruction{op=.Return_Void, type=types.VOID, target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION, diagnostic=source.INVALID_DIAGNOSTIC})
|
append_instruction(&state, ir.Instruction{op=.Return_Void, type=types.VOID, target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION, diagnostic=source.INVALID_DIAGNOSTIC})
|
||||||
|
} else if types.is_noreturn(function.result) {
|
||||||
|
append_instruction(&state, ir.Instruction{
|
||||||
|
op=.Trap, type=types.NORETURN, integer=1,
|
||||||
|
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
|
||||||
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
value := append_instruction(&state, ir.Instruction{
|
value := append_instruction(&state, ir.Instruction{
|
||||||
op=.Const,
|
op=.Const,
|
||||||
@@ -1633,64 +1761,167 @@ lower_global_initializer :: proc(hir_module: ^hir.Module, global: hir.Global, al
|
|||||||
|
|
||||||
append_injected_main :: proc(module: ^ir.Module, hir_module: ^hir.Module, allocator: mem.Allocator) {
|
append_injected_main :: proc(module: ^ir.Module, hir_module: ^hir.Module, allocator: mem.Allocator) {
|
||||||
main_index, main_ok := hir.index(hir_module.injected_main, hir.INVALID_FUNCTION, len(hir_module.functions))
|
main_index, main_ok := hir.index(hir_module.injected_main, hir.INVALID_FUNCTION, len(hir_module.functions))
|
||||||
|
if !main_ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
main_function := &hir_module.functions[main_index]
|
||||||
provider_index, provider_ok := hir.index(hir_module.io_provider, hir.INVALID_FUNCTION, len(hir_module.functions))
|
provider_index, provider_ok := hir.index(hir_module.io_provider, hir.INVALID_FUNCTION, len(hir_module.functions))
|
||||||
if !main_ok || !provider_ok {
|
param_index := -1
|
||||||
|
if len(main_function.params) == 1 {
|
||||||
|
param_index, main_ok = hir.index(main_function.params[0], hir.INVALID_LOCAL, len(main_function.locals))
|
||||||
|
if !main_ok || !provider_ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else if len(main_function.params) != 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
instructions: [dynamic]ir.Instruction
|
instructions: [dynamic]ir.Instruction
|
||||||
instructions.allocator = allocator
|
instructions.allocator = allocator
|
||||||
provider_call := ir.instruction_id(len(instructions))
|
args: []ir.Instruction_Id
|
||||||
append(&instructions, ir.Instruction{
|
if param_index >= 0 {
|
||||||
op=.Call,
|
provider_call := ir.instruction_id(len(instructions))
|
||||||
type=hir_module.functions[provider_index].result,
|
append(&instructions, ir.Instruction{
|
||||||
target=ir.function_ref(ir.Function_Id(provider_index)),
|
op=.Call,
|
||||||
a=ir.INVALID_INSTRUCTION,
|
type=hir_module.functions[provider_index].result,
|
||||||
b=ir.INVALID_INSTRUCTION,
|
target=ir.function_ref(ir.Function_Id(provider_index)),
|
||||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
a=ir.INVALID_INSTRUCTION,
|
||||||
})
|
b=ir.INVALID_INSTRUCTION,
|
||||||
args := make([]ir.Instruction_Id, 1, allocator)
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
args[0] = provider_call
|
})
|
||||||
|
init_args := make([]ir.Instruction_Id, 1, allocator)
|
||||||
|
init_args[0] = provider_call
|
||||||
|
init_value := ir.instruction_id(len(instructions))
|
||||||
|
append(&instructions, ir.Instruction{
|
||||||
|
op=.Aggregate,
|
||||||
|
type=main_function.locals[param_index].type,
|
||||||
|
args=init_args,
|
||||||
|
target=ir.INVALID_REF,
|
||||||
|
a=ir.INVALID_INSTRUCTION,
|
||||||
|
b=ir.INVALID_INSTRUCTION,
|
||||||
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
|
})
|
||||||
|
args = make([]ir.Instruction_Id, 1, allocator)
|
||||||
|
args[0] = init_value
|
||||||
|
}
|
||||||
main_call := ir.instruction_id(len(instructions))
|
main_call := ir.instruction_id(len(instructions))
|
||||||
append(&instructions, ir.Instruction{
|
append(&instructions, ir.Instruction{
|
||||||
op=.Call,
|
op=.Call,
|
||||||
type=hir_module.functions[main_index].result,
|
type=main_function.result,
|
||||||
args=args,
|
args=args,
|
||||||
target=ir.function_ref(ir.Function_Id(main_index)),
|
target=ir.function_ref(ir.Function_Id(main_index)),
|
||||||
a=ir.INVALID_INSTRUCTION,
|
a=ir.INVALID_INSTRUCTION,
|
||||||
b=ir.INVALID_INSTRUCTION,
|
b=ir.INVALID_INSTRUCTION,
|
||||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
})
|
})
|
||||||
if types.is_void(hir_module.functions[main_index].result) {
|
|
||||||
|
if types.kind(main_function.result, &hir_module.types) == .Fallible {
|
||||||
|
success := types.fallible_success(main_function.result, &hir_module.types)
|
||||||
|
channel_slot := ir.instruction_id(len(instructions))
|
||||||
append(&instructions, ir.Instruction{
|
append(&instructions, ir.Instruction{
|
||||||
op=.Return_Void,
|
op=.Alloca, type=main_function.result, target=ir.INVALID_REF,
|
||||||
type=types.VOID,
|
a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
|
||||||
target=ir.INVALID_REF,
|
|
||||||
a=ir.INVALID_INSTRUCTION,
|
|
||||||
b=ir.INVALID_INSTRUCTION,
|
|
||||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
})
|
})
|
||||||
} else {
|
|
||||||
append(&instructions, ir.Instruction{
|
append(&instructions, ir.Instruction{
|
||||||
op=.Return,
|
op=.Store, type=main_function.result, target=ir.INVALID_REF,
|
||||||
type=hir_module.functions[main_index].result,
|
a=channel_slot, b=main_call, diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
target=ir.INVALID_REF,
|
})
|
||||||
a=main_call,
|
code := ir.instruction_id(len(instructions))
|
||||||
b=ir.INVALID_INSTRUCTION,
|
append(&instructions, ir.Instruction{
|
||||||
|
op=.Union_Tag, type=types.U16, target=ir.INVALID_REF,
|
||||||
|
a=channel_slot, b=ir.INVALID_INSTRUCTION, diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
|
})
|
||||||
|
zero_tag := ir.instruction_id(len(instructions))
|
||||||
|
append(&instructions, ir.Instruction{
|
||||||
|
op=.Const, type=types.U16, integer=0, target=ir.INVALID_REF,
|
||||||
|
a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
|
||||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
})
|
})
|
||||||
|
ok := ir.instruction_id(len(instructions))
|
||||||
|
append(&instructions, ir.Instruction{
|
||||||
|
op=.Compare, type=types.BOOL, integer=i64(ir.Compare_Predicate.Eq),
|
||||||
|
target=ir.INVALID_REF, a=code, b=zero_tag,
|
||||||
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
|
})
|
||||||
|
append(&instructions, ir.Instruction{
|
||||||
|
op=.Cond_Br, type=types.VOID, integer=1, target=ir.Ref(0), a=ok,
|
||||||
|
b=ir.INVALID_INSTRUCTION, diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
|
})
|
||||||
|
append(&instructions, ir.Instruction{
|
||||||
|
op=.Label, type=types.VOID, integer=0, target=ir.INVALID_REF,
|
||||||
|
a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
|
||||||
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
|
})
|
||||||
|
failure := ir.instruction_id(len(instructions))
|
||||||
|
append(&instructions, ir.Instruction{
|
||||||
|
op=.Const, type=types.I32, integer=1, target=ir.INVALID_REF,
|
||||||
|
a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
|
||||||
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
|
})
|
||||||
|
append(&instructions, ir.Instruction{
|
||||||
|
op=.Return, type=types.I32, target=ir.INVALID_REF, a=failure,
|
||||||
|
b=ir.INVALID_INSTRUCTION, diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
|
})
|
||||||
|
append(&instructions, ir.Instruction{
|
||||||
|
op=.Label, type=types.VOID, integer=1, target=ir.INVALID_REF,
|
||||||
|
a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
|
||||||
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
|
})
|
||||||
|
success_value := ir.INVALID_INSTRUCTION
|
||||||
|
if types.is_void(success) {
|
||||||
|
success_value = ir.instruction_id(len(instructions))
|
||||||
|
append(&instructions, ir.Instruction{
|
||||||
|
op=.Const, type=types.I32, integer=0, target=ir.INVALID_REF,
|
||||||
|
a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
|
||||||
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
payload := ir.instruction_id(len(instructions))
|
||||||
|
append(&instructions, ir.Instruction{
|
||||||
|
op=.Field_Address, type=success, integer=0, target=ir.INVALID_REF,
|
||||||
|
a=channel_slot, b=ir.INVALID_INSTRUCTION,
|
||||||
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
|
})
|
||||||
|
success_value = ir.instruction_id(len(instructions))
|
||||||
|
append(&instructions, ir.Instruction{
|
||||||
|
op=.Load, type=success, target=ir.INVALID_REF, a=payload,
|
||||||
|
b=ir.INVALID_INSTRUCTION, diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
append(&instructions, ir.Instruction{
|
||||||
|
op=.Return, type=types.I32, target=ir.INVALID_REF, a=success_value,
|
||||||
|
b=ir.INVALID_INSTRUCTION, diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
exit_value := main_call
|
||||||
|
if types.is_void(main_function.result) {
|
||||||
|
exit_value = ir.instruction_id(len(instructions))
|
||||||
|
append(&instructions, ir.Instruction{
|
||||||
|
op=.Const, type=types.I32, integer=0, target=ir.INVALID_REF,
|
||||||
|
a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
|
||||||
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
append(&instructions, ir.Instruction{
|
||||||
|
op=.Return, type=types.I32, target=ir.INVALID_REF, a=exit_value,
|
||||||
|
b=ir.INVALID_INSTRUCTION, diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
problematic := main_function.problematic
|
||||||
|
if provider_ok {
|
||||||
|
problematic = problematic || hir_module.functions[provider_index].problematic
|
||||||
|
}
|
||||||
append(&module.functions, ir.Function{
|
append(&module.functions, ir.Function{
|
||||||
link_name=fmt.aprintf("main", allocator=allocator),
|
link_name=fmt.aprintf("main", allocator=allocator),
|
||||||
calling_convention=.C,
|
calling_convention=.C,
|
||||||
implementation=.Definition,
|
implementation=.Definition,
|
||||||
linkage=.External,
|
linkage=.External,
|
||||||
is_main=true,
|
is_main=true,
|
||||||
result=hir_module.functions[main_index].result,
|
result=types.I32,
|
||||||
instructions=instructions[:],
|
instructions=instructions[:],
|
||||||
problematic=hir_module.functions[main_index].problematic ||
|
problematic=problematic,
|
||||||
hir_module.functions[provider_index].problematic,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1708,6 +1939,7 @@ lower :: proc(hir_module: ^hir.Module, allocator := context.allocator) -> ir.Mod
|
|||||||
link_name=fmt.aprintf("%s", global.link_name, allocator=allocator),
|
link_name=fmt.aprintf("%s", global.link_name, allocator=allocator),
|
||||||
type=global.type,
|
type=global.type,
|
||||||
is_static=global.is_static,
|
is_static=global.is_static,
|
||||||
|
eager=global.eager,
|
||||||
external=global.external,
|
external=global.external,
|
||||||
writable=global.writable,
|
writable=global.writable,
|
||||||
static_value=global.static_value,
|
static_value=global.static_value,
|
||||||
|
|||||||
+818
-137
File diff suppressed because it is too large
Load Diff
+233
-10
@@ -2,6 +2,7 @@ package source
|
|||||||
|
|
||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
import "core:mem"
|
import "core:mem"
|
||||||
|
import "core:strings"
|
||||||
|
|
||||||
Source_Id :: distinct u32
|
Source_Id :: distinct u32
|
||||||
Diagnostic_Id :: distinct u32
|
Diagnostic_Id :: distinct u32
|
||||||
@@ -58,6 +59,20 @@ Diagnostic :: struct {
|
|||||||
severity: Severity,
|
severity: Severity,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Annotation_Kind :: enum u8 {
|
||||||
|
Primary,
|
||||||
|
Secondary,
|
||||||
|
Note,
|
||||||
|
Help,
|
||||||
|
}
|
||||||
|
|
||||||
|
Annotation :: struct {
|
||||||
|
owner: Diagnostic_Id,
|
||||||
|
span: Span,
|
||||||
|
message: string,
|
||||||
|
kind: Annotation_Kind,
|
||||||
|
}
|
||||||
|
|
||||||
Severity :: enum u8 {
|
Severity :: enum u8 {
|
||||||
Error,
|
Error,
|
||||||
Warning,
|
Warning,
|
||||||
@@ -67,6 +82,7 @@ Diagnostics :: struct {
|
|||||||
source: ^Source,
|
source: ^Source,
|
||||||
store: ^Store,
|
store: ^Store,
|
||||||
items: [dynamic]Diagnostic,
|
items: [dynamic]Diagnostic,
|
||||||
|
annotations: [dynamic]Annotation,
|
||||||
index: map[Diagnostic_Key]Diagnostic_Id,
|
index: map[Diagnostic_Key]Diagnostic_Id,
|
||||||
allocator: mem.Allocator,
|
allocator: mem.Allocator,
|
||||||
}
|
}
|
||||||
@@ -130,6 +146,7 @@ init_diagnostics :: proc(source_file: ^Source, allocator := context.allocator) -
|
|||||||
result.source = source_file
|
result.source = source_file
|
||||||
result.allocator = allocator
|
result.allocator = allocator
|
||||||
result.items.allocator = allocator
|
result.items.allocator = allocator
|
||||||
|
result.annotations.allocator = allocator
|
||||||
result.index.allocator = allocator
|
result.index.allocator = allocator
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
@@ -139,6 +156,7 @@ init_store_diagnostics :: proc(store: ^Store, allocator := context.allocator) ->
|
|||||||
result.store = store
|
result.store = store
|
||||||
result.allocator = allocator
|
result.allocator = allocator
|
||||||
result.items.allocator = allocator
|
result.items.allocator = allocator
|
||||||
|
result.annotations.allocator = allocator
|
||||||
result.index.allocator = allocator
|
result.index.allocator = allocator
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
@@ -148,7 +166,11 @@ destroy_diagnostics :: proc(diagnostics: ^Diagnostics) {
|
|||||||
for diagnostic in diagnostics.items {
|
for diagnostic in diagnostics.items {
|
||||||
delete(diagnostic.message, diagnostics.allocator)
|
delete(diagnostic.message, diagnostics.allocator)
|
||||||
}
|
}
|
||||||
|
for annotation in diagnostics.annotations {
|
||||||
|
delete(annotation.message, diagnostics.allocator)
|
||||||
|
}
|
||||||
delete(diagnostics.items)
|
delete(diagnostics.items)
|
||||||
|
delete(diagnostics.annotations)
|
||||||
}
|
}
|
||||||
|
|
||||||
add_with_severity :: proc(diagnostics: ^Diagnostics, span: Span, message: string, severity: Severity) -> Diagnostic_Id {
|
add_with_severity :: proc(diagnostics: ^Diagnostics, span: Span, message: string, severity: Severity) -> Diagnostic_Id {
|
||||||
@@ -192,6 +214,50 @@ addf_warning :: proc(diagnostics: ^Diagnostics, span: Span, format: string, args
|
|||||||
return addf_with_severity(diagnostics, span, .Warning, format, ..args)
|
return addf_with_severity(diagnostics, span, .Warning, format, ..args)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
add_annotation :: proc(
|
||||||
|
diagnostics: ^Diagnostics,
|
||||||
|
owner: Diagnostic_Id,
|
||||||
|
kind: Annotation_Kind,
|
||||||
|
span: Span,
|
||||||
|
message: string,
|
||||||
|
) {
|
||||||
|
if _, ok := diagnostic_index(owner, len(diagnostics.items)); !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for annotation in diagnostics.annotations {
|
||||||
|
if annotation.owner == owner && annotation.kind == kind && annotation.span == span &&
|
||||||
|
annotation.message == message {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
append(&diagnostics.annotations, Annotation{
|
||||||
|
owner=owner,
|
||||||
|
kind=kind,
|
||||||
|
span=span,
|
||||||
|
message=fmt.aprintf("%s", message, allocator=diagnostics.allocator),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
set_primary_label :: proc(diagnostics: ^Diagnostics, owner: Diagnostic_Id, message: string) {
|
||||||
|
index, ok := diagnostic_index(owner, len(diagnostics.items))
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
add_annotation(diagnostics, owner, .Primary, diagnostics.items[index].span, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
add_secondary_label :: proc(diagnostics: ^Diagnostics, owner: Diagnostic_Id, span: Span, message: string) {
|
||||||
|
add_annotation(diagnostics, owner, .Secondary, span, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
add_note :: proc(diagnostics: ^Diagnostics, owner: Diagnostic_Id, message: string) {
|
||||||
|
add_annotation(diagnostics, owner, .Note, {}, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
add_help :: proc(diagnostics: ^Diagnostics, owner: Diagnostic_Id, message: string) {
|
||||||
|
add_annotation(diagnostics, owner, .Help, {}, message)
|
||||||
|
}
|
||||||
|
|
||||||
line_and_column :: proc(source_file: ^Source, offset: Offset) -> (line, column: int) {
|
line_and_column :: proc(source_file: ^Source, offset: Offset) -> (line, column: int) {
|
||||||
if len(source_file.line_starts) > 0 {
|
if len(source_file.line_starts) > 0 {
|
||||||
limit := min(int(offset), len(source_file.text))
|
limit := min(int(offset), len(source_file.text))
|
||||||
@@ -232,6 +298,140 @@ source_for_span :: proc(diagnostics: ^Diagnostics, span: Span) -> ^Source {
|
|||||||
return diagnostics.source
|
return diagnostics.source
|
||||||
}
|
}
|
||||||
|
|
||||||
|
annotation_for :: proc(diagnostics: ^Diagnostics, owner: Diagnostic_Id, kind: Annotation_Kind) -> (Annotation, bool) {
|
||||||
|
for annotation in diagnostics.annotations {
|
||||||
|
if annotation.owner == owner && annotation.kind == kind {
|
||||||
|
return annotation, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
line_bounds :: proc(source_file: ^Source, line: int) -> (start, end: int, ok: bool) {
|
||||||
|
if line <= 0 {
|
||||||
|
return 0, 0, false
|
||||||
|
}
|
||||||
|
if len(source_file.line_starts) == 0 {
|
||||||
|
current := 1
|
||||||
|
start = 0
|
||||||
|
for value, index in transmute([]byte)source_file.text {
|
||||||
|
if current == line && value == '\n' {
|
||||||
|
end = index
|
||||||
|
if end > start && source_file.text[end-1] == '\r' {
|
||||||
|
end -= 1
|
||||||
|
}
|
||||||
|
return start, end, true
|
||||||
|
}
|
||||||
|
if value == '\n' {
|
||||||
|
current += 1
|
||||||
|
start = index+1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if current == line {
|
||||||
|
return start, len(source_file.text), true
|
||||||
|
}
|
||||||
|
return 0, 0, false
|
||||||
|
}
|
||||||
|
if line > len(source_file.line_starts) {
|
||||||
|
return 0, 0, false
|
||||||
|
}
|
||||||
|
start = int(source_file.line_starts[line-1])
|
||||||
|
end = len(source_file.text)
|
||||||
|
if line < len(source_file.line_starts) {
|
||||||
|
end = int(source_file.line_starts[line])-1
|
||||||
|
}
|
||||||
|
if end > start && source_file.text[end-1] == '\r' {
|
||||||
|
end -= 1
|
||||||
|
}
|
||||||
|
return start, end, true
|
||||||
|
}
|
||||||
|
|
||||||
|
write_spaces :: proc(builder: ^strings.Builder, count: int) {
|
||||||
|
for _ in 0..<max(count, 0) {
|
||||||
|
strings.write_byte(builder, ' ')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
write_expanded :: proc(builder: ^strings.Builder, text: string, start_column := 0) -> int {
|
||||||
|
column := start_column
|
||||||
|
for value in transmute([]byte)text {
|
||||||
|
if value == '\t' {
|
||||||
|
width := 4-column%4
|
||||||
|
write_spaces(builder, width)
|
||||||
|
column += width
|
||||||
|
} else {
|
||||||
|
strings.write_byte(builder, value)
|
||||||
|
column += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return column
|
||||||
|
}
|
||||||
|
|
||||||
|
display_width :: proc(text: string, start_column := 0) -> int {
|
||||||
|
column := start_column
|
||||||
|
for value in transmute([]byte)text {
|
||||||
|
column += 4-column%4 if value == '\t' else 1
|
||||||
|
}
|
||||||
|
return column-start_column
|
||||||
|
}
|
||||||
|
|
||||||
|
decimal_width :: proc(value: int) -> int {
|
||||||
|
width := 1
|
||||||
|
for remaining := value; remaining >= 10; remaining /= 10 {
|
||||||
|
width += 1
|
||||||
|
}
|
||||||
|
return width
|
||||||
|
}
|
||||||
|
|
||||||
|
write_excerpt :: proc(
|
||||||
|
builder: ^strings.Builder,
|
||||||
|
diagnostics: ^Diagnostics,
|
||||||
|
span: Span,
|
||||||
|
label: string,
|
||||||
|
primary: bool,
|
||||||
|
) -> bool {
|
||||||
|
if span == (Span{}) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
source_file := source_for_span(diagnostics, span)
|
||||||
|
if source_file == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
line, column := line_and_column(source_file, span.start)
|
||||||
|
line_start, line_end, ok := line_bounds(source_file, line)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
prefix := " -->" if primary else " :::"
|
||||||
|
fmt.sbprintf(builder, "%s %s:%d:%d\n", prefix, source_file.path, line, column)
|
||||||
|
gutter := decimal_width(line)
|
||||||
|
write_spaces(builder, gutter+1)
|
||||||
|
strings.write_string(builder, "|\n")
|
||||||
|
fmt.sbprintf(builder, "%*d | ", gutter, line)
|
||||||
|
_ = write_expanded(builder, source_file.text[line_start:line_end])
|
||||||
|
strings.write_byte(builder, '\n')
|
||||||
|
write_spaces(builder, gutter+1)
|
||||||
|
strings.write_string(builder, "| ")
|
||||||
|
start := clamp(int(span.start), line_start, line_end)
|
||||||
|
indent := display_width(source_file.text[line_start:start])
|
||||||
|
width := 1
|
||||||
|
if start < line_end {
|
||||||
|
end := clamp(int(span.end), start+1, line_end)
|
||||||
|
width = max(display_width(source_file.text[start:end], indent), 1)
|
||||||
|
}
|
||||||
|
write_spaces(builder, indent)
|
||||||
|
marker := u8('^') if primary else u8('-')
|
||||||
|
for _ in 0..<width {
|
||||||
|
strings.write_byte(builder, marker)
|
||||||
|
}
|
||||||
|
if len(label) > 0 {
|
||||||
|
strings.write_byte(builder, ' ')
|
||||||
|
strings.write_string(builder, label)
|
||||||
|
}
|
||||||
|
strings.write_byte(builder, '\n')
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
format :: proc(diagnostics: ^Diagnostics, id: Diagnostic_Id, allocator := context.allocator) -> string {
|
format :: proc(diagnostics: ^Diagnostics, id: Diagnostic_Id, allocator := context.allocator) -> string {
|
||||||
index, ok := diagnostic_index(id, len(diagnostics.items))
|
index, ok := diagnostic_index(id, len(diagnostics.items))
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -246,19 +446,42 @@ format :: proc(diagnostics: ^Diagnostics, id: Diagnostic_Id, allocator := contex
|
|||||||
diagnostic := diagnostics.items[index]
|
diagnostic := diagnostics.items[index]
|
||||||
source_file := source_for_span(diagnostics, diagnostic.span)
|
source_file := source_for_span(diagnostics, diagnostic.span)
|
||||||
severity := "warning" if diagnostic.severity == .Warning else "error"
|
severity := "warning" if diagnostic.severity == .Warning else "error"
|
||||||
|
if diagnostic.span == (Span{}) {
|
||||||
|
path := source_file.path if source_file != nil else "<unknown>"
|
||||||
|
return fmt.aprintf("%s: %s: %s", path, severity, diagnostic.message, allocator=allocator)
|
||||||
|
}
|
||||||
if source_file == nil {
|
if source_file == nil {
|
||||||
return fmt.aprintf("<unknown>: %s: %s", severity, diagnostic.message, allocator=allocator)
|
return fmt.aprintf("<unknown>: %s: %s", severity, diagnostic.message, allocator=allocator)
|
||||||
}
|
}
|
||||||
line, column := line_and_column(source_file, diagnostic.span.start)
|
builder := strings.builder_make(allocator)
|
||||||
return fmt.aprintf(
|
fmt.sbprintf(&builder, "%s: %s\n", severity, diagnostic.message)
|
||||||
"%s:%d:%d: %s: %s",
|
primary_label := ""
|
||||||
source_file.path,
|
if annotation, found := annotation_for(diagnostics, id, .Primary); found {
|
||||||
line,
|
primary_label = annotation.message
|
||||||
column,
|
}
|
||||||
severity,
|
_ = write_excerpt(&builder, diagnostics, diagnostic.span, primary_label, true)
|
||||||
diagnostic.message,
|
for annotation in diagnostics.annotations {
|
||||||
allocator=allocator,
|
if annotation.owner == id && annotation.kind == .Secondary {
|
||||||
)
|
_ = write_excerpt(&builder, diagnostics, annotation.span, annotation.message, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for annotation in diagnostics.annotations {
|
||||||
|
if annotation.owner != id {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
#partial switch annotation.kind {
|
||||||
|
case .Note:
|
||||||
|
fmt.sbprintf(&builder, "note: %s\n", annotation.message)
|
||||||
|
case .Help:
|
||||||
|
fmt.sbprintf(&builder, "help: %s\n", annotation.message)
|
||||||
|
case:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result := strings.to_string(builder)
|
||||||
|
if len(result) > 0 && result[len(result)-1] == '\n' {
|
||||||
|
return result[:len(result)-1]
|
||||||
|
}
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
print_all :: proc(diagnostics: ^Diagnostics) {
|
print_all :: proc(diagnostics: ^Diagnostics) {
|
||||||
|
|||||||
@@ -0,0 +1,296 @@
|
|||||||
|
package compiler
|
||||||
|
|
||||||
|
import "./ast"
|
||||||
|
import "./lexer"
|
||||||
|
import "./parser"
|
||||||
|
import "./source"
|
||||||
|
import "./symbol"
|
||||||
|
import "./types"
|
||||||
|
import "core:fmt"
|
||||||
|
import "core:path/filepath"
|
||||||
|
import "core:slice"
|
||||||
|
import "core:strings"
|
||||||
|
|
||||||
|
Test_Entry :: struct {
|
||||||
|
function: ast.Function_Id,
|
||||||
|
package_path: string,
|
||||||
|
source_path: string,
|
||||||
|
offset: source.Offset,
|
||||||
|
}
|
||||||
|
|
||||||
|
test_entry_less :: proc(a, b: Test_Entry) -> bool {
|
||||||
|
if a.package_path != b.package_path {
|
||||||
|
return a.package_path < b.package_path
|
||||||
|
}
|
||||||
|
if a.source_path != b.source_path {
|
||||||
|
return a.source_path < b.source_path
|
||||||
|
}
|
||||||
|
return a.offset < b.offset
|
||||||
|
}
|
||||||
|
|
||||||
|
testing_package :: proc(module: ^ast.Module, project_root: string) -> ast.Package_Id {
|
||||||
|
path, err := filepath.join({project_root, "std", "testing"}, module.allocator)
|
||||||
|
if err != nil {
|
||||||
|
return ast.INVALID_PACKAGE
|
||||||
|
}
|
||||||
|
defer delete(path, module.allocator)
|
||||||
|
canonical, ok := filepath.abs(path, module.allocator)
|
||||||
|
if !ok {
|
||||||
|
return ast.INVALID_PACKAGE
|
||||||
|
}
|
||||||
|
defer delete(canonical, module.allocator)
|
||||||
|
for pkg, index in module.packages {
|
||||||
|
if pkg.path == canonical {
|
||||||
|
return ast.package_id(index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ast.INVALID_PACKAGE
|
||||||
|
}
|
||||||
|
|
||||||
|
source_file_id :: proc(module: ^ast.Module, id: source.Source_Id) -> ast.File_Id {
|
||||||
|
for file, index in module.files {
|
||||||
|
if file.source == id {
|
||||||
|
return ast.file_id(index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ast.INVALID_FILE
|
||||||
|
}
|
||||||
|
|
||||||
|
append_ast_expr :: proc(module: ^ast.Module, expr: ast.Expr) -> ast.Expr_Id {
|
||||||
|
id := ast.expr_id(len(module.exprs))
|
||||||
|
append(&module.exprs, expr)
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
|
||||||
|
location_expr :: proc(
|
||||||
|
module: ^ast.Module,
|
||||||
|
sources: ^source.Store,
|
||||||
|
symbols: ^symbol.Table,
|
||||||
|
span: source.Span,
|
||||||
|
qualifier: symbol.Id,
|
||||||
|
) -> ast.Expr_Id {
|
||||||
|
line, column := 1, 1
|
||||||
|
path := "<unknown>"
|
||||||
|
if int(span.file) < len(sources.items) {
|
||||||
|
file := &sources.items[span.file]
|
||||||
|
line, column = source.line_and_column(file, span.start)
|
||||||
|
path = file.path
|
||||||
|
}
|
||||||
|
string_index := u64(len(module.strings))
|
||||||
|
append(&module.strings, strings.clone(path, module.allocator))
|
||||||
|
file_expr := append_ast_expr(module, ast.Expr{
|
||||||
|
kind=.String, span=span, integer=string_index,
|
||||||
|
left=ast.INVALID_EXPR, right=ast.INVALID_EXPR,
|
||||||
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
|
})
|
||||||
|
line_expr := append_ast_expr(module, ast.Expr{
|
||||||
|
kind=.Integer, span=span, integer=u64(line),
|
||||||
|
left=ast.INVALID_EXPR, right=ast.INVALID_EXPR,
|
||||||
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
|
})
|
||||||
|
column_expr := append_ast_expr(module, ast.Expr{
|
||||||
|
kind=.Integer, span=span, integer=u64(column),
|
||||||
|
left=ast.INVALID_EXPR, right=ast.INVALID_EXPR,
|
||||||
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
|
})
|
||||||
|
values := []ast.Expr_Id{file_expr, line_expr, column_expr}
|
||||||
|
names := []string{"file", "line", "column"}
|
||||||
|
fields := make([]ast.Expr_Id, 3, module.allocator)
|
||||||
|
for index in 0..<3 {
|
||||||
|
fields[index] = append_ast_expr(module, ast.Expr{
|
||||||
|
kind=.Keyed, span=span,
|
||||||
|
name=symbol.intern(symbols, names[index]),
|
||||||
|
left=values[index], right=ast.INVALID_EXPR,
|
||||||
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return append_ast_expr(module, ast.Expr{
|
||||||
|
kind=.Struct_Literal, span=span,
|
||||||
|
qualifier=qualifier,
|
||||||
|
name=symbol.intern(symbols, "SourceLocation"),
|
||||||
|
args=fields,
|
||||||
|
left=ast.INVALID_EXPR, right=ast.INVALID_EXPR,
|
||||||
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
inject_assertion_locations :: proc(
|
||||||
|
module: ^ast.Module,
|
||||||
|
sources: ^source.Store,
|
||||||
|
symbols: ^symbol.Table,
|
||||||
|
testing_pkg: ast.Package_Id,
|
||||||
|
) {
|
||||||
|
expect := symbol.intern(symbols, "expect")
|
||||||
|
expect_equal := symbol.intern(symbols, "expect_equal")
|
||||||
|
expect_type := symbol.intern(symbols, "expect_type")
|
||||||
|
original_count := len(module.exprs)
|
||||||
|
for index in 0..<original_count {
|
||||||
|
expr := &module.exprs[index]
|
||||||
|
if expr.kind != .Call || expr.intrinsic || !symbol.is_valid(expr.qualifier) ||
|
||||||
|
(expr.name != expect && expr.name != expect_equal && expr.name != expect_type) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
file := source_file_id(module, expr.span.file)
|
||||||
|
matched := false
|
||||||
|
for import_item in module.imports {
|
||||||
|
if !import_item.test_only && import_item.file == file &&
|
||||||
|
import_item.alias == expr.qualifier && import_item.target == testing_pkg {
|
||||||
|
matched = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !matched {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
location := location_expr(module, sources, symbols, expr.span, expr.qualifier)
|
||||||
|
expr = &module.exprs[index]
|
||||||
|
args := make([]ast.Expr_Id, len(expr.args)+1, module.allocator)
|
||||||
|
copy(args, expr.args)
|
||||||
|
args[len(expr.args)] = location
|
||||||
|
delete(expr.args, module.allocator)
|
||||||
|
expr.args = args
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
write_brolang_string :: proc(builder: ^strings.Builder, value: string) {
|
||||||
|
for byte_value in transmute([]byte)value {
|
||||||
|
if byte_value == '\\' || byte_value == '"' {
|
||||||
|
strings.write_byte(builder, '\\')
|
||||||
|
}
|
||||||
|
strings.write_byte(builder, byte_value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
append_runner :: proc(
|
||||||
|
module: ^ast.Module,
|
||||||
|
sources: ^source.Store,
|
||||||
|
diagnostics: ^source.Diagnostics,
|
||||||
|
symbols: ^symbol.Table,
|
||||||
|
testing_pkg: ast.Package_Id,
|
||||||
|
testing_error: types.Type,
|
||||||
|
tests: []Test_Entry,
|
||||||
|
) {
|
||||||
|
builder := strings.builder_make(module.allocator)
|
||||||
|
defer strings.builder_destroy(&builder)
|
||||||
|
for entry, index in tests {
|
||||||
|
test := module.functions[entry.function]
|
||||||
|
alias := fmt.tprintf("__brolang_test_%d", index)
|
||||||
|
fmt.sbprintf(&builder, "@hide __brolang_test_adapter_%d func() void ! __brolang_testing.Error ", index)
|
||||||
|
strings.write_string(&builder, "{\n\t")
|
||||||
|
fmt.sbprintf(&builder, "%s.%s() catch |_| ", alias, symbol.resolve(symbols, test.name))
|
||||||
|
strings.write_string(&builder, "{\n\t\treturn .expectation_failed\n\t}\n}\n\n")
|
||||||
|
}
|
||||||
|
strings.write_string(&builder, "main func() i32 {\n\tfailed i32 := 0\n")
|
||||||
|
for entry, index in tests {
|
||||||
|
test_id := entry.function
|
||||||
|
test := module.functions[test_id]
|
||||||
|
pkg := module.packages[test.pkg]
|
||||||
|
name := fmt.tprintf("%s.%s", filepath.base(pkg.path), symbol.resolve(symbols, test.name))
|
||||||
|
strings.write_string(&builder, "\tif (!__brolang_testing.run(\"")
|
||||||
|
write_brolang_string(&builder, name)
|
||||||
|
fmt.sbprintf(&builder, "\", __brolang_test_adapter_%d)) ", index)
|
||||||
|
strings.write_string(&builder, "{\n\t\tfailed += 1\n\t}\n")
|
||||||
|
}
|
||||||
|
fmt.sbprintf(&builder, "\t__brolang_testing.summary(%d - failed, failed)\n", len(tests))
|
||||||
|
strings.write_string(&builder, "\tif (failed != 0) return 1\n\treturn 0\n}\n")
|
||||||
|
|
||||||
|
runner_text := strings.to_string(builder)
|
||||||
|
source_id := source.add_source(sources, "<brolang-test-runner>", runner_text)
|
||||||
|
file_id := ast.file_id(len(module.files))
|
||||||
|
append(&module.files, ast.File{source=source_id, pkg=0})
|
||||||
|
stream := lexer.lex(&sources.items[source_id], diagnostics, symbols, module.allocator)
|
||||||
|
defer delete(stream.items)
|
||||||
|
function_start := len(module.functions)
|
||||||
|
parser.parse_into(&stream, &sources.items[source_id], diagnostics, module, 0, file_id)
|
||||||
|
for index in 0..<len(tests) {
|
||||||
|
module.functions[function_start+index].error = testing_error
|
||||||
|
}
|
||||||
|
|
||||||
|
append(&module.imports, ast.Import{
|
||||||
|
alias=symbol.intern(symbols, "__brolang_testing"),
|
||||||
|
path=strings.clone("@std/testing", module.allocator),
|
||||||
|
pkg=0, file=file_id, target=testing_pkg,
|
||||||
|
valid=true, used=true,
|
||||||
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
|
})
|
||||||
|
for entry, index in tests {
|
||||||
|
test_id := entry.function
|
||||||
|
test := module.functions[test_id]
|
||||||
|
append(&module.imports, ast.Import{
|
||||||
|
alias=symbol.intern(symbols, fmt.tprintf("__brolang_test_%d", index)),
|
||||||
|
path=strings.clone(module.packages[test.pkg].path, module.allocator),
|
||||||
|
pkg=0, file=file_id, target=test.pkg,
|
||||||
|
valid=true, used=true,
|
||||||
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
prepare_tests :: proc(
|
||||||
|
module: ^ast.Module,
|
||||||
|
sources: ^source.Store,
|
||||||
|
diagnostics: ^source.Diagnostics,
|
||||||
|
symbols: ^symbol.Table,
|
||||||
|
mode: ast.Compile_Mode,
|
||||||
|
project_root: string,
|
||||||
|
) -> bool {
|
||||||
|
if mode == .Executable {
|
||||||
|
for &function in module.functions {
|
||||||
|
if !function.test {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
function.generated = true
|
||||||
|
for &import_item in module.imports {
|
||||||
|
if !import_item.test_only && import_item.file == function.file {
|
||||||
|
import_item.used = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
testing_pkg := testing_package(module, project_root)
|
||||||
|
if testing_pkg == ast.INVALID_PACKAGE {
|
||||||
|
source.add(diagnostics, source.Span{}, "test builds require @std/testing")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
error_type := types.find_named(
|
||||||
|
&module.type_store,
|
||||||
|
u32(testing_pkg),
|
||||||
|
u32(symbol.intern(symbols, "Error")),
|
||||||
|
)
|
||||||
|
if !types.is_enum(error_type, &module.type_store) {
|
||||||
|
source.add(diagnostics, source.Span{}, "@std/testing must declare Error as an enum")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
tests: [dynamic]Test_Entry
|
||||||
|
tests.allocator = module.allocator
|
||||||
|
defer delete(tests)
|
||||||
|
main_name := symbol.intern(symbols, "main")
|
||||||
|
for &function, index in module.functions {
|
||||||
|
if function.test {
|
||||||
|
if int(function.pkg) < len(module.packages) && module.packages[function.pkg].test {
|
||||||
|
function.result = types.VOID
|
||||||
|
function.error = error_type
|
||||||
|
file := module.files[function.file]
|
||||||
|
append(&tests, Test_Entry{
|
||||||
|
function=ast.function_id(index),
|
||||||
|
package_path=module.packages[function.pkg].path,
|
||||||
|
source_path=sources.items[file.source].path,
|
||||||
|
offset=function.span.start,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
function.generated = true
|
||||||
|
}
|
||||||
|
} else if function.pkg == 0 && function.name == main_name {
|
||||||
|
function.generated = true
|
||||||
|
function.analysis_root = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
slice.sort_by(tests[:], test_entry_less)
|
||||||
|
inject_assertion_locations(module, sources, symbols, testing_pkg)
|
||||||
|
append_runner(module, sources, diagnostics, symbols, testing_pkg, error_type, tests[:])
|
||||||
|
return true
|
||||||
|
}
|
||||||
@@ -16,14 +16,21 @@ Kind :: enum u8 {
|
|||||||
Underscore,
|
Underscore,
|
||||||
Colon,
|
Colon,
|
||||||
Colon_Colon,
|
Colon_Colon,
|
||||||
|
Colon_Equal,
|
||||||
Equal,
|
Equal,
|
||||||
Equal_Equal,
|
Equal_Equal,
|
||||||
Bang,
|
Bang,
|
||||||
Bang_Equal,
|
Bang_Equal,
|
||||||
Less,
|
Less,
|
||||||
Less_Equal,
|
Less_Equal,
|
||||||
|
Less_Less,
|
||||||
|
Less_Less_Equal,
|
||||||
|
Less_Less_Pipe,
|
||||||
|
Less_Less_Pipe_Equal,
|
||||||
Greater,
|
Greater,
|
||||||
Greater_Equal,
|
Greater_Equal,
|
||||||
|
Greater_Greater,
|
||||||
|
Greater_Greater_Equal,
|
||||||
Plus,
|
Plus,
|
||||||
Minus,
|
Minus,
|
||||||
Slash,
|
Slash,
|
||||||
@@ -39,7 +46,10 @@ Kind :: enum u8 {
|
|||||||
Dollar,
|
Dollar,
|
||||||
Star,
|
Star,
|
||||||
Ampersand,
|
Ampersand,
|
||||||
|
Ampersand_Equal,
|
||||||
Caret,
|
Caret,
|
||||||
|
Tilde,
|
||||||
|
Xor_Equal,
|
||||||
Question,
|
Question,
|
||||||
Semicolon,
|
Semicolon,
|
||||||
Left_Bracket,
|
Left_Bracket,
|
||||||
@@ -50,6 +60,8 @@ Kind :: enum u8 {
|
|||||||
Right_Brace,
|
Right_Brace,
|
||||||
Comma,
|
Comma,
|
||||||
Pipe,
|
Pipe,
|
||||||
|
Pipe_Equal,
|
||||||
|
Keyword_Test,
|
||||||
Keyword_Func,
|
Keyword_Func,
|
||||||
Keyword_C_Func,
|
Keyword_C_Func,
|
||||||
Keyword_Struct,
|
Keyword_Struct,
|
||||||
@@ -64,14 +76,16 @@ Kind :: enum u8 {
|
|||||||
Keyword_Try,
|
Keyword_Try,
|
||||||
Keyword_Catch,
|
Keyword_Catch,
|
||||||
Keyword_Mut,
|
Keyword_Mut,
|
||||||
Keyword_None,
|
Keyword_Null,
|
||||||
Keyword_Undefined,
|
Keyword_Undefined,
|
||||||
Keyword_Orelse,
|
Keyword_Orelse,
|
||||||
Keyword_And,
|
Keyword_And,
|
||||||
Keyword_Or,
|
Keyword_Or,
|
||||||
|
Keyword_Xor,
|
||||||
Keyword_If,
|
Keyword_If,
|
||||||
Keyword_While,
|
Keyword_While,
|
||||||
Keyword_For,
|
Keyword_For,
|
||||||
|
Keyword_Inline,
|
||||||
Keyword_Break,
|
Keyword_Break,
|
||||||
Keyword_Continue,
|
Keyword_Continue,
|
||||||
Keyword_Defer,
|
Keyword_Defer,
|
||||||
@@ -82,9 +96,12 @@ Kind :: enum u8 {
|
|||||||
Keyword_True,
|
Keyword_True,
|
||||||
Keyword_False,
|
Keyword_False,
|
||||||
Keyword_Void,
|
Keyword_Void,
|
||||||
|
Keyword_Noreturn,
|
||||||
Keyword_Anyopaque,
|
Keyword_Anyopaque,
|
||||||
|
Keyword_Unreachable,
|
||||||
Keyword_Bool,
|
Keyword_Bool,
|
||||||
Keyword_Int,
|
Keyword_Int,
|
||||||
|
Keyword_Uint,
|
||||||
Keyword_Float,
|
Keyword_Float,
|
||||||
Keyword_Range,
|
Keyword_Range,
|
||||||
Keyword_I8,
|
Keyword_I8,
|
||||||
@@ -116,7 +133,7 @@ Kind :: enum u8 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
is_keyword :: proc(kind: Kind) -> bool {
|
is_keyword :: proc(kind: Kind) -> bool {
|
||||||
return kind >= .Keyword_Func && kind <= .Keyword_C_Longdouble
|
return kind >= .Keyword_Test && kind <= .Keyword_C_Longdouble
|
||||||
}
|
}
|
||||||
|
|
||||||
Token :: struct {
|
Token :: struct {
|
||||||
|
|||||||
@@ -17,32 +17,206 @@ import "core:fmt"
|
|||||||
import "core:mem"
|
import "core:mem"
|
||||||
import "core:strings"
|
import "core:strings"
|
||||||
|
|
||||||
|
Package_Input :: struct {
|
||||||
|
result: ^cimport.Result,
|
||||||
|
header: string,
|
||||||
|
name: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
Package_Output :: struct {
|
||||||
|
name: string,
|
||||||
|
source: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
Declaration_Kind :: enum u8 {
|
||||||
|
Record,
|
||||||
|
Alias,
|
||||||
|
Macro,
|
||||||
|
Function,
|
||||||
|
}
|
||||||
|
|
||||||
|
Declaration :: struct {
|
||||||
|
name: string,
|
||||||
|
canonical: string,
|
||||||
|
kind: Declaration_Kind,
|
||||||
|
}
|
||||||
|
|
||||||
|
Declaration_Registry :: struct {
|
||||||
|
lookup: map[string]int,
|
||||||
|
declarations: [dynamic]Declaration,
|
||||||
|
error: string,
|
||||||
|
allocator: mem.Allocator,
|
||||||
|
}
|
||||||
|
|
||||||
|
init_declaration_registry :: proc(allocator: mem.Allocator) -> Declaration_Registry {
|
||||||
|
registry := Declaration_Registry{allocator=allocator}
|
||||||
|
registry.lookup.allocator = allocator
|
||||||
|
registry.declarations.allocator = allocator
|
||||||
|
return registry
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy_declaration_registry :: proc(registry: ^Declaration_Registry) {
|
||||||
|
for declaration in registry.declarations {
|
||||||
|
delete(declaration.name, registry.allocator)
|
||||||
|
delete(declaration.canonical, registry.allocator)
|
||||||
|
}
|
||||||
|
delete(registry.error, registry.allocator)
|
||||||
|
delete(registry.lookup)
|
||||||
|
delete(registry.declarations)
|
||||||
|
}
|
||||||
|
|
||||||
|
write_declaration :: proc(
|
||||||
|
b: ^strings.Builder,
|
||||||
|
registry: ^Declaration_Registry,
|
||||||
|
name: string,
|
||||||
|
kind: Declaration_Kind,
|
||||||
|
actual: string,
|
||||||
|
canonical := "",
|
||||||
|
) -> (emitted, ok: bool) {
|
||||||
|
if registry == nil {
|
||||||
|
strings.write_string(b, actual)
|
||||||
|
return true, true
|
||||||
|
}
|
||||||
|
comparison := canonical if len(canonical) > 0 else actual
|
||||||
|
if index, found := registry.lookup[name]; found {
|
||||||
|
previous := registry.declarations[index]
|
||||||
|
if previous.kind == kind && previous.canonical == comparison {
|
||||||
|
return false, true
|
||||||
|
}
|
||||||
|
if len(registry.error) == 0 {
|
||||||
|
registry.error = fmt.aprintf(
|
||||||
|
"conflicting generated C declaration '%s'",
|
||||||
|
name,
|
||||||
|
allocator=registry.allocator,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return false, false
|
||||||
|
}
|
||||||
|
owned_name := strings.clone(name, registry.allocator)
|
||||||
|
owned_canonical := strings.clone(comparison, registry.allocator)
|
||||||
|
registry.lookup[owned_name] = len(registry.declarations)
|
||||||
|
append(®istry.declarations, Declaration{
|
||||||
|
name=owned_name,
|
||||||
|
canonical=owned_canonical,
|
||||||
|
kind=kind,
|
||||||
|
})
|
||||||
|
strings.write_string(b, actual)
|
||||||
|
return true, true
|
||||||
|
}
|
||||||
|
|
||||||
emit :: proc(result: ^cimport.Result, header: string, allocator := context.allocator) -> string {
|
emit :: proc(result: ^cimport.Result, header: string, allocator := context.allocator) -> string {
|
||||||
|
record_names := record_name_table(result, allocator)
|
||||||
|
defer destroy_record_name_table(record_names, allocator)
|
||||||
|
output, ok := emit_with_record_names(result, header, record_names, nil, allocator)
|
||||||
|
assert(ok)
|
||||||
|
return output
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy_package_outputs :: proc(outputs: []Package_Output, allocator := context.allocator) {
|
||||||
|
for output in outputs {
|
||||||
|
delete(output.name, allocator)
|
||||||
|
delete(output.source, allocator)
|
||||||
|
}
|
||||||
|
delete(outputs, allocator)
|
||||||
|
}
|
||||||
|
|
||||||
|
emit_package :: proc(inputs: []Package_Input, allocator := context.allocator) -> ([]Package_Output, string) {
|
||||||
|
registry := init_declaration_registry(allocator)
|
||||||
|
defer destroy_declaration_registry(®istry)
|
||||||
|
|
||||||
|
tables := make([][]string, len(inputs), allocator)
|
||||||
|
defer {
|
||||||
|
for table in tables {
|
||||||
|
destroy_record_name_table(table, allocator)
|
||||||
|
}
|
||||||
|
delete(tables, allocator)
|
||||||
|
}
|
||||||
|
for input, index in inputs {
|
||||||
|
tables[index] = record_name_table(input.result, allocator, input.name)
|
||||||
|
}
|
||||||
|
identities: map[string]string
|
||||||
|
identities.allocator = allocator
|
||||||
|
defer delete(identities)
|
||||||
|
for input, input_index in inputs {
|
||||||
|
for record, record_index in input.result.records {
|
||||||
|
if len(record.identity) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if canonical, found := identities[record.identity]; found {
|
||||||
|
delete(tables[input_index][record_index], allocator)
|
||||||
|
tables[input_index][record_index] = strings.clone(canonical, allocator)
|
||||||
|
} else {
|
||||||
|
identities[record.identity] = tables[input_index][record_index]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
outputs: [dynamic]Package_Output
|
||||||
|
outputs.allocator = allocator
|
||||||
|
for input, index in inputs {
|
||||||
|
source, ok := emit_with_record_names(
|
||||||
|
input.result,
|
||||||
|
input.header,
|
||||||
|
tables[index],
|
||||||
|
®istry,
|
||||||
|
allocator,
|
||||||
|
)
|
||||||
|
if !ok {
|
||||||
|
delete(source, allocator)
|
||||||
|
destroy_package_outputs(outputs[:], allocator)
|
||||||
|
return nil, strings.clone(registry.error, allocator)
|
||||||
|
}
|
||||||
|
append(&outputs, Package_Output{
|
||||||
|
name=strings.clone(input.name, allocator),
|
||||||
|
source=source,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return outputs[:], ""
|
||||||
|
}
|
||||||
|
|
||||||
|
emit_with_record_names :: proc(
|
||||||
|
result: ^cimport.Result,
|
||||||
|
header: string,
|
||||||
|
record_names: []string,
|
||||||
|
registry: ^Declaration_Registry,
|
||||||
|
allocator: mem.Allocator,
|
||||||
|
) -> (string, bool) {
|
||||||
b := strings.builder_make(allocator)
|
b := strings.builder_make(allocator)
|
||||||
fmt.sbprintf(&b, "# generated by brolang translate-c from %s\n\n", header)
|
fmt.sbprintf(&b, "# generated by brolang translate-c from %s\n\n", header)
|
||||||
|
|
||||||
record_names := record_name_table(result, allocator)
|
if !emit_records(&b, result, record_names, registry) ||
|
||||||
defer delete(record_names, allocator)
|
!emit_aliases(&b, result, record_names, registry) ||
|
||||||
|
!emit_macros(&b, result, record_names, registry) ||
|
||||||
emit_records(&b, result, record_names)
|
!emit_functions(&b, result, record_names, registry) {
|
||||||
emit_aliases(&b, result, record_names)
|
return strings.to_string(b), false
|
||||||
emit_macros(&b, result, record_names)
|
}
|
||||||
emit_functions(&b, result, record_names)
|
|
||||||
emit_variables(&b, result)
|
emit_variables(&b, result)
|
||||||
emit_unsupported(&b, result)
|
emit_unsupported(&b, result)
|
||||||
|
|
||||||
return strings.to_string(b)
|
return strings.to_string(b), true
|
||||||
}
|
}
|
||||||
|
|
||||||
// record_name_table maps each record index to the brolang identifier it is
|
// record_name_table maps each record index to the brolang identifier it is
|
||||||
// emitted under: its C tag name, or — for an anonymous record named only by a
|
// emitted under: its C tag name, or — for an anonymous record named only by a
|
||||||
// typedef — that typedef's name (the typedef is then skipped). Truly anonymous
|
// typedef — that typedef's name (the typedef is then skipped). Truly anonymous
|
||||||
// records fall back to the loader's synthetic `__c_record_N`.
|
// records fall back to the loader's synthetic `__c_record_N`.
|
||||||
record_name_table :: proc(result: ^cimport.Result, allocator: mem.Allocator) -> []string {
|
record_name_table :: proc(result: ^cimport.Result, allocator: mem.Allocator, prefix := "") -> []string {
|
||||||
names := make([]string, len(result.records), allocator)
|
names := make([]string, len(result.records), allocator)
|
||||||
for record, idx in result.records {
|
for record, idx in result.records {
|
||||||
if len(record.name) > 0 {
|
if binding_identifier(record.name) {
|
||||||
names[idx] = record.name
|
if record_name_conflicts(result, idx, record.name) {
|
||||||
|
fragment := safe_name_fragment(prefix, context.temp_allocator)
|
||||||
|
if len(fragment) > 0 {
|
||||||
|
names[idx] = fmt.aprintf("__c_%s_%s_record", fragment, record.name, allocator=allocator)
|
||||||
|
} else {
|
||||||
|
names[idx] = fmt.aprintf("__c_%s_record", record.name, allocator=allocator)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
names[idx] = strings.clone(record.name, allocator)
|
||||||
|
}
|
||||||
|
} else if len(prefix) > 0 {
|
||||||
|
fragment := safe_name_fragment(prefix, context.temp_allocator)
|
||||||
|
names[idx] = fmt.aprintf("__c_%s_record_%d", fragment, idx, allocator=allocator)
|
||||||
} else {
|
} else {
|
||||||
names[idx] = fmt.aprintf("__c_record_%d", idx, allocator=allocator)
|
names[idx] = fmt.aprintf("__c_record_%d", idx, allocator=allocator)
|
||||||
}
|
}
|
||||||
@@ -59,13 +233,73 @@ record_name_table :: proc(result: ^cimport.Result, allocator: mem.Allocator) ->
|
|||||||
if target.kind != .Record || int(target.record) >= len(result.records) {
|
if target.kind != .Record || int(target.record) >= len(result.records) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if len(result.records[target.record].name) == 0 {
|
if !binding_identifier(result.records[target.record].name) {
|
||||||
names[target.record] = alias.name
|
delete(names[target.record], allocator)
|
||||||
|
names[target.record] = strings.clone(alias.name, allocator)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return names
|
return names
|
||||||
}
|
}
|
||||||
|
|
||||||
|
binding_identifier :: proc(value: string) -> bool {
|
||||||
|
if len(value) == 0 || lexer.keyword_kind(value) != .Identifier {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for byte, index in transmute([]byte)value {
|
||||||
|
letter := byte == '_' || byte >= 'a' && byte <= 'z' || byte >= 'A' && byte <= 'Z'
|
||||||
|
if !letter && (index == 0 || byte < '0' || byte > '9') {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
record_name_conflicts :: proc(result: ^cimport.Result, record_index: int, name: string) -> bool {
|
||||||
|
for function in result.functions {
|
||||||
|
if function.name == name && len(function.reason) == 0 &&
|
||||||
|
(len(function.link_name) == 0 || function.link_name == function.name) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for macro in result.macros {
|
||||||
|
if macro.name == name && len(macro.reason) == 0 &&
|
||||||
|
lexer.keyword_kind(macro.name) == .Identifier {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for alias in result.aliases {
|
||||||
|
if alias.name != name || len(alias.reason) > 0 || alias.type == cimport.INVALID_TYPE ||
|
||||||
|
int(alias.type) < 0 || int(alias.type) >= len(result.types) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
target := result.types[alias.type]
|
||||||
|
if target.kind != .Record || int(target.record) != record_index {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy_record_name_table :: proc(names: []string, allocator: mem.Allocator) {
|
||||||
|
for name in names {
|
||||||
|
delete(name, allocator)
|
||||||
|
}
|
||||||
|
delete(names, allocator)
|
||||||
|
}
|
||||||
|
|
||||||
|
safe_name_fragment :: proc(value: string, allocator: mem.Allocator) -> string {
|
||||||
|
b := strings.builder_make(allocator)
|
||||||
|
for byte in transmute([]byte)value {
|
||||||
|
if byte >= 'a' && byte <= 'z' || byte >= 'A' && byte <= 'Z' ||
|
||||||
|
byte >= '0' && byte <= '9' || byte == '_' {
|
||||||
|
strings.write_byte(&b, byte)
|
||||||
|
} else {
|
||||||
|
strings.write_byte(&b, '_')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return strings.to_string(b)
|
||||||
|
}
|
||||||
|
|
||||||
// render_type writes the brolang spelling of a C type. Mirror of
|
// render_type writes the brolang spelling of a C type. Mirror of
|
||||||
// loader.translate_c_type — keep the two in lockstep.
|
// loader.translate_c_type — keep the two in lockstep.
|
||||||
render_type :: proc(b: ^strings.Builder, result: ^cimport.Result, id: cimport.Type_Id, record_names: []string) {
|
render_type :: proc(b: ^strings.Builder, result: ^cimport.Result, id: cimport.Type_Id, record_names: []string) {
|
||||||
@@ -150,36 +384,68 @@ render_c_func :: proc(
|
|||||||
render_type(b, result, ret, record_names)
|
render_type(b, result, ret, record_names)
|
||||||
}
|
}
|
||||||
|
|
||||||
emit_records :: proc(b: ^strings.Builder, result: ^cimport.Result, record_names: []string) {
|
emit_records :: proc(
|
||||||
|
b: ^strings.Builder,
|
||||||
|
result: ^cimport.Result,
|
||||||
|
record_names: []string,
|
||||||
|
registry: ^Declaration_Registry,
|
||||||
|
) -> bool {
|
||||||
wrote := false
|
wrote := false
|
||||||
for record, idx in result.records {
|
for record, idx in result.records {
|
||||||
name := record_names[idx]
|
name := record_names[idx]
|
||||||
|
representable := record_has_native_spelling(result, u32(idx))
|
||||||
if record.kind == .Union {
|
if record.kind == .Union {
|
||||||
fmt.sbprintf(b, "# unsupported in bindings: C union '%s' has no native spelling\n", name)
|
fmt.sbprintf(b, "# unsupported in bindings: C union '%s' has no native spelling\n", name)
|
||||||
wrote = true
|
wrote = true
|
||||||
continue
|
} else if record.complete && len(record.reason) == 0 && !representable {
|
||||||
}
|
fmt.sbprintf(b, "# unsupported in bindings: C record '%s' contains an unsupported field type\n", name)
|
||||||
if !record.complete || len(record.reason) > 0 {
|
|
||||||
// opaque / pointer-only struct
|
|
||||||
fmt.sbprintf(b, "%s :: opaque\n", name)
|
|
||||||
wrote = true
|
wrote = true
|
||||||
|
}
|
||||||
|
if !representable {
|
||||||
|
// opaque / pointer-only struct
|
||||||
|
text := fmt.tprintf("%s :: opaque\n", name)
|
||||||
|
canonical := text
|
||||||
|
if len(record.identity) > 0 {
|
||||||
|
canonical = fmt.tprintf("%s\n%s", record.identity, text)
|
||||||
|
}
|
||||||
|
emitted, ok := write_declaration(b, registry, name, .Record, text, canonical)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
wrote = wrote || emitted
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
fmt.sbprintf(b, "%s :: c_struct {{\n", name)
|
declaration := strings.builder_make(context.temp_allocator)
|
||||||
|
fmt.sbprintf(&declaration, "%s :: c_struct {{\n", name)
|
||||||
for field in record.fields {
|
for field in record.fields {
|
||||||
fmt.sbprintf(b, "\t%s ", field.name)
|
fmt.sbprintf(&declaration, "\t%s ", field.name)
|
||||||
render_type(b, result, field.type, record_names)
|
render_type(&declaration, result, field.type, record_names)
|
||||||
strings.write_byte(b, '\n')
|
strings.write_byte(&declaration, '\n')
|
||||||
}
|
}
|
||||||
strings.write_string(b, "}\n")
|
strings.write_string(&declaration, "}\n")
|
||||||
wrote = true
|
text := strings.to_string(declaration)
|
||||||
|
canonical := text
|
||||||
|
if len(record.identity) > 0 {
|
||||||
|
canonical = fmt.tprintf("%s\n%s", record.identity, text)
|
||||||
|
}
|
||||||
|
emitted, ok := write_declaration(b, registry, name, .Record, text, canonical)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
wrote = wrote || emitted
|
||||||
}
|
}
|
||||||
if wrote {
|
if wrote {
|
||||||
strings.write_byte(b, '\n')
|
strings.write_byte(b, '\n')
|
||||||
}
|
}
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
emit_aliases :: proc(b: ^strings.Builder, result: ^cimport.Result, record_names: []string) {
|
emit_aliases :: proc(
|
||||||
|
b: ^strings.Builder,
|
||||||
|
result: ^cimport.Result,
|
||||||
|
record_names: []string,
|
||||||
|
registry: ^Declaration_Registry,
|
||||||
|
) -> bool {
|
||||||
wrote := false
|
wrote := false
|
||||||
for alias in result.aliases {
|
for alias in result.aliases {
|
||||||
if len(alias.reason) > 0 {
|
if len(alias.reason) > 0 {
|
||||||
@@ -187,27 +453,46 @@ emit_aliases :: proc(b: ^strings.Builder, result: ^cimport.Result, record_names:
|
|||||||
wrote = true
|
wrote = true
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if !type_has_native_spelling(result, alias.type) {
|
||||||
|
fmt.sbprintf(b, "# unsupported in bindings: typedef '%s' — underlying type has no native spelling\n", alias.name)
|
||||||
|
wrote = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
// Skip a typedef that merely (re)names a record under the name we already
|
// Skip a typedef that merely (re)names a record under the name we already
|
||||||
// emitted the record with (anonymous-struct collapse, or `typedef struct
|
// emitted the record with (anonymous-struct collapse, or `typedef struct
|
||||||
// Foo Foo;`).
|
// Foo Foo;`).
|
||||||
if ti := alias.type; int(ti) >= 0 && int(ti) < len(result.types) {
|
if ti := alias.type; int(ti) >= 0 && int(ti) < len(result.types) {
|
||||||
target := result.types[ti]
|
target := result.types[ti]
|
||||||
if target.kind == .Record && int(target.record) < len(record_names) &&
|
if target.kind == .Record && int(target.record) < len(record_names) {
|
||||||
record_names[target.record] == alias.name {
|
record := result.records[target.record]
|
||||||
continue
|
if record_names[target.record] == alias.name || record.name == alias.name {
|
||||||
|
continue
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fmt.sbprintf(b, "%s :: alias ", alias.name)
|
declaration := strings.builder_make(context.temp_allocator)
|
||||||
render_type(b, result, alias.type, record_names)
|
fmt.sbprintf(&declaration, "%s :: alias ", alias.name)
|
||||||
strings.write_byte(b, '\n')
|
render_type(&declaration, result, alias.type, record_names)
|
||||||
wrote = true
|
strings.write_byte(&declaration, '\n')
|
||||||
|
text := strings.to_string(declaration)
|
||||||
|
emitted, ok := write_declaration(b, registry, alias.name, .Alias, text)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
wrote = wrote || emitted
|
||||||
}
|
}
|
||||||
if wrote {
|
if wrote {
|
||||||
strings.write_byte(b, '\n')
|
strings.write_byte(b, '\n')
|
||||||
}
|
}
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
emit_macros :: proc(b: ^strings.Builder, result: ^cimport.Result, record_names: []string) {
|
emit_macros :: proc(
|
||||||
|
b: ^strings.Builder,
|
||||||
|
result: ^cimport.Result,
|
||||||
|
record_names: []string,
|
||||||
|
registry: ^Declaration_Registry,
|
||||||
|
) -> bool {
|
||||||
wrote := false
|
wrote := false
|
||||||
for macro in result.macros {
|
for macro in result.macros {
|
||||||
if len(macro.reason) > 0 {
|
if len(macro.reason) > 0 {
|
||||||
@@ -223,23 +508,41 @@ emit_macros :: proc(b: ^strings.Builder, result: ^cimport.Result, record_names:
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if macro.aggregate {
|
if macro.aggregate {
|
||||||
emit_aggregate_macro(b, result, macro, record_names)
|
declaration := strings.builder_make(context.temp_allocator)
|
||||||
wrote = true
|
binding := emit_aggregate_macro(&declaration, result, macro, record_names)
|
||||||
|
text := strings.to_string(declaration)
|
||||||
|
if binding {
|
||||||
|
emitted, ok := write_declaration(b, registry, macro.name, .Macro, text)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
wrote = wrote || emitted
|
||||||
|
} else {
|
||||||
|
strings.write_string(b, text)
|
||||||
|
wrote = true
|
||||||
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
strings.write_string(b, macro.name)
|
declaration := strings.builder_make(context.temp_allocator)
|
||||||
|
strings.write_string(&declaration, macro.name)
|
||||||
if macro_scalar_kind(result, macro.type) {
|
if macro_scalar_kind(result, macro.type) {
|
||||||
strings.write_byte(b, ' ')
|
strings.write_byte(&declaration, ' ')
|
||||||
render_type(b, result, macro.type, record_names)
|
render_type(&declaration, result, macro.type, record_names)
|
||||||
}
|
}
|
||||||
strings.write_string(b, " :: ")
|
strings.write_string(&declaration, " :: ")
|
||||||
render_macro_value(b, macro.value)
|
render_macro_value(&declaration, macro.value)
|
||||||
strings.write_byte(b, '\n')
|
strings.write_byte(&declaration, '\n')
|
||||||
wrote = true
|
text := strings.to_string(declaration)
|
||||||
|
emitted, ok := write_declaration(b, registry, macro.name, .Macro, text)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
wrote = wrote || emitted
|
||||||
}
|
}
|
||||||
if wrote {
|
if wrote {
|
||||||
strings.write_byte(b, '\n')
|
strings.write_byte(b, '\n')
|
||||||
}
|
}
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
emit_aggregate_macro :: proc(
|
emit_aggregate_macro :: proc(
|
||||||
@@ -247,11 +550,11 @@ emit_aggregate_macro :: proc(
|
|||||||
result: ^cimport.Result,
|
result: ^cimport.Result,
|
||||||
macro: cimport.Macro_Constant,
|
macro: cimport.Macro_Constant,
|
||||||
record_names: []string,
|
record_names: []string,
|
||||||
) {
|
) -> bool {
|
||||||
ti := macro.type
|
ti := macro.type
|
||||||
if int(ti) >= 0 && int(ti) < len(result.types) && result.types[ti].kind == .Record {
|
if int(ti) >= 0 && int(ti) < len(result.types) && result.types[ti].kind == .Record {
|
||||||
ridx := int(result.types[ti].record)
|
ridx := int(result.types[ti].record)
|
||||||
if ridx < len(result.records) {
|
if ridx < len(result.records) && record_has_native_spelling(result, u32(ridx)) {
|
||||||
record := result.records[ridx]
|
record := result.records[ridx]
|
||||||
if len(record.fields) == len(macro.values) && !record_has_pointer_field(result, record) {
|
if len(record.fields) == len(macro.values) && !record_has_pointer_field(result, record) {
|
||||||
fmt.sbprintf(b, "%s :: %s {{", macro.name, record_names[ridx])
|
fmt.sbprintf(b, "%s :: %s {{", macro.name, record_names[ridx])
|
||||||
@@ -263,14 +566,20 @@ emit_aggregate_macro :: proc(
|
|||||||
render_macro_value(b, macro.values[index])
|
render_macro_value(b, macro.values[index])
|
||||||
}
|
}
|
||||||
strings.write_string(b, " }\n")
|
strings.write_string(b, " }\n")
|
||||||
return
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fmt.sbprintf(b, "# unsupported in bindings: aggregate macro '%s' has no native spelling\n", macro.name)
|
fmt.sbprintf(b, "# unsupported in bindings: aggregate macro '%s' has no native spelling\n", macro.name)
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
emit_functions :: proc(b: ^strings.Builder, result: ^cimport.Result, record_names: []string) {
|
emit_functions :: proc(
|
||||||
|
b: ^strings.Builder,
|
||||||
|
result: ^cimport.Result,
|
||||||
|
record_names: []string,
|
||||||
|
registry: ^Declaration_Registry,
|
||||||
|
) -> bool {
|
||||||
wrote := false
|
wrote := false
|
||||||
for function in result.functions {
|
for function in result.functions {
|
||||||
if len(function.reason) > 0 {
|
if len(function.reason) > 0 {
|
||||||
@@ -278,19 +587,41 @@ emit_functions :: proc(b: ^strings.Builder, result: ^cimport.Result, record_name
|
|||||||
wrote = true
|
wrote = true
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if !function_has_native_spelling(result, function) {
|
||||||
|
fmt.sbprintf(b, "# unsupported in bindings: function '%s' — signature has no native spelling\n", function.name)
|
||||||
|
wrote = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
if len(function.link_name) > 0 && function.link_name != function.name {
|
if len(function.link_name) > 0 && function.link_name != function.name {
|
||||||
fmt.sbprintf(b, "# unsupported in bindings: function '%s' is a static inline function (needs trampoline)\n", function.name)
|
fmt.sbprintf(b, "# unsupported in bindings: function '%s' is a static inline function (needs trampoline)\n", function.name)
|
||||||
wrote = true
|
wrote = true
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
fmt.sbprintf(b, "%s ", function.name)
|
declaration := strings.builder_make(context.temp_allocator)
|
||||||
render_c_func(b, result, function.params, function.param_names, function.result, function.variadic, record_names)
|
fmt.sbprintf(&declaration, "%s ", function.name)
|
||||||
strings.write_byte(b, '\n')
|
render_c_func(&declaration, result, function.params, function.param_names, function.result, function.variadic, record_names)
|
||||||
wrote = true
|
strings.write_byte(&declaration, '\n')
|
||||||
|
canonical := strings.builder_make(context.temp_allocator)
|
||||||
|
fmt.sbprintf(&canonical, "%s ", function.name)
|
||||||
|
render_c_func(&canonical, result, function.params, nil, function.result, function.variadic, record_names)
|
||||||
|
strings.write_byte(&canonical, '\n')
|
||||||
|
emitted, ok := write_declaration(
|
||||||
|
b,
|
||||||
|
registry,
|
||||||
|
function.name,
|
||||||
|
.Function,
|
||||||
|
strings.to_string(declaration),
|
||||||
|
strings.to_string(canonical),
|
||||||
|
)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
wrote = wrote || emitted
|
||||||
}
|
}
|
||||||
if wrote {
|
if wrote {
|
||||||
strings.write_byte(b, '\n')
|
strings.write_byte(b, '\n')
|
||||||
}
|
}
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
emit_variables :: proc(b: ^strings.Builder, result: ^cimport.Result) {
|
emit_variables :: proc(b: ^strings.Builder, result: ^cimport.Result) {
|
||||||
@@ -350,6 +681,73 @@ macro_scalar_kind :: proc(result: ^cimport.Result, id: cimport.Type_Id) -> bool
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function_has_native_spelling :: proc(result: ^cimport.Result, function: cimport.Function) -> bool {
|
||||||
|
for param in function.params {
|
||||||
|
if !type_has_native_spelling(result, param) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return type_has_native_spelling(result, function.result, allow_void=true)
|
||||||
|
}
|
||||||
|
|
||||||
|
record_has_native_spelling :: proc(result: ^cimport.Result, index: u32, depth := 0) -> bool {
|
||||||
|
if depth > 64 || int(index) < 0 || int(index) >= len(result.records) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
record := result.records[index]
|
||||||
|
if record.kind == .Union || !record.complete || len(record.reason) > 0 || len(record.fields) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for field in record.fields {
|
||||||
|
if !type_has_native_spelling(result, field.type, depth=depth+1) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
type_has_native_spelling :: proc(
|
||||||
|
result: ^cimport.Result,
|
||||||
|
id: cimport.Type_Id,
|
||||||
|
allow_void := false,
|
||||||
|
depth := 0,
|
||||||
|
) -> bool {
|
||||||
|
if depth > 64 || id == cimport.INVALID_TYPE || int(id) < 0 || int(id) >= len(result.types) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
item := result.types[id]
|
||||||
|
switch item.kind {
|
||||||
|
case .Void:
|
||||||
|
return allow_void
|
||||||
|
case .C_Bool, .C_Char, .C_Schar, .C_Uchar, .C_Short, .C_Ushort, .C_Int, .C_Uint,
|
||||||
|
.C_Long, .C_Ulong, .C_Longlong, .C_Ulonglong, .C_Float, .C_Double, .C_Longdouble:
|
||||||
|
return true
|
||||||
|
case .Pointer:
|
||||||
|
if item.child == cimport.INVALID_TYPE || int(item.child) < 0 || int(item.child) >= len(result.types) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
child := result.types[item.child]
|
||||||
|
if child.kind == .Void || child.kind == .Record {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return type_has_native_spelling(result, item.child, allow_void=true, depth=depth+1)
|
||||||
|
case .Array:
|
||||||
|
return type_has_native_spelling(result, item.child, depth=depth+1)
|
||||||
|
case .Function:
|
||||||
|
for param in item.params {
|
||||||
|
if !type_has_native_spelling(result, param, depth=depth+1) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return type_has_native_spelling(result, item.child, allow_void=true, depth=depth+1)
|
||||||
|
case .Record:
|
||||||
|
return record_has_native_spelling(result, item.record, depth+1)
|
||||||
|
case .Invalid:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
record_has_pointer_field :: proc(result: ^cimport.Result, record: cimport.Record) -> bool {
|
record_has_pointer_field :: proc(result: ^cimport.Result, record: cimport.Record) -> bool {
|
||||||
for field in record.fields {
|
for field in record.fields {
|
||||||
if int(field.type) >= 0 && int(field.type) < len(result.types) &&
|
if int(field.type) >= 0 && int(field.type) < len(result.types) &&
|
||||||
|
|||||||
+335
-38
@@ -44,6 +44,8 @@ BOOL :: Type(29)
|
|||||||
FLOAT :: Type(30)
|
FLOAT :: Type(30)
|
||||||
RANGE :: Type(31)
|
RANGE :: Type(31)
|
||||||
ANYOPAQUE :: Type(32)
|
ANYOPAQUE :: Type(32)
|
||||||
|
UINT :: Type(33)
|
||||||
|
NORETURN :: Type(34)
|
||||||
|
|
||||||
DYNAMIC_START :: Type(64)
|
DYNAMIC_START :: Type(64)
|
||||||
|
|
||||||
@@ -54,11 +56,19 @@ Numeric_Category :: enum u8 {
|
|||||||
Float,
|
Float,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Visibility :: enum u8 {
|
||||||
|
Public,
|
||||||
|
Package,
|
||||||
|
File,
|
||||||
|
}
|
||||||
|
|
||||||
Kind :: enum u8 {
|
Kind :: enum u8 {
|
||||||
Invalid,
|
Invalid,
|
||||||
Void,
|
Void,
|
||||||
|
Noreturn,
|
||||||
Anyopaque,
|
Anyopaque,
|
||||||
Int_Constraint,
|
Int_Constraint,
|
||||||
|
Uint_Constraint,
|
||||||
Float_Constraint,
|
Float_Constraint,
|
||||||
Range_Constraint,
|
Range_Constraint,
|
||||||
Scalar,
|
Scalar,
|
||||||
@@ -75,6 +85,7 @@ Kind :: enum u8 {
|
|||||||
Struct,
|
Struct,
|
||||||
Union,
|
Union,
|
||||||
Fallible,
|
Fallible,
|
||||||
|
Sum,
|
||||||
Type_Call,
|
Type_Call,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,9 +112,10 @@ Node :: struct {
|
|||||||
c_abi: bool,
|
c_abi: bool,
|
||||||
variadic: bool,
|
variadic: bool,
|
||||||
c_layout: bool,
|
c_layout: bool,
|
||||||
|
tuple: bool,
|
||||||
opaque: bool,
|
opaque: bool,
|
||||||
declared: bool,
|
declared: bool,
|
||||||
file_hidden: bool,
|
visibility: Visibility,
|
||||||
explicit_backing: bool,
|
explicit_backing: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,43 +199,65 @@ intern :: proc(store: ^Store, candidate: Node) -> Type {
|
|||||||
return id
|
return id
|
||||||
}
|
}
|
||||||
|
|
||||||
named :: proc(store: ^Store, pkg, name: u32, qualifier: u32 = 0, file: u32 = 0xffff_ffff, file_hidden := false) -> Type {
|
named :: proc(
|
||||||
normalized_file := file if qualifier != 0 || file_hidden else u32(0)
|
store: ^Store,
|
||||||
|
pkg, name: u32,
|
||||||
|
qualifier: u32 = 0,
|
||||||
|
file: u32 = 0xffff_ffff,
|
||||||
|
visibility := Visibility.Public,
|
||||||
|
) -> Type {
|
||||||
|
normalized_file := file if qualifier != 0 || visibility == .File else u32(0)
|
||||||
for existing, index in store.nodes {
|
for existing, index in store.nodes {
|
||||||
visibility_matches := existing.file_hidden == file_hidden &&
|
|
||||||
(!file_hidden || existing.file == normalized_file)
|
|
||||||
if (existing.kind == .Named || existing.kind == .Alias || existing.kind == .Distinct ||
|
if (existing.kind == .Named || existing.kind == .Alias || existing.kind == .Distinct ||
|
||||||
existing.kind == .Enum || existing.kind == .Struct || existing.kind == .Union) &&
|
existing.kind == .Enum || existing.kind == .Struct || existing.kind == .Union) &&
|
||||||
existing.pkg == pkg && existing.name == name && existing.qualifier == qualifier &&
|
existing.pkg == pkg && existing.name == name && existing.qualifier == qualifier &&
|
||||||
(visibility_matches || qualifier == 0 && existing.file_hidden != file_hidden) {
|
((qualifier != 0 && existing.file == normalized_file) ||
|
||||||
|
(qualifier == 0 && visibility == .File && existing.visibility == .File && existing.file == normalized_file) ||
|
||||||
|
(qualifier == 0 && visibility != .File && existing.visibility != .File)) {
|
||||||
return DYNAMIC_START+Type(index)
|
return DYNAMIC_START+Type(index)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return intern(store, Node{kind=.Named, pkg=pkg, name=name, qualifier=qualifier, file=normalized_file, file_hidden=file_hidden})
|
return intern(store, Node{
|
||||||
|
kind=.Named,
|
||||||
|
pkg=pkg,
|
||||||
|
name=name,
|
||||||
|
qualifier=qualifier,
|
||||||
|
file=normalized_file,
|
||||||
|
visibility=visibility,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
find_named :: proc(store: ^Store, pkg, name: u32, qualifier: u32 = 0, file: u32 = 0xffff_ffff) -> Type {
|
find_named :: proc(store: ^Store, pkg, name: u32, qualifier: u32 = 0, file: u32 = 0xffff_ffff) -> Type {
|
||||||
if file != 0xffff_ffff {
|
fallback := INVALID
|
||||||
for existing, index in store.nodes {
|
for existing, index in store.nodes {
|
||||||
if (existing.kind == .Named || existing.kind == .Alias || existing.kind == .Distinct ||
|
if (existing.kind != .Named && existing.kind != .Alias && existing.kind != .Distinct &&
|
||||||
existing.kind == .Enum || existing.kind == .Struct || existing.kind == .Union) &&
|
existing.kind != .Enum && existing.kind != .Struct && existing.kind != .Union) ||
|
||||||
existing.pkg == pkg && existing.name == name && existing.qualifier == qualifier &&
|
existing.pkg != pkg || existing.name != name || existing.qualifier != qualifier {
|
||||||
existing.file_hidden && existing.file == file {
|
continue
|
||||||
|
}
|
||||||
|
if qualifier != 0 {
|
||||||
|
if existing.file == file {
|
||||||
|
return DYNAMIC_START+Type(index)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
switch existing.visibility {
|
||||||
|
case .Public:
|
||||||
|
fallback = DYNAMIC_START+Type(index)
|
||||||
|
case .Package:
|
||||||
|
if file != 0xffff_ffff {
|
||||||
|
fallback = DYNAMIC_START+Type(index)
|
||||||
|
}
|
||||||
|
case .File:
|
||||||
|
if existing.file == file {
|
||||||
return DYNAMIC_START+Type(index)
|
return DYNAMIC_START+Type(index)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for existing, index in store.nodes {
|
return fallback
|
||||||
if (existing.kind == .Named || existing.kind == .Alias || existing.kind == .Distinct ||
|
|
||||||
existing.kind == .Enum || existing.kind == .Struct || existing.kind == .Union) &&
|
|
||||||
existing.pkg == pkg && existing.name == name && existing.qualifier == qualifier && !existing.file_hidden {
|
|
||||||
return DYNAMIC_START+Type(index)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return INVALID
|
|
||||||
}
|
}
|
||||||
|
|
||||||
define_alias :: proc(store: ^Store, id, child: Type) -> bool {
|
define_alias :: proc(store: ^Store, id, child: Type, visibility := Visibility.Public) -> bool {
|
||||||
existing, ok := node(store, id)
|
existing, ok := node(store, id)
|
||||||
if !ok || existing.kind != .Named || existing.declared {
|
if !ok || existing.kind != .Named || existing.declared {
|
||||||
return false
|
return false
|
||||||
@@ -231,11 +265,12 @@ define_alias :: proc(store: ^Store, id, child: Type) -> bool {
|
|||||||
index := int(id-DYNAMIC_START)
|
index := int(id-DYNAMIC_START)
|
||||||
store.nodes[index].kind = .Alias
|
store.nodes[index].kind = .Alias
|
||||||
store.nodes[index].child = child
|
store.nodes[index].child = child
|
||||||
|
store.nodes[index].visibility = visibility
|
||||||
store.nodes[index].declared = true
|
store.nodes[index].declared = true
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
define_distinct :: proc(store: ^Store, id, child: Type) -> bool {
|
define_distinct :: proc(store: ^Store, id, child: Type, visibility := Visibility.Public) -> bool {
|
||||||
existing, ok := node(store, id)
|
existing, ok := node(store, id)
|
||||||
if !ok || existing.kind != .Named || existing.declared {
|
if !ok || existing.kind != .Named || existing.declared {
|
||||||
return false
|
return false
|
||||||
@@ -243,11 +278,12 @@ define_distinct :: proc(store: ^Store, id, child: Type) -> bool {
|
|||||||
index := int(id-DYNAMIC_START)
|
index := int(id-DYNAMIC_START)
|
||||||
store.nodes[index].kind = .Distinct
|
store.nodes[index].kind = .Distinct
|
||||||
store.nodes[index].child = child
|
store.nodes[index].child = child
|
||||||
|
store.nodes[index].visibility = visibility
|
||||||
store.nodes[index].declared = true
|
store.nodes[index].declared = true
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
define_enum :: proc(store: ^Store, id, backing: Type, members: []Enum_Member, explicit_backing: bool) -> bool {
|
define_enum :: proc(store: ^Store, id, backing: Type, members: []Enum_Member, explicit_backing: bool, visibility := Visibility.Public) -> bool {
|
||||||
existing, ok := node(store, id)
|
existing, ok := node(store, id)
|
||||||
if !ok || existing.kind != .Named || existing.declared {
|
if !ok || existing.kind != .Named || existing.declared {
|
||||||
return false
|
return false
|
||||||
@@ -258,6 +294,7 @@ define_enum :: proc(store: ^Store, id, backing: Type, members: []Enum_Member, ex
|
|||||||
store.nodes[index].field_start = u32(len(store.enum_members))
|
store.nodes[index].field_start = u32(len(store.enum_members))
|
||||||
store.nodes[index].field_count = u32(len(members))
|
store.nodes[index].field_count = u32(len(members))
|
||||||
store.nodes[index].explicit_backing = explicit_backing
|
store.nodes[index].explicit_backing = explicit_backing
|
||||||
|
store.nodes[index].visibility = visibility
|
||||||
store.nodes[index].declared = true
|
store.nodes[index].declared = true
|
||||||
append(&store.enum_members, ..members)
|
append(&store.enum_members, ..members)
|
||||||
return true
|
return true
|
||||||
@@ -273,6 +310,8 @@ define_record :: proc(
|
|||||||
explicit_alignment: u32 = 0,
|
explicit_alignment: u32 = 0,
|
||||||
tag: Type = INVALID,
|
tag: Type = INVALID,
|
||||||
declared_tag: Type = INVALID,
|
declared_tag: Type = INVALID,
|
||||||
|
tuple := false,
|
||||||
|
visibility := Visibility.Public,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
existing, ok := node(store, id)
|
existing, ok := node(store, id)
|
||||||
if !ok || (existing.kind != .Named && existing.kind != .Struct && existing.kind != .Union) ||
|
if !ok || (existing.kind != .Named && existing.kind != .Struct && existing.kind != .Union) ||
|
||||||
@@ -282,7 +321,9 @@ define_record :: proc(
|
|||||||
index := int(id-DYNAMIC_START)
|
index := int(id-DYNAMIC_START)
|
||||||
store.nodes[index].kind = .Union if is_union else .Struct
|
store.nodes[index].kind = .Union if is_union else .Struct
|
||||||
store.nodes[index].c_layout = c_layout
|
store.nodes[index].c_layout = c_layout
|
||||||
|
store.nodes[index].tuple = tuple
|
||||||
store.nodes[index].opaque = opaque
|
store.nodes[index].opaque = opaque
|
||||||
|
store.nodes[index].visibility = visibility
|
||||||
store.nodes[index].declared = true
|
store.nodes[index].declared = true
|
||||||
store.nodes[index].explicit_size = explicit_size
|
store.nodes[index].explicit_size = explicit_size
|
||||||
store.nodes[index].explicit_alignment = explicit_alignment
|
store.nodes[index].explicit_alignment = explicit_alignment
|
||||||
@@ -342,10 +383,10 @@ anonymous_struct_fields_equal :: proc(store: ^Store, item: Node, fields: []Field
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
struct_anonymous :: proc(store: ^Store, fields: []Field) -> Type {
|
struct_anonymous :: proc(store: ^Store, fields: []Field, tuple := false) -> Type {
|
||||||
for existing, index in store.nodes {
|
for existing, index in store.nodes {
|
||||||
if existing.kind == .Struct && existing.name == 0 && existing.declared &&
|
if existing.kind == .Struct && existing.name == 0 && existing.declared &&
|
||||||
!existing.c_layout && !existing.opaque &&
|
!existing.c_layout && existing.tuple == tuple && !existing.opaque &&
|
||||||
anonymous_struct_fields_equal(store, existing, fields) {
|
anonymous_struct_fields_equal(store, existing, fields) {
|
||||||
return DYNAMIC_START+Type(index)
|
return DYNAMIC_START+Type(index)
|
||||||
}
|
}
|
||||||
@@ -356,13 +397,14 @@ struct_anonymous :: proc(store: ^Store, fields: []Field) -> Type {
|
|||||||
kind=.Struct,
|
kind=.Struct,
|
||||||
field_start=start,
|
field_start=start,
|
||||||
field_count=u32(len(fields)),
|
field_count=u32(len(fields)),
|
||||||
|
tuple=tuple,
|
||||||
declared=true,
|
declared=true,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generated structs are nominal per comptime type-expression specialization.
|
// Generated structs are nominal per comptime type-expression specialization.
|
||||||
// The checker owns canonicalization; this routine deliberately creates a fresh node.
|
// The checker owns canonicalization; this routine deliberately creates a fresh node.
|
||||||
struct_generated :: proc(store: ^Store, fields: []Field) -> Type {
|
struct_generated :: proc(store: ^Store, fields: []Field, tuple := false, c_layout := false) -> Type {
|
||||||
start := u32(len(store.fields))
|
start := u32(len(store.fields))
|
||||||
append(&store.fields, ..fields)
|
append(&store.fields, ..fields)
|
||||||
id := DYNAMIC_START+Type(len(store.nodes))
|
id := DYNAMIC_START+Type(len(store.nodes))
|
||||||
@@ -370,6 +412,8 @@ struct_generated :: proc(store: ^Store, fields: []Field) -> Type {
|
|||||||
kind=.Struct,
|
kind=.Struct,
|
||||||
field_start=start,
|
field_start=start,
|
||||||
field_count=u32(len(fields)),
|
field_count=u32(len(fields)),
|
||||||
|
tuple=tuple,
|
||||||
|
c_layout=c_layout,
|
||||||
declared=true,
|
declared=true,
|
||||||
})
|
})
|
||||||
return id
|
return id
|
||||||
@@ -404,6 +448,10 @@ fallible_error :: proc(value: Type, store: ^Store) -> Type {
|
|||||||
return item.extra if ok && item.kind == .Fallible else INVALID
|
return item.extra if ok && item.kind == .Fallible else INVALID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sum_syntax :: proc(store: ^Store, left, right: Type) -> Type {
|
||||||
|
return intern(store, Node{kind=.Sum, child=left, extra=right})
|
||||||
|
}
|
||||||
|
|
||||||
append_sum_variants :: proc(store: ^Store, value: Type, out: ^[dynamic]Sum_Variant) -> bool {
|
append_sum_variants :: proc(store: ^Store, value: Type, out: ^[dynamic]Sum_Variant) -> bool {
|
||||||
item, ok := node(store, value)
|
item, ok := node(store, value)
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -537,6 +585,73 @@ can_sum_widen :: proc(from, to: Type, store: ^Store) -> bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reports whether every selected variant from `from` is represented identically in
|
||||||
|
// `to`. The checker uses this only after control flow has proven that the source value
|
||||||
|
// is one of `selected`; it is not a general implicit narrowing rule.
|
||||||
|
selected_sum_fits :: proc(from, to: Type, selected: []u32, store: ^Store) -> bool {
|
||||||
|
if len(selected) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
from_variants: [dynamic]Sum_Variant
|
||||||
|
from_variants.allocator = store.allocator
|
||||||
|
defer delete(from_variants)
|
||||||
|
to_variants: [dynamic]Sum_Variant
|
||||||
|
to_variants.allocator = store.allocator
|
||||||
|
defer delete(to_variants)
|
||||||
|
if !append_sum_variants(store, from, &from_variants) ||
|
||||||
|
!append_sum_variants(store, to, &to_variants) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for name in selected {
|
||||||
|
source_variant: Sum_Variant
|
||||||
|
source_found := false
|
||||||
|
for variant in from_variants {
|
||||||
|
if variant.name == name {
|
||||||
|
source_variant = variant
|
||||||
|
source_found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !source_found {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
matched := false
|
||||||
|
for variant in to_variants {
|
||||||
|
if variant.id == source_variant.id && variant.payload == source_variant.payload {
|
||||||
|
matched = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !matched {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sum_Project is emitted only with a checker proof. This weaker structural predicate is
|
||||||
|
// retained by the backend as a defensive check that the two sums share a valid variant.
|
||||||
|
can_sum_project :: proc(from, to: Type, store: ^Store) -> bool {
|
||||||
|
from_variants: [dynamic]Sum_Variant
|
||||||
|
from_variants.allocator = store.allocator
|
||||||
|
defer delete(from_variants)
|
||||||
|
to_variants: [dynamic]Sum_Variant
|
||||||
|
to_variants.allocator = store.allocator
|
||||||
|
defer delete(to_variants)
|
||||||
|
if !append_sum_variants(store, from, &from_variants) ||
|
||||||
|
!append_sum_variants(store, to, &to_variants) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for source_variant in from_variants {
|
||||||
|
for target_variant in to_variants {
|
||||||
|
if target_variant.id == source_variant.id && target_variant.payload == source_variant.payload {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
define_struct :: proc(store: ^Store, id: Type, fields: []Field, c_layout, opaque: bool) -> bool {
|
define_struct :: proc(store: ^Store, id: Type, fields: []Field, c_layout, opaque: bool) -> bool {
|
||||||
return define_record(store, id, fields, c_layout, opaque)
|
return define_record(store, id, fields, c_layout, opaque)
|
||||||
}
|
}
|
||||||
@@ -554,6 +669,43 @@ fields_for :: proc(store: ^Store, value: Type) -> []Field {
|
|||||||
return store.fields[start:end]
|
return store.fields[start:end]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
field_layout_precedes :: proc(
|
||||||
|
store: ^Store,
|
||||||
|
value: Type,
|
||||||
|
left, right: int,
|
||||||
|
selected := target.DEFAULT,
|
||||||
|
) -> bool {
|
||||||
|
fields := fields_for(store, value)
|
||||||
|
item, ok := node(store, value)
|
||||||
|
if !ok || item.kind != .Struct || item.c_layout ||
|
||||||
|
left < 0 || left >= len(fields) || right < 0 || right >= len(fields) {
|
||||||
|
return left < right
|
||||||
|
}
|
||||||
|
left_alignment := alignment_of(fields[left].type, store, selected)
|
||||||
|
right_alignment := alignment_of(fields[right].type, store, selected)
|
||||||
|
return left_alignment > right_alignment ||
|
||||||
|
(left_alignment == right_alignment && left < right)
|
||||||
|
}
|
||||||
|
|
||||||
|
physical_field_index :: proc(
|
||||||
|
store: ^Store,
|
||||||
|
value: Type,
|
||||||
|
logical_index: int,
|
||||||
|
selected := target.DEFAULT,
|
||||||
|
) -> int {
|
||||||
|
fields := fields_for(store, value)
|
||||||
|
if logical_index < 0 || logical_index >= len(fields) {
|
||||||
|
return logical_index
|
||||||
|
}
|
||||||
|
result := 0
|
||||||
|
for _, other_index in fields {
|
||||||
|
if field_layout_precedes(store, value, other_index, logical_index, selected) {
|
||||||
|
result += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
params_for :: proc(store: ^Store, value: Type) -> []Field {
|
params_for :: proc(store: ^Store, value: Type) -> []Field {
|
||||||
item, ok := node(store, value)
|
item, ok := node(store, value)
|
||||||
if !ok || item.kind != .Function {
|
if !ok || item.kind != .Function {
|
||||||
@@ -586,10 +738,14 @@ kind :: proc(value: Type, store: ^Store = nil) -> Kind {
|
|||||||
return .Invalid
|
return .Invalid
|
||||||
case VOID:
|
case VOID:
|
||||||
return .Void
|
return .Void
|
||||||
|
case NORETURN:
|
||||||
|
return .Noreturn
|
||||||
case ANYOPAQUE:
|
case ANYOPAQUE:
|
||||||
return .Anyopaque
|
return .Anyopaque
|
||||||
case INT:
|
case INT:
|
||||||
return .Int_Constraint
|
return .Int_Constraint
|
||||||
|
case UINT:
|
||||||
|
return .Uint_Constraint
|
||||||
case FLOAT:
|
case FLOAT:
|
||||||
return .Float_Constraint
|
return .Float_Constraint
|
||||||
case RANGE:
|
case RANGE:
|
||||||
@@ -628,6 +784,10 @@ is_void :: proc(value: Type) -> bool {
|
|||||||
return value == VOID
|
return value == VOID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
is_noreturn :: proc(value: Type) -> bool {
|
||||||
|
return value == NORETURN
|
||||||
|
}
|
||||||
|
|
||||||
is_anyopaque :: proc(value: Type) -> bool {
|
is_anyopaque :: proc(value: Type) -> bool {
|
||||||
return value == ANYOPAQUE
|
return value == ANYOPAQUE
|
||||||
}
|
}
|
||||||
@@ -637,7 +797,7 @@ is_bool :: proc(value: Type) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
is_constraint :: proc(value: Type) -> bool {
|
is_constraint :: proc(value: Type) -> bool {
|
||||||
return value == INT || value == FLOAT || value == RANGE
|
return value == INT || value == UINT || value == FLOAT || value == RANGE
|
||||||
}
|
}
|
||||||
|
|
||||||
// constraint_target reports the concrete type a constraint binding (local, or a
|
// constraint_target reports the concrete type a constraint binding (local, or a
|
||||||
@@ -651,6 +811,8 @@ constraint_target :: proc(constraint, inferred: Type, store: ^Store = nil) -> Ty
|
|||||||
switch constraint {
|
switch constraint {
|
||||||
case INT:
|
case INT:
|
||||||
return inferred if is_concrete_integer(inferred) else INVALID
|
return inferred if is_concrete_integer(inferred) else INVALID
|
||||||
|
case UINT:
|
||||||
|
return inferred if is_unsigned(inferred) else INVALID
|
||||||
case FLOAT:
|
case FLOAT:
|
||||||
if is_float(inferred) {
|
if is_float(inferred) {
|
||||||
return inferred
|
return inferred
|
||||||
@@ -668,6 +830,8 @@ constraint_accepts :: proc(constraint, concrete: Type, store: ^Store = nil) -> b
|
|||||||
switch constraint {
|
switch constraint {
|
||||||
case INT:
|
case INT:
|
||||||
return is_concrete_integer(concrete)
|
return is_concrete_integer(concrete)
|
||||||
|
case UINT:
|
||||||
|
return is_unsigned(concrete)
|
||||||
case FLOAT:
|
case FLOAT:
|
||||||
return is_float(concrete)
|
return is_float(concrete)
|
||||||
case RANGE:
|
case RANGE:
|
||||||
@@ -944,6 +1108,33 @@ is_optional_pointer :: proc(value: Type, store: ^Store) -> bool {
|
|||||||
return ok && item.kind == .Optional && is_pointer(item.child, store)
|
return ok && item.kind == .Optional && is_pointer(item.child, store)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
is_comptime_only :: proc(value: Type, store: ^Store, depth := 0) -> bool {
|
||||||
|
if depth > 256 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
item, ok := node(store, value)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if item.kind == .Function {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if item.kind == .Pointer || item.kind == .Slice || item.kind == .Range || item.kind == .Fallible {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if item.kind == .Array || item.kind == .Optional || item.kind == .Distinct || item.kind == .Enum || item.kind == .Alias {
|
||||||
|
return is_comptime_only(item.child, store, depth+1)
|
||||||
|
}
|
||||||
|
if item.kind == .Struct || item.kind == .Union {
|
||||||
|
for field in fields_for(store, value) {
|
||||||
|
if is_comptime_only(field.type, store, depth+1) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
is_runtime_value :: proc(value: Type, store: ^Store, depth := 0) -> bool {
|
is_runtime_value :: proc(value: Type, store: ^Store, depth := 0) -> bool {
|
||||||
if depth > 256 {
|
if depth > 256 {
|
||||||
return false
|
return false
|
||||||
@@ -968,7 +1159,8 @@ is_runtime_value :: proc(value: Type, store: ^Store, depth := 0) -> bool {
|
|||||||
}
|
}
|
||||||
if value_kind == .Struct || value_kind == .Union {
|
if value_kind == .Struct || value_kind == .Union {
|
||||||
item, ok := node(store, value)
|
item, ok := node(store, value)
|
||||||
return ok && item.declared && !item.opaque && (!item.c_layout || item.field_count > 0)
|
return ok && item.declared && !item.opaque && (!item.c_layout || item.field_count > 0) &&
|
||||||
|
!is_comptime_only(value, store)
|
||||||
}
|
}
|
||||||
if value_kind == .Fallible {
|
if value_kind == .Fallible {
|
||||||
item, ok := node(store, value)
|
item, ok := node(store, value)
|
||||||
@@ -984,9 +1176,33 @@ is_runtime_value :: proc(value: Type, store: ^Store, depth := 0) -> bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
can_construct_distinct :: proc(from, to: Type, store: ^Store) -> bool {
|
distinct_backing :: proc(value: Type, store: ^Store) -> (Type, bool) {
|
||||||
item, ok := node(store, to)
|
item, ok := node(store, value)
|
||||||
return ok && item.kind == .Distinct && item.declared && equal(from, item.child)
|
if !ok || item.kind != .Distinct || !item.declared {
|
||||||
|
return INVALID, false
|
||||||
|
}
|
||||||
|
return item.child, true
|
||||||
|
}
|
||||||
|
|
||||||
|
distinct_scalar_backing :: proc(value: Type, store: ^Store) -> (Type, bool) {
|
||||||
|
_, ok := distinct_backing(value, store)
|
||||||
|
if !ok {
|
||||||
|
return INVALID, false
|
||||||
|
}
|
||||||
|
backing := runtime_representation(value, store)
|
||||||
|
return backing, is_concrete_scalar(backing)
|
||||||
|
}
|
||||||
|
|
||||||
|
can_retype_distinct :: proc(from, to: Type, store: ^Store) -> bool {
|
||||||
|
if backing, ok := distinct_backing(to, store); ok && equal(from, backing) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
backing, ok := distinct_backing(from, store)
|
||||||
|
if !ok || !equal(to, backing) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
_, scalar := distinct_scalar_backing(from, store)
|
||||||
|
return scalar
|
||||||
}
|
}
|
||||||
|
|
||||||
runtime_representation :: proc(value: Type, store: ^Store, depth := 0) -> Type {
|
runtime_representation :: proc(value: Type, store: ^Store, depth := 0) -> Type {
|
||||||
@@ -1252,6 +1468,20 @@ function_pointer :: proc(value: Type, store: ^Store) -> (pointer_item, function_
|
|||||||
return pointer_node, function_node, pointer_node.child, true
|
return pointer_node, function_node, pointer_node.child, true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
callable_function :: proc(value: Type, store: ^Store) -> (pointer_item, function_item: Node, function_type: Type, ok: bool) {
|
||||||
|
item, item_ok := node(store, value)
|
||||||
|
if item_ok && item.kind == .Function {
|
||||||
|
return {}, item, value, true
|
||||||
|
}
|
||||||
|
return function_pointer(value, store)
|
||||||
|
}
|
||||||
|
|
||||||
|
can_coerce_function_pointer :: proc(actual, expected: Type, store: ^Store) -> bool {
|
||||||
|
actual_item, actual_ok := node(store, actual)
|
||||||
|
_, _, expected_function, expected_ok := function_pointer(expected, store)
|
||||||
|
return actual_ok && actual_item.kind == .Function && expected_ok && equal(actual, expected_function)
|
||||||
|
}
|
||||||
|
|
||||||
replace_pointer_child :: proc(store: ^Store, value, child: Type) -> (Type, bool) {
|
replace_pointer_child :: proc(store: ^Store, value, child: Type) -> (Type, bool) {
|
||||||
item, ok := node(store, value)
|
item, ok := node(store, value)
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -1271,6 +1501,45 @@ replace_pointer_child :: proc(store: ^Store, value, child: Type) -> (Type, bool)
|
|||||||
return intern(store, item), true
|
return intern(store, item), true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
restore_mutability :: proc(store: ^Store, value: Type) -> (Type, bool) {
|
||||||
|
item, ok := node(store, resolve_alias(value, store))
|
||||||
|
if !ok {
|
||||||
|
return INVALID, false
|
||||||
|
}
|
||||||
|
if item.kind == .Optional {
|
||||||
|
restored, restored_ok := restore_mutability(store, item.child)
|
||||||
|
if !restored_ok || !is_pointer(restored, store) {
|
||||||
|
return INVALID, false
|
||||||
|
}
|
||||||
|
return optional(store, restored), true
|
||||||
|
}
|
||||||
|
if item.kind != .Pointer && item.kind != .Slice {
|
||||||
|
return INVALID, false
|
||||||
|
}
|
||||||
|
item.mutable = true
|
||||||
|
return intern(store, item), true
|
||||||
|
}
|
||||||
|
|
||||||
|
same_constcast_shape :: proc(from, to: Type, store: ^Store) -> bool {
|
||||||
|
from_item, from_ok := node(store, resolve_alias(from, store))
|
||||||
|
to_item, to_ok := node(store, resolve_alias(to, store))
|
||||||
|
if !from_ok || !to_ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if from_item.kind == .Optional || to_item.kind == .Optional {
|
||||||
|
return from_item.kind == .Optional && to_item.kind == .Optional &&
|
||||||
|
is_pointer(from_item.child, store) && is_pointer(to_item.child, store) &&
|
||||||
|
same_constcast_shape(from_item.child, to_item.child, store)
|
||||||
|
}
|
||||||
|
return (from_item.kind == .Pointer || from_item.kind == .Slice) &&
|
||||||
|
from_item.kind == to_item.kind &&
|
||||||
|
from_item.child == to_item.child &&
|
||||||
|
from_item.many == to_item.many &&
|
||||||
|
to_item.mutable &&
|
||||||
|
from_item.has_sentinel == to_item.has_sentinel &&
|
||||||
|
(!from_item.has_sentinel || from_item.sentinel == to_item.sentinel)
|
||||||
|
}
|
||||||
|
|
||||||
same_pointer_shape :: proc(left, right: Type, store: ^Store) -> bool {
|
same_pointer_shape :: proc(left, right: Type, store: ^Store) -> bool {
|
||||||
left_item, left_ok := node(store, left)
|
left_item, left_ok := node(store, left)
|
||||||
right_item, right_ok := node(store, right)
|
right_item, right_ok := node(store, right)
|
||||||
@@ -1478,7 +1747,7 @@ is_opaque_struct :: proc(value: Type, store: ^Store) -> bool {
|
|||||||
size :: proc(value: Type, store: ^Store, selected := target.DEFAULT) -> u64 {
|
size :: proc(value: Type, store: ^Store, selected := target.DEFAULT) -> u64 {
|
||||||
#partial switch kind(value, store) {
|
#partial switch kind(value, store) {
|
||||||
case .Scalar:
|
case .Scalar:
|
||||||
return u64(bits(value, selected)/8)
|
return u64((bits(value, selected)+7)/8)
|
||||||
case .Pointer:
|
case .Pointer:
|
||||||
return u64(target.pointer_bits(selected)/8)
|
return u64(target.pointer_bits(selected)/8)
|
||||||
case .Slice:
|
case .Slice:
|
||||||
@@ -1510,11 +1779,18 @@ size :: proc(value: Type, store: ^Store, selected := target.DEFAULT) -> u64 {
|
|||||||
}
|
}
|
||||||
offset: u64
|
offset: u64
|
||||||
max_align: u64 = 1
|
max_align: u64 = 1
|
||||||
for field in fields_for(store, value) {
|
if !item.c_layout {
|
||||||
field_align := u64(alignment_of(field.type, store, selected))
|
for field in fields_for(store, value) {
|
||||||
offset = (offset+field_align-1)/field_align*field_align
|
offset += size(field.type, store, selected)
|
||||||
offset += size(field.type, store, selected)
|
max_align = max(max_align, u64(alignment_of(field.type, store, selected)))
|
||||||
max_align = max(max_align, field_align)
|
}
|
||||||
|
} else {
|
||||||
|
for field in fields_for(store, value) {
|
||||||
|
field_align := u64(alignment_of(field.type, store, selected))
|
||||||
|
offset = (offset+field_align-1)/field_align*field_align
|
||||||
|
offset += size(field.type, store, selected)
|
||||||
|
max_align = max(max_align, field_align)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return (offset+max_align-1)/max_align*max_align
|
return (offset+max_align-1)/max_align*max_align
|
||||||
case .Union:
|
case .Union:
|
||||||
@@ -1656,6 +1932,12 @@ can_coerce_c_scalar :: proc(from, to: Type, selected := target.DEFAULT) -> bool
|
|||||||
}
|
}
|
||||||
|
|
||||||
widest :: proc(a, b: Type) -> Type {
|
widest :: proc(a, b: Type) -> Type {
|
||||||
|
if is_noreturn(a) {
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
if is_noreturn(b) {
|
||||||
|
return a
|
||||||
|
}
|
||||||
if equal(a, b) && is_concrete_scalar(a) {
|
if equal(a, b) && is_concrete_scalar(a) {
|
||||||
return a
|
return a
|
||||||
}
|
}
|
||||||
@@ -1681,13 +1963,28 @@ smallest_signed_for_literal :: proc(value: i64) -> Type {
|
|||||||
return I64
|
return I64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
smallest_unsigned_for_literal :: proc(value: u64) -> Type {
|
||||||
|
if value <= 255 {
|
||||||
|
return U8
|
||||||
|
}
|
||||||
|
if value <= 65535 {
|
||||||
|
return U16
|
||||||
|
}
|
||||||
|
if value <= 4294967295 {
|
||||||
|
return U32
|
||||||
|
}
|
||||||
|
return U64
|
||||||
|
}
|
||||||
|
|
||||||
name :: proc(value: Type) -> string {
|
name :: proc(value: Type) -> string {
|
||||||
switch value {
|
switch value {
|
||||||
case INVALID: return "<invalid>"
|
case INVALID: return "<invalid>"
|
||||||
case VOID: return "void"
|
case VOID: return "void"
|
||||||
|
case NORETURN: return "noreturn"
|
||||||
case ANYOPAQUE: return "anyopaque"
|
case ANYOPAQUE: return "anyopaque"
|
||||||
case BOOL: return "bool"
|
case BOOL: return "bool"
|
||||||
case INT: return "int"
|
case INT: return "int"
|
||||||
|
case UINT: return "uint"
|
||||||
case FLOAT: return "float"
|
case FLOAT: return "float"
|
||||||
case RANGE: return "range"
|
case RANGE: return "range"
|
||||||
case I8: return "i8"
|
case I8: return "i8"
|
||||||
|
|||||||
+5946
-467
File diff suppressed because it is too large
Load Diff
@@ -40,7 +40,7 @@ Ball :: struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# A frame's worth of player intent, as a tagged union. Each arm carries exactly
|
# A frame's worth of player intent, as a tagged union. Each arm carries exactly
|
||||||
# the data that action needs (or `void` when it needs none).
|
# the data that action needs (or `void` when it needs null).
|
||||||
Command :: union(enum) {
|
Command :: union(enum) {
|
||||||
spawn rl.Vector2 # spawn a shape at this point
|
spawn rl.Vector2 # spawn a shape at this point
|
||||||
push struct { dx f32, dy f32 } # blow every shape this way
|
push struct { dx f32, dy f32 } # blow every shape this way
|
||||||
@@ -79,8 +79,8 @@ read_command func() Command {
|
|||||||
if rl.IsMouseButtonPressed(rl.MOUSE_BUTTON_LEFT) return .spawn{ rl.GetMousePosition() }
|
if rl.IsMouseButtonPressed(rl.MOUSE_BUTTON_LEFT) return .spawn{ rl.GetMousePosition() }
|
||||||
if rl.IsKeyPressed(rl.KEY_SPACE) return .clear
|
if rl.IsKeyPressed(rl.KEY_SPACE) return .clear
|
||||||
|
|
||||||
fx f32 = 0.0
|
fx f32 := 0.0
|
||||||
fy f32 = 0.0
|
fy f32 := 0.0
|
||||||
if rl.IsKeyDown(rl.KEY_A) fx -= FORCE
|
if rl.IsKeyDown(rl.KEY_A) fx -= FORCE
|
||||||
if rl.IsKeyDown(rl.KEY_D) fx += FORCE
|
if rl.IsKeyDown(rl.KEY_D) fx += FORCE
|
||||||
if rl.IsKeyDown(rl.KEY_W) fy -= FORCE
|
if rl.IsKeyDown(rl.KEY_W) fy -= FORCE
|
||||||
@@ -130,14 +130,14 @@ step func(b @mut Ball) void {
|
|||||||
|
|
||||||
draw_ball func(b @Ball, highlight bool) void {
|
draw_ball func(b @Ball, highlight bool) void {
|
||||||
col :: color_for(b.kind)
|
col :: color_for(b.kind)
|
||||||
center rl.Vector2 = rl.Vector2{ x = b.x, y = b.y }
|
center rl.Vector2 := rl.Vector2{ x = b.x, y = b.y }
|
||||||
match b.kind {
|
match b.kind {
|
||||||
.circle: rl.DrawCircleV(center, b.radius, col)
|
.circle: rl.DrawCircleV(center, b.radius, col)
|
||||||
.square: rl.DrawPoly(center, 4, b.radius, 45.0, col)
|
.square: rl.DrawPoly(center, 4, b.radius, 45.0, col)
|
||||||
.triangle: rl.DrawPoly(center, 3, b.radius, 0.0, col)
|
.triangle: rl.DrawPoly(center, 3, b.radius, 0.0, col)
|
||||||
}
|
}
|
||||||
if highlight {
|
if highlight {
|
||||||
ring rl.Color = rl.Color{ r = 250, g = 245, b = 200, a = 255 }
|
ring rl.Color := rl.Color{ r = 250, g = 245, b = 200, a = 255 }
|
||||||
rl.DrawPoly(center, 24, b.radius + RING_PAD, 0.0, ring)
|
rl.DrawPoly(center, 24, b.radius + RING_PAD, 0.0, ring)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -152,11 +152,11 @@ main func() i32 {
|
|||||||
`[click] spawn a shape [WASD/arrows] blow wind
|
`[click] spawn a shape [WASD/arrows] blow wind
|
||||||
`[space] clear
|
`[space] clear
|
||||||
|
|
||||||
balls [CAP]mut Ball = undefined
|
balls [CAP]mut Ball := undefined
|
||||||
count usize = 0 # number of live balls, in slots 0..count
|
count usize := 0 # number of live balls, in slots 0..count
|
||||||
kc Kind = .circle # next kind to spawn
|
kc Kind := .circle # next kind to spawn
|
||||||
spin f32 = 1.0 # rotates spawn velocity for variety
|
spin f32 := 1.0 # rotates spawn velocity for variety
|
||||||
at_cap bool = false # show the "at capacity" banner
|
at_cap bool := false # show the "at capacity" banner
|
||||||
|
|
||||||
bg :: rl.Color{ r = 24, g = 26, b = 34, a = 255 }
|
bg :: rl.Color{ r = 24, g = 26, b = 34, a = 255 }
|
||||||
text :: rl.Color{ r = 225, g = 225, b = 230, a = 255 }
|
text :: rl.Color{ r = 225, g = 225, b = 230, a = 255 }
|
||||||
@@ -211,9 +211,9 @@ main func() i32 {
|
|||||||
# --- which shape is under the cursor? (optional via a value-loop) ---
|
# --- which shape is under the cursor? (optional via a value-loop) ---
|
||||||
mouse :: rl.GetMousePosition()
|
mouse :: rl.GetMousePosition()
|
||||||
sel :: for 0..(count) |i| hover: {
|
sel :: for 0..(count) |i| hover: {
|
||||||
c rl.Vector2 = rl.Vector2{ x = balls[i].x, y = balls[i].y }
|
c rl.Vector2 := rl.Vector2{ x = balls[i].x, y = balls[i].y }
|
||||||
if rl.CheckCollisionPointCircle(mouse, c, balls[i].radius) yield :hover i
|
if rl.CheckCollisionPointCircle(mouse, c, balls[i].radius) yield :hover i
|
||||||
yield none
|
yield null
|
||||||
}
|
}
|
||||||
|
|
||||||
# --- draw -----------------------------------------------------------
|
# --- draw -----------------------------------------------------------
|
||||||
@@ -222,7 +222,7 @@ main func() i32 {
|
|||||||
|
|
||||||
for (&balls) |@b, i| {
|
for (&balls) |@b, i| {
|
||||||
if (i >= count) break
|
if (i >= count) break
|
||||||
hot bool = false
|
hot bool := false
|
||||||
if sel |s| {
|
if sel |s| {
|
||||||
if (s == i) hot = true # true only for the hovered ball
|
if (s == i) hot = true # true only for the hovered ball
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ native_pair func(value native.Pair) native.Pair {
|
|||||||
global_pair native.Pair :: native.Pair { left = 1, right = 2 }
|
global_pair native.Pair :: native.Pair { left = 1, right = 2 }
|
||||||
|
|
||||||
main func() i32 {
|
main func() i32 {
|
||||||
pair native.Pair = native_pair(global_pair)
|
pair native.Pair := native_pair(global_pair)
|
||||||
pair.left = 10
|
pair.left = 10
|
||||||
pair = native.echo_pair(pair)
|
pair = native.echo_pair(pair)
|
||||||
pairs [1]native.Pair :: [pair]
|
pairs [1]native.Pair :: [pair]
|
||||||
@@ -33,7 +33,7 @@ main func() i32 {
|
|||||||
tail = 13,
|
tail = 13,
|
||||||
})
|
})
|
||||||
arrays native.Arrays :: native.echo_arrays(native.Arrays { values = [14, 15, 16] })
|
arrays native.Arrays :: native.echo_arrays(native.Arrays { values = [14, 15, 16] })
|
||||||
choice native.Choice = native.Choice { decimal = 1.0 }
|
choice native.Choice := native.Choice { decimal = 1.0 }
|
||||||
choice.integer = 17
|
choice.integer = 17
|
||||||
choice = native.echo_choice(choice)
|
choice = native.echo_choice(choice)
|
||||||
forward native.Forward :: native.echo_forward(native.Forward { value = 19 })
|
forward native.Forward :: native.echo_forward(native.Forward { value = 19 })
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ native :: import "../include/native.h"
|
|||||||
# self-referential `?*mut Node` field as C-layout-compatible.
|
# self-referential `?*mut Node` field as C-layout-compatible.
|
||||||
|
|
||||||
main func() i32 {
|
main func() i32 {
|
||||||
node native.Node = native.Node { next = none, value = 7 }
|
node native.Node := native.Node { next = null, value = 7 }
|
||||||
if node.next |_| {
|
if node.next |_| {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,23 @@
|
|||||||
_sibling func() i32 {
|
@hide sibling func() i32 {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
_Sibling :: struct {
|
@hide Sibling :: struct {
|
||||||
value i32
|
value i32
|
||||||
}
|
}
|
||||||
|
|
||||||
_sibling_value :: 1
|
@hide sibling_value :: 1
|
||||||
|
|
||||||
_collision c_func() i32 {
|
@hide:file file_sibling func() i32 {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
@hide:file File_Sibling :: struct {
|
||||||
|
value i32
|
||||||
|
}
|
||||||
|
|
||||||
|
@hide:file file_sibling_value :: 1
|
||||||
|
|
||||||
|
collision c_func() i32 {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,23 @@
|
|||||||
import "../dep"
|
import "../dep"
|
||||||
|
|
||||||
_collision func() i32 {
|
@hide collision func() i32 {
|
||||||
return 2
|
return 2
|
||||||
}
|
}
|
||||||
|
|
||||||
read_sibling func(value _Sibling) i32 {
|
read_sibling func(value Sibling) i32 {
|
||||||
return value.value + _sibling_value
|
return value.value + sibling_value
|
||||||
}
|
}
|
||||||
|
|
||||||
read_sibling_value func() i32 {
|
read_sibling_value func() i32 {
|
||||||
return _sibling_value
|
return sibling_value
|
||||||
|
}
|
||||||
|
|
||||||
|
read_file_sibling func(value File_Sibling) i32 {
|
||||||
|
return value.value
|
||||||
}
|
}
|
||||||
|
|
||||||
main func() i32 {
|
main func() i32 {
|
||||||
return _sibling() + dep._secret() + read_sibling(_Sibling { value = 1 }) + read_sibling_value()
|
return file_sibling() + file_sibling_value + sibling() + dep.secret() +
|
||||||
|
read_sibling(Sibling { value = 1 }) + read_sibling_value() +
|
||||||
|
read_file_sibling(File_Sibling { value = 1 })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
_secret func() i32 {
|
@hide:package secret c_func() i32 {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,29 @@
|
|||||||
_Thing :: struct {
|
@hide:package
|
||||||
|
helper func() i32 {
|
||||||
|
thing Thing := Thing { value = value }
|
||||||
|
return thing.value
|
||||||
|
}
|
||||||
|
|
||||||
|
@hide
|
||||||
|
Thing :: struct {
|
||||||
value i32
|
value i32
|
||||||
}
|
}
|
||||||
|
|
||||||
_value :: 1
|
@hide Local_Union :: union {
|
||||||
|
value i32
|
||||||
_helper func() i32 {
|
|
||||||
thing _Thing = _Thing { value = _value }
|
|
||||||
return thing.value
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@hide Local_Enum :: enum {
|
||||||
|
value
|
||||||
|
}
|
||||||
|
|
||||||
|
@hide Local_Opaque :: opaque
|
||||||
|
@hide Local_Distinct :: distinct i32
|
||||||
|
@hide Local_Alias :: alias i32
|
||||||
|
|
||||||
|
@hide value :: 1
|
||||||
|
@hide mutable_value i32 := 1
|
||||||
|
|
||||||
_foreign c_func() i32 {
|
_foreign c_func() i32 {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
@@ -17,6 +32,27 @@ _C_Record :: c_struct {
|
|||||||
value c_int
|
value c_int
|
||||||
}
|
}
|
||||||
|
|
||||||
from_a func() i32 {
|
@hide local_foreign c_func() i32 {
|
||||||
return _helper()
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
@hide Local_C_Record :: c_struct {
|
||||||
|
value c_int
|
||||||
|
}
|
||||||
|
|
||||||
|
@hide:file
|
||||||
|
file_helper func() i32 {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
@hide:file File_Thing :: struct {
|
||||||
|
value i32
|
||||||
|
}
|
||||||
|
|
||||||
|
@hide:file file_value :: 1
|
||||||
|
|
||||||
|
from_a func() i32 {
|
||||||
|
record Local_C_Record := Local_C_Record { value = 0 }
|
||||||
|
thing File_Thing := File_Thing { value = file_value }
|
||||||
|
return helper() + local_foreign() + file_helper() + thing.value + i32(record.value)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +1,25 @@
|
|||||||
_Thing :: struct {
|
import "../dep"
|
||||||
value i32
|
|
||||||
}
|
|
||||||
|
|
||||||
_value :: 2
|
|
||||||
|
|
||||||
_helper func() i32 {
|
|
||||||
thing _Thing = _Thing { value = _value }
|
|
||||||
return thing.value
|
|
||||||
}
|
|
||||||
|
|
||||||
Box :: struct {
|
Box :: struct {
|
||||||
_value i32
|
_value i32
|
||||||
}
|
}
|
||||||
|
|
||||||
from_b func(_input i32) i32 {
|
@hide:file file_helper func() i32 {
|
||||||
_local Box = Box { _value = _input }
|
return 2
|
||||||
record _C_Record = _C_Record { value = 0 }
|
}
|
||||||
return _helper() + _local._value + _foreign() + i32(record.value)
|
|
||||||
|
@hide:file File_Thing :: struct {
|
||||||
|
value i32
|
||||||
|
}
|
||||||
|
|
||||||
|
@hide:file file_value :: 2
|
||||||
|
|
||||||
|
from_b func(_input i32) i32 {
|
||||||
|
_local Box := Box { _value = _input }
|
||||||
|
record _C_Record := _C_Record { value = 0 }
|
||||||
|
thing Thing := Thing { value = value }
|
||||||
|
file_thing File_Thing := File_Thing { value = file_value }
|
||||||
|
return helper() + thing.value + _local._value + _foreign() + i32(record.value) +
|
||||||
|
dep._visible() + file_helper() + file_thing.value
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
_visible func() i32 {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
@@ -1 +1 @@
|
|||||||
bad = 1
|
bad := undefined
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
main func() i32 {
|
main func() i32 {
|
||||||
n usize = 4
|
n usize := 4
|
||||||
items [n]mut i32 = undefined
|
items [n]mut i32 := undefined
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,35 +2,53 @@ arraylist :: import "@std/arraylist"
|
|||||||
mem :: import "@std/mem"
|
mem :: import "@std/mem"
|
||||||
std :: import "@std"
|
std :: import "@std"
|
||||||
|
|
||||||
_fail_alloc func(_ ?*mut anyopaque, _ usize, _ usize) ?*mut u8 {
|
Token :: struct { value i32 }
|
||||||
return none
|
ScanDiagnostic :: struct { value i32 }
|
||||||
|
|
||||||
|
State :: struct {
|
||||||
|
tokens std.ArrayList(Token)
|
||||||
|
diagnostics std.ArrayList(ScanDiagnostic)
|
||||||
}
|
}
|
||||||
|
|
||||||
_fail_realloc func(_ ?*mut anyopaque, _ ?*mut u8, _ usize, _ usize, _ usize) ?*mut u8 {
|
arrlist_test std.ArrayList(Token) := arraylist.init(mem.c_allocator)
|
||||||
return none
|
|
||||||
|
init func(allocator mem.Allocator) State {
|
||||||
|
return State {
|
||||||
|
tokens = arraylist.init(allocator),
|
||||||
|
diagnostics = arraylist.init(allocator),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_fail_free func(_ ?*mut anyopaque, _ ?*mut u8, _ usize, _ usize) void {}
|
@hide fail_alloc func(_ ?@mut anyopaque, _ usize, _ usize) ?*mut u8 {
|
||||||
|
return null
|
||||||
_fail_vtable mem.AllocatorVTable :: mem.AllocatorVTable {
|
|
||||||
alloc = _fail_alloc,
|
|
||||||
realloc = _fail_realloc,
|
|
||||||
free = _fail_free,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_fail_allocator mem.Allocator :: mem.Allocator {
|
@hide fail_realloc func(_ ?@mut anyopaque, _ ?*mut u8, _ usize, _ usize, _ usize) ?*mut u8 {
|
||||||
context = none,
|
return null
|
||||||
vtable = &_fail_vtable,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_noop func() void {}
|
@hide fail_free func(_ ?@mut anyopaque, _ ?*mut u8, _ usize, _ usize) void {}
|
||||||
|
|
||||||
|
@hide fail_vtable mem.AllocatorVTable :: mem.AllocatorVTable {
|
||||||
|
alloc = fail_alloc,
|
||||||
|
realloc = fail_realloc,
|
||||||
|
free = fail_free,
|
||||||
|
}
|
||||||
|
|
||||||
|
@hide fail_allocator mem.Allocator :: mem.Allocator {
|
||||||
|
context = null,
|
||||||
|
vtable = &fail_vtable,
|
||||||
|
}
|
||||||
|
|
||||||
run func() i32 ! mem.AllocError {
|
run func() i32 ! mem.AllocError {
|
||||||
values std.ArrayList(i32) = arraylist.init(mem.c_allocator)
|
state State :: init(mem.c_allocator)
|
||||||
|
_ = state
|
||||||
|
|
||||||
|
values std.ArrayList(i32) := arraylist.init(mem.c_allocator)
|
||||||
defer arraylist.deinit(&values)
|
defer arraylist.deinit(&values)
|
||||||
if (values.items.len != 0 or values.capacity != 0) return 1
|
if (values.items.len != 0 or values.capacity != 0) return 1
|
||||||
|
|
||||||
i usize = 0
|
i usize := 0
|
||||||
while i < 20 : i += 1 {
|
while i < 20 : i += 1 {
|
||||||
arraylist.append(&values, i32(i)) catch |_| {
|
arraylist.append(&values, i32(i)) catch |_| {
|
||||||
return .out_of_memory
|
return .out_of_memory
|
||||||
@@ -53,7 +71,7 @@ run func() i32 ! mem.AllocError {
|
|||||||
}
|
}
|
||||||
if (values.items.len != 1 or values.items[0] != 7 or values.capacity != capacity) return 7
|
if (values.items.len != 1 or values.items[0] != 7 or values.capacity != capacity) return 7
|
||||||
|
|
||||||
empty_values arraylist.ArrayList([0]u8) = arraylist.init(mem.c_allocator)
|
empty_values arraylist.ArrayList([0]u8) := arraylist.init(mem.c_allocator)
|
||||||
defer arraylist.deinit(&empty_values)
|
defer arraylist.deinit(&empty_values)
|
||||||
zero [0]u8 :: []
|
zero [0]u8 :: []
|
||||||
arraylist.append(&empty_values, zero) catch |_| {
|
arraylist.append(&empty_values, zero) catch |_| {
|
||||||
@@ -61,11 +79,10 @@ run func() i32 ! mem.AllocError {
|
|||||||
}
|
}
|
||||||
if (empty_values.items.len != 1) return 8
|
if (empty_values.items.len != 1) return 8
|
||||||
|
|
||||||
failed arraylist.ArrayList(i32) = arraylist.init(i32, _fail_allocator)
|
failed arraylist.ArrayList(i32) := arraylist.init(i32, fail_allocator)
|
||||||
failed_as_expected bool = false
|
failed_as_expected bool := false
|
||||||
arraylist.append(&failed, 1) catch |_| {
|
arraylist.append(&failed, 1) catch |_| {
|
||||||
failed_as_expected = true
|
failed_as_expected = true
|
||||||
yield _noop()
|
|
||||||
}
|
}
|
||||||
if (failed_as_expected == false or failed.items.len != 0 or failed.capacity != 0) return 9
|
if (failed_as_expected == false or failed.items.len != 0 or failed.capacity != 0) return 9
|
||||||
arraylist.deinit(&failed)
|
arraylist.deinit(&failed)
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
fold_bits func(value u8) u8 {
|
||||||
|
return (~value & 255) xor 15
|
||||||
|
}
|
||||||
|
|
||||||
|
folded u8 :: fold_bits(240)
|
||||||
|
contextual u8 :: ~0 & 255
|
||||||
|
contextual_shift u8 :: 1 << 7
|
||||||
|
Buffer :: alias [u8(1) << 3]u8
|
||||||
|
|
||||||
|
runtime_left func(value u16, count u8) u16 {
|
||||||
|
return value << count
|
||||||
|
}
|
||||||
|
|
||||||
|
c_count_shift func(value u8, count c_uint) u8 {
|
||||||
|
return value << count
|
||||||
|
}
|
||||||
|
|
||||||
|
c_count_fold u8 :: c_count_shift(3, 2)
|
||||||
|
|
||||||
|
main func() i32 {
|
||||||
|
buffer Buffer := undefined
|
||||||
|
if (folded != 0) return 1
|
||||||
|
if (contextual != 255) return 28
|
||||||
|
if (contextual_shift != 128) return 29
|
||||||
|
if (buffer.len != 8) return 2
|
||||||
|
if ((u8(240) & u8(204)) != 192) return 3
|
||||||
|
if ((u8(240) | u8(15)) != 255) return 4
|
||||||
|
if ((u8(240) xor u8(255)) != 15) return 5
|
||||||
|
if ((u8(129) << 1) != 2) return 6
|
||||||
|
if ((i8(-4) >> 1) != -2) return 7
|
||||||
|
if ((u8(128) >> 1) != 64) return 8
|
||||||
|
if ((u8(64) <<| 2) != 255) return 9
|
||||||
|
if ((i8(64) <<| 2) != 127) return 10
|
||||||
|
if ((i8(-64) <<| 2) != -128) return 11
|
||||||
|
if ((u8(1) <<| 8) != 255) return 12
|
||||||
|
if ((u8(0) <<| 80) != 0) return 13
|
||||||
|
if (runtime_left(3, 4) != 48) return 14
|
||||||
|
if (c_count_shift(3, 2) != 12) return 30
|
||||||
|
if (c_count_fold != 12) return 31
|
||||||
|
if (~u16(0) != 65535) return 16
|
||||||
|
if ((i16(-2) >> 1) != -1) return 17
|
||||||
|
if ((u32(2147483648) >> 31) != 1) return 18
|
||||||
|
if ((i32(-2147483647) << 1) != 2) return 19
|
||||||
|
if ((~u64(0) >> 63) != 1) return 20
|
||||||
|
if ((i64(-2) >> 1) != -1) return 21
|
||||||
|
if ((c_uchar(128) >> 7) != 1) return 22
|
||||||
|
if ((c_int(-2) >> 1) != -1) return 23
|
||||||
|
if ((c_uint(3) << 4) != 48) return 24
|
||||||
|
if ((c_ulonglong(1) <<| 64) != ~c_ulonglong(0)) return 25
|
||||||
|
if ((c_ushort(240) xor c_ushort(255)) != 15) return 26
|
||||||
|
if ((c_longlong(64) <<| 60) != maxval!(c_longlong)) return 27
|
||||||
|
|
||||||
|
value u8 := 3
|
||||||
|
value <<= 2
|
||||||
|
value |= 1
|
||||||
|
value xor= 5
|
||||||
|
value &= 15
|
||||||
|
value >>= 1
|
||||||
|
value <<|= 7
|
||||||
|
if (value != 255) return 15
|
||||||
|
return 42
|
||||||
|
}
|
||||||
@@ -7,8 +7,8 @@
|
|||||||
|
|
||||||
main func() i32 {
|
main func() i32 {
|
||||||
# 1. `break` out of a `while` once i reaches 5.
|
# 1. `break` out of a `while` once i reaches 5.
|
||||||
i i32 = 0
|
i i32 := 0
|
||||||
a i32 = 0
|
a i32 := 0
|
||||||
while i < 100 : i += 1 {
|
while i < 100 : i += 1 {
|
||||||
if (i == 5) break
|
if (i == 5) break
|
||||||
a += 1
|
a += 1
|
||||||
@@ -16,7 +16,7 @@ main func() i32 {
|
|||||||
if (a != 5) return 101
|
if (a != 5) return 101
|
||||||
|
|
||||||
# 2. `continue` past n == 3 while summing 0..9 (45 - 3 = 42).
|
# 2. `continue` past n == 3 while summing 0..9 (45 - 3 = 42).
|
||||||
b i32 = 0
|
b i32 := 0
|
||||||
for 0..10 |n| {
|
for 0..10 |n| {
|
||||||
if (n == 3) continue
|
if (n == 3) continue
|
||||||
b = b + n
|
b = b + n
|
||||||
@@ -25,7 +25,7 @@ main func() i32 {
|
|||||||
|
|
||||||
# 3. Nested loops: the inner `break` exits only the inner loop, so the outer
|
# 3. Nested loops: the inner `break` exits only the inner loop, so the outer
|
||||||
# loop still runs all three iterations (each contributing one y == 0 pass).
|
# loop still runs all three iterations (each contributing one y == 0 pass).
|
||||||
c i32 = 0
|
c i32 := 0
|
||||||
for 0..3 |x| {
|
for 0..3 |x| {
|
||||||
for 0..3 |y| {
|
for 0..3 |y| {
|
||||||
if (y == 1) break
|
if (y == 1) break
|
||||||
@@ -38,7 +38,7 @@ main func() i32 {
|
|||||||
# 4. `continue` on the final element of an inclusive range bounded by the
|
# 4. `continue` on the final element of an inclusive range bounded by the
|
||||||
# element type's maximum must exit cleanly, not overflow the increment.
|
# element type's maximum must exit cleanly, not overflow the increment.
|
||||||
hi u8 :: 255
|
hi u8 :: 255
|
||||||
d i32 = 0
|
d i32 := 0
|
||||||
for 0..=hi |v| {
|
for 0..=hi |v| {
|
||||||
if (v == 255) continue
|
if (v == 255) continue
|
||||||
d += 1
|
d += 1
|
||||||
@@ -47,7 +47,7 @@ main func() i32 {
|
|||||||
|
|
||||||
# 5. `while true` is exitable via `break` (so it is not an infinite loop and
|
# 5. `while true` is exitable via `break` (so it is not an infinite loop and
|
||||||
# the code after it is reachable).
|
# the code after it is reachable).
|
||||||
e i32 = 0
|
e i32 := 0
|
||||||
while true {
|
while true {
|
||||||
e += 1
|
e += 1
|
||||||
if (e == 7) break
|
if (e == 7) break
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# Compound assignment (`+=`, `-=`, `*=`, `/=`) and explicit integer division.
|
# Compound assignment (`+=`, `-=`, `*=`, `/=`) and explicit integer division.
|
||||||
|
|
||||||
check_float func() i32 {
|
check_float func() i32 {
|
||||||
x f64 = 10.0
|
x f64 := 10.0
|
||||||
x /= 4.0 # 2.5
|
x /= 4.0 # 2.5
|
||||||
x *= 2.0 # 5.0
|
x *= 2.0 # 5.0
|
||||||
x -= 1.0 # 4.0
|
x -= 1.0 # 4.0
|
||||||
@@ -13,8 +13,8 @@ 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
|
||||||
@@ -23,17 +23,17 @@ check_unsigned func() i32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
main func() i32 {
|
main func() i32 {
|
||||||
total i32 = 0
|
total i32 := 0
|
||||||
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
|
||||||
|
|
||||||
# compound assignment as a while-loop update
|
# compound assignment as a while-loop update
|
||||||
i i32 = 0
|
i i32 := 0
|
||||||
while i < 5 : i += 1 {
|
while i < 5 : i += 1 {
|
||||||
total += 1 # +5 => 21
|
total += 1 # +5 => 21
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ nested func(value int) int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
make_array func($N usize) [N]u8 {
|
make_array func($N usize) [N]u8 {
|
||||||
data [N]u8 = undefined
|
data [N]u8 := undefined
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,13 +14,13 @@ id func($T type, value T) T {
|
|||||||
}
|
}
|
||||||
|
|
||||||
buffer func($T type, $N usize, value T) [N]T {
|
buffer func($T type, $N usize, value T) [N]T {
|
||||||
data [N]T = undefined
|
data [N]T := undefined
|
||||||
_ = value
|
_ = value
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
zero func($T type) T {
|
zero func($T type) T {
|
||||||
value T = undefined
|
value T := undefined
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,6 +38,10 @@ fixed_len func($T type, $N usize, value @Fixed(T, N)) usize {
|
|||||||
return value.values.len
|
return value.values.len
|
||||||
}
|
}
|
||||||
|
|
||||||
|
same_type func($Expected, $Actual type, _ Actual) bool {
|
||||||
|
return $(Expected == Actual)
|
||||||
|
}
|
||||||
|
|
||||||
take_i32 func(value i32) i32 {
|
take_i32 func(value i32) i32 {
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
@@ -82,10 +86,17 @@ main func() i32 {
|
|||||||
if fixed_len(&fixed) != 3 {
|
if fixed_len(&fixed) != 3 {
|
||||||
return 7
|
return 7
|
||||||
}
|
}
|
||||||
assigned i32 = 1
|
assigned i32 := 1
|
||||||
assigned = zero()
|
assigned = zero()
|
||||||
_ = assigned
|
_ = assigned
|
||||||
_ = take_i32(zero())
|
_ = take_i32(zero())
|
||||||
_ = return_zero()
|
_ = return_zero()
|
||||||
|
optional ?u32 := null
|
||||||
|
if !same_type(?u32, optional) {
|
||||||
|
return 8
|
||||||
|
}
|
||||||
|
if same_type(?u16, optional) {
|
||||||
|
return 9
|
||||||
|
}
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,15 @@ Point :: struct {
|
|||||||
y i32
|
y i32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Callback_Config :: struct {
|
||||||
|
call func(value i32) i32
|
||||||
|
}
|
||||||
|
|
||||||
|
Callback_Choice :: union(enum) {
|
||||||
|
call func(value i32) i32
|
||||||
|
empty void
|
||||||
|
}
|
||||||
|
|
||||||
Box :: union(enum) {
|
Box :: union(enum) {
|
||||||
point Point
|
point Point
|
||||||
empty void
|
empty void
|
||||||
@@ -23,8 +32,8 @@ make_point func() Point {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sum_loop func(limit i32) i32 {
|
sum_loop func(limit i32) i32 {
|
||||||
total i32 = 0
|
total i32 := 0
|
||||||
i i32 = 0
|
i i32 := 0
|
||||||
while i < limit {
|
while i < limit {
|
||||||
i += 1
|
i += 1
|
||||||
if i == 2 {
|
if i == 2 {
|
||||||
@@ -36,8 +45,8 @@ sum_loop func(limit i32) i32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sum_for func() i32 {
|
sum_for func() i32 {
|
||||||
total i32 = 0
|
total i32 := 0
|
||||||
values [_]i32 = [1, 2, 3]
|
values [_]i32 := [1, 2, 3]
|
||||||
for values |value, index| {
|
for values |value, index| {
|
||||||
total += value + index
|
total += value + index
|
||||||
}
|
}
|
||||||
@@ -45,7 +54,7 @@ sum_for func() i32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
defer_value func() i32 {
|
defer_value func() i32 {
|
||||||
value i32 = 1
|
value i32 := 1
|
||||||
{
|
{
|
||||||
defer value += 10
|
defer value += 10
|
||||||
value += 1
|
value += 1
|
||||||
@@ -64,7 +73,7 @@ maybe func(flag bool) ?i32 {
|
|||||||
if flag {
|
if flag {
|
||||||
return 9
|
return 9
|
||||||
}
|
}
|
||||||
return none
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
may_fail func(flag bool) i32 ! Error {
|
may_fail func(flag bool) i32 ! Error {
|
||||||
@@ -78,10 +87,49 @@ increment func(value i32) i32 {
|
|||||||
return value + 1
|
return value + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double func(value i32) i32 {
|
||||||
|
return value * 2
|
||||||
|
}
|
||||||
|
|
||||||
|
decrement_c c_func(value i32) i32 {
|
||||||
|
return value - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
apply_comptime func($callback func(value i32) i32, value i32) i32 {
|
||||||
|
return callback(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
apply_comptime_c func($callback c_func(value i32) i32, value i32) i32 {
|
||||||
|
return callback(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
apply_comptime_config func($config Callback_Config, value i32) i32 {
|
||||||
|
return config.call(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
apply_comptime_array func($callbacks [2]func(value i32) i32, value i32) i32 {
|
||||||
|
return callbacks[0](value) + callbacks[1](value)
|
||||||
|
}
|
||||||
|
|
||||||
|
apply_comptime_optional func($callback ?func(value i32) i32, value i32) i32 {
|
||||||
|
return callback?(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
apply_comptime_choice func($choice Callback_Choice, value i32) i32 {
|
||||||
|
return match choice {
|
||||||
|
.call |callback|: callback(value)
|
||||||
|
.empty: value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
call_native func(callback @func(value i32) i32, value i32) i32 {
|
call_native func(callback @func(value i32) i32, value i32) i32 {
|
||||||
return callback(value)
|
return callback(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
materialize_callback func($callback func(value i32) i32, value i32) i32 {
|
||||||
|
return call_native(callback, value)
|
||||||
|
}
|
||||||
|
|
||||||
call_fallible func(callback @func(flag bool) i32 ! Error, flag bool) i32 ! Error {
|
call_fallible func(callback @func(flag bool) i32 ! Error, flag bool) i32 ! Error {
|
||||||
return try callback(flag)
|
return try callback(flag)
|
||||||
}
|
}
|
||||||
@@ -92,7 +140,7 @@ use_try func() i32 ! Error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ct_errdefer func(fail bool) i32 ! Error {
|
ct_errdefer func(fail bool) i32 ! Error {
|
||||||
trace i32 = 0
|
trace i32 := 0
|
||||||
defer trace = trace * 10 + 1
|
defer trace = trace * 10 + 1
|
||||||
errdefer |err| {
|
errdefer |err| {
|
||||||
if (err == .bad) trace = trace * 10 + 2
|
if (err == .bad) trace = trace * 10 + 2
|
||||||
@@ -103,7 +151,7 @@ ct_errdefer func(fail bool) i32 ! Error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ct_try_errdefer func() i32 ! Error {
|
ct_try_errdefer func() i32 ! Error {
|
||||||
trace i32 = 0
|
trace i32 := 0
|
||||||
defer trace = trace * 10 + 4
|
defer trace = trace * 10 + 4
|
||||||
errdefer |err| {
|
errdefer |err| {
|
||||||
if (err == .bad) trace = trace * 10 + 5
|
if (err == .bad) trace = trace * 10 + 5
|
||||||
@@ -122,10 +170,8 @@ ct_errdefer_check func() i32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
recover func() i32 {
|
recover func() i32 {
|
||||||
return may_fail(true) catch |e| {
|
return may_fail(true) catch |e| match e {
|
||||||
match e {
|
.bad: 5
|
||||||
.bad: yield 5
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,20 +185,20 @@ alias_add func(left @mut i32, right @mut i32) void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
storage_mutation func() i32 {
|
storage_mutation func() i32 {
|
||||||
values [3]mut i32 = [1, 2, 3]
|
values [3]mut i32 := [1, 2, 3]
|
||||||
values[0] += 1
|
values[0] += 1
|
||||||
bump_ptr(&values[1])
|
bump_ptr(&values[1])
|
||||||
|
|
||||||
view []mut i32 = values[..]
|
view []mut i32 := values[..]
|
||||||
for view |@item| {
|
for view |@item| {
|
||||||
item^ += 1
|
item^ += 1
|
||||||
}
|
}
|
||||||
|
|
||||||
pointer *mut i32 = view.ptr
|
pointer *mut i32 := view.ptr
|
||||||
pointer[2] += 1
|
pointer[2] += 1
|
||||||
alias_add(&values[0], &view[0])
|
alias_add(&values[0], &view[0])
|
||||||
|
|
||||||
box Box = Box { point = Point { x = 2, y = 3 } }
|
box Box := Box { point = Point { x = 2, y = 3 } }
|
||||||
match box {
|
match box {
|
||||||
.point |@p|: p.x += values[1]
|
.point |@p|: p.x += values[1]
|
||||||
.empty: values[0] = values[0]
|
.empty: values[0] = values[0]
|
||||||
@@ -180,6 +226,13 @@ main func() i32 {
|
|||||||
recovered i32 :: $recover()
|
recovered i32 :: $recover()
|
||||||
storage i32 :: $storage_mutation()
|
storage i32 :: $storage_mutation()
|
||||||
called i32 :: $call_native(increment, 11)
|
called i32 :: $call_native(increment, 11)
|
||||||
|
comptime_callback i32 :: $apply_comptime(increment, 12)
|
||||||
|
comptime_literal i32 :: $apply_comptime(func(value i32) i32 { return value + 2 }, 12)
|
||||||
|
comptime_c_callback i32 :: $apply_comptime_c(decrement_c, 15)
|
||||||
|
comptime_config i32 :: $apply_comptime_config(Callback_Config {call = increment}, 15)
|
||||||
|
comptime_array i32 :: $apply_comptime_array([increment, double], 3)
|
||||||
|
comptime_optional i32 :: $apply_comptime_optional(increment, 16)
|
||||||
|
comptime_choice i32 :: $apply_comptime_choice(Callback_Choice {call = double}, 9)
|
||||||
fallible_ok i32 :: $call_fallible(may_fail, false) catch 0
|
fallible_ok i32 :: $call_fallible(may_fail, false) catch 0
|
||||||
fallible_err i32 :: $call_fallible(may_fail, true) catch |e| {
|
fallible_err i32 :: $call_fallible(may_fail, true) catch |e| {
|
||||||
result i32 :: match e {
|
result i32 :: match e {
|
||||||
@@ -239,5 +292,29 @@ main func() i32 {
|
|||||||
if ERRDEFER != 42 {
|
if ERRDEFER != 42 {
|
||||||
return 17
|
return 17
|
||||||
}
|
}
|
||||||
|
if comptime_callback != 13 or comptime_literal != 14 or
|
||||||
|
comptime_c_callback != 14 or comptime_config != 16 or
|
||||||
|
comptime_array != 10 or comptime_optional != 17 or comptime_choice != 18 {
|
||||||
|
return 18
|
||||||
|
}
|
||||||
|
if apply_comptime(increment, 20) != 21 or apply_comptime(increment, 21) != 22 or
|
||||||
|
apply_comptime(double, 20) != 40 {
|
||||||
|
return 19
|
||||||
|
}
|
||||||
|
if apply_comptime(func(value i32) i32 { return value + 3 }, 20) != 23 {
|
||||||
|
return 20
|
||||||
|
}
|
||||||
|
if apply_comptime_c(decrement_c, 20) != 19 or
|
||||||
|
apply_comptime_config(Callback_Config {call = increment}, 20) != 21 {
|
||||||
|
return 21
|
||||||
|
}
|
||||||
|
if apply_comptime_array([increment, double], 4) != 13 or
|
||||||
|
apply_comptime_optional(increment, 4) != 5 or
|
||||||
|
apply_comptime_choice(Callback_Choice {call = double}, 4) != 8 {
|
||||||
|
return 22
|
||||||
|
}
|
||||||
|
if materialize_callback(increment, 30) != 31 {
|
||||||
|
return 23
|
||||||
|
}
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
make_array func($N usize) [N]u8 {
|
make_array func($N usize) [N]u8 {
|
||||||
data [N]u8 = undefined
|
data [N]u8 := undefined
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,18 +6,18 @@ observe func(counter @mut i32, value ?i32) ?i32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
main func() i32 {
|
main func() i32 {
|
||||||
total i32 = 0
|
total i32 := 0
|
||||||
|
|
||||||
# present optional scalar -> binds v to the unwrapped value
|
# present optional scalar -> binds v to the unwrapped value
|
||||||
a ?i32 = 40
|
a ?i32 := 40
|
||||||
if a |v| {
|
if a |v| {
|
||||||
total = total + v # 40
|
total = total + v # 40
|
||||||
} else {
|
} else {
|
||||||
total = total + 99
|
total = total + 99
|
||||||
}
|
}
|
||||||
|
|
||||||
# none -> else branch taken; the binding is not in scope there
|
# null -> else branch taken; the binding is not in scope there
|
||||||
b ?i32 = none
|
b ?i32 := null
|
||||||
if b |v| {
|
if b |v| {
|
||||||
total = total + v
|
total = total + v
|
||||||
} else {
|
} else {
|
||||||
@@ -25,20 +25,20 @@ main func() i32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# optional pointer present -> binds q to a non-null @i32; deref proves it
|
# optional pointer present -> binds q to a non-null @i32; deref proves it
|
||||||
n i32 = 0
|
n i32 := 0
|
||||||
p ?@i32 = &n
|
p ?@i32 := &n
|
||||||
if p |q| {
|
if p |q| {
|
||||||
total = total + q^ # +0
|
total = total + q^ # +0
|
||||||
}
|
}
|
||||||
|
|
||||||
# optional pointer none -> skipped
|
# optional pointer null -> skipped
|
||||||
z ?@i32 = none
|
z ?@i32 := null
|
||||||
if z |_| {
|
if z |_| {
|
||||||
total = total + 1000
|
total = total + 1000
|
||||||
}
|
}
|
||||||
|
|
||||||
# guarded multi-unwrap exposes every capture to the guard and then-block
|
# guarded multi-unwrap exposes every capture to the guard and then-block
|
||||||
age ?i32 = 2
|
age ?i32 := 2
|
||||||
if a and age |value, years : value + years == 42| {
|
if a and age |value, years : value + years == 42| {
|
||||||
total = total
|
total = total
|
||||||
} else {
|
} else {
|
||||||
@@ -46,7 +46,7 @@ main func() i32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# parenthesized chains and three-value unwraps are equivalent
|
# parenthesized chains and three-value unwraps are equivalent
|
||||||
bonus ?i32 = 0
|
bonus ?i32 := 0
|
||||||
if (a and age and bonus) |value, years, extra : value + years + extra == 42| {
|
if (a and age and bonus) |value, years, extra : value + years + extra == 42| {
|
||||||
total = total
|
total = total
|
||||||
} else {
|
} else {
|
||||||
@@ -64,7 +64,7 @@ main func() i32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# a failed unwrap prevents later expressions from being evaluated
|
# a failed unwrap prevents later expressions from being evaluated
|
||||||
calls i32 = 0
|
calls i32 := 0
|
||||||
if b and observe(&calls, age) |missing, observed| {
|
if b and observe(&calls, age) |missing, observed| {
|
||||||
total = total + missing + observed
|
total = total + missing + observed
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ noisy func() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
main func() i32 {
|
main func() i32 {
|
||||||
total i32 = 0
|
total i32 := 0
|
||||||
|
|
||||||
# comparisons drive if / else if / else
|
# comparisons drive if / else if / else
|
||||||
total = total + classify(-5) # 1
|
total = total + classify(-5) # 1
|
||||||
@@ -45,9 +45,9 @@ main func() i32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# block scoping: inner bindings do not escape the block
|
# block scoping: inner bindings do not escape the block
|
||||||
x i32 = 1
|
x i32 := 1
|
||||||
if x == 1 {
|
if x == 1 {
|
||||||
inner_x i32 = 100
|
inner_x i32 := 100
|
||||||
if inner_x == 100 {
|
if inner_x == 100 {
|
||||||
total = total + 5 # 40
|
total = total + 5 # 40
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
# The return value is captured before defers run, so the mutation here does not
|
# The return value is captured before defers run, so the mutation here does not
|
||||||
# change what is returned (Zig semantics).
|
# change what is returned (Zig semantics).
|
||||||
spill_check func() i32 {
|
spill_check func() i32 {
|
||||||
x i32 = 5
|
x i32 := 5
|
||||||
defer x = 999
|
defer x = 999
|
||||||
return x
|
return x
|
||||||
}
|
}
|
||||||
@@ -15,7 +15,7 @@ spill_check func() i32 {
|
|||||||
# A function-scope defer runs only at function exit; a `break` runs the loop-body
|
# A function-scope defer runs only at function exit; a `break` runs the loop-body
|
||||||
# defer but NOT the enclosing function-scope defer.
|
# defer but NOT the enclosing function-scope defer.
|
||||||
enclosing_defer_check func() i32 {
|
enclosing_defer_check func() i32 {
|
||||||
v i32 = 0
|
v i32 := 0
|
||||||
defer v = v + 100
|
defer v = v + 100
|
||||||
for 0..3 |i| {
|
for 0..3 |i| {
|
||||||
defer v = v + 1
|
defer v = v + 1
|
||||||
@@ -89,7 +89,7 @@ main func() i32 {
|
|||||||
if (spill_check() != 5) return 101
|
if (spill_check() != 5) return 101
|
||||||
|
|
||||||
# 2. LIFO ordering, run at end of each loop iteration.
|
# 2. LIFO ordering, run at end of each loop iteration.
|
||||||
r i32 = 0
|
r i32 := 0
|
||||||
for 0..1 |i| {
|
for 0..1 |i| {
|
||||||
defer r = r * 2 + 1 # registered first -> runs last
|
defer r = r * 2 + 1 # registered first -> runs last
|
||||||
defer r = r * 2 # registered second -> runs first
|
defer r = r * 2 # registered second -> runs first
|
||||||
@@ -99,16 +99,16 @@ main func() i32 {
|
|||||||
|
|
||||||
# 3. scoped bare block + scoped defer (defer fires at the closing brace, and
|
# 3. scoped bare block + scoped defer (defer fires at the closing brace, and
|
||||||
# the block-local is not visible afterwards).
|
# the block-local is not visible afterwards).
|
||||||
a i32 = 1
|
a i32 := 1
|
||||||
{
|
{
|
||||||
defer a = 4
|
defer a = 4
|
||||||
c i32 = 3
|
c i32 := 3
|
||||||
_ = c
|
_ = c
|
||||||
}
|
}
|
||||||
if (a != 4) return 103
|
if (a != 4) return 103
|
||||||
|
|
||||||
# 4. `defer { ... }` block: all its statements run (in order) at scope close.
|
# 4. `defer { ... }` block: all its statements run (in order) at scope close.
|
||||||
s i32 = 0
|
s i32 := 0
|
||||||
{
|
{
|
||||||
defer {
|
defer {
|
||||||
s = s + 1
|
s = s + 1
|
||||||
@@ -119,7 +119,7 @@ main func() i32 {
|
|||||||
if (s != 60) return 104 # 5 -> 6 -> 60
|
if (s != 60) return 104 # 5 -> 6 -> 60
|
||||||
|
|
||||||
# 5. `break` flushes the loop-body defer.
|
# 5. `break` flushes the loop-body defer.
|
||||||
bc i32 = 0
|
bc i32 := 0
|
||||||
for 0..5 |i| {
|
for 0..5 |i| {
|
||||||
defer bc = bc + 1
|
defer bc = bc + 1
|
||||||
if (i == 2) break
|
if (i == 2) break
|
||||||
@@ -127,7 +127,7 @@ main func() i32 {
|
|||||||
if (bc != 3) return 105 # i=0,1 fall-through + i=2 break
|
if (bc != 3) return 105 # i=0,1 fall-through + i=2 break
|
||||||
|
|
||||||
# 6. `continue` flushes the loop-body defer.
|
# 6. `continue` flushes the loop-body defer.
|
||||||
cc i32 = 0
|
cc i32 := 0
|
||||||
for 0..3 |i| {
|
for 0..3 |i| {
|
||||||
defer cc = cc + 1
|
defer cc = cc + 1
|
||||||
if (i == 1) continue
|
if (i == 1) continue
|
||||||
@@ -139,7 +139,7 @@ main func() i32 {
|
|||||||
if (enclosing_defer_check() != 2) return 107
|
if (enclosing_defer_check() != 2) return 107
|
||||||
|
|
||||||
# 8. errdefer is skipped on success; ordinary defers stay interleaved.
|
# 8. errdefer is skipped on success; ordinary defers stay interleaved.
|
||||||
trace i32 = 0
|
trace i32 := 0
|
||||||
if ((explicit_cleanup(false, &trace) catch 0) != 7 or trace != 31) return 108
|
if ((explicit_cleanup(false, &trace) catch 0) != 7 or trace != 31) return 108
|
||||||
|
|
||||||
# 9. Explicit errors run errdefer and expose the captured error.
|
# 9. Explicit errors run errdefer and expose the captured error.
|
||||||
|
|||||||
@@ -9,8 +9,14 @@ LocalID :: distinct u32
|
|||||||
PointID :: distinct Point
|
PointID :: distinct Point
|
||||||
Bytes :: distinct [2]u8
|
Bytes :: distinct [2]u8
|
||||||
WrappedID :: distinct LocalID
|
WrappedID :: distinct LocalID
|
||||||
|
Signed :: distinct i32
|
||||||
|
Mask :: distinct u8
|
||||||
|
Real :: distinct f64
|
||||||
|
|
||||||
|
|
||||||
static_id LocalID :: LocalID(42)
|
static_id LocalID :: LocalID(42)
|
||||||
|
static_expression LocalID :: (LocalID(3) + 5) * 2
|
||||||
|
|
||||||
|
|
||||||
take func(value LocalID) LocalID {
|
take func(value LocalID) LocalID {
|
||||||
return value
|
return value
|
||||||
@@ -18,20 +24,84 @@ take func(value LocalID) LocalID {
|
|||||||
|
|
||||||
main func() i32 {
|
main func() i32 {
|
||||||
id LocalID :: LocalID(7)
|
id LocalID :: LocalID(7)
|
||||||
copy LocalID = take(id)
|
copy LocalID := take(id)
|
||||||
maybe ?LocalID = copy
|
maybe ?LocalID := copy
|
||||||
pointer @LocalID = ©
|
pointer @LocalID := ©
|
||||||
point PointID :: PointID(Point { x = 1, y = 2 })
|
point PointID :: PointID(Point { x = 1, y = 2 })
|
||||||
bytes Bytes :: Bytes([3, 4])
|
bytes Bytes :: Bytes([3, 4])
|
||||||
wrapped WrappedID :: WrappedID(id)
|
wrapped WrappedID :: WrappedID(id)
|
||||||
remote ids.UserID :: ids.UserID(8)
|
remote ids.UserID :: ids.UserID(8)
|
||||||
remote_copy ids.UserID :: ids.make(9)
|
remote_copy ids.UserID :: ids.make(9)
|
||||||
_ = static_id
|
|
||||||
|
if id + 1 != 8 or 1 + id != 8 { return 1 }
|
||||||
|
if id - 2 != 5 or 2 * id != 14 or id * 2 != 14 { return 2 }
|
||||||
|
if -Signed(5) != -5 { return 3 }
|
||||||
|
|
||||||
|
real Real :: Real(1.5)
|
||||||
|
if real + 0.5 != 2.0 or 0.5 + real != 2.0 or real - 0.5 != 1.0 or
|
||||||
|
real * 2.0 != 3.0 or real / 0.5 != 3.0 {
|
||||||
|
return 4
|
||||||
|
}
|
||||||
|
|
||||||
|
if !(id == 7) or !(7 == id) or !(id != 8) or !(8 != id) or
|
||||||
|
!(id < 8) or !(6 < id) or !(id <= 7) or !(7 <= id) or
|
||||||
|
!(id > 6) or !(8 > id) or !(id >= 7) or !(7 >= id) {
|
||||||
|
return 5
|
||||||
|
}
|
||||||
|
|
||||||
|
signed Signed :: Signed(-7)
|
||||||
|
if divtrunc!(signed, 3) != -2 or divfloor!(signed, 3) != -3 or
|
||||||
|
divexact!(Signed(8), 2) != 4 or divceil!(signed, 3) != -2 or
|
||||||
|
rem!(signed, 3) != -1 or mod!(signed, 3) != 2 {
|
||||||
|
return 6
|
||||||
|
}
|
||||||
|
|
||||||
|
mask Mask :: Mask(10)
|
||||||
|
if ~mask != 245 or (mask & 6) != 2 or (mask | 5) != 15 or (mask xor 3) != 9 {
|
||||||
|
return 7
|
||||||
|
}
|
||||||
|
if mask << u8(1) != 20 or mask >> u8(1) != 5 or Mask(128) <<| u8(1) != 255 {
|
||||||
|
return 8
|
||||||
|
}
|
||||||
|
|
||||||
|
arithmetic Signed := Signed(4)
|
||||||
|
arithmetic += 3
|
||||||
|
arithmetic -= 2
|
||||||
|
arithmetic *= 5
|
||||||
|
fraction Real := Real(3.0)
|
||||||
|
fraction /= 2.0
|
||||||
|
if arithmetic != 25 or fraction != 1.5 { return 9 }
|
||||||
|
|
||||||
|
bits Mask := Mask(3)
|
||||||
|
bits |= 8
|
||||||
|
bits xor= 2
|
||||||
|
bits &= 9
|
||||||
|
bits <<= u8(1)
|
||||||
|
bits >>= u8(1)
|
||||||
|
bits <<|= u8(5)
|
||||||
|
if bits != 255 { return 10 }
|
||||||
|
|
||||||
|
values [10]u8 := [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
||||||
|
if values[usize(LocalID(4))] != 4 { return 11 }
|
||||||
|
section []u8 := values[usize(LocalID(2))..usize(LocalID(5))]
|
||||||
|
if section.len != 3 or section[usize(0)] != 2 or section[usize(2)] != 4 { return 12 }
|
||||||
|
|
||||||
|
if u32(id) != 7 or usize(id) != 7 or f64(id) != 7.0 { return 13 }
|
||||||
|
extracted LocalID := LocalID(wrapped)
|
||||||
|
if extracted != id { return 14 }
|
||||||
|
|
||||||
|
minimum Signed := minval!(Signed)
|
||||||
|
maximum Mask := maxval!(Mask)
|
||||||
|
nested_max WrappedID := maxval!(WrappedID)
|
||||||
|
if i32(minimum) != minval!(i32) or u8(maximum) != 255 or u32(nested_max) != maxval!(u32) {
|
||||||
|
return 15
|
||||||
|
}
|
||||||
|
if static_expression != 16 { return 16 }
|
||||||
|
|
||||||
_ = maybe
|
_ = maybe
|
||||||
_ = pointer
|
_ = pointer
|
||||||
_ = point
|
_ = point
|
||||||
_ = bytes
|
_ = bytes
|
||||||
_ = wrapped
|
|
||||||
_ = remote
|
_ = remote
|
||||||
_ = remote_copy
|
_ = remote_copy
|
||||||
return 0
|
return 0
|
||||||
|
|||||||
@@ -3,6 +3,9 @@ Animal :: enum {
|
|||||||
cat
|
cat
|
||||||
}
|
}
|
||||||
|
|
||||||
|
STATE_STARTED c_int :: 10
|
||||||
|
STATE_STOPPED c_int :: 20
|
||||||
|
|
||||||
favorite func() Animal {
|
favorite func() Animal {
|
||||||
return .cat
|
return .cat
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
animals :: import "./animals"
|
animals :: import "./animals"
|
||||||
|
|
||||||
State :: enum(u16) {
|
State :: enum(u16) {
|
||||||
started = 10
|
started = animals.STATE_STARTED
|
||||||
running
|
running
|
||||||
stopped = 20
|
stopped = animals.STATE_STOPPED
|
||||||
}
|
}
|
||||||
|
|
||||||
initial State :: State.started
|
initial State :: State.started
|
||||||
@@ -17,10 +17,13 @@ identity c_func(value State) State {
|
|||||||
}
|
}
|
||||||
|
|
||||||
main func() i32 {
|
main func() i32 {
|
||||||
state State = identity(.running)
|
state State := identity(.running)
|
||||||
values [2]State :: [.started, State.stopped]
|
values [2]State :: [.started, State.stopped]
|
||||||
animal animals.Animal :: animals.Animal.dog
|
animal animals.Animal :: animals.Animal.dog
|
||||||
if same(state, .running) and
|
if same(state, .running) and
|
||||||
|
u16(State.started) == 10 and
|
||||||
|
u16(State.running) == 11 and
|
||||||
|
u16(State.stopped) == 20 and
|
||||||
values[0] != values[1] and
|
values[0] != values[1] and
|
||||||
animal != animals.favorite() and
|
animal != animals.favorite() and
|
||||||
initial == State.started {
|
initial == State.started {
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ inline_detail func(value i32) i32 ! union(enum) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
main func() i32 {
|
main func() i32 {
|
||||||
acc i32 = 0
|
acc i32 := 0
|
||||||
|
|
||||||
a :: maybe(0) catch 7
|
a :: maybe(0) catch 7
|
||||||
b :: maybe(4) catch 99
|
b :: maybe(4) catch 99
|
||||||
@@ -166,11 +166,11 @@ main func() i32 {
|
|||||||
|
|
||||||
acc = acc + a + b + c + d + e + f + g + h + i + j + k + l + m + n + o + p
|
acc = acc + a + b + c + d + e + f + g + h + i + j + k + l + m + n + o + p
|
||||||
acc = acc + pick(.left)
|
acc = acc + pick(.left)
|
||||||
r Right = .right
|
r Right := .right
|
||||||
acc = acc + pick(r)
|
acc = acc + pick(r)
|
||||||
box BoxA = .a{8}
|
box BoxA := .a{8}
|
||||||
acc = acc + payload(box)
|
acc = acc + payload(box)
|
||||||
empty BoxB = .b
|
empty BoxB := .b
|
||||||
acc = acc + payload(empty)
|
acc = acc + payload(empty)
|
||||||
acc = acc + payload(.a{9})
|
acc = acc + payload(.a{9})
|
||||||
|
|
||||||
|
|||||||
@@ -5,9 +5,9 @@ pass func(value range) range {
|
|||||||
}
|
}
|
||||||
|
|
||||||
main func() i32 {
|
main func() i32 {
|
||||||
total i32 = 0
|
total i32 := 0
|
||||||
|
|
||||||
items [3]mut i32 = [1, 2, 3]
|
items [3]mut i32 := [1, 2, 3]
|
||||||
for items |item, index| {
|
for items |item, index| {
|
||||||
total = total + item
|
total = total + item
|
||||||
_ = index
|
_ = index
|
||||||
@@ -17,7 +17,7 @@ main func() i32 {
|
|||||||
item^ = item^ + 1
|
item^ = item^ + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
view []mut i32 = items[..]
|
view []mut i32 := items[..]
|
||||||
for view |@item, index| {
|
for view |@item, index| {
|
||||||
item^ = item^ + 1
|
item^ = item^ + 1
|
||||||
_ = index
|
_ = index
|
||||||
|
|||||||
@@ -6,23 +6,23 @@ make_range func(calls @mut i32, end usize) range {
|
|||||||
global_range :: 0..1
|
global_range :: 0..1
|
||||||
|
|
||||||
main func() i32 {
|
main func() i32 {
|
||||||
total i32 = 0
|
total i32 := 0
|
||||||
|
|
||||||
first u8 = 254
|
first u8 := 254
|
||||||
last u8 = 255
|
last u8 := 255
|
||||||
for first..=last |value| {
|
for first..=last |value| {
|
||||||
_ = value
|
_ = value
|
||||||
total = total + 1
|
total = total + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
signed_start i8 = -2
|
signed_start i8 := -2
|
||||||
signed_end i8 = 1
|
signed_end i8 := 1
|
||||||
for signed_start..signed_end |value| {
|
for signed_start..signed_end |value| {
|
||||||
_ = value
|
_ = value
|
||||||
total = total + 1
|
total = total + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
limit usize = 3
|
limit usize := 3
|
||||||
for 0..(limit + 1) |value| {
|
for 0..(limit + 1) |value| {
|
||||||
_ = value
|
_ = value
|
||||||
total = total + 1
|
total = total + 1
|
||||||
@@ -41,19 +41,19 @@ main func() i32 {
|
|||||||
total = total + 100
|
total = total + 100
|
||||||
}
|
}
|
||||||
|
|
||||||
empty [0]i32 = []
|
empty [0]i32 := []
|
||||||
for empty |value| {
|
for empty |value| {
|
||||||
_ = value
|
_ = value
|
||||||
total = total + 100
|
total = total + 100
|
||||||
}
|
}
|
||||||
|
|
||||||
pointed i32 = 3
|
pointed i32 := 3
|
||||||
pointers [1]@mut i32 = [&pointed]
|
pointers [1]@mut i32 := [&pointed]
|
||||||
for pointers |pointer| {
|
for pointers |pointer| {
|
||||||
total = total + pointer^
|
total = total + pointer^
|
||||||
}
|
}
|
||||||
|
|
||||||
calls i32 = 0
|
calls i32 := 0
|
||||||
for make_range(&calls, 2) |value| {
|
for make_range(&calls, 2) |value| {
|
||||||
_ = value
|
_ = value
|
||||||
total = total + 1
|
total = total + 1
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
main func() i32 {
|
main func() i32 {
|
||||||
items [3]mut i32 = undefined
|
items [3]mut i32 := undefined
|
||||||
i i32 = 1
|
i i32 := 1
|
||||||
items[i] = 42
|
items[i] = 42
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
main func() i32 {
|
main func() i32 {
|
||||||
items [3]mut i32 = undefined
|
items [3]mut i32 := undefined
|
||||||
i int = 1
|
i u32 := 1
|
||||||
items[i] = 42
|
items[i] = 42
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
Kind :: enum {
|
||||||
|
first
|
||||||
|
second
|
||||||
|
third
|
||||||
|
}
|
||||||
|
|
||||||
|
Pair :: struct {
|
||||||
|
left i32
|
||||||
|
right i32
|
||||||
|
}
|
||||||
|
|
||||||
|
Value :: union(enum) {
|
||||||
|
number i32
|
||||||
|
pair Pair
|
||||||
|
empty void
|
||||||
|
}
|
||||||
|
|
||||||
|
enum_score func(kind Kind) i32 {
|
||||||
|
result :: match kind {
|
||||||
|
.first: 1
|
||||||
|
inline |value|: {
|
||||||
|
yield match value {
|
||||||
|
.second: 2
|
||||||
|
.third: 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
equal_value func(a, b Value) bool {
|
||||||
|
if (tag!(a) != tag!(b)) return false
|
||||||
|
result :: match a {
|
||||||
|
inline |value, tag|: {
|
||||||
|
yield match tag {
|
||||||
|
.number: value == field!(b, tagname!(tag))
|
||||||
|
.pair: {
|
||||||
|
other :: field!(b, tagname!(tag))
|
||||||
|
yield value.left == other.left and value.right == other.right
|
||||||
|
}
|
||||||
|
.empty: {
|
||||||
|
yield true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
increment func(value @mut Value) void {
|
||||||
|
match value^ {
|
||||||
|
inline |@payload, tag|: match tag {
|
||||||
|
.number: payload^ += 1
|
||||||
|
.pair: payload.left += 1
|
||||||
|
.empty: _ = payload
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main func() i32 {
|
||||||
|
if enum_score(.first) != 1 or enum_score(.second) != 2 or enum_score(.third) != 3 {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
known :: $tag!(Value{number = 1})
|
||||||
|
known_direct :: tag!(Value{pair = Pair{left = 0, right = 0}})
|
||||||
|
if (known != .number) or (known_direct != .pair) {
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
|
||||||
|
a Value := .number{41}
|
||||||
|
b Value := .number{41}
|
||||||
|
if !equal_value(a, b) or equal_value(a, .pair{left = 41, right = 0}) {
|
||||||
|
return 3
|
||||||
|
}
|
||||||
|
increment(&a)
|
||||||
|
if a.number != 42 {
|
||||||
|
return 4
|
||||||
|
}
|
||||||
|
|
||||||
|
pair_a Value := .pair{left = 2, right = 3}
|
||||||
|
pair_b Value := .pair{left = 2, right = 3}
|
||||||
|
if !equal_value(pair_a, pair_b) or !equal_value(.empty, .empty) {
|
||||||
|
return 5
|
||||||
|
}
|
||||||
|
increment(&pair_a)
|
||||||
|
if pair_a.pair.left != 3 {
|
||||||
|
return 6
|
||||||
|
}
|
||||||
|
|
||||||
|
empty Value := .empty
|
||||||
|
increment(&empty)
|
||||||
|
return 0
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
bad int = 4
|
bad int := 4
|
||||||
|
|
||||||
read_bad func() int {
|
read_bad func() int {
|
||||||
return bad
|
return bad
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
io :: import "@std/io"
|
io :: import "@std/io"
|
||||||
|
process :: import "@std/process"
|
||||||
|
|
||||||
_read_ok func(_ ?*mut anyopaque, _ io.ReadStream, buffer []mut u8) usize ! io.ReadError {
|
@hide read_ok func(_ ?@mut anyopaque, _ io.Handle, buffer []mut u8) usize ! io.ReadError {
|
||||||
if buffer.len == 0 {
|
if buffer.len == 0 {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
@@ -12,113 +13,138 @@ _read_ok func(_ ?*mut anyopaque, _ io.ReadStream, buffer []mut u8) usize ! io.Re
|
|||||||
return 2
|
return 2
|
||||||
}
|
}
|
||||||
|
|
||||||
_read_too_much func(_ ?*mut anyopaque, _ io.ReadStream, buffer []mut u8) usize ! io.ReadError {
|
@hide read_too_much func(_ ?@mut anyopaque, _ io.Handle, buffer []mut u8) usize ! io.ReadError {
|
||||||
return buffer.len + 1
|
return buffer.len + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
_read_eof func(_ ?*mut anyopaque, _ io.ReadStream, _ []mut u8) usize ! io.ReadError {
|
@hide read_eof func(_ ?@mut anyopaque, _ io.Handle, _ []mut u8) usize ! io.ReadError {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
_write_short func(_ ?*mut anyopaque, _ io.WriteStream, bytes []u8) usize ! io.WriteError {
|
@hide write_short func(_ ?@mut anyopaque, _ io.Handle, bytes []u8) usize ! io.WriteError {
|
||||||
if bytes.len > 2 {
|
if bytes.len > 2 {
|
||||||
return 2
|
return 2
|
||||||
}
|
}
|
||||||
return bytes.len
|
return bytes.len
|
||||||
}
|
}
|
||||||
|
|
||||||
_write_none func(_ ?*mut anyopaque, _ io.WriteStream, _ []u8) usize ! io.WriteError {
|
@hide write_none func(_ ?@mut anyopaque, _ io.Handle, _ []u8) usize ! io.WriteError {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
_write_too_much func(_ ?*mut anyopaque, _ io.WriteStream, bytes []u8) usize ! io.WriteError {
|
@hide write_too_much func(_ ?@mut anyopaque, _ io.Handle, bytes []u8) usize ! io.WriteError {
|
||||||
return bytes.len + 1
|
return bytes.len + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
_ok_vtable io.IoVTable :: io.IoVTable {
|
ok_reader func() io.Reader {
|
||||||
read = _read_ok,
|
|
||||||
write = _write_short,
|
|
||||||
}
|
|
||||||
|
|
||||||
_read_bad_vtable io.IoVTable :: io.IoVTable {
|
|
||||||
read = _read_too_much,
|
|
||||||
write = _write_short,
|
|
||||||
}
|
|
||||||
|
|
||||||
_eof_vtable io.IoVTable :: io.IoVTable {
|
|
||||||
read = _read_eof,
|
|
||||||
write = _write_short,
|
|
||||||
}
|
|
||||||
|
|
||||||
_write_none_vtable io.IoVTable :: io.IoVTable {
|
|
||||||
read = _read_ok,
|
|
||||||
write = _write_none,
|
|
||||||
}
|
|
||||||
|
|
||||||
_write_bad_vtable io.IoVTable :: io.IoVTable {
|
|
||||||
read = _read_ok,
|
|
||||||
write = _write_too_much,
|
|
||||||
}
|
|
||||||
|
|
||||||
reader_for func(vtable @io.IoVTable) io.Reader {
|
|
||||||
return io.Reader {
|
return io.Reader {
|
||||||
impl = io.Io {context = none, vtable = vtable},
|
context = null,
|
||||||
stream = .stdin,
|
handle = io.Handle {file_desc = 0},
|
||||||
|
read = read_ok,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
writer_for func(vtable @io.IoVTable) io.Writer {
|
bad_reader func() io.Reader {
|
||||||
|
return io.Reader {
|
||||||
|
context = null,
|
||||||
|
handle = io.Handle {file_desc = 0},
|
||||||
|
read = read_too_much,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
eof_reader func() io.Reader {
|
||||||
|
return io.Reader {
|
||||||
|
context = null,
|
||||||
|
handle = io.Handle {file_desc = 0},
|
||||||
|
read = read_eof,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
short_writer func() io.Writer {
|
||||||
return io.Writer {
|
return io.Writer {
|
||||||
impl = io.Io {context = none, vtable = vtable},
|
context = null,
|
||||||
stream = .stdout,
|
handle = io.Handle {file_desc = 0},
|
||||||
|
write = write_short,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
none_writer func() io.Writer {
|
||||||
|
return io.Writer {
|
||||||
|
context = null,
|
||||||
|
handle = io.Handle {file_desc = 0},
|
||||||
|
write = write_none,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bad_writer func() io.Writer {
|
||||||
|
return io.Writer {
|
||||||
|
context = null,
|
||||||
|
handle = io.Handle {file_desc = 0},
|
||||||
|
write = write_too_much,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rejects_bad_read func() bool {
|
rejects_bad_read func() bool {
|
||||||
buffer [1]mut u8 = [0]
|
buffer [1]mut u8 := [0]
|
||||||
_ = io.read(reader_for(&_read_bad_vtable), buffer[..]) catch |err| {
|
_ = io.read(bad_reader(), buffer[..]) catch |err| {
|
||||||
return err == .read_failed
|
return err == .read_failed
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
rejects_no_progress func() bool {
|
rejects_no_progress func() bool {
|
||||||
io.write_all(writer_for(&_write_none_vtable), "x") catch |err| {
|
io.print(none_writer(), "{s}", {"x",}) catch |err| {
|
||||||
return err == .no_progress
|
return err == .no_progress
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
rejects_bad_write func() bool {
|
rejects_bad_write func() bool {
|
||||||
_ = io.write(writer_for(&_write_bad_vtable), "x") catch |err| {
|
_ = io.write(bad_writer(), "x") catch |err| {
|
||||||
return err == .write_failed
|
return err == .write_failed
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
main func(system io.Io) i32 {
|
main func(init process.Init) i32 {
|
||||||
buffer [2]mut u8 = [0, 0]
|
system io.Io :: init.io
|
||||||
count usize :: io.read(reader_for(&_ok_vtable), buffer[..]) catch 0
|
buffer [2]mut u8 := [0, 0]
|
||||||
|
count usize :: io.read(ok_reader(), buffer[..]) catch 0
|
||||||
if count != 2 or buffer[0] != 'o' or buffer[1] != 'k' {
|
if count != 2 or buffer[0] != 'o' or buffer[1] != 'k' {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
eof usize :: io.read(reader_for(&_eof_vtable), buffer[..]) catch 1
|
eof usize :: io.read(eof_reader(), buffer[..]) catch 1
|
||||||
empty_read usize :: io.read(reader_for(&_read_bad_vtable), buffer[0..0]) catch 1
|
empty_read usize :: io.read(bad_reader(), buffer[0..0]) catch 1
|
||||||
empty_write usize :: io.write(writer_for(&_write_bad_vtable), "") catch 1
|
empty_write usize :: io.write(bad_writer(), "") catch 1
|
||||||
if eof != 0 or empty_read != 0 or empty_write != 0 {
|
if eof != 0 or empty_read != 0 or empty_write != 0 {
|
||||||
return 5
|
return 5
|
||||||
}
|
}
|
||||||
io.write_all(writer_for(&_ok_vtable), "partial") catch |_| {
|
io.print(short_writer(), "{s}{d}", {"partial", 37}) catch |_| {
|
||||||
return 2
|
return 2
|
||||||
}
|
}
|
||||||
if !rejects_bad_read() or !rejects_no_progress() or !rejects_bad_write() {
|
if !rejects_bad_read() or !rejects_no_progress() or !rejects_bad_write() {
|
||||||
return 3
|
return 3
|
||||||
}
|
}
|
||||||
io.write_all(io.Writer {
|
io.print(io.stdout(system), "io-ok {d} {{bro}}\n", {37,}) catch |_| {
|
||||||
impl = system,
|
|
||||||
stream = .stdout,
|
|
||||||
}, "io-ok\n") catch |_| {
|
|
||||||
return 4
|
return 4
|
||||||
}
|
}
|
||||||
|
io.print(io.stdout(system), "bounds={d}/{d} {d}/{d} {d}/{d} {d}/{d} {d}/{d} {d}/{d} {d}/{d} {d}/{d} {d}/{d} {d}/{d} {d}/{d} {d}/{d} {d}/{d} {d}/{d}\n", {
|
||||||
|
minval!(i8), maxval!(u8),
|
||||||
|
minval!(i16), maxval!(u16),
|
||||||
|
minval!(i32), maxval!(u32),
|
||||||
|
minval!(i64), maxval!(u64),
|
||||||
|
minval!(isize), maxval!(usize),
|
||||||
|
minval!(c_char), maxval!(c_char),
|
||||||
|
minval!(c_schar), maxval!(c_uchar),
|
||||||
|
minval!(c_short), maxval!(c_ushort),
|
||||||
|
minval!(c_int), maxval!(c_uint),
|
||||||
|
minval!(c_long), maxval!(c_ulong),
|
||||||
|
minval!(c_longlong), maxval!(c_ulonglong),
|
||||||
|
0, 0,
|
||||||
|
1, 1,
|
||||||
|
-1, 1,
|
||||||
|
}) catch |_| {
|
||||||
|
return 6
|
||||||
|
}
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,18 +63,18 @@ make_box func() Box {
|
|||||||
}
|
}
|
||||||
|
|
||||||
main func() i32 {
|
main func() i32 {
|
||||||
acc i32 = 0
|
acc i32 := 0
|
||||||
|
|
||||||
dog Data = Data{ dog = 9 }
|
dog Data := Data{ dog = 9 }
|
||||||
bird Data = Data{ bird = 38 }
|
bird Data := Data{ bird = 38 }
|
||||||
acc = acc + describe(dog) + describe(bird) # 10 + 40 = 50
|
acc = acc + describe(dog) + describe(bird) # 10 + 40 = 50
|
||||||
|
|
||||||
# same-type multi-pattern capture
|
# same-type multi-pattern capture
|
||||||
acc = acc + payload_of(dog) + payload_of(bird) # 9 + 38 = 47
|
acc = acc + payload_of(dog) + payload_of(bird) # 9 + 38 = 47
|
||||||
|
|
||||||
# enum statement match, exhaustive, with a multi-pattern arm
|
# enum statement match, exhaustive, with a multi-pattern arm
|
||||||
a Animal = .bird
|
a Animal := .bird
|
||||||
rank i32 = 0
|
rank i32 := 0
|
||||||
match a {
|
match a {
|
||||||
.dog, .cat: rank = 1
|
.dog, .cat: rank = 1
|
||||||
.bird: rank = 3
|
.bird: rank = 3
|
||||||
@@ -89,7 +89,7 @@ main func() i32 {
|
|||||||
acc = acc + legs # +2
|
acc = acc + legs # +2
|
||||||
|
|
||||||
# scalar match: a range arm, a multi-literal arm, and a mandatory else
|
# scalar match: a range arm, a multi-literal arm, and a mandatory else
|
||||||
bucket i32 = 0
|
bucket i32 := 0
|
||||||
match rank {
|
match rank {
|
||||||
0..3: bucket = 1 # exclusive 0,1,2 — does not include 3
|
0..3: bucket = 1 # exclusive 0,1,2 — does not include 3
|
||||||
3, 4: bucket = 5 # rank is 3
|
3, 4: bucket = 5 # rank is 3
|
||||||
@@ -98,8 +98,8 @@ main func() i32 {
|
|||||||
acc = acc + bucket # +5
|
acc = acc + bucket # +5
|
||||||
|
|
||||||
# void-payload variant: contextual construction (`.empty` coerces to Box) + no-capture arm
|
# void-payload variant: contextual construction (`.empty` coerces to Box) + no-capture arm
|
||||||
e Box = .empty
|
e Box := .empty
|
||||||
hit i32 = 0
|
hit i32 := 0
|
||||||
match e {
|
match e {
|
||||||
.point |pt|: hit = pt.x
|
.point |pt|: hit = pt.x
|
||||||
.empty: hit = 7
|
.empty: hit = 7
|
||||||
@@ -107,7 +107,7 @@ main func() i32 {
|
|||||||
acc = acc + hit # +7
|
acc = acc + hit # +7
|
||||||
|
|
||||||
# pointer capture mutates the subject's payload in place
|
# pointer capture mutates the subject's payload in place
|
||||||
b Box = Box{ point = Point{ x = 1, y = 2 } }
|
b Box := Box{ point = Point{ x = 1, y = 2 } }
|
||||||
match b {
|
match b {
|
||||||
.point |@p|: p.x = 10
|
.point |@p|: p.x = 10
|
||||||
.empty: hit = hit
|
.empty: hit = hit
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
mem :: import "@std/mem"
|
mem :: import "@std/mem"
|
||||||
|
|
||||||
raw_allocator_test func() i32 {
|
raw_allocator_test func() i32 {
|
||||||
resized ?*mut u8 = mem.raw_realloc(mem.c_allocator, none, 0, 4, 1)
|
resized ?*mut u8 := mem.raw_realloc(mem.c_allocator, null, 0, 4, 1)
|
||||||
if resized |bytes| {
|
if resized |bytes| {
|
||||||
bytes[0] = 10
|
bytes[0] = 10
|
||||||
bytes[1] = 20
|
bytes[1] = 20
|
||||||
@@ -11,7 +11,7 @@ raw_allocator_test func() i32 {
|
|||||||
return 20
|
return 20
|
||||||
}
|
}
|
||||||
|
|
||||||
grown ?*mut u8 = mem.raw_realloc(mem.c_allocator, resized, 4, 8, 1)
|
grown ?*mut u8 := mem.raw_realloc(mem.c_allocator, resized, 4, 8, 1)
|
||||||
if grown |bytes| {
|
if grown |bytes| {
|
||||||
resized = grown
|
resized = grown
|
||||||
if bytes[0] != 10 or bytes[1] != 20 or bytes[2] != 30 or bytes[3] != 40 {
|
if bytes[0] != 10 or bytes[1] != 20 or bytes[2] != 30 or bytes[3] != 40 {
|
||||||
@@ -23,7 +23,7 @@ raw_allocator_test func() i32 {
|
|||||||
return 22
|
return 22
|
||||||
}
|
}
|
||||||
|
|
||||||
shrunk ?*mut u8 = mem.raw_realloc(mem.c_allocator, resized, 8, 2, 1)
|
shrunk ?*mut u8 := mem.raw_realloc(mem.c_allocator, resized, 8, 2, 1)
|
||||||
if shrunk |bytes| {
|
if shrunk |bytes| {
|
||||||
resized = shrunk
|
resized = shrunk
|
||||||
if bytes[0] != 10 or bytes[1] != 20 {
|
if bytes[0] != 10 or bytes[1] != 20 {
|
||||||
@@ -35,7 +35,7 @@ raw_allocator_test func() i32 {
|
|||||||
return 24
|
return 24
|
||||||
}
|
}
|
||||||
|
|
||||||
invalid ?*mut u8 = mem.raw_realloc(mem.c_allocator, resized, 2, 4, 24)
|
invalid ?*mut u8 := mem.raw_realloc(mem.c_allocator, resized, 2, 4, 24)
|
||||||
if invalid |memory| {
|
if invalid |memory| {
|
||||||
mem.raw_free(mem.c_allocator, memory, 4, 24)
|
mem.raw_free(mem.c_allocator, memory, 4, 24)
|
||||||
mem.raw_free(mem.c_allocator, resized, 2, 1)
|
mem.raw_free(mem.c_allocator, resized, 2, 1)
|
||||||
@@ -54,14 +54,14 @@ raw_allocator_test func() i32 {
|
|||||||
return 27
|
return 27
|
||||||
}
|
}
|
||||||
|
|
||||||
over_aligned ?*mut u8 = mem.raw_alloc(mem.c_allocator, 4, 32)
|
over_aligned ?*mut u8 := mem.raw_alloc(mem.c_allocator, 4, 32)
|
||||||
if over_aligned |bytes| {
|
if over_aligned |bytes| {
|
||||||
bytes[0] = 11
|
bytes[0] = 11
|
||||||
bytes[1] = 22
|
bytes[1] = 22
|
||||||
} else {
|
} else {
|
||||||
return 28
|
return 28
|
||||||
}
|
}
|
||||||
over_aligned_grown ?*mut u8 = mem.raw_realloc(mem.c_allocator, over_aligned, 4, 8, 32)
|
over_aligned_grown ?*mut u8 := mem.raw_realloc(mem.c_allocator, over_aligned, 4, 8, 32)
|
||||||
if over_aligned_grown |bytes| {
|
if over_aligned_grown |bytes| {
|
||||||
if bytes[0] != 11 or bytes[1] != 22 {
|
if bytes[0] != 11 or bytes[1] != 22 {
|
||||||
mem.raw_free(mem.c_allocator, over_aligned_grown, 8, 32)
|
mem.raw_free(mem.c_allocator, over_aligned_grown, 8, 32)
|
||||||
@@ -73,19 +73,19 @@ raw_allocator_test func() i32 {
|
|||||||
return 30
|
return 30
|
||||||
}
|
}
|
||||||
|
|
||||||
zero_alignment ?*mut u8 = mem.raw_alloc(mem.c_allocator, 8, 0)
|
zero_alignment ?*mut u8 := mem.raw_alloc(mem.c_allocator, 8, 0)
|
||||||
if zero_alignment |memory| {
|
if zero_alignment |memory| {
|
||||||
mem.raw_free(mem.c_allocator, memory, 8, 0)
|
mem.raw_free(mem.c_allocator, memory, 8, 0)
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
bad_alignment ?*mut u8 = mem.raw_alloc(mem.c_allocator, 8, 24)
|
bad_alignment ?*mut u8 := mem.raw_alloc(mem.c_allocator, 8, 24)
|
||||||
if bad_alignment |memory| {
|
if bad_alignment |memory| {
|
||||||
mem.raw_free(mem.c_allocator, memory, 8, 24)
|
mem.raw_free(mem.c_allocator, memory, 8, 24)
|
||||||
return 2
|
return 2
|
||||||
}
|
}
|
||||||
|
|
||||||
aligned ?*mut u8 = mem.raw_alloc(mem.c_allocator, 64, 32)
|
aligned ?*mut u8 := mem.raw_alloc(mem.c_allocator, 64, 32)
|
||||||
defer mem.raw_free(mem.c_allocator, aligned, 64, 32)
|
defer mem.raw_free(mem.c_allocator, aligned, 64, 32)
|
||||||
if aligned |bytes| {
|
if aligned |bytes| {
|
||||||
bytes[0] = 1
|
bytes[0] = 1
|
||||||
|
|||||||
@@ -11,9 +11,9 @@ TaskList :: struct {
|
|||||||
|
|
||||||
task_list_init func(allocator mem.Allocator) TaskList {
|
task_list_init func(allocator mem.Allocator) TaskList {
|
||||||
return TaskList {
|
return TaskList {
|
||||||
ids = none,
|
ids = null,
|
||||||
priorities = none,
|
priorities = null,
|
||||||
durations = none,
|
durations = null,
|
||||||
len = 0,
|
len = 0,
|
||||||
capacity = 0,
|
capacity = 0,
|
||||||
allocator = allocator,
|
allocator = allocator,
|
||||||
@@ -21,13 +21,13 @@ task_list_init func(allocator mem.Allocator) TaskList {
|
|||||||
}
|
}
|
||||||
|
|
||||||
alloc_i32s func(allocator mem.Allocator, count usize) ?[]mut i32 {
|
alloc_i32s func(allocator mem.Allocator, count usize) ?[]mut i32 {
|
||||||
fallback [1]mut i32 = undefined
|
fallback [1]mut i32 := undefined
|
||||||
failed bool = false
|
failed bool := false
|
||||||
values []mut i32 = mem.alloc(allocator, count) catch |_| {
|
values []mut i32 := mem.alloc(allocator, count) catch |_| {
|
||||||
failed = true
|
failed = true
|
||||||
yield (&fallback).ptr[..0]
|
yield (&fallback).ptr[..0]
|
||||||
}
|
}
|
||||||
if (failed) return none
|
if (failed) return null
|
||||||
return values
|
return values
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,14 +42,14 @@ task_list_reserve func(list @mut TaskList, capacity usize) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
new_ids ?[]mut i32 = alloc_i32s(list.allocator, capacity)
|
new_ids ?[]mut i32 := alloc_i32s(list.allocator, capacity)
|
||||||
new_priorities ?[]mut i32 = alloc_i32s(list.allocator, capacity)
|
new_priorities ?[]mut i32 := alloc_i32s(list.allocator, capacity)
|
||||||
new_durations ?[]mut i32 = alloc_i32s(list.allocator, capacity)
|
new_durations ?[]mut i32 := alloc_i32s(list.allocator, capacity)
|
||||||
|
|
||||||
if (new_ids and new_priorities and new_durations) |ids, priorities, durations| {
|
if (new_ids and new_priorities and new_durations) |ids, priorities, durations| {
|
||||||
if list.len > 0 {
|
if list.len > 0 {
|
||||||
if (list.ids and list.priorities and list.durations) |old_ids, old_priorities, old_durations| {
|
if (list.ids and list.priorities and list.durations) |old_ids, old_priorities, old_durations| {
|
||||||
i usize = 0
|
i usize := 0
|
||||||
while i < list.len : i += 1 {
|
while i < list.len : i += 1 {
|
||||||
ids[i] = old_ids[i]
|
ids[i] = old_ids[i]
|
||||||
priorities[i] = old_priorities[i]
|
priorities[i] = old_priorities[i]
|
||||||
@@ -82,7 +82,7 @@ task_list_reserve func(list @mut TaskList, capacity usize) bool {
|
|||||||
|
|
||||||
task_list_push func(list @mut TaskList, id i32, priority i32, duration i32) bool {
|
task_list_push func(list @mut TaskList, id i32, priority i32, duration i32) bool {
|
||||||
if list.len == list.capacity {
|
if list.len == list.capacity {
|
||||||
new_capacity usize = 2
|
new_capacity usize := 2
|
||||||
if list.capacity != 0 {
|
if list.capacity != 0 {
|
||||||
new_capacity = list.capacity * 2
|
new_capacity = list.capacity * 2
|
||||||
}
|
}
|
||||||
@@ -92,7 +92,7 @@ task_list_push func(list @mut TaskList, id i32, priority i32, duration i32) bool
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (list.ids and list.priorities and list.durations) |ids, priorities, durations| {
|
if (list.ids and list.priorities and list.durations) |ids, priorities, durations| {
|
||||||
index usize = list.len
|
index usize := list.len
|
||||||
ids[index] = id
|
ids[index] = id
|
||||||
priorities[index] = priority
|
priorities[index] = priority
|
||||||
durations[index] = duration
|
durations[index] = duration
|
||||||
@@ -113,11 +113,11 @@ task_list_best_id func(list @mut TaskList) i32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (list.ids and list.priorities and list.durations) |ids, priorities, durations| {
|
if (list.ids and list.priorities and list.durations) |ids, priorities, durations| {
|
||||||
best_index usize = 0
|
best_index usize := 0
|
||||||
best_score i32 = task_score(priorities[0], durations[0])
|
best_score i32 := task_score(priorities[0], durations[0])
|
||||||
i usize = 1
|
i usize := 1
|
||||||
while i < list.len : i += 1 {
|
while i < list.len : i += 1 {
|
||||||
score i32 = task_score(priorities[i], durations[i])
|
score i32 := task_score(priorities[i], durations[i])
|
||||||
if score > best_score {
|
if score > best_score {
|
||||||
best_score = score
|
best_score = score
|
||||||
best_index = i
|
best_index = i
|
||||||
@@ -130,9 +130,9 @@ task_list_best_id func(list @mut TaskList) i32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
task_list_total_duration func(list @mut TaskList) i32 {
|
task_list_total_duration func(list @mut TaskList) i32 {
|
||||||
total i32 = 0
|
total i32 := 0
|
||||||
if list.durations |durations| {
|
if list.durations |durations| {
|
||||||
i usize = 0
|
i usize := 0
|
||||||
while i < list.len : i += 1 {
|
while i < list.len : i += 1 {
|
||||||
total += durations[i]
|
total += durations[i]
|
||||||
}
|
}
|
||||||
@@ -144,15 +144,15 @@ task_list_deinit func(list @mut TaskList) void {
|
|||||||
free_i32s(list.allocator, list.ids)
|
free_i32s(list.allocator, list.ids)
|
||||||
free_i32s(list.allocator, list.priorities)
|
free_i32s(list.allocator, list.priorities)
|
||||||
free_i32s(list.allocator, list.durations)
|
free_i32s(list.allocator, list.durations)
|
||||||
list.ids = none
|
list.ids = null
|
||||||
list.priorities = none
|
list.priorities = null
|
||||||
list.durations = none
|
list.durations = null
|
||||||
list.len = 0
|
list.len = 0
|
||||||
list.capacity = 0
|
list.capacity = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
task_list_test func() i32 {
|
task_list_test func() i32 {
|
||||||
tasks TaskList = task_list_init(mem.c_allocator)
|
tasks TaskList := task_list_init(mem.c_allocator)
|
||||||
defer task_list_deinit(&tasks)
|
defer task_list_deinit(&tasks)
|
||||||
|
|
||||||
if task_list_push(&tasks, 101, 3, 5) == false {
|
if task_list_push(&tasks, 101, 3, 5) == false {
|
||||||
|
|||||||
@@ -1,55 +1,55 @@
|
|||||||
mem :: import "@std/mem"
|
mem :: import "@std/mem"
|
||||||
|
|
||||||
_probe_count func(context ?*mut anyopaque) void {
|
@hide probe_count func(context ?@mut anyopaque) void {
|
||||||
if context |raw| {
|
if context |raw| {
|
||||||
counts *mut usize :: ptr_cast(usize, raw)
|
count @mut usize :: ptrcast!(usize, raw)
|
||||||
counts[0] += 1
|
count^ += 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_probe_alloc func(context ?*mut anyopaque, _ usize, _ usize) ?*mut u8 {
|
@hide probe_alloc func(context ?@mut anyopaque, _ usize, _ usize) ?*mut u8 {
|
||||||
_probe_count(context)
|
probe_count(context)
|
||||||
return none
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
_probe_realloc func(context ?*mut anyopaque, _ ?*mut u8, _ usize, _ usize, _ usize) ?*mut u8 {
|
@hide probe_realloc func(context ?@mut anyopaque, _ ?*mut u8, _ usize, _ usize, _ usize) ?*mut u8 {
|
||||||
_probe_count(context)
|
probe_count(context)
|
||||||
return none
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
_probe_free func(context ?*mut anyopaque, _ ?*mut u8, _ usize, _ usize) void {
|
@hide probe_free func(context ?@mut anyopaque, _ ?*mut u8, _ usize, _ usize) void {
|
||||||
_probe_count(context)
|
probe_count(context)
|
||||||
}
|
}
|
||||||
|
|
||||||
_probe_vtable mem.AllocatorVTable :: mem.AllocatorVTable {
|
@hide probe_vtable mem.AllocatorVTable :: mem.AllocatorVTable {
|
||||||
alloc = _probe_alloc,
|
alloc = probe_alloc,
|
||||||
realloc = _probe_realloc,
|
realloc = probe_realloc,
|
||||||
free = _probe_free,
|
free = probe_free,
|
||||||
}
|
}
|
||||||
|
|
||||||
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]
|
||||||
first_allocator mem.Allocator :: mem.Allocator {
|
first_allocator mem.Allocator :: mem.Allocator {
|
||||||
context = (&first_calls).ptr,
|
context = &first_calls,
|
||||||
vtable = &_probe_vtable,
|
vtable = &probe_vtable,
|
||||||
}
|
}
|
||||||
second_allocator mem.Allocator :: mem.Allocator {
|
second_allocator mem.Allocator :: mem.Allocator {
|
||||||
context = (&second_calls).ptr,
|
context = &second_calls,
|
||||||
vtable = &_probe_vtable,
|
vtable = &probe_vtable,
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = mem.raw_alloc(first_allocator, 1, 1)
|
_ = mem.raw_alloc(first_allocator, 1, 1)
|
||||||
_ = mem.raw_realloc(first_allocator, none, 0, 1, 1)
|
_ = mem.raw_realloc(first_allocator, null, 0, 1, 1)
|
||||||
mem.raw_free(first_allocator, none, 0, 1)
|
mem.raw_free(first_allocator, null, 0, 1)
|
||||||
_ = mem.raw_alloc(second_allocator, 1, 1)
|
_ = mem.raw_alloc(second_allocator, 1, 1)
|
||||||
if (first_calls[0] != 3 or second_calls[0] != 1) return 32
|
if (first_calls[0] != 3 or second_calls[0] != 1) return 32
|
||||||
|
|
||||||
i32_fallback [1]mut i32 = undefined
|
i32_fallback [1]mut i32 := undefined
|
||||||
empty_failed bool = false
|
empty_failed bool := false
|
||||||
empty []mut i32 = mem.alloc(first_allocator, 0) catch |_| {
|
empty []mut i32 := mem.alloc(first_allocator, 0) catch |_| {
|
||||||
empty_failed = true
|
empty_failed = true
|
||||||
yield (&i32_fallback).ptr[..0]
|
yield (&i32_fallback).ptr[..0]
|
||||||
}
|
}
|
||||||
@@ -57,9 +57,9 @@ typed_allocator_test func() i32 {
|
|||||||
mem.free(first_allocator, empty)
|
mem.free(first_allocator, empty)
|
||||||
if (first_calls[0] != 3) return 33
|
if (first_calls[0] != 3) return 33
|
||||||
|
|
||||||
zero_sized_fallback [1]mut [0]u8 = undefined
|
zero_sized_fallback [1]mut [0]u8 := undefined
|
||||||
zero_sized_failed bool = false
|
zero_sized_failed bool := false
|
||||||
zero_sized []mut [0]u8 = mem.alloc([0]u8, first_allocator, 3) catch |_| {
|
zero_sized []mut [0]u8 := mem.alloc([0]u8, first_allocator, 3) catch |_| {
|
||||||
zero_sized_failed = true
|
zero_sized_failed = true
|
||||||
yield (&zero_sized_fallback).ptr[..0]
|
yield (&zero_sized_fallback).ptr[..0]
|
||||||
}
|
}
|
||||||
@@ -68,22 +68,22 @@ typed_allocator_test func() i32 {
|
|||||||
mem.free([0]u8, first_allocator, zero_sized)
|
mem.free([0]u8, first_allocator, zero_sized)
|
||||||
if (first_calls[0] != 3) return 35
|
if (first_calls[0] != 3) return 35
|
||||||
|
|
||||||
u64_fallback [1]mut u64 = undefined
|
u64_fallback [1]mut u64 := undefined
|
||||||
overflow_fallback_failed bool = false
|
overflow_fallback_failed bool := false
|
||||||
overflow_fallback []mut u64 = mem.alloc(first_allocator, 0) catch |_| {
|
overflow_fallback []mut u64 := mem.alloc(first_allocator, 0) catch |_| {
|
||||||
overflow_fallback_failed = true
|
overflow_fallback_failed = true
|
||||||
yield (&u64_fallback).ptr[..0]
|
yield (&u64_fallback).ptr[..0]
|
||||||
}
|
}
|
||||||
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
|
||||||
}
|
}
|
||||||
if (overflow_failed == false or first_calls[0] != 3) return 40
|
if (overflow_failed == false or first_calls[0] != 3) return 40
|
||||||
|
|
||||||
typed_failed bool = false
|
typed_failed bool := false
|
||||||
typed []mut i32 = mem.alloc(mem.c_allocator, 4) catch |_| {
|
typed []mut i32 := mem.alloc(mem.c_allocator, 4) catch |_| {
|
||||||
typed_failed = true
|
typed_failed = true
|
||||||
yield (&i32_fallback).ptr[..0]
|
yield (&i32_fallback).ptr[..0]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,26 +48,26 @@ score_for func(k Kind) i32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
main func() i32 {
|
main func() i32 {
|
||||||
items [LEN]mut i32 = undefined
|
items [LEN]mut i32 := undefined
|
||||||
items[0] = 10
|
items[0] = 10
|
||||||
items[1] = 20
|
items[1] = 20
|
||||||
|
|
||||||
idx u8 = 2
|
idx usize := 2
|
||||||
items[idx] = items[0] + items[1]
|
items[idx] = items[0] + items[1]
|
||||||
if (items[2] != 30) return 1
|
if (items[2] != 30) return 1
|
||||||
|
|
||||||
native_i i32 = 12
|
native_i i32 := 12
|
||||||
if (take_c_int(native_i) != 12) return 2
|
if (take_c_int(native_i) != 12) return 2
|
||||||
|
|
||||||
native_u u8 = 7
|
native_u u8 := 7
|
||||||
if (take_c_uchar(native_u) != 7) return 3
|
if (take_c_uchar(native_u) != 7) return 3
|
||||||
|
|
||||||
native_f f32 = 3.25
|
native_f f32 := 3.25
|
||||||
cf :: take_c_float(native_f)
|
cf :: take_c_float(native_f)
|
||||||
if (cf < 3.0 or cf > 4.0) return 4
|
if (cf < 3.0 or cf > 4.0) return 4
|
||||||
if (cf == 0.0) return 5
|
if (cf == 0.0) return 5
|
||||||
|
|
||||||
native_d f64 = 5.0
|
native_d f64 := 5.0
|
||||||
cd :: take_c_double(native_d)
|
cd :: take_c_double(native_d)
|
||||||
if (cd != 5.0) return 6
|
if (cd != 5.0) return 6
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,129 @@
|
|||||||
|
io :: import "@std/io"
|
||||||
|
process :: import "@std/process"
|
||||||
|
debug :: import "@std/debug"
|
||||||
|
|
||||||
|
State :: enum {
|
||||||
|
idle
|
||||||
|
running
|
||||||
|
}
|
||||||
|
|
||||||
|
Payload :: union(enum) {
|
||||||
|
count i32
|
||||||
|
empty void
|
||||||
|
}
|
||||||
|
|
||||||
|
Pair :: struct { i32, bool }
|
||||||
|
|
||||||
|
Config :: struct {
|
||||||
|
enabled bool
|
||||||
|
ratio f64
|
||||||
|
label []u8
|
||||||
|
state State
|
||||||
|
values [2]i32
|
||||||
|
pair Pair
|
||||||
|
maybe ?i32
|
||||||
|
payload Payload
|
||||||
|
}
|
||||||
|
Count :: distinct i32
|
||||||
|
Byte :: distinct u8
|
||||||
|
Ratio :: distinct f32
|
||||||
|
Inner :: distinct u32
|
||||||
|
Outer :: distinct Inner
|
||||||
|
|
||||||
|
|
||||||
|
first Config :: Config {
|
||||||
|
enabled = true,
|
||||||
|
ratio = 1.5,
|
||||||
|
label = "bro",
|
||||||
|
state = .running,
|
||||||
|
values = [20, 22],
|
||||||
|
pair = Pair {7, true},
|
||||||
|
maybe = 9,
|
||||||
|
payload = Payload {count = 4},
|
||||||
|
}
|
||||||
|
|
||||||
|
same Config :: Config {
|
||||||
|
enabled = true,
|
||||||
|
ratio = 1.5,
|
||||||
|
label = "bro",
|
||||||
|
state = .running,
|
||||||
|
values = [20, 22],
|
||||||
|
pair = Pair {7, true},
|
||||||
|
maybe = 9,
|
||||||
|
payload = Payload {count = 4},
|
||||||
|
}
|
||||||
|
|
||||||
|
different Config :: Config {
|
||||||
|
enabled = false,
|
||||||
|
ratio = 2.5,
|
||||||
|
label = "bro",
|
||||||
|
state = .idle,
|
||||||
|
values = [21, 21],
|
||||||
|
pair = Pair {7, false},
|
||||||
|
maybe = null,
|
||||||
|
payload = .empty,
|
||||||
|
}
|
||||||
|
|
||||||
|
score func($config Config) i32 {
|
||||||
|
return config.values[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
Carrier func($config Config) type {
|
||||||
|
return struct { value i32 }
|
||||||
|
}
|
||||||
|
|
||||||
|
read_carrier func($config Config, carrier Carrier(config)) i32 {
|
||||||
|
return carrier.value + config.values[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
main func(init process.Init) i32 {
|
||||||
|
if score(first) != 20 or score(same) != 20 or score(different) != 21 {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
carrier Carrier(first) :: Carrier(first) {value = 22}
|
||||||
|
if read_carrier(carrier) != 42 {
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
|
||||||
|
writer io.Writer :: io.stdout(init.io)
|
||||||
|
positive f64 := 1.0
|
||||||
|
zero f64 := 0.0
|
||||||
|
infinity f64 :: positive / zero
|
||||||
|
nan f64 :: zero / zero
|
||||||
|
io.print(writer, "{} {} {} {} {s} {d} {b} {o} {x} {X} {c} {e} {{}} {d} {b} {} {} {e}\n", {
|
||||||
|
true,
|
||||||
|
-42,
|
||||||
|
1.5,
|
||||||
|
State.running,
|
||||||
|
"bro",
|
||||||
|
2.5,
|
||||||
|
10,
|
||||||
|
10,
|
||||||
|
255,
|
||||||
|
255,
|
||||||
|
u8('A'),
|
||||||
|
1.5,
|
||||||
|
minval!(i64),
|
||||||
|
u64(0),
|
||||||
|
infinity,
|
||||||
|
nan,
|
||||||
|
f32(1.5),
|
||||||
|
}) catch |_| {
|
||||||
|
return 3
|
||||||
|
}
|
||||||
|
io.print(writer, "{} {d} {b} {o} {x} {X} {c} {e} {}\n", {
|
||||||
|
Count(i32(-42)),
|
||||||
|
Count(i32(-42)),
|
||||||
|
Byte(u8(10)),
|
||||||
|
Byte(u8(10)),
|
||||||
|
Byte(u8(255)),
|
||||||
|
Byte(u8(255)),
|
||||||
|
Byte(u8('A')),
|
||||||
|
Ratio(f32(1.5)),
|
||||||
|
Outer(Inner(u32(7))),
|
||||||
|
}) catch |_| {
|
||||||
|
return 4
|
||||||
|
}
|
||||||
|
debug.print("debug={} {x}\n", {State.idle, 42})
|
||||||
|
return 0
|
||||||
|
}
|
||||||
@@ -2,11 +2,11 @@ Point :: struct {
|
|||||||
x i32
|
x i32
|
||||||
}
|
}
|
||||||
|
|
||||||
counter int = 0
|
counter := i32(0)
|
||||||
ratio float = 1
|
ratio := 1.0
|
||||||
span range = 0..2
|
span := 0..2
|
||||||
point Point = Point { x = 1 }
|
point Point := Point { x = 1 }
|
||||||
values [_]mut i32 = [10, 20]
|
values [_]mut i32 := [10, 20]
|
||||||
|
|
||||||
bump func(value @mut i32) void {
|
bump func(value @mut i32) void {
|
||||||
value^ += 1
|
value^ += 1
|
||||||
@@ -19,7 +19,7 @@ main func() i32 {
|
|||||||
point.x += counter
|
point.x += counter
|
||||||
values[1] = point.x
|
values[1] = point.x
|
||||||
|
|
||||||
total i32 = counter + point.x + values[1]
|
total i32 := counter + point.x + values[1]
|
||||||
for span |i| {
|
for span |i| {
|
||||||
total += i
|
total += i
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
main func() i32 {
|
main func() i32 {
|
||||||
value i32 = 1
|
value := i32(1)
|
||||||
value = value + 2
|
value = value + 2
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
main func() void {
|
main func() void {
|
||||||
value i8 = 127
|
value i8 := 127
|
||||||
_ = value + 1
|
_ = value + 1
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ sum_brolang func(a, b int) int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
main func() void {
|
main func() void {
|
||||||
y int = 4
|
y int := 4
|
||||||
a_add_b_c :: sum_c(1, 2)
|
a_add_b_c :: sum_c(1, 2)
|
||||||
a_add_b_brolang :: sum_brolang(1, 2)
|
a_add_b_brolang :: sum_brolang(1, 2)
|
||||||
_ = y
|
_ = y
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
main func() i32 {
|
main func() i32 {
|
||||||
flag bool = true
|
flag bool := true
|
||||||
value :: i32(flag)
|
value :: i32(flag)
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ Thing :: union(enum) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
main func() i32 {
|
main func() i32 {
|
||||||
x Data = Data{ bird = 37 }
|
x Data := Data{ bird = 37 }
|
||||||
y Thing = Thing{ a = 5 }
|
y Thing := Thing{ a = 5 }
|
||||||
return x.bird + y.a
|
return x.bird + y.a
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,88 @@
|
|||||||
|
debug :: import "@std/debug"
|
||||||
|
meta :: import "@std/meta"
|
||||||
|
|
||||||
|
Numbers :: struct { i8, i16, i32 }
|
||||||
|
Row :: struct { value i32 }
|
||||||
|
|
||||||
|
format func() []u8 {
|
||||||
|
return "tuple={d}/{s}, limits={d}/{d}"
|
||||||
|
}
|
||||||
|
|
||||||
|
sum func($T type, value T) i32 {
|
||||||
|
total i32 := 0
|
||||||
|
match typeinfo!(T) {
|
||||||
|
.record |record|: inline for record.fields |field| {
|
||||||
|
total += i32(field!(value, field.name))
|
||||||
|
}
|
||||||
|
else: compile_error!("sum requires a record")
|
||||||
|
}
|
||||||
|
return total
|
||||||
|
}
|
||||||
|
|
||||||
|
static_control func($T type, value T) i32 {
|
||||||
|
total i32 := 0
|
||||||
|
match typeinfo!(T) {
|
||||||
|
.record |record|: inline for record.fields |field| {
|
||||||
|
{
|
||||||
|
if field.index == 1 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
total += i32(field!(value, field.name))
|
||||||
|
match typeinfo!(field.type) {
|
||||||
|
.integer: {
|
||||||
|
if field.index == 2 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else: _ = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
total += 100
|
||||||
|
}
|
||||||
|
else: compile_error!("static_control requires a record")
|
||||||
|
}
|
||||||
|
return total
|
||||||
|
}
|
||||||
|
|
||||||
|
row_value func(row Row) i32 {
|
||||||
|
return row.value
|
||||||
|
}
|
||||||
|
|
||||||
|
static_aggregates func() i32 {
|
||||||
|
total i32 := 0
|
||||||
|
inline for {Row {value = 2}, Row {value = 40}} |row| {
|
||||||
|
total += row_value(row)
|
||||||
|
}
|
||||||
|
return total
|
||||||
|
}
|
||||||
|
|
||||||
|
main func() i32 {
|
||||||
|
numbers Numbers := Numbers {1, 2, 39}
|
||||||
|
singleton :: {42,}
|
||||||
|
empty :: {}
|
||||||
|
block_value :: {
|
||||||
|
yield 42
|
||||||
|
}
|
||||||
|
_ = empty
|
||||||
|
if singleton.0 != block_value {
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
if sum(Numbers, numbers) != 42 {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
if static_control(Numbers, numbers) != 140 {
|
||||||
|
return 3
|
||||||
|
}
|
||||||
|
if static_aggregates() != 42 {
|
||||||
|
return 4
|
||||||
|
}
|
||||||
|
field!(&numbers, "2") += 1
|
||||||
|
debug.print("hello!\n", {})
|
||||||
|
debug.print(format(), {
|
||||||
|
numbers.2,
|
||||||
|
"bro",
|
||||||
|
minval!(i64),
|
||||||
|
maxval!(u64),
|
||||||
|
})
|
||||||
|
return 0
|
||||||
|
}
|
||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,6 @@ Val :: union {
|
|||||||
}
|
}
|
||||||
|
|
||||||
main func() i32 {
|
main func() i32 {
|
||||||
x Val = Val{ n = 42 }
|
x Val := Val{ n = 42 }
|
||||||
return x.n
|
return x.n
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
warn_only func(value i32, unused i32) i32 {
|
warn_only func(value i32, unused i32) i32 {
|
||||||
local i32 = 1
|
local i32 := 1
|
||||||
write_only i32 = 2
|
write_only i32 := 2
|
||||||
write_only = 3
|
write_only = 3
|
||||||
consumed i32 = value
|
consumed i32 := value
|
||||||
_ = consumed
|
_ = consumed
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,31 +1,31 @@
|
|||||||
# Milestone 5: boolean while loops with optional post-iteration updates.
|
# Milestone 5: boolean while loops with optional post-iteration updates.
|
||||||
|
|
||||||
return_before_update func() i32 {
|
return_before_update func() i32 {
|
||||||
i i32 = 0
|
i i32 := 0
|
||||||
while true : i = i + 1 {
|
while true : i = i + 1 {
|
||||||
return i
|
return i
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
main func() i32 {
|
main func() i32 {
|
||||||
total i32 = 0
|
total i32 := 0
|
||||||
|
|
||||||
# ordinary condition and update
|
# ordinary condition and update
|
||||||
i u32 = 0
|
i u32 := 0
|
||||||
while i < 5 : i = i + 1 {
|
while i < 5 : i = i + 1 {
|
||||||
total = total + 2
|
total = total + 2
|
||||||
}
|
}
|
||||||
|
|
||||||
# equivalent parenthesized header
|
# equivalent parenthesized header
|
||||||
j u32 = 0
|
j u32 := 0
|
||||||
while (j < 4) : (j = j + 1) {
|
while (j < 4) : (j = j + 1) {
|
||||||
total = total + 3
|
total = total + 3
|
||||||
}
|
}
|
||||||
|
|
||||||
# nested loops and body-local storage
|
# nested loops and body-local storage
|
||||||
outer u32 = 0
|
outer u32 := 0
|
||||||
while outer < 2 : outer = outer + 1 {
|
while outer < 2 : outer = outer + 1 {
|
||||||
inner u32 = 0
|
inner u32 := 0
|
||||||
while inner < 3 : inner = inner + 1 {
|
while inner < 3 : inner = inner + 1 {
|
||||||
total = total + 2
|
total = total + 2
|
||||||
}
|
}
|
||||||
@@ -33,9 +33,9 @@ main func() i32 {
|
|||||||
|
|
||||||
# Body-local storage stays scoped to the body. The update still targets
|
# Body-local storage stays scoped to the body. The update still targets
|
||||||
# the mutable k declared before the loop.
|
# the mutable k declared before the loop.
|
||||||
k u32 = 0
|
k u32 := 0
|
||||||
while k < 4 : k = k + 1 {
|
while k < 4 : k = k + 1 {
|
||||||
body_k u32 = 100
|
body_k u32 := 100
|
||||||
if body_k == 100 {
|
if body_k == 100 {
|
||||||
total = total + 2
|
total = total + 2
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ basic func() i32 {
|
|||||||
|
|
||||||
# Typed `T =`: the yield coerces to the annotation.
|
# Typed `T =`: the yield coerces to the annotation.
|
||||||
typed func() i64 {
|
typed func() i64 {
|
||||||
x i64 = {
|
x i64 := {
|
||||||
yield 100
|
yield 100
|
||||||
}
|
}
|
||||||
return x
|
return x
|
||||||
@@ -29,7 +29,7 @@ typed func() i64 {
|
|||||||
# local, but the captured value is unchanged.
|
# local, but the captured value is unchanged.
|
||||||
spill func() i32 {
|
spill func() i32 {
|
||||||
v :: {
|
v :: {
|
||||||
n i32 = 5
|
n i32 := 5
|
||||||
defer n = 999
|
defer n = 999
|
||||||
yield n
|
yield n
|
||||||
}
|
}
|
||||||
@@ -38,7 +38,7 @@ spill func() i32 {
|
|||||||
|
|
||||||
# Reassignment into an existing mutable local.
|
# Reassignment into an existing mutable local.
|
||||||
reassign func() i32 {
|
reassign func() i32 {
|
||||||
r i32 = 0
|
r i32 := 0
|
||||||
r = {
|
r = {
|
||||||
yield 7
|
yield 7
|
||||||
}
|
}
|
||||||
@@ -61,13 +61,13 @@ vif_untyped func(sel i32) i32 {
|
|||||||
|
|
||||||
# Typed `T =`: every branch coerces to the annotation.
|
# Typed `T =`: every branch coerces to the annotation.
|
||||||
vif_typed func(sel i32) i32 {
|
vif_typed func(sel i32) i32 {
|
||||||
r i32 = if (sel == 0) { yield 100 } else { yield 200 }
|
r i32 := if (sel == 0) { yield 100 } else { yield 200 }
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
# Assigned into an existing local.
|
# Assigned into an existing local.
|
||||||
vif_reassign func(sel i32) i32 {
|
vif_reassign func(sel i32) i32 {
|
||||||
r i32 = 0
|
r i32 := 0
|
||||||
r = if (sel == 0) { yield 7 } else { yield 9 }
|
r = if (sel == 0) { yield 7 } else { yield 9 }
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
@@ -76,7 +76,7 @@ vif_reassign func(sel i32) i32 {
|
|||||||
# the yield.
|
# the yield.
|
||||||
vif_defer func() i32 {
|
vif_defer func() i32 {
|
||||||
r :: if (true) {
|
r :: if (true) {
|
||||||
n i32 = 5
|
n i32 := 5
|
||||||
defer n = 999
|
defer n = 999
|
||||||
yield n
|
yield n
|
||||||
} else {
|
} else {
|
||||||
@@ -85,16 +85,21 @@ vif_defer func() i32 {
|
|||||||
return r # 5
|
return r # 5
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vif_expression func(old_entries []u8) usize {
|
||||||
|
new_size :: if (old_entries.len > 0) old_entries.len * 2 else 8
|
||||||
|
return new_size
|
||||||
|
}
|
||||||
|
|
||||||
# --- value loops (milestone 20.5) --------------------------------------------
|
# --- value loops (milestone 20.5) --------------------------------------------
|
||||||
|
|
||||||
# Labeled `for` used as a value: `yield :blk i` exits early with a value, the
|
# Labeled `for` used as a value: `yield :blk i` exits early with a value, the
|
||||||
# trailing `yield none` supplies the value when the loop completes. The `{i,
|
# trailing `yield null` supplies the value when the loop completes. The `{i,
|
||||||
# none}` yields resolve the result to an optional.
|
# null}` yields resolve the result to an optional.
|
||||||
loop_search func() i32 {
|
loop_search func() i32 {
|
||||||
# first i in 0..10 whose square exceeds 40 (6*6=36 no, 7*7=49 yes -> 7).
|
# first i in 0..10 whose square exceeds 40 (6*6=36 no, 7*7=49 yes -> 7).
|
||||||
idx :: for 0..10 |i| blk: {
|
idx :: for 0..10 |i| blk: {
|
||||||
if (i * i > 40) yield :blk i
|
if (i * i > 40) yield :blk i
|
||||||
yield none
|
yield null
|
||||||
}
|
}
|
||||||
if idx |found| {
|
if idx |found| {
|
||||||
if (found == 7) return 0
|
if (found == 7) return 0
|
||||||
@@ -103,11 +108,11 @@ loop_search func() i32 {
|
|||||||
return 2
|
return 2
|
||||||
}
|
}
|
||||||
|
|
||||||
# Same loop, but nothing matches -> the fall-through `yield none` is the result.
|
# Same loop, but nothing matches -> the fall-through `yield null` is the result.
|
||||||
loop_none func() i32 {
|
loop_none func() i32 {
|
||||||
idx :: for 0..10 |i| blk: {
|
idx :: for 0..10 |i| blk: {
|
||||||
if (i > 100) yield :blk i
|
if (i > 100) yield :blk i
|
||||||
yield none
|
yield null
|
||||||
}
|
}
|
||||||
if idx |found| {
|
if idx |found| {
|
||||||
_ = found
|
_ = found
|
||||||
@@ -118,10 +123,10 @@ loop_none func() i32 {
|
|||||||
|
|
||||||
# Labeled `while` value loop (label follows the `: update` clause).
|
# Labeled `while` value loop (label follows the `: update` clause).
|
||||||
loop_while func() i32 {
|
loop_while func() i32 {
|
||||||
n i32 = 0
|
n i32 := 0
|
||||||
found :: while n < 100 : n += 1 blk: {
|
found :: while n < 100 : n += 1 blk: {
|
||||||
if (n == 8) yield :blk n
|
if (n == 8) yield :blk n
|
||||||
yield none
|
yield null
|
||||||
}
|
}
|
||||||
if found |v| {
|
if found |v| {
|
||||||
if (v == 8) return 0
|
if (v == 8) return 0
|
||||||
@@ -159,13 +164,13 @@ orelse_value func(opt ?i32) i32 {
|
|||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
# Untyped value loop where `none` is yielded (in a labeled yield) before any
|
# Untyped value loop where `null` is yielded (in a labeled yield) before any
|
||||||
# concrete value: the element type still resolves to ?<i> from `yield :blk i`.
|
# concrete value: the element type still resolves to ?<i> from `yield :blk i`.
|
||||||
loop_none_first func() i32 {
|
loop_none_first func() i32 {
|
||||||
r :: for 0..10 |i| blk: {
|
r :: for 0..10 |i| blk: {
|
||||||
if (i > 100) yield :blk none
|
if (i > 100) yield :blk null
|
||||||
if (i * i > 40) yield :blk i # first concrete yield: i == 7
|
if (i * i > 40) yield :blk i # first concrete yield: i == 7
|
||||||
yield none
|
yield null
|
||||||
}
|
}
|
||||||
if r |found| {
|
if r |found| {
|
||||||
if (found == 7) return 0
|
if (found == 7) return 0
|
||||||
@@ -194,7 +199,7 @@ lblock func(sel i32) i32 {
|
|||||||
# the block's defer runs.
|
# the block's defer runs.
|
||||||
lblock_defer func() i32 {
|
lblock_defer func() i32 {
|
||||||
r :: blk: {
|
r :: blk: {
|
||||||
n i32 = 5
|
n i32 := 5
|
||||||
defer n = 999
|
defer n = 999
|
||||||
if (true) yield :blk n
|
if (true) yield :blk n
|
||||||
yield :blk 0
|
yield :blk 0
|
||||||
@@ -202,10 +207,10 @@ lblock_defer func() i32 {
|
|||||||
return r # 5, not 999
|
return r # 5, not 999
|
||||||
}
|
}
|
||||||
|
|
||||||
# A labeled block whose `{T, none}` yields resolve the result to an optional.
|
# A labeled block whose `{T, null}` yields resolve the result to an optional.
|
||||||
lblock_optional func(present i32) i32 {
|
lblock_optional func(present i32) i32 {
|
||||||
r :: blk: {
|
r :: blk: {
|
||||||
if (present == 0) yield :blk none
|
if (present == 0) yield :blk null
|
||||||
yield :blk 8
|
yield :blk 8
|
||||||
}
|
}
|
||||||
if r |v| {
|
if r |v| {
|
||||||
@@ -220,7 +225,7 @@ yield_outer func(target i32) i32 {
|
|||||||
for 0..3 |col| {
|
for 0..3 |col| {
|
||||||
if (row * 3 + col == target) yield :outer (row * 10 + col)
|
if (row * 3 + col == target) yield :outer (row * 10 + col)
|
||||||
}
|
}
|
||||||
yield none
|
yield null
|
||||||
}
|
}
|
||||||
if found |v| {
|
if found |v| {
|
||||||
return v
|
return v
|
||||||
@@ -230,7 +235,7 @@ yield_outer func(target i32) i32 {
|
|||||||
|
|
||||||
# Plain `break :outer` exits an outer loop from an inner loop.
|
# Plain `break :outer` exits an outer loop from an inner loop.
|
||||||
break_outer func() i32 {
|
break_outer func() i32 {
|
||||||
count i32 = 0
|
count i32 := 0
|
||||||
for 0..3 |a| outer: {
|
for 0..3 |a| outer: {
|
||||||
for 0..3 |b| {
|
for 0..3 |b| {
|
||||||
count += 1
|
count += 1
|
||||||
@@ -242,7 +247,7 @@ break_outer func() i32 {
|
|||||||
|
|
||||||
# A labeled block *statement* (not a value source): `break :blk` exits it early.
|
# A labeled block *statement* (not a value source): `break :blk` exits it early.
|
||||||
stmt_block func(early i32) i32 {
|
stmt_block func(early i32) i32 {
|
||||||
x i32 = 0
|
x i32 := 0
|
||||||
blk: {
|
blk: {
|
||||||
x = 1
|
x = 1
|
||||||
if (early == 1) break :blk
|
if (early == 1) break :blk
|
||||||
@@ -254,7 +259,7 @@ stmt_block func(early i32) i32 {
|
|||||||
# `break :search` escapes a nested loop and the block in one jump; the block's
|
# `break :search` escapes a nested loop and the block in one jump; the block's
|
||||||
# defer still runs on the way out.
|
# defer still runs on the way out.
|
||||||
stmt_block_escape func() i32 {
|
stmt_block_escape func() i32 {
|
||||||
hits i32 = 0
|
hits i32 := 0
|
||||||
search: {
|
search: {
|
||||||
defer hits += 1000
|
defer hits += 1000
|
||||||
for 0..10 |i| {
|
for 0..10 |i| {
|
||||||
@@ -266,11 +271,24 @@ stmt_block_escape func() i32 {
|
|||||||
return hits # 4 + 1000 (defer) = 1004
|
return hits # 4 + 1000 (defer) = 1004
|
||||||
}
|
}
|
||||||
|
|
||||||
# Item B: a `none` yielded before a concrete `yield :blk` that references a block local.
|
# A labeled block can also be exited through an ordinary nested block.
|
||||||
|
stmt_block_nested func() i32 {
|
||||||
|
hits i32 := 0
|
||||||
|
outer: {
|
||||||
|
{
|
||||||
|
hits = 1
|
||||||
|
break :outer
|
||||||
|
}
|
||||||
|
hits = 100 # skipped by break :outer
|
||||||
|
}
|
||||||
|
return hits
|
||||||
|
}
|
||||||
|
|
||||||
|
# Item B: a `null` yielded before a concrete `yield :blk` that references a block local.
|
||||||
lblock_local func() i32 {
|
lblock_local func() i32 {
|
||||||
r :: blk: {
|
r :: blk: {
|
||||||
val :: 9
|
val :: 9
|
||||||
if (false) yield :blk none
|
if (false) yield :blk null
|
||||||
yield :blk val
|
yield :blk val
|
||||||
}
|
}
|
||||||
if r |v| {
|
if r |v| {
|
||||||
@@ -293,6 +311,8 @@ main func() i32 {
|
|||||||
if (vif_reassign(0) != 7) return 110
|
if (vif_reassign(0) != 7) return 110
|
||||||
if (vif_reassign(9) != 9) return 111
|
if (vif_reassign(9) != 9) return 111
|
||||||
if (vif_defer() != 5) return 112
|
if (vif_defer() != 5) return 112
|
||||||
|
if (vif_expression("") != 8) return 136
|
||||||
|
if (vif_expression("abc") != 6) return 137
|
||||||
|
|
||||||
if (loop_search() != 0) return 113
|
if (loop_search() != 0) return 113
|
||||||
if (loop_none() != 0) return 114
|
if (loop_none() != 0) return 114
|
||||||
@@ -301,9 +321,9 @@ main func() i32 {
|
|||||||
if (vif_return(0) != 11) return 116
|
if (vif_return(0) != 11) return 116
|
||||||
if (vif_return(1) != 55) return 117
|
if (vif_return(1) != 55) return 117
|
||||||
if (vif_unwrap(21) != 42) return 118
|
if (vif_unwrap(21) != 42) return 118
|
||||||
if (vif_unwrap(none) != 99) return 119
|
if (vif_unwrap(null) != 99) return 119
|
||||||
if (orelse_value(5) != 5) return 120
|
if (orelse_value(5) != 5) return 120
|
||||||
if (orelse_value(none) != 7) return 121
|
if (orelse_value(null) != 7) return 121
|
||||||
if (loop_none_first() != 0) return 122
|
if (loop_none_first() != 0) return 122
|
||||||
|
|
||||||
if (lblock(0) != 10) return 123
|
if (lblock(0) != 10) return 123
|
||||||
@@ -319,6 +339,7 @@ main func() i32 {
|
|||||||
if (stmt_block(0) != 2) return 132
|
if (stmt_block(0) != 2) return 132
|
||||||
if (stmt_block_escape() != 1004) return 133
|
if (stmt_block_escape() != 1004) return 133
|
||||||
if (lblock_local() != 9) return 134
|
if (lblock_local() != 9) return 134
|
||||||
|
if (stmt_block_nested() != 1) return 135
|
||||||
|
|
||||||
return 42
|
return 42
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
id = "brolang"
|
|
||||||
name = "Brolang"
|
|
||||||
version = "0.1.1"
|
|
||||||
schema_version = 1
|
|
||||||
authors = ["Brolang contributors"]
|
|
||||||
description = "Brolang language support"
|
|
||||||
repository = "ssh://git@gitea.hl-valdemar.dev:2222/hl-valdemar/brolang.git"
|
|
||||||
languages = ["languages/brolang"]
|
|
||||||
|
|
||||||
[grammars.brolang]
|
|
||||||
repository = "file:///Users/valdemar/Developer/Personal/Languages/brolang"
|
|
||||||
rev = "ba171a2d5e248fa24f5f4ee960e21056893f72e7"
|
|
||||||
path = "tree-sitter-brolang"
|
|
||||||
+120
@@ -0,0 +1,120 @@
|
|||||||
|
# generated by brolang translate-c from errno.h
|
||||||
|
|
||||||
|
errno_t :: alias c_int
|
||||||
|
|
||||||
|
EPERM c_int :: 1
|
||||||
|
ENOENT c_int :: 2
|
||||||
|
ESRCH c_int :: 3
|
||||||
|
EINTR c_int :: 4
|
||||||
|
EIO c_int :: 5
|
||||||
|
ENXIO c_int :: 6
|
||||||
|
E2BIG c_int :: 7
|
||||||
|
ENOEXEC c_int :: 8
|
||||||
|
EBADF c_int :: 9
|
||||||
|
ECHILD c_int :: 10
|
||||||
|
EDEADLK c_int :: 11
|
||||||
|
ENOMEM c_int :: 12
|
||||||
|
EACCES c_int :: 13
|
||||||
|
EFAULT c_int :: 14
|
||||||
|
ENOTBLK c_int :: 15
|
||||||
|
EBUSY c_int :: 16
|
||||||
|
EEXIST c_int :: 17
|
||||||
|
EXDEV c_int :: 18
|
||||||
|
ENODEV c_int :: 19
|
||||||
|
ENOTDIR c_int :: 20
|
||||||
|
EISDIR c_int :: 21
|
||||||
|
EINVAL c_int :: 22
|
||||||
|
ENFILE c_int :: 23
|
||||||
|
EMFILE c_int :: 24
|
||||||
|
ENOTTY c_int :: 25
|
||||||
|
ETXTBSY c_int :: 26
|
||||||
|
EFBIG c_int :: 27
|
||||||
|
ENOSPC c_int :: 28
|
||||||
|
ESPIPE c_int :: 29
|
||||||
|
EROFS c_int :: 30
|
||||||
|
EMLINK c_int :: 31
|
||||||
|
EPIPE c_int :: 32
|
||||||
|
EDOM c_int :: 33
|
||||||
|
ERANGE c_int :: 34
|
||||||
|
EAGAIN c_int :: 35
|
||||||
|
EINPROGRESS c_int :: 36
|
||||||
|
EALREADY c_int :: 37
|
||||||
|
ENOTSOCK c_int :: 38
|
||||||
|
EDESTADDRREQ c_int :: 39
|
||||||
|
EMSGSIZE c_int :: 40
|
||||||
|
EPROTOTYPE c_int :: 41
|
||||||
|
ENOPROTOOPT c_int :: 42
|
||||||
|
EPROTONOSUPPORT c_int :: 43
|
||||||
|
ESOCKTNOSUPPORT c_int :: 44
|
||||||
|
ENOTSUP c_int :: 45
|
||||||
|
EPFNOSUPPORT c_int :: 46
|
||||||
|
EAFNOSUPPORT c_int :: 47
|
||||||
|
EADDRINUSE c_int :: 48
|
||||||
|
EADDRNOTAVAIL c_int :: 49
|
||||||
|
ENETDOWN c_int :: 50
|
||||||
|
ENETUNREACH c_int :: 51
|
||||||
|
ENETRESET c_int :: 52
|
||||||
|
ECONNABORTED c_int :: 53
|
||||||
|
ECONNRESET c_int :: 54
|
||||||
|
ENOBUFS c_int :: 55
|
||||||
|
EISCONN c_int :: 56
|
||||||
|
ENOTCONN c_int :: 57
|
||||||
|
ESHUTDOWN c_int :: 58
|
||||||
|
ETOOMANYREFS c_int :: 59
|
||||||
|
ETIMEDOUT c_int :: 60
|
||||||
|
ECONNREFUSED c_int :: 61
|
||||||
|
ELOOP c_int :: 62
|
||||||
|
ENAMETOOLONG c_int :: 63
|
||||||
|
EHOSTDOWN c_int :: 64
|
||||||
|
EHOSTUNREACH c_int :: 65
|
||||||
|
ENOTEMPTY c_int :: 66
|
||||||
|
EPROCLIM c_int :: 67
|
||||||
|
EUSERS c_int :: 68
|
||||||
|
EDQUOT c_int :: 69
|
||||||
|
ESTALE c_int :: 70
|
||||||
|
EREMOTE c_int :: 71
|
||||||
|
EBADRPC c_int :: 72
|
||||||
|
ERPCMISMATCH c_int :: 73
|
||||||
|
EPROGUNAVAIL c_int :: 74
|
||||||
|
EPROGMISMATCH c_int :: 75
|
||||||
|
EPROCUNAVAIL c_int :: 76
|
||||||
|
ENOLCK c_int :: 77
|
||||||
|
ENOSYS c_int :: 78
|
||||||
|
EFTYPE c_int :: 79
|
||||||
|
EAUTH c_int :: 80
|
||||||
|
ENEEDAUTH c_int :: 81
|
||||||
|
EPWROFF c_int :: 82
|
||||||
|
EDEVERR c_int :: 83
|
||||||
|
EOVERFLOW c_int :: 84
|
||||||
|
EBADEXEC c_int :: 85
|
||||||
|
EBADARCH c_int :: 86
|
||||||
|
ESHLIBVERS c_int :: 87
|
||||||
|
EBADMACHO c_int :: 88
|
||||||
|
ECANCELED c_int :: 89
|
||||||
|
EIDRM c_int :: 90
|
||||||
|
ENOMSG c_int :: 91
|
||||||
|
EILSEQ c_int :: 92
|
||||||
|
ENOATTR c_int :: 93
|
||||||
|
EBADMSG c_int :: 94
|
||||||
|
EMULTIHOP c_int :: 95
|
||||||
|
ENODATA c_int :: 96
|
||||||
|
ENOLINK c_int :: 97
|
||||||
|
ENOSR c_int :: 98
|
||||||
|
ENOSTR c_int :: 99
|
||||||
|
EPROTO c_int :: 100
|
||||||
|
ETIME c_int :: 101
|
||||||
|
EOPNOTSUPP c_int :: 102
|
||||||
|
ENOPOLICY c_int :: 103
|
||||||
|
ENOTRECOVERABLE c_int :: 104
|
||||||
|
EOWNERDEAD c_int :: 105
|
||||||
|
EQFULL c_int :: 106
|
||||||
|
ENOTCAPABLE c_int :: 107
|
||||||
|
ELAST c_int :: 107
|
||||||
|
|
||||||
|
__error c_func() ?*mut c_int
|
||||||
|
|
||||||
|
# unsupported in bindings: _SYS_ERRNO_H_ — C macro has no replacement value
|
||||||
|
# unsupported in bindings: _CDEFS_H_ — C macro has no replacement value
|
||||||
|
# unsupported in bindings: _ERRNO_T — C macro has no replacement value
|
||||||
|
# unsupported in bindings: errno — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: EWOULDBLOCK — C macro is not a supported constant
|
||||||
+393
@@ -0,0 +1,393 @@
|
|||||||
|
# generated by brolang translate-c from fcntl.h
|
||||||
|
|
||||||
|
# unsupported in bindings: C union '__mbstate_t' has no native spelling
|
||||||
|
__c_fcntl_bro_flock_record :: c_struct {
|
||||||
|
l_start c_longlong
|
||||||
|
l_len c_longlong
|
||||||
|
l_pid c_int
|
||||||
|
l_type c_short
|
||||||
|
l_whence c_short
|
||||||
|
}
|
||||||
|
flocktimeout :: c_struct {
|
||||||
|
fl __c_fcntl_bro_flock_record
|
||||||
|
timeout timespec
|
||||||
|
}
|
||||||
|
radvisory :: c_struct {
|
||||||
|
ra_offset c_longlong
|
||||||
|
ra_count c_int
|
||||||
|
}
|
||||||
|
fsignatures :: c_struct {
|
||||||
|
fs_file_start c_longlong
|
||||||
|
fs_blob_start ?*mut anyopaque
|
||||||
|
fs_blob_size c_ulong
|
||||||
|
fs_fsignatures_size c_ulong
|
||||||
|
fs_cdhash [20]c_char
|
||||||
|
fs_hash_type c_int
|
||||||
|
}
|
||||||
|
fsupplement :: c_struct {
|
||||||
|
fs_file_start c_longlong
|
||||||
|
fs_blob_start c_longlong
|
||||||
|
fs_blob_size c_ulong
|
||||||
|
fs_orig_fd c_int
|
||||||
|
}
|
||||||
|
fchecklv :: c_struct {
|
||||||
|
lv_file_start c_longlong
|
||||||
|
lv_error_message_size c_ulong
|
||||||
|
lv_error_message ?*mut anyopaque
|
||||||
|
}
|
||||||
|
fgetsigsinfo :: c_struct {
|
||||||
|
fg_file_start c_longlong
|
||||||
|
fg_info_request c_int
|
||||||
|
fg_sig_is_platform c_int
|
||||||
|
}
|
||||||
|
fstore :: c_struct {
|
||||||
|
fst_flags c_uint
|
||||||
|
fst_posmode c_int
|
||||||
|
fst_offset c_longlong
|
||||||
|
fst_length c_longlong
|
||||||
|
fst_bytesalloc c_longlong
|
||||||
|
}
|
||||||
|
fpunchhole :: c_struct {
|
||||||
|
fp_flags c_uint
|
||||||
|
reserved c_uint
|
||||||
|
fp_offset c_longlong
|
||||||
|
fp_length c_longlong
|
||||||
|
}
|
||||||
|
ftrimactivefile :: c_struct {
|
||||||
|
fta_offset c_longlong
|
||||||
|
fta_length c_longlong
|
||||||
|
}
|
||||||
|
fspecread :: c_struct {
|
||||||
|
fsr_flags c_uint
|
||||||
|
reserved c_uint
|
||||||
|
fsr_offset c_longlong
|
||||||
|
fsr_length c_longlong
|
||||||
|
}
|
||||||
|
fattributiontag :: c_struct {
|
||||||
|
ft_flags c_uint
|
||||||
|
ft_hash c_ulonglong
|
||||||
|
ft_attribution_name [255]c_char
|
||||||
|
}
|
||||||
|
log2phys :: c_struct {
|
||||||
|
l2p_flags c_uint
|
||||||
|
l2p_contigbytes c_longlong
|
||||||
|
l2p_devoffset c_longlong
|
||||||
|
}
|
||||||
|
_filesec :: opaque
|
||||||
|
|
||||||
|
# unsupported in bindings: typedef '__mbstate_t' — underlying type has no native spelling
|
||||||
|
# unsupported in bindings: typedef '__darwin_mbstate_t' — underlying type has no native spelling
|
||||||
|
fsignatures_t :: alias fsignatures
|
||||||
|
fsupplement_t :: alias fsupplement
|
||||||
|
fchecklv_t :: alias fchecklv
|
||||||
|
fgetsigsinfo_t :: alias fgetsigsinfo
|
||||||
|
fstore_t :: alias fstore
|
||||||
|
fpunchhole_t :: alias fpunchhole
|
||||||
|
ftrimactivefile_t :: alias ftrimactivefile
|
||||||
|
fspecread_t :: alias fspecread
|
||||||
|
fattributiontag_t :: alias fattributiontag
|
||||||
|
# unsupported in bindings: typedef '_filesec' — underlying type has no native spelling
|
||||||
|
filesec_t :: alias ?*mut _filesec
|
||||||
|
filesec_property_t :: alias c_uint
|
||||||
|
|
||||||
|
O_RDONLY c_int :: 0
|
||||||
|
O_WRONLY c_int :: 1
|
||||||
|
O_RDWR c_int :: 2
|
||||||
|
O_ACCMODE c_int :: 3
|
||||||
|
FREAD c_int :: 1
|
||||||
|
FWRITE c_int :: 2
|
||||||
|
O_NONBLOCK c_int :: 4
|
||||||
|
O_APPEND c_int :: 8
|
||||||
|
O_SYNC c_int :: 128
|
||||||
|
O_SHLOCK c_int :: 16
|
||||||
|
O_EXLOCK c_int :: 32
|
||||||
|
O_ASYNC c_int :: 64
|
||||||
|
O_NOFOLLOW c_int :: 256
|
||||||
|
O_CREAT c_int :: 512
|
||||||
|
O_TRUNC c_int :: 1024
|
||||||
|
O_EXCL c_int :: 2048
|
||||||
|
O_RESOLVE_BENEATH c_int :: 4096
|
||||||
|
O_UNIQUE c_int :: 8192
|
||||||
|
O_EVTONLY c_int :: 32768
|
||||||
|
O_NOCTTY c_int :: 131072
|
||||||
|
O_DIRECTORY c_int :: 1048576
|
||||||
|
O_SYMLINK c_int :: 2097152
|
||||||
|
O_DSYNC c_int :: 4194304
|
||||||
|
O_CLOEXEC c_int :: 16777216
|
||||||
|
O_NOFOLLOW_ANY c_int :: 536870912
|
||||||
|
O_EXEC c_int :: 1073741824
|
||||||
|
AT_FDCWD c_int :: -2
|
||||||
|
AT_EACCESS c_int :: 16
|
||||||
|
AT_SYMLINK_NOFOLLOW c_int :: 32
|
||||||
|
AT_SYMLINK_FOLLOW c_int :: 64
|
||||||
|
AT_REMOVEDIR c_int :: 128
|
||||||
|
AT_REALDEV c_int :: 512
|
||||||
|
AT_FDONLY c_int :: 1024
|
||||||
|
AT_SYMLINK_NOFOLLOW_ANY c_int :: 2048
|
||||||
|
AT_RESOLVE_BENEATH c_int :: 8192
|
||||||
|
AT_NODELETEBUSY c_int :: 16384
|
||||||
|
AT_UNIQUE c_int :: 32768
|
||||||
|
O_DP_GETRAWENCRYPTED c_int :: 1
|
||||||
|
O_DP_GETRAWUNENCRYPTED c_int :: 2
|
||||||
|
O_DP_AUTHENTICATE c_int :: 4
|
||||||
|
AUTH_OPEN_NOAUTHFD c_int :: -1
|
||||||
|
CPF_OVERWRITE c_int :: 1
|
||||||
|
CPF_IGNORE_MODE c_int :: 2
|
||||||
|
F_DUPFD c_int :: 0
|
||||||
|
F_GETFD c_int :: 1
|
||||||
|
F_SETFD c_int :: 2
|
||||||
|
F_GETFL c_int :: 3
|
||||||
|
F_SETFL c_int :: 4
|
||||||
|
F_GETOWN c_int :: 5
|
||||||
|
F_SETOWN c_int :: 6
|
||||||
|
F_GETLK c_int :: 7
|
||||||
|
F_SETLK c_int :: 8
|
||||||
|
F_SETLKW c_int :: 9
|
||||||
|
F_SETLKWTIMEOUT c_int :: 10
|
||||||
|
F_FLUSH_DATA c_int :: 40
|
||||||
|
F_CHKCLEAN c_int :: 41
|
||||||
|
F_PREALLOCATE c_int :: 42
|
||||||
|
F_SETSIZE c_int :: 43
|
||||||
|
F_RDADVISE c_int :: 44
|
||||||
|
F_RDAHEAD c_int :: 45
|
||||||
|
F_NOCACHE c_int :: 48
|
||||||
|
F_LOG2PHYS c_int :: 49
|
||||||
|
F_GETPATH c_int :: 50
|
||||||
|
F_FULLFSYNC c_int :: 51
|
||||||
|
F_PATHPKG_CHECK c_int :: 52
|
||||||
|
F_FREEZE_FS c_int :: 53
|
||||||
|
F_THAW_FS c_int :: 54
|
||||||
|
F_GLOBAL_NOCACHE c_int :: 55
|
||||||
|
F_ADDSIGS c_int :: 59
|
||||||
|
F_ADDFILESIGS c_int :: 61
|
||||||
|
F_NODIRECT c_int :: 62
|
||||||
|
F_GETPROTECTIONCLASS c_int :: 63
|
||||||
|
F_SETPROTECTIONCLASS c_int :: 64
|
||||||
|
F_LOG2PHYS_EXT c_int :: 65
|
||||||
|
F_GETLKPID c_int :: 66
|
||||||
|
F_SETBACKINGSTORE c_int :: 70
|
||||||
|
F_GETPATH_MTMINFO c_int :: 71
|
||||||
|
F_GETCODEDIR c_int :: 72
|
||||||
|
F_SETNOSIGPIPE c_int :: 73
|
||||||
|
F_GETNOSIGPIPE c_int :: 74
|
||||||
|
F_TRANSCODEKEY c_int :: 75
|
||||||
|
F_SINGLE_WRITER c_int :: 76
|
||||||
|
F_GETPROTECTIONLEVEL c_int :: 77
|
||||||
|
F_FINDSIGS c_int :: 78
|
||||||
|
F_ADDFILESIGS_FOR_DYLD_SIM c_int :: 83
|
||||||
|
F_BARRIERFSYNC c_int :: 85
|
||||||
|
F_OFD_SETLK c_int :: 90
|
||||||
|
F_OFD_SETLKW c_int :: 91
|
||||||
|
F_OFD_GETLK c_int :: 92
|
||||||
|
F_OFD_SETLKWTIMEOUT c_int :: 93
|
||||||
|
F_ADDFILESIGS_RETURN c_int :: 97
|
||||||
|
F_CHECK_LV c_int :: 98
|
||||||
|
F_PUNCHHOLE c_int :: 99
|
||||||
|
F_TRIM_ACTIVE_FILE c_int :: 100
|
||||||
|
F_SPECULATIVE_READ c_int :: 101
|
||||||
|
F_GETPATH_NOFIRMLINK c_int :: 102
|
||||||
|
F_ADDFILESIGS_INFO c_int :: 103
|
||||||
|
F_ADDFILESUPPL c_int :: 104
|
||||||
|
F_GETSIGSINFO c_int :: 105
|
||||||
|
F_SETLEASE c_int :: 106
|
||||||
|
F_GETLEASE c_int :: 107
|
||||||
|
F_TRANSFEREXTENTS c_int :: 110
|
||||||
|
F_ATTRIBUTION_TAG c_int :: 111
|
||||||
|
F_NOCACHE_EXT c_int :: 112
|
||||||
|
F_ADDSIGS_MAIN_BINARY c_int :: 113
|
||||||
|
FCNTL_FS_SPECIFIC_BASE c_int :: 65536
|
||||||
|
F_DUPFD_CLOEXEC c_int :: 67
|
||||||
|
FD_CLOEXEC c_int :: 1
|
||||||
|
F_RDLCK c_int :: 1
|
||||||
|
F_UNLCK c_int :: 2
|
||||||
|
F_WRLCK c_int :: 3
|
||||||
|
S_IFMT c_int :: 61440
|
||||||
|
S_IFIFO c_int :: 4096
|
||||||
|
S_IFCHR c_int :: 8192
|
||||||
|
S_IFDIR c_int :: 16384
|
||||||
|
S_IFBLK c_int :: 24576
|
||||||
|
S_IFREG c_int :: 32768
|
||||||
|
S_IFLNK c_int :: 40960
|
||||||
|
S_IFSOCK c_int :: 49152
|
||||||
|
S_IFWHT c_int :: 57344
|
||||||
|
S_IRWXU c_int :: 448
|
||||||
|
S_IRUSR c_int :: 256
|
||||||
|
S_IWUSR c_int :: 128
|
||||||
|
S_IXUSR c_int :: 64
|
||||||
|
S_IRWXG c_int :: 56
|
||||||
|
S_IRGRP c_int :: 32
|
||||||
|
S_IWGRP c_int :: 16
|
||||||
|
S_IXGRP c_int :: 8
|
||||||
|
S_IRWXO c_int :: 7
|
||||||
|
S_IROTH c_int :: 4
|
||||||
|
S_IWOTH c_int :: 2
|
||||||
|
S_IXOTH c_int :: 1
|
||||||
|
S_ISUID c_int :: 2048
|
||||||
|
S_ISGID c_int :: 1024
|
||||||
|
S_ISVTX c_int :: 512
|
||||||
|
F_ALLOCATECONTIG c_int :: 2
|
||||||
|
F_ALLOCATEALL c_int :: 4
|
||||||
|
F_ALLOCATEPERSIST c_int :: 8
|
||||||
|
F_PEOFPOSMODE c_int :: 3
|
||||||
|
F_VOLPOSMODE c_int :: 4
|
||||||
|
USER_FSIGNATURES_CDHASH_LEN c_int :: 20
|
||||||
|
GETSIGSINFO_PLATFORM_BINARY c_int :: 1
|
||||||
|
LOCK_SH c_int :: 1
|
||||||
|
LOCK_EX c_int :: 2
|
||||||
|
LOCK_NB c_int :: 4
|
||||||
|
LOCK_UN c_int :: 8
|
||||||
|
ATTRIBUTION_NAME_MAX c_int :: 255
|
||||||
|
F_CREATE_TAG c_int :: 1
|
||||||
|
F_DELETE_TAG c_int :: 2
|
||||||
|
F_QUERY_TAG c_int :: 4
|
||||||
|
O_POPUP c_uint :: 2147483648
|
||||||
|
O_ALERT c_int :: 536870912
|
||||||
|
FILESEC_OWNER c_uint :: 1
|
||||||
|
FILESEC_GROUP c_uint :: 2
|
||||||
|
FILESEC_UUID c_uint :: 3
|
||||||
|
FILESEC_MODE c_uint :: 4
|
||||||
|
FILESEC_ACL c_uint :: 5
|
||||||
|
FILESEC_GRPUUID c_uint :: 6
|
||||||
|
FILESEC_ACL_RAW c_uint :: 100
|
||||||
|
FILESEC_ACL_ALLOCSIZE c_uint :: 101
|
||||||
|
|
||||||
|
open c_func(_ ?*c_char, _ c_int, ...) c_int
|
||||||
|
openat c_func(_ c_int, _ ?*c_char, _ c_int, ...) c_int
|
||||||
|
creat c_func(_ ?*c_char, _ c_ushort) c_int
|
||||||
|
fcntl c_func(_ c_int, _ c_int, ...) c_int
|
||||||
|
openx_np c_func(_ ?*c_char, _ c_int, _ ?*mut _filesec) c_int
|
||||||
|
open_dprotected_np c_func(_ ?*c_char, _ c_int, _ c_int, _ c_int, ...) c_int
|
||||||
|
openat_dprotected_np c_func(_ c_int, _ ?*c_char, _ c_int, _ c_int, _ c_int, ...) c_int
|
||||||
|
openat_authenticated_np c_func(_ c_int, _ ?*c_char, _ c_int, _ c_int) c_int
|
||||||
|
flock c_func(_ c_int, _ c_int) c_int
|
||||||
|
filesec_init c_func() ?*mut _filesec
|
||||||
|
filesec_dup c_func(_ ?*mut _filesec) ?*mut _filesec
|
||||||
|
filesec_free c_func(_ ?*mut _filesec) void
|
||||||
|
filesec_get_property c_func(_ ?*mut _filesec, _ c_uint, _ ?*mut anyopaque) c_int
|
||||||
|
filesec_query_property c_func(_ ?*mut _filesec, _ c_uint, _ ?*mut c_int) c_int
|
||||||
|
filesec_set_property c_func(_ ?*mut _filesec, _ c_uint, _ ?*anyopaque) c_int
|
||||||
|
filesec_unset_property c_func(_ ?*mut _filesec, _ c_uint) c_int
|
||||||
|
|
||||||
|
# unsupported in bindings: _SYS_FCNTL_H_ — C macro has no replacement value
|
||||||
|
# unsupported in bindings: _SYS__TYPES_H_ — C macro has no replacement value
|
||||||
|
# unsupported in bindings: _CDEFS_H_ — C macro has no replacement value
|
||||||
|
# unsupported in bindings: _BSD_MACHINE__TYPES_H_ — C macro has no replacement value
|
||||||
|
# unsupported in bindings: _BSD_ARM__TYPES_H_ — C macro has no replacement value
|
||||||
|
# unsupported in bindings: _SYS__PTHREAD_TYPES_H_ — C macro has no replacement value
|
||||||
|
# unsupported in bindings: MAC_OS_X_VERSION_10_0 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_X_VERSION_10_1 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_X_VERSION_10_2 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_X_VERSION_10_3 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_X_VERSION_10_4 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_X_VERSION_10_5 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_X_VERSION_10_6 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_X_VERSION_10_7 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_X_VERSION_10_8 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_X_VERSION_10_9 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_X_VERSION_10_10 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_X_VERSION_10_10_2 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_X_VERSION_10_10_3 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_X_VERSION_10_11 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_X_VERSION_10_11_2 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_X_VERSION_10_11_3 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_X_VERSION_10_11_4 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_X_VERSION_10_12 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_X_VERSION_10_12_1 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_X_VERSION_10_12_2 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_X_VERSION_10_12_4 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_X_VERSION_10_13 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_X_VERSION_10_13_1 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_X_VERSION_10_13_2 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_X_VERSION_10_13_4 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_X_VERSION_10_14 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_X_VERSION_10_14_1 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_X_VERSION_10_14_4 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_X_VERSION_10_14_5 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_X_VERSION_10_14_6 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_X_VERSION_10_15 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_X_VERSION_10_15_1 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_X_VERSION_10_15_4 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_X_VERSION_10_16 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_11_0 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_11_1 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_11_3 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_11_4 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_11_5 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_11_6 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_12_0 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_12_1 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_12_2 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_12_3 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_12_4 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_12_5 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_12_6 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_12_7 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_13_0 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_13_1 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_13_2 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_13_3 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_13_4 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_13_5 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_13_6 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_13_7 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_14_0 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_14_1 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_14_2 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_14_3 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_14_4 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_14_5 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_14_6 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_14_7 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_15_0 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_15_1 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_15_2 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_15_3 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_15_4 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_15_5 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_15_6 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_16_0 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_26_0 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_26_1 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_26_2 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_26_3 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_26_4 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _SIZE_T — C macro has no replacement value
|
||||||
|
# unsupported in bindings: _MODE_T — C macro has no replacement value
|
||||||
|
# unsupported in bindings: _OFF_T — C macro has no replacement value
|
||||||
|
# unsupported in bindings: _PID_T — C macro has no replacement value
|
||||||
|
# unsupported in bindings: O_FSYNC — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: O_SEARCH — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: FAPPEND — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: FASYNC — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: FFSYNC — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: FFDSYNC — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: FNONBLOCK — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: FNDELAY — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: O_NDELAY — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: CPF_MASK — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: F_SETLEASE_ARG — C function-like macros are not supported
|
||||||
|
# unsupported in bindings: _SEEK_SET_H_ — C macro has no replacement value
|
||||||
|
# unsupported in bindings: S_ISTXT — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: S_IREAD — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: S_IWRITE — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: S_IEXEC — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _STRUCT_TIMESPEC — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _BSD_MACHINE_TYPES_H_ — C macro has no replacement value
|
||||||
|
# unsupported in bindings: _ARM_MACHTYPES_H_ — C macro has no replacement value
|
||||||
|
# unsupported in bindings: _MACHTYPES_H_ — C macro has no replacement value
|
||||||
|
# unsupported in bindings: _INT8_T — C macro has no replacement value
|
||||||
|
# unsupported in bindings: _INT16_T — C macro has no replacement value
|
||||||
|
# unsupported in bindings: _INT32_T — C macro has no replacement value
|
||||||
|
# unsupported in bindings: _INT64_T — C macro has no replacement value
|
||||||
|
# unsupported in bindings: _U_INT8_T — C macro has no replacement value
|
||||||
|
# unsupported in bindings: _U_INT16_T — C macro has no replacement value
|
||||||
|
# unsupported in bindings: _U_INT32_T — C macro has no replacement value
|
||||||
|
# unsupported in bindings: _U_INT64_T — C macro has no replacement value
|
||||||
|
# unsupported in bindings: _INTPTR_T — C macro has no replacement value
|
||||||
|
# unsupported in bindings: _UINTPTR_T — C macro has no replacement value
|
||||||
|
# unsupported in bindings: USER_ADDR_NULL — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: CAST_USER_ADDR_T — C function-like macros are not supported
|
||||||
|
# unsupported in bindings: _FILESEC_T — C macro has no replacement value
|
||||||
|
# unsupported in bindings: FILESEC_GUID — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _FILESEC_UNSET_PROPERTY — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _FILESEC_REMOVE_ACL — C macro is not a supported constant
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
read c_func(_ c_int, _ ?*mut anyopaque, _ c_ulong) c_long
|
|
||||||
write c_func(_ c_int, _ ?*anyopaque, _ c_ulong) c_long
|
|
||||||
__error c_func() *mut c_int
|
|
||||||
+4
-1
@@ -1,6 +1,7 @@
|
|||||||
# generated by brolang translate-c from stdio.h
|
# generated by brolang translate-c from stdio.h
|
||||||
|
|
||||||
# unsupported in bindings: C union '__mbstate_t' has no native spelling
|
# unsupported in bindings: C union '__mbstate_t' has no native spelling
|
||||||
|
__mbstate_t :: opaque
|
||||||
__darwin_pthread_handler_rec :: c_struct {
|
__darwin_pthread_handler_rec :: c_struct {
|
||||||
__routine ?*c_func(_ ?*mut anyopaque) void
|
__routine ?*c_func(_ ?*mut anyopaque) void
|
||||||
__arg ?*mut anyopaque
|
__arg ?*mut anyopaque
|
||||||
@@ -82,7 +83,8 @@ __uint64_t :: alias c_ulonglong
|
|||||||
__darwin_intptr_t :: alias c_long
|
__darwin_intptr_t :: alias c_long
|
||||||
__darwin_natural_t :: alias c_uint
|
__darwin_natural_t :: alias c_uint
|
||||||
__darwin_ct_rune_t :: alias c_int
|
__darwin_ct_rune_t :: alias c_int
|
||||||
__darwin_mbstate_t :: alias __mbstate_t
|
# unsupported in bindings: typedef '__mbstate_t' — underlying type has no native spelling
|
||||||
|
# unsupported in bindings: typedef '__darwin_mbstate_t' — underlying type has no native spelling
|
||||||
__darwin_ptrdiff_t :: alias c_long
|
__darwin_ptrdiff_t :: alias c_long
|
||||||
__darwin_size_t :: alias c_ulong
|
__darwin_size_t :: alias c_ulong
|
||||||
__darwin_va_list :: alias ?*mut c_char
|
__darwin_va_list :: alias ?*mut c_char
|
||||||
@@ -148,6 +150,7 @@ syscall_arg_t :: alias c_ulonglong
|
|||||||
va_list :: alias ?*mut c_char
|
va_list :: alias ?*mut c_char
|
||||||
size_t :: alias c_ulong
|
size_t :: alias c_ulong
|
||||||
fpos_t :: alias c_longlong
|
fpos_t :: alias c_longlong
|
||||||
|
# unsupported in bindings: typedef '__sFILEX' — underlying type has no native spelling
|
||||||
FILE :: alias __sFILE
|
FILE :: alias __sFILE
|
||||||
off_t :: alias c_longlong
|
off_t :: alias c_longlong
|
||||||
ssize_t :: alias c_long
|
ssize_t :: alias c_long
|
||||||
|
|||||||
+1091
-1
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,661 @@
|
|||||||
|
# generated by brolang translate-c from unistd.h
|
||||||
|
|
||||||
|
# unsupported in bindings: C union '__mbstate_t' has no native spelling
|
||||||
|
accessx_descriptor :: c_struct {
|
||||||
|
ad_name_offset c_uint
|
||||||
|
ad_flags c_int
|
||||||
|
ad_pad [2]c_int
|
||||||
|
}
|
||||||
|
fd_set :: c_struct {
|
||||||
|
fds_bits [32]c_int
|
||||||
|
}
|
||||||
|
timespec :: c_struct {
|
||||||
|
tv_sec c_long
|
||||||
|
tv_nsec c_long
|
||||||
|
}
|
||||||
|
fssearchblock :: opaque
|
||||||
|
searchstate :: opaque
|
||||||
|
|
||||||
|
# unsupported in bindings: typedef '__mbstate_t' — underlying type has no native spelling
|
||||||
|
# unsupported in bindings: typedef '__darwin_mbstate_t' — underlying type has no native spelling
|
||||||
|
gid_t :: alias c_uint
|
||||||
|
useconds_t :: alias c_uint
|
||||||
|
time_t :: alias c_long
|
||||||
|
suseconds_t :: alias c_int
|
||||||
|
uuid_t :: alias [16]c_uchar
|
||||||
|
# unsupported in bindings: typedef 'fssearchblock' — underlying type has no native spelling
|
||||||
|
# unsupported in bindings: typedef 'searchstate' — underlying type has no native spelling
|
||||||
|
|
||||||
|
_POSIX_VERSION c_long :: 200112
|
||||||
|
_POSIX2_VERSION c_long :: 200112
|
||||||
|
_POSIX_THREAD_KEYS_MAX c_int :: 128
|
||||||
|
F_OK c_int :: 0
|
||||||
|
ACCESSX_MAX_DESCRIPTORS c_int :: 100
|
||||||
|
_PC_LINK_MAX c_int :: 1
|
||||||
|
_PC_MAX_CANON c_int :: 2
|
||||||
|
_PC_MAX_INPUT c_int :: 3
|
||||||
|
_PC_NAME_MAX c_int :: 4
|
||||||
|
_PC_PATH_MAX c_int :: 5
|
||||||
|
_PC_PIPE_BUF c_int :: 6
|
||||||
|
_PC_CHOWN_RESTRICTED c_int :: 7
|
||||||
|
_PC_NO_TRUNC c_int :: 8
|
||||||
|
_PC_VDISABLE c_int :: 9
|
||||||
|
_PC_NAME_CHARS_MAX c_int :: 10
|
||||||
|
_PC_CASE_SENSITIVE c_int :: 11
|
||||||
|
_PC_CASE_PRESERVING c_int :: 12
|
||||||
|
_PC_EXTENDED_SECURITY_NP c_int :: 13
|
||||||
|
_PC_AUTH_OPAQUE_NP c_int :: 14
|
||||||
|
_PC_2_SYMLINKS c_int :: 15
|
||||||
|
_PC_ALLOC_SIZE_MIN c_int :: 16
|
||||||
|
_PC_ASYNC_IO c_int :: 17
|
||||||
|
_PC_FILESIZEBITS c_int :: 18
|
||||||
|
_PC_PRIO_IO c_int :: 19
|
||||||
|
_PC_REC_INCR_XFER_SIZE c_int :: 20
|
||||||
|
_PC_REC_MAX_XFER_SIZE c_int :: 21
|
||||||
|
_PC_REC_MIN_XFER_SIZE c_int :: 22
|
||||||
|
_PC_REC_XFER_ALIGN c_int :: 23
|
||||||
|
_PC_SYMLINK_MAX c_int :: 24
|
||||||
|
_PC_SYNC_IO c_int :: 25
|
||||||
|
_PC_XATTR_SIZE_BITS c_int :: 26
|
||||||
|
_PC_MIN_HOLE_SIZE c_int :: 27
|
||||||
|
_CS_PATH c_int :: 1
|
||||||
|
STDIN_FILENO c_int :: 0
|
||||||
|
STDOUT_FILENO c_int :: 1
|
||||||
|
STDERR_FILENO c_int :: 2
|
||||||
|
_XOPEN_VERSION c_int :: 600
|
||||||
|
_XOPEN_XCU_VERSION c_int :: 4
|
||||||
|
_POSIX_CHOWN_RESTRICTED c_long :: 200112
|
||||||
|
_POSIX_FSYNC c_long :: 200112
|
||||||
|
_POSIX_IPV6 c_long :: 200112
|
||||||
|
_POSIX_JOB_CONTROL c_long :: 200112
|
||||||
|
_POSIX_MAPPED_FILES c_long :: 200112
|
||||||
|
_POSIX_MEMORY_PROTECTION c_long :: 200112
|
||||||
|
_POSIX_NO_TRUNC c_long :: 200112
|
||||||
|
_POSIX_READER_WRITER_LOCKS c_long :: 200112
|
||||||
|
_POSIX_REGEXP c_long :: 200112
|
||||||
|
_POSIX_SAVED_IDS c_long :: 200112
|
||||||
|
_POSIX_SHELL c_long :: 200112
|
||||||
|
_POSIX_SPAWN c_long :: 200112
|
||||||
|
_POSIX_THREAD_ATTR_STACKADDR c_long :: 200112
|
||||||
|
_POSIX_THREAD_ATTR_STACKSIZE c_long :: 200112
|
||||||
|
_POSIX_THREAD_PROCESS_SHARED c_long :: 200112
|
||||||
|
_POSIX_THREAD_SAFE_FUNCTIONS c_long :: 200112
|
||||||
|
_POSIX_THREADS c_long :: 200112
|
||||||
|
_POSIX2_C_BIND c_long :: 200112
|
||||||
|
_POSIX2_C_DEV c_long :: 200112
|
||||||
|
_POSIX2_CHAR_TERM c_long :: 200112
|
||||||
|
_POSIX2_FORT_RUN c_long :: 200112
|
||||||
|
_POSIX2_LOCALEDEF c_long :: 200112
|
||||||
|
_POSIX2_SW_DEV c_long :: 200112
|
||||||
|
_POSIX2_UPE c_long :: 200112
|
||||||
|
_SC_ARG_MAX c_int :: 1
|
||||||
|
_SC_CHILD_MAX c_int :: 2
|
||||||
|
_SC_CLK_TCK c_int :: 3
|
||||||
|
_SC_NGROUPS_MAX c_int :: 4
|
||||||
|
_SC_OPEN_MAX c_int :: 5
|
||||||
|
_SC_JOB_CONTROL c_int :: 6
|
||||||
|
_SC_SAVED_IDS c_int :: 7
|
||||||
|
_SC_VERSION c_int :: 8
|
||||||
|
_SC_BC_BASE_MAX c_int :: 9
|
||||||
|
_SC_BC_DIM_MAX c_int :: 10
|
||||||
|
_SC_BC_SCALE_MAX c_int :: 11
|
||||||
|
_SC_BC_STRING_MAX c_int :: 12
|
||||||
|
_SC_COLL_WEIGHTS_MAX c_int :: 13
|
||||||
|
_SC_EXPR_NEST_MAX c_int :: 14
|
||||||
|
_SC_LINE_MAX c_int :: 15
|
||||||
|
_SC_RE_DUP_MAX c_int :: 16
|
||||||
|
_SC_2_VERSION c_int :: 17
|
||||||
|
_SC_2_C_BIND c_int :: 18
|
||||||
|
_SC_2_C_DEV c_int :: 19
|
||||||
|
_SC_2_CHAR_TERM c_int :: 20
|
||||||
|
_SC_2_FORT_DEV c_int :: 21
|
||||||
|
_SC_2_FORT_RUN c_int :: 22
|
||||||
|
_SC_2_LOCALEDEF c_int :: 23
|
||||||
|
_SC_2_SW_DEV c_int :: 24
|
||||||
|
_SC_2_UPE c_int :: 25
|
||||||
|
_SC_STREAM_MAX c_int :: 26
|
||||||
|
_SC_TZNAME_MAX c_int :: 27
|
||||||
|
_SC_ASYNCHRONOUS_IO c_int :: 28
|
||||||
|
_SC_PAGESIZE c_int :: 29
|
||||||
|
_SC_MEMLOCK c_int :: 30
|
||||||
|
_SC_MEMLOCK_RANGE c_int :: 31
|
||||||
|
_SC_MEMORY_PROTECTION c_int :: 32
|
||||||
|
_SC_MESSAGE_PASSING c_int :: 33
|
||||||
|
_SC_PRIORITIZED_IO c_int :: 34
|
||||||
|
_SC_PRIORITY_SCHEDULING c_int :: 35
|
||||||
|
_SC_REALTIME_SIGNALS c_int :: 36
|
||||||
|
_SC_SEMAPHORES c_int :: 37
|
||||||
|
_SC_FSYNC c_int :: 38
|
||||||
|
_SC_SHARED_MEMORY_OBJECTS c_int :: 39
|
||||||
|
_SC_SYNCHRONIZED_IO c_int :: 40
|
||||||
|
_SC_TIMERS c_int :: 41
|
||||||
|
_SC_AIO_LISTIO_MAX c_int :: 42
|
||||||
|
_SC_AIO_MAX c_int :: 43
|
||||||
|
_SC_AIO_PRIO_DELTA_MAX c_int :: 44
|
||||||
|
_SC_DELAYTIMER_MAX c_int :: 45
|
||||||
|
_SC_MQ_OPEN_MAX c_int :: 46
|
||||||
|
_SC_MAPPED_FILES c_int :: 47
|
||||||
|
_SC_RTSIG_MAX c_int :: 48
|
||||||
|
_SC_SEM_NSEMS_MAX c_int :: 49
|
||||||
|
_SC_SEM_VALUE_MAX c_int :: 50
|
||||||
|
_SC_SIGQUEUE_MAX c_int :: 51
|
||||||
|
_SC_TIMER_MAX c_int :: 52
|
||||||
|
_SC_NPROCESSORS_CONF c_int :: 57
|
||||||
|
_SC_NPROCESSORS_ONLN c_int :: 58
|
||||||
|
_SC_2_PBS c_int :: 59
|
||||||
|
_SC_2_PBS_ACCOUNTING c_int :: 60
|
||||||
|
_SC_2_PBS_CHECKPOINT c_int :: 61
|
||||||
|
_SC_2_PBS_LOCATE c_int :: 62
|
||||||
|
_SC_2_PBS_MESSAGE c_int :: 63
|
||||||
|
_SC_2_PBS_TRACK c_int :: 64
|
||||||
|
_SC_ADVISORY_INFO c_int :: 65
|
||||||
|
_SC_BARRIERS c_int :: 66
|
||||||
|
_SC_CLOCK_SELECTION c_int :: 67
|
||||||
|
_SC_CPUTIME c_int :: 68
|
||||||
|
_SC_FILE_LOCKING c_int :: 69
|
||||||
|
_SC_GETGR_R_SIZE_MAX c_int :: 70
|
||||||
|
_SC_GETPW_R_SIZE_MAX c_int :: 71
|
||||||
|
_SC_HOST_NAME_MAX c_int :: 72
|
||||||
|
_SC_LOGIN_NAME_MAX c_int :: 73
|
||||||
|
_SC_MONOTONIC_CLOCK c_int :: 74
|
||||||
|
_SC_MQ_PRIO_MAX c_int :: 75
|
||||||
|
_SC_READER_WRITER_LOCKS c_int :: 76
|
||||||
|
_SC_REGEXP c_int :: 77
|
||||||
|
_SC_SHELL c_int :: 78
|
||||||
|
_SC_SPAWN c_int :: 79
|
||||||
|
_SC_SPIN_LOCKS c_int :: 80
|
||||||
|
_SC_SPORADIC_SERVER c_int :: 81
|
||||||
|
_SC_THREAD_ATTR_STACKADDR c_int :: 82
|
||||||
|
_SC_THREAD_ATTR_STACKSIZE c_int :: 83
|
||||||
|
_SC_THREAD_CPUTIME c_int :: 84
|
||||||
|
_SC_THREAD_DESTRUCTOR_ITERATIONS c_int :: 85
|
||||||
|
_SC_THREAD_KEYS_MAX c_int :: 86
|
||||||
|
_SC_THREAD_PRIO_INHERIT c_int :: 87
|
||||||
|
_SC_THREAD_PRIO_PROTECT c_int :: 88
|
||||||
|
_SC_THREAD_PRIORITY_SCHEDULING c_int :: 89
|
||||||
|
_SC_THREAD_PROCESS_SHARED c_int :: 90
|
||||||
|
_SC_THREAD_SAFE_FUNCTIONS c_int :: 91
|
||||||
|
_SC_THREAD_SPORADIC_SERVER c_int :: 92
|
||||||
|
_SC_THREAD_STACK_MIN c_int :: 93
|
||||||
|
_SC_THREAD_THREADS_MAX c_int :: 94
|
||||||
|
_SC_TIMEOUTS c_int :: 95
|
||||||
|
_SC_THREADS c_int :: 96
|
||||||
|
_SC_TRACE c_int :: 97
|
||||||
|
_SC_TRACE_EVENT_FILTER c_int :: 98
|
||||||
|
_SC_TRACE_INHERIT c_int :: 99
|
||||||
|
_SC_TRACE_LOG c_int :: 100
|
||||||
|
_SC_TTY_NAME_MAX c_int :: 101
|
||||||
|
_SC_TYPED_MEMORY_OBJECTS c_int :: 102
|
||||||
|
_SC_V6_ILP32_OFF32 c_int :: 103
|
||||||
|
_SC_V6_ILP32_OFFBIG c_int :: 104
|
||||||
|
_SC_V6_LP64_OFF64 c_int :: 105
|
||||||
|
_SC_V6_LPBIG_OFFBIG c_int :: 106
|
||||||
|
_SC_IPV6 c_int :: 118
|
||||||
|
_SC_RAW_SOCKETS c_int :: 119
|
||||||
|
_SC_SYMLOOP_MAX c_int :: 120
|
||||||
|
_SC_ATEXIT_MAX c_int :: 107
|
||||||
|
_SC_IOV_MAX c_int :: 56
|
||||||
|
_SC_XOPEN_CRYPT c_int :: 108
|
||||||
|
_SC_XOPEN_ENH_I18N c_int :: 109
|
||||||
|
_SC_XOPEN_LEGACY c_int :: 110
|
||||||
|
_SC_XOPEN_REALTIME c_int :: 111
|
||||||
|
_SC_XOPEN_REALTIME_THREADS c_int :: 112
|
||||||
|
_SC_XOPEN_SHM c_int :: 113
|
||||||
|
_SC_XOPEN_STREAMS c_int :: 114
|
||||||
|
_SC_XOPEN_UNIX c_int :: 115
|
||||||
|
_SC_XOPEN_VERSION c_int :: 116
|
||||||
|
_SC_XOPEN_XCU_VERSION c_int :: 121
|
||||||
|
_SC_XBS5_ILP32_OFF32 c_int :: 122
|
||||||
|
_SC_XBS5_ILP32_OFFBIG c_int :: 123
|
||||||
|
_SC_XBS5_LP64_OFF64 c_int :: 124
|
||||||
|
_SC_XBS5_LPBIG_OFFBIG c_int :: 125
|
||||||
|
_SC_SS_REPL_MAX c_int :: 126
|
||||||
|
_SC_TRACE_EVENT_NAME_MAX c_int :: 127
|
||||||
|
_SC_TRACE_NAME_MAX c_int :: 128
|
||||||
|
_SC_TRACE_SYS_MAX c_int :: 129
|
||||||
|
_SC_TRACE_USER_EVENT_MAX c_int :: 130
|
||||||
|
_SC_PASS_MAX c_int :: 131
|
||||||
|
_SC_PHYS_PAGES c_int :: 200
|
||||||
|
_CS_POSIX_V6_ILP32_OFF32_CFLAGS c_int :: 2
|
||||||
|
_CS_POSIX_V6_ILP32_OFF32_LDFLAGS c_int :: 3
|
||||||
|
_CS_POSIX_V6_ILP32_OFF32_LIBS c_int :: 4
|
||||||
|
_CS_POSIX_V6_ILP32_OFFBIG_CFLAGS c_int :: 5
|
||||||
|
_CS_POSIX_V6_ILP32_OFFBIG_LDFLAGS c_int :: 6
|
||||||
|
_CS_POSIX_V6_ILP32_OFFBIG_LIBS c_int :: 7
|
||||||
|
_CS_POSIX_V6_LP64_OFF64_CFLAGS c_int :: 8
|
||||||
|
_CS_POSIX_V6_LP64_OFF64_LDFLAGS c_int :: 9
|
||||||
|
_CS_POSIX_V6_LP64_OFF64_LIBS c_int :: 10
|
||||||
|
_CS_POSIX_V6_LPBIG_OFFBIG_CFLAGS c_int :: 11
|
||||||
|
_CS_POSIX_V6_LPBIG_OFFBIG_LDFLAGS c_int :: 12
|
||||||
|
_CS_POSIX_V6_LPBIG_OFFBIG_LIBS c_int :: 13
|
||||||
|
_CS_POSIX_V6_WIDTH_RESTRICTED_ENVS c_int :: 14
|
||||||
|
_CS_XBS5_ILP32_OFF32_CFLAGS c_int :: 20
|
||||||
|
_CS_XBS5_ILP32_OFF32_LDFLAGS c_int :: 21
|
||||||
|
_CS_XBS5_ILP32_OFF32_LIBS c_int :: 22
|
||||||
|
_CS_XBS5_ILP32_OFF32_LINTFLAGS c_int :: 23
|
||||||
|
_CS_XBS5_ILP32_OFFBIG_CFLAGS c_int :: 24
|
||||||
|
_CS_XBS5_ILP32_OFFBIG_LDFLAGS c_int :: 25
|
||||||
|
_CS_XBS5_ILP32_OFFBIG_LIBS c_int :: 26
|
||||||
|
_CS_XBS5_ILP32_OFFBIG_LINTFLAGS c_int :: 27
|
||||||
|
_CS_XBS5_LP64_OFF64_CFLAGS c_int :: 28
|
||||||
|
_CS_XBS5_LP64_OFF64_LDFLAGS c_int :: 29
|
||||||
|
_CS_XBS5_LP64_OFF64_LIBS c_int :: 30
|
||||||
|
_CS_XBS5_LP64_OFF64_LINTFLAGS c_int :: 31
|
||||||
|
_CS_XBS5_LPBIG_OFFBIG_CFLAGS c_int :: 32
|
||||||
|
_CS_XBS5_LPBIG_OFFBIG_LDFLAGS c_int :: 33
|
||||||
|
_CS_XBS5_LPBIG_OFFBIG_LIBS c_int :: 34
|
||||||
|
_CS_XBS5_LPBIG_OFFBIG_LINTFLAGS c_int :: 35
|
||||||
|
_CS_DARWIN_USER_DIR c_int :: 65536
|
||||||
|
_CS_DARWIN_USER_TEMP_DIR c_int :: 65537
|
||||||
|
_CS_DARWIN_USER_CACHE_DIR c_int :: 65538
|
||||||
|
F_ULOCK c_int :: 0
|
||||||
|
F_LOCK c_int :: 1
|
||||||
|
F_TLOCK c_int :: 2
|
||||||
|
F_TEST c_int :: 3
|
||||||
|
SYNC_VOLUME_FULLSYNC c_int :: 1
|
||||||
|
SYNC_VOLUME_WAIT c_int :: 2
|
||||||
|
|
||||||
|
getattrlistbulk c_func(_ c_int, _ ?*mut anyopaque, _ ?*mut anyopaque, _ c_ulong, _ c_ulonglong) c_int
|
||||||
|
getattrlistat c_func(_ c_int, _ ?*c_char, _ ?*mut anyopaque, _ ?*mut anyopaque, _ c_ulong, _ c_ulong) c_int
|
||||||
|
setattrlistat c_func(_ c_int, _ ?*c_char, _ ?*mut anyopaque, _ ?*mut anyopaque, _ c_ulong, _ c_uint) c_int
|
||||||
|
freadlink c_func(_ c_int, _ ?*mut c_char, _ c_ulong) c_long
|
||||||
|
faccessat c_func(_ c_int, _ ?*c_char, _ c_int, _ c_int) c_int
|
||||||
|
fchownat c_func(_ c_int, _ ?*c_char, _ c_uint, _ c_uint, _ c_int) c_int
|
||||||
|
linkat c_func(_ c_int, _ ?*c_char, _ c_int, _ ?*c_char, _ c_int) c_int
|
||||||
|
readlinkat c_func(_ c_int, _ ?*c_char, _ ?*mut c_char, _ c_ulong) c_long
|
||||||
|
symlinkat c_func(_ ?*c_char, _ c_int, _ ?*c_char) c_int
|
||||||
|
unlinkat c_func(_ c_int, _ ?*c_char, _ c_int) c_int
|
||||||
|
_exit c_func(_ c_int) void
|
||||||
|
access c_func(_ ?*c_char, _ c_int) c_int
|
||||||
|
alarm c_func(_ c_uint) c_uint
|
||||||
|
chdir c_func(_ ?*c_char) c_int
|
||||||
|
chown c_func(_ ?*c_char, _ c_uint, _ c_uint) c_int
|
||||||
|
close c_func(_ c_int) c_int
|
||||||
|
dup c_func(_ c_int) c_int
|
||||||
|
dup2 c_func(_ c_int, _ c_int) c_int
|
||||||
|
execl c_func(__path ?*c_char, __arg0 ?*c_char, ...) c_int
|
||||||
|
execle c_func(__path ?*c_char, __arg0 ?*c_char, ...) c_int
|
||||||
|
execlp c_func(__file ?*c_char, __arg0 ?*c_char, ...) c_int
|
||||||
|
execv c_func(__path ?*c_char, __argv ?*?*mut c_char) c_int
|
||||||
|
execve c_func(__file ?*c_char, __argv ?*?*mut c_char, __envp ?*?*mut c_char) c_int
|
||||||
|
execvp c_func(__file ?*c_char, __argv ?*?*mut c_char) c_int
|
||||||
|
fork c_func() c_int
|
||||||
|
fpathconf c_func(_ c_int, _ c_int) c_long
|
||||||
|
getcwd c_func(_ ?*mut c_char, __size c_ulong) ?*mut c_char
|
||||||
|
getegid c_func() c_uint
|
||||||
|
geteuid c_func() c_uint
|
||||||
|
getgid c_func() c_uint
|
||||||
|
getgroups c_func(__gidsetsize c_int, _ ?*mut c_uint) c_int
|
||||||
|
getlogin c_func() ?*mut c_char
|
||||||
|
getpgrp c_func() c_int
|
||||||
|
getpid c_func() c_int
|
||||||
|
getppid c_func() c_int
|
||||||
|
getuid c_func() c_uint
|
||||||
|
isatty c_func(_ c_int) c_int
|
||||||
|
link c_func(_ ?*c_char, _ ?*c_char) c_int
|
||||||
|
lseek c_func(_ c_int, _ c_longlong, _ c_int) c_longlong
|
||||||
|
pathconf c_func(_ ?*c_char, _ c_int) c_long
|
||||||
|
pause c_func() c_int
|
||||||
|
pipe c_func(_ ?*mut c_int) c_int
|
||||||
|
read c_func(_ c_int, _ ?*mut anyopaque, __nbyte c_ulong) c_long
|
||||||
|
rmdir c_func(_ ?*c_char) c_int
|
||||||
|
setgid c_func(_ c_uint) c_int
|
||||||
|
setpgid c_func(_ c_int, _ c_int) c_int
|
||||||
|
setsid c_func() c_int
|
||||||
|
setuid c_func(_ c_uint) c_int
|
||||||
|
sleep c_func(_ c_uint) c_uint
|
||||||
|
sysconf c_func(_ c_int) c_long
|
||||||
|
tcgetpgrp c_func(_ c_int) c_int
|
||||||
|
tcsetpgrp c_func(_ c_int, _ c_int) c_int
|
||||||
|
ttyname c_func(_ c_int) ?*mut c_char
|
||||||
|
ttyname_r c_func(_ c_int, _ ?*mut c_char, __len c_ulong) c_int
|
||||||
|
unlink c_func(_ ?*c_char) c_int
|
||||||
|
write c_func(__fd c_int, __buf ?*anyopaque, __nbyte c_ulong) c_long
|
||||||
|
confstr c_func(_ c_int, _ ?*mut c_char, __len c_ulong) c_ulong
|
||||||
|
getopt c_func(__argc c_int, _ ?*?*mut c_char, _ ?*c_char) c_int
|
||||||
|
brk c_func(_ ?*anyopaque) ?*mut anyopaque
|
||||||
|
chroot c_func(_ ?*c_char) c_int
|
||||||
|
crypt c_func(_ ?*c_char, _ ?*c_char) ?*mut c_char
|
||||||
|
encrypt c_func(_ ?*mut c_char, _ c_int) void
|
||||||
|
fchdir c_func(_ c_int) c_int
|
||||||
|
gethostid c_func() c_long
|
||||||
|
getpgid c_func(_ c_int) c_int
|
||||||
|
getsid c_func(_ c_int) c_int
|
||||||
|
getdtablesize c_func() c_int
|
||||||
|
getpagesize c_func() c_int
|
||||||
|
getpass c_func(_ ?*c_char) ?*mut c_char
|
||||||
|
getwd c_func(_ ?*mut c_char) ?*mut c_char
|
||||||
|
lchown c_func(_ ?*c_char, _ c_uint, _ c_uint) c_int
|
||||||
|
lockf c_func(_ c_int, _ c_int, _ c_longlong) c_int
|
||||||
|
nice c_func(_ c_int) c_int
|
||||||
|
pread c_func(__fd c_int, __buf ?*mut anyopaque, __nbyte c_ulong, __offset c_longlong) c_long
|
||||||
|
pwrite c_func(__fd c_int, __buf ?*anyopaque, __nbyte c_ulong, __offset c_longlong) c_long
|
||||||
|
sbrk c_func(_ c_int) ?*mut anyopaque
|
||||||
|
setpgrp c_func() c_int
|
||||||
|
setregid c_func(_ c_uint, _ c_uint) c_int
|
||||||
|
setreuid c_func(_ c_uint, _ c_uint) c_int
|
||||||
|
swab c_func(_ ?*anyopaque, _ ?*mut anyopaque, __nbytes c_long) void
|
||||||
|
sync c_func() void
|
||||||
|
truncate c_func(_ ?*c_char, _ c_longlong) c_int
|
||||||
|
ualarm c_func(_ c_uint, _ c_uint) c_uint
|
||||||
|
usleep c_func(_ c_uint) c_int
|
||||||
|
vfork c_func() c_int
|
||||||
|
fsync c_func(_ c_int) c_int
|
||||||
|
ftruncate c_func(_ c_int, _ c_longlong) c_int
|
||||||
|
getlogin_r c_func(_ ?*mut c_char, __namelen c_ulong) c_int
|
||||||
|
fchown c_func(_ c_int, _ c_uint, _ c_uint) c_int
|
||||||
|
gethostname c_func(_ ?*mut c_char, __namelen c_ulong) c_int
|
||||||
|
readlink c_func(_ ?*c_char, _ ?*mut c_char, __bufsize c_ulong) c_long
|
||||||
|
setegid c_func(_ c_uint) c_int
|
||||||
|
seteuid c_func(_ c_uint) c_int
|
||||||
|
symlink c_func(_ ?*c_char, _ ?*c_char) c_int
|
||||||
|
__darwin_check_fd_set_overflow c_func(_ c_int, _ ?*anyopaque, _ c_int) c_int
|
||||||
|
__darwin_check_fd_set c_func(_a c_int, _b ?*anyopaque) c_int
|
||||||
|
__darwin_fd_isset c_func(_fd c_int, _p ?*fd_set) c_int
|
||||||
|
__darwin_fd_set c_func(_fd c_int, _p ?*mut fd_set) void
|
||||||
|
__darwin_fd_clr c_func(_fd c_int, _p ?*mut fd_set) void
|
||||||
|
pselect c_func(_ c_int, _ ?*mut fd_set, _ ?*mut fd_set, _ ?*mut fd_set, _ ?*timespec, _ ?*c_uint) c_int
|
||||||
|
select c_func(_ c_int, _ ?*mut fd_set, _ ?*mut fd_set, _ ?*mut fd_set, _ ?*mut timeval) c_int
|
||||||
|
accessx_np c_func(_ ?*accessx_descriptor, __sz c_ulong, _ ?*mut c_int, _ c_uint) c_int
|
||||||
|
acct c_func(_ ?*c_char) c_int
|
||||||
|
add_profil c_func(_ ?*mut c_char, __bufsiz c_ulong, _ c_ulong, _ c_uint) c_int
|
||||||
|
endusershell c_func() void
|
||||||
|
execvP c_func(__file ?*c_char, __searchpath ?*c_char, __argv ?*?*mut c_char) c_int
|
||||||
|
fflagstostr c_func(_ c_ulong) ?*mut c_char
|
||||||
|
getdomainname c_func(_ ?*mut c_char, __namelen c_int) c_int
|
||||||
|
getgrouplist c_func(_ ?*c_char, _ c_int, _ ?*mut c_int, __ngroups ?*mut c_int) c_int
|
||||||
|
gethostuuid c_func(_ ?*mut c_uchar, _ ?*timespec) c_int
|
||||||
|
getmode c_func(_ ?*anyopaque, _ c_ushort) c_ushort
|
||||||
|
getpeereid c_func(_ c_int, _ ?*mut c_uint, _ ?*mut c_uint) c_int
|
||||||
|
getsgroups_np c_func(_ ?*mut c_int, _ ?*mut c_uchar) c_int
|
||||||
|
getusershell c_func() ?*mut c_char
|
||||||
|
getwgroups_np c_func(_ ?*mut c_int, _ ?*mut c_uchar) c_int
|
||||||
|
initgroups c_func(_ ?*c_char, _ c_int) c_int
|
||||||
|
issetugid c_func() c_int
|
||||||
|
mkdtemp c_func(_ ?*mut c_char) ?*mut c_char
|
||||||
|
mknod c_func(_ ?*c_char, _ c_ushort, _ c_int) c_int
|
||||||
|
mkpath_np c_func(path ?*c_char, omode c_ushort) c_int
|
||||||
|
mkpathat_np c_func(dfd c_int, path ?*c_char, omode c_ushort) c_int
|
||||||
|
mkstemps c_func(_ ?*mut c_char, _ c_int) c_int
|
||||||
|
mkostemp c_func(path ?*mut c_char, oflags c_int) c_int
|
||||||
|
mkostemps c_func(path ?*mut c_char, slen c_int, oflags c_int) c_int
|
||||||
|
mkstemp_dprotected_np c_func(path ?*mut c_char, dpclass c_int, dpflags c_int) c_int
|
||||||
|
mkdtempat_np c_func(dfd c_int, path ?*mut c_char) ?*mut c_char
|
||||||
|
mkstempsat_np c_func(dfd c_int, path ?*mut c_char, slen c_int) c_int
|
||||||
|
mkostempsat_np c_func(dfd c_int, path ?*mut c_char, slen c_int, oflags c_int) c_int
|
||||||
|
nfssvc c_func(_ c_int, _ ?*mut anyopaque) c_int
|
||||||
|
profil c_func(_ ?*mut c_char, __bufsiz c_ulong, _ c_ulong, _ c_uint) c_int
|
||||||
|
pthread_setugid_np c_func(_ c_uint, _ c_uint) c_int
|
||||||
|
pthread_getugid_np c_func(_ ?*mut c_uint, _ ?*mut c_uint) c_int
|
||||||
|
reboot c_func(_ c_int) c_int
|
||||||
|
revoke c_func(_ ?*c_char) c_int
|
||||||
|
rcmd c_func(_ ?*mut ?*mut c_char, _ c_int, _ ?*c_char, _ ?*c_char, _ ?*c_char, _ ?*mut c_int) c_int
|
||||||
|
rcmd_af c_func(_ ?*mut ?*mut c_char, _ c_int, _ ?*c_char, _ ?*c_char, _ ?*c_char, _ ?*mut c_int, _ c_int) c_int
|
||||||
|
rresvport c_func(_ ?*mut c_int) c_int
|
||||||
|
rresvport_af c_func(_ ?*mut c_int, _ c_int) c_int
|
||||||
|
iruserok c_func(_ c_ulong, _ c_int, _ ?*c_char, _ ?*c_char) c_int
|
||||||
|
iruserok_sa c_func(_ ?*anyopaque, _ c_int, _ c_int, _ ?*c_char, _ ?*c_char) c_int
|
||||||
|
ruserok c_func(_ ?*c_char, _ c_int, _ ?*c_char, _ ?*c_char) c_int
|
||||||
|
setdomainname c_func(_ ?*c_char, __namelen c_int) c_int
|
||||||
|
setgroups c_func(_ c_int, _ ?*c_uint) c_int
|
||||||
|
sethostid c_func(_ c_long) void
|
||||||
|
sethostname c_func(_ ?*c_char, __namelen c_int) c_int
|
||||||
|
setlogin c_func(_ ?*c_char) c_int
|
||||||
|
setmode c_func(_ ?*c_char) ?*mut anyopaque
|
||||||
|
setrgid c_func(_ c_uint) c_int
|
||||||
|
setruid c_func(_ c_uint) c_int
|
||||||
|
setsgroups_np c_func(_ c_int, _ ?*mut c_uchar) c_int
|
||||||
|
setusershell c_func() void
|
||||||
|
setwgroups_np c_func(_ c_int, _ ?*mut c_uchar) c_int
|
||||||
|
strtofflags c_func(_ ?*mut ?*mut c_char, _ ?*mut c_ulong, _ ?*mut c_ulong) c_int
|
||||||
|
swapon c_func(_ ?*c_char) c_int
|
||||||
|
ttyslot c_func() c_int
|
||||||
|
undelete c_func(_ ?*c_char) c_int
|
||||||
|
unwhiteout c_func(_ ?*c_char) c_int
|
||||||
|
syscall c_func(_ c_int, ...) c_int
|
||||||
|
fgetattrlist c_func(_ c_int, _ ?*mut anyopaque, _ ?*mut anyopaque, __attrBufSize c_ulong, _ c_uint) c_int
|
||||||
|
fsetattrlist c_func(_ c_int, _ ?*mut anyopaque, _ ?*mut anyopaque, __attrBufSize c_ulong, _ c_uint) c_int
|
||||||
|
getattrlist c_func(_ ?*c_char, _ ?*mut anyopaque, _ ?*mut anyopaque, __attrBufSize c_ulong, _ c_uint) c_int
|
||||||
|
setattrlist c_func(_ ?*c_char, _ ?*mut anyopaque, _ ?*mut anyopaque, __attrBufSize c_ulong, _ c_uint) c_int
|
||||||
|
exchangedata c_func(_ ?*c_char, _ ?*c_char, _ c_uint) c_int
|
||||||
|
getdirentriesattr c_func(_ c_int, _ ?*mut anyopaque, _ ?*mut anyopaque, __attrBufSize c_ulong, _ ?*mut c_uint, _ ?*mut c_uint, _ ?*mut c_uint, _ c_uint) c_int
|
||||||
|
searchfs c_func(_ ?*c_char, _ ?*mut fssearchblock, _ ?*mut c_ulong, _ c_uint, _ c_uint, _ ?*mut searchstate) c_int
|
||||||
|
fsctl c_func(_ ?*c_char, _ c_ulong, _ ?*mut anyopaque, _ c_uint) c_int
|
||||||
|
ffsctl c_func(_ c_int, _ c_ulong, _ ?*mut anyopaque, _ c_uint) c_int
|
||||||
|
fsync_volume_np c_func(_ c_int, _ c_int) c_int
|
||||||
|
sync_volume_np c_func(_ ?*c_char, _ c_int) c_int
|
||||||
|
|
||||||
|
# unsupported in bindings: external variable 'optarg' has no native spelling
|
||||||
|
# unsupported in bindings: external variable 'optind' has no native spelling
|
||||||
|
# unsupported in bindings: external variable 'opterr' has no native spelling
|
||||||
|
# unsupported in bindings: external variable 'optopt' has no native spelling
|
||||||
|
# unsupported in bindings: external variable 'suboptarg' has no native spelling
|
||||||
|
# unsupported in bindings: external variable 'optreset' has no native spelling
|
||||||
|
# unsupported in bindings: _UNISTD_H_ — C macro has no replacement value
|
||||||
|
# unsupported in bindings: _LIBC_BOUNDS_H_ — C macro has no replacement value
|
||||||
|
# unsupported in bindings: _CDEFS_H_ — C macro has no replacement value
|
||||||
|
# unsupported in bindings: _LIBC_COUNT — C function-like macros are not supported
|
||||||
|
# unsupported in bindings: _LIBC_COUNT_OR_NULL — C function-like macros are not supported
|
||||||
|
# unsupported in bindings: _LIBC_SIZE — C function-like macros are not supported
|
||||||
|
# unsupported in bindings: _LIBC_SIZE_OR_NULL — C function-like macros are not supported
|
||||||
|
# unsupported in bindings: _LIBC_ENDED_BY — C function-like macros are not supported
|
||||||
|
# unsupported in bindings: _LIBC_SINGLE — C macro has no replacement value
|
||||||
|
# unsupported in bindings: _LIBC_UNSAFE_INDEXABLE — C macro has no replacement value
|
||||||
|
# unsupported in bindings: _LIBC_CSTR — C macro has no replacement value
|
||||||
|
# unsupported in bindings: _LIBC_NULL_TERMINATED — C macro has no replacement value
|
||||||
|
# unsupported in bindings: _LIBC_FLEX_COUNT — C function-like macros are not supported
|
||||||
|
# unsupported in bindings: _LIBC_SINGLE_BY_DEFAULT — C function-like macros are not supported
|
||||||
|
# unsupported in bindings: _LIBC_PTRCHECK_REPLACED — C function-like macros are not supported
|
||||||
|
# unsupported in bindings: _LIBC_FORGE_PTR — C function-like macros are not supported
|
||||||
|
# unsupported in bindings: _SYS__TYPES_H_ — C macro has no replacement value
|
||||||
|
# unsupported in bindings: _BSD_MACHINE__TYPES_H_ — C macro has no replacement value
|
||||||
|
# unsupported in bindings: _BSD_ARM__TYPES_H_ — C macro has no replacement value
|
||||||
|
# unsupported in bindings: _SYS__PTHREAD_TYPES_H_ — C macro has no replacement value
|
||||||
|
# unsupported in bindings: _SYS_UNISTD_H_ — C macro has no replacement value
|
||||||
|
# unsupported in bindings: _POSIX_VDISABLE — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: X_OK — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: W_OK — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: R_OK — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _READ_OK — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _WRITE_OK — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _EXECUTE_OK — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _DELETE_OK — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _APPEND_OK — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _RMFILE_OK — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _RATTR_OK — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _WATTR_OK — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _REXT_OK — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _WEXT_OK — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _RPERM_OK — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _WPERM_OK — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _CHOWN_OK — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _ACCESS_EXTENDED_MASK — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _SEEK_SET_H_ — C macro has no replacement value
|
||||||
|
# unsupported in bindings: L_SET — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: L_INCR — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: L_XTND — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: ACCESSX_MAX_TABLESIZE — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _SIZE_T — C macro has no replacement value
|
||||||
|
# unsupported in bindings: _SSIZE_T — C macro has no replacement value
|
||||||
|
# unsupported in bindings: _BSD_MACHINE_TYPES_H_ — C macro has no replacement value
|
||||||
|
# unsupported in bindings: _ARM_MACHTYPES_H_ — C macro has no replacement value
|
||||||
|
# unsupported in bindings: _MACHTYPES_H_ — C macro has no replacement value
|
||||||
|
# unsupported in bindings: _INT8_T — C macro has no replacement value
|
||||||
|
# unsupported in bindings: _INT16_T — C macro has no replacement value
|
||||||
|
# unsupported in bindings: _INT32_T — C macro has no replacement value
|
||||||
|
# unsupported in bindings: _INT64_T — C macro has no replacement value
|
||||||
|
# unsupported in bindings: _U_INT8_T — C macro has no replacement value
|
||||||
|
# unsupported in bindings: _U_INT16_T — C macro has no replacement value
|
||||||
|
# unsupported in bindings: _U_INT32_T — C macro has no replacement value
|
||||||
|
# unsupported in bindings: _U_INT64_T — C macro has no replacement value
|
||||||
|
# unsupported in bindings: _INTPTR_T — C macro has no replacement value
|
||||||
|
# unsupported in bindings: _UINTPTR_T — C macro has no replacement value
|
||||||
|
# unsupported in bindings: USER_ADDR_NULL — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: CAST_USER_ADDR_T — C function-like macros are not supported
|
||||||
|
# unsupported in bindings: _UINT64_T — C macro has no replacement value
|
||||||
|
# unsupported in bindings: _UINT32_T — C macro has no replacement value
|
||||||
|
# unsupported in bindings: MAC_OS_X_VERSION_10_0 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_X_VERSION_10_1 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_X_VERSION_10_2 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_X_VERSION_10_3 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_X_VERSION_10_4 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_X_VERSION_10_5 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_X_VERSION_10_6 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_X_VERSION_10_7 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_X_VERSION_10_8 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_X_VERSION_10_9 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_X_VERSION_10_10 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_X_VERSION_10_10_2 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_X_VERSION_10_10_3 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_X_VERSION_10_11 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_X_VERSION_10_11_2 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_X_VERSION_10_11_3 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_X_VERSION_10_11_4 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_X_VERSION_10_12 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_X_VERSION_10_12_1 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_X_VERSION_10_12_2 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_X_VERSION_10_12_4 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_X_VERSION_10_13 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_X_VERSION_10_13_1 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_X_VERSION_10_13_2 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_X_VERSION_10_13_4 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_X_VERSION_10_14 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_X_VERSION_10_14_1 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_X_VERSION_10_14_4 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_X_VERSION_10_14_5 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_X_VERSION_10_14_6 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_X_VERSION_10_15 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_X_VERSION_10_15_1 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_X_VERSION_10_15_4 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_X_VERSION_10_16 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_11_0 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_11_1 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_11_3 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_11_4 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_11_5 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_11_6 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_12_0 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_12_1 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_12_2 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_12_3 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_12_4 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_12_5 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_12_6 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_12_7 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_13_0 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_13_1 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_13_2 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_13_3 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_13_4 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_13_5 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_13_6 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_13_7 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_14_0 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_14_1 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_14_2 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_14_3 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_14_4 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_14_5 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_14_6 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_14_7 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_15_0 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_15_1 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_15_2 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_15_3 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_15_4 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_15_5 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_15_6 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_16_0 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_26_0 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_26_1 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_26_2 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_26_3 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: MAC_OS_VERSION_26_4 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _UID_T — C macro has no replacement value
|
||||||
|
# unsupported in bindings: _GID_T — C macro has no replacement value
|
||||||
|
# unsupported in bindings: _LIBC_COUNT__PATH_MAX — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _OFF_T — C macro has no replacement value
|
||||||
|
# unsupported in bindings: _PID_T — C macro has no replacement value
|
||||||
|
# unsupported in bindings: _USECONDS_T — C macro has no replacement value
|
||||||
|
# unsupported in bindings: NULL — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _POSIX_ADVISORY_INFO — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _POSIX_ASYNCHRONOUS_IO — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _POSIX_BARRIERS — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _POSIX_CLOCK_SELECTION — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _POSIX_CPUTIME — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _POSIX_MEMLOCK — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _POSIX_MEMLOCK_RANGE — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _POSIX_MESSAGE_PASSING — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _POSIX_MONOTONIC_CLOCK — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _POSIX_PRIORITIZED_IO — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _POSIX_PRIORITY_SCHEDULING — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _POSIX_RAW_SOCKETS — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _POSIX_REALTIME_SIGNALS — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _POSIX_SEMAPHORES — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _POSIX_SHARED_MEMORY_OBJECTS — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _POSIX_SPIN_LOCKS — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _POSIX_SPORADIC_SERVER — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _POSIX_SYNCHRONIZED_IO — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _POSIX_THREAD_CPUTIME — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _POSIX_THREAD_PRIO_INHERIT — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _POSIX_THREAD_PRIO_PROTECT — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _POSIX_THREAD_PRIORITY_SCHEDULING — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _POSIX_THREAD_SPORADIC_SERVER — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _POSIX_TIMEOUTS — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _POSIX_TIMERS — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _POSIX_TRACE — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _POSIX_TRACE_EVENT_FILTER — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _POSIX_TRACE_INHERIT — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _POSIX_TRACE_LOG — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _POSIX_TYPED_MEMORY_OBJECTS — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _POSIX2_FORT_DEV — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _POSIX2_PBS — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _POSIX2_PBS_ACCOUNTING — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _POSIX2_PBS_CHECKPOINT — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _POSIX2_PBS_LOCATE — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _POSIX2_PBS_MESSAGE — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _POSIX2_PBS_TRACK — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _POSIX_V6_ILP32_OFF32 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _POSIX_V6_ILP32_OFFBIG — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _POSIX_V6_LP64_OFF64 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _POSIX_V6_LPBIG_OFFBIG — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _POSIX_V7_ILP32_OFF32 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _POSIX_V7_ILP32_OFFBIG — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _POSIX_V7_LP64_OFF64 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _POSIX_V7_LPBIG_OFFBIG — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _V6_ILP32_OFF32 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _V6_ILP32_OFFBIG — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _V6_LP64_OFF64 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _V6_LPBIG_OFFBIG — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _XBS5_ILP32_OFF32 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _XBS5_ILP32_OFFBIG — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _XBS5_LP64_OFF64 — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _XBS5_LPBIG_OFFBIG — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _XOPEN_CRYPT — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _XOPEN_ENH_I18N — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _XOPEN_LEGACY — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _XOPEN_REALTIME — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _XOPEN_REALTIME_THREADS — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _XOPEN_SHM — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _XOPEN_STREAMS — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _XOPEN_UNIX — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _SC_PAGE_SIZE — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _CTERMID_H_ — C macro has no replacement value
|
||||||
|
# unsupported in bindings: _LIBC_COUNT__L_CTERMID — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _SYS_SELECT_H_ — C macro has no replacement value
|
||||||
|
# unsupported in bindings: _FD_SET — C macro has no replacement value
|
||||||
|
# unsupported in bindings: _STRUCT_TIMESPEC — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _STRUCT_TIMEVAL — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: _TIME_T — C macro has no replacement value
|
||||||
|
# unsupported in bindings: _SUSECONDS_T — C macro has no replacement value
|
||||||
|
# unsupported in bindings: _SIGSET_T — C macro has no replacement value
|
||||||
|
# unsupported in bindings: FD_SETSIZE — C macro is not a supported constant
|
||||||
|
# unsupported in bindings: FD_SET — C function-like macros are not supported
|
||||||
|
# unsupported in bindings: FD_CLR — C function-like macros are not supported
|
||||||
|
# unsupported in bindings: FD_ISSET — C function-like macros are not supported
|
||||||
|
# unsupported in bindings: FD_ZERO — C function-like macros are not supported
|
||||||
|
# unsupported in bindings: FD_COPY — C function-like macros are not supported
|
||||||
|
# unsupported in bindings: _SYS__SELECT_H_ — C macro has no replacement value
|
||||||
|
# unsupported in bindings: _DEV_T — C macro has no replacement value
|
||||||
|
# unsupported in bindings: _MODE_T — C macro has no replacement value
|
||||||
|
# unsupported in bindings: _UUID_T — C macro has no replacement value
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
name = "Brolang"
|
|
||||||
grammar = "brolang"
|
|
||||||
path_suffixes = ["bro", "hon"]
|
|
||||||
line_comments = ["# "]
|
|
||||||
hard_tabs = true
|
|
||||||
tab_size = 4
|
|
||||||
autoclose_before = ";:.,=}])>"
|
|
||||||
brackets = [
|
|
||||||
{ start = "{", end = "}", close = true, newline = true },
|
|
||||||
{ start = "[", end = "]", close = true, newline = true },
|
|
||||||
{ start = "(", end = ")", close = true, newline = true },
|
|
||||||
{ start = "'", end = "'", close = true, newline = false, not_in = ["comment", "string"] },
|
|
||||||
{ start = "\"", end = "\"", close = true, newline = false, not_in = ["comment", "string"] },
|
|
||||||
]
|
|
||||||
@@ -1,122 +0,0 @@
|
|||||||
(comment) @comment
|
|
||||||
|
|
||||||
[
|
|
||||||
(string)
|
|
||||||
(multiline_string)
|
|
||||||
] @string
|
|
||||||
|
|
||||||
(character) @string
|
|
||||||
(escape_sequence) @string.escape
|
|
||||||
|
|
||||||
[
|
|
||||||
(integer)
|
|
||||||
(float)
|
|
||||||
] @number
|
|
||||||
|
|
||||||
(boolean) @boolean
|
|
||||||
|
|
||||||
[
|
|
||||||
(none)
|
|
||||||
(undefined)
|
|
||||||
] @constant.builtin
|
|
||||||
|
|
||||||
(builtin_type) @type.builtin
|
|
||||||
(named_type) @type
|
|
||||||
|
|
||||||
(named_type
|
|
||||||
(qualified_identifier
|
|
||||||
(identifier) @function .)
|
|
||||||
(argument_list))
|
|
||||||
|
|
||||||
(struct_literal
|
|
||||||
type: (qualified_identifier
|
|
||||||
(identifier) @function .)
|
|
||||||
(argument_list))
|
|
||||||
|
|
||||||
(type_declaration name: (identifier) @type)
|
|
||||||
(function_declaration name: (identifier) @function)
|
|
||||||
(parameter name: (identifier) @variable.parameter)
|
|
||||||
|
|
||||||
(call_expression function: (expression (identifier) @function))
|
|
||||||
(call_expression function: (expression (field_expression field: (identifier) @function)))
|
|
||||||
(field_expression field: (identifier) @property)
|
|
||||||
(field_initializer name: (identifier) @property)
|
|
||||||
(keyed_field_initializer name: (identifier) @property)
|
|
||||||
(record_field name: (identifier) @property)
|
|
||||||
(enum_member name: (identifier) @property)
|
|
||||||
(enum_literal name: (identifier) @property)
|
|
||||||
|
|
||||||
(import_declaration alias: (identifier) @variable)
|
|
||||||
(opaque_type) @keyword
|
|
||||||
|
|
||||||
[
|
|
||||||
"func"
|
|
||||||
"c_func"
|
|
||||||
"struct"
|
|
||||||
"c_struct"
|
|
||||||
"union"
|
|
||||||
"enum"
|
|
||||||
"distinct"
|
|
||||||
"alias"
|
|
||||||
"import"
|
|
||||||
"return"
|
|
||||||
"try"
|
|
||||||
"catch"
|
|
||||||
"mut"
|
|
||||||
"orelse"
|
|
||||||
"and"
|
|
||||||
"or"
|
|
||||||
"if"
|
|
||||||
"while"
|
|
||||||
"for"
|
|
||||||
"break"
|
|
||||||
"continue"
|
|
||||||
"defer"
|
|
||||||
"errdefer"
|
|
||||||
"yield"
|
|
||||||
"match"
|
|
||||||
"else"
|
|
||||||
] @keyword
|
|
||||||
|
|
||||||
[
|
|
||||||
"::"
|
|
||||||
"="
|
|
||||||
"+="
|
|
||||||
"-="
|
|
||||||
"*="
|
|
||||||
"/="
|
|
||||||
"=="
|
|
||||||
"!="
|
|
||||||
"<"
|
|
||||||
"<="
|
|
||||||
">"
|
|
||||||
">="
|
|
||||||
"+"
|
|
||||||
"-"
|
|
||||||
"*"
|
|
||||||
"/"
|
|
||||||
"!"
|
|
||||||
"&"
|
|
||||||
"@"
|
|
||||||
"?"
|
|
||||||
"^"
|
|
||||||
".."
|
|
||||||
"..="
|
|
||||||
"|"
|
|
||||||
] @operator
|
|
||||||
|
|
||||||
[
|
|
||||||
"("
|
|
||||||
")"
|
|
||||||
"["
|
|
||||||
"]"
|
|
||||||
"{"
|
|
||||||
"}"
|
|
||||||
] @punctuation.bracket
|
|
||||||
|
|
||||||
[
|
|
||||||
","
|
|
||||||
"."
|
|
||||||
":"
|
|
||||||
";"
|
|
||||||
] @punctuation.delimiter
|
|
||||||
@@ -98,10 +98,13 @@ print_usage :: proc() {
|
|||||||
"usage: brolang <package-directory> -o <executable> [--root <project-root>] [--target aarch64-macos] [--c-link <path> | --c-library-path <dir> | --c-library <name> | --c-include-path <dir> | --c-define <name[=value]>]...",
|
"usage: brolang <package-directory> -o <executable> [--root <project-root>] [--target aarch64-macos] [--c-link <path> | --c-library-path <dir> | --c-library <name> | --c-include-path <dir> | --c-define <name[=value]>]...",
|
||||||
)
|
)
|
||||||
fmt.eprintln(
|
fmt.eprintln(
|
||||||
" brolang translate-c|--translate-c <header.h> [--target aarch64-macos] [--c-include-path <dir> | --c-define <name[=value]>]...",
|
" brolang translate-c|--translate-c <header.h>... [--output-dir <dir>] [--target aarch64-macos] [--c-include-path <dir> | --c-define <name[=value]>]...",
|
||||||
)
|
)
|
||||||
fmt.eprintln(
|
fmt.eprintln(
|
||||||
" brolang build [root] (reads root/build.bro; writes root/build/name)",
|
" brolang build [root] (reads exactly one of root/build.bro or root/build.hon; writes root/build/name)",
|
||||||
|
)
|
||||||
|
fmt.eprintln(
|
||||||
|
" brolang test [root] (reads exactly one of root/build.bro or root/build.hon; writes and runs root/build/name-test)",
|
||||||
)
|
)
|
||||||
fmt.eprintln(
|
fmt.eprintln(
|
||||||
" brolang new <project-path>",
|
" brolang new <project-path>",
|
||||||
@@ -122,6 +125,10 @@ is_build_command :: proc(arg: string) -> bool {
|
|||||||
return arg == "build"
|
return arg == "build"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
is_test_command :: proc(arg: string) -> bool {
|
||||||
|
return arg == "test"
|
||||||
|
}
|
||||||
|
|
||||||
is_new_command :: proc(arg: string) -> bool {
|
is_new_command :: proc(arg: string) -> bool {
|
||||||
return arg == "new"
|
return arg == "new"
|
||||||
}
|
}
|
||||||
@@ -135,17 +142,22 @@ is_version_command :: proc(arg: string) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template_root_valid :: proc(root: string) -> bool {
|
template_root_valid :: proc(root: string) -> bool {
|
||||||
std_build, std_error := filepath.join({root, "std", "build", "build.bro"})
|
std_build_bro, bro_error := filepath.join({root, "std", "build", "build.bro"})
|
||||||
if std_error != nil {
|
if bro_error != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
defer delete(std_build)
|
defer delete(std_build_bro)
|
||||||
|
std_build_hon, hon_error := filepath.join({root, "std", "build", "build.hon"})
|
||||||
|
if hon_error != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
defer delete(std_build_hon)
|
||||||
ffi_stdio, ffi_error := filepath.join({root, "ffi", "c", "stdio.bro"})
|
ffi_stdio, ffi_error := filepath.join({root, "ffi", "c", "stdio.bro"})
|
||||||
if ffi_error != nil {
|
if ffi_error != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
defer delete(ffi_stdio)
|
defer delete(ffi_stdio)
|
||||||
return os.exists(std_build) && os.exists(ffi_stdio)
|
return (os.exists(std_build_bro) || os.exists(std_build_hon)) && os.exists(ffi_stdio)
|
||||||
}
|
}
|
||||||
|
|
||||||
find_template_root :: proc(allocator := context.allocator) -> (string, bool) {
|
find_template_root :: proc(allocator := context.allocator) -> (string, bool) {
|
||||||
@@ -227,7 +239,7 @@ default_build_bro :: proc(project_name: string, allocator := context.allocator)
|
|||||||
defer strings.builder_destroy(&builder)
|
defer strings.builder_destroy(&builder)
|
||||||
strings.write_string(&builder, "b :: import \"@std/build\"\n\nconfig :: b.BuildConfig{\n\tname = \"")
|
strings.write_string(&builder, "b :: import \"@std/build\"\n\nconfig :: b.BuildConfig{\n\tname = \"")
|
||||||
write_escaped_brolang_string(&builder, name)
|
write_escaped_brolang_string(&builder, name)
|
||||||
strings.write_string(&builder, "\",\n\tsource = \"source\",\n\tlibraries = &[],\n\tlib_paths = &[],\n\tincludes = &[],\n\tdefines = &[],\n\tlinks = &[],\n}\n")
|
strings.write_string(&builder, "\",\n\tsource = \"source\",\n}\n")
|
||||||
return strings.clone(strings.to_string(builder), allocator)
|
return strings.clone(strings.to_string(builder), allocator)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -429,11 +441,14 @@ run_translate_c :: proc(args: []string) -> int {
|
|||||||
print_usage()
|
print_usage()
|
||||||
return 2
|
return 2
|
||||||
}
|
}
|
||||||
header := args[2]
|
|
||||||
selected := target.DEFAULT
|
selected := target.DEFAULT
|
||||||
|
headers: [dynamic]string
|
||||||
include_paths: [dynamic]string
|
include_paths: [dynamic]string
|
||||||
owned_include_paths: [dynamic]string
|
owned_include_paths: [dynamic]string
|
||||||
defines: [dynamic]string
|
defines: [dynamic]string
|
||||||
|
output_dir := ""
|
||||||
|
output_dir_set := false
|
||||||
|
defer delete(headers)
|
||||||
defer delete(include_paths)
|
defer delete(include_paths)
|
||||||
defer {
|
defer {
|
||||||
for path in owned_include_paths {
|
for path in owned_include_paths {
|
||||||
@@ -442,10 +457,14 @@ run_translate_c :: proc(args: []string) -> int {
|
|||||||
delete(owned_include_paths)
|
delete(owned_include_paths)
|
||||||
}
|
}
|
||||||
defer delete(defines)
|
defer delete(defines)
|
||||||
cursor := 3
|
cursor := 2
|
||||||
for cursor < len(args) {
|
for cursor < len(args) {
|
||||||
option := args[cursor]
|
option := args[cursor]
|
||||||
cursor += 1
|
cursor += 1
|
||||||
|
if !strings.has_prefix(option, "--") {
|
||||||
|
append(&headers, option)
|
||||||
|
continue
|
||||||
|
}
|
||||||
if cursor >= len(args) {
|
if cursor >= len(args) {
|
||||||
fmt.eprintfln("missing value for %s", option)
|
fmt.eprintfln("missing value for %s", option)
|
||||||
return 2
|
return 2
|
||||||
@@ -457,6 +476,13 @@ run_translate_c :: proc(args: []string) -> int {
|
|||||||
append(&include_paths, value)
|
append(&include_paths, value)
|
||||||
case "--c-define":
|
case "--c-define":
|
||||||
append(&defines, value)
|
append(&defines, value)
|
||||||
|
case "--output-dir":
|
||||||
|
if output_dir_set || len(value) == 0 {
|
||||||
|
fmt.eprintln("--output-dir may be provided once with a non-empty value")
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
output_dir = value
|
||||||
|
output_dir_set = true
|
||||||
case "--target":
|
case "--target":
|
||||||
parsed, ok := target.parse(value)
|
parsed, ok := target.parse(value)
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -469,18 +495,112 @@ run_translate_c :: proc(args: []string) -> int {
|
|||||||
return 2
|
return 2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if len(headers) == 0 {
|
||||||
|
print_usage()
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
if len(headers) > 1 && !output_dir_set {
|
||||||
|
fmt.eprintln("multiple headers require --output-dir")
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
names := make([]string, len(headers))
|
||||||
|
defer {
|
||||||
|
for name in names {
|
||||||
|
delete(name)
|
||||||
|
}
|
||||||
|
delete(names)
|
||||||
|
}
|
||||||
|
if output_dir_set {
|
||||||
|
seen_names: map[string]bool
|
||||||
|
defer delete(seen_names)
|
||||||
|
for header, index in headers {
|
||||||
|
base := filepath.base(header)
|
||||||
|
extension := filepath.ext(base)
|
||||||
|
stem := base[:len(base)-len(extension)]
|
||||||
|
if len(stem) == 0 {
|
||||||
|
fmt.eprintfln("cannot derive an output name from header '%s'", header)
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
name := fmt.aprintf("%s.bro", stem)
|
||||||
|
if seen_names[name] {
|
||||||
|
fmt.eprintfln("duplicate generated output name '%s'", name)
|
||||||
|
delete(name)
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
seen_names[name] = true
|
||||||
|
names[index] = name
|
||||||
|
}
|
||||||
|
}
|
||||||
_ = append_zig_libc_include_paths(&include_paths, &owned_include_paths, selected)
|
_ = append_zig_libc_include_paths(&include_paths, &owned_include_paths, selected)
|
||||||
import_header, _ := resolve_translate_c_header(header, include_paths[:])
|
|
||||||
defer delete(import_header)
|
|
||||||
c_options := cimport.Options{include_paths=include_paths[:], defines=defines[:]}
|
c_options := cimport.Options{include_paths=include_paths[:], defines=defines[:]}
|
||||||
result := cimport.import_header(c_options, import_header, selected)
|
resolved_headers: [dynamic]string
|
||||||
defer cimport.destroy_result(&result)
|
results: [dynamic]cimport.Result
|
||||||
if !result.available {
|
defer {
|
||||||
message := result.error_message if len(result.error_message) > 0 else "failed to import header"
|
for header in resolved_headers {
|
||||||
fmt.eprintln(message)
|
delete(header)
|
||||||
|
}
|
||||||
|
delete(resolved_headers)
|
||||||
|
for &result in results {
|
||||||
|
cimport.destroy_result(&result)
|
||||||
|
}
|
||||||
|
delete(results)
|
||||||
|
}
|
||||||
|
for header in headers {
|
||||||
|
resolved, _ := resolve_translate_c_header(header, include_paths[:])
|
||||||
|
append(&resolved_headers, resolved)
|
||||||
|
result := cimport.import_header(c_options, resolved, selected)
|
||||||
|
append(&results, result)
|
||||||
|
if !result.available {
|
||||||
|
message := result.error_message if len(result.error_message) > 0 else "failed to import header"
|
||||||
|
fmt.eprintln(message)
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !output_dir_set {
|
||||||
|
fmt.print(translatec.emit(&results[0], headers[0]))
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
inputs := make([]translatec.Package_Input, len(headers))
|
||||||
|
defer delete(inputs)
|
||||||
|
for header, index in headers {
|
||||||
|
inputs[index] = translatec.Package_Input{
|
||||||
|
result=&results[index],
|
||||||
|
header=header,
|
||||||
|
name=names[index],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
outputs, generation_error := translatec.emit_package(inputs[:])
|
||||||
|
defer translatec.destroy_package_outputs(outputs)
|
||||||
|
defer delete(generation_error)
|
||||||
|
if len(generation_error) > 0 {
|
||||||
|
fmt.eprintln(generation_error)
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
fmt.print(translatec.emit(&result, header))
|
if !ensure_directory(output_dir) {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
paths := make([]string, len(outputs))
|
||||||
|
defer {
|
||||||
|
for path in paths {
|
||||||
|
delete(path)
|
||||||
|
}
|
||||||
|
delete(paths)
|
||||||
|
}
|
||||||
|
for output, index in outputs {
|
||||||
|
path, join_error := filepath.join({output_dir, output.name})
|
||||||
|
if join_error != nil {
|
||||||
|
fmt.eprintfln("failed to form output path for '%s'", output.name)
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
paths[index] = path
|
||||||
|
}
|
||||||
|
for output, index in outputs {
|
||||||
|
if !os.write_entire_file(paths[index], transmute([]byte)output.source) {
|
||||||
|
fmt.eprintfln("failed to write generated bindings '%s'", paths[index])
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
}
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -501,6 +621,14 @@ main :: proc() {
|
|||||||
root := os2.args[2] if len(os2.args) == 3 else ""
|
root := os2.args[2] if len(os2.args) == 3 else ""
|
||||||
os2.exit(compiler.run_build(root))
|
os2.exit(compiler.run_build(root))
|
||||||
}
|
}
|
||||||
|
if len(os2.args) >= 2 && is_test_command(os2.args[1]) {
|
||||||
|
if len(os2.args) > 3 {
|
||||||
|
print_usage()
|
||||||
|
os2.exit(2)
|
||||||
|
}
|
||||||
|
root := os2.args[2] if len(os2.args) == 3 else ""
|
||||||
|
os2.exit(compiler.run_tests(root))
|
||||||
|
}
|
||||||
if len(os2.args) >= 2 && is_new_command(os2.args[1]) {
|
if len(os2.args) >= 2 && is_new_command(os2.args[1]) {
|
||||||
if len(os2.args) != 3 {
|
if len(os2.args) != 3 {
|
||||||
print_usage()
|
print_usage()
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
mem :: import "@std/mem"
|
import "@std/mem"
|
||||||
|
|
||||||
ArrayList func($T type) type {
|
ArrayList func($T type) type {
|
||||||
return struct {
|
return struct {
|
||||||
@@ -23,22 +23,22 @@ deinit func($T type, list @mut ArrayList(T)) void {
|
|||||||
list.capacity = 0
|
list.capacity = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
reserve func($T type, list @mut ArrayList(T), minimum_capacity usize) void ! mem.AllocError {
|
reserve func($T type, list @mut ArrayList(T), min_capacity usize) void ! mem.AllocError {
|
||||||
if minimum_capacity <= list.capacity {
|
if min_capacity <= list.capacity {
|
||||||
return _
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
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 = min_capacity
|
||||||
} else {
|
} else {
|
||||||
new_capacity = list.capacity + half
|
new_capacity = list.capacity + half
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if new_capacity < minimum_capacity {
|
if new_capacity < min_capacity {
|
||||||
new_capacity = minimum_capacity
|
new_capacity = min_capacity
|
||||||
}
|
}
|
||||||
|
|
||||||
length usize :: list.items.len
|
length usize :: list.items.len
|
||||||
@@ -48,18 +48,25 @@ reserve func($T type, list @mut ArrayList(T), minimum_capacity usize) void ! mem
|
|||||||
}
|
}
|
||||||
list.items = grown.ptr[..length]
|
list.items = grown.ptr[..length]
|
||||||
list.capacity = new_capacity
|
list.capacity = new_capacity
|
||||||
return _
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
list.items = list.items.ptr[..length + 1]
|
list.items = list.items.ptr[..length + 1]
|
||||||
list.items[length] = value
|
list.items[length] = value
|
||||||
return _
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
pop func($T type, list @mut ArrayList(T)) ?T {
|
||||||
|
if (list.items.len == 0) return null
|
||||||
|
value :: list.items[list.items.len - 1]
|
||||||
|
list.items = list.items.ptr[..list.items.len - 1]
|
||||||
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
clear func($T type, list @mut ArrayList(T)) void {
|
clear func($T type, list @mut ArrayList(T)) void {
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import "@std/mem"
|
||||||
|
import "@std/testing"
|
||||||
|
|
||||||
|
handles_append test {
|
||||||
|
list ArrayList(i32) := init(mem.c_allocator)
|
||||||
|
defer deinit(&list)
|
||||||
|
|
||||||
|
try append(&list, 42)
|
||||||
|
|
||||||
|
try testing.expect_equal(1, list.items.len)
|
||||||
|
try testing.expect_equal(42, list.items[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
handles_clear test {
|
||||||
|
list ArrayList(i32) := init(mem.c_allocator)
|
||||||
|
defer deinit(&list)
|
||||||
|
|
||||||
|
try append(&list, 42)
|
||||||
|
clear(&list)
|
||||||
|
|
||||||
|
try testing.expect_equal(0, list.items.len)
|
||||||
|
}
|
||||||
|
|
||||||
|
handles_reserve test {
|
||||||
|
list ArrayList(i32) := init(mem.c_allocator)
|
||||||
|
defer deinit(&list)
|
||||||
|
|
||||||
|
try reserve(&list, 10)
|
||||||
|
|
||||||
|
try testing.expect_equal(10, list.capacity)
|
||||||
|
try testing.expect_equal(0, list.items.len)
|
||||||
|
}
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
# Build configuration surface for `brolang build` (v0).
|
|
||||||
#
|
|
||||||
# A project's `build.bro` imports this module and declares a top-level constant
|
|
||||||
# named `config` of type `BuildConfig`. `brolang build [root]` type-checks
|
|
||||||
# build.bro, reads the config, and writes root/build/name.
|
|
||||||
#
|
|
||||||
# Declarative and literal-only: one executable per build. List fields take an
|
|
||||||
# address-of an array literal (`&["raylib"]`); empty lists are written `&[]`.
|
|
||||||
BuildConfig :: struct {
|
|
||||||
name []u8 # output executable name under root/build
|
|
||||||
source []u8 # program package directory, relative to build.bro
|
|
||||||
libraries [][]u8 # library names to link (-l)
|
|
||||||
lib_paths [][]u8 # library search directories (-L)
|
|
||||||
includes [][]u8 # C include directories (-I)
|
|
||||||
defines [][]u8 # C preprocessor defines (name or name=value)
|
|
||||||
links [][]u8 # extra linker inputs (object/source files, -framework pairs)
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# Build configuration surface for `brolang build` (v0).
|
||||||
|
#
|
||||||
|
# A project's `build.bro` or `build.hon` imports this module and declares a
|
||||||
|
# top-level constant named `config` of type `BuildConfig`. `brolang build
|
||||||
|
# [root]` accepts exactly one of those files, reads the config, and writes
|
||||||
|
# root/build/name.
|
||||||
|
#
|
||||||
|
# Declarative and literal-only: one executable per build. List fields take an
|
||||||
|
# address-of an array literal (`&["raylib"]`) and default to empty.
|
||||||
|
BuildConfig :: struct {
|
||||||
|
name []u8 # output executable name under root/build
|
||||||
|
source []u8 # program package directory, relative to the build file
|
||||||
|
libraries [][]u8 = &[] # library names to link (-l)
|
||||||
|
lib_paths [][]u8 = &[] # library search directories (-L)
|
||||||
|
includes [][]u8 = &[] # C include directories (-I)
|
||||||
|
defines [][]u8 = &[] # C preprocessor defines (name or name=value)
|
||||||
|
links [][]u8 = &[] # extra linker inputs (object/source files, -framework pairs)
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import "@ffi/c"
|
||||||
|
import "@std/io"
|
||||||
|
|
||||||
|
print func($format []u8, $Args type, args Args) void {
|
||||||
|
writer io.Writer :: io.Writer{
|
||||||
|
context = null,
|
||||||
|
handle = io.Handle{ file_desc = c_int(io.Stream.stderr) },
|
||||||
|
write = write,
|
||||||
|
}
|
||||||
|
io.print(writer, format, Args, args) catch |_| {}
|
||||||
|
}
|
||||||
|
|
||||||
|
@hide write func(_ ?@mut anyopaque, handle io.Handle, bytes []u8) usize ! io.WriteError {
|
||||||
|
request usize := bytes.len
|
||||||
|
maximum usize :: usize(maxval!(c_long))
|
||||||
|
if request > maximum {
|
||||||
|
request = maximum
|
||||||
|
}
|
||||||
|
while true {
|
||||||
|
count c_long :: c.write(handle.file_desc, bytes.ptr, c_ulong(request))
|
||||||
|
if count >= 0 {
|
||||||
|
return usize(count)
|
||||||
|
}
|
||||||
|
if c.__error()?^ != c.EINTR {
|
||||||
|
return .write_failed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
import "@std/meta"
|
||||||
|
|
||||||
|
EnumMap func($E, $V type) type {
|
||||||
|
match typeinfo!(E) {
|
||||||
|
.enum |info|: return struct {
|
||||||
|
present [info.fields.len]mut bool # fixme: replace with bitset
|
||||||
|
values [info.fields.len]mut V
|
||||||
|
}
|
||||||
|
else: compile_error!("EnumMap key must be an enum")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init func(
|
||||||
|
$E, $V type,
|
||||||
|
values meta.EnumFieldStruct(E, ?V, some!(null)),
|
||||||
|
) EnumMap(E, V) {
|
||||||
|
map EnumMap(E, V) := undefined
|
||||||
|
|
||||||
|
match typeinfo!(E) {
|
||||||
|
.enum |info|: inline for info.fields |field, i| {
|
||||||
|
map.present[i] = false
|
||||||
|
|
||||||
|
if field!(values, field.name) |value| {
|
||||||
|
map.present[i] = true
|
||||||
|
map.values[i] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else: compile_error!("EnumMap key must be an enum")
|
||||||
|
}
|
||||||
|
|
||||||
|
return map
|
||||||
|
}
|
||||||
|
|
||||||
|
get func($E, $V type, map @EnumMap(E, V), key E) ?V {
|
||||||
|
# fixme: linear lookup; implement an enum index/discriminant map for O(1) lookup
|
||||||
|
match typeinfo!(E) {
|
||||||
|
.enum |info|: inline for info.fields |field, i| {
|
||||||
|
if key == field!(E, field.name) {
|
||||||
|
if (map.present[i]) return map.values[i]
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else: compile_error!("EnumMap key must be an enum")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import "@std/mem"
|
||||||
|
import "@std/testing"
|
||||||
|
|
||||||
|
TestEnum :: enum(u8) {
|
||||||
|
ident = 3
|
||||||
|
int = 8
|
||||||
|
eof = 21
|
||||||
|
}
|
||||||
|
|
||||||
|
handles_sparse_enum_get test {
|
||||||
|
names EnumMap(TestEnum, []u8) := init({
|
||||||
|
ident = "identifier",
|
||||||
|
int = "integer",
|
||||||
|
})
|
||||||
|
|
||||||
|
ident :: get(&names, TestEnum.ident)
|
||||||
|
|
||||||
|
try testing.expect_type(?[]u8, ident)
|
||||||
|
try testing.expect(mem.eql("identifier", ident?))
|
||||||
|
|
||||||
|
eof :: get(&names, TestEnum.eof)
|
||||||
|
|
||||||
|
try testing.expect_type(?[]u8, eof)
|
||||||
|
try testing.expect_equal(null, eof)
|
||||||
|
}
|
||||||
@@ -0,0 +1,160 @@
|
|||||||
|
import "@std/mem"
|
||||||
|
|
||||||
|
PutError :: enum { key_exists }
|
||||||
|
|
||||||
|
Entry func($K, $V type) type {
|
||||||
|
return struct {
|
||||||
|
# hash = 0 means empty
|
||||||
|
hash usize = 0
|
||||||
|
key K
|
||||||
|
value V
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HashMap func(
|
||||||
|
$K, $V type,
|
||||||
|
$hash_key func(key K) usize,
|
||||||
|
$keys_eql func(a, b K) bool,
|
||||||
|
) type {
|
||||||
|
return struct {
|
||||||
|
entries []mut Entry(K, V)
|
||||||
|
count usize
|
||||||
|
allocator mem.Allocator
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StringHashMap func($V type) type {
|
||||||
|
return HashMap([]u8, V, str_hash, str_eql)
|
||||||
|
}
|
||||||
|
|
||||||
|
init func(
|
||||||
|
$K, $V type,
|
||||||
|
$hash_key func(key K) usize,
|
||||||
|
$keys_eql func(a, b K) bool,
|
||||||
|
allocator mem.Allocator,
|
||||||
|
) HashMap(K, V, hash_key, keys_eql) {
|
||||||
|
return HashMap(K, V, hash_key, keys_eql){
|
||||||
|
entries = mem.empty(Entry(K, V)),
|
||||||
|
count = 0,
|
||||||
|
allocator = allocator,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#! free the entries in the hash map.
|
||||||
|
#! note: this operation invalidates the map.
|
||||||
|
deinit func(
|
||||||
|
$K, $V type,
|
||||||
|
$hash_key func(key K) usize,
|
||||||
|
$keys_eql func(a, b K) bool,
|
||||||
|
map @HashMap(K, V, hash_key, keys_eql),
|
||||||
|
) void { mem.free(map.allocator, map.entries) }
|
||||||
|
|
||||||
|
get func(
|
||||||
|
$K, $V type,
|
||||||
|
$hash_key func(key K) usize,
|
||||||
|
$keys_eql func(a, b K) bool,
|
||||||
|
map @HashMap(K, V, hash_key, keys_eql),
|
||||||
|
key K,
|
||||||
|
) ?V {
|
||||||
|
if (map.count == 0) return null
|
||||||
|
|
||||||
|
hash :: normalize(hash_key(key))
|
||||||
|
idx usize := hash & (map.entries.len - 1)
|
||||||
|
|
||||||
|
while true {
|
||||||
|
entry :: map.entries[idx]
|
||||||
|
if (entry.hash == 0) return null
|
||||||
|
|
||||||
|
if (entry.hash == hash and keys_eql(entry.key, key)) {
|
||||||
|
return entry.value
|
||||||
|
}
|
||||||
|
|
||||||
|
idx = (idx + 1) & (map.entries.len - 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
put func(
|
||||||
|
$K, $V type,
|
||||||
|
$hash_key func(key K) usize,
|
||||||
|
$keys_eql func(a, b K) bool,
|
||||||
|
map @mut HashMap(K, V, hash_key, keys_eql),
|
||||||
|
key K,
|
||||||
|
value V,
|
||||||
|
) void ! (PutError | mem.AllocError) {
|
||||||
|
# check grow
|
||||||
|
threshold :: map.entries.len - divtrunc!(map.entries.len, 4)
|
||||||
|
if (map.entries.len == 0 or map.count + 1 > threshold) {
|
||||||
|
old_entries :: map.entries
|
||||||
|
new_size :: if (old_entries.len > 0) old_entries.len * 2 else 8
|
||||||
|
new_entries :: try mem.alloc(Entry(K, V), map.allocator, new_size)
|
||||||
|
|
||||||
|
# zero new entries
|
||||||
|
for 0..new_entries.len |i| {
|
||||||
|
new_entries[i].hash = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# move old entries
|
||||||
|
for old_entries |entry| {
|
||||||
|
if (entry.hash == 0) continue
|
||||||
|
|
||||||
|
# find an empty slot
|
||||||
|
idx usize := entry.hash & (new_entries.len - 1)
|
||||||
|
while new_entries[idx].hash != 0 {
|
||||||
|
idx = (idx + 1) & (new_entries.len - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
new_entries[idx] = entry
|
||||||
|
}
|
||||||
|
|
||||||
|
map.entries = new_entries
|
||||||
|
mem.free(map.allocator, old_entries)
|
||||||
|
}
|
||||||
|
|
||||||
|
# put new entry
|
||||||
|
hash :: normalize(hash_key(key))
|
||||||
|
idx usize := hash & (map.entries.len - 1)
|
||||||
|
|
||||||
|
while true {
|
||||||
|
entry :: map.entries[idx]
|
||||||
|
if entry.hash == 0 {
|
||||||
|
map.entries[idx] = Entry(K, V){
|
||||||
|
hash = hash,
|
||||||
|
key = key,
|
||||||
|
value = value,
|
||||||
|
}
|
||||||
|
map.count += 1
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry.hash == hash and keys_eql(entry.key, key)) {
|
||||||
|
return .key_exists
|
||||||
|
}
|
||||||
|
|
||||||
|
idx = (idx + 1) & (map.entries.len - 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@hide normalize func(hash usize) usize {
|
||||||
|
# mapping both 0 and 1 to 1 is safe because equality resolves
|
||||||
|
# collisions (since hash and key must both be equal).
|
||||||
|
if (hash == 0) return 1
|
||||||
|
return hash
|
||||||
|
}
|
||||||
|
|
||||||
|
#! FNV-1a hash implementation.
|
||||||
|
#! note: vulnerable to collision attacks.
|
||||||
|
@hide str_hash func(key []u8) usize {
|
||||||
|
hash u32 := 2166136261 # offset basis
|
||||||
|
prime u32 := 16777619
|
||||||
|
|
||||||
|
for key |byte| {
|
||||||
|
product u64 :: u64(hash xor u32(byte)) * prime
|
||||||
|
hash = u32(product & u64(maxval!(u32)))
|
||||||
|
}
|
||||||
|
|
||||||
|
return usize(hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
@hide str_eql func(a, b []u8) bool {
|
||||||
|
return mem.eql(a, b)
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import "@std/mem"
|
||||||
|
import "@std/testing"
|
||||||
|
|
||||||
|
handles_put_and_get test {
|
||||||
|
map StringHashMap(u32) := init(mem.c_allocator)
|
||||||
|
defer deinit(&map)
|
||||||
|
|
||||||
|
try put(&map, "key", 42)
|
||||||
|
value :: get(&map, "key")
|
||||||
|
|
||||||
|
try testing.expect_type(?u32, value)
|
||||||
|
try testing.expect_equal(42, value?)
|
||||||
|
}
|
||||||
+144
@@ -0,0 +1,144 @@
|
|||||||
|
import "@ffi/c"
|
||||||
|
|
||||||
|
File :: struct {
|
||||||
|
io Io
|
||||||
|
handle Handle
|
||||||
|
}
|
||||||
|
|
||||||
|
FileMode :: enum {
|
||||||
|
read_only
|
||||||
|
write_only
|
||||||
|
read_write
|
||||||
|
}
|
||||||
|
|
||||||
|
OpenError :: enum {
|
||||||
|
open_failed
|
||||||
|
}
|
||||||
|
|
||||||
|
CloseError :: enum {
|
||||||
|
close_failed
|
||||||
|
}
|
||||||
|
|
||||||
|
open func(io Io, path [;0]u8, mode FileMode) File ! OpenError {
|
||||||
|
handle Handle :: io.vtable.open(io.context, path, mode) catch |err| {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return File {io = io, handle = handle}
|
||||||
|
}
|
||||||
|
|
||||||
|
close func(file File) void ! CloseError {
|
||||||
|
try file.io.vtable.close(file.io.context, file.handle)
|
||||||
|
}
|
||||||
|
|
||||||
|
reader func(file File) Reader {
|
||||||
|
return Reader {
|
||||||
|
context = file.io.context,
|
||||||
|
handle = file.handle,
|
||||||
|
read = file.io.vtable.read,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
writer func(file File) Writer {
|
||||||
|
return Writer {
|
||||||
|
context = file.io.context,
|
||||||
|
handle = file.handle,
|
||||||
|
write = file.io.vtable.write,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@hide system_read func(_ ?@mut anyopaque, handle Handle, buffer []mut u8) usize ! ReadError {
|
||||||
|
request usize := buffer.len
|
||||||
|
maximum usize :: usize(maxval!(c_long))
|
||||||
|
if request > maximum {
|
||||||
|
request = maximum
|
||||||
|
}
|
||||||
|
while true {
|
||||||
|
count c_long :: c.read(handle.file_desc, buffer.ptr, c_ulong(request))
|
||||||
|
if count >= 0 {
|
||||||
|
return usize(count)
|
||||||
|
}
|
||||||
|
errno c_int :: c.__error()?^
|
||||||
|
if errno == c.EINTR {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if errno == c.EBADF {
|
||||||
|
return .not_open_for_reading
|
||||||
|
}
|
||||||
|
return .read_failed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@hide system_write func(_ ?@mut anyopaque, handle Handle, bytes []u8) usize ! WriteError {
|
||||||
|
request usize := bytes.len
|
||||||
|
maximum usize :: usize(maxval!(c_long))
|
||||||
|
if request > maximum {
|
||||||
|
request = maximum
|
||||||
|
}
|
||||||
|
while true {
|
||||||
|
count c_long :: c.write(handle.file_desc, bytes.ptr, c_ulong(request))
|
||||||
|
if count >= 0 {
|
||||||
|
return usize(count)
|
||||||
|
}
|
||||||
|
errno c_int :: c.__error()?^
|
||||||
|
if errno == c.EINTR {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if errno == c.EBADF {
|
||||||
|
return .not_open_for_writing
|
||||||
|
}
|
||||||
|
return .write_failed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@hide system_open func(_ ?@mut anyopaque, path [;0]u8, mode FileMode) Handle ! OpenError {
|
||||||
|
flags c_int := c.O_RDONLY
|
||||||
|
match mode {
|
||||||
|
.read_only: flags = c.O_RDONLY
|
||||||
|
.write_only: flags = c.O_WRONLY
|
||||||
|
.read_write: flags = c.O_RDWR
|
||||||
|
}
|
||||||
|
while true {
|
||||||
|
fd c_int :: c.open(ptrcast!(c_char, path.ptr), flags)
|
||||||
|
if fd >= 0 {
|
||||||
|
return Handle {file_desc = fd}
|
||||||
|
}
|
||||||
|
if c.__error()?^ != c.EINTR {
|
||||||
|
return .open_failed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@hide system_close func(_ ?@mut anyopaque, handle Handle) void ! CloseError {
|
||||||
|
if c.close(handle.file_desc) != 0 {
|
||||||
|
return .close_failed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@hide system_stdin func(_ ?@mut anyopaque) Handle {
|
||||||
|
return Handle {file_desc = c_int(Stream.stdin)}
|
||||||
|
}
|
||||||
|
|
||||||
|
@hide system_stdout func(_ ?@mut anyopaque) Handle {
|
||||||
|
return Handle {file_desc = c_int(Stream.stdout)}
|
||||||
|
}
|
||||||
|
|
||||||
|
@hide system_stderr func(_ ?@mut anyopaque) Handle {
|
||||||
|
return Handle {file_desc = c_int(Stream.stderr)}
|
||||||
|
}
|
||||||
|
|
||||||
|
@hide system_vtable IoVTable :: IoVTable {
|
||||||
|
read = system_read,
|
||||||
|
write = system_write,
|
||||||
|
open = system_open,
|
||||||
|
close = system_close,
|
||||||
|
stdin = system_stdin,
|
||||||
|
stdout = system_stdout,
|
||||||
|
stderr = system_stderr,
|
||||||
|
}
|
||||||
|
|
||||||
|
@hide system func() Io {
|
||||||
|
return Io {
|
||||||
|
context = null,
|
||||||
|
vtable = &system_vtable,
|
||||||
|
}
|
||||||
|
}
|
||||||
-122
@@ -1,122 +0,0 @@
|
|||||||
c :: import "@ffi/c"
|
|
||||||
|
|
||||||
ReadError :: enum {
|
|
||||||
read_failed
|
|
||||||
}
|
|
||||||
|
|
||||||
WriteError :: enum {
|
|
||||||
write_failed
|
|
||||||
no_progress
|
|
||||||
}
|
|
||||||
|
|
||||||
Io :: struct {
|
|
||||||
context ?*mut anyopaque
|
|
||||||
vtable @IoVTable
|
|
||||||
}
|
|
||||||
|
|
||||||
IoVTable :: struct {
|
|
||||||
read @func(context ?*mut anyopaque, stream ReadStream, buffer []mut u8) usize ! ReadError
|
|
||||||
write @func(context ?*mut anyopaque, stream WriteStream, bytes []u8) usize ! WriteError
|
|
||||||
}
|
|
||||||
|
|
||||||
ReadStream :: enum(c_int) {
|
|
||||||
stdin = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
WriteStream :: enum(c_int) {
|
|
||||||
stdout = 1
|
|
||||||
stderr = 2
|
|
||||||
}
|
|
||||||
|
|
||||||
Reader :: struct {
|
|
||||||
impl Io
|
|
||||||
stream ReadStream
|
|
||||||
}
|
|
||||||
|
|
||||||
Writer :: struct {
|
|
||||||
impl Io
|
|
||||||
stream WriteStream
|
|
||||||
}
|
|
||||||
|
|
||||||
read func(reader Reader, buffer []mut u8) usize ! ReadError {
|
|
||||||
if buffer.len == 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
count usize :: try reader.impl.vtable.read(reader.impl.context, reader.stream, buffer)
|
|
||||||
if count > buffer.len {
|
|
||||||
return .read_failed
|
|
||||||
}
|
|
||||||
return count
|
|
||||||
}
|
|
||||||
|
|
||||||
write func(writer Writer, bytes []u8) usize ! WriteError {
|
|
||||||
if bytes.len == 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
count usize :: try writer.impl.vtable.write(writer.impl.context, writer.stream, bytes)
|
|
||||||
if count > bytes.len {
|
|
||||||
return .write_failed
|
|
||||||
}
|
|
||||||
return count
|
|
||||||
}
|
|
||||||
|
|
||||||
write_all func(writer Writer, bytes []u8) void ! WriteError {
|
|
||||||
offset usize = 0
|
|
||||||
while offset < bytes.len {
|
|
||||||
count usize :: write(writer, bytes[offset..]) catch |err| {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if count == 0 {
|
|
||||||
return .no_progress
|
|
||||||
}
|
|
||||||
offset += count
|
|
||||||
}
|
|
||||||
return _
|
|
||||||
}
|
|
||||||
|
|
||||||
_system_read func(_ ?*mut anyopaque, stream ReadStream, buffer []mut u8) usize ! ReadError {
|
|
||||||
request usize = buffer.len
|
|
||||||
maximum usize :: usize(max_value(c_long))
|
|
||||||
if request > maximum {
|
|
||||||
request = maximum
|
|
||||||
}
|
|
||||||
while true {
|
|
||||||
count c_long :: c.read(c_int(stream), buffer.ptr, c_ulong(request))
|
|
||||||
if count >= 0 {
|
|
||||||
return usize(count)
|
|
||||||
}
|
|
||||||
if c.__error()^ != 4 {
|
|
||||||
return .read_failed
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_system_write func(_ ?*mut anyopaque, stream WriteStream, bytes []u8) usize ! WriteError {
|
|
||||||
fd c_int :: c_int(stream)
|
|
||||||
request usize = bytes.len
|
|
||||||
maximum usize :: usize(max_value(c_long))
|
|
||||||
if request > maximum {
|
|
||||||
request = maximum
|
|
||||||
}
|
|
||||||
while true {
|
|
||||||
count c_long :: c.write(fd, bytes.ptr, c_ulong(request))
|
|
||||||
if count >= 0 {
|
|
||||||
return usize(count)
|
|
||||||
}
|
|
||||||
if c.__error()^ != 4 {
|
|
||||||
return .write_failed
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_system_vtable IoVTable :: IoVTable {
|
|
||||||
read = _system_read,
|
|
||||||
write = _system_write,
|
|
||||||
}
|
|
||||||
|
|
||||||
_system func() Io {
|
|
||||||
return Io {
|
|
||||||
context = none,
|
|
||||||
vtable = &_system_vtable,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+447
@@ -0,0 +1,447 @@
|
|||||||
|
import "@ffi/c"
|
||||||
|
import "@std/meta"
|
||||||
|
|
||||||
|
ReadError :: enum {
|
||||||
|
not_open_for_reading
|
||||||
|
read_failed
|
||||||
|
}
|
||||||
|
|
||||||
|
WriteError :: enum {
|
||||||
|
not_open_for_writing
|
||||||
|
write_failed
|
||||||
|
no_progress
|
||||||
|
}
|
||||||
|
|
||||||
|
Handle :: union {
|
||||||
|
file_desc c_int
|
||||||
|
ptr @mut anyopaque
|
||||||
|
}
|
||||||
|
|
||||||
|
Stream :: enum(c_int) {
|
||||||
|
stdin = c.STDIN_FILENO
|
||||||
|
stdout = c.STDOUT_FILENO
|
||||||
|
stderr = c.STDERR_FILENO
|
||||||
|
}
|
||||||
|
|
||||||
|
Io :: struct {
|
||||||
|
context ?@mut anyopaque
|
||||||
|
vtable @IoVTable
|
||||||
|
}
|
||||||
|
|
||||||
|
IoVTable :: struct {
|
||||||
|
read @func(context ?@mut anyopaque, handle Handle, buffer []mut u8) usize ! ReadError
|
||||||
|
write @func(context ?@mut anyopaque, handle Handle, bytes []u8) usize ! WriteError
|
||||||
|
open @func(context ?@mut anyopaque, path [;0]u8, mode FileMode) Handle ! OpenError
|
||||||
|
close @func(context ?@mut anyopaque, handle Handle) void ! CloseError
|
||||||
|
stdin @func(context ?@mut anyopaque) Handle
|
||||||
|
stdout @func(context ?@mut anyopaque) Handle
|
||||||
|
stderr @func(context ?@mut anyopaque) Handle
|
||||||
|
}
|
||||||
|
|
||||||
|
Reader :: struct {
|
||||||
|
context ?@mut anyopaque
|
||||||
|
handle Handle
|
||||||
|
read @func(context ?@mut anyopaque, handle Handle, buffer []mut u8) usize ! ReadError
|
||||||
|
}
|
||||||
|
|
||||||
|
Writer :: struct {
|
||||||
|
context ?@mut anyopaque
|
||||||
|
handle Handle
|
||||||
|
write @func(context ?@mut anyopaque, handle Handle, bytes []u8) usize ! WriteError
|
||||||
|
}
|
||||||
|
|
||||||
|
read func(input Reader, buffer []mut u8) usize ! ReadError {
|
||||||
|
if buffer.len == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
count usize :: try input.read(input.context, input.handle, buffer)
|
||||||
|
if count > buffer.len {
|
||||||
|
return .read_failed
|
||||||
|
}
|
||||||
|
return count
|
||||||
|
}
|
||||||
|
|
||||||
|
write func(output Writer, bytes []u8) usize ! WriteError {
|
||||||
|
if bytes.len == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
count usize :: try output.write(output.context, output.handle, bytes)
|
||||||
|
if count > bytes.len {
|
||||||
|
return .write_failed
|
||||||
|
}
|
||||||
|
return count
|
||||||
|
}
|
||||||
|
|
||||||
|
write_all func(output Writer, bytes []u8) void ! WriteError {
|
||||||
|
offset usize := 0
|
||||||
|
while offset < bytes.len {
|
||||||
|
count usize :: write(output, bytes[offset..]) catch |err| {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if count == 0 {
|
||||||
|
return .no_progress
|
||||||
|
}
|
||||||
|
offset += count
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
stdin func(io Io) Reader {
|
||||||
|
return Reader {
|
||||||
|
context = io.context,
|
||||||
|
handle = io.vtable.stdin(io.context),
|
||||||
|
read = io.vtable.read,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stdout func(io Io) Writer {
|
||||||
|
return Writer {
|
||||||
|
context = io.context,
|
||||||
|
handle = io.vtable.stdout(io.context),
|
||||||
|
write = io.vtable.write,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stderr func(io Io) Writer {
|
||||||
|
return Writer {
|
||||||
|
context = io.context,
|
||||||
|
handle = io.vtable.stderr(io.context),
|
||||||
|
write = io.vtable.write,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
print func(output Writer, $format []u8, $Args type, args Args) void ! WriteError {
|
||||||
|
inline for parse_format(format.len, format, Args) |token| {
|
||||||
|
match token.kind {
|
||||||
|
.unused: break
|
||||||
|
.literal: try write_all(output, format[token.start..token.end])
|
||||||
|
.string: try write_all(output, field!(args, token.field))
|
||||||
|
.default: try write_default(output, field!(args, token.field))
|
||||||
|
.decimal: try write_decimal(output, field!(args, token.field))
|
||||||
|
.binary: try write_integer(output, field!(args, token.field), 2, false)
|
||||||
|
.octal: try write_integer(output, field!(args, token.field), 8, false)
|
||||||
|
.hex_lower: try write_integer(output, field!(args, token.field), 16, false)
|
||||||
|
.hex_upper: try write_integer(output, field!(args, token.field), 16, true)
|
||||||
|
.character: try write_character(output, field!(args, token.field))
|
||||||
|
else: try write_float(output, field!(args, token.field), true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@hide write_integer_signed func(output Writer, value i64, base u64, uppercase bool) void ! WriteError {
|
||||||
|
buffer [65]mut u8 := undefined
|
||||||
|
end usize := buffer.len
|
||||||
|
current i64 := value
|
||||||
|
while true {
|
||||||
|
digit_value i64 :: rem!(current, i64(base))
|
||||||
|
digit u8 := 0
|
||||||
|
if digit_value < 0 {
|
||||||
|
digit = u8(-digit_value)
|
||||||
|
} else {
|
||||||
|
digit = u8(digit_value)
|
||||||
|
}
|
||||||
|
end -= 1
|
||||||
|
if digit < 10 {
|
||||||
|
buffer[end] = '0' + digit
|
||||||
|
} else if uppercase {
|
||||||
|
buffer[end] = 'A' + digit - 10
|
||||||
|
} else {
|
||||||
|
buffer[end] = 'a' + digit - 10
|
||||||
|
}
|
||||||
|
current = divtrunc!(current, i64(base))
|
||||||
|
if current == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if value < 0 {
|
||||||
|
end -= 1
|
||||||
|
buffer[end] = '-'
|
||||||
|
}
|
||||||
|
try write_all(output, buffer[end..])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
@hide write_integer_unsigned func(output Writer, value u64, base u64, uppercase bool) void ! WriteError {
|
||||||
|
buffer [65]mut u8 := undefined
|
||||||
|
end usize := buffer.len
|
||||||
|
current u64 := value
|
||||||
|
while true {
|
||||||
|
digit u8 :: u8(rem!(current, base))
|
||||||
|
end -= 1
|
||||||
|
if digit < 10 {
|
||||||
|
buffer[end] = '0' + digit
|
||||||
|
} else if uppercase {
|
||||||
|
buffer[end] = 'A' + digit - 10
|
||||||
|
} else {
|
||||||
|
buffer[end] = 'a' + digit - 10
|
||||||
|
}
|
||||||
|
current = divtrunc!(current, base)
|
||||||
|
if current == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try write_all(output, buffer[end..])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
@hide FormatTokenKind :: enum {
|
||||||
|
unused
|
||||||
|
literal
|
||||||
|
default
|
||||||
|
string
|
||||||
|
decimal
|
||||||
|
binary
|
||||||
|
octal
|
||||||
|
hex_lower
|
||||||
|
hex_upper
|
||||||
|
character
|
||||||
|
scientific
|
||||||
|
}
|
||||||
|
|
||||||
|
@hide FormatToken :: struct {
|
||||||
|
kind FormatTokenKind
|
||||||
|
start usize
|
||||||
|
end usize
|
||||||
|
field []u8
|
||||||
|
}
|
||||||
|
|
||||||
|
@hide parse_format func($N usize, $format []u8, $Args type) [N]mut FormatToken {
|
||||||
|
tokens [N]mut FormatToken := undefined
|
||||||
|
for (usize(0))..format.len |index| {
|
||||||
|
tokens[index] = FormatToken {kind = .unused, start = 0, end = 0, field = ""}
|
||||||
|
}
|
||||||
|
field_count usize := 0
|
||||||
|
match typeinfo!(Args) {
|
||||||
|
.record |record|: {
|
||||||
|
if !record.is_tuple {
|
||||||
|
compile_error!("io.print arguments must be a tuple")
|
||||||
|
}
|
||||||
|
field_count = record.fields.len
|
||||||
|
}
|
||||||
|
else: compile_error!("io.print arguments must be a tuple")
|
||||||
|
}
|
||||||
|
|
||||||
|
token_count usize := 0
|
||||||
|
argument_count usize := 0
|
||||||
|
literal_start usize := 0
|
||||||
|
cursor usize := 0
|
||||||
|
while cursor < format.len {
|
||||||
|
byte :: format[cursor]
|
||||||
|
if byte == '{' {
|
||||||
|
if cursor + 1 >= format.len {
|
||||||
|
compile_error!("io.print format has an unmatched '{'")
|
||||||
|
}
|
||||||
|
if cursor > literal_start {
|
||||||
|
tokens[token_count] = FormatToken {kind = .literal, start = literal_start, end = cursor, field = ""}
|
||||||
|
token_count += 1
|
||||||
|
}
|
||||||
|
next :: format[cursor + 1]
|
||||||
|
if next == '{' {
|
||||||
|
tokens[token_count] = FormatToken {kind = .literal, start = cursor, end = cursor + 1, field = ""}
|
||||||
|
token_count += 1
|
||||||
|
cursor += 2
|
||||||
|
literal_start = cursor
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
kind FormatTokenKind := .default
|
||||||
|
width usize := 2
|
||||||
|
if next != '}' {
|
||||||
|
if cursor + 2 >= format.len or format[cursor + 2] != '}' {
|
||||||
|
compile_error!("io.print format expects a one-character specifier")
|
||||||
|
}
|
||||||
|
width = 3
|
||||||
|
if next == 's' {
|
||||||
|
kind = .string
|
||||||
|
} else if next == 'd' {
|
||||||
|
kind = .decimal
|
||||||
|
} else if next == 'b' {
|
||||||
|
kind = .binary
|
||||||
|
} else if next == 'o' {
|
||||||
|
kind = .octal
|
||||||
|
} else if next == 'x' {
|
||||||
|
kind = .hex_lower
|
||||||
|
} else if next == 'X' {
|
||||||
|
kind = .hex_upper
|
||||||
|
} else if next == 'c' {
|
||||||
|
kind = .character
|
||||||
|
} else if next == 'e' {
|
||||||
|
kind = .scientific
|
||||||
|
} else {
|
||||||
|
compile_error!("io.print format has an unknown specifier")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if argument_count >= field_count {
|
||||||
|
compile_error!("io.print format argument count does not match the tuple")
|
||||||
|
}
|
||||||
|
tokens[token_count] = FormatToken {
|
||||||
|
kind = kind,
|
||||||
|
start = 0,
|
||||||
|
end = 0,
|
||||||
|
field = format_field_name(Args, argument_count),
|
||||||
|
}
|
||||||
|
token_count += 1
|
||||||
|
argument_count += 1
|
||||||
|
cursor += width
|
||||||
|
literal_start = cursor
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if byte == '}' {
|
||||||
|
if cursor + 1 >= format.len or format[cursor + 1] != '}' {
|
||||||
|
compile_error!("io.print format has an unmatched '}'")
|
||||||
|
}
|
||||||
|
if cursor > literal_start {
|
||||||
|
tokens[token_count] = FormatToken {kind = .literal, start = literal_start, end = cursor, field = ""}
|
||||||
|
token_count += 1
|
||||||
|
}
|
||||||
|
tokens[token_count] = FormatToken {kind = .literal, start = cursor, end = cursor + 1, field = ""}
|
||||||
|
token_count += 1
|
||||||
|
cursor += 2
|
||||||
|
literal_start = cursor
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
cursor += 1
|
||||||
|
}
|
||||||
|
if literal_start < format.len {
|
||||||
|
tokens[token_count] = FormatToken {kind = .literal, start = literal_start, end = format.len, field = ""}
|
||||||
|
}
|
||||||
|
if argument_count != field_count {
|
||||||
|
compile_error!("io.print format argument count does not match the tuple")
|
||||||
|
}
|
||||||
|
return tokens
|
||||||
|
}
|
||||||
|
|
||||||
|
@hide format_field_name func($T type, index usize) []u8 {
|
||||||
|
match typeinfo!(T) {
|
||||||
|
.record |record|: return record.fields[index].name
|
||||||
|
else: compile_error!("io.print arguments must be a tuple")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@hide distinct_value func($Backing, $Distinct type, value Distinct) Backing {
|
||||||
|
return ptrcast!(Backing, &value)^
|
||||||
|
}
|
||||||
|
|
||||||
|
@hide scalar_or_distinct_type func($T type) bool {
|
||||||
|
match typeinfo!(T) {
|
||||||
|
.bool: return true
|
||||||
|
.integer: return true
|
||||||
|
.float: return true
|
||||||
|
.distinct: return true
|
||||||
|
else: return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@hide write_integer func(output Writer, $T type, value T, base u64, uppercase bool) void ! WriteError {
|
||||||
|
match typeinfo!(T) {
|
||||||
|
.integer: if minval!(T) < 0 {
|
||||||
|
try write_integer_signed(output, i64(value), base, uppercase)
|
||||||
|
} else {
|
||||||
|
try write_integer_unsigned(output, u64(value), base, uppercase)
|
||||||
|
}
|
||||||
|
.distinct |backing|: if scalar_or_distinct_type(backing) {
|
||||||
|
try write_integer(output, distinct_value(backing, T, value), base, uppercase)
|
||||||
|
} else {
|
||||||
|
compile_error!("io.print integer format requires an integer argument")
|
||||||
|
}
|
||||||
|
else: compile_error!("io.print integer format requires an integer argument")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
# note: libc keeps float formatting small; replace it with a native shortest-roundtrip writer if locale independence matters.
|
||||||
|
@hide write_float func(output Writer, $T type, value T, scientific bool) void ! WriteError {
|
||||||
|
match typeinfo!(T) {
|
||||||
|
.float: {
|
||||||
|
buffer [64]mut u8 := undefined
|
||||||
|
count c_int := 0
|
||||||
|
if sizeof!(T) == 4 {
|
||||||
|
if scientific {
|
||||||
|
count = c.snprintf(ptrcast!(c_char, (&buffer).ptr), c_ulong(buffer.len), "%.8e", value)
|
||||||
|
} else {
|
||||||
|
count = c.snprintf(ptrcast!(c_char, (&buffer).ptr), c_ulong(buffer.len), "%.9g", value)
|
||||||
|
}
|
||||||
|
} else if scientific {
|
||||||
|
count = c.snprintf(ptrcast!(c_char, (&buffer).ptr), c_ulong(buffer.len), "%.16e", value)
|
||||||
|
} else {
|
||||||
|
count = c.snprintf(ptrcast!(c_char, (&buffer).ptr), c_ulong(buffer.len), "%.17g", value)
|
||||||
|
}
|
||||||
|
if count < 0 or usize(count) >= buffer.len {
|
||||||
|
return .write_failed
|
||||||
|
}
|
||||||
|
try write_all(output, buffer[0..usize(count)])
|
||||||
|
}
|
||||||
|
.distinct |backing|: if scalar_or_distinct_type(backing) {
|
||||||
|
try write_float(output, distinct_value(backing, T, value), scientific)
|
||||||
|
} else {
|
||||||
|
compile_error!("io.print float format requires a float argument")
|
||||||
|
}
|
||||||
|
else: compile_error!("io.print float format requires a float argument")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
@hide write_decimal func(output Writer, $T type, value T) void ! WriteError {
|
||||||
|
match typeinfo!(T) {
|
||||||
|
.integer: try write_integer(output, value, 10, false)
|
||||||
|
.float: try write_float(output, value, false)
|
||||||
|
.distinct |backing|: if scalar_or_distinct_type(backing) {
|
||||||
|
try write_decimal(output, distinct_value(backing, T, value))
|
||||||
|
} else {
|
||||||
|
compile_error!("io.print '{d}' requires an integer or float argument")
|
||||||
|
}
|
||||||
|
else: compile_error!("io.print '{d}' requires an integer or float argument")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
@hide write_character func(output Writer, $T type, value T) void ! WriteError {
|
||||||
|
match typeinfo!(T) {
|
||||||
|
.integer: {
|
||||||
|
if minval!(T) < 0 or maxval!(T) > 255 {
|
||||||
|
compile_error!("io.print '{c}' requires an unsigned integer that fits in u8")
|
||||||
|
}
|
||||||
|
buffer [1]u8 := [u8(value)]
|
||||||
|
try write_all(output, buffer[..])
|
||||||
|
}
|
||||||
|
.distinct |backing|: if scalar_or_distinct_type(backing) {
|
||||||
|
try write_character(output, distinct_value(backing, T, value))
|
||||||
|
} else {
|
||||||
|
compile_error!("io.print '{c}' requires an unsigned integer that fits in u8")
|
||||||
|
}
|
||||||
|
else: compile_error!("io.print '{c}' requires an unsigned integer that fits in u8")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
@hide write_default func(output Writer, $T type, value T) void ! WriteError {
|
||||||
|
match typeinfo!(T) {
|
||||||
|
.bool: if value {
|
||||||
|
try write_all(output, "true")
|
||||||
|
} else {
|
||||||
|
try write_all(output, "false")
|
||||||
|
}
|
||||||
|
.integer: try write_integer(output, value, 10, false)
|
||||||
|
.float: try write_float(output, value, false)
|
||||||
|
.array: try write_all(output, value)
|
||||||
|
.pointer: try write_all(output, value)
|
||||||
|
.slice: try write_all(output, value)
|
||||||
|
.enum |enum_info|: {
|
||||||
|
inline for enum_info.fields |field| {
|
||||||
|
if value == field!(T, field.name) {
|
||||||
|
try write_all(output, ".")
|
||||||
|
try write_all(output, field.name)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return .write_failed
|
||||||
|
}
|
||||||
|
.distinct |backing|: if scalar_or_distinct_type(backing) {
|
||||||
|
try write_default(output, distinct_value(backing, T, value))
|
||||||
|
} else {
|
||||||
|
compile_error!("io.print '{}' does not support this argument type")
|
||||||
|
}
|
||||||
|
else: compile_error!("io.print '{}' does not support this argument type")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
-202
@@ -1,202 +0,0 @@
|
|||||||
c :: import "@ffi/c"
|
|
||||||
|
|
||||||
AllocError :: enum {
|
|
||||||
out_of_memory
|
|
||||||
}
|
|
||||||
|
|
||||||
Allocator :: struct {
|
|
||||||
context ?*mut anyopaque
|
|
||||||
vtable @AllocatorVTable
|
|
||||||
}
|
|
||||||
|
|
||||||
AllocatorVTable :: struct {
|
|
||||||
alloc @func(context ?*mut anyopaque, size usize, alignment usize) ?*mut u8
|
|
||||||
realloc @func(context ?*mut anyopaque, memory ?*mut u8, old_size usize, new_size usize, alignment usize) ?*mut u8
|
|
||||||
free @func(context ?*mut anyopaque, memory ?*mut u8, size usize, alignment usize) void
|
|
||||||
}
|
|
||||||
|
|
||||||
raw_alloc func(allocator Allocator, size usize, alignment usize) ?*mut u8 {
|
|
||||||
return allocator.vtable.alloc(allocator.context, size, alignment)
|
|
||||||
}
|
|
||||||
|
|
||||||
raw_realloc func(allocator Allocator, memory ?*mut u8, old_size usize, new_size usize, alignment usize) ?*mut u8 {
|
|
||||||
return allocator.vtable.realloc(allocator.context, memory, old_size, new_size, alignment)
|
|
||||||
}
|
|
||||||
|
|
||||||
raw_free func(allocator Allocator, memory ?*mut u8, size usize, alignment usize) void {
|
|
||||||
allocator.vtable.free(allocator.context, memory, size, alignment)
|
|
||||||
}
|
|
||||||
|
|
||||||
eql func($T type, left, right []T) bool {
|
|
||||||
if left.len != right.len {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
i usize = 0
|
|
||||||
while i < left.len : i += 1 {
|
|
||||||
if left[i] != right[i] {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
_empty_storage [1]mut u64 = [0]
|
|
||||||
|
|
||||||
_empty_slice func($T type, count usize) []mut T {
|
|
||||||
pointer *mut T :: ptr_cast(T, (&_empty_storage).ptr)
|
|
||||||
return pointer[..count]
|
|
||||||
}
|
|
||||||
|
|
||||||
empty func($T type) []mut T {
|
|
||||||
return _empty_slice(T, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
alloc func($T type, allocator Allocator, count usize) []mut T ! AllocError {
|
|
||||||
if count == 0 {
|
|
||||||
return _empty_slice(T, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
element_size usize :: size_of(T)
|
|
||||||
if element_size == 0 {
|
|
||||||
return _empty_slice(T, count)
|
|
||||||
}
|
|
||||||
if count > div_trunc(max_value(usize), element_size) {
|
|
||||||
return .out_of_memory
|
|
||||||
}
|
|
||||||
|
|
||||||
memory ?*mut u8 = raw_alloc(allocator, count * element_size, align_of(T))
|
|
||||||
if memory |bytes| {
|
|
||||||
pointer *mut T :: ptr_cast(T, bytes)
|
|
||||||
return pointer[..count]
|
|
||||||
}
|
|
||||||
return .out_of_memory
|
|
||||||
}
|
|
||||||
|
|
||||||
realloc func($T type, allocator Allocator, memory []mut T, new_count usize) []mut T ! AllocError {
|
|
||||||
if new_count == memory.len {
|
|
||||||
return memory
|
|
||||||
}
|
|
||||||
if new_count == 0 {
|
|
||||||
free(allocator, memory)
|
|
||||||
return _empty_slice(T, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
element_size usize :: size_of(T)
|
|
||||||
if element_size == 0 {
|
|
||||||
return _empty_slice(T, new_count)
|
|
||||||
}
|
|
||||||
if new_count > div_trunc(max_value(usize), element_size) {
|
|
||||||
return .out_of_memory
|
|
||||||
}
|
|
||||||
|
|
||||||
old_memory ?*mut u8 = none
|
|
||||||
old_size usize = 0
|
|
||||||
if memory.len != 0 {
|
|
||||||
old_memory = ptr_cast(u8, memory.ptr)
|
|
||||||
old_size = memory.len * element_size
|
|
||||||
}
|
|
||||||
resized ?*mut u8 = raw_realloc(
|
|
||||||
allocator,
|
|
||||||
old_memory,
|
|
||||||
old_size,
|
|
||||||
new_count * element_size,
|
|
||||||
align_of(T),
|
|
||||||
)
|
|
||||||
if resized |bytes| {
|
|
||||||
pointer *mut T :: ptr_cast(T, bytes)
|
|
||||||
return pointer[..new_count]
|
|
||||||
}
|
|
||||||
return .out_of_memory
|
|
||||||
}
|
|
||||||
|
|
||||||
free func($T type, allocator Allocator, memory []mut T) void {
|
|
||||||
if memory.len != 0 and size_of(T) != 0 {
|
|
||||||
raw_free(allocator, ptr_cast(u8, memory.ptr), memory.len * size_of(T), align_of(T))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_malloc_alignment usize :: 16 # ponytail: aarch64-macos libc malloc alignment assumption.
|
|
||||||
|
|
||||||
_power_of_two func(value usize) bool {
|
|
||||||
if value == 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
current usize = value
|
|
||||||
while current > 1 {
|
|
||||||
half usize = div_trunc(current, 2)
|
|
||||||
if half * 2 != current {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
current = half
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
_c_alloc func(_ ?*mut anyopaque, size usize, alignment usize) ?*mut u8 {
|
|
||||||
if _power_of_two(alignment) == false {
|
|
||||||
return none
|
|
||||||
}
|
|
||||||
|
|
||||||
if alignment <= _malloc_alignment {
|
|
||||||
return ptr_cast(u8, c.malloc(c_ulong(size)))
|
|
||||||
}
|
|
||||||
|
|
||||||
memory [1]mut ?*mut anyopaque = [none]
|
|
||||||
status c_int = c.posix_memalign((&memory).ptr, c_ulong(alignment), c_ulong(size))
|
|
||||||
if status != 0 {
|
|
||||||
return none
|
|
||||||
}
|
|
||||||
|
|
||||||
return ptr_cast(u8, memory[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
_c_realloc func(_ ?*mut anyopaque, memory ?*mut u8, old_size usize, new_size usize, alignment usize) ?*mut u8 {
|
|
||||||
if _power_of_two(alignment) == false {
|
|
||||||
return none
|
|
||||||
}
|
|
||||||
|
|
||||||
if new_size == 0 {
|
|
||||||
c.free(memory)
|
|
||||||
return none
|
|
||||||
}
|
|
||||||
|
|
||||||
if memory |old_memory| {
|
|
||||||
if alignment <= _malloc_alignment {
|
|
||||||
return ptr_cast(u8, c.realloc(old_memory, c_ulong(new_size)))
|
|
||||||
}
|
|
||||||
|
|
||||||
new_memory ?*mut u8 = _c_alloc(none, new_size, alignment)
|
|
||||||
if new_memory |new_bytes| {
|
|
||||||
copy_size usize = old_size
|
|
||||||
if new_size < copy_size {
|
|
||||||
copy_size = new_size
|
|
||||||
}
|
|
||||||
i usize = 0
|
|
||||||
while i < copy_size : i += 1 {
|
|
||||||
new_bytes[i] = old_memory[i]
|
|
||||||
}
|
|
||||||
c.free(old_memory)
|
|
||||||
}
|
|
||||||
return new_memory
|
|
||||||
}
|
|
||||||
|
|
||||||
return _c_alloc(none, new_size, alignment)
|
|
||||||
}
|
|
||||||
|
|
||||||
_c_free func(_ ?*mut anyopaque, memory ?*mut u8, _ usize, _ usize) void {
|
|
||||||
c.free(memory)
|
|
||||||
}
|
|
||||||
|
|
||||||
_c_vtable AllocatorVTable :: AllocatorVTable {
|
|
||||||
alloc = _c_alloc,
|
|
||||||
realloc = _c_realloc,
|
|
||||||
free = _c_free,
|
|
||||||
}
|
|
||||||
|
|
||||||
c_allocator Allocator :: Allocator {
|
|
||||||
context = none,
|
|
||||||
vtable = &_c_vtable,
|
|
||||||
}
|
|
||||||
+190
@@ -0,0 +1,190 @@
|
|||||||
|
import "@ffi/c"
|
||||||
|
|
||||||
|
AllocError :: enum {
|
||||||
|
out_of_memory
|
||||||
|
}
|
||||||
|
|
||||||
|
Allocator :: struct {
|
||||||
|
context ?@mut anyopaque
|
||||||
|
vtable @AllocatorVTable
|
||||||
|
}
|
||||||
|
|
||||||
|
AllocatorVTable :: struct {
|
||||||
|
alloc @func(context ?@mut anyopaque, size usize, alignment usize) ?*mut u8
|
||||||
|
realloc @func(context ?@mut anyopaque, memory ?*mut u8, old_size usize, new_size usize, alignment usize) ?*mut u8
|
||||||
|
free @func(context ?@mut anyopaque, memory ?*mut u8, size usize, alignment usize) void
|
||||||
|
}
|
||||||
|
|
||||||
|
raw_alloc func(allocator Allocator, size usize, alignment usize) ?*mut u8 {
|
||||||
|
return allocator.vtable.alloc(allocator.context, size, alignment)
|
||||||
|
}
|
||||||
|
|
||||||
|
raw_realloc func(allocator Allocator, memory ?*mut u8, old_size usize, new_size usize, alignment usize) ?*mut u8 {
|
||||||
|
return allocator.vtable.realloc(allocator.context, memory, old_size, new_size, alignment)
|
||||||
|
}
|
||||||
|
|
||||||
|
raw_free func(allocator Allocator, memory ?*mut u8, size usize, alignment usize) void {
|
||||||
|
allocator.vtable.free(allocator.context, memory, size, alignment)
|
||||||
|
}
|
||||||
|
|
||||||
|
eql func($T type, left, right []T) bool {
|
||||||
|
if (left.len != right.len) return false
|
||||||
|
for (0..left.len) |i| if (left[i] != right[i]) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
#! allocate memory for a slice of type `T` with `count` elements.
|
||||||
|
alloc func($T type, allocator Allocator, count usize) []mut T ! AllocError {
|
||||||
|
if (count == 0) return empty_slice(T, 0)
|
||||||
|
|
||||||
|
element_size usize :: sizeof!(T)
|
||||||
|
if (element_size == 0) return empty_slice(T, count)
|
||||||
|
|
||||||
|
if count > divtrunc!(maxval!(usize), element_size) {
|
||||||
|
return .out_of_memory
|
||||||
|
}
|
||||||
|
|
||||||
|
memory ?*mut u8 := raw_alloc(allocator, count * element_size, alignof!(T))
|
||||||
|
if memory |bytes| {
|
||||||
|
pointer *mut T :: ptrcast!(T, bytes)
|
||||||
|
return pointer[..count]
|
||||||
|
}
|
||||||
|
return .out_of_memory
|
||||||
|
}
|
||||||
|
|
||||||
|
#! reallocate memory for a slice of type `T` with `new_count` elements.
|
||||||
|
#! reallocating with `new_count == 0` will free the memory and return an empty slice.
|
||||||
|
#! note: memory must be reallocated with the same allocator that was used to allocate it.
|
||||||
|
realloc func($T type, allocator Allocator, memory []mut T, new_count usize) []mut T ! AllocError {
|
||||||
|
if new_count == memory.len {
|
||||||
|
return memory
|
||||||
|
}
|
||||||
|
|
||||||
|
if new_count == 0 {
|
||||||
|
free(allocator, memory)
|
||||||
|
return empty_slice(T, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
element_size usize :: sizeof!(T)
|
||||||
|
if element_size == 0 {
|
||||||
|
return empty_slice(T, new_count)
|
||||||
|
}
|
||||||
|
if new_count > divtrunc!(maxval!(usize), element_size) {
|
||||||
|
return .out_of_memory
|
||||||
|
}
|
||||||
|
|
||||||
|
old_memory ?*mut u8 := null
|
||||||
|
old_size usize := 0
|
||||||
|
if memory.len != 0 {
|
||||||
|
old_memory = ptrcast!(u8, memory.ptr)
|
||||||
|
old_size = memory.len * element_size
|
||||||
|
}
|
||||||
|
resized ?*mut u8 := raw_realloc(
|
||||||
|
allocator,
|
||||||
|
old_memory,
|
||||||
|
old_size,
|
||||||
|
new_count * element_size,
|
||||||
|
alignof!(T),
|
||||||
|
)
|
||||||
|
if resized |bytes| {
|
||||||
|
pointer *mut T :: ptrcast!(T, bytes)
|
||||||
|
return pointer[..new_count]
|
||||||
|
}
|
||||||
|
|
||||||
|
return .out_of_memory
|
||||||
|
}
|
||||||
|
|
||||||
|
#! free memory allocated for a slice of type `T`.
|
||||||
|
#! note: memory must be freed with the same allocator that was used to allocate it.
|
||||||
|
free func($T type, allocator Allocator, memory []T) void {
|
||||||
|
if (memory.len == 0 or sizeof!(T) == 0) return
|
||||||
|
raw_free(allocator, ptrcast!(
|
||||||
|
u8,
|
||||||
|
constcast!(memory).ptr),
|
||||||
|
memory.len * sizeof!(T),
|
||||||
|
alignof!(T),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#! get an empty slice of type `T` with `count` elements.
|
||||||
|
empty_slice func($T type, count usize) []mut T {
|
||||||
|
pointer *mut T :: ptrcast!(T, (&empty_storage).ptr)
|
||||||
|
return pointer[..count]
|
||||||
|
}
|
||||||
|
|
||||||
|
#! get an empty slice of type `T` with 0 elements.
|
||||||
|
empty func($T type) []mut T {
|
||||||
|
return empty_slice(T, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
@hide empty_storage [1]mut u64 := [0]
|
||||||
|
|
||||||
|
@hide malloc_alignment usize :: 16 # ponytail: aarch64-macos libc malloc alignment assumption.
|
||||||
|
|
||||||
|
@hide power_of_two func(value usize) bool {
|
||||||
|
if (value == 0) return false
|
||||||
|
current usize := value
|
||||||
|
while current > 1 {
|
||||||
|
half usize := divtrunc!(current, 2)
|
||||||
|
if (half * 2 != current) return false
|
||||||
|
current = half
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
@hide c_alloc func(_ ?@mut anyopaque, size usize, alignment usize) ?*mut u8 {
|
||||||
|
if (power_of_two(alignment) == false) return null
|
||||||
|
|
||||||
|
if alignment <= malloc_alignment {
|
||||||
|
return ptrcast!(u8, c.malloc(c_ulong(size)))
|
||||||
|
}
|
||||||
|
|
||||||
|
memory [1]mut ?*mut anyopaque := [null]
|
||||||
|
status c_int := c.posix_memalign((&memory).ptr, c_ulong(alignment), c_ulong(size))
|
||||||
|
if (status != 0) return null
|
||||||
|
|
||||||
|
return ptrcast!(u8, memory[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
@hide c_realloc func(_ ?@mut anyopaque, memory ?*mut u8, old_size usize, new_size usize, alignment usize) ?*mut u8 {
|
||||||
|
if (power_of_two(alignment) == false) return null
|
||||||
|
|
||||||
|
if new_size == 0 {
|
||||||
|
c.free(memory)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
if memory |old_memory| {
|
||||||
|
if alignment <= malloc_alignment {
|
||||||
|
return ptrcast!(u8, c.realloc(old_memory, c_ulong(new_size)))
|
||||||
|
}
|
||||||
|
|
||||||
|
new_memory ?*mut u8 := c_alloc(null, new_size, alignment)
|
||||||
|
if new_memory |new_bytes| {
|
||||||
|
copy_size usize := old_size
|
||||||
|
if (new_size < copy_size) copy_size = new_size
|
||||||
|
memcopy!(new_bytes[..copy_size], old_memory[..copy_size])
|
||||||
|
c.free(old_memory)
|
||||||
|
}
|
||||||
|
return new_memory
|
||||||
|
}
|
||||||
|
|
||||||
|
return c_alloc(null, new_size, alignment)
|
||||||
|
}
|
||||||
|
|
||||||
|
@hide c_free func(_ ?@mut anyopaque, memory ?*mut u8, _ usize, _ usize) void {
|
||||||
|
c.free(memory)
|
||||||
|
}
|
||||||
|
|
||||||
|
@hide c_vtable AllocatorVTable :: AllocatorVTable {
|
||||||
|
alloc = c_alloc,
|
||||||
|
realloc = c_realloc,
|
||||||
|
free = c_free,
|
||||||
|
}
|
||||||
|
|
||||||
|
c_allocator Allocator :: Allocator {
|
||||||
|
context = null,
|
||||||
|
vtable = &c_vtable,
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
Layout :: enum { auto, c }
|
||||||
|
|
||||||
|
ArrayInfo :: struct {
|
||||||
|
child type
|
||||||
|
len usize
|
||||||
|
}
|
||||||
|
|
||||||
|
FieldInfo :: struct {
|
||||||
|
name []u8
|
||||||
|
type type
|
||||||
|
index usize
|
||||||
|
}
|
||||||
|
|
||||||
|
RecordInfo :: struct {
|
||||||
|
name ?[]u8
|
||||||
|
fields []FieldInfo
|
||||||
|
is_tuple bool
|
||||||
|
layout Layout
|
||||||
|
}
|
||||||
|
|
||||||
|
EnumInfo :: struct {
|
||||||
|
fields []FieldInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
TypeInfo :: union(enum) {
|
||||||
|
invalid void
|
||||||
|
void void
|
||||||
|
noreturn void
|
||||||
|
anyopaque void
|
||||||
|
bool void
|
||||||
|
integer void
|
||||||
|
float void
|
||||||
|
array ArrayInfo
|
||||||
|
pointer void
|
||||||
|
slice void
|
||||||
|
range void
|
||||||
|
optional void
|
||||||
|
function void
|
||||||
|
enum EnumInfo
|
||||||
|
record RecordInfo
|
||||||
|
union void
|
||||||
|
fallible void
|
||||||
|
distinct type
|
||||||
|
}
|
||||||
|
|
||||||
|
EnumFieldStruct func($E, $Field type, $default ?Field) type {
|
||||||
|
match typeinfo!(E) {
|
||||||
|
.enum |info|: {
|
||||||
|
names [info.fields.len]mut []u8 := undefined
|
||||||
|
field_types [info.fields.len]mut type := undefined
|
||||||
|
defaults [info.fields.len]mut ?Field := undefined
|
||||||
|
inline for info.fields |field, index| {
|
||||||
|
names[index] = field.name
|
||||||
|
field_types[index] = Field
|
||||||
|
defaults[index] = default
|
||||||
|
}
|
||||||
|
return struct_type!(.auto, names, field_types, defaults)
|
||||||
|
}
|
||||||
|
else: compile_error!("EnumFieldStruct key must be an enum")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
testing :: import "@std/testing"
|
||||||
|
|
||||||
|
TestTokenKind :: enum(u8) {
|
||||||
|
ident = 3
|
||||||
|
int = 8
|
||||||
|
eof = 21
|
||||||
|
}
|
||||||
|
|
||||||
|
TestNames :: alias EnumFieldStruct(TestTokenKind, ?[]u8, some!(null))
|
||||||
|
TestArrayAlias :: alias [3]u16
|
||||||
|
TestInner :: distinct u16
|
||||||
|
TestOuter :: distinct TestInner
|
||||||
|
TestOuterAlias :: alias TestOuter
|
||||||
|
|
||||||
|
|
||||||
|
@hide array_info_matches func($Array, $Child type, $len usize) bool {
|
||||||
|
match typeinfo!(Array) {
|
||||||
|
.array |info|: return info.child == Child and info.len == len
|
||||||
|
else: return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@hide distinct_info_matches func($Distinct, $Backing type) bool {
|
||||||
|
match typeinfo!(Distinct) {
|
||||||
|
.distinct |backing|: return backing == Backing
|
||||||
|
else: return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
array_reflection_exposes_child_and_logical_length test {
|
||||||
|
try testing.expect($(array_info_matches([4]i32, i32, 4)))
|
||||||
|
try testing.expect($(array_info_matches([0]bool, bool, 0)))
|
||||||
|
try testing.expect($(array_info_matches(TestArrayAlias, u16, 3)))
|
||||||
|
try testing.expect($(array_info_matches([2]mut i64, i64, 2)))
|
||||||
|
try testing.expect($(array_info_matches([2;0]u8, u8, 2)))
|
||||||
|
}
|
||||||
|
distinct_reflection_exposes_immediate_backing test {
|
||||||
|
try testing.expect($(distinct_info_matches(TestInner, u16)))
|
||||||
|
try testing.expect($(distinct_info_matches(TestOuter, TestInner)))
|
||||||
|
try testing.expect($(distinct_info_matches(TestOuterAlias, TestInner)))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
enum_field_struct_defaults test {
|
||||||
|
names TestNames := {
|
||||||
|
ident = "identifier",
|
||||||
|
int = "integer",
|
||||||
|
}
|
||||||
|
if field!(names, "ident") |value| {
|
||||||
|
try testing.expect(value.len == 10)
|
||||||
|
} else {
|
||||||
|
try testing.expect(false)
|
||||||
|
}
|
||||||
|
if field!(names, "int") |value| {
|
||||||
|
try testing.expect(value.len == 7)
|
||||||
|
} else {
|
||||||
|
try testing.expect(false)
|
||||||
|
}
|
||||||
|
if field!(names, "eof") |_| {
|
||||||
|
try testing.expect(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
empty TestNames := {}
|
||||||
|
if field!(empty, "ident") |_| {
|
||||||
|
try testing.expect(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user