@shd101wyy/yo 0.1.28 → 0.1.30

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 (183) hide show
  1. package/.github/skills/yo-async-effects/SKILL.md +15 -15
  2. package/.github/skills/yo-async-effects/async-effects-recipes.md +118 -121
  3. package/.github/skills/yo-core-patterns/core-patterns-cheatsheet.md +33 -13
  4. package/.github/skills/yo-project-workflow/workflow-cheatsheet.md +1 -1
  5. package/.github/skills/yo-syntax/SKILL.md +2 -2
  6. package/.github/skills/yo-syntax/syntax-cheatsheet.md +108 -96
  7. package/README.md +6 -3
  8. package/out/cjs/index.cjs +812 -706
  9. package/out/cjs/yo-cli.cjs +1023 -907
  10. package/out/cjs/yo-lsp.cjs +836 -730
  11. package/out/esm/index.mjs +757 -651
  12. package/out/types/src/codegen/exprs/async.d.ts +2 -0
  13. package/out/types/src/codegen/exprs/await.d.ts +1 -0
  14. package/out/types/src/codegen/exprs/closures.d.ts +4 -0
  15. package/out/types/src/codegen/functions/context.d.ts +6 -0
  16. package/out/types/src/codegen/functions/declarations.d.ts +1 -1
  17. package/out/types/src/doc/model.d.ts +0 -1
  18. package/out/types/src/env.d.ts +2 -2
  19. package/out/types/src/evaluator/builtins/pragma.d.ts +9 -0
  20. package/out/types/src/evaluator/builtins/unsafe.d.ts +8 -0
  21. package/out/types/src/evaluator/context.d.ts +3 -1
  22. package/out/types/src/evaluator/exprs/{escape.d.ts → unwind.d.ts} +1 -1
  23. package/out/types/src/evaluator/index.d.ts +1 -1
  24. package/out/types/src/evaluator/memory-safety.d.ts +14 -0
  25. package/out/types/src/evaluator/types/flowability.d.ts +6 -0
  26. package/out/types/src/evaluator/types/function.d.ts +1 -2
  27. package/out/types/src/evaluator/utils.d.ts +0 -1
  28. package/out/types/src/expr-traversal.d.ts +1 -0
  29. package/out/types/src/expr.d.ts +9 -7
  30. package/out/types/src/public-safe-report.d.ts +19 -0
  31. package/out/types/src/tests/comptime-ref-gate.test.d.ts +1 -0
  32. package/out/types/src/tests/pragma-validation.test.d.ts +1 -0
  33. package/out/types/src/tests/public-safe-report.test.d.ts +1 -0
  34. package/out/types/src/tests/type-representation-pointer.test.d.ts +1 -0
  35. package/out/types/src/tests/unsafe-gate.test.d.ts +1 -0
  36. package/out/types/src/tests/unsafe-report-classify.test.d.ts +1 -0
  37. package/out/types/src/types/creators.d.ts +4 -6
  38. package/out/types/src/types/definitions.d.ts +9 -16
  39. package/out/types/src/types/guards.d.ts +1 -2
  40. package/out/types/src/types/tags.d.ts +0 -1
  41. package/out/types/src/types/utils.d.ts +5 -0
  42. package/out/types/src/unsafe-report.d.ts +29 -0
  43. package/out/types/src/value.d.ts +1 -0
  44. package/out/types/tsconfig.tsbuildinfo +1 -1
  45. package/package.json +1 -1
  46. package/scripts/add-pragma-for-pointer-decls.ts +134 -0
  47. package/scripts/add-pragma.ts +58 -0
  48. package/scripts/migrate-amp-method-calls.ts +186 -0
  49. package/scripts/migrate-clone-calls.ts +93 -0
  50. package/scripts/migrate-get-unwrap.ts +166 -0
  51. package/scripts/migrate-index-patterns.ts +210 -0
  52. package/scripts/migrate-index-trait.ts +142 -0
  53. package/scripts/migrate-iterator.ts +150 -0
  54. package/scripts/migrate-self-ptr.ts +220 -0
  55. package/scripts/migrate-skip-pragmas.ts +109 -0
  56. package/scripts/migrate-tostring.ts +134 -0
  57. package/scripts/trim-pragma.ts +130 -0
  58. package/scripts/wrap-extern-calls.ts +161 -0
  59. package/std/alg/hash.yo +3 -2
  60. package/std/allocator.yo +6 -5
  61. package/std/async.yo +2 -2
  62. package/std/collections/array_list.yo +59 -40
  63. package/std/collections/btree_map.yo +19 -18
  64. package/std/collections/deque.yo +9 -8
  65. package/std/collections/hash_map.yo +101 -13
  66. package/std/collections/hash_set.yo +5 -4
  67. package/std/collections/linked_list.yo +39 -4
  68. package/std/collections/ordered_map.yo +3 -3
  69. package/std/collections/priority_queue.yo +14 -13
  70. package/std/crypto/md5.yo +2 -1
  71. package/std/crypto/random.yo +21 -20
  72. package/std/crypto/sha256.yo +2 -1
  73. package/std/encoding/base64.yo +18 -18
  74. package/std/encoding/hex.yo +5 -5
  75. package/std/encoding/json.yo +62 -13
  76. package/std/encoding/punycode.yo +24 -23
  77. package/std/encoding/toml.yo +4 -3
  78. package/std/encoding/utf16.yo +3 -3
  79. package/std/env.yo +43 -28
  80. package/std/error.yo +15 -3
  81. package/std/fmt/display.yo +2 -2
  82. package/std/fmt/index.yo +6 -5
  83. package/std/fmt/to_string.yo +39 -38
  84. package/std/fmt/writer.yo +9 -8
  85. package/std/fs/dir.yo +61 -66
  86. package/std/fs/file.yo +121 -126
  87. package/std/fs/metadata.yo +13 -18
  88. package/std/fs/temp.yo +35 -30
  89. package/std/fs/walker.yo +14 -19
  90. package/std/gc.yo +1 -0
  91. package/std/glob.yo +7 -7
  92. package/std/http/client.yo +33 -36
  93. package/std/http/http.yo +6 -6
  94. package/std/http/index.yo +4 -4
  95. package/std/imm/list.yo +33 -0
  96. package/std/imm/map.yo +2 -1
  97. package/std/imm/set.yo +1 -0
  98. package/std/imm/sorted_map.yo +1 -0
  99. package/std/imm/sorted_set.yo +1 -0
  100. package/std/imm/string.yo +27 -23
  101. package/std/imm/vec.yo +18 -2
  102. package/std/io/reader.yo +2 -1
  103. package/std/io/writer.yo +3 -2
  104. package/std/libc/assert.yo +1 -0
  105. package/std/libc/ctype.yo +1 -0
  106. package/std/libc/dirent.yo +1 -0
  107. package/std/libc/errno.yo +1 -0
  108. package/std/libc/fcntl.yo +1 -0
  109. package/std/libc/float.yo +1 -0
  110. package/std/libc/limits.yo +1 -0
  111. package/std/libc/math.yo +1 -0
  112. package/std/libc/signal.yo +1 -0
  113. package/std/libc/stdatomic.yo +1 -0
  114. package/std/libc/stdint.yo +1 -0
  115. package/std/libc/stdio.yo +1 -0
  116. package/std/libc/stdlib.yo +1 -0
  117. package/std/libc/string.yo +1 -0
  118. package/std/libc/sys/stat.yo +1 -0
  119. package/std/libc/time.yo +1 -0
  120. package/std/libc/unistd.yo +1 -0
  121. package/std/libc/wctype.yo +1 -0
  122. package/std/libc/windows.yo +2 -0
  123. package/std/log.yo +7 -6
  124. package/std/net/addr.yo +6 -5
  125. package/std/net/dns.yo +13 -16
  126. package/std/net/errors.yo +9 -9
  127. package/std/net/tcp.yo +71 -74
  128. package/std/net/udp.yo +40 -43
  129. package/std/os/signal.yo +5 -5
  130. package/std/path.yo +1 -0
  131. package/std/prelude.yo +377 -200
  132. package/std/process/command.yo +57 -46
  133. package/std/process/index.yo +2 -1
  134. package/std/regex/compiler.yo +10 -9
  135. package/std/regex/index.yo +41 -41
  136. package/std/regex/match.yo +2 -2
  137. package/std/regex/parser.yo +31 -31
  138. package/std/regex/vm.yo +42 -41
  139. package/std/string/string.yo +95 -40
  140. package/std/string/string_builder.yo +9 -9
  141. package/std/string/unicode.yo +50 -49
  142. package/std/sync/channel.yo +2 -1
  143. package/std/sync/cond.yo +5 -4
  144. package/std/sync/mutex.yo +4 -3
  145. package/std/sys/advise.yo +1 -0
  146. package/std/sys/bufio/buf_reader.yo +27 -26
  147. package/std/sys/bufio/buf_writer.yo +22 -21
  148. package/std/sys/clock.yo +1 -0
  149. package/std/sys/copy.yo +1 -0
  150. package/std/sys/dir.yo +10 -9
  151. package/std/sys/dns.yo +6 -5
  152. package/std/sys/errors.yo +12 -12
  153. package/std/sys/events.yo +1 -0
  154. package/std/sys/externs.yo +38 -37
  155. package/std/sys/file.yo +17 -16
  156. package/std/sys/future.yo +4 -3
  157. package/std/sys/iov.yo +1 -0
  158. package/std/sys/mmap.yo +1 -0
  159. package/std/sys/path.yo +1 -0
  160. package/std/sys/perm.yo +2 -1
  161. package/std/sys/pipe.yo +1 -0
  162. package/std/sys/process.yo +5 -4
  163. package/std/sys/signal.yo +1 -0
  164. package/std/sys/socketpair.yo +1 -0
  165. package/std/sys/sockinfo.yo +1 -0
  166. package/std/sys/statfs.yo +2 -1
  167. package/std/sys/statx.yo +1 -0
  168. package/std/sys/sysinfo.yo +1 -0
  169. package/std/sys/tcp.yo +15 -14
  170. package/std/sys/temp.yo +1 -0
  171. package/std/sys/time.yo +2 -1
  172. package/std/sys/timer.yo +6 -6
  173. package/std/sys/tty.yo +2 -1
  174. package/std/sys/udp.yo +13 -12
  175. package/std/sys/unix.yo +12 -11
  176. package/std/testing/bench.yo +4 -3
  177. package/std/thread.yo +7 -6
  178. package/std/time/datetime.yo +18 -15
  179. package/std/time/duration.yo +11 -10
  180. package/std/time/instant.yo +4 -4
  181. package/std/time/sleep.yo +1 -0
  182. package/std/url/index.yo +5 -5
  183. package/std/worker.yo +4 -3
@@ -0,0 +1,161 @@
1
+ /**
2
+ * plans/EXTERN_UNSAFE_WRAP.md Phase C — apply `unsafe(...)` wraps at
3
+ * every extern "c" call site collected by the evaluator's dump mode.
4
+ *
5
+ * Workflow:
6
+ * 1. YO_EXTERN_WRAP_DUMP_FILE=/tmp/extern_wraps.json ./yo-cli check ./std
7
+ * (the evaluator records each unwrapped call site as JSON)
8
+ * 2. bun run scripts/wrap-extern-calls.ts /tmp/extern_wraps.json --write
9
+ * (this script reads the JSON and applies precise wraps)
10
+ *
11
+ * Dry-run by default. Pass `--write` to apply changes.
12
+ */
13
+ import { readFileSync, writeFileSync } from "node:fs";
14
+ import { fileURLToPath } from "node:url";
15
+
16
+ type Site = {
17
+ modulePath: string;
18
+ characterOffset: number;
19
+ calleeName: string;
20
+ };
21
+
22
+ function findMatchingClose(src: string, openIdx: number): number {
23
+ if (src[openIdx] !== "(") {
24
+ throw new Error(`expected '(' at offset ${openIdx}, got '${src[openIdx]}'`);
25
+ }
26
+ let depth = 0;
27
+ for (let i = openIdx; i < src.length; i++) {
28
+ const c = src[i];
29
+ if (c === "(") depth++;
30
+ else if (c === ")") {
31
+ depth--;
32
+ if (depth === 0) return i;
33
+ }
34
+ }
35
+ throw new Error(`unmatched '(' at offset ${openIdx}`);
36
+ }
37
+
38
+ function alreadyWrapped(src: string, callStart: number): boolean {
39
+ // Look back from callStart past whitespace; check for `unsafe(` immediately
40
+ // before.
41
+ let j = callStart - 1;
42
+ while (j >= 0 && (src[j] === " " || src[j] === "\t" || src[j] === "\n")) {
43
+ j--;
44
+ }
45
+ if (j < 6) return false;
46
+ return src.slice(j - 6, j + 1) === "unsafe(";
47
+ }
48
+
49
+ function findCallOpenParen(src: string, nameStart: number): number {
50
+ // Scan forward from `nameStart` past the function name to find the `(`.
51
+ // The function name is a sequence of identifier chars; the `(` follows
52
+ // (optionally with whitespace).
53
+ let i = nameStart;
54
+ while (i < src.length && /[A-Za-z0-9_]/.test(src[i]!)) i++;
55
+ while (i < src.length && /\s/.test(src[i]!)) i++;
56
+ if (src[i] !== "(") {
57
+ throw new Error(
58
+ `expected '(' after identifier starting at ${nameStart}, got '${src[i]}' at ${i}`
59
+ );
60
+ }
61
+ return i;
62
+ }
63
+
64
+ async function main() {
65
+ const args = process.argv.slice(2);
66
+ const jsonPath = args[0];
67
+ const write = args.includes("--write");
68
+ if (!jsonPath) {
69
+ console.error(
70
+ "usage: bun run scripts/wrap-extern-calls.ts <json-path> [--write]"
71
+ );
72
+ process.exit(1);
73
+ }
74
+ const sites: Site[] = JSON.parse(readFileSync(jsonPath, "utf8"));
75
+
76
+ // Dedupe by (modulePath, characterOffset)
77
+ const seen = new Set<string>();
78
+ const unique: Site[] = [];
79
+ for (const s of sites) {
80
+ const k = `${s.modulePath}@${s.characterOffset}`;
81
+ if (seen.has(k)) continue;
82
+ seen.add(k);
83
+ unique.push(s);
84
+ }
85
+
86
+ // Group by file, sort offsets descending so edits don't shift later
87
+ // offsets in the same file.
88
+ const byFile = new Map<string, Site[]>();
89
+ for (const s of unique) {
90
+ const filePath = fileURLToPath(s.modulePath);
91
+ if (!byFile.has(filePath)) byFile.set(filePath, []);
92
+ byFile.get(filePath)!.push(s);
93
+ }
94
+ for (const list of byFile.values()) {
95
+ list.sort((a, b) => b.characterOffset - a.characterOffset);
96
+ }
97
+
98
+ let totalWraps = 0;
99
+ let totalSkipped = 0;
100
+ const fileStats: { file: string; wraps: number; skipped: number }[] = [];
101
+ for (const [filePath, list] of byFile) {
102
+ let src = readFileSync(filePath, "utf8");
103
+ let wraps = 0;
104
+ let skipped = 0;
105
+ for (const s of list) {
106
+ if (alreadyWrapped(src, s.characterOffset)) {
107
+ skipped++;
108
+ continue;
109
+ }
110
+ let openParen: number;
111
+ try {
112
+ openParen = findCallOpenParen(src, s.characterOffset);
113
+ } catch (e) {
114
+ console.warn(
115
+ `skip ${filePath}@${s.characterOffset} (${s.calleeName}): ${(e as Error).message}`
116
+ );
117
+ skipped++;
118
+ continue;
119
+ }
120
+ let close: number;
121
+ try {
122
+ close = findMatchingClose(src, openParen);
123
+ } catch (e) {
124
+ console.warn(
125
+ `skip ${filePath}@${s.characterOffset} (${s.calleeName}): ${(e as Error).message}`
126
+ );
127
+ skipped++;
128
+ continue;
129
+ }
130
+ src =
131
+ src.slice(0, s.characterOffset) +
132
+ "unsafe(" +
133
+ src.slice(s.characterOffset, close + 1) +
134
+ ")" +
135
+ src.slice(close + 1);
136
+ wraps++;
137
+ }
138
+ if (wraps > 0 && write) {
139
+ writeFileSync(filePath, src);
140
+ }
141
+ if (wraps + skipped > 0) {
142
+ fileStats.push({ file: filePath, wraps, skipped });
143
+ totalWraps += wraps;
144
+ totalSkipped += skipped;
145
+ }
146
+ }
147
+
148
+ fileStats.sort((a, b) => b.wraps - a.wraps);
149
+ for (const s of fileStats) {
150
+ console.log(
151
+ `${s.file}: ${s.wraps} wrap${s.wraps === 1 ? "" : "s"}` +
152
+ (s.skipped ? `, ${s.skipped} skipped` : "")
153
+ );
154
+ }
155
+ console.log(
156
+ `\n${write ? "Applied" : "Would apply"} ${totalWraps} wrap(s) across ${fileStats.length} file(s); ${totalSkipped} skipped.`
157
+ );
158
+ if (!write) console.log("Re-run with --write to apply.");
159
+ }
160
+
161
+ main();
package/std/alg/hash.yo CHANGED
@@ -7,11 +7,12 @@ FNV_OFFSET_BASIS :: u64(14695981039346656037);
7
7
  /// FNV prime constant (0x100000001b3).
8
8
  FNV_PRIME :: u64(1099511628211);
9
9
  /// Hash a byte sequence using FNV-1a.
10
- fnv1a_hash_bytes :: (fn(bytes : *(u8), length : usize) -> u64)({
10
+ fnv1a_hash_bytes :: (fn(bytes : Slice(u8)) -> u64)({
11
11
  hash := FNV_OFFSET_BASIS;
12
+ length := bytes.len();
12
13
  i := usize(0);
13
14
  while(i < length, i = (i + usize(1)), {
14
- byte := (bytes &+ i).*;
15
+ byte := bytes(i);
15
16
  xor := (hash ^ u64(byte));
16
17
  hash = (xor * FNV_PRIME);
17
18
  });
package/std/allocator.yo CHANGED
@@ -1,4 +1,5 @@
1
1
  //! Memory allocation abstractions and global allocator interface.
2
+ pragma(Pragma.AllowUnsafe);
2
3
  { SIZE_MAX } :: import("./libc/stdint.yo");
3
4
  /// Memory allocation error variants.
4
5
  AllocError :: enum(
@@ -39,15 +40,15 @@ size_would_overflow ::
39
40
  /// Implement this trait to provide custom allocation strategies.
40
41
  CustomAllocator :: trait(
41
42
  /// Allocate memory for `count` items of type T.
42
- alloc : (fn(self : *(Self), comptime(T) : Type, count : usize) -> Result(?*(T), AllocError)),
43
+ alloc : (fn(ref(self) : Self, comptime(T) : Type, count : usize) -> Result(?*(T), AllocError)),
43
44
  /// Allocate zeroed memory for `count` items of type T.
44
- alloc_zeroed : (fn(self : *(Self), comptime(T) : Type, count : usize) -> Result(?*(T), AllocError)),
45
+ alloc_zeroed : (fn(ref(self) : Self, comptime(T) : Type, count : usize) -> Result(?*(T), AllocError)),
45
46
  /// Allocate aligned memory for `count` items of type T with the given alignment.
46
- alloc_aligned : (fn(self : *(Self), comptime(T) : Type, count : usize, alignment : usize) -> Result(?*(T), AllocError)),
47
+ alloc_aligned : (fn(ref(self) : Self, comptime(T) : Type, count : usize, alignment : usize) -> Result(?*(T), AllocError)),
47
48
  /// Reallocate memory to `new_count` items, preserving existing data.
48
- realloc : (fn(forall(T : Type), self : *(Self), old_ptr : ?*(T), new_count : usize) -> Result(?*(T), AllocError)),
49
+ realloc : (fn(forall(T : Type), ref(self) : Self, old_ptr : ?*(T), new_count : usize) -> Result(?*(T), AllocError)),
49
50
  /// Deallocate memory pointed to by `ptr`.
50
- dealloc : (fn(forall(T : Type), self : *(Self), ptr : *(T)) -> Result(unit, AllocError))
51
+ dealloc : (fn(forall(T : Type), ref(self) : Self, ptr : *(T)) -> Result(unit, AllocError))
51
52
  );
52
53
  /// Type-erased dynamic allocator wrapping any `CustomAllocator` implementation.
53
54
  Allocator :: Dyn(CustomAllocator);
package/std/async.yo CHANGED
@@ -1,8 +1,8 @@
1
1
  //! Async utilities for cooperative task scheduling.
2
2
  /// Suspend the current async task, allowing other tasks to run on the event loop.
3
3
  /// Implemented as an immediately-completing async block that forces a suspension point.
4
- yield :: (fn(using(io : IO)) -> Impl(Future(unit)))(
5
- io.async(() => {
4
+ yield :: (fn(io : Io) -> Impl(Future(unit)))(
5
+ io.async((io : Io) => {
6
6
  return(());
7
7
  })
8
8
  );
@@ -1,4 +1,5 @@
1
1
  //! Dynamic array (vector) with amortized O(1) push and O(1) indexed access.
2
+ pragma(Pragma.AllowUnsafe);
2
3
  { GlobalAllocator, AllocError } :: import("../allocator.yo");
3
4
  { memmove, memcpy, memset } :: import("../libc/string.yo");
4
5
  { malloc, calloc, realloc, free, aligned_alloc } :: GlobalAllocator;
@@ -303,7 +304,12 @@ impl(
303
304
  bytes_to_move := (elements_after * element_size);
304
305
  dst_void := *(void)(dst_ptr);
305
306
  src_void := *(void)(src_ptr);
306
- memmove(dst_void, src_void, bytes_to_move);
307
+ // SAFETY: dst_ptr and src_ptr both point into self._ptr's
308
+ // contiguous element buffer. `elements_after = self._length - (start + actual_count)`
309
+ // counts elements still in-bounds after the remove range,
310
+ // so `bytes_to_move = elements_after * sizeof(T)` stays within
311
+ // the allocated buffer. memmove handles overlap.
312
+ unsafe(memmove(dst_void, src_void, bytes_to_move));
307
313
  }
308
314
  );
309
315
  },
@@ -429,7 +435,7 @@ impl(
429
435
  self._ptr,
430
436
  .Some(dst_base) => {
431
437
  dst := (*(void))(dst_base &+ self._length);
432
- _ := memcpy(dst, (*(void))(src), count * sizeof(T));
438
+ _ := unsafe(memcpy(dst, (*(void))(src), count * sizeof(T)));
433
439
  self._length = (self._length + count);
434
440
  },
435
441
  .None => panic("ArrayList.extend_from_ptr: no ptr after ensure_total_capacity")
@@ -454,7 +460,7 @@ impl(
454
460
  self._ptr,
455
461
  .None => (),
456
462
  .Some(_ptr) => {
457
- _ := memset((*(void))(_ptr), byte_val, self._length * sizeof(T));
463
+ _ := unsafe(memset((*(void))(_ptr), byte_val, self._length * sizeof(T)));
458
464
  }
459
465
  )
460
466
  ),
@@ -475,7 +481,7 @@ impl(
475
481
  .Some(_ptr) => {
476
482
  start := (*(void))(_ptr &+ self._length);
477
483
  fill_count := ((new_len - self._length) * sizeof(T));
478
- _ := memset(start, byte_val, fill_count);
484
+ _ := unsafe(memset(start, byte_val, fill_count));
479
485
  self._length = new_len;
480
486
  },
481
487
  .None => panic("ArrayList.resize_with_byte: no ptr after ensure")
@@ -491,16 +497,38 @@ impl(
491
497
  /// The output type is the element type `T`.
492
498
  Output : T,
493
499
  /// Returns a pointer to the element at the given index. Panics if the index is out of bounds.
494
- index : (fn(self : *(Self), idx : usize) -> *(Self.Output))({
495
- assert(idx < self.*._length, "ArrayList: index out of bounds");
500
+ index : (fn(ref(self) : Self, idx : usize) -> *(Self.Output))({
501
+ assert(idx < self._length, "ArrayList: index out of bounds");
496
502
  match(
497
- self.*._ptr,
503
+ self._ptr,
498
504
  .Some(_ptr) => (_ptr &+ idx),
499
505
  .None => panic("ArrayList: index on empty list")
500
506
  )
501
507
  })
502
508
  )
503
509
  );
510
+ /// Indexable impl — projection-style indexing yielding `ref(T)`.
511
+ /// See plans/ITERATOR_REDESIGN.md. Body wraps the pointer arithmetic
512
+ /// in `unsafe(...)` to pass the flowability check.
513
+ impl(
514
+ forall(T : Type),
515
+ ArrayList(T),
516
+ Indexable(usize)(
517
+ Element : T,
518
+ project : (fn(ref(self) : Self, pos : usize) -> ref(T))({
519
+ assert(pos < self._length, "ArrayList: project out of bounds");
520
+ match(
521
+ self._ptr,
522
+ // SAFETY: assert above bounds `pos < self._length` and the
523
+ // length invariant guarantees `pos < self._capacity`, so
524
+ // `_ptr &+ pos` stays inside the Rc-managed heap buffer
525
+ // for the duration of the borrow.
526
+ .Some(_ptr) => unsafe(_ptr &+ pos),
527
+ .None => panic("ArrayList: project on empty list")
528
+ )
529
+ })
530
+ )
531
+ );
504
532
  impl(
505
533
  forall(T : Type),
506
534
  ArrayList(T),
@@ -539,7 +567,7 @@ impl(
539
567
  ArrayListIter(T),
540
568
  Iterator(
541
569
  Item : T,
542
- next : (fn(self : *(Self)) -> Option(T))(
570
+ next : (fn(ref(self) : Self) -> Option(T))(
543
571
  cond(
544
572
  (self._index >= self._list._length) =>.None,
545
573
  true => {
@@ -558,34 +586,26 @@ impl(
558
586
  ArrayListIter(T)(_list : self, _index : usize(0))
559
587
  )
560
588
  );
561
- /**
562
- * Pointer iterator for ArrayList - yields pointers to elements (*(T))
563
- * Used by iter() method
564
- */
565
- ArrayListIterPtr :: (fn(comptime(T) : Type) -> comptime(Type))(
566
- struct(
567
- _list : ArrayList(T),
568
- _index : usize
569
- )
589
+ /// Position iterator for ArrayList — yields `usize` indices into the
590
+ /// list. Used by `ArrayList.iter()`; the for-macro pairs each yielded
591
+ /// position with `ArrayList.project(pos)` (Indexable from Phase C) to
592
+ /// bind the body's `ref`-name. See plans/ITERATOR_REDESIGN.md.
593
+ _ArrayListPosIter :: struct(
594
+ _index : usize,
595
+ _len : usize
570
596
  );
571
597
  impl(
572
- forall(T : Type),
573
- ArrayListIterPtr(T),
598
+ _ArrayListPosIter,
574
599
  Iterator(
575
- Item : *(T),
576
- next : (fn(self : *(Self)) -> Option(*(T)))(
600
+ Item : usize,
601
+ next : (fn(ref(self) : Self) -> Option(usize))(
577
602
  cond(
578
- (self._index >= self._list._length) =>.None,
579
- true =>
580
- match(
581
- self._list._ptr,
582
- .Some(ptr) => {
583
- element_ptr := (ptr &+ self._index);
584
- self._index = (self._index + usize(1));
585
- .Some(element_ptr)
586
- },
587
- .None =>.None
588
- )
603
+ (self._index >= self._len) =>.None,
604
+ true => {
605
+ out := self._index;
606
+ self._index = (self._index + usize(1));
607
+ .Some(out)
608
+ }
589
609
  )
590
610
  )
591
611
  )
@@ -593,8 +613,8 @@ impl(
593
613
  impl(
594
614
  forall(T : Type),
595
615
  ArrayList(T),
596
- iter : (fn(self : *(Self)) -> ArrayListIterPtr(T))(
597
- ArrayListIterPtr(T)(_list : self.*, _index : usize(0))
616
+ iter : (fn(ref(self) : Self) -> _ArrayListPosIter)(
617
+ _ArrayListPosIter(_index : usize(0), _len : self._length)
598
618
  )
599
619
  );
600
620
  impl(
@@ -760,16 +780,16 @@ impl(
760
780
  where(T <: Clone),
761
781
  ArrayList(T),
762
782
  Clone(
763
- clone : (fn(self : *(Self)) -> Self)({
764
- n := self.*.len();
783
+ clone : (fn(ref(self) : Self) -> Self)({
784
+ n := self.len();
765
785
  result := Self.with_capacity(n);
766
786
  i := usize(0);
767
787
  while(i < n, {
768
- item_opt := self.*.get(i);
788
+ item_opt := self.get(i);
769
789
  match(
770
790
  item_opt,
771
791
  .Some(item) => {
772
- result.push((&(item)).clone());
792
+ result.push(item.clone());
773
793
  },
774
794
  .None => ()
775
795
  );
@@ -782,8 +802,7 @@ impl(
782
802
  export(
783
803
  ArrayList,
784
804
  ArrayListError,
785
- ArrayListIter,
786
- ArrayListIterPtr
805
+ ArrayListIter
787
806
  );
788
807
  // =============================================================================
789
808
  // array_list! literal macro
@@ -10,6 +10,7 @@
10
10
  //! m.set(String.from("a"), 1);
11
11
  //! // Iteration order: a, b
12
12
  //! ```
13
+ pragma(Pragma.AllowUnsafe);
13
14
  { ArrayList } :: import("./array_list");
14
15
  open(import("../string"));
15
16
  /// Key-value pair entry stored in a BTreeMap.
@@ -48,7 +49,7 @@ impl(
48
49
  hi := self._entries.len();
49
50
  while(lo < hi, {
50
51
  mid := (lo + ((hi - lo) / usize(2)));
51
- entry_key := self._entries.get(mid).unwrap().key;
52
+ entry_key := self._entries(mid).key;
52
53
  cond(
53
54
  (entry_key < k) => {
54
55
  lo = (mid + usize(1));
@@ -67,7 +68,7 @@ impl(
67
68
  get : (fn(self : Self, k : K, where(K <: Ord(K))) -> ?(V))({
68
69
  r := self._find(k);
69
70
  cond(
70
- r.found =>.Some(self._entries.get(r.idx).unwrap().value),
71
+ r.found =>.Some(self._entries(r.idx).value),
71
72
  true =>.None
72
73
  )
73
74
  }),
@@ -77,7 +78,7 @@ impl(
77
78
  r := self._find(k);
78
79
  cond(
79
80
  r.found => {
80
- entry := self._entries.get(r.idx).unwrap();
81
+ entry := self._entries(r.idx);
81
82
  &(self._entries(r.idx)).* = BTreeEntry(K, V)(key : entry.key, value : v);
82
83
  },
83
84
  true => {
@@ -85,8 +86,8 @@ impl(
85
86
  self._entries.push(BTreeEntry(K, V)(key : k, value : v));
86
87
  pos := (self._entries.len() - usize(1));
87
88
  while(pos > r.idx, pos = (pos - usize(1)), {
88
- curr := self._entries.get(pos).unwrap();
89
- prev := self._entries.get(pos - usize(1)).unwrap();
89
+ curr := self._entries(pos);
90
+ prev := self._entries(pos - usize(1));
90
91
  &(self._entries(pos)).* = prev;
91
92
  &(self._entries(pos - usize(1))).* = curr;
92
93
  });
@@ -98,7 +99,7 @@ impl(
98
99
  r := self._find(k);
99
100
  cond(
100
101
  r.found => {
101
- v := self._entries.get(r.idx).unwrap().value;
102
+ v := self._entries(r.idx).value;
102
103
  self._entries.remove(r.idx);
103
104
  .Some(v)
104
105
  },
@@ -109,14 +110,14 @@ impl(
109
110
  min : (fn(self : Self) -> ?(BTreeEntry(K, V)))(
110
111
  cond(
111
112
  (self._entries.len() == usize(0)) =>.None,
112
- true =>.Some(self._entries.get(usize(0)).unwrap())
113
+ true =>.Some(self._entries(usize(0)))
113
114
  )
114
115
  ),
115
116
  // Return the entry with the largest key, or .None.
116
117
  max : (fn(self : Self) -> ?(BTreeEntry(K, V)))(
117
118
  cond(
118
119
  (self._entries.len() == usize(0)) =>.None,
119
- true =>.Some(self._entries.get(self._entries.len() - usize(1)).unwrap())
120
+ true =>.Some(self._entries(self._entries.len() - usize(1)))
120
121
  )
121
122
  )
122
123
  );
@@ -134,11 +135,11 @@ impl(
134
135
  BTreeMapIter(K, V),
135
136
  Iterator(
136
137
  Item : BTreeEntry(K, V),
137
- next : (fn(self : *(Self)) -> Option(BTreeEntry(K, V)))(
138
+ next : (fn(ref(self) : Self) -> Option(BTreeEntry(K, V)))(
138
139
  cond(
139
140
  (self._index >= self._entries.len()) =>.None,
140
141
  true => {
141
- entry := self._entries.get(self._index).unwrap();
142
+ entry := self._entries(self._index);
142
143
  self._index = (self._index + usize(1));
143
144
  .Some(entry)
144
145
  }
@@ -167,7 +168,7 @@ impl(
167
168
  BTreeMapIterPtr(K, V),
168
169
  Iterator(
169
170
  Item : *(BTreeEntry(K, V)),
170
- next : (fn(self : *(Self)) -> Option(*(BTreeEntry(K, V))))(
171
+ next : (fn(ref(self) : Self) -> Option(*(BTreeEntry(K, V))))(
171
172
  cond(
172
173
  (self._index >= self._entries.len()) =>.None,
173
174
  true =>
@@ -187,8 +188,8 @@ impl(
187
188
  impl(
188
189
  forall(K : Type, V : Type),
189
190
  BTreeMap(K, V),
190
- iter : (fn(self : *(Self)) -> BTreeMapIterPtr(K, V))(
191
- BTreeMapIterPtr(K, V)(_entries : self.*._entries, _index : usize(0))
191
+ iter : (fn(self : Self) -> BTreeMapIterPtr(K, V))(
192
+ BTreeMapIterPtr(K, V)(_entries : self._entries, _index : usize(0))
192
193
  )
193
194
  );
194
195
  /**
@@ -204,7 +205,7 @@ impl(
204
205
  BTreeMapKeys(K, V),
205
206
  Iterator(
206
207
  Item : K,
207
- next : (fn(self : *(Self)) -> Option(K))(
208
+ next : (fn(ref(self) : Self) -> Option(K))(
208
209
  match(
209
210
  self._inner.next(),
210
211
  .None =>.None,
@@ -233,7 +234,7 @@ impl(
233
234
  BTreeMapValues(K, V),
234
235
  Iterator(
235
236
  Item : V,
236
- next : (fn(self : *(Self)) -> Option(V))(
237
+ next : (fn(ref(self) : Self) -> Option(V))(
237
238
  match(
238
239
  self._inner.next(),
239
240
  .None =>.None,
@@ -254,10 +255,10 @@ impl(
254
255
  BTreeMap(K, V),
255
256
  Index(K)(
256
257
  Output : V,
257
- index : (fn(self : *(Self), idx : K, where(K <: Ord(K))) -> *(Self.Output))({
258
- r := self.*._find(idx);
258
+ index : (fn(ref(self) : Self, idx : K, where(K <: Ord(K))) -> *(Self.Output))({
259
+ r := self._find(idx);
259
260
  assert(r.found, "BTreeMap: key not found");
260
- entry_ptr := &(self.*._entries(r.idx));
261
+ entry_ptr := &(self._entries(r.idx));
261
262
  &(entry_ptr.*.value)
262
263
  })
263
264
  )
@@ -10,6 +10,7 @@
10
10
  //! d.push_front(0);
11
11
  //! x := d.pop_front().unwrap(); // 0
12
12
  //! ```
13
+ pragma(Pragma.AllowUnsafe);
13
14
  { GlobalAllocator, AllocError } :: import("../allocator");
14
15
  { malloc, realloc, free } :: GlobalAllocator;
15
16
  /// Circular buffer supporting efficient push/pop at both front and back.
@@ -188,7 +189,7 @@ impl(
188
189
  DequeIter(T),
189
190
  Iterator(
190
191
  Item : T,
191
- next : (fn(self : *(Self)) -> Option(T))(
192
+ next : (fn(ref(self) : Self) -> Option(T))(
192
193
  cond(
193
194
  (self._remaining == usize(0)) =>.None,
194
195
  true => {
@@ -227,7 +228,7 @@ impl(
227
228
  DequeIterPtr(T),
228
229
  Iterator(
229
230
  Item : *(T),
230
- next : (fn(self : *(Self)) -> Option(*(T)))(
231
+ next : (fn(ref(self) : Self) -> Option(*(T)))(
231
232
  cond(
232
233
  (self._remaining == usize(0)) =>.None,
233
234
  true => {
@@ -245,8 +246,8 @@ impl(
245
246
  impl(
246
247
  forall(T : Type),
247
248
  Deque(T),
248
- iter : (fn(self : *(Self)) -> DequeIterPtr(T))(
249
- DequeIterPtr(T)(_deque : self.*, _pos : usize(0), _remaining : self.*._len)
249
+ iter : (fn(self : Self) -> DequeIterPtr(T))(
250
+ DequeIterPtr(T)(_deque : self, _pos : usize(0), _remaining : self._len)
250
251
  )
251
252
  );
252
253
  impl(
@@ -254,10 +255,10 @@ impl(
254
255
  Deque(T),
255
256
  Index(usize)(
256
257
  Output : T,
257
- index : (fn(self : *(Self), idx : usize) -> *(Self.Output))({
258
- assert(idx < self.*._len, "Deque: index out of bounds");
259
- buf := self.*._buf.unwrap();
260
- physical_idx := ((self.*._head + idx) % self.*._capacity);
258
+ index : (fn(ref(self) : Self, idx : usize) -> *(Self.Output))({
259
+ assert(idx < self._len, "Deque: index out of bounds");
260
+ buf := self._buf.unwrap();
261
+ physical_idx := ((self._head + idx) % self._capacity);
261
262
  buf &+ physical_idx
262
263
  })
263
264
  )