Repository navigation
-
Upgrading from
moc1.x? The moc v1 → v2 migration guide
lists every breaking change below with the message you will see and its fix (#6417). -
motoko (
moc)-
Persistence
-
Breaking change: actors are
persistentby default. A bareactoror
actor classkeeps its fields across upgrades; mark fields that should
reset on upgradetransient. The--default-persistent-actors,
--require-persistent-actorsand--legacy-actorsflags are removed,
so the old transient-by-default behaviour cannot be restored (#6356). -
Breaking change: classical (32-bit) persistence is removed.
mocalways
targets enhanced orthogonal persistence (EOP) with a persistent 64-bit
main memory, and the incremental GC is the only GC. The flags
--legacy-persistence,--copying-gc,--compacting-gc,
--generational-gc,--rts-stack-pages,
--skip-gc-deprecation-warning,--incremental-gcand
--experimental-rttiare rejected as unknown options. Projects that
still need a classical module must keep an oldermoc(1.14.x)
(#6362, #6380). -
Existing classical canisters still upgrade. The runtime reads every
earlier classical stable-memory format, and a classical canister moves
to EOP on its next upgrade. Compile that one upgrade with
--enhanced-orthogonal-persistenceand without--enhanced-migration;
otherwise it traps with "Detected implicit upgrade from classical
orthogonal persistence to enhanced orthogonal persistence" or "Cannot
upgrade from classical orthogonal persistence with
--enhanced-migration". The migration is irreversible and later upgrades
need no flag.--enhanced-migrationno longer requires
--enhanced-orthogonal-persistence(#6362). -
Breaking change: the
flexiblekeyword is removed. Writetransient;
flexibleis now an ordinary identifier (#6407). -
Breaking change:
ExperimentalStableMemoryis gone. The
Prim.stableMemory*primitives,Prim.stableVarQuery, the
__motoko_stable_var_infocanister query and the
--experimental-stable-memoryflag are removed, so importing
mo:base/ExperimentalStableMemoryno longer type-checks;coreis
unaffected. UseRegioninstead. TheM0199diagnostic is retired
(#6357, #6378, #6403).
-
-
Warnings that are now errors (
-W <code>downgrades a code again)-
Breaking change: code that traps or silently does not do what it says
is an error. Non-exhaustive patterns inswitch,let,catch,for
and function parameters (M0145),ignoreof anasync*value (M0222),
misplaced parentheticals (M0210), unknown parenthetical attributes such
as(with cycle = ...)(M0212), record fields the expected type drops,
such as the typo in{ user with emial = e }(M0215), functions named
like system methods but not declaredsystem(M0128), implicit oneway
public funcs (M0242), and import paths whose letter case differs
from the file name (M0005). Fix withlet ... else, acase _branch,
or an explicit: ()/: async ()annotation (#6410). -
Breaking change: an inferred type that collapses to
AnyorNoneis
an error. Array literals (M0074),ifbranches (M0081) andswitch
branches (M0101) whose only common type isAny, and intersections
that areNone(M0166) or unions that areAny(M0167) although
neither operand is. Joins into a smaller but useful type, such as the
common fields of two records, are unaffected. Annotate the type,
let xs : [Any] = ..., whenAnyis intended (#6412). -
Breaking change: a comparison whose result is constant is error M0276.
It covers comparisons at a type with a single value, such asAny,
{}or():n == twithn : Natandt : Text, two records that
share no field, orx == yon type parameters. Comparisons at a common
type that still has content stay warning M0062 (#6415). -
Breaking change: a file in the
--enhanced-migrationdirectory that
does not export a publicmigrationfunction is error M0278 instead of
warning M0251. The file was skipped, so the build succeeded and the
migration never ran (#6429). -
Breaking change: an imported library must be a
module { ... }or a
named actor class. A bare sequence of declarations is error M0142
instead of a deprecation warning; wrap it inmodule { ... }and mark
the exportspublic.-A,-Wand-Eno longer acceptM0142
(#6407). -
Breaking change: an actor class must return
async.
actor class C() : actor {} { ... }is error M0193 instead of warning
M0135; write: async actor {}.-A,-Wand-Eno longer accept
M0135(#6407).
-
-
Syntax
-
feat:
if,while,switchandforheads need no parentheses.
if f(x) { e1 } else { e2 },while n > 0 { n -= 1 },
switch p.x { ... },for x in xs.vals() { }; the branches or body
are then blocks. The old forms keep working. A record literal as head
needs parentheses,switch ({ x = 0 }) { ... }(M0272), andbreak l e
takes any expression (#6348, #6388). -
Breaking change: a bare branch glued to the condition no longer parses.
Afterif corif (c), a glued(,[or prefix operator continues
the condition and a space starts the branch:if xs[i] { }indexes,
if (c) [i] else []branches,if (c)-1 else 1is rejected,
if (c) -1 else 1branches, and an operator spaced on both sides is
just an operator,if n - 1 > 0 { }. Add the space. A condition that is
more than a name or a parenthesized expression requires braced branches
(M0275):if f(x) e1 else e2is rejected. Outside conditions nothing
changes; the style guide has the two rules to follow (#6348, #6388). -
Breaking change:
??is whitespace-sensitive, like<and>.
a ?? bis null-coalescing and??xis two option introductions,
?(?x).a ??bno longer parses, and the compatibility hack that read
the operator token?? eas?(?e)in expressions, types and patterns
is removed. The right-hand side is now an expression:
opt ?? { x = 0 }is a record literal, and a block must be written
opt ?? do { ... }(#6358). -
feat:
switchcases are lighter. The;between cases is optional, and
patterns with a deterministic extent need no parentheses:case null,
case -1,case ?v,case #tag,case #tag(p), combined withor,
andand: Tas incase #less or #equal { ... }or
case x : Nat { ... }.catchaccepts the same patterns,
catch e : Error { ... }. A variant payload always takes its own
parentheses,case #tag(p), nevercase #tag p(#6358, #6394). -
feat:
do { ... }anddo ? { ... }work as operator operands:
1 + do { 2 },-do { ... },debug_show do { ... }. A postfix form
needs parentheses around the block,(do { ... }).field(#6395). -
feat: targeted parse errors with fix-its replace the generic M0001.
M0272 flags a record literal in block position, such as acasearm,
a function body or aswitch/if/whilehead, and suggests nesting
it as the block's result; M0273 flags a block in record-literal
position and suggestsdo { ... }; M0274 flags a reserved keyword such
asqueryorimplicitused as an identifier (#6358, #6348).
-
-
Type checking
-
feat: a function passed where an
asyncfunction type is expected is
asynchronous without annotations. Forrun : (Nat -> async Nat) -> async Nat,run(func(n) { n + 1 })takes bothn : Natand the
async Natreturn type fromrun, as non-async functions already
did. This coversasync*and generic calls such as
forEach(xs, func(x) { await process(x) })(#6428). -
Breaking change:
func f() : T = eandfunc f() : T { e }mean the
same. A body is implicitlyasyncexactly when the return type is
async, and a one-waysharedfunction's body is implicitly
ignore async, so= async { ... }and= ignore (async ...)are
redundant and rejected with M0277, whose fix-it drops theasync. A
parenthetical on the body,= (with cycles = n) async { ... }, goes at
the call site,(with cycles = n) f(), and a local function can no
longer return a future of its enclosing function (= fut). M0078,
M0079 and M0213 are retired (#6428). -
Breaking change:
{ base with ... }copies the base'svarfields.
The result is the equivalent field-for-field record literal, so
mutating the copy does not mutate the base. This was error M0179, or
aliasing under the now-removed--experimental-field-aliasingflag;
code that relied on aliasing must keep the shared state in one object
referenced from both records (#6346). -
feat: implicit arguments are also found in nested modules. When
Mis
in scope, candidates such asM.N.comparecount, up to a nesting depth
of 8 (#6084). -
feat: contextual dot also finds functions in nested modules, so
importing a facade that re-exports its package's modules is enough for
bothe.f(...)and implicits. Both resolve in the same tiers: a local
value, then the direct fields of the modules in scope, then their nested
modules. A nested candidate only counts when no direct field matches, so
a module reached through a facade never competes with a module in scope
and existing calls keep their meaning. M0236 also suggestse.f(...)
forM.N.f(e, ...)(#6419). -
feat: read-only primitives no longer require the
systemcapability,
soqueryandcomposite querymethods can call them:
Prim.getSelfPrincipal,Prim.envVarNames,Prim.envVar,
Prim.callerInfoSigner,Prim.callerInfoData,Prim.getCandidLimits
andPrim.getCandidTypeLimits. An explicit<system>on a function
that does not need it is warning M0196, with a fix-it deleting it,
instead of an error (#6414). -
feat: the dot-notation suggestion (M0236) is on by default.
mocwarns
about calls likeMap.filter(map, ...)that could be
map.filter(...); silence it with-A M0236. M0223 (redundant type
instantiation) and M0237 (redundant explicit arguments) stay off by
default (#6361). -
bugfix: contextual dot resolves functions returning
asyncin an async
context, e.g.await x.asyncFunc()(#6085). -
Breaking change: a pattern is checked against the type of the value it
matches, not a larger type. A variant tag the type lacks is error M0116,
with a suggestion for a misspelled tag (case (#suspnded)on
{#active; #suspended}); a signed literal againstNatis error M0050
(case (-1)), aslet n : Nat = -1already is; an option pattern
againstNullis error M0115. Before, only the coverage check flagged
these, and nothing did inlet ... else. To match at a larger type,
annotate the matched value:switch (n : Int). A pattern that fits the
type but is unreachable, such as a duplicate case, stays warning M0146
(#6429). -
bugfix: in
let ... else, an alternative that is never matched, such as
the second1inlet (1 or 1) = n else { ... }, is warning M0146, as
in aswitch. In any pattern, an unreached pattern nested inside
another one is no longer reported a second time (#6429).
-
-
Command line and tooling
-
Breaking change:
moc --check a.mo b.mochecks each file on its own.
Each file sees only its own imports, every imported library is checked
once, duplicate diagnostics are reported once, and an error in one file
does not stop the others.mocno longer concatenates several files
into one program:-c,--idland-rtake exactly one main file,
and the REPL (-i) preloads at most one. Use imports to split a
program across files (#6397). -
Breaking change: dead flags and primitives are removed.
-no-system-api(use-wasi-system-apito run outside the Internet
Computer, e.g. inwasmtime),-ref-system-api(the default needs no
flag),--trap-on-call-error(a failed call always throws an
Error),--generate-view-queriesand the__<var>queries it
generated,--(no-)experimental-multi-value,
--print-source-on-error,-no-link,--profile,--profile-file,
--profile-line-prefixand--profile-field.Prim.createActoris
removed; use actor classes, or the management canister's
create_canisterandinstall_code(#6357, #6403, #6405, #6411, #6417). -
chore:
-gemits only the DWARF line table,.debug_lineand
.debug_line_str. The.debug_abbrev,.debug_addrand
.debug_rnglistssections only served a.debug_infosection that
mocnever emitted (#6406). -
Breaking change: no more Intel-Mac release binaries. The
motoko-Darwin-x86_64tarball and its CI legs are dropped; on an Intel
Mac, build from source. x86_64 Linux, aarch64 Linux and Apple Silicon
binaries continue. Themotoko-base-library.tar.gzartifact is also
dropped;motoko-core.tar.gzis unaffected (#6355). -
feat: M0217 (redundant
persistent) and M0269 (deprecated.vals())
ship machine-applicable edits, somops check --fixremoves the
persistentand rewrites.vals()to.values()(#6439).
-
-
Bug fixes
-
bugfix: a type pattern field such as
let { type T } = min the body of
let o = object { ... }ormodule M { ... }no longer crashes the
compiler with an internal assertion. A well-typed binding is accepted,
and a scrutinee that is not an object reports M0113 as it does
elsewhere (#6431). -
bugfix: an upgrade that drops a stable variable without a migration
function is rejected even when it also adds a stable variable.
Depending on how the names hashed, as when renamingbalancesto
users, the runtime check accepted it and the dropped variable's
value was lost (#6432). -
bugfix: numeric field ids in imported Candid (
.did) files no longer
crashmoc. Hex ids such as0x7fparse, and an id that does not fit
in 32 bits is a syntax error (#6421). -
bugfix: clearer diagnostics. Calling a local
asyncorasync*
function from aquery(M0188) orcomposite query(M0187) says so
instead of claiming asharedfunction was called, and the M0185
warning on importing a Candid service constructor explains that its
initialization arguments are ignored;moc --explain M0185has the
details (#6425). -
bugfix: the interpreter (
moc -r) traps where compiled code does. An
out-of-boundsBlobindex or an index of 2^62 or more no longer
crashes with an internal error,Array_initandArray_tabulatetrap
on a length of 2^48 or more, andNat/Int**traps on an exponent
of 2^32 or more (#6422). -
bugfix: checking time with
--error-recovery, whichmoc.jsalways
enables, no longer grows cubically with the number of syntax errors. A
file with 400 syntax errors takes 0.05 s instead of 7 s (#6424). -
bugfix: a
switchon a two-legged variant that repeats a tag, such as
case (#admin t)followed bycase (#admin u), no longer compiles the
second case without its tag test. Passing the other leg bound that
leg's payload at the first case's type instead of trapping (#6427). -
bugfix: a class method returning
async*with an= ebody, such as
public func next() : async* Nat = v, no longer fails with M0137 when
the class is used at a record type: the method binds its own async
scope, like a block body always did (#6428). -
bugfix: generated Candid never reuses a user-written type name. A
suffixed namemocpicks for a generic instantiation or a same-named
type, e.g.Foo_1forFoo<Text>, no longer crashes withNot_found
or silently emits a wrong type when a user type has that name (#6416). -
bugfix:
--error-format=jsonbyte offsets are correct in files with
CRLF line endings.byte_start/byte_endwere one byte early on every
line after a CRLF, and a form feed, NEL, U+2028 or U+2029 in a comment
or text literal shifted every later offset, so tools applying edits by
offset, such asmops check --fix, garbled the file. The M0236
suggestion is also no longer dropped after a line ending in a lone CR
(#6393). -
bugfix: the M0236 and M0237 fix-its that remove a call's last argument
remove its trailing comma too, somops check --fixno longer turns a
multi-line call into an M0001 syntax error. Removing a juxtaposed sole
argument (f x) leavesf ()(#6392).
-
-
-
motoko-js (
moc.js)