nomen-lang 0.6.12 → 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 +47 -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 +28088 -24186
- 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
|
+
}
|