PatLang Capabilities & Honest Limitations
Language documentation tends to undersell gaps. This page does the opposite deliberately: a direct list of what PatLang currently cannot do, alongside what it genuinely can, sourced from code comments and verified parser/runtime behaviour rather than aspiration. Where a limitation is scoped future work rather than a permanent design decision, that's stated too.
What actually works, end to end
- Two real, independently-verified compilation paths (native Rust codegen, self-hosted
patc1.exe) plus an interpreter, with parity checks between interpreted, natively-compiled, and self-hosted-compiled output for language features as they're added. - A self-hosted compiler fixpoint-verified against its own source —
patc1.exerecompiling its own compiler source produces byte-identical Rust. - Nine working paradigms (see the Paradigms Guide), including genuine OS-thread parallelism, cooperative fibers, and predictive time-budgeted blocks (
budgeted(...)) — all three verified across interpreted, natively-compiled, and self-hosted-compiled execution paths; fibers/budgetedadditionally verified compiled to real threaded WebAssembly, running live in a browser via a hand-rolled WASI threads proposal implementation. - An exact numeric tower (Int → BigInt on overflow, Int/Int → exact Rational on uneven division, real → Complex on negative sqrt) rather than silent truncation or precision loss.
- A runtime-extensible syntax mechanism (
syntax NAME { ... }) that genuinely works at runtime, not just at compile time.
The Stage 0 / Stage 1 dialect split — mostly closed, one real gap remains
A mid-2026 audit found Stage 1 (self-hosted, what patc1.exe actually parses) had drifted narrower than Stage 0 (native) on several fronts that were all cosmetic or mechanical, not fundamental: block delimiters (Stage 1 was do...end-only), mutability (Stage 1 had no bare reassignment at all), and member-assignment (Stage 1 had no obj.prop = value). All three are now fixed — both stages accept both delimiter families for every block-shaped construct, both enforce the same let/let mut mutability rule, and both support member-assignment. Full detail is in the Grammar & Syntax compatibility table.
One real gap remains, and it's not cosmetic: Stage 1's parser has no node shape at all for fact/query/goal/rule as declarative syntax, or for constrain/pursue/reasoning/relationship/activate/case. Logic programming's call-form (fact(...), query(...), goal(...)) works in both, but the declarative-looking syntax only exists (as a no-op) in Stage 0. Reviving this in both stages as genuinely working syntax is scoped as its own future phase, gated on first building real rule-expansion-and-backtracking behind it (see below) — bringing the syntax back without the engine to back it would just be a bigger no-op.
Logic engine: facts only, no rule inference yet
logic_engine.rs documents its own current scope directly: query supports direct fact lookup, not rule-based inference over declared rules. The rule keyword parses (in Stage 0) but rules aren't yet consulted by queries. Treat the logic paradigm today as "assert facts, query facts" rather than a general inference engine.
Complex numbers: division and square root are asymmetric
sqrt promotes negative real inputs to Complex but explicitly rejects a Complex input with "sqrt: complex input not supported" — there's no complex square root. Complex modulo is likewise an explicit error, not silently coerced to something else. There also appear to be two separate numeric-operator implementations in the runtime (rust-runtime/src/ir/ops.rs and the numeric functions embedded in codegen.rs), with complex-division error messages that don't perfectly match between them — worth verifying against the actual execution path in use before relying on specific complex-division error text.
Concurrency: fibers work compiled-native AND compiled to threaded WASM now; neither primitive goes through the chunk system
parallel_map and the fiber functions (plus budgeted(...), built on fibers) work and are verified across interpreted, natively compiled (pat --patc), self-hosted compiled (patc1.exe), and compiled to threaded WASM — confirmed by a 200,000-iteration resumption test converging identically (17 rounds of pause/resume) across the first three, and by the fiber portfolio demo's real Fibonacci-generator transcript matching byte-for-byte between the native run and a real headless-browser run of the compiled WASM binary. Compiled-native fiber support was ported directly into codegen.rs's generated program text (mirrored into self_hosting/lib/runtime_rs.patlang), reusing the same "resolve and call a program function by name" mechanism parallel_map's compiled path had already established as working.
One thing remains genuinely true, not just historical: both primitives sit entirely outside HOST_CHUNK_TABLE, special-cased directly in the interpreter and codegen rather than following the same modular chunk pattern as the rest of the host surface — a real asymmetry, not an oversight to be quietly normalised in documentation.
WASM threading is real now, but on a second, opt-in target, not the default one. The ordinary wasm32-wasip1 target (still this project's default WASM build) has no std::thread support, so fiber_new/fiber_resume/fiber_yield/fiber_alive/budgeted(...) still return a clear runtime error there, unchanged. A new wasm32-wasip1-threads target, compiled with a nightly toolchain and -C target-feature=+atomics,+bulk-memory,+mutable-globals, genuinely runs them — the cfg gates in codegen.rs/runtime_rs.patlang now key off target_feature = "atomics" rather than target_arch = "wasm32", so real fiber support compiles in for any target with atomics (native or threaded-WASM) and stays cleanly stubbed for the ordinary non-threaded WASM build. Running it in a browser needed a hand-rolled implementation of the WASI threads proposal (real Workers + SharedArrayBuffer-backed shared memory, no wasm-bindgen) — see the fiber demo page for a live "run in browser" button using it, and the paradigms guide for the one hard architectural constraint discovered building it (a fixed, generously-sized worker pool pre-warmed before execution starts, not literally-unbounded dynamic growth — see below).
The WASI/browser sandbox has no real filesystem, and only the opt-in threaded target has real threading
Portfolio demo pages that run PatLang in the browser via WebAssembly (the playground, the maze/flow-graph WASM drivers) necessarily run in a sandbox with no real filesystem access, regardless of target. Real OS-thread-backed concurrency, however, is no longer universally absent in the browser: the fiber demo's browser button is genuinely live — the same compiled WASM binary, actually running via real Workers, not a mock. The threading demo (parallel_map) still shows a native transcript alongside an honest simulated step-through; the same target_feature = "atomics" cfg gate change makes its compiled path available on the threaded target too in principle (both go through the same generic hand-rolled thread-spawn JS shim), but that's unverified and undemoed so far — fibers/budgeted are the only concurrency primitive actually proven working live in-browser to date.
Self-hosting bootstrap: rustc has a real limit on generated program size
Rebuilding patc1.exe from its own PatLang source, then having it recompile itself again (the fixpoint check), surfaced two genuine constraints worth knowing before touching the self-hosted compiler's own source:
rustc's optimizer has been observed to blow up to 30GB+ RAM (and, separately, to hang indefinitely burning CPU with no progress) compiling the self-hosted compiler's own ~1.6MB generated interpreter source at the default optimization level. This is specific to that file's unusual size, not a general problem — ordinary, much smaller generated programs compile fine at the default level.rustc_buildnow accepts an optional opt-level override for exactly this case; the bootstrap tooling passes0for its own build, confirmed to compile the same source correctly in seconds with no blowup.- Lower optimization levels remove the stack-frame-shrinking effects that were keeping deep recursion within the OS-default thread stack. The self-hosted compiler's own lex/parse/lower/codegen pipeline is a genuinely deep recursive-descent interpreter running over its own source; compiled at
opt-level=0it could overflow the default main-thread stack. Generated native programs (and thepatCLI itself) now run their real work on a spawned thread with an explicitly larger stack for exactly this reason — WASM targets are unaffected (and unchanged), sincestd::threadsupport isn't guaranteed across WASI runtimes.
Neither of these affects ordinary, reasonably-sized PatLang programs — both are specific to the self-hosted compiler's own unusually large generated source, encountered only when rebuilding patc1.exe itself.
Structural gaps worth knowing
oounconditionally pulls inlogicas a compiled-program dependency, becausesend's relation-inference dispatch reads state the logic chunk declares — even for programs that never touch logic programming directly.- Dead-code elimination is whole-chunk, not per-function. Using one function from a chunk pulls the whole chunk's prelude text into the compiled output.
Value's variant set is an all-or-nothing choice per build. A program either compiles against the fast,Number(f64)-onlyValueprelude or the full numeric-towerValueprelude (Int/Float/BigInt/Rational/Complex) — there's no mixing the two within one compiled program.- Templates (
make a template called NAME { ... }) parse but their body is discarded. They're a pure no-op in Stage 0 today, not a working feature. - The Stage 0 IR/compare CLI pipeline (
--ir/--compareflags) has its own, narrower construct support than ordinarypat run— the CLI itself scans for unsupported high-level constructs (functions, facts/rules/goals, reasoning mode, object literals) up front and prints a direct warning rather than failing confusingly deep inside the pipeline. This is a limitation of those specific debugging flags, not of the interpreter or normal compiled path.
Longer-term direction, explicitly not current work
patc1.exe still shells out to rustc as its final native-codegen backend — self-hosting today means the lexer/parser/lowerer/codegen are all PatLang, but the very last step that turns generated Rust text into a real executable is still rustc. A genuine PatLang-to-native compiler, with no rustc involved for that last step, is a stated future direction — not scoped or started.
See also
- Grammar & Syntax — full detail on the dialect split.
- Standard Library & Host Function Reference — the chunk system these limitations sit inside.
- PatLang Real OS Threads and PatLang Fibers — the concurrency demos referenced above, including their honest WASM mocks.