nomen-lang 0.6.13 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/NOMEN_AGENTS.md +27 -13
- package/audit_runtime.c +2 -0
- package/core/System/Arena.nm +4 -4
- package/core/System/Array.nm +108 -78
- package/core/System/Atomic.nm +81 -0
- package/core/System/Awaitable.nm +12 -5
- package/core/System/BigInt.nm +1 -1
- package/core/System/Box.nm +32 -0
- package/core/System/Buffer.nm +18 -18
- package/core/System/Channel.nm +27 -11
- package/core/System/ClassBuffer.nm +24 -28
- package/core/System/Console.nm +4 -4
- package/core/System/Controls/Button.nm +8 -3
- package/core/System/Controls/CheckBox.nm +8 -3
- package/core/System/Controls/Container.nm +18 -11
- package/core/System/Controls/Layout.nm +1 -1
- package/core/System/Controls/Text.nm +9 -4
- package/core/System/Controls/TextBox.nm +8 -3
- package/core/System/Controls/Window.nm +10 -3
- package/core/System/Fiber.nm +6 -2
- package/core/System/Graph.nm +2 -2
- package/core/System/Init.nm +2 -2
- package/core/System/LinkedList.nm +2 -2
- package/core/System/List.nm +7 -7
- package/core/System/Map.nm +10 -7
- package/core/System/Math.nm +1 -1
- package/core/System/Mutex.nm +14 -2
- package/core/System/Random.nm +7 -1
- package/core/System/Result.nm +1 -1
- package/core/System/Sendable.nm +2 -2
- package/core/System/Set.nm +6 -6
- package/core/System/Spawnable.nm +9 -9
- package/core/System/Stream/Directory.nm +1 -2
- package/core/System/Stream/File.nm +8 -9
- package/core/System/Stream/Http.nm +2 -3
- package/core/System/Stream/Tcp.nm +13 -8
- package/core/System/String.nm +22 -12
- package/core/System/StringBuilder.nm +2 -2
- package/core/System/Task.nm +21 -29
- package/core/System/Test.nm +10 -4
- package/core/System/Text/CharIndex.nm +2 -3
- package/core/System/Text/Json.nm +8 -12
- package/core/System/Text/JsonTree.nm +4 -4
- package/core/System/Text/Regex.nm +7 -7
- package/core/System/Text/Utf8.nm +2 -3
- package/core/System/Thread.nm +9 -4
- package/core/System/Tree.nm +2 -2
- package/core/System/Viewable.nm +2 -2
- package/core/System/WeakHandle.nm +56 -0
- package/core/System/bool.nm +1 -1
- package/core/System/char.nm +1 -1
- package/core/System/float.nm +1 -1
- package/core/System/float32.nm +1 -1
- package/core/System/float64.nm +1 -1
- package/core/System/int.nm +1 -1
- package/core/System/int16.nm +1 -1
- package/core/System/int32.nm +1 -1
- package/core/System/int64.nm +1 -1
- package/core/System/int8.nm +1 -1
- package/core/System/ufloat.nm +1 -1
- package/core/System/ufloat32.nm +1 -1
- package/core/System/ufloat64.nm +1 -1
- package/core/System/uint.nm +1 -1
- package/core/System/uint16.nm +1 -1
- package/core/System/uint32.nm +1 -1
- package/core/System/uint64.nm +1 -1
- package/core/System/uint8.nm +1 -1
- package/core/docs/System/Result.md +2 -2
- package/dist/index.mjs +29031 -25546
- package/package.json +1 -1
- package/src/test.ts +26 -11
package/NOMEN_AGENTS.md
CHANGED
|
@@ -75,8 +75,7 @@ which evaluates cases in order and runs the first true one.
|
|
|
75
75
|
switch {
|
|
76
76
|
case moved { ... }
|
|
77
77
|
case is_ins { ... }
|
|
78
|
-
|
|
79
|
-
}
|
|
78
|
+
} else { ... }
|
|
80
79
|
```
|
|
81
80
|
|
|
82
81
|
Independent conditions that can all fire stay as separate `if`s.
|
|
@@ -100,10 +99,16 @@ func safe = (string[] s, int i: i >= 0 && i < s.length, out string) {
|
|
|
100
99
|
| -------- | ---------------------------------------------- |
|
|
101
100
|
| `ref T` | mutable borrow; caller must also write `ref` |
|
|
102
101
|
| `view T` | read-only borrow |
|
|
103
|
-
| `
|
|
102
|
+
| `mv T` | transfer ownership; caller writes `mv` |
|
|
104
103
|
| `out T` | output parameter, assigned inside the function |
|
|
105
104
|
|
|
106
105
|
`const` values cannot be passed to `ref` — rebind the caller as `var` first.
|
|
106
|
+
At local bindings the same keywords spell the binding's relationship to its
|
|
107
|
+
initializer: `ref Box q = p` is a mv-struct ALIAS (a plain `var Box q = p`
|
|
108
|
+
copy of a mv value — including a `string` — is an error: say `ref`, `mv`,
|
|
109
|
+
or `.copy()`), `mv` transfers, `view` is a slice. A `ref` alias is not
|
|
110
|
+
reassignable, mutation through it needs a `var` source, and nothing moves
|
|
111
|
+
through it.
|
|
107
112
|
|
|
108
113
|
### Return types are `out` at the end
|
|
109
114
|
|
|
@@ -133,8 +138,8 @@ const y = x + 1 // Error: 'x' is null
|
|
|
133
138
|
private to its declaring scope.
|
|
134
139
|
|
|
135
140
|
`readonly` is a field-write modifier, not a visibility: the field reads at its
|
|
136
|
-
declared visibility but can only be assigned inside its declaring struct
|
|
137
|
-
|
|
141
|
+
declared visibility but can only be assigned inside its declaring struct (and
|
|
142
|
+
its `extend`s). `const` fields are immutable everywhere.
|
|
138
143
|
|
|
139
144
|
### Imports
|
|
140
145
|
|
|
@@ -156,10 +161,10 @@ declarations, so never import a sibling. Only cross-namespace references
|
|
|
156
161
|
Failures are signalled via return values — nullable results, status structs,
|
|
157
162
|
or boolean + out-parameter pairs. There is no `throw`/`try`/`catch`.
|
|
158
163
|
|
|
159
|
-
`Result<T, E>` is declared `
|
|
164
|
+
`Result<T, E>` is declared `checked`: a call returning it must bind or match
|
|
160
165
|
the value (`var _ = f.close()` to discard deliberately; a bare `f.close()`
|
|
161
166
|
statement is a compile error). Any enum or bitset can opt into this with the
|
|
162
|
-
`
|
|
167
|
+
`checked` modifier.
|
|
163
168
|
|
|
164
169
|
## Tests
|
|
165
170
|
|
|
@@ -196,10 +201,10 @@ var int y = 10 // mutable, explicit type
|
|
|
196
201
|
const int[] nums = [1, 2, 3]
|
|
197
202
|
const int[10] buf = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] // fixed-length
|
|
198
203
|
var int? maybe = null // nullable
|
|
199
|
-
ref
|
|
204
|
+
ref Box q = p // mv-struct alias — must be spelled (no plain copies)
|
|
200
205
|
```
|
|
201
206
|
|
|
202
|
-
Keywords (`out`, `
|
|
207
|
+
Keywords (`out`, `match`, `if`, `of`, `as`, `swap`, …) are reserved and
|
|
203
208
|
cannot be used as variable, parameter, field, type, or case names. Function
|
|
204
209
|
names are exempt since calls go through `.name(...)` (e.g. `Regex.match`).
|
|
205
210
|
|
|
@@ -230,10 +235,10 @@ while cond { ... }
|
|
|
230
235
|
while cond; update { ... } // post-statement (e.g. while i < n; i += 1)
|
|
231
236
|
```
|
|
232
237
|
|
|
233
|
-
### Structs
|
|
238
|
+
### Structs
|
|
234
239
|
|
|
235
240
|
```nomen
|
|
236
|
-
pub struct Point {
|
|
241
|
+
pub struct Point { // Copyable value type
|
|
237
242
|
pub var int x
|
|
238
243
|
pub var int y
|
|
239
244
|
pub func add = (self, Point other, out Point) {
|
|
@@ -241,12 +246,15 @@ pub struct Point {
|
|
|
241
246
|
}
|
|
242
247
|
}
|
|
243
248
|
|
|
244
|
-
|
|
249
|
+
mv struct Node { // single-owner, move-only (bindings mv)
|
|
245
250
|
var int value
|
|
246
251
|
}
|
|
247
252
|
```
|
|
248
253
|
|
|
249
|
-
Construct by calling the type name as a function: `Point(1, 2)`.
|
|
254
|
+
Construct by calling the type name as a function: `Point(1, 2)`. A `struct`
|
|
255
|
+
whose fields are not all Copyable is a compile error — mark it
|
|
256
|
+
`mv struct`. Sharing a mv instance is spelled: `ref` (alias),
|
|
257
|
+
`Box<T>` (owned heap slot), `WeakHandle<T>` (checked back-pointer).
|
|
250
258
|
|
|
251
259
|
### Strings
|
|
252
260
|
|
|
@@ -256,6 +264,12 @@ Console.write("hello \{name}\n") // interpolation with \{...}
|
|
|
256
264
|
Console.write_line("with newline")
|
|
257
265
|
```
|
|
258
266
|
|
|
267
|
+
`string` is **mv**: `var b = a` (two string variables) needs `mv b = a`
|
|
268
|
+
or `a.copy()`; `return self.field` and `var s = obj.field` need `.copy()`;
|
|
269
|
+
storing a bare `string` parameter into a string field needs `.copy()`;
|
|
270
|
+
`xs.push(s)` / `m.set(k, v)` need `mv`/`.copy()`. Literals are fresh values.
|
|
271
|
+
`view string` is the cheap read-only borrow.
|
|
272
|
+
|
|
259
273
|
### Traits
|
|
260
274
|
|
|
261
275
|
```nomen
|
package/audit_runtime.c
CHANGED
|
@@ -57,6 +57,7 @@ static void *audit_alloc(unsigned long size) {
|
|
|
57
57
|
header->magic = NOMEN_AUDIT_MAGIC;
|
|
58
58
|
header->size = size;
|
|
59
59
|
header->state = NOMEN_AUDIT_LIVE;
|
|
60
|
+
if (getenv("NOMEN_AUDIT_TRACE")) fprintf(stderr, "ALLOC %lu\n", (unsigned long)size);
|
|
60
61
|
__atomic_add_fetch(&nomen_malloc_count, 1, __ATOMIC_SEQ_CST);
|
|
61
62
|
return (void *)(header + 1);
|
|
62
63
|
}
|
|
@@ -73,6 +74,7 @@ static nomen_audit_header *audit_header(void *ptr) {
|
|
|
73
74
|
// Retire a block: poison the header first so a second free of the same pointer
|
|
74
75
|
// is recognized as stale, then hand the memory back and drop the count.
|
|
75
76
|
static void audit_release(nomen_audit_header *header) {
|
|
77
|
+
if (getenv("NOMEN_AUDIT_TRACE")) fprintf(stderr, "FREE %lu\n", (unsigned long)header->size);
|
|
76
78
|
header->magic = 0;
|
|
77
79
|
header->state = NOMEN_AUDIT_DEAD;
|
|
78
80
|
__atomic_sub_fetch(&nomen_malloc_count, 1, __ATOMIC_SEQ_CST);
|
package/core/System/Arena.nm
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
// the arena's own #destroy reclaims every still-live slot at scope exit.
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
|
-
* A copyable, non-
|
|
19
|
+
* A copyable, non-mv handle to an `Arena<T>` slot. The generation makes a
|
|
20
20
|
* reused slot distinguishable from the one this handle was issued for.
|
|
21
21
|
**/
|
|
22
22
|
pub struct ArenaRef<T> {
|
|
@@ -32,7 +32,7 @@ pub struct ArenaRef<T> {
|
|
|
32
32
|
* A generational arena: one owner of a set of `T` values, addressed by
|
|
33
33
|
* copyable `ArenaRef<T>` handles.
|
|
34
34
|
**/
|
|
35
|
-
pub struct Arena<T> {
|
|
35
|
+
pub mv struct Arena<T> {
|
|
36
36
|
// Slot payloads. T = value struct/primitive routes to Buffer<T>; T =
|
|
37
37
|
// class/trait routes to ClassBuffer<T> (monomorphize rewrites the field),
|
|
38
38
|
// so slot ownership follows the usual element rules.
|
|
@@ -63,7 +63,7 @@ pub struct Arena<T> {
|
|
|
63
63
|
* Store `value` in a slot (reusing a freed slot when one is available) and
|
|
64
64
|
* return a handle to it.
|
|
65
65
|
**/
|
|
66
|
-
pub func alloc = (ref self,
|
|
66
|
+
pub func alloc = (ref self, mv T value, out ArenaRef<T>) {
|
|
67
67
|
var int index = -1
|
|
68
68
|
if self.free_count > 0 {
|
|
69
69
|
// Reuse a freed slot. Its generation was already bumped by `free`,
|
|
@@ -140,7 +140,7 @@ pub struct Arena<T> {
|
|
|
140
140
|
/**
|
|
141
141
|
* Replace the value `handle` names (same slot, same handle).
|
|
142
142
|
**/
|
|
143
|
-
pub func set = (ref self, ArenaRef<T> handle,
|
|
143
|
+
pub func set = (ref self, ArenaRef<T> handle, mv T value) {
|
|
144
144
|
if !self.is_valid(handle) {
|
|
145
145
|
panic("arena: set through a stale handle")
|
|
146
146
|
}
|
package/core/System/Array.nm
CHANGED
|
@@ -92,7 +92,7 @@ pub struct Array<T>: Viewable {
|
|
|
92
92
|
}
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
-
// A non-
|
|
95
|
+
// A non-mv (ptr, len) slice [start, end) over self's inline element
|
|
96
96
|
// storage, returned as a `view T`. Borrows from self (may not outlive it,
|
|
97
97
|
// invalidated on reassignment). Element access via view.at / view.length.
|
|
98
98
|
pub func slice = (
|
|
@@ -139,83 +139,113 @@ pub struct Array<T>: Viewable {
|
|
|
139
139
|
}
|
|
140
140
|
return _result;
|
|
141
141
|
```
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
142
|
+
```
|
|
143
|
+
#arch: aarch64
|
|
144
|
+
// Returns x0 = heap-allocated array pointer ([ptr]=length, [ptr+8..]=data).
|
|
145
|
+
// Entry dispatch (the asm twin of the C body's #if T_NEEDS_STRDUP): a
|
|
146
|
+
// FAT T (string) arrives as the (ptr, len) pair in x0/x1 with the count
|
|
147
|
+
// in x2; a thin T arrives as the value in x0 with the count in x1.
|
|
148
|
+
// STRUCT_PLAN_3 B1 cutover: a FAT seed must be strdup'd per slot — every
|
|
149
|
+
// slot sharing one pointer would be freed n times by the mv
|
|
150
|
+
// set/destroy paths (pre-B1 the displaced-set free was record-guarded,
|
|
151
|
+
// so a raw rodata seed was simply never freed).
|
|
152
|
+
stp x19, x20, [sp, #-16]!
|
|
153
|
+
mov x7, #T_FAT
|
|
154
|
+
cmp x7, #1
|
|
155
|
+
b.eq .Larray_with_fat_entry
|
|
156
|
+
// Thin: [sp] = value, count in x1.
|
|
157
|
+
stp xzr, x0, [sp, #-16]!
|
|
158
|
+
mov x19, x1
|
|
159
|
+
b .Larray_with_counted
|
|
160
|
+
.Larray_with_fat_entry:
|
|
161
|
+
// Fat: [sp] = len, [sp+8] = ptr; count in x2.
|
|
162
|
+
stp x1, x0, [sp, #-16]!
|
|
163
|
+
mov x19, x2
|
|
164
|
+
.Larray_with_counted:
|
|
165
|
+
|
|
166
|
+
// ptr = malloc(8 + count * T_SIZE)
|
|
167
|
+
mov x2, #T_SIZE
|
|
168
|
+
mul x0, x19, x2
|
|
169
|
+
add x0, x0, #8
|
|
170
|
+
bl _malloc
|
|
171
|
+
mov x20, x0 // x20 = base pointer
|
|
172
|
+
str x19, [x20] // [ptr] = count
|
|
173
|
+
|
|
174
|
+
cbz x19, .Larray_with_end
|
|
175
|
+
mov x2, #0 // i = 0
|
|
176
|
+
.Larray_with_loop:
|
|
177
|
+
mov x3, #T_SIZE
|
|
178
|
+
mul x4, x2, x3
|
|
179
|
+
add x5, x20, #8
|
|
180
|
+
add x5, x5, x4 // addr = data + offset
|
|
181
|
+
|
|
182
|
+
// T_FAT (string): strdup the seed's ptr half into each slot.
|
|
183
|
+
mov x7, #T_FAT
|
|
184
|
+
cmp x7, #1
|
|
185
|
+
b.ne .Larray_with_plain
|
|
186
|
+
ldr x0, [sp, #8]
|
|
187
|
+
cbz x0, .Larray_with_pair
|
|
188
|
+
bl _strdup
|
|
189
|
+
ldr x1, [sp]
|
|
190
|
+
str x0, [x5]
|
|
191
|
+
str x1, [x5, #8]
|
|
192
|
+
b .Larray_with_next
|
|
193
|
+
.Larray_with_pair:
|
|
194
|
+
stp xzr, xzr, [x5]
|
|
195
|
+
b .Larray_with_next
|
|
196
|
+
|
|
197
|
+
.Larray_with_plain:
|
|
198
|
+
// Store the value (parked at [sp+8]) using a width that matches
|
|
199
|
+
// T_SIZE. For T_SIZE <= 8 the value is a scalar; T_SIZE > 8 (a
|
|
200
|
+
// non-fat struct) byte-copies from the parked pointer.
|
|
201
|
+
cmp x3, #8
|
|
202
|
+
b.gt .Larray_with_copy_loop
|
|
203
|
+
cmp x3, #4
|
|
204
|
+
b.eq .Larray_with_store4
|
|
205
|
+
cmp x3, #2
|
|
206
|
+
b.eq .Larray_with_store2
|
|
207
|
+
cmp x3, #1
|
|
208
|
+
b.eq .Larray_with_store1
|
|
209
|
+
ldr x0, [sp, #8]
|
|
210
|
+
str x0, [x5]
|
|
211
|
+
b .Larray_with_next
|
|
212
|
+
.Larray_with_store4:
|
|
213
|
+
ldr x0, [sp, #8]
|
|
214
|
+
str w0, [x5]
|
|
215
|
+
b .Larray_with_next
|
|
216
|
+
.Larray_with_store2:
|
|
217
|
+
ldr x0, [sp, #8]
|
|
218
|
+
strh w0, [x5]
|
|
219
|
+
b .Larray_with_next
|
|
220
|
+
.Larray_with_store1:
|
|
221
|
+
ldr x0, [sp, #8]
|
|
222
|
+
strb w0, [x5]
|
|
223
|
+
b .Larray_with_next
|
|
224
|
+
|
|
225
|
+
.Larray_with_copy_loop:
|
|
226
|
+
// Struct element (T_SIZE > 8): [sp+8] is a pointer to the bytes —
|
|
227
|
+
// copy them one by one so adjacent slots aren't clobbered.
|
|
228
|
+
mov x6, #0
|
|
229
|
+
ldr x0, [sp, #8]
|
|
230
|
+
.Larray_with_byte:
|
|
231
|
+
ldrb w7, [x0, x6]
|
|
232
|
+
strb w7, [x5, x6]
|
|
233
|
+
add x6, x6, #1
|
|
234
|
+
cmp x6, x3
|
|
235
|
+
blt .Larray_with_byte
|
|
236
|
+
|
|
237
|
+
.Larray_with_next:
|
|
238
|
+
add x2, x2, #1
|
|
239
|
+
cmp x2, x19
|
|
240
|
+
blt .Larray_with_loop
|
|
241
|
+
|
|
242
|
+
.Larray_with_end:
|
|
243
|
+
mov x0, x20 // return heap pointer
|
|
244
|
+
add sp, sp, #16 // pop the parked value pair
|
|
245
|
+
ldp x19, x20, [sp], #16
|
|
246
|
+
// Fall through to the builder's .return epilogue (it pops the raw
|
|
247
|
+
// block's stack frame) — a bare `ret` here would skip it.
|
|
248
|
+
```
|
|
219
249
|
}
|
|
220
250
|
|
|
221
251
|
pub func add = (self, Array other, out Array) {
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// Atomic<T> (STRUCT_PLAN_3 N4) — a lock-free protected slot for 64-bit
|
|
2
|
+
// integer state shared across tasks.
|
|
3
|
+
//
|
|
4
|
+
// A type may be mutated through a cross-worker borrowed capture iff the
|
|
5
|
+
// mutated slot is protected: an `Atomic<T>` field (lock-free), a `Shared<T>`
|
|
6
|
+
// field (lock-guarded), or a handle. The Atomic's METHODS are the sanctioned
|
|
7
|
+
// mutation path — the R3 freeze (checked region borrows) deliberately
|
|
8
|
+
// exempts them, so a spawned worker can bump a borrowed counter:
|
|
9
|
+
//
|
|
10
|
+
// var Atomic<int> counter = Atomic<int>()
|
|
11
|
+
// async {
|
|
12
|
+
// var t = Thread(work(counter)).start() // counter borrowed
|
|
13
|
+
// ...
|
|
14
|
+
// }
|
|
15
|
+
// func work = (ref Atomic<int> counter) {
|
|
16
|
+
// counter.fetch_add(1)
|
|
17
|
+
// }
|
|
18
|
+
//
|
|
19
|
+
// T must be a 64-bit integer type — the slot is one machine word and the
|
|
20
|
+
// operations are single-word atomics.
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* A lock-free 64-bit integer slot for cross-task shared state
|
|
24
|
+
**/
|
|
25
|
+
pub struct Atomic<T> {
|
|
26
|
+
// The machine word. Private by discipline: mutate through the methods
|
|
27
|
+
// (direct assignment replaces the atomic — the checker's freeze blocks
|
|
28
|
+
// that through borrowed captures).
|
|
29
|
+
var uint64 word = 0
|
|
30
|
+
|
|
31
|
+
pub func load = (self, out T) {
|
|
32
|
+
```
|
|
33
|
+
#arch: c
|
|
34
|
+
return __atomic_load_n(&self->word, __ATOMIC_SEQ_CST);
|
|
35
|
+
```
|
|
36
|
+
```
|
|
37
|
+
#arch: aarch64
|
|
38
|
+
// Load-acquire takes no offset — compute the word's address first.
|
|
39
|
+
add x0, x0, #8
|
|
40
|
+
ldar x0, [x0]
|
|
41
|
+
```
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
pub func store = (ref self, T v) {
|
|
45
|
+
```
|
|
46
|
+
#arch: c
|
|
47
|
+
__atomic_store_n(&self->word, v, __ATOMIC_SEQ_CST);
|
|
48
|
+
```
|
|
49
|
+
```
|
|
50
|
+
#arch: aarch64
|
|
51
|
+
add x8, x0, #8
|
|
52
|
+
stlr x1, [x8]
|
|
53
|
+
```
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Adds v and returns the PREVIOUS value (fetch-and-add).
|
|
57
|
+
pub func fetch_add = (ref self, T v, out T) {
|
|
58
|
+
```
|
|
59
|
+
#arch: c
|
|
60
|
+
return __atomic_fetch_add(&self->word, v, __ATOMIC_SEQ_CST);
|
|
61
|
+
```
|
|
62
|
+
```
|
|
63
|
+
#arch: aarch64
|
|
64
|
+
add x8, x0, #8
|
|
65
|
+
ldaddal x1, x0, [x8]
|
|
66
|
+
```
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Replaces the value and returns the PREVIOUS one.
|
|
70
|
+
pub func swap = (ref self, T v, out T) {
|
|
71
|
+
```
|
|
72
|
+
#arch: c
|
|
73
|
+
return __atomic_exchange_n(&self->word, v, __ATOMIC_SEQ_CST);
|
|
74
|
+
```
|
|
75
|
+
```
|
|
76
|
+
#arch: aarch64
|
|
77
|
+
add x8, x0, #8
|
|
78
|
+
swpal x1, x0, [x8]
|
|
79
|
+
```
|
|
80
|
+
}
|
|
81
|
+
}
|
package/core/System/Awaitable.nm
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// Awaitable — the consumption side of the async model (ASYNC.md,
|
|
2
2
|
// "Awaitable trait"). Park-flavored by design: `wait` MAY park the current
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
3
|
+
// context (inside a fiber it parks the fiber and frees its worker; on a raw
|
|
4
|
+
// thread it blocks). A Rust-style poll/Waker trait is deliberately rejected —
|
|
5
|
+
// it would smuggle the state-machine transform back in through the trait
|
|
6
|
+
// definition.
|
|
7
7
|
//
|
|
8
8
|
// `Task<T>` is the conformer: `Thread(...).start()` and `Fiber(...).start()`
|
|
9
9
|
// both yield one, so a generic helper taking `Awaitable` waits on either.
|
|
@@ -11,11 +11,18 @@
|
|
|
11
11
|
// start-optional lifecycles are legitimate for user primitives; must-start
|
|
12
12
|
// is a per-struct `#destroy` contract (see Thread.nm / Fiber.nm and
|
|
13
13
|
// ASYNC.md).
|
|
14
|
+
//
|
|
15
|
+
// `wait` takes the receiver by VALUE (bare `self`, not `ref self`): waiting
|
|
16
|
+
// never mutates the handle — it parks on the future the handle NAMES. That
|
|
17
|
+
// makes the bare `Awaitable` parameter (the T3 stack-copy box — zero
|
|
18
|
+
// malloc, caller keeps ownership) the sanctioned dispatch form; a `ref
|
|
19
|
+
// self` wait would be a mutating call, and through a boxed mv struct
|
|
20
|
+
// the mutation spellings cannot cross a borrow (STRUCT_PLAN_3 B4/T2).
|
|
14
21
|
|
|
15
22
|
/**
|
|
16
23
|
* Something whose completion can be waited on (may park the current context)
|
|
17
24
|
**/
|
|
18
25
|
pub trait Awaitable {
|
|
19
|
-
func wait = (
|
|
26
|
+
func wait = (self) {
|
|
20
27
|
}
|
|
21
28
|
}
|
package/core/System/BigInt.nm
CHANGED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// Box<T> (STRUCT_PLAN_3 B2) — a mv pointer to a heap-allocated T: the
|
|
2
|
+
// cycle-breaking and stable-address shape for mv structs.
|
|
3
|
+
//
|
|
4
|
+
// mv struct Node {
|
|
5
|
+
// var int value
|
|
6
|
+
// var Box<Node>? next
|
|
7
|
+
// }
|
|
8
|
+
//
|
|
9
|
+
// DECLARED as `mv struct` (R tranche 4 — the `class` keyword is
|
|
10
|
+
// retiring), but the runtime representation is unchanged: ONE POINTER at
|
|
11
|
+
// runtime (the checker stamps the wrapper's storage class at gather time —
|
|
12
|
+
// a compiler-generated wrapper holding the value inline). `Box<T>?` is the
|
|
13
|
+
// natural null slot (B6's null-pointer lowering), construction mallocs the
|
|
14
|
+
// wrapper and moves the value in, and destroy reclaims the pointee. Field
|
|
15
|
+
// and method access THROUGH a box is transparent — the checker rewrites
|
|
16
|
+
// `box.field` to `box.value.field` — so the wrapper never leaks into user
|
|
17
|
+
// code. The declared `mv` is the move-only marker (boxing TAKES
|
|
18
|
+
// OWNERSHIP of the pointee); the stamped storage class is the shape.
|
|
19
|
+
//
|
|
20
|
+
// `T` is a value struct. (Boxing a reference is pointless — trait handles
|
|
21
|
+
// are already boxed.)
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* A mv pointer to a heap-allocated value — the cycle-breaking shape
|
|
25
|
+
**/
|
|
26
|
+
pub mv struct Box<T> {
|
|
27
|
+
// The wrapped value. Access through the box is transparent: the checker
|
|
28
|
+
// rewrites `box.f` to `box.__value.f`, so this field never appears in
|
|
29
|
+
// user code (the `__` prefix keeps it out of every legal field name).
|
|
30
|
+
// `mv` spelling: boxing TAKES OWNERSHIP of the pointee.
|
|
31
|
+
mv T __value
|
|
32
|
+
}
|
package/core/System/Buffer.nm
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// class, the monomorphizer rewrites a Buffer<T> field to ClassBuffer<T>.
|
|
4
4
|
//
|
|
5
5
|
// Public API is the size-aware family (`alloc`/`grow`/`load`/`replace`/
|
|
6
|
-
// `
|
|
6
|
+
// `mv`/`modify`/`shift`/`slice` plus the readonly `cap`). `replace` is the
|
|
7
7
|
// public write — it reclaims the displaced element, so it is correct on both
|
|
8
8
|
// fresh and occupied slots. `store` (which assumes a fresh slot and leaks on
|
|
9
9
|
// overwrite), `zero`, the fixed-width `*_u32`/`*_int`/`*_float` primitives,
|
|
@@ -288,7 +288,7 @@ pub struct Buffer<T> {
|
|
|
288
288
|
}
|
|
289
289
|
|
|
290
290
|
// Store `val` into slot `i`, which MUST be fresh (calloc'd, or zeroed by
|
|
291
|
-
//
|
|
291
|
+
// mv). For a mv T (string, or a struct with string fields) the
|
|
292
292
|
// specialised body deep-copies the incoming value but does NOT free the
|
|
293
293
|
// slot's previous occupant — a second `store` at an occupied index
|
|
294
294
|
// leaks it. To overwrite a live slot use replace (frees the displaced
|
|
@@ -337,11 +337,11 @@ pub struct Buffer<T> {
|
|
|
337
337
|
}
|
|
338
338
|
|
|
339
339
|
// Move a value out of a T_SIZE-sized slot: return it and zero the slot so the
|
|
340
|
-
// buffer no longer holds a (possibly
|
|
340
|
+
// buffer no longer holds a (possibly mv) copy. Declared `mv out T` so
|
|
341
341
|
// the result is treated as OWNED by the borrow checker — the slot is
|
|
342
342
|
// relinquished, so the caller is the sole remaining owner. The value
|
|
343
343
|
// counterpart of ClassBuffer.move_int — used by List.pop for any value T.
|
|
344
|
-
inline func move = (ref self, int i: i >= 0 && i < self.cap,
|
|
344
|
+
inline func move = (ref self, int i: i >= 0 && i < self.cap, mv out T) {
|
|
345
345
|
```
|
|
346
346
|
#arch: c
|
|
347
347
|
T* _slots = (T*)(unsigned long long)self->data;
|
|
@@ -404,8 +404,8 @@ pub struct Buffer<T> {
|
|
|
404
404
|
// aliases the slot's own copy (the round-trip case) is kept, not freed or
|
|
405
405
|
// re-copied. Unlike store this never leaks on overwrite, and unlike
|
|
406
406
|
// replace it is safe to derive the new value from the old one. The
|
|
407
|
-
// returned struct's
|
|
408
|
-
// the corresponding input fields (no cross-field aliasing). For
|
|
407
|
+
// returned struct's mv fields must be fresh, null, or identical to
|
|
408
|
+
// the corresponding input fields (no cross-field aliasing). For mv
|
|
409
409
|
// elements (string / structs with string fields) the build phase replaces
|
|
410
410
|
// this body with a specialized one (see owning_buffer_specialize.ts);
|
|
411
411
|
// this raw form is correct for trivially-copyable elements only. NOT
|
|
@@ -493,7 +493,7 @@ pub struct Buffer<T> {
|
|
|
493
493
|
|
|
494
494
|
// Store into a T_SIZE-sized slot, overwriting the previous value. For
|
|
495
495
|
// trivially-destructible value structs this is a plain overwrite. For
|
|
496
|
-
//
|
|
496
|
+
// mv value structs (with string fields), the build phase replaces this
|
|
497
497
|
// body with a specialised version that calls T_destroy on the old slot
|
|
498
498
|
// value, then deep-copies (strdup per string field). See
|
|
499
499
|
// owning_buffer_specialize.ts (both backends). ClassBuffer.replace
|
|
@@ -541,12 +541,12 @@ pub struct Buffer<T> {
|
|
|
541
541
|
|
|
542
542
|
// Move-taking write, mirroring `List.set`: the caller's value is
|
|
543
543
|
// consumed and an independent copy is installed in the slot (replace
|
|
544
|
-
// deep-copies
|
|
544
|
+
// deep-copies mv elements). The mv entry point a caller uses
|
|
545
545
|
// when it wants to hand over a value — `replace`'s by-value parameter
|
|
546
|
-
// cannot accept the `
|
|
546
|
+
// cannot accept the `mv` keyword, so a borrow being transferred
|
|
547
547
|
// (e.g. a loaded element) must go through here rather than be bound to
|
|
548
548
|
// a freed local.
|
|
549
|
-
pub func set = (ref self, int i: i >= 0 && i < self.cap,
|
|
549
|
+
pub func set = (ref self, int i: i >= 0 && i < self.cap, mv T value) {
|
|
550
550
|
self.replace(i, value)
|
|
551
551
|
}
|
|
552
552
|
|
|
@@ -555,7 +555,7 @@ pub struct Buffer<T> {
|
|
|
555
555
|
// ownership-transferring primitive behind backward-shift deletion
|
|
556
556
|
// (Map/Set.remove) — unlike store, which gives the slot its own deep
|
|
557
557
|
// copy and leaves the source slot intact, a shift transfers the source's
|
|
558
|
-
// heap pointers wholesale. For
|
|
558
|
+
// heap pointers wholesale. For mv elements (string / value structs
|
|
559
559
|
// with string fields) the build replaces this body with a specialized
|
|
560
560
|
// version that also reclaims dst's previous value first (see
|
|
561
561
|
// owning_buffer_specialize.ts); this raw form is correct for
|
|
@@ -584,7 +584,7 @@ pub struct Buffer<T> {
|
|
|
584
584
|
b.eq .Lbuffer_shift_T_end
|
|
585
585
|
cmp x3, #8
|
|
586
586
|
b.gt .Lbuffer_shift_T_copy
|
|
587
|
-
// Width-matched
|
|
587
|
+
// Width-matched mv: an 8-byte access on a sub-8 element would
|
|
588
588
|
// clobber the following slot (and run past the slab's end).
|
|
589
589
|
cmp x3, #1
|
|
590
590
|
b.eq .Lbuffer_shift_T_1
|
|
@@ -626,7 +626,7 @@ pub struct Buffer<T> {
|
|
|
626
626
|
```
|
|
627
627
|
}
|
|
628
628
|
|
|
629
|
-
// A non-
|
|
629
|
+
// A non-mv (ptr, len) slice [start, end) over this buffer's element
|
|
630
630
|
// storage, returned as a `view T`. It borrows from self: it may not outlive
|
|
631
631
|
// self's scope and is invalidated if self is reassigned. This is the single
|
|
632
632
|
// primitive every other slice (Array<T>, List<T>, user containers) builds
|
|
@@ -655,13 +655,13 @@ pub struct Buffer<T> {
|
|
|
655
655
|
```
|
|
656
656
|
}
|
|
657
657
|
|
|
658
|
-
// Internal: zeroes slots without reclaiming
|
|
658
|
+
// Internal: zeroes slots without reclaiming mv elements.
|
|
659
659
|
internal inline func zero = (ref self, int len) {
|
|
660
660
|
memset(self.data, 0, len * T_SIZE)
|
|
661
661
|
}
|
|
662
662
|
|
|
663
663
|
// An independent copy: fresh slab, per-element deep copies. Each slot
|
|
664
|
-
// round-trips load→replace through a local, so
|
|
664
|
+
// round-trips load→replace through a local, so mv element types get
|
|
665
665
|
// the same treatment a List<T>.copy slot does (the load materializes an
|
|
666
666
|
// owned copy, replace gives the destination its own). Class elements
|
|
667
667
|
// never reach this body (a `Buffer<T>` with T a class monomorphizes to
|
|
@@ -672,10 +672,10 @@ pub struct Buffer<T> {
|
|
|
672
672
|
dst.alloc(self.cap)
|
|
673
673
|
var i = 0
|
|
674
674
|
while i < self.cap {
|
|
675
|
-
// Bind the loaded element and hand it to `set` (
|
|
675
|
+
// Bind the loaded element and hand it to `set` (mv): for a
|
|
676
676
|
// string element load returns a BORROW of the source slot, and
|
|
677
|
-
// the
|
|
678
|
-
// reclaim (which would free the source's buffer); for
|
|
677
|
+
// the mv transfers it into the write without a scope-exit
|
|
678
|
+
// reclaim (which would free the source's buffer); for a mv
|
|
679
679
|
// struct element load returns an owned copy that transfers the
|
|
680
680
|
// same way. Mirrors List.copy's `var T v = self.at(i)` +
|
|
681
681
|
// `dst.push(v)`.
|