@shd101wyy/yo 0.1.24 → 0.1.26

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 (104) hide show
  1. package/.github/skills/yo-async-effects/async-effects-recipes.md +6 -6
  2. package/.github/skills/yo-core-patterns/core-patterns-cheatsheet.md +34 -0
  3. package/.github/skills/yo-syntax/syntax-cheatsheet.md +441 -2
  4. package/out/cjs/index.cjs +563 -567
  5. package/out/cjs/yo-cli.cjs +656 -651
  6. package/out/cjs/yo-lsp.cjs +614 -618
  7. package/out/esm/index.mjs +582 -586
  8. package/out/types/src/codegen/codegen-c.d.ts +2 -2
  9. package/out/types/src/codegen/functions/collection.d.ts +2 -2
  10. package/out/types/src/codegen/functions/context.d.ts +3 -2
  11. package/out/types/src/codegen/types/collection.d.ts +2 -2
  12. package/out/types/src/codegen/utils/index.d.ts +4 -1
  13. package/out/types/src/doc/builder.d.ts +2 -2
  14. package/out/types/src/evaluator/calls/closure-type.d.ts +2 -2
  15. package/out/types/src/evaluator/calls/record-type.d.ts +11 -0
  16. package/out/types/src/evaluator/context.d.ts +8 -9
  17. package/out/types/src/evaluator/index.d.ts +3 -3
  18. package/out/types/src/evaluator/types/record.d.ts +14 -0
  19. package/out/types/src/evaluator/types/validation.d.ts +2 -2
  20. package/out/types/src/evaluator/values/anonymous-module.d.ts +5 -5
  21. package/out/types/src/evaluator/values/impl.d.ts +1 -1
  22. package/out/types/src/expr.d.ts +2 -4
  23. package/out/types/src/function-value.d.ts +1 -1
  24. package/out/types/src/lsp/document-manager.d.ts +1 -1
  25. package/out/types/src/module-manager.d.ts +3 -3
  26. package/out/types/src/test-runner.d.ts +1 -0
  27. package/out/types/src/types/creators.d.ts +3 -4
  28. package/out/types/src/types/definitions.d.ts +8 -19
  29. package/out/types/src/types/guards.d.ts +3 -3
  30. package/out/types/src/types/tags.d.ts +0 -1
  31. package/out/types/src/types/utils.d.ts +1 -1
  32. package/out/types/src/value-tag.d.ts +0 -1
  33. package/out/types/src/value.d.ts +6 -13
  34. package/out/types/tsconfig.tsbuildinfo +1 -1
  35. package/package.json +1 -1
  36. package/std/error.yo +6 -6
  37. package/std/prelude.yo +1 -7
  38. package/vendor/mimalloc/.github/workflows/release.yaml +55 -0
  39. package/vendor/mimalloc/.github/workflows/stale.yaml +27 -0
  40. package/vendor/mimalloc/.github/workflows/test.yaml +163 -0
  41. package/vendor/mimalloc/CMakeLists.txt +52 -33
  42. package/vendor/mimalloc/azure-pipelines.yml +4 -3
  43. package/vendor/mimalloc/bin/bundle.bat +74 -0
  44. package/vendor/mimalloc/bin/bundle.sh +232 -0
  45. package/vendor/mimalloc/cmake/mimalloc-config-version.cmake +2 -2
  46. package/vendor/mimalloc/contrib/docker/alpine/Dockerfile +1 -1
  47. package/vendor/mimalloc/contrib/docker/alpine-arm32v7/Dockerfile +2 -2
  48. package/vendor/mimalloc/contrib/docker/alpine-x86/Dockerfile +1 -1
  49. package/vendor/mimalloc/contrib/docker/manylinux-x64/Dockerfile +1 -1
  50. package/vendor/mimalloc/contrib/vcpkg/portfile.cmake +4 -3
  51. package/vendor/mimalloc/contrib/vcpkg/vcpkg.json +1 -1
  52. package/vendor/mimalloc/doc/mimalloc-doc.h +42 -4
  53. package/vendor/mimalloc/doc/release-notes.md +15 -0
  54. package/vendor/mimalloc/ide/vs2022/mimalloc-lib.vcxproj +3 -3
  55. package/vendor/mimalloc/ide/vs2022/mimalloc-override-static-lib.vcxproj +511 -0
  56. package/vendor/mimalloc/ide/vs2022/mimalloc-override-static-lib.vcxproj.filters +117 -0
  57. package/vendor/mimalloc/ide/vs2022/mimalloc-test-dep.vcxproj +360 -0
  58. package/vendor/mimalloc/ide/vs2022/mimalloc-test-override-static.vcxproj +310 -0
  59. package/vendor/mimalloc/ide/vs2022/mimalloc.sln +92 -35
  60. package/vendor/mimalloc/include/mimalloc/atomic.h +178 -182
  61. package/vendor/mimalloc/include/mimalloc/bits.h +8 -10
  62. package/vendor/mimalloc/include/mimalloc/internal.h +76 -32
  63. package/vendor/mimalloc/include/mimalloc/prim.h +25 -18
  64. package/vendor/mimalloc/include/mimalloc/track.h +7 -2
  65. package/vendor/mimalloc/include/mimalloc/types.h +57 -29
  66. package/vendor/mimalloc/include/mimalloc-override.h +10 -10
  67. package/vendor/mimalloc/include/mimalloc-stats.h +18 -6
  68. package/vendor/mimalloc/include/mimalloc.h +22 -12
  69. package/vendor/mimalloc/readme.md +42 -17
  70. package/vendor/mimalloc/src/alloc-aligned.c +13 -11
  71. package/vendor/mimalloc/src/alloc-override.c +97 -17
  72. package/vendor/mimalloc/src/alloc-posix.c +44 -27
  73. package/vendor/mimalloc/src/alloc.c +73 -23
  74. package/vendor/mimalloc/src/arena-meta.c +3 -3
  75. package/vendor/mimalloc/src/arena.c +380 -192
  76. package/vendor/mimalloc/src/bitmap.c +68 -18
  77. package/vendor/mimalloc/src/bitmap.h +8 -4
  78. package/vendor/mimalloc/src/free.c +83 -47
  79. package/vendor/mimalloc/src/heap.c +94 -40
  80. package/vendor/mimalloc/src/init.c +273 -102
  81. package/vendor/mimalloc/src/libc.c +53 -8
  82. package/vendor/mimalloc/src/options.c +43 -40
  83. package/vendor/mimalloc/src/os.c +110 -45
  84. package/vendor/mimalloc/src/page-map.c +14 -8
  85. package/vendor/mimalloc/src/page-queue.c +9 -6
  86. package/vendor/mimalloc/src/page.c +26 -16
  87. package/vendor/mimalloc/src/prim/emscripten/prim.c +10 -1
  88. package/vendor/mimalloc/src/prim/osx/alloc-override-zone.c +35 -16
  89. package/vendor/mimalloc/src/prim/unix/prim.c +26 -22
  90. package/vendor/mimalloc/src/prim/wasi/prim.c +7 -4
  91. package/vendor/mimalloc/src/prim/windows/prim.c +247 -44
  92. package/vendor/mimalloc/src/random.c +8 -3
  93. package/vendor/mimalloc/src/stats.c +59 -48
  94. package/vendor/mimalloc/src/theap.c +85 -44
  95. package/vendor/mimalloc/src/threadlocal.c +102 -41
  96. package/vendor/mimalloc/test/main-override-static.c +31 -2
  97. package/vendor/mimalloc/test/main-override.c +27 -14
  98. package/vendor/mimalloc/test/main-static-dep.cpp +46 -0
  99. package/vendor/mimalloc/test/main-static-dep.h +11 -0
  100. package/vendor/mimalloc/test/test-api-fill.c +2 -2
  101. package/vendor/mimalloc/test/test-stress.c +3 -3
  102. package/vendor/mimalloc/test/test-wrong.c +11 -7
  103. package/out/types/src/evaluator/calls/module-type.d.ts +0 -11
  104. package/out/types/src/evaluator/types/module.d.ts +0 -19
@@ -186,7 +186,7 @@ process_dir :: (fn(root: Path, using(io: IO, exn: Exception)) -> Impl(Future(uni
186
186
 
187
187
  ## Exception (non-resumable)
188
188
 
189
- `Exception` is a built-in module effect for non-resumable error handling. When the handler calls `escape`, the continuation is discarded:
189
+ `Exception` is a built-in struct-record effect for non-resumable error handling. When the handler calls `escape`, the continuation is discarded:
190
190
 
191
191
  ```rust
192
192
  open import "std/error";
@@ -251,7 +251,7 @@ Key: the `return` inside the handler resumes the _effect invocation site_ with t
251
251
 
252
252
  **`escape T_value` constraint**: `escape T_value` inside an `Exception` handler requires that the enclosing `io.async` closure's return type matches `T_value`. Due to forward type inference, the evaluator may not know the closure's return type at the point where `given` is declared. This causes a "Expected: unit" error when `escape non_unit` is used in a handler declared before the final return expression. Prefer `return fallback_value` (resume) when possible.
253
253
 
254
- `ResumableException(ResumeType)` is a module effect for resumable error handling. The handler uses `return` to resume with a recovery value:
254
+ `ResumableException(ResumeType)` is a struct-record effect for resumable error handling. The handler uses `return` to resume with a recovery value:
255
255
 
256
256
  ```rust
257
257
  open import "std/error";
@@ -282,19 +282,19 @@ export main;
282
282
  - Handler uses `return value` to resume the continuation with the recovery value
283
283
  - The call site receives the returned value and continues normally
284
284
 
285
- ## Module effects vs function-type effects
285
+ ## Struct-record effects vs function-type effects
286
286
 
287
- Effects in Yo can be plain function types or module types:
287
+ Effects in Yo can be plain function types or struct-record types:
288
288
 
289
289
  ```rust
290
290
  Raise :: (fn(msg : String) -> i32);
291
291
 
292
- Logger :: module(
292
+ Logger :: struct(
293
293
  log : (fn(level : i32, msg : String) -> unit)
294
294
  );
295
295
  ```
296
296
 
297
- Both kinds use `using(...)` / `given(...)` with the same semantics — they compile to evidence passing (function pointers as implicit C parameters). Module effects group related operations under a single name.
297
+ Both kinds use `using(...)` / `given(...)` with the same semantics — they compile to evidence passing (function pointers as implicit C parameters). Struct-record effects group related operations under a single nominal type.
298
298
 
299
299
  ## Async with effects
300
300
 
@@ -190,6 +190,10 @@ TcpStream :: object(fd : i32, buffer : ArrayList(u8));
190
190
 
191
191
  - Use `newtype(...)` when the type has exactly one field
192
192
  - Use `object(...)` for types that need shared ownership
193
+ - Source-file imports are namespace structs. The old `module(...)`, `Module`,
194
+ and `SelfModule` syntax is gone; use `struct(...)`, `Type`, and normal `Self`.
195
+ - Fields written as `name :: value` or `comptime(name) : Type` are compile-time-only static fields/methods and have no runtime layout
196
+ - A normal field with a compile-time-only type, such as `x : comptime_int`, is still a data field and makes the struct comptime-only
193
197
 
194
198
  ## Impl blocks and generics
195
199
 
@@ -310,6 +314,21 @@ Yo's reference counting handles shallow copies automatically (no `Clone` trait c
310
314
  the `Clone` trait is only for deep cloning and has the same circularity problem.
311
315
  In practice, passing `EvalValue`-like types by value works fine without a `Clone` impl.
312
316
 
317
+ ### Comparing complex enum types
318
+
319
+ Complex enum types (structs/enums with nested fields) do not support `==`/`!=` unless `Eq` is
320
+ derived or implemented. For **tag-only equality** (checking which variant), use a tag function:
321
+
322
+ ```rust
323
+ // WRONG — TypeValue enum doesn't support !=
324
+ if(my_type != t_unit(), { ... });
325
+
326
+ // CORRECT — compare tags instead
327
+ { type_value_tag } :: import "../../types/type.yo";
328
+ { TypeTag } :: import "../../types/tags.yo";
329
+ if((type_value_tag(my_type) != TypeTag.TUnit), { ... });
330
+ ```
331
+
313
332
  ## Error handling
314
333
 
315
334
  ```rust
@@ -403,3 +422,18 @@ result := my_module.helper(i32(5));
403
422
 
404
423
  - `impl { ... }` creates a module namespace
405
424
  - Only `::` (compile-time) bindings are allowed inside
425
+
426
+ ## yo-self API: String vs str parameter gotchas
427
+
428
+ Several `yo-self/` APIs take `String` (not `str`) parameters even when the argument is conceptually a name:
429
+
430
+ - `get_variables_from_env(env, name: String)` — pass the `String` directly, do NOT call `.as_str()` first
431
+ - Most other env/value/type lookup functions follow the same convention
432
+
433
+ ```rust
434
+ // ❌ Wrong — .as_str() converts String → str but the param is String
435
+ vars := get_variables_from_env(env, prop_name_su.as_str());
436
+
437
+ // ✅ Correct — pass the String directly
438
+ vars := get_variables_from_env(env, prop_name_su);
439
+ ```
@@ -90,6 +90,7 @@ Key rules:
90
90
  - In **runtime** code, `"hello"` is `str`. Mixing literals and variables in `cond`/`match` branches is fine.
91
91
  - In **comptime** functions (return type `comptime(...)`), `"hello"` is `comptime_string` — it does NOT auto-convert to `str`.
92
92
  - For `String` constants, prefer `` `hello` `` over `String.from("hello")`.
93
+ - **`String.from(`` `...` ``)` is WRONG**: `` `...` `` is already `String`; `String.from` takes `str`. Use `` `...` `` directly or `String.from("...")` with double quotes.
93
94
  - **`assert` takes `str`, not `String`**: `assert(cond, "message")` — always use `""`. Passing a template string `` `...` `` causes a type mismatch. Use a custom `check_str` helper when you need `String` diagnostics.
94
95
 
95
96
  ## Calls, operators, and whitespace
@@ -103,9 +104,10 @@ masked := ((A | B) | C);
103
104
  - Prefer parenthesized calls: `func(arg1, arg2)`
104
105
  - `func (a, b)` is a different parse shape than `func(a, b)`
105
106
  - Yo has no operator precedence; fully parenthesize binary expressions
106
- - **All unary operators (`!`, `&`, `-`, `~`) greedily consume everything that follows, including comma-separated args.** `func(&s, a, b)` is parsed as `func(&(s, a, b))` — ONE tuple argument! Always wrap: `p := &s; func(p, a, b)` or use `func((&s), a, b)`.
107
+ - **All unary operators (`!`, `&`, `-`, `~`) greedily consume everything that follows, including comma-separated args.** `func(&s, a, b)` is parsed as `func(&(s, a, b))` — ONE tuple argument! Preferred fix: parenthesize the operand: `func(&(s), a, b)`. The outer-parens form `func((&s), a, b)` also works. Either is fine; the operand-parens form `&(x)` matches how the parser thinks about it.
107
108
  - Parenthesize other unary operands too: `!(ready)`, `-(value)`
108
109
  - **`!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.
110
+ - **Nested `&&` / `||` in a single compound condition causes "Ambiguous operator precedence"** even with explicit parentheses: `((A && B) && (C && D))` on one line triggers the error. Fix: extract sub-conditions into named booleans first: `_c1 := (A && B); _c2 := (C && D); if((_c1 && _c2), ...)`.
109
111
 
110
112
  ## Functions and methods
111
113
 
@@ -127,6 +129,11 @@ impl(Counter,
127
129
  - No space between a function type and its body: `(fn(...) -> T)(...)`
128
130
  - Use `Self` in method signatures and in type definitions for recursive references (the type name is not available during its own definition)
129
131
  - `Self` also works inside generic type constructors — it refers to the current instantiation (e.g., `Tree(T)` inside `Tree`). Use `recur(args)` only when type arguments differ from the current instantiation.
132
+ - Use `struct(...)` for record and effect-record types. The legacy `module(...)`,
133
+ `Module`, and `SelfModule` syntax has been removed; imported files are
134
+ represented as namespace structs, and recursive references use normal `Self`.
135
+ - Bare `Module` is not a type alias. Use `Type` for comptime type values; type
136
+ reflection reports source-module namespaces as `TypeInfo.Struct(...)`.
130
137
  - Wrap `fn` types in parentheses when they appear after `:`
131
138
  - **Forward references between methods in the same `impl` block are supported.** A method defined later in the block can be called by a method defined earlier. Both `self.method()` and `Self.method(...)` dispatch work. Only the canonical `name : (fn(...) -> R)(body)` method shape participates; bare lambdas do not get forward-ref shells.
132
139
  - **Module-level `::` function definitions are processed in order.** A function body that calls another function declared later in the same file will fail with "Variable not found". Always define leaf helpers first (bottom-up order): `eval_identifier` → `eval_atom` → `evaluate`.
@@ -241,6 +248,8 @@ match(s,
241
248
 
242
249
  Curly `{a, b: c}` is sugar for `(a: a, b: c)` — order-free, supports partial matches (omit fields). Use `{label: _}` to ignore a specific field. Bare `{_}` and empty `{}` are rejected.
243
250
 
251
+ > **Critical**: Within a single match arm, you must use **either all positional or all named** field patterns. Mixing positional and named fields in the same arm (e.g., `.Foo(x, y: z, w)`) causes C codegen to emit undeclared identifiers for the named fields. This is a parser/codegen limitation — do not mix.
252
+
244
253
  ## Generics and compile-time
245
254
 
246
255
  ```rust
@@ -321,12 +330,27 @@ while true, {
321
330
  while comptime(i < 10), {
322
331
  // body evaluated/unrolled at compile time
323
332
  };
333
+
334
+ // for loop — 2-arg prelude macro; first arg MUST be an iterator (has .next()):
335
+ for(list.iter(), x => { // ArrayList, array → call .iter() first
336
+ process(x);
337
+ });
338
+
339
+ for(map.into_iter(), bucket => {
340
+ println(bucket.key);
341
+ });
342
+
343
+ for(list.iter(), (ptr) => { // ptr is a mutable reference to each element
344
+ ptr.* = transform(ptr.*);
345
+ });
324
346
  ```
325
347
 
326
348
  - Use `recur(...)` for self-recursion
327
349
  - `while cond` is **always a runtime loop** — use this for open-ended loops (e.g., server accept loops, event loops)
328
350
  - `while comptime(cond)` explicitly unrolls at compile time — `cond` must be a compile-time-known value
329
351
  - Using a comptime-only (`::`) variable in a bare `while` condition without `comptime()` is a **compile error** (would be an infinite loop at runtime)
352
+ - **`for(arr, item => { body })`** — correct 2-arg prelude macro form. The `item => { body }` is an anonymous closure.
353
+ - **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`)
330
354
 
331
355
  ## Return and branch safety
332
356
 
@@ -426,6 +450,35 @@ test "Async test", {
426
450
 
427
451
  ## Common pitfalls
428
452
 
453
+ ### `&&` short-circuit with `match`/`cond` on RHS causes C codegen scope bug
454
+
455
+ Using `&&` where the right-hand side is a `match` or `cond` expression causes
456
+ a C codegen bug: the temp variable for the RHS is declared inside the short-circuit
457
+ `if` block but the cleanup drop is emitted outside it. This produces a C compile
458
+ error ("use of undeclared identifier").
459
+
460
+ ```rust
461
+ // WRONG — triggers codegen scope bug:
462
+ is_ok := (av.is_compile_time_only && match(av.value,
463
+ .Some(v) => compute(v),
464
+ .None => false
465
+ ));
466
+
467
+ // CORRECT — use an explicit if block to scope the match:
468
+ (is_ok : bool) = false;
469
+ if(av.is_compile_time_only, {
470
+ is_ok = match(av.value,
471
+ .Some(v) => compute(v),
472
+ .None => false
473
+ );
474
+ });
475
+ ```
476
+
477
+ This only affects `&&`/`||` where the **right-hand side contains a `match`,
478
+ `cond`, or other expression that allocates heap-managed temporaries** (e.g.,
479
+ `String`, `ArrayList`, `Option(HeapType)`). Pure boolean expressions on both
480
+ sides are fine.
481
+
429
482
  ### `impl(...)` requires a trailing semicolon
430
483
 
431
484
  ```rust
@@ -455,6 +508,28 @@ foo();
455
508
  bar();
456
509
  ```
457
510
 
511
+ ### Enum pattern matching does NOT support literal values
512
+
513
+ Match patterns on enum variants only support **variable binding**, not literal comparison.
514
+ `.BoolVal(true)` binds the inner value to a variable named `true` — it does NOT check
515
+ if the value is `true`. The arm always matches any `BoolVal`.
516
+
517
+ ```rust
518
+ // ❌ WRONG — always matches (true is a variable binding, not a comparison)
519
+ match(val,
520
+ .BoolVal(true) => handle_true(),
521
+ _ => ()
522
+ );
523
+
524
+ // ✅ CORRECT — bind to variable, then check with cond
525
+ match(val,
526
+ .BoolVal(b) => cond(b => handle_true(), true => ()),
527
+ _ => ()
528
+ );
529
+ ```
530
+
531
+ Same applies to `.IntLit(42)`, `.StrLit("hello")`, etc.
532
+
458
533
  ### `type` is a reserved keyword — avoid as field/param name
459
534
 
460
535
  ```rust
@@ -545,7 +620,44 @@ This applies to ALL callee-before-caller relationships:
545
620
  - `print_build_summary` before `execute_step`
546
621
  - Exports section must come AFTER all definitions
547
622
 
548
- ### `if(!cond, block)` use parentheses
623
+ ### Named tuple fields in type syntax are not allowed
624
+
625
+ Yo does not support named tuple field types in the syntax `(name : Type, ...)`. Use an `object` struct instead:
626
+
627
+ ```rust
628
+ // WRONG — "Labelled field is not allowed in tuple value":
629
+ get_range :: (fn(ty : TypeValue) -> Option((min : i64, max : u64)))(
630
+ .Some((min: i64(-128), max: u64(127)))
631
+ );
632
+
633
+ // CORRECT — define a named struct:
634
+ Range :: object(min : i64, max : u64);
635
+ get_range :: (fn(ty : TypeValue) -> Option(Range))(
636
+ .Some({min: i64(-128), max: u64(127)})
637
+ );
638
+ ```
639
+
640
+ Named fields work in struct/object constructors `{min: ..., max: ...}`, just not in the type syntax for tuples.
641
+
642
+ ### `Option` equality comparison limitations
643
+
644
+ Comparing `Option(T)` with `== .None` or `== .Some(...)` fails when the inner type `T` lacks a derived `Eq` implementation or when `.None` is type-ambiguous. Always use the method API:
645
+
646
+ ```rust
647
+ // WRONG — "No matching call found with arguments: r == .None":
648
+ assert((r == .None), "should be None");
649
+
650
+ // CORRECT — use .is_none() / .is_some() / .unwrap():
651
+ assert(r.is_none(), "should be None");
652
+ assert(r.is_some(), "should be Some");
653
+ assert((r.unwrap() == expected_value), "value check");
654
+
655
+ // Also fine in match:
656
+ match(r,
657
+ .Some(v) => assert((v == expected_value), "value check"),
658
+ .None => assert(false, "unexpected None")
659
+ );
660
+ ```
549
661
 
550
662
  The `!` operator is greedy and consumes all following args including the block. Always parenthesize:
551
663
 
@@ -557,6 +669,79 @@ if(!cond, { do_thing(); });
557
669
  if((!cond), { do_thing(); });
558
670
  ```
559
671
 
672
+ ### `escape` requires a nested-function context
673
+
674
+ `escape value` exits the **enclosing function** — the nearest `fn(...)` that
675
+ wraps the current code. It requires that the code is inside a nested function
676
+ (e.g., a closure or `given` handler lambda), NOT at the top level of a
677
+ standalone function definition.
678
+
679
+ ```rust
680
+ // CORRECT — escape inside a given handler lambda (lambda has enclosing fn):
681
+ given(exn) := Exception(throw: ((err) -> {
682
+ escape result // exits the outer function that contains this given()
683
+ }));
684
+ do_something(using(exn));
685
+
686
+ // CORRECT — escape inside a closure passed as argument:
687
+ result := match(opt, .Some(x) => x, .None => {
688
+ // This is NOT a nested function — use return:
689
+ // WRONG: escape default_val // ERROR: no enclosing fn
690
+ return default_val // CORRECT: return exits the enclosing fn directly
691
+ });
692
+
693
+ // WRONG — escape inside a match arm (not a nested fn):
694
+ match(opt,
695
+ .Some(x) => { escape x } // ERROR: "can only be used inside a function that has an enclosing function"
696
+ );
697
+ ```
698
+
699
+ **Rule of thumb**: Use `escape` only inside lambdas passed to `given()` handlers.
700
+ In all other contexts (match arms, if blocks, begin blocks, top-level fn bodies),
701
+ use `return` for early exit.
702
+
703
+ `return` inside a lambda exits that lambda only; `escape` exits the OUTER function
704
+ (the one that installed the `given` handler).
705
+
706
+ ### Parameter reassignment
707
+
708
+ Function parameters are **NOT reassignable**. To reassign, declare a mutable local:
709
+
710
+ ```rust
711
+ // WRONG — cannot reassign parameter 'env':
712
+ my_fn :: (fn(env : Environment) -> Environment)({
713
+ env = other_env; // ERROR: "cannot reassign itself"
714
+ env
715
+ });
716
+
717
+ // CORRECT — create a mutable local copy:
718
+ my_fn :: (fn(init_env : Environment) -> Environment)({
719
+ (env : Environment) = init_env;
720
+ env = other_env; // OK — reassigning local variable
721
+ env
722
+ });
723
+ ```
724
+
725
+ This also applies to `object` types: you can mutate fields (`env.field = val`)
726
+ but cannot rebind the variable (`env = other_env`).
727
+
728
+ ### String cloning
729
+
730
+ Calling `.clone()` on a `String` field from a struct/method chain requires a
731
+ reference — take `&` first:
732
+
733
+ ```rust
734
+ // WRONG — .clone() requires *(Self) but gets Self value from field access:
735
+ name := token.value.clone(); // ERROR
736
+
737
+ // CORRECT — take reference first:
738
+ tok := some_fn_call();
739
+ name := (&tok.value).clone(); // OK
740
+
741
+ // ALSO CORRECT — use String.from on the str slice:
742
+ name := String.from(token.value.as_str());
743
+ ```
744
+
560
745
  ### Template strings produce `String`, literals are `str`
561
746
 
562
747
  ```rust
@@ -584,3 +769,257 @@ These features are powerful but less commonly used. Consult the linked docs for
584
769
  | Isolated types | `Iso(T)` for data-race-free parallelism | [ISOLATED.md](https://github.com/shd101wyy/Yo/blob/develop/docs/en-US/ISOLATED.md) |
585
770
  | 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) |
586
771
  | Parallelism | Thread pool, `io.spawn` for parallel work | [PARALLELISM.md](https://github.com/shd101wyy/Yo/blob/develop/docs/en-US/PARALLELISM.md) |
772
+
773
+ ---
774
+
775
+ ## Self-hosted evaluator (`yo-self/`) — known limitations and pitfalls
776
+
777
+ These constraints apply **only** to the self-hosted Yo evaluator (code inside `yo-self/`). The TypeScript-compiled Yo compiler does not have these restrictions.
778
+
779
+ ### Match patterns: no bare identifier catch-all
780
+
781
+ The self-hosted parser only accepts three match arm forms:
782
+
783
+ - `.VariantName` — unit variant
784
+ - `.VariantName(p1, p2, ...)` — tuple variant
785
+ - `_` — wildcard
786
+
787
+ **Bare identifier catch-all `t => ...` is NOT supported.** Use `_` and access the outer binding directly:
788
+
789
+ ```rust
790
+ // ❌ NOT supported in yo-self/
791
+ match(val, {
792
+ .SomeVariant(x) => x,
793
+ t => t, // ERROR: bare identifier not a valid pattern
794
+ })
795
+
796
+ // ✅ Correct — use outer binding
797
+ match(val, {
798
+ .SomeVariant(x) => x,
799
+ _ => val, // refer to outer binding
800
+ })
801
+ ```
802
+
803
+ ### String concatenation: no `String + str` operator
804
+
805
+ The `+` operator does not accept mixed `String`/`str` operands.
806
+ **Always use template strings** for concatenation:
807
+
808
+ ```rust
809
+ // ❌ Type error
810
+ result := (parts + ", ");
811
+ result := (parts + item.as_str());
812
+
813
+ // ✅ Template strings
814
+ result := `${parts}, `;
815
+ result := `${parts}${item}`;
816
+ ```
817
+
818
+ ### `clone()` on extracted String fields is ambiguous
819
+
820
+ When a `String` field is bound in a match arm, calling `.clone()` triggers an ambiguity error (two impls: `fn(self: String)` and `fn(self: *(String))`).
821
+
822
+ ```rust
823
+ // ❌ Ambiguous
824
+ .StructVal(name, fields) => name.clone()
825
+
826
+ // ✅ Use from + as_str
827
+ .StructVal(name, fields) => String.from(name.as_str())
828
+ ```
829
+
830
+ ### `box(val)` is a move — cannot box the same value twice
831
+
832
+ ```rust
833
+ // ❌ Move error: target is moved by first box(target)
834
+ p1 := PtrVal(box(target), usize(0));
835
+ p2 := PtrVal(box(target), usize(0)); // ERROR: target already moved
836
+
837
+ // ✅ Create separate instances
838
+ p1 := PtrVal(box(EvalValue.IntLit(String.from("42"))), usize(0));
839
+ p2 := PtrVal(box(EvalValue.IntLit(String.from("42"))), usize(0));
840
+ ```
841
+
842
+ ### `recur(...)` for self-recursive lambdas
843
+
844
+ Lambdas defined as `name :: (fn(args) -> T)(body)` cannot call `name` inside `body`.
845
+ Use `recur(...)` instead:
846
+
847
+ ```rust
848
+ // ❌ Would not find `my_fn` inside its own body
849
+ my_fn :: (fn(x : i32) -> i32)({
850
+ my_fn(x - 1) // ERROR: `my_fn` not in scope yet
851
+ });
852
+
853
+ // ✅ Use recur
854
+ my_fn :: (fn(x : i32) -> i32)({
855
+ recur(x - 1)
856
+ });
857
+ ```
858
+
859
+ ### `{ expr }` without semicolons is a struct literal, not a block
860
+
861
+ ```rust
862
+ // ❌ Parsed as struct literal `{ match(...) }`
863
+ fn :: (fn() -> T)({ match(x, arms) })
864
+
865
+ // ✅ Remove braces or add semicolon
866
+ fn :: (fn() -> T)(match(x, arms))
867
+ fn :: (fn() -> T)({ match(x, arms); })
868
+ ```
869
+
870
+ ### Template strings cannot be nested inside `${...}` interpolations
871
+
872
+ A template string literal (`` ` `` ... `` ` ``) inside a `${...}` interpolation of another template string closes the outer string. The compiler gives confusing parse errors.
873
+
874
+ ```rust
875
+ // ❌ Inner backtick closes the outer template string — parse error
876
+ lines.push(`**Implements:** ${`, `.join(names)}`);
877
+
878
+ // ✅ Assign the separator to a variable first
879
+ sep := `, `;
880
+ lines.push(`**Implements:** ${sep.join(names)}`);
881
+ ```
882
+
883
+ ### Pushing RC struct fields into ArrayList does not need `.clone()`
884
+
885
+ String (and other RC object) fields of structs can be passed directly to `ArrayList.push()`. Calling `.clone()` triggers an ambiguity error between `fn(self: String)` and `fn(self: *(String))` overloads.
886
+
887
+ ```rust
888
+ // ❌ Ambiguous clone call
889
+ names.push(param.name.clone());
890
+
891
+ // ✅ Push directly — RC bump happens automatically
892
+ names.push(param.name);
893
+ ```
894
+
895
+ If explicit clone is needed elsewhere, use `(&field).clone()` to select the pointer overload.
896
+
897
+ ### `.Some(expr)` in expression position is parsed as a 2-arg property access
898
+
899
+ Using `.Some(x)` as an expression (not inside a match pattern) is parsed by the Yo parser
900
+ as a 2-arg dot property access: `obj.(prop, arg)`. This means `evaluate_property_access` is
901
+ invoked on it at compile time, causing confusing errors like "Failed to infer enum variant type".
902
+
903
+ ```rust
904
+ // ❌ Parsed as 2-arg property access — NOT an Option::Some constructor call
905
+ val := .Some(oi.ty);
906
+
907
+ // ✅ Use the explicit fully-qualified form
908
+ val := Option(TypeValue).Some(oi.ty);
909
+ ```
910
+
911
+ ### `||` chaining requires explicit parentheses for 3+ operands
912
+
913
+ Chaining three or more `||` terms in a single expression is rejected with a precedence error.
914
+ Always add explicit parentheses around each pair:
915
+
916
+ ```rust
917
+ // ❌ Rejected — ambiguous precedence
918
+ if ((is_tuple_type(ty) || is_struct_type(ty) || is_union_type(ty)), ...)
919
+
920
+ // ✅ Parenthesise each pair
921
+ if (((is_tuple_type(ty) || is_struct_type(ty)) || is_union_type(ty)), ...)
922
+ ```
923
+
924
+ ### Duplicate imports from the same path must be merged
925
+
926
+ Having two `:: import "path"` lines importing from the same file causes a compile error.
927
+ Always merge them into a single destructuring import:
928
+
929
+ ```rust
930
+ // ❌ Two imports from the same path
931
+ { Foo } :: import "../../mod.yo";
932
+ { Bar } :: import "../../mod.yo";
933
+
934
+ // ✅ Merged
935
+ { Foo, Bar } :: import "../../mod.yo";
936
+ ```
937
+
938
+ ### Implicit (`using`) parameters cannot be used with `:=` assignment
939
+
940
+ Implicit effect parameters introduced via `using(name : Type)` cannot be bound
941
+ to a discarded variable with `:= name`. They can only be passed via `using()`.
942
+ To suppress "unused parameter" warnings for an implicit param, simply omit the
943
+ discard assignment — implicit params never trigger unused-variable errors.
944
+
945
+ ```rust
946
+ // WRONG — implicit variable cannot be used in assignment:
947
+ foo :: (fn(using(exn : Exception)) -> unit)({
948
+ _ := exn; // ERROR: Cannot use implicit variable "exn" in assignment
949
+ ()
950
+ });
951
+
952
+ // CORRECT — just omit the discard line:
953
+ foo :: (fn(using(exn : Exception)) -> unit)({
954
+ ()
955
+ });
956
+ ```
957
+
958
+ ### Nested `Option` patterns require staging
959
+
960
+ `match` does not support nested destructuring patterns like `.Some(.TypeVal(x))`.
961
+ Split into two separate `match` expressions.
962
+
963
+ ```rust
964
+ // WRONG — nested option pattern:
965
+ match(opt_value,
966
+ .Some(.TypeVal(box)) => { ... }, // ERROR
967
+ _ => { ... }
968
+ );
969
+
970
+ // CORRECT — match in two stages:
971
+ match(opt_value,
972
+ .Some(v) => match(v,
973
+ .TypeVal(box) => { ... },
974
+ _ => { ... }
975
+ ),
976
+ .None => { ... }
977
+ );
978
+ ```
979
+
980
+ ### Outer match on `Option` must have `.None` arm
981
+
982
+ When using `match(opt, .Some(x) => match(x, ...), ...)`, the outer match
983
+ needs its own `.None` arm. The inner match's `_ =>` wildcard does NOT cover
984
+ the outer match's `.None` variant.
985
+
986
+ ```rust
987
+ // WRONG — outer match missing .None:
988
+ match(opt_callee_value,
989
+ .Some(cv) => match(cv,
990
+ .Foo(x) => { ... },
991
+ _ => { throw_phase3() } // inner wildcard, does NOT cover outer .None
992
+ ) // outer match closes here — .None uncovered!
993
+ );
994
+
995
+ // CORRECT — add explicit .None arm to outer match:
996
+ match(opt_callee_value,
997
+ .Some(cv) => match(cv,
998
+ .Foo(x) => { ... },
999
+ _ => { throw_phase3() }
1000
+ ),
1001
+ .None => { throw_phase3_none() }
1002
+ );
1003
+ ```
1004
+
1005
+ ### Parenthesis balance in deeply nested matches
1006
+
1007
+ When using `match(outer, .Some(x) => match(inner, ...), .None => ...)`,
1008
+ count parentheses carefully:
1009
+
1010
+ - The inner `match(inner, ...)` closes with its own `)`
1011
+ - AFTER that `)`, add a `,` then the outer `.None =>` arm
1012
+ - The outer match closes with its own `)`
1013
+ - Only then does `});` close the function body
1014
+
1015
+ ```rust
1016
+ // Correct structure:
1017
+ match(outer_val,
1018
+ .Some(x) => match(x,
1019
+ arm1,
1020
+ arm2,
1021
+ _ => { fallback() } // last inner arm, no trailing comma
1022
+ ), // ← closes inner match; `,` continues outer
1023
+ .None => { fallback() } // ← outer .None arm
1024
+ ) // ← closes outer match
1025
+ ```