Language support statusalpha
Which Motoko features are supported, partial, or unsupported?
Support here means measured, not intended: a feature is supported when corpus programs using it compile to the same bytes as the reference compiler and behave the same under the runtimes. The compiler is a port of moc 1.14.1 and its output carries that claim in its motoko:compiler metadata, earned by measurement: zero upstream language tests added between 1.8.2 and 1.14.1 remain unported, a sweep of the top 40 mops packages compiles all 384 modules moc compiles with zero moc-only failures, 359 of moc's own run-drun programs behave identically under our runtime, and the compiler compiles itself — locally and on mainnet — to a byte-identical fixed point. Beyond that, 221+ corpus programs are byte-identical across independent compiler builds and 160 behave identically browser-versus-native with zero divergences.
What the evidence is#
| Evidence | Covers |
|---|---|
scripts/compiler_byte_neutral.sh | the compiler's output bytes, over moc's own corpus |
scripts/web_corpus_gate.sh | runtime behaviour, browser vs native vs (transitively) a replica |
32 gates in scripts/ | one property each, listed in the corpus page |
cargo test --workspace | 81 native runtime tests |
| The compiler self-compiling on-chain | 177 files, 2,345 messages, artifact 830f3e87… |
evm_engine building on-chain | 661 files, 4,138 messages, artifact 178c6429… |
Two real applications ship as flagship demos and are gated: MoxDB (rows as actors, tables that upgrade their own rows) and Knights (actors that call an LLM, compile the reply in-tab, and upgrade themselves).
Supported#
| Area | Evidence |
|---|---|
| Actors, one message at a time, rollback on trap | corpus; moxzid_e2e.sh |
async / await, futures, async* | corpus; web_async_gate.sh |
| Actor-to-actor calls, including re-entrancy A → B → A | web_reentrancy_gate.sh |
| Actor classes; canisters creating canisters | web_spawn_gate.sh, moxzid_spawn.sh |
| Upgrades under enhanced orthogonal persistence | web_upgrade_gate.sh, moxzid_upgrade.sh |
| Timers and heartbeats | moxzid_timers.sh, web_timer_gate.sh |
| Queries, with heap writes discarded | web_readonly_gate.sh, runtime/tests/queries_dont_write.rs |
| Cycles as a real budget: attach, refuse, top up | moxzid_cycles.sh, runtime/tests/cycles.rs |
| HTTPS outcalls, including the transform function | moxzid_outcall.sh, web_outcall_gate.sh |
Candid encode/decode, to_candid / from_candid | corpus |
mo: package imports through mops | web_mops_gate.sh; one resolver shared by CLI, server and browser |
| Threshold ECDSA | runtime/tests/ecdsa.rs |
| Management canister: create, install, chunked install, snapshots, canister info | runtime/tests/management.rs |
on_low_wasm_memory | runtime/tests/low_memory.rs |
Best-effort responses and ic0.msg_deadline | moxzi/runtime/src/sys.rs:803 |
Mixins and include, including nested mixins and mixin <system> capability | scripts/mixin_check_gate.sh: typecheck, splice hygiene, codegen and runtime |
Enhanced migration chains (--enhanced-migration, --stable-baseline) | scripts/enhanced_migration_gate.sh: signatures byte-identical to moc, runtime fast-forward, the full baseline lifecycle; see Migrations |
Candid interface imports (import S "idl:x.did") | scripts/idl_import_gate.sh: types round-trip to Candid byte-identically to moc |
Stable-signature compatibility (moxzi stable-compatible, M0169/M0170/M0216) | scripts/stable_compat_gate.sh: verdicts agree with moc |
moxzid refusing a provably-unsafe upgrade (pre-flight through the same checker; ?force=true skips it, the RTS still has the final word) | scripts/moxzid_stable_types.sh |
Durable timers surviving a process restart (Prim.setTimer → kill → recover → fires) | runtime/tests/timer_survives_restart.rs |
Implicit arguments, including structural synthesis (__record/__tuple/__variant) | moc's own implicit-derivation tests |
and- and or-patterns | M0184, M0260, M0261, M0262 |
| Migration expressions | M0209 |
do ? { } and ! | M0064 |
| Float literals: exponents, hex floats, underscores, subnormals — bit-exact | scripts/float_literals_gate.sh holds seven literals to the exact bits |
Environment variables (Prim.envVar<system>), set through update_settings | scripts/moxzid_env_vars.sh: the full round trip, both read paths |
http_request as a website, including SSE and every verb | moxzid_web_gate.sh, moxzid_webdav_gate.sh |
Partial#
| Feature | State | Consequence |
|---|---|---|
| Warnings | four codes are emitted and printed — M0089, M0254, M0265 and M0266 | the rest of moc's 42 warning codes are still absent, notably M0194 unused-identifier and M0145/M0146 pattern coverage. A warning-clean moxzi build is not evidence of a warning-clean moc build |
| Diagnostic explanations | a handful of codes carry long-form text | there is no per-code explainer command |
| Contextual dot notation (M0236) | machinery exists, no emission site, suppressed by default | the suggestion never appears |
| Deeply nested expressions | fine natively; ~60 chained operators in a browser tab | generated code should chunk expressions or use the CLI |
| Off-chain certificates | absent by design | certified-data patterns need a real replica; @dfinity/agent's Actor is unsupported off-chain, the generated idlFactory is |
Not supported#
| Feature | What happens |
|---|---|
Serializing a mutable array ([var T]) through candid's alias / write_alias | the compiler emits a trap that names itself — SERIALIZE-GAP mutable array (write_alias) — rather than writing a malformed value silently (CompileEnhanced.mo:16853, and the async path at :16990) |
| Any unported front-end form | M9000 unimplemented: <what>, at the point it is hit. This is a compiler gap, not a program error — report it |
Importing an actor-class library in a --remote build | refused with a message; build the library first, or compile locally |
| Certificates, in any off-chain runtime | ic0.data_certificate_present returns 0 |
| Per-instruction metering in a browser, without instrumentation | V8 has no fuel. meter: true supplies it by rewriting the module; unmetered, preemption is a wall-clock worker deadline |
Rejected on purpose#
These are refusals, not gaps. Each one is a case where accepting the program would be worse than refusing it.
| Program shape | Code | Reason |
|---|---|---|
A non-persistent actor | M0220 | persistent is how a program states that its state survives an upgrade. --default-persistent-actors rewrites bare actors when you are porting |
| An actor not at the top level | M0069 | as moc does. moxzi reports M0069 alone where moc adds M0037 and M0038 |
| Anything else beside a top-level actor | M0141 | move it into the actor body |
prim without MOC_UNLOCK_PRIM | M0057 | an authorization gate: prim is the compiler's own back door |
| An expression or type nested past 128 | parse diagnostic | a clean error, never a stack overflow (scripts/parser_depth_gate.sh) |
| A self-call chain past 512 deep | runtime error | the same number in every runtime, so a program refused in one is refused in all |
Where to look when something does not compile#
A moxzi diagnostic with an M#### code means your program; M9000 or a trap naming a GAP means moxzi. The distinction is deliberate — the compiler is built to announce its own gaps at runtime rather than emit code that is quietly wrong.
Next#
- Known differences — where moxzi and moc disagree on purpose.
- Error and diagnostic codes — what each code above means.
- Reporting a bug — what to send when you hit M9000.