Known Limitations
This page catalogues the known limitations of every shipped
frontend, with file:line references and the test status where
applicable.
Universal limitations (apply to all frontends)
These are intrinsic to the current single-backend C++ implementation,
not specific to a source language:
| Limitation |
Workaround |
| Single backend: C++ only |
Compile to C++ first, then call another compiler |
| No link-time optimisation |
Use compiler-specific LTO flags on the generated C++ |
| No incremental compilation |
Re-compile all files when source changes |
StdLib calls must be in MPL_Standard/Specification.json |
Add new entries to the spec |
UnsupportedNode IR for unknown syntax |
Lower manually via runtime helper functions |
JavaScript / TypeScript
Real-time, concurrency, and Node-specific features
| Limitation |
File / Notes |
JSX is emitted as a placeholder FunctionCallNode("jsx") |
JSFrontend.h:907-930. Real JSX → C++ codegen is not yet shipped. |
| Decorators: Stage 3 semantics only |
js_semantics.h (decorators section). Legacy / experimental decorators not supported. |
?? and ?. |
Lowered correctly via BraceFrontendBase; rare corner cases (e.g. chained a?.b?.c) may not bind exactly as JS does. |
| Proxies |
Reflected via mpl_get_value(globalThis, "Proxy") but Proxy semantics not deeply lowered. |
| Web Workers |
Worker threads emit __mpl_register_es2026_extras hooks but real worker scheduling is not implemented. |
SharedArrayBuffer |
Stored as a singleton, no actual shared-memory backend. |
| WeakRefs |
Tracked but finalisation may not match JS exactly. |
Module resolution edge cases
| Limitation |
Notes |
node:sqlite |
Lowered to __mpl_sqlite_require__ with stubs (js_es2026.h:702). Real SQLite binding not yet shipped. |
undici |
Heavy HTTP machinery is stubbed via __undici_stub (JSFrontend.h:46-87). |
| Circular ESM dependencies |
Module graph expansion handles them but execution order may differ. |
package.json#imports |
Self-references via #name are resolved but #name/... sub-paths may not be. |
Conditional exports ("browser") |
Only "import", "require", "node", "default" are matched. |
Subpath imports (exports: { "./foo": ... }) |
Basic form supported; conditional arrays may not all be resolved. |
WILL_FAIL tests
The following tests are expected to fail (the compiler correctly
rejects the input — these are intentional negative tests):
| Test |
Reason |
js_strict_mode_violation_impl_test |
Octal escape or duplicate binding in strict mode |
js_module_duplicate_lexical_impl_test |
Same lexical binding twice in ESM |
js_module_lexical_var_conflict_impl_test |
Lexical/var conflict in ESM |
js_module_duplicate_params_impl_test |
Duplicate params in ESM |
js_module_implicit_strict_violation_impl_test |
with or octal escape in implicit-strict ESM |
js_strict_binding_violation_impl_test |
eval / arguments reassignment in strict |
js_strict_reserved_binding_violation_impl_test |
Reserved-word binding in strict |
js_strict_duplicate_params_impl_test |
Duplicate params in strict function |
js_strict_arrow_duplicate_params_impl_test |
Duplicate params in strict arrow |
js_class_method_strict_binding_impl_test |
Strict-binding violation inside class method |
js_throw_newline_violation_impl_test |
throw\n (no semicolon) is illegal in strict |
js_with_module_class_nonfunc_impl_test |
with in module class non-function |
js_bare_specifier_exports_null_block_impl_test |
Bare specifier with null block |
js_package_imports_null_block_impl_test |
#imports block with null |
Not supported
eval of arbitrary strings (no eval() runtime).
with statement (rejected in strict; ignored in sloppy).
eval / arguments reassignment (rejected in strict).
- Direct
arguments.callee access (rejected in strict).
- Non-standard
__proto__ set in sloppy mode (consistent with ES2015+).
- Legacy octal escapes (
\077) in strict mode.
- HTML comments (
<!-- -->) at the top of script files.
Python
Type system
| Limitation |
File:line |
| Type annotations are type-erased |
PythonFrontend.h ignores all : T annotations |
TypedDict, Protocol, Generic[T], Optional[T], Union[A, B] |
Silently dropped |
Type aliases (type X = ..., PEP 695) |
Not supported |
Generic classes (class Foo[T]: ...) |
Not supported |
Type narrowing (TypeGuard, TypeIs) |
Not supported |
match patterns |
Only literal patterns and wildcard _; dict / class / capture patterns are not supported (PythonFrontend.h:1867-1903) |
Async / generators
| Limitation |
Notes |
async def lowers correctly but real async semantics depend on the C++ backend |
metadata["async"]="true" |
await requires async context (compile-time check not enforced) |
|
yield from |
Lowered as ordinary yield |
| Generator state |
Not modelled — generators run as ordinary functions |
Context managers (with)
| Limitation |
Notes |
with expr as var: … becomes a TryCatchNode but __enter__ / __exit__ are not auto-emitted |
PythonFrontend.h:1844-1865 |
contextlib.contextmanager decorator |
Not recognised |
Other gaps
| Limitation |
Notes |
__slots__, descriptors, metaclasses |
Not modelled |
Star expressions inside patterns (*x) |
Not supported in match patterns |
Exception groups (except*) |
Not supported |
f-string conversion (!r, !s) |
Format specs captured but conversion flags ignored |
@override, @final |
Accepted, ignored |
super() with class and self arguments |
Lowered as ordinary call |
| Class decorators with arguments |
Treated as ordinary calls |
C# (CSharpFrontend.cpp)
Real-time / type system
| Limitation |
File:line |
Nullable types (int?) are type-erased |
No Nullable<T> emission. The ? is dropped. |
fixed buffer — emitted as __mpl_fixed__ but no codegen |
CSharpFrontend.cpp:1215-1237 |
volatile, readonly field modifiers |
Recognised, ignored |
extern alias declarations |
Not supported |
Operator overloads (operator +) |
Detected (metadata["operator"]="true") but no lowering |
Conversion operators (implicit/explicit) |
Detected, no lowering |
Indexer (this[...]) |
Flagged in metadata, no lowering |
record primary constructor parameter binding |
Class is created but parameters are not auto-promoted to properties |
Source generators / partial methods |
Not modelled |
nameof constant evaluation |
Emitted as a runtime call |
dynamic type |
Not supported |
unsafe blocks |
BlockNode(metadata["unsafe"]="true") — no actual unsafe semantics |
stackalloc |
Detected but no special lowering |
Native interop (DllImport) |
Stored as metadata only |
Pattern matching
| Limitation |
Notes |
case Type y (declaration pattern) |
Emitted as __mpl_pattern_match__ runtime call |
Relational patterns (> 0 and < 100) |
Converted to && |
Or-patterns (1 or 2) |
Converted to || |
case var y when cond: |
Pattern + when clause; __mpl_when__ runtime hook |
Property patterns (case { Prop: val }) |
Not yet supported |
List patterns (case [1, 2, ..]) |
Not yet supported |
LINQ
| Limitation |
Notes |
| All 50+ LINQ extension methods are mapped |
CSharpStdLib.cpp methodMap |
IQueryable providers |
Not supported — only IEnumerable |
| Expression trees |
Not preserved |
AsQueryable() |
Not supported |
Go
MVP synchronous semantics
| Limitation |
File:line |
Notes |
go f() runs inline (no goroutine) |
GoFrontend.h:361-373 |
MVP — no thread pool |
defer f() runs at defer site (no LIFO) |
GoFrontend.h:347-359 |
MVP — no deferred semantics |
select { case … } runs cases in order |
GoFrontend.h:1054-1197 |
MVP — no readiness check |
for-range body executes once (synchronous iteration) |
GoFrontend.h:1208+ |
MVP — __go_range_* runtime hooks |
Channel direction (chan<- T, <-chan T) |
direction is dropped |
|
Type system
| Limitation |
Notes |
Generic type parameters (func F[T any](x T)) |
Passed through as raw identifier |
type switch (switch v := x.(type)) |
Emitted as a regular switch |
| Embedding with promoted methods |
Only the embedded field is emitted; promotion is a backend concern |
iota in const blocks |
Not yet supported |
Build constraints (//go:build linux) |
Not honoured |
unsafe.Pointer |
Detected but no special lowering |
Not supported
- Real concurrency primitives
- Context propagation (
context.WithCancel etc.)
- Reflection (
reflect.TypeOf, etc.) — only basic type-of is available
- Goroutine stacks, panic recovery across goroutines
init() ordering across packages
- Build tags
Rust
Type system and ownership
| Limitation |
File:line |
| Borrow checker is not implemented |
Lifetimes (<'a>) are stored in metadata["rust_lifetimes"] but not validated |
Generic type parameters (<T: Bound>) |
Stripped to the type identifier |
where clauses (where T: Bound) |
Dropped |
Trait object dispatch (dyn Trait) |
Passes through but not lowered to virtual table |
impl Trait return types |
Stripped |
Const generics (<const N: usize>) |
Stripped |
Visibility modifiers (pub(crate), pub(super)) |
Stripped to pub |
| GAT (Generic Associated Types) |
Not supported |
Pattern matching
| Limitation |
File:line |
let-else diverge body is dropped |
RustFrontend.h:421-425 (destructuring patterns lose divergence) |
if let chain destructuring with complex patterns |
May lower incorrectly |
Async / generators
| Limitation |
Notes |
async fn in traits |
Not modelled |
gen blocks (gen {}) |
Not yet supported beyond yield expr |
async blocks (async {}) |
Recognised but no lowering |
? operator |
Not yet lowered to a Try-step call |
Macros
| Limitation |
Notes |
macro_rules! |
Detected and lowered to FunctionDeclNode(metadata["macro_rules"]="true") — body is not processed |
Procedural macros (#[derive(...)]) |
derive is ignored; the trait impls are not synthesised |
include!, concat!, etc. |
Detected as ordinary calls, no special handling |
PHP
Real-time / runtime
| Limitation |
Notes |
| Real enum runtime |
Lowered to class with const values (transformEnumAccess, line 913) |
| Fibers |
Fiber::suspend() / Fiber::resume() are ordinary method calls — no coroutine semantics |
Generators with yield from |
Lowered as ordinary statements |
First-class callable (strlen(...)) |
Emitted as a runtime call, not a closure |
match without default |
The frontend does NOT throw UnhandledMatchError (PHP would) |
readonly enforcement |
Modifier is metadata only |
Attributes & type system
| Limitation |
Notes |
Attributes (#[Attr]) |
Single-line forms are skipped via isIgnoredLine (line 369) — they're type-erased |
| Property hooks (PHP 8.4 RFC) |
Not yet supported |
Asymmetric visibility (public private set) |
Not supported |
Type juggling (string ↔ int in ==) |
The lowered C++ backend uses strict === always |
Intersection types (A&B) |
Not supported |
readonly class |
Modifier stored but not enforced |
enum backed values |
Handled via transformEnumAccess constant substitution |
match expression arms with side effects |
Only first matching arm fires (correct) |
PSR / composer
| Limitation |
Notes |
| PSR-4 autoloading |
Not honoured — files are loaded individually |
composer.json resolution |
Not implemented |
| Namespaces map to file paths |
Convention not enforced |
Ruby
Object system
| Limitation |
Notes |
Refinements (refine String do … end) |
No refinement scoping |
Singleton methods (def obj.method; end) |
Not recognised outside class body |
| Method missing / respond_to_missing? |
Not synthesised |
Struct.new(:x) accessors |
Auto-generated getters/setters may not match Ruby's []/[]= semantics |
| Symbol vs string |
:"foo" and ':foo' are stored as the same identifier |
Frozen string literals (frozen_string_literal: true) |
Ignored |
Lazy enumerator (Enumerator::Lazy) |
Not detected |
Pattern matching (case … in pattern) |
Not yet supported |
Numbered parameters (_1, _2, …) |
Not yet recognised |
Safe navigation (obj&.method) |
Converted to obj != null ? obj.method() : nil only in obvious cases |
Block / Proc semantics
| Limitation |
Notes |
Proc vs lambda difference (arity) |
Both lower to LambdaNode; strict-arity check not modelled |
&block parameter |
The implicit __ruby_block_param is added but explicit &block is dropped |
Block-local variables (; separator) |
Not modelled |
Other
| Limitation |
Notes |
BEGIN / END blocks |
Not supported |
require_relative |
Lowered as require |
autoload |
Not supported |
__method__, __FILE__, __LINE__ |
Not yet special-cased |
prepend |
Stored as mixin metadata; no MRO enforcement |
Java
Type system & modern features
| Limitation |
Notes |
| Real sealed class enforcement |
permits parsed but not used to validate subclasses |
| Virtual threads |
Thread.startVirtualThread(...) is an ordinary call — no coroutine semantics |
| Scoped values (Java 21+ preview) |
Not supported |
| Structured concurrency (Java 21+ incubator) |
Not supported |
String templates (STR."Hello \{name}") |
Parsed as ordinary string concatenation |
| Implicitly declared classes / instance main methods (Java 21+ preview) |
void main() parsed as regular method |
FFM API (java.lang.foreign) |
References pass through; no FFM bindings emitted |
record compact constructor (public Point { … }) |
Not specially recognised |
record explicit canonical constructor override |
Auto-generated, may differ from explicit |
Pattern matching for switch
| Limitation |
Notes |
Type patterns (case Integer i) |
Supported — binding extracted |
Guarded patterns (case Integer i when i > 0) |
Not yet modelled |
Record patterns (case Point(int x, int y) p) |
Not yet supported |
Array patterns (case int[] arr when arr.length == 3) |
Not yet supported |
| Null case (Java 21+ preview) |
Parsed but lowered as ordinary case |
EMPLS
| Limitation |
Notes |
Generic type parameters beyond List<T> |
Parsed as plain identifiers |
| Async / await |
Not yet modelled |
| Traits / interfaces |
Body not specialised |
Multi-file module split (module math { … } in one file, used by import "math" in another) |
Flat file paths under source root, no real module system |
| Async lambdas |
Not yet recognised |
| Operator overloading |
Not supported |
| Compile-time function evaluation |
Not yet modelled |
| Pattern matching |
Not supported |
Cross-language limitations
StdLib coverage
| Module |
Status |
Console.* (WriteLine, Write, ErrorWriteLine, ErrorWrite, ReadLine) |
All frontends |
Math.* (Abs, Floor, Ceil, Round, Sqrt, Pow, Max, Min, Random, Trunc, Sign, Log, Log2, Log10, Sin, Cos, Tan, Atan2, sumPrecise) |
All frontends |
String.* (Length, ToLower, ToUpper, Trim, Split, Join, StartsWith, EndsWith, Format) |
All frontends |
IO.* (ReadFileText, WriteFileText, FileExists, CreateDirectory, Path.Join, Path.GetDirectoryName, Path.GetFileName) |
All frontends |
Process.* (Exit) |
All frontends |
Environment.* (GetVariable, SetVariable, GetCurrentDirectory) |
All frontends |
DateTime.* (NowUnixMs, NowUnixSeconds) |
Not yet mapped from any source language |
Json.* (Parse, Stringify) |
JS, Python, Go, Java, Rust, Ruby (partial), PHP |
Convert.* (ToInt, ToFloat, ToString) |
Python, Java |
Collection.* (Length) |
Python (len) |
Crypto.*, OS.*, PQC.* |
JS only (via runtime headers) |
Runtime semantics
| Aspect |
Status |
| Type-erasure |
All source types become auto/any in the IR unless explicitly typed (EMPLS only) |
| Reference equality vs value equality |
== always lowers to value equality on the C++ side |
| Garbage collection |
Reference-counted mpl_object with cycles broken by mpl_gc_collect |
| Async runtime |
JS uses mpl_promise<T>; other languages use ad-hoc approaches |
| Error model |
throw / try-catch lowers to __mpl_throw__ / C++ exceptions |
| Operator precedence |
Mirrors the source language as closely as possible; rare JS ?? / ?. cases may differ |
Adding a new language to the supported set
See registration.md for the full procedure. The
process is:
- Create
frontends/<lang>/<Lang>Frontend.h.
- Inherit
BraceFrontendBase (or FrontendBase for line-based, or
ILanguageFrontend for bespoke parsers like Python / Ruby).
- Optionally add
frontends/<lang>/<Lang>StdLib.h.
- Register in
MPL_Compiler.cpp:registerPlugins().
- Add to
CMakeLists.txt.
- Add tests in
tests/.
- Document in this
docs/frontends/ directory.
Tracking limitations
Every limitation documented here should have a corresponding issue or
test. To track progress:
cd MPL_Compiler/build
ctest -L known_issue --output-on-failure
Tests labelled known_issue are expected to fail until the corresponding
limitation is addressed.