Skip to content

Known Limitations

Wyn is honest about what works and what doesn't. Every item below was re-run against the published v1.21.0 binary rather than carried forward from the last release. Two entries moved to "no longer limitations" as a result, and five are new - four of those found by re-running the list rather than by a bug report, which is the argument for re-running it.

Runtime

  • spawn on a closure returns the wrong answer, silently. A closure called directly returns its value; the same closure spawned returns 0. wyn check passes, the build succeeds, and the process exits 0:

    wyn
    var n = 5
    g = (() => n * 2)
    println(g())              // 10
    f = spawn (() => n * 2)
    println(await f)          // 0  <-- wrong

    spawn needs a function pointer, and a closure's captured environment is not carried across. Until this is fixed, pass captured values as explicit parameters to a named function and spawn that. This is the most serious item on this page because nothing warns you.

  • parallel { } overlaps only two CPU-bound branches. Two branches of fib(35) inside parallel { } finish in the time of one; three or four take two dispatch rounds. Four spawns plus await_all on the same work finish in one round. Use spawn + await_all when you need dependable overlap of more than two CPU-bound branches.

  • Recursive spawn - spawn inside recursive functions works reliably up to ~28K outstanding tasks (fib(22) verified on v1.21.0). Beyond that, the thread pool can deadlock. Use sequential recursion for deep trees, spawn for parallel leaf work.

  • .len() is O(n) on some StringBuilder results. Strings normally carry a cached length in the RC header, so .len() is O(1) - about 1.7ns regardless of length. Some StringBuilder.to_string() results arrive without the cache and fall back to a scan: ~2.5μs per call on a 100,000-character string. Hoist .len() out of the loop if you are calling it repeatedly on a builder result.

  • string.len() returns bytes - UTF-8 byte count, not character count. "héllo".len() is 6 and "日本語".len() is 9.

  • Math.checked_add() panics, it does not return a Result. It is a guard against silently wrapping, not a recoverable check: Math.checked_add(9223372036854775807, 1) aborts with panic: integer overflow in checked_add(...).

  • println(v) on a struct with a nested-struct or array field fails to build. It passes wyn check and then the C compiler rejects the generated printf. Structs whose fields are all scalars or strings print fine. Workaround: interpolate instead

    • println("${v}") handles nested structs, arrays and floats correctly:
    wyn
    struct Point { x: int, y: float }
    struct Box { label: string, p: Point, tags: [int] }
    b = Box { label: "hi", p: Point { x: 1, y: 2.5 }, tags: [1, 2, 3] }
    println("${b}")   // Box { label: "hi", p: Point { x: 1, y: 2.5 }, tags: [1, 2, 3] }
    println(b)        // check passes, build fails
  • Unknown methods on a builtin namespace pass wyn check and fail to build.Time.millis() type-checks clean and then fails in the C compiler with call to undeclared function 'Time_millis'. The real name is DateTime.millis(). It fails loudly at build time rather than producing a wrong answer, but it is a check-passes/build-fails hole and it is filed as one.

No Longer Limitations

  • Integer overflow - silent wrap-around. As of v1.21.0, wrapping is defined. Wyn compiles with -fwrapv, so signed integer overflow wraps two's complement and is guaranteed to: 9223372036854775807 + 1 is -9223372036854775808, every time, at every optimisation level. Previously the optimiser was entitled to assume overflow could not happen, which is how wrapping code turns into deleted comparisons. It costs nothing measurable - fib(35) is unchanged. Use Math.checked_add() if you want the overflow to abort instead.
  • Sort performance - slower than Go. Re-measured on v1.21.0 with both sides optimised: [int].sort() does 1M ints in 73ms against Go's sort.Ints at 83ms, and 100K in 6.3ms against 7.0ms. The old figures (124ms vs 101ms) compared a Wyn dev build against go build, which optimises by default. A dev build really does take 110ms, so use --release for anything you intend to time.

Compile Time

  • Compile speed - wyn build takes ~356ms (dev) or ~1.18s (release) for hello world, and ~505ms / 1.32s at 5,000 lines. wyn check alone is 12ms. The bottleneck is the C compiler (clang/gcc), not Wyn's codegen. Go's purpose-built compiler is faster (go build hello world, forcing a real link: 189ms).

Platform Notes

  • Windows cross-compilation - not yet supported from macOS. Linux x64/ARM64 work.
  • Regex shorthand classes \d, \w, \s are unsupported on every platform, and fail silently. The built-in NFA engine treats \d as a literal d, so Regex.replace(s, "\\d+", "N") rewrites the letters and leaves the digits. Use [0-9], [A-Za-z0-9_] and [ \t\n], which work. Also unsupported: backreferences, lookaround, and capture-group extraction - Regex.match returns a plain bool, there is no match object and no .group(n). Everything else works: ., classes, *, +, ?, {n}, {n,m}, alternation, and ^/$.
  • Regex.find_all and Regex.split return a newline-joined string, not an array - as does Db.query (rows by \n, columns by |). Iterate with .trim().split("\n").
  • Mobile - system()/popen() return stubs on iOS/Android.
  • WASM - not yet supported. Runtime has POSIX dependencies that need #ifdef guards.

See Also

MIT License - v1.21.0