Repository navigation
v0.16.0 — Mojo 1.1.0, and error messages that say what they mean
Adopts the Mojo 1.1.0 stable toolchain (max = "==26.6.0", from Mojo
1.0.0 / 26.5.0). This is a minor rather than a patch for that reason alone:
downstream addons compile against the published src/, so taking this upgrade
moves them onto that toolchain. No public Mojo signature changed, so they
compile unmodified once there. Full account in
docs/toolchain-migrations.md.
It also corrects every computed error message the framework produces. Those
trailed heap garbage in every release to date — see Fixed, below. Anything
asserting on a type-mismatch message with a substring match will keep passing;
anything comparing one whole will now see the message it expected.
Fixed
- Every computed error message trailed heap garbage. The eight
throw_js_*_dynamichelpers passed a MojoString's buffer to
napi_throw_*, which reads a NUL-terminatedconst char*; a MojoString
carries no terminator, so JS sawgreet: expected string, got numberuffer
(the tail is whatever the heap held next). Every generated type-mismatch
error was affected. The message is now NUL-terminated with
as_c_string_span()before the call, and the tests assert the whole
message withtoBerather thantoContain, which is how the garbage
passed. Found by runningexamples/codegen/codegen.jsin CI for the first
time and reading its output. Mutation-checked: reverting all eight
terminations fails 2 of the 6 exact assertions — the tail is whatever the
allocator left adjacent, so a given message may come back clean, which is
the other reasontoContaincould never have caught this. - Legacy closure parameters spelled bare
capturingread a dead stack slot
on 1.1.0, silently.with_handle_scopeandparallelize_safenow declare
capturing[_], which binds the captured values' origins. The addon's
scopedCallwas reading a garbageJsFunctionfrom a reclaimed slot and
failing withnapi_invalid_arg;tests/host.test.jscaught it. No warning
or error is emitted for the bare form, so the only signal is behaviour. parallelize_safedid not compile against MAX 26.6, which moved
parallelizeto a unified closure argument (parallelize(func, n)). It
keeps its legacy public signature and wrapsfuncin a unified closure.
The breakage was invisible tobuild.shand to the whole Jest suite —
nothing instantiatesparallelize_safein the addon graph, so Mojo never
elaborated its body — and was surfaced only by
tests/compile/framework_coverage.mojo.
Added
docs/MOJO-RULES.mdships in the package — the "Mojo dialect and FFI
rules" section of the repository'sCLAUDE.md, extracted verbatim by
scripts/generate-rules-extract.mjsand held in lockstep by CI. A downstream
addon compiles against this framework's source and until now got none of the
rules that keep that code from crashing: a barecapturingreads a dead
stack slot,_ = x^is not a keep-alive, the function-pointer bitcast is
spelled in exactly one place. A hand-copied extract would drift silently,
which is why it is generated.NodeHost.console_error(msg)and thehostConsoleErrorexport (#113) —
writes one line to stderr through the host'sconsole.error, the diagnostic
counterpart toconsole_log.console_logwas the only output method on
NodeHost, so a host-mode program whose stdout is piped into something else
had no way to say anything about itself without corrupting that stream.
Routing through the host console (rather than writing to fd 2 directly)
keeps output interleaved correctly with the host's own writes and honours
whatever the embedder has done toconsole.parallelSquares(n, scale)— returns aFloat64Arraywhere
[i] = i * i * scale, computed throughparallelize_safe. A diagnostic
export in the spirit ofasyncRuntimeInitOk(), covering the other silent
failure mode: the work runs but the captures are wrong. Every element
depends on both captured values and the buffer is pre-filled with a
sentinel, so a broken capture or a skipped index cannot produce a correct
array. It is also the only thing that instantiatesparallelize_safein the
addon graph, so a break there can no longer be invisible tobuild.sh—
which is exactly how this bump's breakage hid. Mutation-checked: reverting
runtime.mojoto barecapturingmakes it SIGSEGV (node exits 139), so the
regression surfaces as a crashed Jest worker rather than an assertion diff.
Changed
@parameter→@__parameteron closures passed as parameters (1.1.0
rename; the old spelling warns).StringLiteral.unsafe_ptr()→ptr()at the 38 sites the compiler flagged.
String.unsafe_ptr()andStaticString.unsafe_ptr()are unchanged.- The addon build and the compile-coverage gate are warning-clean again. Five
"assignment never used" warnings that the dev2026080905 notes recorded as
false positives oncapturingclosures are gone: they were a symptom of the
untracked capture, not a compiler quirk. - CI now compiles every piece of the repo's own Mojo with
--Werror
(build.shandtests/codegen/build.shdefault to it;NAPI_MOJO_WERROR=0
opts out for a toolchain bump), withscripts/check-werror.mjsproving on
every run that the flag still fails on a warning inside an imported module.
spike/ffi_probe.mojo's deadexceptbranch was flattened to get there.
The Mojo samples in README, TUTORIAL, CONTRIBUTING and CLAUDE.md are now
checked verbatim against CI-compiled source (scripts/check-doc-samples.mjs);
the README's struct example quoted a function that existed nowhere and now
quotes the tutorial's.examples/codegen/is built and run in CI for the
first time. Decisions and the deliberately ungated remainder are recorded in
docs/plan-lazily-checked-artifacts.md. - The changelog-diffing recipe in
docs/toolchain-migrations.mdnow points at
the modular monorepo's current layout (Mojo/docs/site/..., release tags
mojo/v<X.Y.Z>). The oldmojo/docs/...paths 404, which reads as "no such
changelog" rather than "wrong path".