@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
@@ -78,7 +78,7 @@ if(done, println("done"), println("pending"));
78
78
  - Always write `match(...)`, never bare `match ...`
79
79
  - `if(a, b)` and `if(a, b, c)` are macro forms over `cond`
80
80
  - Write `return(value)` or `return()`; `return value` is invalid.
81
- - Write `escape(value)` or `escape()`; `escape value` is invalid.
81
+ - Write `unwind(value)` or `unwind()`; `unwind value` is invalid.
82
82
  - If a `match`/`cond` branch returns an enum variant and inference fails, qualify
83
83
  the variant with its enum type: `TypeValue.Unit` instead of `.Unit`.
84
84
  - Do not match enum payload literals directly, e.g. avoid `.Some(false)` and
@@ -121,12 +121,22 @@ masked := ((A | B) | C);
121
121
  - Yo has no operator precedence; fully parenthesize binary expressions
122
122
  - Preserve grouping around infix expressions on operator RHS positions: `true => (x / y)`, `value := (x + y)`, `(ptr &+ 1).*`
123
123
  - Line breaks can disambiguate operator chains; keep line-leading operators like `(4\n| 5\n| 6)` and newlines after `:` before a lambda unless you add equivalent grouping
124
- - When an operator ends a line, indent its RHS one level as a continuation: `(given(x) : T) =\n (v) -> { ... }`
124
+ - When an operator ends a line, indent its RHS one level as a continuation: `(x : T) =\n (v) -> { ... }`
125
125
  - Prefix operators (`!`, `&`, `-`, `~`) require parenthesized operands: `func(&(s), a, b)`, `!(ready)`, `-(value)`.
126
126
  - Tight special forms also require immediate parentheses: `#(expr)`, `?*(u8)`, `T <: !(Runtime)`
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.
@@ -180,12 +190,12 @@ create_user(name: `Bob`, age: 30);
180
190
  - Named arguments must keep the same order as the definition
181
191
  - Default values use `?=` and must be compile-time known
182
192
 
183
- ### Implicit parameters (`using` / `given`)
193
+ ### Effect parameters (explicit)
184
194
 
185
195
  ```rust
186
- Raise :: (fn(msg : String) -> i32);
196
+ Raise :: (ctl(msg : String) -> i32);
187
197
 
188
- safe_divide :: (fn(x : i32, y : i32, using(raise : Raise)) -> i32)(
198
+ safe_divide :: (fn(x : i32, y : i32, raise : Raise) -> i32)(
189
199
  cond(
190
200
  (y == i32(0)) => raise(`divide by zero`),
191
201
  true => (x / y)
@@ -193,18 +203,20 @@ safe_divide :: (fn(x : i32, y : i32, using(raise : Raise)) -> i32)(
193
203
  );
194
204
 
195
205
  caller :: (fn() -> i32)({
196
- (given(raise) : Raise) = (fn(msg : String) -> i32)({
197
- return(i32(0));
206
+ // Handler value bound to a local. Lambdas on the RHS of `=` need outer parens.
207
+ (raise : Raise) = ((msg) -> {
208
+ unwind(i32(0));
198
209
  });
199
210
 
200
- safe_divide(i32(10), i32(0))
211
+ safe_divide(i32(10), i32(0), raise)
201
212
  });
202
213
  ```
203
214
 
204
- - `using(name : Type)` declares an implicit parameter (effect)
205
- - `given(name) := Type(fields...)` installs a handler in the caller's scope
206
- - Effects are matched by **type**, not by name
207
- - The handler is auto-resolved at call sites; pass explicitly with `using(name)`
215
+ - Effect handlers are regular parameters pass them explicitly at the call site.
216
+ - `ctl(args) -> R` types a handler that may `unwind` (discard the continuation).
217
+ Use plain `fn(args) -> R` for handlers that always resume.
218
+ - Bundle multiple effects into a struct (`Ctx :: struct(raise : Raise, log : Log)`)
219
+ and pass one parameter when there are many.
208
220
 
209
221
  ### Closures and anonymous functions
210
222
 
@@ -214,8 +226,8 @@ caller :: (fn() -> i32)({
214
226
  result := closure(i32(5));
215
227
 
216
228
  transform :: (fn(list : ArrayList(i32), f : Impl(Fn(x : i32) -> i32)) -> unit)({
217
- for(list.iter(), (ptr) => {
218
- ptr.* = f(ptr.*);
229
+ for(list, ref(x) => {
230
+ x = f(x);
219
231
  });
220
232
  });
221
233
  ```
@@ -369,25 +381,29 @@ while(comptime((i < 10)), {
369
381
  // body evaluated/unrolled at compile time
370
382
  });
371
383
 
372
- // for loop — 2-arg prelude macro; first arg MUST be an iterator (has .next()):
373
- 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()
374
388
  process(x);
375
389
  });
376
390
 
377
- for(map.into_iter(), bucket => {
378
- println(bucket.key);
391
+ for(list, ref(x) => { // borrow form: iter() + project(pos)
392
+ x = transform(x); // writes propagate back into list
379
393
  });
380
394
 
381
- for(list.iter(), (ptr) => { // ptr is a mutable reference to each element
382
- ptr.* = transform(ptr.*);
383
- });
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));
384
398
  ```
385
399
 
386
400
  - Use `recur(...)` for self-recursion
387
401
  - `while(cond, body)` is **always a runtime loop** — use this for open-ended loops (e.g., server accept loops, event loops)
388
402
  - `while(comptime(cond), body)` explicitly unrolls at compile time — `cond` must be a compile-time-known value
389
403
  - Using a comptime-only (`::`) variable in a bare `while` condition without `comptime()` is a **compile error** (would be an infinite loop at runtime)
390
- - **`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()`.
391
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`)
392
408
 
393
409
  ## Return and branch safety
@@ -450,18 +466,22 @@ list := ArrayList(i32).new();
450
466
  list.push(i32(10));
451
467
  list.push(i32(20));
452
468
 
453
- for(list.iter(), (ptr) => {
454
- println(ptr.*);
469
+ // Value form — implicit .into_iter().
470
+ for(list, (value) => {
471
+ println(value);
455
472
  });
456
473
 
457
- for(list.into_iter(), (value) => {
458
- 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));
459
478
  });
460
479
  ```
461
480
 
462
- - `for(collection, (variable) => { body })` iterates via the `Iterator` trait
463
- - `.iter()` borrows the collection and yields pointers
464
- - `.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.
465
485
 
466
486
  ## Testing
467
487
 
@@ -480,7 +500,7 @@ test("Async test", {
480
500
  });
481
501
  ```
482
502
 
483
- - `test("description", { body })` defines a test — `io : IO` is automatically available
503
+ - `test("description", { body })` defines a test — `io : Io` is automatically available
484
504
  - All tests can use `io.async(...)`, `io.await(...)`, etc. without a `using` clause
485
505
  - `assert(condition, "message")` — runtime assertion (always include a message)
486
506
  - `comptime_assert(condition)` — compile-time assertion
@@ -602,11 +622,11 @@ list := ArrayList(i32).new();
602
622
  list.push(i32(42));
603
623
 
604
624
  val := list(usize(0)); // → i32 (value copy via Index trait)
605
- list(usize(0)) = i32(99); // mutate in place directly
625
+ list(usize(0)) = i32(99); // mutate in place directly
606
626
 
607
627
  // When you need the pointer explicitly:
608
- ptr := &(list(usize(0))); // → *(i32)
609
- ptr.* = i32(99); // also works
628
+ ptr := &(list(usize(0))); // → *(i32)
629
+ ptr.* = i32(99); // also works
610
630
 
611
631
  // Safe access (returns Option(T)):
612
632
  match(list.get(usize(0)),
@@ -620,6 +640,26 @@ match(list.get(usize(0)),
620
640
  - `&(list(i))` returns `*(T)` if you need the pointer explicitly
621
641
  - `list.get(i)` returns `Option(T)` for safe bounds-checked access
622
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
+
623
663
  ### Named fields required for `struct`/`object` constructors
624
664
 
625
665
  ```rust
@@ -724,46 +764,38 @@ if(!cond, { do_thing(); });
724
764
  if((!cond), { do_thing(); });
725
765
  ```
726
766
 
727
- ### `escape` requires a nested-function context
767
+ ### `unwind` requires a nested-function context
728
768
 
729
- `escape(value)` exits the **enclosing function** — the nearest `fn(...)` that
730
- wraps the current code. It requires that the code is inside a nested function
731
- (e.g., a closure or `given` handler lambda), NOT at the top level of a
732
- standalone function definition.
769
+ `unwind(value)` exits the **install frame** — the function that bound the
770
+ `ctl(...) -> R` value being called. It is only valid inside the body of a
771
+ `ctl(...) -> R` value (an effect handler).
733
772
 
734
773
  ```rust
735
- // CORRECT escape inside a given handler lambda (lambda has enclosing fn):
736
- given(exn) := Exception(throw: ((err) -> {
737
- escape(result); // exits the outer function that contains this given()
738
- }));
739
- do_something(using(exn));
774
+ Raise :: (ctl(msg : String) -> i32);
740
775
 
741
- // CORRECT even after process-exit helpers, satisfy the handler's resume type:
742
- given(exn2) := Exception(throw: ((err) -> {
743
- eprintln(err.to_string());
744
- exit(int(1));
745
- escape(); // required because exit() returns unit, not the handler ResumeType
746
- }));
776
+ caller :: (fn() -> i32)({
777
+ // The handler is a `ctl` value bound in `caller`. `unwind` exits `caller`.
778
+ (raise : Raise) = ((msg) -> {
779
+ eprintln(msg);
780
+ unwind(i32(-1));
781
+ });
782
+ safe_divide(i32(10), i32(0), raise) // call site: handler is passed explicitly
783
+ });
747
784
 
748
- // CORRECTescape inside a closure passed as argument:
749
- result := match(opt, .Some(x) => x, .None => {
750
- // This is NOT a nested function — use return:
751
- // WRONG: escape(default_val) // ERROR: no enclosing fn
752
- return(default_val); // CORRECT: return exits the enclosing fn directly
785
+ // WRONG`unwind` in a regular `fn` body (no install frame here) is rejected.
786
+ bad :: (fn() -> unit)({
787
+ unwind(()); // ERROR: unwind requires a ctl(...) body
753
788
  });
754
789
 
755
- // WRONG — escape inside a match arm (not a nested fn):
756
- match(opt,
757
- .Some(x) => { escape(x); } // ERROR: "can only be used inside a function that has an enclosing function"
790
+ // WRONG — capturing a `ctl` value into a closure is rejected (closures escape).
791
+ make_closure :: (fn(raise : Raise) -> Impl(Fn() -> unit))(
792
+ () => { raise(`x`); } // ERROR: closure captures a control-bound value
758
793
  );
759
794
  ```
760
795
 
761
- **Rule of thumb**: Use `escape` only inside lambdas passed to `given()` handlers.
762
- In all other contexts (match arms, if blocks, begin blocks, top-level fn bodies),
763
- use `return` for early exit.
764
-
765
- `return` inside a lambda exits that lambda only; `escape` exits the OUTER function
766
- (the one that installed the `given` handler).
796
+ **Rule of thumb**: `unwind` belongs only inside the lambda bound to a
797
+ `ctl(...) -> R` handler. From any other position, use `return` to exit the
798
+ current `fn`.
767
799
 
768
800
  ### Parameter reassignment
769
801
 
@@ -818,19 +850,19 @@ fn_taking_str((`prefix_${value}`).as_str());
818
850
 
819
851
  These features are powerful but less commonly used. Consult the linked docs for full details.
820
852
 
821
- | Feature | Syntax hint | Documentation |
822
- | ---------------------- | -------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
823
- | Higher-Kinded Types | `forall(F : (fn(comptime(T) : Type) -> comptime(Type)))` | [DESIGN.md § HKT](https://github.com/shd101wyy/Yo/blob/develop/docs/en-US/DESIGN.md#higher-kinded-types-hkt) |
824
- | GADTs | `enum(IntVal(i : i32) -> recur(i32))` | [GADTS.md](https://github.com/shd101wyy/Yo/blob/develop/docs/en-US/GADTS.md) |
825
- | Derive traits | `derive(MyType, Eq, Hash, Clone, Ord, ToString)` | [DERIVE_TRAITS.md](https://github.com/shd101wyy/Yo/blob/develop/docs/en-US/DERIVE_TRAITS.md) |
826
- | Type reflection | `Type.get_info(T)` returns `TypeInfo` | [TYPE_REFLECTION.md](https://github.com/shd101wyy/Yo/blob/develop/docs/en-US/TYPE_REFLECTION.md) |
827
- | Inline assembly | `asm("mov {0}, #42", out(reg, i32))` | [INLINE_ASSEMBLY.md](https://github.com/shd101wyy/Yo/blob/develop/docs/en-US/INLINE_ASSEMBLY.md) |
828
- | Metaprogramming | `quote(...)`, `unquote(...)`, `unquote_splicing(...)` | [DESIGN.md § Meta](https://github.com/shd101wyy/Yo/blob/develop/docs/en-US/DESIGN.md#meta-programming) |
829
- | Effect row variables | `forall(...(E))` with `using(...(E))` | [ALGEBRAIC_EFFECTS.md](https://github.com/shd101wyy/Yo/blob/develop/docs/en-US/ALGEBRAIC_EFFECTS.md) |
830
- | Custom derive rules | `derive_rule(MyTrait, (fn(...) -> unquote(Expr)){...})` | [DERIVE_TRAITS.md](https://github.com/shd101wyy/Yo/blob/develop/docs/en-US/DERIVE_TRAITS.md#user-defined-derive-rules) |
831
- | Isolated types | `Iso(T)` for data-race-free parallelism | [ISOLATED.md](https://github.com/shd101wyy/Yo/blob/develop/docs/en-US/ISOLATED.md) |
832
- | Arc (atomic ref count) | `arc(value)`, `shared.(*)` for cross-thread sharing | [ARC.md](https://github.com/shd101wyy/Yo/blob/develop/docs/en-US/ARC.md) |
833
- | Parallelism | Thread pool, `io.spawn` for parallel work | [PARALLELISM.md](https://github.com/shd101wyy/Yo/blob/develop/docs/en-US/PARALLELISM.md) |
853
+ | Feature | Syntax hint | Documentation |
854
+ | -------------------------- | -------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
855
+ | Higher-Kinded Types | `forall(F : (fn(comptime(T) : Type) -> comptime(Type)))` | [DESIGN.md § HKT](https://github.com/shd101wyy/Yo/blob/develop/docs/en-US/DESIGN.md#higher-kinded-types-hkt) |
856
+ | GADTs | `enum(IntVal(i : i32) -> recur(i32))` | [GADTS.md](https://github.com/shd101wyy/Yo/blob/develop/docs/en-US/GADTS.md) |
857
+ | Derive traits | `derive(MyType, Eq, Hash, Clone, Ord, ToString)` | [DERIVE_TRAITS.md](https://github.com/shd101wyy/Yo/blob/develop/docs/en-US/DERIVE_TRAITS.md) |
858
+ | Type reflection | `Type.get_info(T)` returns `TypeInfo` | [TYPE_REFLECTION.md](https://github.com/shd101wyy/Yo/blob/develop/docs/en-US/TYPE_REFLECTION.md) |
859
+ | Inline assembly | `asm("mov {0}, #42", out(reg, i32))` | [INLINE_ASSEMBLY.md](https://github.com/shd101wyy/Yo/blob/develop/docs/en-US/INLINE_ASSEMBLY.md) |
860
+ | Metaprogramming | `quote(...)`, `unquote(...)`, `unquote_splicing(...)` | [DESIGN.md § Meta](https://github.com/shd101wyy/Yo/blob/develop/docs/en-US/DESIGN.md#meta-programming) |
861
+ | Effect bundle polymorphism | `forall(E : Type.Struct)` over a bundle struct | [ALGEBRAIC_EFFECTS.md](https://github.com/shd101wyy/Yo/blob/develop/docs/en-US/ALGEBRAIC_EFFECTS.md) |
862
+ | Custom derive rules | `derive_rule(MyTrait, (fn(...) -> unquote(Expr)){...})` | [DERIVE_TRAITS.md](https://github.com/shd101wyy/Yo/blob/develop/docs/en-US/DERIVE_TRAITS.md#user-defined-derive-rules) |
863
+ | Isolated types | `Iso(T)` for data-race-free parallelism | [ISOLATED.md](https://github.com/shd101wyy/Yo/blob/develop/docs/en-US/ISOLATED.md) |
864
+ | Arc (atomic ref count) | `arc(value)`, `shared.(*)` for cross-thread sharing | [ARC.md](https://github.com/shd101wyy/Yo/blob/develop/docs/en-US/ARC.md) |
865
+ | Parallelism | Thread pool, `io.spawn` for parallel work | [PARALLELISM.md](https://github.com/shd101wyy/Yo/blob/develop/docs/en-US/PARALLELISM.md) |
834
866
 
835
867
  ---
836
868
 
@@ -997,26 +1029,6 @@ Always merge them into a single destructuring import:
997
1029
  { Foo, Bar } :: import("../../mod.yo");
998
1030
  ```
999
1031
 
1000
- ### Implicit (`using`) parameters cannot be used with `:=` assignment
1001
-
1002
- Implicit effect parameters introduced via `using(name : Type)` cannot be bound
1003
- to a discarded variable with `:= name`. They can only be passed via `using()`.
1004
- To suppress "unused parameter" warnings for an implicit param, simply omit the
1005
- discard assignment — implicit params never trigger unused-variable errors.
1006
-
1007
- ```rust
1008
- // WRONG — implicit variable cannot be used in assignment:
1009
- foo :: (fn(using(exn : Exception)) -> unit)({
1010
- _ := exn; // ERROR: Cannot use implicit variable "exn" in assignment
1011
- ()
1012
- });
1013
-
1014
- // CORRECT — just omit the discard line:
1015
- foo :: (fn(using(exn : Exception)) -> unit)({
1016
- ()
1017
- });
1018
- ```
1019
-
1020
1032
  ### Nested `Option` patterns require staging
1021
1033
 
1022
1034
  `match` does not support nested destructuring patterns like `.Some(.TypeVal(x))`.
package/README.md CHANGED
@@ -8,6 +8,8 @@
8
8
 
9
9
  https://shd101wyy.github.io/Yo
10
10
 
11
+ **LLM-friendly to write, human-friendly to read.**
12
+
11
13
  A multi-paradigm, general-purpose, compiled programming language.
12
14
  Yo aims to be **Simple** and **Fast** (around 0% - 15% slower than C).
13
15
 
@@ -53,7 +55,8 @@ Below is a non-exhaustive list of features that Yo supports:
53
55
  - Homoiconicity and metaprogramming (**Yo** syntax is inspired by the **Lisp** S expression. Simple syntax rule, Human & AI friendly).
54
56
  - Closure.
55
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)).
56
- - [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.
57
60
  - `object` type with [Non-atomic Reference Counting and Thread-Local Cycle Collection](./docs/en-US/CYCLE_COLLECTION.md).
58
61
  - [Compile-time Reference Counting with Ownership and Lifetime Analysis](./docs/en-US/COMPILE_TIME_RC_WITH_OWNERSHIP_ANALYSIS.md).
59
62
  - Thread-per-core parallelism model (see [PARALLELISM.md](./docs/en-US/PARALLELISM.md)).
@@ -232,7 +235,7 @@ Every Yo file automatically imports **[std/prelude.yo](./std/prelude.yo)**, whic
232
235
  - **C-compatible types**: `int`, `uint`, `short`, `long`, `longlong`, `char`, etc.
233
236
  - **Core traits**: `Eq`, `Ord`, `Add`, `Sub`, `Mul`, `Div`, `Iterator`, `IntoIterator`, `TryFrom`, `TryInto`, `Dispose`, `Send`, `Rc`, `Acyclic`, etc.
234
237
  - **Metaprogramming**: `Type`, `Expr`, `ExprList`, `Var`
235
- - **Async**: `IO`, `FutureState`, `JoinHandle`
238
+ - **Async**: `Io`, `FutureState`, `JoinHandle`
236
239
  - **Utilities**: `assert`, `unsafe`, `try`, `for`, `not`, `arc`, `Box`, `box`
237
240
  - etc.
238
241
 
@@ -376,7 +379,7 @@ This repository ships a set of **agent skill files** that teach AI agents how to
376
379
  | -------------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
377
380
  | [`yo-syntax`](.github/skills/yo-syntax/SKILL.md) | Core language syntax: curly braces, cond/match, structs, enums, operators, modules |
378
381
  | [`yo-core-patterns`](.github/skills/yo-core-patterns/SKILL.md) | Everyday patterns: types, generics, traits, error handling, collections, iterators |
379
- | [`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 |
380
383
  | [`yo-project-workflow`](.github/skills/yo-project-workflow/SKILL.md) | `yo` CLI commands, `build.yo` project files, dependency management |
381
384
 
382
385
  ### Using in your own project