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.
Files changed (71) hide show
  1. package/NOMEN_AGENTS.md +27 -13
  2. package/audit_runtime.c +2 -0
  3. package/core/System/Arena.nm +4 -4
  4. package/core/System/Array.nm +108 -78
  5. package/core/System/Atomic.nm +81 -0
  6. package/core/System/Awaitable.nm +12 -5
  7. package/core/System/BigInt.nm +1 -1
  8. package/core/System/Box.nm +32 -0
  9. package/core/System/Buffer.nm +47 -18
  10. package/core/System/Channel.nm +27 -11
  11. package/core/System/ClassBuffer.nm +24 -28
  12. package/core/System/Console.nm +4 -4
  13. package/core/System/Controls/Button.nm +8 -3
  14. package/core/System/Controls/CheckBox.nm +8 -3
  15. package/core/System/Controls/Container.nm +18 -11
  16. package/core/System/Controls/Layout.nm +1 -1
  17. package/core/System/Controls/Text.nm +9 -4
  18. package/core/System/Controls/TextBox.nm +8 -3
  19. package/core/System/Controls/Window.nm +10 -3
  20. package/core/System/Fiber.nm +6 -2
  21. package/core/System/Graph.nm +2 -2
  22. package/core/System/Init.nm +2 -2
  23. package/core/System/LinkedList.nm +2 -2
  24. package/core/System/List.nm +7 -7
  25. package/core/System/Map.nm +10 -7
  26. package/core/System/Math.nm +1 -1
  27. package/core/System/Mutex.nm +14 -2
  28. package/core/System/Random.nm +7 -1
  29. package/core/System/Result.nm +1 -1
  30. package/core/System/Sendable.nm +2 -2
  31. package/core/System/Set.nm +6 -6
  32. package/core/System/Spawnable.nm +9 -9
  33. package/core/System/Stream/Directory.nm +1 -2
  34. package/core/System/Stream/File.nm +8 -9
  35. package/core/System/Stream/Http.nm +2 -3
  36. package/core/System/Stream/Tcp.nm +13 -8
  37. package/core/System/String.nm +22 -12
  38. package/core/System/StringBuilder.nm +2 -2
  39. package/core/System/Task.nm +21 -29
  40. package/core/System/Test.nm +10 -4
  41. package/core/System/Text/CharIndex.nm +2 -3
  42. package/core/System/Text/Json.nm +8 -12
  43. package/core/System/Text/JsonTree.nm +4 -4
  44. package/core/System/Text/Regex.nm +7 -7
  45. package/core/System/Text/Utf8.nm +2 -3
  46. package/core/System/Thread.nm +9 -4
  47. package/core/System/Tree.nm +2 -2
  48. package/core/System/Viewable.nm +2 -2
  49. package/core/System/WeakHandle.nm +56 -0
  50. package/core/System/bool.nm +1 -1
  51. package/core/System/char.nm +1 -1
  52. package/core/System/float.nm +1 -1
  53. package/core/System/float32.nm +1 -1
  54. package/core/System/float64.nm +1 -1
  55. package/core/System/int.nm +1 -1
  56. package/core/System/int16.nm +1 -1
  57. package/core/System/int32.nm +1 -1
  58. package/core/System/int64.nm +1 -1
  59. package/core/System/int8.nm +1 -1
  60. package/core/System/ufloat.nm +1 -1
  61. package/core/System/ufloat32.nm +1 -1
  62. package/core/System/ufloat64.nm +1 -1
  63. package/core/System/uint.nm +1 -1
  64. package/core/System/uint16.nm +1 -1
  65. package/core/System/uint32.nm +1 -1
  66. package/core/System/uint64.nm +1 -1
  67. package/core/System/uint8.nm +1 -1
  68. package/core/docs/System/Result.md +2 -2
  69. package/dist/index.mjs +28088 -24186
  70. package/package.json +1 -1
  71. 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
- else { ... }
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
- | `move T` | transfer ownership; caller writes `move` |
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/class
137
- (and its `extend`s). `const` fields are immutable everywhere.
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 `must_use`: a call returning it must bind or match
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
- `must_use` modifier.
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 int ptr // reference to an int
204
+ ref Box q = p // mv-struct alias — must be spelled (no plain copies)
200
205
  ```
201
206
 
202
- Keywords (`out`, `in`, `match`, `if`, `of`, `as`, `swap`, …) are reserved and
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 and classes
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
- class Node { // class = heap-allocated, reference semantics
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);
@@ -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-owning handle to an `Arena<T>` slot. The generation makes a
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, move T value, out ArenaRef<T>) {
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, move T value) {
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
  }
@@ -92,7 +92,7 @@ pub struct Array<T>: Viewable {
92
92
  }
93
93
  }
94
94
 
95
- // A non-owning (ptr, len) slice [start, end) over self's inline element
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
- #arch: aarch64
144
- // x0 = value, x1 = count
145
- // Returns x0 = heap-allocated array pointer ([ptr]=length, [ptr+8..]=data).
146
- // This is a regular static function: the caller receives the pointer in x0,
147
- // so no sret/x8 buffer is involved.
148
- stp x19, x20, [sp, #-16]!
149
- stp x1, x0, [sp, #-16]! // [sp]=count, [sp+8]=value
150
-
151
- // ptr = malloc(8 + count * T_SIZE)
152
- mov x2, #T_SIZE
153
- mul x0, x1, x2
154
- add x0, x0, #8
155
- bl _malloc
156
- mov x19, x0 // x19 = ptr
157
-
158
- // Store the length prefix and set up the data area.
159
- ldr x1, [sp] // count
160
- str x1, [x19] // [ptr] = count
161
- add x20, x19, #8 // x20 = data area
162
-
163
- cbz x1, .Larray_with_end
164
-
165
- ldr x0, [sp, #8] // value
166
- mov x2, #0 // i = 0
167
- .Larray_with_loop:
168
- mov x3, #T_SIZE
169
- mul x4, x2, x3 // offset = i * T_SIZE
170
- add x5, x20, x4 // addr = data + offset
171
-
172
- // Store the value (in x0) into [x5] using a width that matches
173
- // T_SIZE. For T_SIZE <= 8 the value is in x0 directly (char/int/
174
- // pointer); for T_SIZE > 8 (struct) x0 is a pointer to the bytes
175
- // and we copy them one by one.
176
- cmp x3, #8
177
- b.gt .Larray_with_copy_loop
178
- cmp x3, #4
179
- b.eq .Larray_with_store4
180
- cmp x3, #2
181
- b.eq .Larray_with_store2
182
- cmp x3, #1
183
- b.eq .Larray_with_store1
184
- // T_SIZE == 8 (int/string-ptr/class-ptr): single 8-byte store
185
- str x0, [x5]
186
- b .Larray_with_next
187
- .Larray_with_store4:
188
- str w0, [x5]
189
- b .Larray_with_next
190
- .Larray_with_store2:
191
- strh w0, [x5]
192
- b .Larray_with_next
193
- .Larray_with_store1:
194
- strb w0, [x5]
195
- b .Larray_with_next
196
-
197
- .Larray_with_copy_loop:
198
- // Struct element (T_SIZE > 8): x0 is a pointer to the bytes — copy
199
- // them one by one so adjacent slots aren't clobbered.
200
- mov x6, #0 // j = 0
201
- .Larray_with_byte:
202
- ldrb w7, [x0, x6]
203
- strb w7, [x5, x6]
204
- add x6, x6, #1
205
- cmp x6, x3
206
- blt .Larray_with_byte
207
-
208
- .Larray_with_next:
209
- add x2, x2, #1
210
- ldr x1, [sp]
211
- cmp x2, x1
212
- blt .Larray_with_loop
213
-
214
- .Larray_with_end:
215
- mov x0, x19 // return heap pointer
216
- add sp, sp, #16
217
- ldp x19, x20, [sp], #16
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
+ }
@@ -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
- // execution context (inside a fiber it parks the fiber and frees its
4
- // worker; on a raw thread it blocks). A Rust-style poll/Waker trait is
5
- // deliberately rejected — it would smuggle the state-machine transform back
6
- // in through the trait definition.
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 = (ref self) {
26
+ func wait = (self) {
20
27
  }
21
28
  }
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * An arbitrary-precision integer
3
3
  **/
4
- pub struct BigInt {
4
+ pub mv struct BigInt {
5
5
  var sign = 0
6
6
  var len = 0
7
7
  var digits = Buffer<int>()
@@ -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
+ }