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.
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 +18 -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 +29031 -25546
  70. package/package.json +1 -1
  71. package/src/test.ts +26 -11
@@ -28,5 +28,5 @@ pub struct Math {
28
28
  }
29
29
 
30
30
  // libm natural logarithm — extern.
31
- extern func log = (float x, out float)
31
+ foreign func log = (float x, out float)
32
32
  }
@@ -10,16 +10,28 @@
10
10
  // observes the cancel flag at its next checkpoint after acquiring — so a
11
11
  // matching unlock is always sound.
12
12
 
13
+ // Mutex is an OWNING STRUCT (STRUCT_PLAN_3 Phase R, tranche 3 — the shape
14
+ // menu): the single field is an opaque handle, so the value is movable and
15
+ // its `#destroy` closes what the handle names. Moving a Mutex transfers the
16
+ // handle; there is no aliasing copy to misuse. Sharing one across tasks
17
+ // rides the checked region borrows (`async { }`) — the lock/unlock methods
18
+ // take the receiver bare (struct receivers travel by address), so a locked
19
+ // section mutates the kernel object the handle names, never the binding.
20
+
13
21
  /**
14
22
  * A pthread-backed mutual-exclusion lock for protecting shared mutable state
15
23
  **/
16
- pub class Mutex: Sendable {
24
+ pub mv struct Mutex {
17
25
  pub var uint64 handle = 0
18
26
 
19
27
  pub func #init = (self) {
28
+ // The constructor's `self` is the INLINE instance (a local value in the
29
+ // C lowering — methods take the pointer form, the ctor does not), so the
30
+ // C body uses the `.` accessor; the aarch64 body writes through x19 =
31
+ // &instance as always.
20
32
  ```
21
33
  #arch: c
22
- self->handle = (unsigned long long)__nomen_mutex_create();
34
+ self.handle = (unsigned long long)__nomen_mutex_create();
23
35
  ```
24
36
  ```
25
37
  #arch: aarch64
@@ -15,10 +15,16 @@
15
15
  //
16
16
  import Time
17
17
 
18
+ // Random is an OWNING STRUCT (STRUCT_PLAN_3 Phase R, tranche 3 — the shape
19
+ // menu): bindings mv instead of alias. The state is a bare uint64, so the
20
+ // value is freely movable (and trivially copyable in principle — `mv`
21
+ // is the uniform spelling for the launch/IO primitives; a `ref Random`
22
+ // parameter is the read-only borrow that keeps one stream per binding).
23
+
18
24
  /**
19
25
  * A fast splitmix64 pseudo-random number generator
20
26
  **/
21
- pub class Random {
27
+ pub mv struct Random {
22
28
  // The raw 64-bit generator state (seeded from the wall clock by the
23
29
  // no-arg constructor, or an explicit seed for reproducible sequences).
24
30
  pub var uint64 state = 0
@@ -1,4 +1,4 @@
1
- pub must_use enum Result<T, E> {
1
+ pub checked enum Result<T, E> {
2
2
  case ok(T value)
3
3
  case error(E error)
4
4
  }
@@ -1,9 +1,9 @@
1
- // Marker trait for types whose values are safe to move across task boundaries.
1
+ // Marker trait for types whose values are safe to mv across task boundaries.
2
2
  // See ASYNC.md for the design. Auto-derivable in a follow-up; for now, structs
3
3
  // opt in explicitly with `: Sendable`.
4
4
 
5
5
  /**
6
- * A marker trait for values that are safe to move across task boundaries
6
+ * A marker trait for values that are safe to mv across task boundaries
7
7
  **/
8
8
  pub trait Sendable {
9
9
  }
@@ -7,12 +7,12 @@
7
7
  * slot indexing is a mask (`hash & (cap - 1)`) — correct for negative hashes
8
8
  * too (two's-complement `&` already lands in `[0, cap)`).
9
9
  **/
10
- pub struct Set<T: Hashable + Equatable> {
10
+ pub mv struct Set<T: Hashable + Equatable> {
11
11
  var length = 0
12
12
  var slots = Buffer<T>()
13
13
  var used = Buffer<int>()
14
14
 
15
- pub func add = (ref self, move T value) {
15
+ pub func add = (ref self, mv T value) {
16
16
  var int cap = self.slots.cap
17
17
  if cap == 0 {
18
18
  cap = self.slots.alloc(8)
@@ -148,8 +148,8 @@ pub struct Set<T: Hashable + Equatable> {
148
148
  }
149
149
 
150
150
  func rehash = (ref self, int new_cap) {
151
- var Buffer<T> old_slots = move self.slots swap Buffer<T>()
152
- var Buffer<int> old_used = move self.used swap Buffer<int>()
151
+ var Buffer<T> old_slots = mv self.slots swap Buffer<T>()
152
+ var Buffer<int> old_used = mv self.used swap Buffer<int>()
153
153
  var int old_cap = old_slots.cap
154
154
 
155
155
  self.slots.alloc(new_cap)
@@ -163,12 +163,12 @@ pub struct Set<T: Hashable + Equatable> {
163
163
  // is owned by exactly one buffer (the new self) afterwards.
164
164
  // load_T would leave the old slot referencing the same class
165
165
  // pointer — a double-free when old_slots is destroyed. The
166
- // result is bound to a local so an owning-element copy (e.g. a
166
+ // result is bound to a local so a mv-element copy (e.g. a
167
167
  // `Buffer<string>` slot, which store_T strdup's) is freed here
168
168
  // once the new buffer has made its own copy — passing it inline
169
169
  // would orphan the move_T result.
170
170
  var T v = old_slots.move(i)
171
- self.add(move v)
171
+ self.add(mv v)
172
172
  }
173
173
  }
174
174
  }
@@ -1,18 +1,18 @@
1
1
  // Spawnable — the trait role of a deferred-work LAUNCHER (the C# shape;
2
- // see docs/ASYNC.md, "Design decisions"). A Spawnable class packs a deferred unit of work
3
- // (the construction special form) and its `start` submits it, yielding the
4
- // library-owned `Task<T>` handle — so the await half is guaranteed by the
5
- // return type and each type carries exactly one role: `Thread<T>`/`Fiber<T>`
6
- // are spawnable, `Task<T>` is awaitable. `Awaitable` stays non-generic (its
7
- // `wait` does not mention T); `Spawnable` is generic because `start`'s
8
- // return type does.
2
+ // see docs/ASYNC.md, "Design decisions"). A Spawnable type packs a deferred
3
+ // unit of work (the construction special form) and its `start` submits it,
4
+ // yielding the library-owned `Task<T>` handle — so the await half is
5
+ // guaranteed by the return type and each type carries exactly one role:
6
+ // `Thread<T>`/`Fiber<T>` are spawnable, `Task<T>` is awaitable.
7
+ // `Awaitable` stays non-generic (its `wait` does not mention T);
8
+ // `Spawnable` is generic because `start`'s return type does.
9
9
  //
10
10
  // `detach` is the daemon role: run the work unjoinable, statement only.
11
11
 
12
12
  /**
13
- * A class that can launch a deferred call and hand back its Task<T> handle
13
+ * A type that can launch a deferred call and hand back its Task<T> handle
14
14
  **/
15
15
  pub trait Spawnable<T> {
16
- func start = (ref self, move out Task<T>)
16
+ func start = (ref self, mv out Task<T>)
17
17
  func detach = (ref self)
18
18
  }
@@ -28,10 +28,9 @@ pub struct Directory {
28
28
  case err == 66 {
29
29
  return DirectoryError.not_empty
30
30
  }
31
- else {
31
+ } else {
32
32
  return DirectoryError.other
33
33
  }
34
- }
35
34
  }
36
35
 
37
36
  // (static) Create a directory at `path`; returns errno (0 on success).
@@ -56,10 +56,9 @@ pub struct File {
56
56
  case err == 17 {
57
57
  return FileError.already_exists
58
58
  }
59
- else {
59
+ } else {
60
60
  return FileError.other
61
61
  }
62
- }
63
62
  }
64
63
 
65
64
  // Open the file at `path` with a C mode string ("r", "w", "a", …).
@@ -157,9 +156,9 @@ pub struct File {
157
156
  }
158
157
 
159
158
  // Read every byte from the current position to EOF and return it. Sets
160
- // `eof` and `error`. Declared `move out string`: the raw body mallocs a
159
+ // `eof` and `error`. Declared `mv out string`: the raw body mallocs a
161
160
  // fresh buffer and hands ownership to the caller.
162
- func raw_read_all = (ref self, move out string) {
161
+ func raw_read_all = (ref self, mv out string) {
163
162
  ```
164
163
  #arch: c
165
164
  FILE *f = (FILE*)(unsigned long long)self->handle;
@@ -272,8 +271,8 @@ pub struct File {
272
271
 
273
272
  // Read up to the next '\n' (the newline is not included). Sets `eof`
274
273
  // when the end of the file is reached and `error` on failure. The raw
275
- // body mallocs a fresh buffer — `move out string` transfers it.
276
- func raw_read_line = (ref self, move out string) {
274
+ // body mallocs a fresh buffer — `mv out string` transfers it.
275
+ func raw_read_line = (ref self, mv out string) {
277
276
  ```
278
277
  #arch: c
279
278
  FILE *f = (FILE*)(unsigned long long)self->handle;
@@ -416,8 +415,8 @@ pub struct File {
416
415
 
417
416
  // Read up to `size` bytes from the current position. Sets `eof` if fewer
418
417
  // than `size` bytes remain and `error` on failure. The raw body mallocs a
419
- // fresh buffer — `move out string` transfers it.
420
- func raw_read_chunk = (ref self, int size, move out string) {
418
+ // fresh buffer — `mv out string` transfers it.
419
+ func raw_read_chunk = (ref self, int size, mv out string) {
421
420
  ```
422
421
  #arch: c
423
422
  FILE *f = (FILE*)(unsigned long long)self->handle;
@@ -590,7 +589,7 @@ pub struct File {
590
589
 
591
590
  // (static) Write `data` to `path` (replacing any existing contents) in
592
591
  // one open/write/close. A write failure is reported as `.error`, not
593
- // silently swallowed (the `must_use` Result discard check forces this to
592
+ // silently swallowed (the `checked` Result discard check forces this to
594
593
  // be deliberate).
595
594
  pub func write_all = (string path, string data, out Result<bool, FileError>) {
596
595
  var Result<bool, FileError> result = .error(FileError.other)
@@ -48,10 +48,9 @@ pub struct Http {
48
48
  case err == 3 {
49
49
  return HttpError.connect
50
50
  }
51
- else {
51
+ } else {
52
52
  return HttpError.other
53
53
  }
54
- }
55
54
  }
56
55
 
57
56
  // Shared socket round-trip used by get/post. `method` is the request
@@ -59,7 +58,7 @@ pub struct Http {
59
58
  // (sends `body` and the Content-* headers). Sets self->status (0 on
60
59
  // failure) and self->error, and returns the response body (empty string
61
60
  // on failure).
62
- func raw_exchange = (ref self, string method, string url, string body, move out string) {
61
+ func raw_exchange = (ref self, string method, string url, string body, mv out string) {
63
62
  // Parse the URL: [http://]host[:port][/path]
64
63
  var host_start = 0
65
64
  if url.starts_with("http://") {
@@ -23,10 +23,15 @@
23
23
  // linked companion on aarch64) — OS socket/DNS interop, where hand-written
24
24
  // per-target assembly is not practical.
25
25
 
26
+ // Tcp is an OWNING STRUCT (STRUCT_PLAN_3 Phase R, tranche 3 — the shape
27
+ // menu): the fields are the fd and an error code — the fd is the handle, so
28
+ // the value is movable and `#destroy` closes it. A moved Tcp transfers the
29
+ // socket; there is no aliasing copy to double-close.
30
+
26
31
  /**
27
32
  * A non-blocking TCP socket whose waits park the calling fiber
28
33
  **/
29
- pub class Tcp: Sendable {
34
+ pub mv struct Tcp {
30
35
  // The underlying file descriptor, -1 when closed/failed.
31
36
  pub var int fd = -1
32
37
  // 0 = ok; 1 = DNS, 2 = socket, 3 = connect, 4 = listen, 5 = closed.
@@ -64,7 +69,7 @@ pub class Tcp: Sendable {
64
69
  // ---- listening ----
65
70
 
66
71
  // Create a listening socket bound to `port` on all interfaces.
67
- pub func listen = (int port, int backlog, move out Tcp) {
72
+ pub func listen = (int port, int backlog, mv out Tcp) {
68
73
  var int handle = Tcp.listen_fd(port, backlog)
69
74
  var t = Tcp(handle)
70
75
  if handle < 0 {
@@ -76,7 +81,7 @@ pub class Tcp: Sendable {
76
81
  // Accept the next connection, parking the fiber while the backlog is
77
82
  // empty. The returned socket is -1 if the listener was cancelled or
78
83
  // failed.
79
- pub func accept = (ref self, move out Tcp) {
84
+ pub func accept = (ref self, mv out Tcp) {
80
85
  var int handle = Tcp.accept_fd(self.fd)
81
86
  var t = Tcp(handle)
82
87
  if handle < 0 {
@@ -88,7 +93,7 @@ pub class Tcp: Sendable {
88
93
  // ---- connecting ----
89
94
 
90
95
  // Connect to `host`:`port`. Check `fd >= 0` (or `error == 0`) for success.
91
- pub func connect = (string host, int port, move out Tcp) {
96
+ pub func connect = (string host, int port, mv out Tcp) {
92
97
  var int handle = Tcp.socket_fd()
93
98
  if handle < 0 {
94
99
  var bad = Tcp(-1)
@@ -115,12 +120,12 @@ pub class Tcp: Sendable {
115
120
 
116
121
  // Receive up to `max` bytes. Parks the fiber until data arrives; returns
117
122
  // the empty string at end-of-stream, on error, or when cancelled.
118
- pub func recv = (ref self, int max, move out string) {
123
+ pub func recv = (ref self, int max, mv out string) {
119
124
  return Tcp.recv_some(self.fd, max)
120
125
  }
121
126
 
122
127
  // Receive until the peer closes (how Connection: close HTTP works).
123
- pub func recv_all = (ref self, move out string) {
128
+ pub func recv_all = (ref self, mv out string) {
124
129
  return Tcp.recv_until_close(self.fd)
125
130
  }
126
131
 
@@ -326,7 +331,7 @@ pub class Tcp: Sendable {
326
331
  ```
327
332
  }
328
333
 
329
- func recv_some = (int handle, int max, move out string) {
334
+ func recv_some = (int handle, int max, mv out string) {
330
335
  ```
331
336
  #arch: c
332
337
  long cap = max > 0 ? max : 1;
@@ -375,7 +380,7 @@ pub class Tcp: Sendable {
375
380
  ```
376
381
  }
377
382
 
378
- func recv_until_close = (int handle, move out string) {
383
+ func recv_until_close = (int handle, mv out string) {
379
384
  ```
380
385
  #arch: c
381
386
  long cap = 4096, len = 0;
@@ -1,7 +1,7 @@
1
1
  // FFI: the C library's heap-copy entry point (string.h). The adapter
2
2
  // marshals the fat string to the thin char* the C ABI expects; the string
3
3
  // result is re-wrapped with its length.
4
- extern func strdup = (string s, move out string)
4
+ foreign func strdup = (string s, mv out string)
5
5
 
6
6
  // Raw memory externs — the allocation primitives every `unsafe` Nomen body
7
7
  // across core (Buffer, StringBuilder, Array, ...) is built on. This file is
@@ -9,17 +9,20 @@ extern func strdup = (string s, move out string)
9
9
  // every core file regardless of pull order. Free externs emit under an
10
10
  // `extern_<name>` label that wraps the identically-named C symbol, so these
11
11
  // can never collide with it (docs/CORE_RAW.md roadmap items 1+2).
12
- extern func calloc = (int count, int size, out uint64)
13
- extern func malloc = (int bytes, out uint64)
14
- extern func realloc = (uint64 address, int bytes, out uint64)
15
- extern func free = (uint64 address)
16
- extern func memcpy = (uint64 dst, uint64 src, int bytes, out uint64)
17
- extern func memset = (uint64 dst, int value, int bytes, out uint64)
12
+ foreign func calloc = (int count, int size, out uint64)
13
+ foreign func malloc = (int bytes, out uint64)
14
+ foreign func realloc = (uint64 address, int bytes, out uint64)
15
+ foreign func free = (uint64 address)
16
+ foreign func memcpy = (uint64 dst, uint64 src, int bytes, out uint64)
17
+ foreign func memset = (uint64 dst, int value, int bytes, out uint64)
18
18
 
19
19
  /**
20
- * A UTF-8 text string — a heap-owned, length-tracked sequence of bytes
20
+ * A UTF-8 text string — a heap-owned, length-tracked sequence of bytes.
21
+ * STRUCT_PLAN_3 D3: `string` is OWNING — the fat (ptr, len) pair owns its
22
+ * bytes, so bindings mv unless a copy is spelled (`.copy()`); `view string`
23
+ * is the cheap read-only passing form.
21
24
  **/
22
- pub struct string: Stringable, Hashable, Equatable {
25
+ pub mv struct string: Stringable, Hashable, Equatable {
23
26
  pub func hash = (self, out uint) {
24
27
  // djb2 over exactly self.length bytes — matches the byte count of
25
28
  // owned strings and literals (views hash the viewed bytes only).
@@ -31,12 +34,19 @@ pub struct string: Stringable, Hashable, Equatable {
31
34
  return h
32
35
  }
33
36
 
34
- // Returns an OWNED byte-exact copy of self (`move out string`). Fresh
37
+ // The uniform mv-type verb: a fresh, owned byte-exact copy of self.
38
+ // The deep-copy spelling the binding/assignment copy errors name — same
39
+ // allocation contract as to_string (fresh buffer, never an alias).
40
+ pub func copy = (self, mv out string) {
41
+ return self.to_string()
42
+ }
43
+
44
+ // Returns an OWNED byte-exact copy of self (`mv out string`). Fresh
35
45
  // allocation, never an alias of self's storage — the result can outlive
36
46
  // the receiver (e.g. escape a match branch that reclaims the scrutinee
37
47
  // temp). Copies by tracked length, so embedded NULs survive (an extern
38
48
  // strdup boundary would re-synthesize length via strlen and truncate).
39
- pub func to_string = (self, move out string) {
49
+ pub func to_string = (self, mv out string) {
40
50
  ```
41
51
  #arch: c
42
52
  // Fat-string ABI: copy self.len bytes; no strlen anywhere.
@@ -90,7 +100,7 @@ pub struct string: Stringable, Hashable, Equatable {
90
100
  panic("index out of range")
91
101
  }
92
102
 
93
- // A non-owning slice [start, end) into self's buffer. Returns a `view
103
+ // A non-mv slice [start, end) into self's buffer. Returns a `view
94
104
  // string` (ptr, len) that borrows from self: it may not outlive self's
95
105
  // scope and is invalidated if self is reassigned. Use .to_string() to
96
106
  // materialize an owned copy.
@@ -178,9 +178,9 @@ pub struct StringBuilder {
178
178
  // measure-and-restart shape: record `length()`, append a section, then
179
179
  // `take_since(mark)` to measure it and re-emit decorated. `mark` clamps
180
180
  // to [0, len]; the returned string is always an independent allocation.
181
- // (`move out string` — the raw body mallocs the result, so the caller
181
+ // (`mv out string` — the raw body mallocs the result, so the caller
182
182
  // owns it by signature, like `Console.read_line`.)
183
- pub func take_since = (ref self, int mark, move out string) {
183
+ pub func take_since = (ref self, int mark, mv out string) {
184
184
  ```
185
185
  #arch: c
186
186
  int start = mark;
@@ -11,13 +11,18 @@
11
11
  // on the first spawn — not in Task's own methods — so that Task can be
12
12
  // generic without duplicating the pool per instantiation.
13
13
  //
14
+ // Task is an OWNING STRUCT (STRUCT_PLAN_3 Phase R, tranche 2 — the shape
15
+ // menu): every field is an opaque handle (uint64/bool), so the handle value
16
+ // is movable and its `#destroy` releases the future reference it holds.
17
+ // Moving a Task transfers that reference; there is no aliasing copy.
18
+ //
14
19
  // See ASYNC.md for the design.
15
20
 
16
21
  /**
17
22
  * A handle to an asynchronously-running computation, backed by a shared thread pool.
18
23
  * Conforms to `Awaitable`: `.wait()` may park the current execution context.
19
24
  **/
20
- pub class Task<T> : Sendable, Awaitable {
25
+ pub mv struct Task<T> : Awaitable {
21
26
  pub var uint64 handle = 0
22
27
  pub var done = false
23
28
  pub var uint64 result_slot = 0
@@ -35,40 +40,27 @@ pub class Task<T> : Sendable, Awaitable {
35
40
  // Block until the task finishes. Idempotent — calling twice is safe, and
36
41
  // a Task captured from a nursery spawn can be waited on here AND joined
37
42
  // by the nursery at block exit (both wait on the same shared future).
38
- pub func wait = (ref self) {
43
+ //
44
+ // The receiver is a bare VALUE self (matching the Awaitable trait):
45
+ // waiting never mutates the handle — it parks on the future the handle
46
+ // names. Struct receivers travel by address regardless (the raw body
47
+ // reads `self->future`), so the ABI is unchanged; the bare spelling is
48
+ // what lets the read-only `Awaitable` dispatch form typecheck.
49
+ // The legacy `pthread_join` fallback is gone — `handle` has been
50
+ // permanently zero since the pool migration.
51
+ pub func wait = (self) {
39
52
  ```
40
53
  #arch: c
41
54
  if (self->future) {
42
55
  __nomen_future_wait((struct nomen_future *)self->future);
43
- } else if (self->handle && !self->done) {
44
- void *ret;
45
- pthread_join((pthread_t)self->handle, &ret);
46
- self->handle = 0;
47
- self->done = 1;
48
56
  }
49
57
  ```
50
58
  ```
51
59
  #arch: aarch64
52
- // if (self->future) goto wait_future
60
+ // if (self->future) __nomen_future_wait(self->future)
53
61
  ldr x0, [x19, #40]
54
- cbz x0, .Lwait_handle
55
- bl ___nomen_future_wait
56
- b .Lwait_done
57
- .Lwait_handle:
58
- // else if (self->handle && !self->done)
59
- ldr x0, [x19, #8]
60
62
  cbz x0, .Lwait_done
61
- ldrb w1, [x19, #16]
62
- cbnz w1, .Lwait_done
63
- // pthread_join((pthread_t)self->handle, &ret)
64
- sub sp, sp, #16
65
- mov x1, sp
66
- bl _pthread_join
67
- add sp, sp, #16
68
- // self->handle = 0; self->done = 1
69
- str xzr, [x19, #8]
70
- mov w0, #1
71
- strb w0, [x19, #16]
63
+ bl ___nomen_future_wait
72
64
  .Lwait_done:
73
65
  ```
74
66
  }
@@ -78,10 +70,10 @@ pub class Task<T> : Sendable, Awaitable {
78
70
  // the full return value — including the 16-byte fat-string pair — and the
79
71
  // trampoline writes the function's return value there before exiting.
80
72
  // The result is MOVED OUT: the slot is zeroed as the value leaves, and
81
- // `move out T` marks the caller as the owner. Call it once; a second call
73
+ // `mv out T` marks the caller as the owner. Call it once; a second call
82
74
  // returns the zero value. For tasks with no return value (void
83
75
  // functions), returns the zero value.
84
- pub func result = (ref self, move out T) {
76
+ pub func result = (ref self, mv out T) {
85
77
  ```
86
78
  #arch: c
87
79
  if (self->future) {
@@ -284,7 +276,7 @@ pub class Task<T> : Sendable, Awaitable {
284
276
  // ---- User-defined async primitive seam (ASYNC.md, "User-defined async
285
277
  // primitives") ----
286
278
  // The construction sugar stores the packed-task machinery in a user
287
- // class's plain uint64 fields (task / result_slot / cancel_flag /
279
+ // spawnable's plain uint64 fields (task / result_slot / cancel_flag /
288
280
  // future); these statics are the library boundary its own launch
289
281
  // methods drive the runtime through — the same calls the generated
290
282
  // Thread/Fiber launch code makes, so a user primitive parks fibers,
@@ -294,7 +286,7 @@ pub class Task<T> : Sendable, Awaitable {
294
286
 
295
287
  // Submit a packed task closure (the `task` field's value) to the shared
296
288
  // worker pool. The closure must come from a construction of an
297
- // Awaitable-conforming class — it is not callable.
289
+ // Awaitable-conforming spawnable — it is not callable.
298
290
  pub func pool_submit = (uint64 closure) {
299
291
  ```
300
292
  #arch: c
@@ -32,6 +32,12 @@
32
32
  //
33
33
  // See TESTING.md for the full design.
34
34
 
35
+ // Tester is an OWNING STRUCT (STRUCT_PLAN_3 Phase R, tranche 3 — the shape
36
+ // menu): the harness builds exactly one per run, so the single-owner rule
37
+ // costs nothing and the record state moves with the value. The string
38
+ // fields (current/bench_label) are normalized heap-owned by the compiler's
39
+ // struct string machinery, so `#destroy` reclaims them cleanly.
40
+
35
41
  /**
36
42
  * Records assertions and benchmarks for one test file.
37
43
  *
@@ -47,7 +53,7 @@
47
53
  * skipped, so a single root cause reports as a single failure. Check
48
54
  * `t.has_failed` to bail out of expensive follow-up work yourself.
49
55
  **/
50
- pub class Tester {
56
+ pub mv struct Tester {
51
57
  // Has the *current* test recorded a failure? Reset by `begin_test`.
52
58
  pub var has_failed = false
53
59
 
@@ -94,7 +100,7 @@ pub class Tester {
94
100
  self.has_failed = false
95
101
  self.passed = 0
96
102
  self.failed = 0
97
- self.current = name
103
+ self.current = name.copy()
98
104
  Console.write_line("\\nomen|start|" + name)
99
105
  self.started = Time.now_ns()
100
106
  }
@@ -211,7 +217,7 @@ pub class Tester {
211
217
  return
212
218
  }
213
219
  self.bench_pending = true
214
- self.bench_label = label
220
+ self.bench_label = label.copy()
215
221
  self.bench_samples = 1000
216
222
  }
217
223
 
@@ -225,7 +231,7 @@ pub class Tester {
225
231
  return
226
232
  }
227
233
  self.bench_pending = true
228
- self.bench_label = label
234
+ self.bench_label = label.copy()
229
235
  self.bench_samples = samples
230
236
  }
231
237
 
@@ -20,7 +20,7 @@ pub struct CharRun {
20
20
  * A run-length character-width index (construct with `CharIndex(text)`,
21
21
  * then `byte_offset_of` / `char_index_of` / `char_at` in O(log runs))
22
22
  **/
23
- pub struct CharIndex {
23
+ pub mv struct CharIndex {
24
24
  var view string source
25
25
  var runs = List<CharRun>()
26
26
  var char_count = 0
@@ -50,7 +50,7 @@ pub struct CharIndex {
50
50
  pos = pos + w
51
51
  chars = chars + 1
52
52
  }
53
- else {
53
+ } else {
54
54
  if open_at >= 0 {
55
55
  self.runs.push(CharRun(open_at, open_start, open_width, open_count))
56
56
  }
@@ -61,7 +61,6 @@ pub struct CharIndex {
61
61
  pos = pos + w
62
62
  chars = chars + 1
63
63
  }
64
- }
65
64
  }
66
65
  if open_at >= 0 {
67
66
  self.runs.push(CharRun(open_at, open_start, open_width, open_count))
@@ -14,9 +14,9 @@
14
14
  **/
15
15
  pub struct Json {
16
16
  // Serialize a string to a JSON string literal, escaping the special
17
- // characters ", \, newline, tab and carriage return. `move out string`:
17
+ // characters ", \, newline, tab and carriage return. `mv out string`:
18
18
  // returns the StringBuilder's owned heap copy.
19
- pub func serialize = (string value, move out string) {
19
+ pub func serialize = (string value, mv out string) {
20
20
  var sb = StringBuilder()
21
21
  sb.append_char('"')
22
22
  var i = 0
@@ -40,18 +40,17 @@ pub struct Json {
40
40
  case code == 13 {
41
41
  sb.append_string("\x5cr")
42
42
  }
43
- else {
43
+ } else {
44
44
  sb.append_char(c)
45
45
  }
46
- }
47
46
  }
48
47
  sb.append_char('"')
49
48
  return sb.to_string()
50
49
  }
51
50
 
52
51
  // Deserialize a JSON string literal to the raw, unescaped value.
53
- // `move out string`: returns the StringBuilder's owned heap copy.
54
- pub func deserialize = (string json, move out string) {
52
+ // `mv out string`: returns the StringBuilder's owned heap copy.
53
+ pub func deserialize = (string json, mv out string) {
55
54
  var sb = StringBuilder()
56
55
  var int len = json.length
57
56
  var i = 0
@@ -90,10 +89,9 @@ pub struct Json {
90
89
  case ec == 92 {
91
90
  sb.append_string("\x5c")
92
91
  }
93
- else {
92
+ } else {
94
93
  sb.append_char(e)
95
94
  }
96
- }
97
95
  }
98
96
  } else {
99
97
  sb.append_char(c)
@@ -165,10 +163,9 @@ func json_parse_string = (string s, ref Buffer<int> pos, int len, ref JsonTree t
165
163
  case ec == 92 {
166
164
  sb.append_string("\x5c")
167
165
  }
168
- else {
166
+ } else {
169
167
  sb.append_char(e)
170
168
  }
171
- }
172
169
  }
173
170
  } else {
174
171
  sb.append_char(c)
@@ -218,11 +215,10 @@ func json_parse_literal = (string s, ref Buffer<int> pos, int len, ref JsonTree
218
215
  tree.set_kind(n, 6)
219
216
  adv = 5
220
217
  }
221
- else {
218
+ } else {
222
219
  tree.set_kind(n, 0)
223
220
  adv = 4
224
221
  }
225
- }
226
222
  pos.store_int(0, i + adv)
227
223
  return n
228
224
  }