@shd101wyy/yo 0.1.24 → 0.1.25

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 (76) hide show
  1. package/.github/skills/yo-core-patterns/core-patterns-cheatsheet.md +30 -0
  2. package/.github/skills/yo-syntax/syntax-cheatsheet.md +436 -2
  3. package/out/cjs/index.cjs +551 -553
  4. package/out/cjs/yo-cli.cjs +638 -632
  5. package/out/cjs/yo-lsp.cjs +595 -597
  6. package/out/esm/index.mjs +487 -489
  7. package/out/types/src/codegen/utils/index.d.ts +1 -0
  8. package/out/types/src/expr.d.ts +1 -0
  9. package/out/types/src/test-runner.d.ts +1 -0
  10. package/out/types/tsconfig.tsbuildinfo +1 -1
  11. package/package.json +1 -1
  12. package/vendor/mimalloc/.github/workflows/release.yaml +55 -0
  13. package/vendor/mimalloc/.github/workflows/stale.yaml +27 -0
  14. package/vendor/mimalloc/.github/workflows/test.yaml +163 -0
  15. package/vendor/mimalloc/CMakeLists.txt +52 -33
  16. package/vendor/mimalloc/azure-pipelines.yml +4 -3
  17. package/vendor/mimalloc/bin/bundle.bat +74 -0
  18. package/vendor/mimalloc/bin/bundle.sh +232 -0
  19. package/vendor/mimalloc/cmake/mimalloc-config-version.cmake +2 -2
  20. package/vendor/mimalloc/contrib/docker/alpine/Dockerfile +1 -1
  21. package/vendor/mimalloc/contrib/docker/alpine-arm32v7/Dockerfile +2 -2
  22. package/vendor/mimalloc/contrib/docker/alpine-x86/Dockerfile +1 -1
  23. package/vendor/mimalloc/contrib/docker/manylinux-x64/Dockerfile +1 -1
  24. package/vendor/mimalloc/contrib/vcpkg/portfile.cmake +4 -3
  25. package/vendor/mimalloc/contrib/vcpkg/vcpkg.json +1 -1
  26. package/vendor/mimalloc/doc/mimalloc-doc.h +42 -4
  27. package/vendor/mimalloc/doc/release-notes.md +15 -0
  28. package/vendor/mimalloc/ide/vs2022/mimalloc-lib.vcxproj +3 -3
  29. package/vendor/mimalloc/ide/vs2022/mimalloc-override-static-lib.vcxproj +511 -0
  30. package/vendor/mimalloc/ide/vs2022/mimalloc-override-static-lib.vcxproj.filters +117 -0
  31. package/vendor/mimalloc/ide/vs2022/mimalloc-test-dep.vcxproj +360 -0
  32. package/vendor/mimalloc/ide/vs2022/mimalloc-test-override-static.vcxproj +310 -0
  33. package/vendor/mimalloc/ide/vs2022/mimalloc.sln +92 -35
  34. package/vendor/mimalloc/include/mimalloc/atomic.h +178 -182
  35. package/vendor/mimalloc/include/mimalloc/bits.h +8 -10
  36. package/vendor/mimalloc/include/mimalloc/internal.h +76 -32
  37. package/vendor/mimalloc/include/mimalloc/prim.h +25 -18
  38. package/vendor/mimalloc/include/mimalloc/track.h +7 -2
  39. package/vendor/mimalloc/include/mimalloc/types.h +57 -29
  40. package/vendor/mimalloc/include/mimalloc-override.h +10 -10
  41. package/vendor/mimalloc/include/mimalloc-stats.h +18 -6
  42. package/vendor/mimalloc/include/mimalloc.h +22 -12
  43. package/vendor/mimalloc/readme.md +42 -17
  44. package/vendor/mimalloc/src/alloc-aligned.c +13 -11
  45. package/vendor/mimalloc/src/alloc-override.c +97 -17
  46. package/vendor/mimalloc/src/alloc-posix.c +44 -27
  47. package/vendor/mimalloc/src/alloc.c +73 -23
  48. package/vendor/mimalloc/src/arena-meta.c +3 -3
  49. package/vendor/mimalloc/src/arena.c +380 -192
  50. package/vendor/mimalloc/src/bitmap.c +68 -18
  51. package/vendor/mimalloc/src/bitmap.h +8 -4
  52. package/vendor/mimalloc/src/free.c +83 -47
  53. package/vendor/mimalloc/src/heap.c +94 -40
  54. package/vendor/mimalloc/src/init.c +273 -102
  55. package/vendor/mimalloc/src/libc.c +53 -8
  56. package/vendor/mimalloc/src/options.c +43 -40
  57. package/vendor/mimalloc/src/os.c +110 -45
  58. package/vendor/mimalloc/src/page-map.c +14 -8
  59. package/vendor/mimalloc/src/page-queue.c +9 -6
  60. package/vendor/mimalloc/src/page.c +26 -16
  61. package/vendor/mimalloc/src/prim/emscripten/prim.c +10 -1
  62. package/vendor/mimalloc/src/prim/osx/alloc-override-zone.c +35 -16
  63. package/vendor/mimalloc/src/prim/unix/prim.c +26 -22
  64. package/vendor/mimalloc/src/prim/wasi/prim.c +7 -4
  65. package/vendor/mimalloc/src/prim/windows/prim.c +247 -44
  66. package/vendor/mimalloc/src/random.c +8 -3
  67. package/vendor/mimalloc/src/stats.c +59 -48
  68. package/vendor/mimalloc/src/theap.c +85 -44
  69. package/vendor/mimalloc/src/threadlocal.c +102 -41
  70. package/vendor/mimalloc/test/main-override-static.c +31 -2
  71. package/vendor/mimalloc/test/main-override.c +27 -14
  72. package/vendor/mimalloc/test/main-static-dep.cpp +46 -0
  73. package/vendor/mimalloc/test/main-static-dep.h +11 -0
  74. package/vendor/mimalloc/test/test-api-fill.c +2 -2
  75. package/vendor/mimalloc/test/test-stress.c +3 -3
  76. package/vendor/mimalloc/test/test-wrong.c +11 -7
@@ -310,6 +310,21 @@ Yo's reference counting handles shallow copies automatically (no `Clone` trait c
310
310
  the `Clone` trait is only for deep cloning and has the same circularity problem.
311
311
  In practice, passing `EvalValue`-like types by value works fine without a `Clone` impl.
312
312
 
313
+ ### Comparing complex enum types
314
+
315
+ Complex enum types (structs/enums with nested fields) do not support `==`/`!=` unless `Eq` is
316
+ derived or implemented. For **tag-only equality** (checking which variant), use a tag function:
317
+
318
+ ```rust
319
+ // WRONG — TypeValue enum doesn't support !=
320
+ if(my_type != t_unit(), { ... });
321
+
322
+ // CORRECT — compare tags instead
323
+ { type_value_tag } :: import "../../types/type.yo";
324
+ { TypeTag } :: import "../../types/tags.yo";
325
+ if((type_value_tag(my_type) != TypeTag.TUnit), { ... });
326
+ ```
327
+
313
328
  ## Error handling
314
329
 
315
330
  ```rust
@@ -403,3 +418,18 @@ result := my_module.helper(i32(5));
403
418
 
404
419
  - `impl { ... }` creates a module namespace
405
420
  - Only `::` (compile-time) bindings are allowed inside
421
+
422
+ ## yo-self API: String vs str parameter gotchas
423
+
424
+ Several `yo-self/` APIs take `String` (not `str`) parameters even when the argument is conceptually a name:
425
+
426
+ - `get_variables_from_env(env, name: String)` — pass the `String` directly, do NOT call `.as_str()` first
427
+ - Most other env/value/type lookup functions follow the same convention
428
+
429
+ ```rust
430
+ // ❌ Wrong — .as_str() converts String → str but the param is String
431
+ vars := get_variables_from_env(env, prop_name_su.as_str());
432
+
433
+ // ✅ Correct — pass the String directly
434
+ vars := get_variables_from_env(env, prop_name_su);
435
+ ```
@@ -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
 
@@ -241,6 +243,8 @@ match(s,
241
243
 
242
244
  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
245
 
246
+ > **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.
247
+
244
248
  ## Generics and compile-time
245
249
 
246
250
  ```rust
@@ -321,12 +325,27 @@ while true, {
321
325
  while comptime(i < 10), {
322
326
  // body evaluated/unrolled at compile time
323
327
  };
328
+
329
+ // for loop — 2-arg prelude macro; first arg MUST be an iterator (has .next()):
330
+ for(list.iter(), x => { // ArrayList, array → call .iter() first
331
+ process(x);
332
+ });
333
+
334
+ for(map.into_iter(), bucket => {
335
+ println(bucket.key);
336
+ });
337
+
338
+ for(list.iter(), (ptr) => { // ptr is a mutable reference to each element
339
+ ptr.* = transform(ptr.*);
340
+ });
324
341
  ```
325
342
 
326
343
  - Use `recur(...)` for self-recursion
327
344
  - `while cond` is **always a runtime loop** — use this for open-ended loops (e.g., server accept loops, event loops)
328
345
  - `while comptime(cond)` explicitly unrolls at compile time — `cond` must be a compile-time-known value
329
346
  - Using a comptime-only (`::`) variable in a bare `while` condition without `comptime()` is a **compile error** (would be an infinite loop at runtime)
347
+ - **`for(arr, item => { body })`** — correct 2-arg prelude macro form. The `item => { body }` is an anonymous closure.
348
+ - **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
349
 
331
350
  ## Return and branch safety
332
351
 
@@ -426,6 +445,35 @@ test "Async test", {
426
445
 
427
446
  ## Common pitfalls
428
447
 
448
+ ### `&&` short-circuit with `match`/`cond` on RHS causes C codegen scope bug
449
+
450
+ Using `&&` where the right-hand side is a `match` or `cond` expression causes
451
+ a C codegen bug: the temp variable for the RHS is declared inside the short-circuit
452
+ `if` block but the cleanup drop is emitted outside it. This produces a C compile
453
+ error ("use of undeclared identifier").
454
+
455
+ ```rust
456
+ // WRONG — triggers codegen scope bug:
457
+ is_ok := (av.is_compile_time_only && match(av.value,
458
+ .Some(v) => compute(v),
459
+ .None => false
460
+ ));
461
+
462
+ // CORRECT — use an explicit if block to scope the match:
463
+ (is_ok : bool) = false;
464
+ if(av.is_compile_time_only, {
465
+ is_ok = match(av.value,
466
+ .Some(v) => compute(v),
467
+ .None => false
468
+ );
469
+ });
470
+ ```
471
+
472
+ This only affects `&&`/`||` where the **right-hand side contains a `match`,
473
+ `cond`, or other expression that allocates heap-managed temporaries** (e.g.,
474
+ `String`, `ArrayList`, `Option(HeapType)`). Pure boolean expressions on both
475
+ sides are fine.
476
+
429
477
  ### `impl(...)` requires a trailing semicolon
430
478
 
431
479
  ```rust
@@ -455,6 +503,28 @@ foo();
455
503
  bar();
456
504
  ```
457
505
 
506
+ ### Enum pattern matching does NOT support literal values
507
+
508
+ Match patterns on enum variants only support **variable binding**, not literal comparison.
509
+ `.BoolVal(true)` binds the inner value to a variable named `true` — it does NOT check
510
+ if the value is `true`. The arm always matches any `BoolVal`.
511
+
512
+ ```rust
513
+ // ❌ WRONG — always matches (true is a variable binding, not a comparison)
514
+ match(val,
515
+ .BoolVal(true) => handle_true(),
516
+ _ => ()
517
+ );
518
+
519
+ // ✅ CORRECT — bind to variable, then check with cond
520
+ match(val,
521
+ .BoolVal(b) => cond(b => handle_true(), true => ()),
522
+ _ => ()
523
+ );
524
+ ```
525
+
526
+ Same applies to `.IntLit(42)`, `.StrLit("hello")`, etc.
527
+
458
528
  ### `type` is a reserved keyword — avoid as field/param name
459
529
 
460
530
  ```rust
@@ -545,7 +615,44 @@ This applies to ALL callee-before-caller relationships:
545
615
  - `print_build_summary` before `execute_step`
546
616
  - Exports section must come AFTER all definitions
547
617
 
548
- ### `if(!cond, block)` use parentheses
618
+ ### Named tuple fields in type syntax are not allowed
619
+
620
+ Yo does not support named tuple field types in the syntax `(name : Type, ...)`. Use an `object` struct instead:
621
+
622
+ ```rust
623
+ // WRONG — "Labelled field is not allowed in tuple value":
624
+ get_range :: (fn(ty : TypeValue) -> Option((min : i64, max : u64)))(
625
+ .Some((min: i64(-128), max: u64(127)))
626
+ );
627
+
628
+ // CORRECT — define a named struct:
629
+ Range :: object(min : i64, max : u64);
630
+ get_range :: (fn(ty : TypeValue) -> Option(Range))(
631
+ .Some({min: i64(-128), max: u64(127)})
632
+ );
633
+ ```
634
+
635
+ Named fields work in struct/object constructors `{min: ..., max: ...}`, just not in the type syntax for tuples.
636
+
637
+ ### `Option` equality comparison limitations
638
+
639
+ 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:
640
+
641
+ ```rust
642
+ // WRONG — "No matching call found with arguments: r == .None":
643
+ assert((r == .None), "should be None");
644
+
645
+ // CORRECT — use .is_none() / .is_some() / .unwrap():
646
+ assert(r.is_none(), "should be None");
647
+ assert(r.is_some(), "should be Some");
648
+ assert((r.unwrap() == expected_value), "value check");
649
+
650
+ // Also fine in match:
651
+ match(r,
652
+ .Some(v) => assert((v == expected_value), "value check"),
653
+ .None => assert(false, "unexpected None")
654
+ );
655
+ ```
549
656
 
550
657
  The `!` operator is greedy and consumes all following args including the block. Always parenthesize:
551
658
 
@@ -557,6 +664,79 @@ if(!cond, { do_thing(); });
557
664
  if((!cond), { do_thing(); });
558
665
  ```
559
666
 
667
+ ### `escape` requires a nested-function context
668
+
669
+ `escape value` exits the **enclosing function** — the nearest `fn(...)` that
670
+ wraps the current code. It requires that the code is inside a nested function
671
+ (e.g., a closure or `given` handler lambda), NOT at the top level of a
672
+ standalone function definition.
673
+
674
+ ```rust
675
+ // CORRECT — escape inside a given handler lambda (lambda has enclosing fn):
676
+ given(exn) := Exception(throw: ((err) -> {
677
+ escape result // exits the outer function that contains this given()
678
+ }));
679
+ do_something(using(exn));
680
+
681
+ // CORRECT — escape inside a closure passed as argument:
682
+ result := match(opt, .Some(x) => x, .None => {
683
+ // This is NOT a nested function — use return:
684
+ // WRONG: escape default_val // ERROR: no enclosing fn
685
+ return default_val // CORRECT: return exits the enclosing fn directly
686
+ });
687
+
688
+ // WRONG — escape inside a match arm (not a nested fn):
689
+ match(opt,
690
+ .Some(x) => { escape x } // ERROR: "can only be used inside a function that has an enclosing function"
691
+ );
692
+ ```
693
+
694
+ **Rule of thumb**: Use `escape` only inside lambdas passed to `given()` handlers.
695
+ In all other contexts (match arms, if blocks, begin blocks, top-level fn bodies),
696
+ use `return` for early exit.
697
+
698
+ `return` inside a lambda exits that lambda only; `escape` exits the OUTER function
699
+ (the one that installed the `given` handler).
700
+
701
+ ### Parameter reassignment
702
+
703
+ Function parameters are **NOT reassignable**. To reassign, declare a mutable local:
704
+
705
+ ```rust
706
+ // WRONG — cannot reassign parameter 'env':
707
+ my_fn :: (fn(env : Environment) -> Environment)({
708
+ env = other_env; // ERROR: "cannot reassign itself"
709
+ env
710
+ });
711
+
712
+ // CORRECT — create a mutable local copy:
713
+ my_fn :: (fn(init_env : Environment) -> Environment)({
714
+ (env : Environment) = init_env;
715
+ env = other_env; // OK — reassigning local variable
716
+ env
717
+ });
718
+ ```
719
+
720
+ This also applies to `object` types: you can mutate fields (`env.field = val`)
721
+ but cannot rebind the variable (`env = other_env`).
722
+
723
+ ### String cloning
724
+
725
+ Calling `.clone()` on a `String` field from a struct/method chain requires a
726
+ reference — take `&` first:
727
+
728
+ ```rust
729
+ // WRONG — .clone() requires *(Self) but gets Self value from field access:
730
+ name := token.value.clone(); // ERROR
731
+
732
+ // CORRECT — take reference first:
733
+ tok := some_fn_call();
734
+ name := (&tok.value).clone(); // OK
735
+
736
+ // ALSO CORRECT — use String.from on the str slice:
737
+ name := String.from(token.value.as_str());
738
+ ```
739
+
560
740
  ### Template strings produce `String`, literals are `str`
561
741
 
562
742
  ```rust
@@ -584,3 +764,257 @@ These features are powerful but less commonly used. Consult the linked docs for
584
764
  | Isolated types | `Iso(T)` for data-race-free parallelism | [ISOLATED.md](https://github.com/shd101wyy/Yo/blob/develop/docs/en-US/ISOLATED.md) |
585
765
  | 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
766
  | Parallelism | Thread pool, `io.spawn` for parallel work | [PARALLELISM.md](https://github.com/shd101wyy/Yo/blob/develop/docs/en-US/PARALLELISM.md) |
767
+
768
+ ---
769
+
770
+ ## Self-hosted evaluator (`yo-self/`) — known limitations and pitfalls
771
+
772
+ These constraints apply **only** to the self-hosted Yo evaluator (code inside `yo-self/`). The TypeScript-compiled Yo compiler does not have these restrictions.
773
+
774
+ ### Match patterns: no bare identifier catch-all
775
+
776
+ The self-hosted parser only accepts three match arm forms:
777
+
778
+ - `.VariantName` — unit variant
779
+ - `.VariantName(p1, p2, ...)` — tuple variant
780
+ - `_` — wildcard
781
+
782
+ **Bare identifier catch-all `t => ...` is NOT supported.** Use `_` and access the outer binding directly:
783
+
784
+ ```rust
785
+ // ❌ NOT supported in yo-self/
786
+ match(val, {
787
+ .SomeVariant(x) => x,
788
+ t => t, // ERROR: bare identifier not a valid pattern
789
+ })
790
+
791
+ // ✅ Correct — use outer binding
792
+ match(val, {
793
+ .SomeVariant(x) => x,
794
+ _ => val, // refer to outer binding
795
+ })
796
+ ```
797
+
798
+ ### String concatenation: no `String + str` operator
799
+
800
+ The `+` operator does not accept mixed `String`/`str` operands.
801
+ **Always use template strings** for concatenation:
802
+
803
+ ```rust
804
+ // ❌ Type error
805
+ result := (parts + ", ");
806
+ result := (parts + item.as_str());
807
+
808
+ // ✅ Template strings
809
+ result := `${parts}, `;
810
+ result := `${parts}${item}`;
811
+ ```
812
+
813
+ ### `clone()` on extracted String fields is ambiguous
814
+
815
+ 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))`).
816
+
817
+ ```rust
818
+ // ❌ Ambiguous
819
+ .StructVal(name, fields) => name.clone()
820
+
821
+ // ✅ Use from + as_str
822
+ .StructVal(name, fields) => String.from(name.as_str())
823
+ ```
824
+
825
+ ### `box(val)` is a move — cannot box the same value twice
826
+
827
+ ```rust
828
+ // ❌ Move error: target is moved by first box(target)
829
+ p1 := PtrVal(box(target), usize(0));
830
+ p2 := PtrVal(box(target), usize(0)); // ERROR: target already moved
831
+
832
+ // ✅ Create separate instances
833
+ p1 := PtrVal(box(EvalValue.IntLit(String.from("42"))), usize(0));
834
+ p2 := PtrVal(box(EvalValue.IntLit(String.from("42"))), usize(0));
835
+ ```
836
+
837
+ ### `recur(...)` for self-recursive lambdas
838
+
839
+ Lambdas defined as `name :: (fn(args) -> T)(body)` cannot call `name` inside `body`.
840
+ Use `recur(...)` instead:
841
+
842
+ ```rust
843
+ // ❌ Would not find `my_fn` inside its own body
844
+ my_fn :: (fn(x : i32) -> i32)({
845
+ my_fn(x - 1) // ERROR: `my_fn` not in scope yet
846
+ });
847
+
848
+ // ✅ Use recur
849
+ my_fn :: (fn(x : i32) -> i32)({
850
+ recur(x - 1)
851
+ });
852
+ ```
853
+
854
+ ### `{ expr }` without semicolons is a struct literal, not a block
855
+
856
+ ```rust
857
+ // ❌ Parsed as struct literal `{ match(...) }`
858
+ fn :: (fn() -> T)({ match(x, arms) })
859
+
860
+ // ✅ Remove braces or add semicolon
861
+ fn :: (fn() -> T)(match(x, arms))
862
+ fn :: (fn() -> T)({ match(x, arms); })
863
+ ```
864
+
865
+ ### Template strings cannot be nested inside `${...}` interpolations
866
+
867
+ A template string literal (`` ` `` ... `` ` ``) inside a `${...}` interpolation of another template string closes the outer string. The compiler gives confusing parse errors.
868
+
869
+ ```rust
870
+ // ❌ Inner backtick closes the outer template string — parse error
871
+ lines.push(`**Implements:** ${`, `.join(names)}`);
872
+
873
+ // ✅ Assign the separator to a variable first
874
+ sep := `, `;
875
+ lines.push(`**Implements:** ${sep.join(names)}`);
876
+ ```
877
+
878
+ ### Pushing RC struct fields into ArrayList does not need `.clone()`
879
+
880
+ 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.
881
+
882
+ ```rust
883
+ // ❌ Ambiguous clone call
884
+ names.push(param.name.clone());
885
+
886
+ // ✅ Push directly — RC bump happens automatically
887
+ names.push(param.name);
888
+ ```
889
+
890
+ If explicit clone is needed elsewhere, use `(&field).clone()` to select the pointer overload.
891
+
892
+ ### `.Some(expr)` in expression position is parsed as a 2-arg property access
893
+
894
+ Using `.Some(x)` as an expression (not inside a match pattern) is parsed by the Yo parser
895
+ as a 2-arg dot property access: `obj.(prop, arg)`. This means `evaluate_property_access` is
896
+ invoked on it at compile time, causing confusing errors like "Failed to infer enum variant type".
897
+
898
+ ```rust
899
+ // ❌ Parsed as 2-arg property access — NOT an Option::Some constructor call
900
+ val := .Some(oi.ty);
901
+
902
+ // ✅ Use the explicit fully-qualified form
903
+ val := Option(TypeValue).Some(oi.ty);
904
+ ```
905
+
906
+ ### `||` chaining requires explicit parentheses for 3+ operands
907
+
908
+ Chaining three or more `||` terms in a single expression is rejected with a precedence error.
909
+ Always add explicit parentheses around each pair:
910
+
911
+ ```rust
912
+ // ❌ Rejected — ambiguous precedence
913
+ if ((is_tuple_type(ty) || is_struct_type(ty) || is_union_type(ty)), ...)
914
+
915
+ // ✅ Parenthesise each pair
916
+ if (((is_tuple_type(ty) || is_struct_type(ty)) || is_union_type(ty)), ...)
917
+ ```
918
+
919
+ ### Duplicate imports from the same path must be merged
920
+
921
+ Having two `:: import "path"` lines importing from the same file causes a compile error.
922
+ Always merge them into a single destructuring import:
923
+
924
+ ```rust
925
+ // ❌ Two imports from the same path
926
+ { Foo } :: import "../../mod.yo";
927
+ { Bar } :: import "../../mod.yo";
928
+
929
+ // ✅ Merged
930
+ { Foo, Bar } :: import "../../mod.yo";
931
+ ```
932
+
933
+ ### Implicit (`using`) parameters cannot be used with `:=` assignment
934
+
935
+ Implicit effect parameters introduced via `using(name : Type)` cannot be bound
936
+ to a discarded variable with `:= name`. They can only be passed via `using()`.
937
+ To suppress "unused parameter" warnings for an implicit param, simply omit the
938
+ discard assignment — implicit params never trigger unused-variable errors.
939
+
940
+ ```rust
941
+ // WRONG — implicit variable cannot be used in assignment:
942
+ foo :: (fn(using(exn : Exception)) -> unit)({
943
+ _ := exn; // ERROR: Cannot use implicit variable "exn" in assignment
944
+ ()
945
+ });
946
+
947
+ // CORRECT — just omit the discard line:
948
+ foo :: (fn(using(exn : Exception)) -> unit)({
949
+ ()
950
+ });
951
+ ```
952
+
953
+ ### Nested `Option` patterns require staging
954
+
955
+ `match` does not support nested destructuring patterns like `.Some(.TypeVal(x))`.
956
+ Split into two separate `match` expressions.
957
+
958
+ ```rust
959
+ // WRONG — nested option pattern:
960
+ match(opt_value,
961
+ .Some(.TypeVal(box)) => { ... }, // ERROR
962
+ _ => { ... }
963
+ );
964
+
965
+ // CORRECT — match in two stages:
966
+ match(opt_value,
967
+ .Some(v) => match(v,
968
+ .TypeVal(box) => { ... },
969
+ _ => { ... }
970
+ ),
971
+ .None => { ... }
972
+ );
973
+ ```
974
+
975
+ ### Outer match on `Option` must have `.None` arm
976
+
977
+ When using `match(opt, .Some(x) => match(x, ...), ...)`, the outer match
978
+ needs its own `.None` arm. The inner match's `_ =>` wildcard does NOT cover
979
+ the outer match's `.None` variant.
980
+
981
+ ```rust
982
+ // WRONG — outer match missing .None:
983
+ match(opt_callee_value,
984
+ .Some(cv) => match(cv,
985
+ .Foo(x) => { ... },
986
+ _ => { throw_phase3() } // inner wildcard, does NOT cover outer .None
987
+ ) // outer match closes here — .None uncovered!
988
+ );
989
+
990
+ // CORRECT — add explicit .None arm to outer match:
991
+ match(opt_callee_value,
992
+ .Some(cv) => match(cv,
993
+ .Foo(x) => { ... },
994
+ _ => { throw_phase3() }
995
+ ),
996
+ .None => { throw_phase3_none() }
997
+ );
998
+ ```
999
+
1000
+ ### Parenthesis balance in deeply nested matches
1001
+
1002
+ When using `match(outer, .Some(x) => match(inner, ...), .None => ...)`,
1003
+ count parentheses carefully:
1004
+
1005
+ - The inner `match(inner, ...)` closes with its own `)`
1006
+ - AFTER that `)`, add a `,` then the outer `.None =>` arm
1007
+ - The outer match closes with its own `)`
1008
+ - Only then does `});` close the function body
1009
+
1010
+ ```rust
1011
+ // Correct structure:
1012
+ match(outer_val,
1013
+ .Some(x) => match(x,
1014
+ arm1,
1015
+ arm2,
1016
+ _ => { fallback() } // last inner arm, no trailing comma
1017
+ ), // ← closes inner match; `,` continues outer
1018
+ .None => { fallback() } // ← outer .None arm
1019
+ ) // ← closes outer match
1020
+ ```