mutable decl syntax change

This commit is contained in:
2026-08-05 21:19:41 +02:00
parent 88b94197c9
commit 081a3fd5df
74 changed files with 870 additions and 733 deletions
+19 -20
View File
@@ -12,7 +12,7 @@
- unsigned integers, floats, and target-dependent c scalar types
- atomic `c_*` primitive types remain distinct until target-aware lowering
- `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
- `[N]T`: array with `N` logical elements
- `[N;S]T`: array with `N` logical elements followed by sentinel `S`
@@ -184,7 +184,7 @@
- undefined values are assigned a poison value (0xaa...)
- allows for something like:
```
a int = undefined
a int := undefined
if (condition) {
a = 42
} else {
@@ -323,7 +323,7 @@
- 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
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] = { ... }`)
- 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
@@ -695,9 +695,9 @@
27.8 source-defined mutable runtime globals (implemented)
- allow mutable global declarations in Brolang source for process-global runtime
state, matching the writable-global support already needed for imported C globals
- require source type syntax and an initializer; constraints (`int`/`float`/`range`)
and inferred array counts may resolve through the existing inference fixpoint, but
the final type must be concrete runtime storage
- require an initializer and infer or explicitly declare a concrete runtime storage type;
constraints (`int`/`float`/`range`) and inferred array counts resolve through the existing
inference fixpoint
- 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
@@ -774,7 +774,7 @@
improving layout or specialization
- 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)
try arraylist.append(&values, 42)
```
@@ -988,9 +988,9 @@ f: f32 = 3.14
bits := bitcast(f, u32) # IEEE 754 representation
# pointer casts
buf *u8 = get_buffer()
ints *u32 = ptrcast!(u32, buf) # element type change, same pointer shape
writable *mut u8 = constcast!(buf) # explicit unsafe mutability restoration
buf *u8 := get_buffer()
ints *u32 := ptrcast!(u32, buf) # element type change, same pointer shape
writable *mut u8 := constcast!(buf) # explicit unsafe mutability restoration
```
## A word on multi-unwrap
@@ -1069,8 +1069,7 @@ layer:
```bro
UserID :: distinct u32
OuterID :: distinct UserID
index usize = 42
index usize := 42
id UserID :: UserID(index)
raw u32 :: u32(id)
outer OuterID :: OuterID(id)
@@ -1222,7 +1221,7 @@ data :: {
}
# match arms
label []u8 = match p {
label []u8 := match p {
.high: "HIGH", # single expression: implicit yield
.low: {
log("low priority")
@@ -1231,7 +1230,7 @@ label []u8 = match p {
}
# catch handlers (planned; block form deferred in milestone 23 v1)
data []u8 = read(path) catch |e| {
data []u8 := read(path) catch |e| {
log(e)
yield fallback_data # block: explicit yield
}
@@ -1252,7 +1251,7 @@ result :: if a {
}
# yielding to a variable
result int = if a {
result int := if a {
yield 1
} else if b {
yield 2
@@ -1482,7 +1481,7 @@ Fallible functions use ordinary `return` for both channels. If the returned expr
```
parse_section func(p: @mut Parser) void ! ParseError {
start_line Line = p.line
start_line Line := p.line
p.advance()
# ... parsing logic ...
@@ -1497,7 +1496,7 @@ Since errors are just union values, you can also construct them separately:
```
# Construct error value (it's just a union)
e ParseError = .timeout{500}
e ParseError := .timeout{500}
# Return it via error channel later
return e
@@ -1647,7 +1646,7 @@ Brolang provides a libc-backed allocator value:
mem :: import "@std/mem"
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)
# ... work with temp ...
@@ -1677,7 +1676,7 @@ mem :: import "@std/mem"
# Allocation escapes via return value — requires allocator
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)
return result # caller manages this memory
}
@@ -1690,7 +1689,7 @@ init func(obj @mut MyStruct, allocator mem.Allocator) void {
# No allocation escapes — no allocator needed
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)
# ... work with temp ...
return compute_hash(temp)