242 lines
11 KiB
Markdown
242 lines
11 KiB
Markdown
# "quick" / "easy" fixes
|
|
|
|
- for global initialization cycles, report also starting and ending lines
|
|
|
|
# milestones
|
|
|
|
1. interop type foundation (implemented)
|
|
- unsigned integers, floats, and target-dependent c scalar types
|
|
- atomic `c_*` primitive types remain distinct until target-aware lowering
|
|
- `c_func` and pointer-only `c_struct`; `c` remains an ordinary identifier
|
|
- keep binding mutability (`::` / `=`) separate from element or pointee mutability (`mut`)
|
|
- arrays and indexing
|
|
- `[N]T`: array with `N` logical elements
|
|
- `[N;S]T`: array with `N` logical elements followed by sentinel `S`
|
|
- pointers
|
|
- `@T` / `@mut T`: non-null single-item pointer without arithmetic
|
|
- `*T` / `*mut T`: non-null many-item pointer with arithmetic
|
|
- optional pointers represent nullable pointers (i.e. `?@T` / `?@mut T`, `?*T` / `?*mut T`)
|
|
- slices and slicing
|
|
- `[]T`: pointer and length
|
|
- `[;S]T`: pointer and length with a sentinel invariant
|
|
- ordinary slices do not guarantee null termination
|
|
- string literals as immutable sentinel slices backed by static arrays (superseded by milestone 3.5)
|
|
- character literals
|
|
- optionals with trapping unwrap and fallback operations
|
|
- native structs with compiler-controlled layout
|
|
- pointer-only `c_struct` support with target c layout
|
|
- `Some :: c_struct { ... }`: defined c-layout struct
|
|
- `Some :: c_struct`: opaque c-layout struct
|
|
- passing c structs by value was deferred until milestone 4.1
|
|
|
|
2. restricted c header imports (implemented)
|
|
- treat an imported header as a synthetic, file-local package namespace
|
|
- `native :: import "relative/path/to/header.h"`
|
|
- import functions, typedefs, scalar types, and pointers to opaque records
|
|
- keep implementation linking separate from header imports
|
|
- cache imports by canonical header path and target/include/define configuration
|
|
- diagnose unsupported declarations when referenced
|
|
- dynamically load libclang behind a replaceable c importer boundary
|
|
|
|
3. c variadic calls (implemented)
|
|
- represent c variadics as a fixed parameter count plus a variadic flag
|
|
- apply c default argument promotions at call sites
|
|
- emit LLVM c-variadic declarations and calls
|
|
- keep native brolang variadics and tuple design separate
|
|
|
|
3.5. sentinel pointers and c strings (implemented)
|
|
- add sentinel many-item pointers: `[*;S]T`
|
|
- represent string literals as immutable pointers to statically stored sentinel arrays: `@[N;0]u8`
|
|
- arrays expose `.len` but no `.ptr`; slices and pointers-to-arrays expose sentinel-preserving `.ptr`
|
|
- allow pointer-to-array `.len`, indexing, slicing, pointer decay, and slice construction without explicit dereference
|
|
- preserve or forget sentinel information through compatible pointer and slice coercions without copying arrays
|
|
- allow zero-terminated immutable byte pointer views to convert to immutable `*c_char` and `[*;0]c_char`
|
|
- keep `u8` and `c_char` distinct to preserve target-dependent scalar c semantics
|
|
- reject general `u8`/`c_char` interchange, slice-to-pointer coercion, and conversion to mutable c character pointers
|
|
|
|
4. advanced c interop
|
|
- by-value records and unions (implemented)
|
|
- complete plain imported structs/unions and manual `c_struct` values
|
|
- fixed C arrays inside imported records
|
|
- keyed struct literals and exactly-one-field union literals
|
|
- field reads/writes, storage, and fixed-signature calls/returns
|
|
- aarch64-macos small aggregate, homogeneous float aggregate, and indirect ABI lowering
|
|
- keep incomplete, bitfield, packed, flexible-array, qualified-field, and otherwise non-plain records pointer-only
|
|
- keep C variadic record arguments unsupported
|
|
- function pointers and callbacks (implemented)
|
|
- imported C function pointer typedefs lower to nullable pointer types
|
|
- manual `?*c_func(...) T` callback type spelling
|
|
- concrete `c_func` declarations/definitions can be passed as callback values
|
|
- postfix calls through non-null function pointers, including `callback?(...)`
|
|
- fixed and C-variadic callback ABI emission through LLVM indirect calls
|
|
- external variables (implemented)
|
|
- imported external C object variables lower to direct LLVM external global references
|
|
- top-level `const` object variables are read-only from brolang
|
|
- mutable external scalars/records can be assigned through qualified package globals
|
|
- unsupported variable types remain lazy diagnostics when referenced
|
|
- object-like macro constants (implemented)
|
|
- scalar integer/float literal macros import as immutable globals
|
|
- `CLITERAL(Type){ ... }` / `(Type){ ... }` record literal macros import as immutable globals
|
|
- function-like macros and non-literal macro expressions remain unsupported
|
|
- static inline functions (implemented)
|
|
|
|
5. control flow
|
|
- boolean expressions (implemented)
|
|
- `bool` type with `true` / `false` literals
|
|
- comparison operators: `==`, `!=`, `<`, `<=`, `>`, `>=` (numeric operands widen; `bool` supports only `==` / `!=`)
|
|
- operators: `and`, `or`, `!`
|
|
- lazy evaluation / short-circuit evaluation
|
|
- if statements (implemented). example: `if condition { ... } else if { ... } else { ... }`
|
|
- conditions must be `bool`; block-scoped locals with shadowing across blocks
|
|
- 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`
|
|
- 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)
|
|
- new `Optional_Is_Some` / `Optional_Value` IR opcodes (the `Unwrap` presence-test + extract, minus the trap)
|
|
- conditional unwrapping with guard clause: `if val |v : v >= 10| { ... } else { ... }` - unwrap `val` into `v` if it is not `none`
|
|
- multi-unwrap (see section below)
|
|
- while loops (implemented; operates on boolean conditions). examples:
|
|
- `while condition { ... }` - iterate while the condition is true
|
|
- `while condition : i = i + 1 { ... }` - execute the update after each completed iteration
|
|
- the condition and update may be parenthesized independently for visual clarity
|
|
- update targets must already be declared and mutable; loops do not introduce implicit induction variables
|
|
- compound assignment (`+=`) remains deferred
|
|
- ranges (implemented; see section below)
|
|
- for loops (implemented; operates on ranges, arrays, slices, and pointers-to-arrays). examples:
|
|
- `for items |item| { ... }` - capture just the `item` value in the array/slice (uses copy semantics, i.e. gets a `T`)
|
|
- `for (&items) |@item| { ... }` - capture a pointer to each array element; its `@T` / `@mut T` mutability follows the iterable
|
|
- `for items_slice |@item| { ... }` - slices already refer to backing storage and support pointer capture directly
|
|
- `for items |item, idx| { ... }` - capture `item` and its index index in the array/slice
|
|
- `for 0..10 |i| { ... }` - iterate over the range `0..10` (exclusive)
|
|
- `for 0..=10 |i| { ... }` - iterate over the range `0..10` (inclusive)
|
|
- `for 0..(len) |i| { ... }` or equivalently `for 0..=(len - 1) |i| { ... }` - calculating range bounds, expressions must be parenthesized
|
|
- for all conditionals/guards, parentheses are optional but allowed for visual clarity
|
|
|
|
6. compound assignment: `+=`, `-=`, `*=`, `/=`
|
|
|
|
7. enums (native and c interop) (see below)
|
|
|
|
8. distinct types (see below)
|
|
|
|
## A word on multi-unwrap
|
|
|
|
Unwrap multiple optionals with `and`. This **short-circuits**: if the first optional is none, subsequent expressions are not evaluated.
|
|
|
|
```honey
|
|
name: ?[]u8 = get_name()
|
|
age: ?u8 = get_age()
|
|
if name and age |n, a| {
|
|
# both n and a are guaranteed non-none here
|
|
print("{s} is {d} years old", {n, a})
|
|
}
|
|
```
|
|
|
|
**With guard clause on multiple values:**
|
|
|
|
```honey
|
|
if name and hat |n, h : n == "Huginn" and h.brand == .gucci| {
|
|
print("{s}'s got that drip\n", {n})
|
|
}
|
|
```
|
|
|
|
Parentheses around the expression are optional, but can aid readability when combined with guards:
|
|
|
|
```honey
|
|
# without parentheses
|
|
if name and hat |n, h : guard| { ... }
|
|
|
|
# with parentheses for clarity
|
|
if (name and hat) |n, h : guard| { ... }
|
|
```
|
|
|
|
## A word on lazy / short-circuit evaluation
|
|
|
|
The `and` in multi-unwrap short-circuits left-to-right:
|
|
|
|
```honey
|
|
if get_name() and get_hat() |n, h| {
|
|
# get_hat() is only called if get_name() returned non-none
|
|
}
|
|
```
|
|
|
|
This is important for avoiding unnecessary computation or side effects.
|
|
|
|
## A word on ranges
|
|
|
|
Ranges represent a sequence of values, commonly used in for loops, and is itself a value type:
|
|
|
|
```
|
|
0..10 # exclusive: 0, 1, 2, ..., 9
|
|
0..=10 # inclusive: 0, 1, 2, ..., 10
|
|
```
|
|
|
|
**Parenthesization rule:** Each side of `..` must be either a simple term (literal or identifier) or a parenthesized expression. This eliminates precedence ambiguity:
|
|
|
|
```
|
|
0..10 # OK: both sides are literals
|
|
0..n # OK: both sides are simple
|
|
0..(n + 1) # OK: complex expression is parenthesized
|
|
(a + 1)..(b - 1) # OK: both sides parenthesized
|
|
# 0..n + 1 # ERROR: must parenthesize complex expressions
|
|
```
|
|
|
|
This rule keeps the grammar simple and forces clarity at the call site — no precedence rules to remember. Also, being a value type, ranges can be assigned to variables and passed around like any other value. Range bounds are evaluated once, must have compatible concrete integer types, and descending ranges are empty.
|
|
|
|
For-loop captures are immutable and scoped to the loop body. Sequence index captures are `usize`. Pointer capture uses `|@item|`; arrays must be passed by pointer (for example `&items`), while slices can be used directly. Sentinel elements are not included in iteration.
|
|
|
|
# A word on distinct types
|
|
|
|
Distinct types are considered distinct from their backing type. They do not implicitly coerce to their backing type.
|
|
|
|
```
|
|
# distinct type
|
|
UserID :: distinct u32
|
|
|
|
# instantiate distinct type
|
|
my_id UserId :: UserID(42) # value must be of to backing type
|
|
```
|
|
|
|
# A word on enums
|
|
|
|
```
|
|
# standard enums
|
|
Animal :: enum {
|
|
dog
|
|
cat
|
|
bird
|
|
lizard
|
|
}
|
|
|
|
# enums with backing type
|
|
Nat :: enum(u8) { # in this case, a maximum of 256 values are possible
|
|
one # default: implicitly starts from value 0
|
|
two
|
|
three
|
|
four
|
|
five
|
|
}
|
|
|
|
# enums with backing type with explicit associated values
|
|
# note: must not be jumbled (i.e. `first_val = 1` must come before `other_val = 2`), but is allowed to be discontiguous (i.e. `one = 1` can be followed by `three = 3` without `two = 2` in between)
|
|
Nat :: enum(u8) {
|
|
one = 1
|
|
two = 2
|
|
three = 3
|
|
# no four
|
|
five = 5
|
|
}
|
|
|
|
# enums with backing type with semi-implicit associated values
|
|
Nat :: enum(u8) {
|
|
one = 1 # starts from value 1
|
|
two # implicitly gets value 2
|
|
three # etc...
|
|
four
|
|
five
|
|
}
|
|
|
|
# using enums
|
|
dog_tag1 Animal :: Animal.dog
|
|
dog_tag2 Animal :: .dog # type inferred
|
|
```
|