@shd101wyy/yo 0.1.29 → 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 (174) hide show
  1. package/.github/skills/yo-async-effects/SKILL.md +3 -3
  2. package/.github/skills/yo-async-effects/async-effects-recipes.md +19 -11
  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/syntax-cheatsheet.md +59 -21
  6. package/README.md +4 -3
  7. package/out/cjs/index.cjs +771 -676
  8. package/out/cjs/yo-cli.cjs +1003 -898
  9. package/out/cjs/yo-lsp.cjs +834 -739
  10. package/out/esm/index.mjs +716 -621
  11. package/out/types/src/codegen/exprs/async.d.ts +2 -0
  12. package/out/types/src/codegen/exprs/await.d.ts +1 -0
  13. package/out/types/src/codegen/exprs/closures.d.ts +4 -0
  14. package/out/types/src/codegen/functions/context.d.ts +6 -0
  15. package/out/types/src/env.d.ts +2 -0
  16. package/out/types/src/evaluator/builtins/pragma.d.ts +9 -0
  17. package/out/types/src/evaluator/builtins/unsafe.d.ts +8 -0
  18. package/out/types/src/evaluator/context.d.ts +2 -0
  19. package/out/types/src/evaluator/index.d.ts +1 -1
  20. package/out/types/src/evaluator/memory-safety.d.ts +14 -0
  21. package/out/types/src/evaluator/types/flowability.d.ts +6 -0
  22. package/out/types/src/expr-traversal.d.ts +1 -0
  23. package/out/types/src/expr.d.ts +4 -1
  24. package/out/types/src/public-safe-report.d.ts +19 -0
  25. package/out/types/src/tests/comptime-ref-gate.test.d.ts +1 -0
  26. package/out/types/src/tests/pragma-validation.test.d.ts +1 -0
  27. package/out/types/src/tests/public-safe-report.test.d.ts +1 -0
  28. package/out/types/src/tests/type-representation-pointer.test.d.ts +1 -0
  29. package/out/types/src/tests/unsafe-gate.test.d.ts +1 -0
  30. package/out/types/src/tests/unsafe-report-classify.test.d.ts +1 -0
  31. package/out/types/src/types/definitions.d.ts +2 -0
  32. package/out/types/src/types/utils.d.ts +4 -0
  33. package/out/types/src/unsafe-report.d.ts +29 -0
  34. package/out/types/src/value.d.ts +1 -0
  35. package/out/types/tsconfig.tsbuildinfo +1 -1
  36. package/package.json +1 -1
  37. package/scripts/add-pragma-for-pointer-decls.ts +134 -0
  38. package/scripts/add-pragma.ts +58 -0
  39. package/scripts/migrate-amp-method-calls.ts +186 -0
  40. package/scripts/migrate-clone-calls.ts +93 -0
  41. package/scripts/migrate-get-unwrap.ts +166 -0
  42. package/scripts/migrate-index-patterns.ts +210 -0
  43. package/scripts/migrate-index-trait.ts +142 -0
  44. package/scripts/migrate-iterator.ts +150 -0
  45. package/scripts/migrate-self-ptr.ts +220 -0
  46. package/scripts/migrate-skip-pragmas.ts +109 -0
  47. package/scripts/migrate-tostring.ts +134 -0
  48. package/scripts/trim-pragma.ts +130 -0
  49. package/scripts/wrap-extern-calls.ts +161 -0
  50. package/std/alg/hash.yo +3 -2
  51. package/std/allocator.yo +6 -5
  52. package/std/async.yo +2 -2
  53. package/std/collections/array_list.yo +59 -40
  54. package/std/collections/btree_map.yo +19 -18
  55. package/std/collections/deque.yo +9 -8
  56. package/std/collections/hash_map.yo +101 -13
  57. package/std/collections/hash_set.yo +5 -4
  58. package/std/collections/linked_list.yo +39 -4
  59. package/std/collections/ordered_map.yo +3 -3
  60. package/std/collections/priority_queue.yo +14 -13
  61. package/std/crypto/md5.yo +2 -1
  62. package/std/crypto/random.yo +16 -15
  63. package/std/crypto/sha256.yo +2 -1
  64. package/std/encoding/base64.yo +14 -14
  65. package/std/encoding/hex.yo +3 -3
  66. package/std/encoding/json.yo +59 -10
  67. package/std/encoding/punycode.yo +24 -23
  68. package/std/encoding/toml.yo +4 -3
  69. package/std/encoding/utf16.yo +2 -2
  70. package/std/env.yo +43 -28
  71. package/std/error.yo +6 -6
  72. package/std/fmt/display.yo +2 -2
  73. package/std/fmt/index.yo +6 -5
  74. package/std/fmt/to_string.yo +39 -38
  75. package/std/fmt/writer.yo +9 -8
  76. package/std/fs/dir.yo +34 -33
  77. package/std/fs/file.yo +52 -51
  78. package/std/fs/metadata.yo +10 -9
  79. package/std/fs/temp.yo +24 -13
  80. package/std/fs/walker.yo +10 -9
  81. package/std/gc.yo +1 -0
  82. package/std/glob.yo +7 -7
  83. package/std/http/client.yo +15 -14
  84. package/std/http/http.yo +6 -6
  85. package/std/http/index.yo +1 -1
  86. package/std/imm/list.yo +33 -0
  87. package/std/imm/map.yo +2 -1
  88. package/std/imm/set.yo +1 -0
  89. package/std/imm/sorted_map.yo +1 -0
  90. package/std/imm/sorted_set.yo +1 -0
  91. package/std/imm/string.yo +27 -23
  92. package/std/imm/vec.yo +18 -2
  93. package/std/io/reader.yo +2 -1
  94. package/std/io/writer.yo +3 -2
  95. package/std/libc/assert.yo +1 -0
  96. package/std/libc/ctype.yo +1 -0
  97. package/std/libc/dirent.yo +1 -0
  98. package/std/libc/errno.yo +1 -0
  99. package/std/libc/fcntl.yo +1 -0
  100. package/std/libc/float.yo +1 -0
  101. package/std/libc/limits.yo +1 -0
  102. package/std/libc/math.yo +1 -0
  103. package/std/libc/signal.yo +1 -0
  104. package/std/libc/stdatomic.yo +1 -0
  105. package/std/libc/stdint.yo +1 -0
  106. package/std/libc/stdio.yo +1 -0
  107. package/std/libc/stdlib.yo +1 -0
  108. package/std/libc/string.yo +1 -0
  109. package/std/libc/sys/stat.yo +1 -0
  110. package/std/libc/time.yo +1 -0
  111. package/std/libc/unistd.yo +1 -0
  112. package/std/libc/wctype.yo +1 -0
  113. package/std/libc/windows.yo +2 -0
  114. package/std/log.yo +7 -6
  115. package/std/net/addr.yo +5 -4
  116. package/std/net/dns.yo +7 -6
  117. package/std/net/errors.yo +8 -8
  118. package/std/net/tcp.yo +19 -18
  119. package/std/net/udp.yo +13 -12
  120. package/std/os/signal.yo +3 -3
  121. package/std/path.yo +1 -0
  122. package/std/prelude.yo +353 -182
  123. package/std/process/command.yo +40 -23
  124. package/std/process/index.yo +2 -1
  125. package/std/regex/compiler.yo +10 -9
  126. package/std/regex/index.yo +41 -41
  127. package/std/regex/match.yo +2 -2
  128. package/std/regex/parser.yo +21 -21
  129. package/std/regex/vm.yo +42 -41
  130. package/std/string/string.yo +95 -40
  131. package/std/string/string_builder.yo +9 -9
  132. package/std/string/unicode.yo +50 -49
  133. package/std/sync/channel.yo +2 -1
  134. package/std/sync/cond.yo +5 -4
  135. package/std/sync/mutex.yo +4 -3
  136. package/std/sys/advise.yo +1 -0
  137. package/std/sys/bufio/buf_reader.yo +17 -16
  138. package/std/sys/bufio/buf_writer.yo +10 -9
  139. package/std/sys/clock.yo +1 -0
  140. package/std/sys/copy.yo +1 -0
  141. package/std/sys/dir.yo +10 -9
  142. package/std/sys/dns.yo +6 -5
  143. package/std/sys/errors.yo +11 -11
  144. package/std/sys/events.yo +1 -0
  145. package/std/sys/externs.yo +38 -37
  146. package/std/sys/file.yo +17 -16
  147. package/std/sys/future.yo +4 -3
  148. package/std/sys/iov.yo +1 -0
  149. package/std/sys/mmap.yo +1 -0
  150. package/std/sys/path.yo +1 -0
  151. package/std/sys/perm.yo +2 -1
  152. package/std/sys/pipe.yo +1 -0
  153. package/std/sys/process.yo +5 -4
  154. package/std/sys/signal.yo +1 -0
  155. package/std/sys/socketpair.yo +1 -0
  156. package/std/sys/sockinfo.yo +1 -0
  157. package/std/sys/statfs.yo +2 -1
  158. package/std/sys/statx.yo +1 -0
  159. package/std/sys/sysinfo.yo +1 -0
  160. package/std/sys/tcp.yo +15 -14
  161. package/std/sys/temp.yo +1 -0
  162. package/std/sys/time.yo +2 -1
  163. package/std/sys/timer.yo +6 -6
  164. package/std/sys/tty.yo +2 -1
  165. package/std/sys/udp.yo +13 -12
  166. package/std/sys/unix.yo +12 -11
  167. package/std/testing/bench.yo +4 -3
  168. package/std/thread.yo +7 -6
  169. package/std/time/datetime.yo +18 -15
  170. package/std/time/duration.yo +11 -10
  171. package/std/time/instant.yo +4 -4
  172. package/std/time/sleep.yo +1 -0
  173. package/std/url/index.yo +3 -3
  174. package/std/worker.yo +4 -3
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: yo-async-effects
3
- description: Write Yo async code and algebraic effect handlers. Use this when working with IO, Future, JoinHandle, ctl/fn handlers, io.async, io.await, io.spawn, return, and unwind.
3
+ description: Write Yo async code and algebraic effect handlers. Use this when working with Io, Future, JoinHandle, ctl/fn handlers, io.async, io.await, io.spawn, return, and unwind.
4
4
  argument-hint: "[async task, effect, or API]"
5
5
  ---
6
6
 
@@ -14,7 +14,7 @@ If a repository wraps these primitives, keep the same semantics and verify wheth
14
14
 
15
15
  Use this skill when you need to:
16
16
 
17
- - write functions that take an `io : IO` parameter
17
+ - write functions that take an `io : Io` parameter
18
18
  - return or consume `Future(...)` values
19
19
  - run tasks with `io.async`, `io.await`, or `io.spawn`
20
20
  - define handlers using `ctl(args) -> R` and install them as plain local bindings
@@ -23,7 +23,7 @@ Use this skill when you need to:
23
23
  ## Workflow
24
24
 
25
25
  1. Decide whether the task needs sequential async, concurrent async on one thread, or true parallelism.
26
- 2. Add the effect parameters (`io : IO`, `raise : Raise`, …) to function signatures and call sites. There is no implicit injection — pass them explicitly.
26
+ 2. Add the effect parameters (`io : Io`, `raise : Raise`, …) to function signatures and call sites. There is no implicit injection — pass them explicitly.
27
27
  3. Use the [async and effects recipes](./async-effects-recipes.md) for working patterns.
28
28
  4. Re-check handler semantics before finalizing:
29
29
  - `return(value)` resumes the continuation
@@ -16,8 +16,8 @@ These patterns cover normal Yo async code and algebraic effects.
16
16
  ```rust
17
17
  { yield } :: import("std/async");
18
18
 
19
- pause_then_answer :: (fn(io : IO) -> Impl(Future(i32, IO)))(
20
- io.async((io : IO) => {
19
+ pause_then_answer :: (fn(io : Io) -> Impl(Future(i32, Io)))(
20
+ io.async((io : Io) => {
21
21
  io.await(yield(), io);
22
22
  i32(42)
23
23
  })
@@ -25,7 +25,7 @@ pause_then_answer :: (fn(io : IO) -> Impl(Future(i32, IO)))(
25
25
  ```
26
26
 
27
27
  - `io.async(...)` is lazy.
28
- - The closure's parameter is the effect bundle. The simplest bundle is just `IO`.
28
+ - The closure's parameter is the effect bundle. The simplest bundle is just `Io`.
29
29
  - The `Future(T, E)` return type names the same bundle type that the closure consumes.
30
30
 
31
31
  ## Sequential await
@@ -33,8 +33,8 @@ pause_then_answer :: (fn(io : IO) -> Impl(Future(i32, IO)))(
33
33
  ```rust
34
34
  { yield } :: import("std/async");
35
35
 
36
- main :: (fn(io : IO) -> unit)({
37
- task := io.async((io : IO) => {
36
+ main :: (fn(io : Io) -> unit)({
37
+ task := io.async((io : Io) => {
38
38
  io.await(yield(), io);
39
39
  i32(1)
40
40
  });
@@ -51,12 +51,12 @@ export(main);
51
51
  ```rust
52
52
  { yield } :: import("std/async");
53
53
 
54
- main :: (fn(io : IO) -> unit)({
55
- task1 := io.async((io : IO) => {
54
+ main :: (fn(io : Io) -> unit)({
55
+ task1 := io.async((io : Io) => {
56
56
  io.await(yield(), io);
57
57
  i32(1)
58
58
  });
59
- task2 := io.async((io : IO) => {
59
+ task2 := io.async((io : Io) => {
60
60
  io.await(yield(), io);
61
61
  i32(2)
62
62
  });
@@ -128,7 +128,7 @@ bundle struct and pass that.
128
128
  { yield } :: import("std/async");
129
129
 
130
130
  Raise :: (ctl(msg : String) -> i32);
131
- TaskCtx :: struct(io : IO, raise : Raise);
131
+ TaskCtx :: struct(io : Io, raise : Raise);
132
132
 
133
133
  work :: (fn(ctx : TaskCtx) -> Impl(Future(i32, TaskCtx)))(
134
134
  io.async((ctx : TaskCtx) => {
@@ -153,7 +153,7 @@ work :: (fn(ctx : TaskCtx) -> Impl(Future(i32, TaskCtx)))(
153
153
  ```rust
154
154
  { read_dir, DirEntry } :: import("std/fs/dir");
155
155
 
156
- WalkCtx :: struct(io : IO, exn : Exception);
156
+ WalkCtx :: struct(io : Io, exn : Exception);
157
157
 
158
158
  process_dir :: (fn(root: Path, ctx : WalkCtx) -> Impl(Future(unit, WalkCtx)))(
159
159
  io.async((ctx : WalkCtx) => {
@@ -190,9 +190,17 @@ process_dir :: (fn(root: Path, ctx : WalkCtx) -> Impl(Future(unit, WalkCtx)))(
190
190
  (match arm, `cond` branch, `begin` block, plain `fn` body), use `return`.
191
191
  - `unwind` inside an async task aborts the future instead of completing it normally.
192
192
  - `io.await(...)` on an already-aborted future can panic; `JoinHandle.await(...)` converts abort into `.None`.
193
- - Closures cannot be `ctl`, and they cannot capture a `ctl`-typed value. Handlers are bare (non-capturing) anonymous functions.
193
+ - Closures cannot be `ctl`, and they cannot capture a `ctl`-typed value. Handlers are bare (non-capturing) anonymous functions. If you need to use a `ctl` handler from inside a closure body, pass it in as an explicit parameter instead of capturing it.
194
194
  - Pointers and references to `ctl` types (or structs containing them) are rejected.
195
195
  - **`recur` inside `io.async` calls the lambda, not the outer function** — use an iterative worklist for async recursion.
196
+ - **Closure params bound to a generic `E : Type.Struct` need an explicit annotation.** When a generic function takes `callback : Impl(Fn(v : V, e : E) -> R)` and you pass a closure, the closure's `e` parameter type cannot be inferred from the call's bundle argument — `E` is still unbound at the closure body's evaluation site. Define a concrete struct first and annotate the closure parameter:
197
+ ```rust
198
+ // ✗ fails with "Cannot infer the type of anonymous closure parameter `e`"
199
+ traverse(arr, (v, e) => { e.log(v); ... }, { yield, log });
200
+ // ✓ annotate via a named bundle struct
201
+ Eff :: struct(yield : Yield, log : Log);
202
+ traverse(arr, (v : i32, e : Eff) => { e.log(v); ... }, Eff(yield, log));
203
+ ```
196
204
 
197
205
  ## Exception (non-resumable)
198
206
 
@@ -105,6 +105,12 @@ match(numbers.get(usize(0)),
105
105
  .None => ()
106
106
  );
107
107
 
108
+ // Do NOT write the verbose forms when X(i) works:
109
+ // ✗ (&(numbers)).index(usize(0)).* = i32(99); // needs pragma, scans as raw-ptr code
110
+ // ✗ v := numbers.get(usize(0)).unwrap(); // panic-on-OOB anyway — just use call syntax
111
+ // ✓ numbers(usize(0)) = i32(99);
112
+ // ✓ v := numbers(usize(0));
113
+
108
114
  counts := HashMap(String, i32).new();
109
115
  counts.set(`yo`, i32(1));
110
116
  ```
@@ -123,7 +129,7 @@ counts.set(`yo`, i32(1));
123
129
  ```rust
124
130
  Iterator :: trait(
125
131
  Item : Type,
126
- next : (fn(self : *(Self)) -> Option(Self.Item))
132
+ next : (fn(ref(self) : Self) -> Option(Self.Item))
127
133
  );
128
134
  ```
129
135
 
@@ -193,6 +199,15 @@ TcpStream :: object(fd : i32, buffer : ArrayList(u8));
193
199
 
194
200
  - Use `newtype(...)` when the type has exactly one field
195
201
  - Use `object(...)` for types that need shared ownership
202
+ - **Parameter form by type kind:**
203
+ - `object(...)`: plain `name : Type` (reference semantics — no pointer or ref needed).
204
+ `foo :: (fn(ctx : EvalContext) -> unit)(ctx.do_stuff());`
205
+ - `struct(...)` / `enum(...)` / primitive, read-only: plain `name : Type`.
206
+ - `struct(...)` / `enum(...)` / primitive, need mutation: `ref(name) : Type`.
207
+ `swap :: (fn(ref(a) : i32, ref(b) : i32) -> unit)(...);`
208
+ - Method receiver on `object`: plain `self : Self`.
209
+ - Method receiver on value type (traits + inherent mutators): `ref(self) : Self`.
210
+ - Raw FFI pointer: `name : *(T)` (requires `pragma(Pragma.AllowUnsafe);`).
196
211
  - Source-file imports are namespace structs. The old `module(...)`, `Module`,
197
212
  and `SelfModule` syntax is gone; use `struct(...)`, `Type`, and normal `Self`.
198
213
  - Fields written as `name :: value` or `comptime(name) : Type` are compile-time-only static fields/methods and have no runtime layout
@@ -361,8 +376,8 @@ safe_div :: (fn(a : i32, b : i32) -> Result(i32, DivError))(
361
376
  result := inc(i32(5));
362
377
 
363
378
  transform :: (fn(values : ArrayList(i32), f : Impl(Fn(x : i32) -> i32)) -> unit)({
364
- for(values.iter(), (ptr) => {
365
- ptr.* = f(ptr.*);
379
+ for(values, ref(x) => {
380
+ x = f(x);
366
381
  });
367
382
  });
368
383
  ```
@@ -381,22 +396,27 @@ list := ArrayList(i32).new();
381
396
  list.push(i32(1));
382
397
  list.push(i32(2));
383
398
 
384
- for(list.iter(), (ptr) => {
385
- println(ptr.*);
399
+ // Value form — implicit .into_iter().
400
+ for(list, (value) => {
401
+ println(value);
386
402
  });
387
403
 
388
- for(list.into_iter(), (value) => {
389
- println(value);
404
+ // Borrow form — implicit .iter() + .project(pos). `x` is a writable
405
+ // binding into the collection; assignments propagate back.
406
+ for(list, ref(x) => {
407
+ x = (x + i32(10));
390
408
  });
391
409
  ```
392
410
 
393
- | Method | Yields | Semantics |
394
- | -------------- | ------ | ----------------------------------- |
395
- | `.iter()` | `*(T)` | Borrow via pointer; yields pointers |
396
- | `.into_iter()` | `T` | Takes ownership; yields values |
411
+ | Form | Expansion | When to use |
412
+ | ----------------------------- | ------------------------------------------ | --------------------------------------------- |
413
+ | `for(coll, (x) => …)` | `coll.into_iter()`, yields `T` by value | Read-only iteration; combinator chains |
414
+ | `for(coll, ref(x) => …)` | `coll.iter()` + `coll.project(pos)` borrow | Mutation in place; writes propagate to `coll` |
415
+ | `for(chain.map(f), (x) => …)` | Treats chain as the iterator (value form) | Computed values; chains support only value |
397
416
 
398
- - Implement `Iterator` trait to make custom types iterable
399
- - Implement `IntoIterator` trait for collection-style iteration
417
+ - `Iterator` trait defines `next() -> Option(Item)`. Custom iterables impl this.
418
+ - `IntoIterator` trait — defines `into_iter() -> IntoIter`. Collections impl this so `for(coll, ...)` works.
419
+ - `Indexable(Position)` trait — defines `project(pos) -> ref(Element)`. Collections impl this to support the borrow form.
400
420
 
401
421
  ## Module-level mutable variables
402
422
 
@@ -146,7 +146,7 @@ test("Async test", {
146
146
  });
147
147
  ```
148
148
 
149
- - `test("name", { body })` defines a test — `io : IO` is automatically available
149
+ - `test("name", { body })` defines a test — `io : Io` is automatically available
150
150
  - All tests can use `io.async(...)`, `io.await(...)`, etc. without a `using` clause
151
151
  - `assert(condition, "message")` — always include a message string
152
152
  - `comptime_assert(expr)` — verified at compile time
@@ -127,6 +127,16 @@ masked := ((A | B) | C);
127
127
  - Dynamic field access with unquote must keep grouping after the dot: `value.(#(field_expr))`, not `value.#(field_expr)`.
128
128
  - Unquote splicing is the tight operator `...#(exprs)`; do not insert a space between `...` and `#`.
129
129
  - Canonical pointer dereference is `ptr.*`; formatter should canonicalize legacy `ptr.(*)` to `ptr.*`.
130
+ - **Pointer deref (`p.*`), arithmetic (`&+`, `&-`, `&/`), and `consume(p.* = v)` require `unsafe(...)`, AND the file must declare `pragma(Pragma.AllowUnsafe);` at the top before `unsafe(...)` is usable.** Pointer comparison (`&==`, `&<`, etc.) and pointer-type casts (`*(u8)(p)`) stay safe. `unsafe(expr)` is a one-arg builtin call: `v := unsafe(p.*);`, `unsafe(p.* = i32(5));`, `unsafe(p &+ usize(1))`. Every file in `std/`, `yo-self/`, and `tests/` declares the pragma explicitly. User code (default) does not, so attempts to use `unsafe(...)` are rejected with a hint to add the pragma. See `plans/MEMORY_SAFETY.md`.
131
+ - **In-place mutation without raw pointers:** use the `ref(name) : T` parameter modifier (parallel to `own(name)`). `swap :: (fn(ref(a) : i32, ref(b) : i32) -> unit)({ tmp := a; a = b; b = tmp; });` — caller writes `swap(x, y)` with no `&()` syntax. The compiler lowers `ref(name) : T` to `T*` in C and inserts `&(arg)` at the call site automatically. Cannot combine with `own(...)` or with `forall`/`using` (those are erased at runtime — no binding to mutate). CAN combine with `comptime` as `comptime(ref(name)) : T` — the parameter is erased at runtime and mutations propagate via the evaluator's compile-time binding update path (used by prelude `ComptimeIndex`). See `plans/MEMORY_SAFETY.md` Phase B.
132
+ - **Object-type params:** use plain `name : Type`, NOT `*(Type)` or `ref(name) : Type`. Object types (`Environment`, `EvalContext`, `CodegenContext`, `Emitter`, `HashMap`, `ArrayList`, …) carry reference semantics — passing by name already shares the underlying RC state, so mutations through the param propagate to the caller. `*(Type)` requires `pragma(Pragma.AllowUnsafe);` for the `.* ` derefs and clutters the API; `ref(name) : Type` is redundant since object semantics already share state. Use the plain form: `foo :: (fn(ctx : EvalContext) -> unit)(ctx.method());`. The same applies at call sites — don't wrap object arguments with `&(obj)`; just pass `obj`. For receivers on object methods, plain `self : Self` is the idiom (`yo-self/env.yo`, `yo-self/codegen/context.yo`, `yo-self/emitter.yo` all follow this). `ref(self) : Self` is reserved for receivers on value-type methods (the form used by `Hash`, `Clone`, `ToString`, `Index`, `ComptimeIndex`, `Writer`, `Reader`).
133
+ - **Byte-buffer params:** prefer `Slice(u8)` over `*(u8) + usize` for public signatures (e.g. `random_bytes`, `fnv1a_hash_bytes`). `Slice` carries the length, eliminating the (`ptr`, `wrong-size`) footgun. Convert at the FFI seam with `slice.ptr()` and `slice.len()`; construct from existing storage with `Slice(u8).from_raw_parts(&(buf(0)), len)`. The `_cstr` family is the explicit raw-pointer variant — those names signal raw-pointer use by contract.
134
+ - **Audit public stdlib safety with `./yo-cli public-safe-report [path]`.** Flags every top-level public `fn(...)` whose params or return type expose `*(T)` outside an `extern(...)` block. Skips FFI-by-construction directories (`libc/`, `linux/`, `darwin/`, `cuda/`, `sys/`, `sync/`) and names that signal raw-pointer use by contract (`*_cstr`, `*_ptr`, `*_raw`, `raw_*`, `from_raw_parts`, `as_ptr`, `argv`, `argc`). Currently reports 0 findings on `./std` and `./yo-self`; keep it that way when adding new APIs.
135
+ - **Extern "c" call sites require `unsafe(...)` even in pragma'd files.** `unsafe(memcpy(dst, src, n))`, `unsafe(strlen(s))`, etc. The pragma authorizes DECLARING the FFI symbol via `extern(...)` / `c_include(...)`; the wrap is the per-call audit marker so `yo unsafe-report` lines up with UB-capable lines. `asm(...)` and `extern(...)` / `c_include(...)` declarations themselves do NOT need a wrap (the keyword / declaration syntax is its own marker). See `plans/EXTERN_UNSAFE_WRAP.md`.
136
+ - **Slice-flowability rule:** a function returning a slice-bearing type (`Slice(T)`, `str`, a struct wrapping a Slice, ...) must root the returned value in caller-owned storage (a `ref`-bound parameter, any non-`ref` parameter, a `comptime`/literal source, or a flowable projection chain). `(fn() -> Option(Slice(i32)))({ arr := ArrayList(i32).new(); arr.as_slice() })` is rejected; `(fn(ref(arr) : ArrayList(i32)) -> Option(Slice(i32)))(arr.as_slice())` is accepted. See `plans/SLICE_FLOWABILITY.md`.
137
+ - **Signed-integer overflow is defined (wrap-around).** Yo passes `-fwrapv` to clang/gcc/zig by default so `x + i32(1)` on `i32(MAX)` wraps to `i32(MIN)` instead of UB. Opt-out: `--cflags='-fno-wrapv'`.
138
+ - **`// SAFETY:` comment convention.** Every non-obvious `unsafe(...)` site in stdlib should have a `// SAFETY:` comment in the previous ~8 lines explaining the contract. `yo unsafe-report` picks them up and shows them inline under each finding.
139
+ - **User-facing memory-safety guide:** `docs/en-US/MEMORY_SAFETY.md` (English) and `docs/zh-CN/MEMORY_SAFETY.md` (Chinese). Refer users there instead of `plans/MEMORY_SAFETY.md` (which is the design document — not shipped via npm).
130
140
  - Keep single-line array and tuple literals compact during formatting: `[1, 2, 3]`, `(1, 2, 3)`.
131
141
  - Parenthesize other unary operands too: `!(ready)`, `-(value)`
132
142
  - **`!x && y` is parsed as `!(x && y)`**, not `(!x) && y`. Prefix `!` greedily consumes the full right-hand expression. To get `(!x) && y`, write `((!x) && y)` with explicit inner parens.
@@ -216,8 +226,8 @@ caller :: (fn() -> i32)({
216
226
  result := closure(i32(5));
217
227
 
218
228
  transform :: (fn(list : ArrayList(i32), f : Impl(Fn(x : i32) -> i32)) -> unit)({
219
- for(list.iter(), (ptr) => {
220
- ptr.* = f(ptr.*);
229
+ for(list, ref(x) => {
230
+ x = f(x);
221
231
  });
222
232
  });
223
233
  ```
@@ -371,25 +381,29 @@ while(comptime((i < 10)), {
371
381
  // body evaluated/unrolled at compile time
372
382
  });
373
383
 
374
- // for loop — 2-arg prelude macro; first arg MUST be an iterator (has .next()):
375
- for(list.iter(), x => { // ArrayList, array call .iter() first
384
+ // for loop — 2-arg prelude macro. First arg is the collection
385
+ // directly; the macro dispatches on the body's binding shape to
386
+ // pick value-form vs borrow-form iteration:
387
+ for(list, (x) => { // value form: implicit .into_iter()
376
388
  process(x);
377
389
  });
378
390
 
379
- for(map.into_iter(), bucket => {
380
- println(bucket.key);
391
+ for(list, ref(x) => { // borrow form: iter() + project(pos)
392
+ x = transform(x); // writes propagate back into list
381
393
  });
382
394
 
383
- for(list.iter(), (ptr) => { // ptr is a mutable reference to each element
384
- ptr.* = transform(ptr.*);
385
- });
395
+ // Combinator chains (.map / .filter / .into_iter / etc.) yield
396
+ // computed values; pass them as the first arg in the value form:
397
+ for(list.iter().map((x) => (x + i32(1))), (y) => println(y));
386
398
  ```
387
399
 
388
400
  - Use `recur(...)` for self-recursion
389
401
  - `while(cond, body)` is **always a runtime loop** — use this for open-ended loops (e.g., server accept loops, event loops)
390
402
  - `while(comptime(cond), body)` explicitly unrolls at compile time — `cond` must be a compile-time-known value
391
403
  - Using a comptime-only (`::`) variable in a bare `while` condition without `comptime()` is a **compile error** (would be an infinite loop at runtime)
392
- - **`for(arr, item => { body })`** — correct 2-arg prelude macro form. The `item => { body }` is an anonymous closure.
404
+ - **`for(coll, (x) => body)`** — value form; macro expands to `coll.into_iter()` then iterates by value (`x : T`).
405
+ - **`for(coll, ref(x) => body)`** — borrow form; macro expands to `coll.iter()` (position iterator) + `coll.project(pos)` (`Indexable.project` impl) so `x` is a writable binding. Writes propagate back into the collection.
406
+ - **Do NOT write `for(coll.iter(), (x) => …)` for the value form** — `.iter()` yields positions (usize), not the collection's elements. Use the bare collection or `.into_iter()`.
393
407
  - **Do NOT use `for(x, arr, { body })`** — this older 3-arg form is an evaluator-internal representation, not valid top-level Yo syntax. (The self-hosted evaluator currently only understands the 3-arg form in its internal for-loop handler; track issue: `issues/eval-for-loop-3arg-vs-2arg.md`)
394
408
 
395
409
  ## Return and branch safety
@@ -452,18 +466,22 @@ list := ArrayList(i32).new();
452
466
  list.push(i32(10));
453
467
  list.push(i32(20));
454
468
 
455
- for(list.iter(), (ptr) => {
456
- println(ptr.*);
469
+ // Value form — implicit .into_iter().
470
+ for(list, (value) => {
471
+ println(value);
457
472
  });
458
473
 
459
- for(list.into_iter(), (value) => {
460
- println(value);
474
+ // Borrow form — implicit .iter() + .project(pos). `x` is a writable
475
+ // binding into the collection; assignments propagate back.
476
+ for(list, ref(x) => {
477
+ x = (x + i32(1));
461
478
  });
462
479
  ```
463
480
 
464
- - `for(collection, (variable) => { body })` iterates via the `Iterator` trait
465
- - `.iter()` borrows the collection and yields pointers
466
- - `.into_iter()` takes ownership and yields values
481
+ - `for(coll, (x) => body)` value form. Macro expands to `coll.into_iter()` and yields elements by value.
482
+ - `for(coll, ref(x) => body)` — borrow form. Macro expands to `coll.iter()` (a position iterator yielding `usize`) + `coll.project(pos)` (from the `Indexable` trait) so the body sees a writable binding.
483
+ - Combinator chains (`coll.iter().map(f).filter(g)`) only support the value form — pass the chain as the first arg with `(x) => body`.
484
+ - The for macro accepts the collection directly; do NOT call `.iter()` for the value form, that yields positions (usize), not elements.
467
485
 
468
486
  ## Testing
469
487
 
@@ -482,7 +500,7 @@ test("Async test", {
482
500
  });
483
501
  ```
484
502
 
485
- - `test("description", { body })` defines a test — `io : IO` is automatically available
503
+ - `test("description", { body })` defines a test — `io : Io` is automatically available
486
504
  - All tests can use `io.async(...)`, `io.await(...)`, etc. without a `using` clause
487
505
  - `assert(condition, "message")` — runtime assertion (always include a message)
488
506
  - `comptime_assert(condition)` — compile-time assertion
@@ -604,11 +622,11 @@ list := ArrayList(i32).new();
604
622
  list.push(i32(42));
605
623
 
606
624
  val := list(usize(0)); // → i32 (value copy via Index trait)
607
- list(usize(0)) = i32(99); // mutate in place directly
625
+ list(usize(0)) = i32(99); // mutate in place directly
608
626
 
609
627
  // When you need the pointer explicitly:
610
- ptr := &(list(usize(0))); // → *(i32)
611
- ptr.* = i32(99); // also works
628
+ ptr := &(list(usize(0))); // → *(i32)
629
+ ptr.* = i32(99); // also works
612
630
 
613
631
  // Safe access (returns Option(T)):
614
632
  match(list.get(usize(0)),
@@ -622,6 +640,26 @@ match(list.get(usize(0)),
622
640
  - `&(list(i))` returns `*(T)` if you need the pointer explicitly
623
641
  - `list.get(i)` returns `Option(T)` for safe bounds-checked access
624
642
 
643
+ **Don't write `(&(X)).index(i).*` or `X.get(i).unwrap()` when you mean
644
+ `X(i)`.** Use the call-syntax form everywhere it works:
645
+
646
+ ```rust
647
+ // ✗ Verbose, scans like raw-pointer code (and requires the file's
648
+ // pragma(Pragma.AllowUnsafe); because `.*` is gated):
649
+ (&(self.field)).index(i).* = value;
650
+ elem := (&(self.field)).index(i).*;
651
+ v := list.get(usize(0)).unwrap();
652
+
653
+ // ✓ Same semantics, no `.*`, no pragma needed:
654
+ self.field(i) = value;
655
+ elem := self.field(i);
656
+ v := list(usize(0));
657
+ ```
658
+
659
+ Both forms call the same `Index` trait method. The call-syntax form
660
+ is shorter, doesn't need raw-pointer plumbing in user code, and
661
+ panics on out-of-bounds identically to `.unwrap()` on `.get(...)`.
662
+
625
663
  ### Named fields required for `struct`/`object` constructors
626
664
 
627
665
  ```rust
package/README.md CHANGED
@@ -55,7 +55,8 @@ Below is a non-exhaustive list of features that Yo supports:
55
55
  - Homoiconicity and metaprogramming (**Yo** syntax is inspired by the **Lisp** S expression. Simple syntax rule, Human & AI friendly).
56
56
  - Closure.
57
57
  - [Algebraic Effects and Handlers](./docs/en-US/ALGEBRAIC_EFFECTS.md) (One-shot delimited continuation. Tail-Resumptive. Implicit parameters via `using`/`given`, effect handlers with `return`/`escape`, by [Evidence Passing](https://xnning.github.io/papers/multip.pdf)).
58
- - [Async/await](./docs/en-US/ASYNC_AWAIT.md) (Builtin `IO` effect. Stackless coroutine & Cooperative multi-tasking. Lazy Futures, multi-await, single-threaded concurrency via state machine transformation).
58
+ - [Async/await](./docs/en-US/ASYNC_AWAIT.md) (Builtin `Io` effect. Stackless coroutine & Cooperative multi-tasking. Lazy Futures, multi-await, single-threaded concurrency via state machine transformation).
59
+ - [Memory safety by default](./docs/en-US/MEMORY_SAFETY.md) — user code can't write UB (no raw pointers, no FFI, no inline assembly) without an explicit `pragma(Pragma.AllowUnsafe);` opt-in. `ref(name)` for in-place mutation; `yo unsafe-report` for auditing the unsafe surface.
59
60
  - `object` type with [Non-atomic Reference Counting and Thread-Local Cycle Collection](./docs/en-US/CYCLE_COLLECTION.md).
60
61
  - [Compile-time Reference Counting with Ownership and Lifetime Analysis](./docs/en-US/COMPILE_TIME_RC_WITH_OWNERSHIP_ANALYSIS.md).
61
62
  - Thread-per-core parallelism model (see [PARALLELISM.md](./docs/en-US/PARALLELISM.md)).
@@ -234,7 +235,7 @@ Every Yo file automatically imports **[std/prelude.yo](./std/prelude.yo)**, whic
234
235
  - **C-compatible types**: `int`, `uint`, `short`, `long`, `longlong`, `char`, etc.
235
236
  - **Core traits**: `Eq`, `Ord`, `Add`, `Sub`, `Mul`, `Div`, `Iterator`, `IntoIterator`, `TryFrom`, `TryInto`, `Dispose`, `Send`, `Rc`, `Acyclic`, etc.
236
237
  - **Metaprogramming**: `Type`, `Expr`, `ExprList`, `Var`
237
- - **Async**: `IO`, `FutureState`, `JoinHandle`
238
+ - **Async**: `Io`, `FutureState`, `JoinHandle`
238
239
  - **Utilities**: `assert`, `unsafe`, `try`, `for`, `not`, `arc`, `Box`, `box`
239
240
  - etc.
240
241
 
@@ -378,7 +379,7 @@ This repository ships a set of **agent skill files** that teach AI agents how to
378
379
  | -------------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
379
380
  | [`yo-syntax`](.github/skills/yo-syntax/SKILL.md) | Core language syntax: curly braces, cond/match, structs, enums, operators, modules |
380
381
  | [`yo-core-patterns`](.github/skills/yo-core-patterns/SKILL.md) | Everyday patterns: types, generics, traits, error handling, collections, iterators |
381
- | [`yo-async-effects`](.github/skills/yo-async-effects/SKILL.md) | Async/await, algebraic effects, Exception, IO, spawning tasks |
382
+ | [`yo-async-effects`](.github/skills/yo-async-effects/SKILL.md) | Async/await, algebraic effects, Exception, Io, spawning tasks |
382
383
  | [`yo-project-workflow`](.github/skills/yo-project-workflow/SKILL.md) | `yo` CLI commands, `build.yo` project files, dependency management |
383
384
 
384
385
  ### Using in your own project