RunMat
GitHub
Back to Blog

RunMat Runtime 0.5 is out: multi-file project support, and resolving MATLAB language semantics like Rust's compiler

Published06/03/2026
9 min read

RunMat Runtime 0.5.0 is now available in the CLI, via Crates.io, and via RunMat Desktop.

If you have been watching RunMat and waiting for it to handle more of the MATLAB language surface, this is the release where a lot of that missing shape lands.

0.5.0 adds:

  • folder-backed projects with runmat.toml and runmat.json
  • named entrypoints runnable through runmat run <name>
  • source-root, package-folder, class-folder, private-helper, and local dependency discovery
  • requested-output-aware calls, including nargin, nargout, varargin, and varargout
  • local functions, nested functions, anonymous functions, captures, function handles, and feval
  • source classdef object construction and runtime class metadata for constructors, properties, methods, inheritance, access rules, value vs handle behavior, static members, super calls, and custom indexing hooks
  • explicit indexing context for reads, writes, deletes, cell expansion, end, and object dispatch
  • workspace behavior for script exports, function locals, ans, and dynamic workspace operations through VM workspace context
  • typed builtin descriptors for signatures, output behavior, completions, docs, and stable errors
  • structured execution outcomes for REPLs, notebooks, Desktop, WebAssembly, the LSP, and embedded hosts
  • better semantic inputs for acceleration planning and future JIT coverage

RunMat now resolves much more MATLAB meaning before bytecode is generated, and those facts feed runtime execution, the language server, bytecode generation, JIT fallback decisions, and acceleration planning.

Project-shaped code now works

RunMat 0.5.0 adds project composition around runmat.toml and runmat.json manifests. A project can name its package, declare source roots, list local dependencies, and expose runnable entrypoints.

[package]
name = "signal-demo"
version = "0.1.0"
runmat-version = "0.5"

[sources]
roots = ["src", "examples"]

[dependencies]
filters = { path = "deps/filters", version = "0.1.0" }

[entrypoints.main]
path = "src/main"

[entrypoints.demo]
path = "examples/demo"

That manifest can sit next to a normal MATLAB-style source tree:

signal-demo/
  runmat.toml
  src/
    main.m
    @SignalModel/
      SignalModel.m
    private/
      normalizeSamples.m
  examples/
    demo.m
  deps/
    filters/
      runmat.toml
      src/
        +filters/
          lowpass.m

Note: a runmat.toml file is optional for direct file execution. Without a manifest, RunMat can still execute a file and discover supported nearby companion sources. The manifest makes source roots, dependencies, imports, and named entrypoints explicit and reproducible.

The MATLAB parts remain MATLAB parts. @SignalModel is still a class folder. +filters is still a package folder. private/normalizeSamples.m is still private to its source area. main.m can still be an ordinary script or function file.

What runmat.toml adds is a stable project boundary. [sources] tells RunMat which folders belong to the project source index. [dependencies] makes the local filters project available to name resolution. Named entrypoints give users, tools, and hosts a stable way to run workflows:

cd signal-demo
runmat run main
runmat run demo

Direct file execution still works. You can still run a single .m file by path. The difference is that a manifest lets RunMat know the intended source roots, dependency projects, and runnable entrypoints up front.

Source-level import keeps its MATLAB role. The dependency declaration makes the filters project available; import controls which names are visible and convenient inside a file:

import filters.*

model = SignalModel(samples);
[filtered, stats] = lowpass(model.samples, 30);
model.history{end + 1} = stats;
plot(filtered);

In that example, SignalModel can resolve against the class under src, and lowpass can resolve through the declared filters dependency. The same project graph is visible to runtime execution, the language server, bytecode generation, JIT fallback decisions, and acceleration planning.

The compiler now resolves MATLAB semantics first

The compiler pipeline now looks like this:

source
  -> AST
  -> semantic HIR
  -> MIR
  -> MIR analysis
  -> VM layout + bytecode
  -> runtime/providers

RunMat now lowers source into progressively more explicit compiler products before execution, similar to how Rust's compiler pipeline works.

That matters because MATLAB syntax is compact and context-dependent. SignalModel(x) could be construction, function call, variable indexing, or an unresolved external. A call with one output is not the same semantic operation as the same call with two outputs. A(i), A(i) = v, A(i) = [], C{:}, and object indexing all have different runtime meaning.

In earlier designs, those decisions tend to spread across the interpreter, runtime dispatch, bytecode lowering, editor tooling, JIT, and acceleration planner. 0.5.0 gives RunMat a shared representation for the answer.

For the compiler pipeline deep dive, see the runtime/compiler pipeline post and the compiler pipeline docs.

What is now implemented

The 0.5.0 compiler/runtime migration makes several MATLAB concepts first-class.

Function calls carry callable identity and requested output count. That output count is observable through nargout, affects varargout, and matters for function handles, feval, cell expansion, and multi-assignment. The VM no longer has to infer it from stack shape or string lookup.

Functions are assembly-level semantic products. RunMat can represent named functions, local functions, nested functions, anonymous functions, captures, recursive calls, nargin, nargout, varargin, varargout, function handles, feval, and the supported arguments subset. Nested functions are no longer compiled as accidental global-looking names; they carry parent and capture relationships before VM layout assigns slots.

Source classdef now participates in the same pipeline as scripts and functions. Class source produces runtime class metadata for names, constructors, properties, methods, inheritance, access rules, value vs handle behavior, static members, super calls, and custom indexing hooks. There are still metaclass edges outside 0.5.0, but class source is no longer just a parsed shape with a struct-like fallback.

Indexing and assignment carry context. HIR records whether an index expression is a read, assignment target, deletion target, comma-list read, or function-argument expansion. MIR then lowers that into explicit place/value behavior before bytecode is emitted. This is especially important for end, cell indexing, deletion, and overloaded object indexing.

Workspace-visible bindings are also explicit. Scripts can export top-level variables, functions can keep locals inside function frames, ans can remain special, and dynamic workspace operations can route through VM workspace context instead of bypassing the compiler model.

Builtins, tooling, and hosts share more metadata

Builtins now carry typed descriptors for signatures, output behavior, completion policy, and stable errors. That metadata feeds runtime execution, the language server, bytecode generation, JIT fallback decisions, and acceleration planning.

The execution API is also more structured. RunMat execution can report workspace deltas, display events, stream output, diagnostics, observed workspace and environment effects, figures touched, profiling data, suspension state, and fusion-plan metadata. That matters for REPLs, notebooks, Desktop, WebAssembly, language-server workflows, and embedded hosts. They need to know what happened, not only whether a scalar was returned.

Acceleration and JIT status

0.5.0 gives acceleration better inputs, but it does not move concrete GPU residency into the compiler. The compiler can identify semantic fusion candidates, operation kinds, effects, and instruction windows. The runtime and provider still decide whether values are resident on a GPU, whether an operation is supported, whether data must be gathered, and which concrete host or provider path should run.

The WGPU provider was also split out of a monolithic implementation into clearer state, initialization, helper, trait, and operation modules. That refactor is mostly about making future provider work easier to extend as more kernels, residency policies, and diagnostics land.

The JIT path has moved forward too, but it is not complete yet. Most programs still fall back to the interpreter today. In 0.5.0, the JIT gets a better semantic boundary to compile through: a tagged value ABI for runtime values, with semantic function identity and requested-output behavior preserved across host calls.

From here, broader Cranelift lowering is mostly compiler engineering and coverage work, not a new MATLAB semantics project. More JIT coverage is planned before 1.0, with the interpreter remaining the correctness fallback whenever a bytecode pattern is not compiled yet.

What this changes for future work

0.5.0 also changes how future RunMat work should be done. New language behavior should become a semantic HIR, MIR, analysis, layout, runtime, or provider product. It should not require the VM to guess what source pattern produced a stack shape.

That is why this release matters beyond the features listed above. More builtins, more class behavior, broader JIT coverage, richer acceleration, and better tooling now have a shared compiler/runtime foundation instead of separate lookup and dispatch paths.

Scope and remaining work

0.5.0 is a major compiler/runtime revision, and an important step forward for RunMat.

The JIT is not finished and most execution still falls back to the interpreter, so CPU heavy bytecode patterns may still fall back to the interpreter and therefore execute slower than native code. The supported arguments subset covers common forms, not every advanced declaration shape. Some introspection surfaces intentionally return narrower metadata than MATLAB. Full meta.class behavior is not implemented. Async scheduling, cancellation, blocking-pool behavior, GPU lifetime, residency, and provider-handle work continue beyond this release.

Those are scoped limitations. RunMat now has the compiler products needed to keep moving those behaviors into the right layer as coverage expands.

Try it

For an existing RunMat user, the best way to exercise 0.5.0 is to point it at a real folder-backed project.

Add a runmat.toml, declare the source roots, give the project one named entrypoint, and run it:

[package]
name = "my-project"

[sources]
roots = ["src"]

[entrypoints.main]
path = "src/main"
cd my-project
runmat run main

If the project depends on another local RunMat project, add it under [dependencies] and use normal MATLAB import syntax inside the source where that improves readability.

Enjoyed this post? Join the newsletter

Monthly updates on RunMat internals, development, and performance tips.

Download RunMat

Download RunMat for full performance, or use RunMat in your browser for zero setup.