Nimony progress release (October 2025)
What it is Nimony is a new compiler for a Nim variant aimed at becoming Nim 3.0 over time. The current focus is the aufbruch mode: destructor-based memory management (ARC), value-centric data layouts, predictable code generation, and a plugin-based metaprogramming model. A compat mode exists to keep Nim 2 code viable, with known ABI differences.
This release is another incremental step. It adds core language checks, a handful of standard modules, early plugin support, and compiler/tooling pieces that make day-to-day work less fragile. It is not “done.”
What changed since the last release Grouped by area, these are the changes that are confirmed to work in code and examples from the linked reports and docs.
Language and type system
- Generic code is type-checked at declaration time, not only at instantiation. Concepts are enforced, giving earlier and clearer diagnostics.
- Flow-sensitive not-nil analysis for refs works. Code that demonstrates nil/not nil refs and control-flow-based checks now compiles and runs. The global mode switch for default ref-nilness is not implemented yet.
- Exceptions: try/except works with ErrorCode. Routines that may raise must be annotated {.raises.}. Raising other exception types is not supported yet.
- Bounds and overflow checks:
- Builtin arrays perform runtime bounds checks.
- Overflow flag support is exposed via NIFC builtins (keepOverflowFlag / overflowFlag).
- Iterators and yield compile and run. fields and fieldPairs are available.
- Converters work (including string → openArray[char]).
- Operators and overloads for [], []=, ==, $ function as expected.
- Closures compile and run in basic cases; non-escaping closures avoid heap allocation; generic inner procs are lambda-lifted.
- Defer and using statements implemented.
- packed and union pragmas implemented.
Memory management and data structures
- ARC-based memory management is in place. The reports note no leaks in the exercised examples; recursive ref destructors work.
- ref objects (including recursive types) are supported.
- seq works (allocation, indexing, len, assignment), including synthesized hooks/destructors.
- openArray is a library-defined view type (not compiler magic) with .requires/.ensures-style preconditions to eliminate bounds checks when proven.
Metaprogramming and plugins
- Templates work. Template expansion can delegate to compiled plugins via {.plugin: "..."}; plugin output is sem-checked and cached.
- Beginnings of a plugin API exist. Source code filters are supported.
- A rewrite rule (pragma-to-section) is implemented: proc p {.t.} → t: proc p ...
- astToStr, system.compiles, do notation are implemented.
Standard library
- Newly working/ported modules: std/[tables, hashes], std/intsets, std/parseopt, std/dirs, std/unicode, std/encodings, std/monotimes, std/locks, std/rawthreads, std/syncio.
- std/math is partially ported (contains many consts and procs). strutils is listed as an active target; not done.
Compiler and backend
- Precompiled modules reimplemented.
- Whole-program dead-code elimination; merges generic instances.
- “Find usages” and “go to definition” (—usages/—def) work.
- Control-flow graph construction was rewritten (“split streams” algorithm).
- Code generation via NIFC is in place.
Concurrency
- Raw threads are supported via std/rawthreads.
- .passive (CPS) procs exist in basic form, but integration with try/finally/raise requires performing CPS later in the pipeline. Early releases will likely not include .passive features.
Two small examples that reflect what’s actually implemented
- Error handling via ErrorCode:
import std/errorcodes
proc usePositive(x: int) {.raises.} =
if x < 0:
raise ErrorCode.RangeError
# use x- openArray as a library view (converter from string):
type
openArray*[T] {.view.} = object
a: ptr T
len: int
converter toOpenArray*(s: string): openArray[char] =
openArray[char](a: rawData(s), len: s.len)Why this matters in practice
- Earlier, stricter generic checking reduces iteration time and improves IDE tooling (you get real errors and completions before instantiation).
- Destructor-based ARC composes with resources. You can build containers of things that hold OS handles and have them clean up deterministically.
- ErrorCode gives a single, allocation-free error path and aligns with OS and protocol error domains (errno, Win32 error codes, HTTP status).
- Making openArray a plain view type, plus contract annotations, removes “special case” magic and makes it easier to write zero-overhead, safe APIs.
- The nascent plugin system shifts metaprogramming to compiled transformations that run after type checking, which is simpler to reason about than untyped macros.
- Whole-program DCE and generic merging keep binaries and compile times in check as libraries grow.
- With unicode, encodings, locks, monotimes, and raw threads, small real programs (CLI tools, file walkers, table-driven code) are practical today.
Compatibility and modes
- Two language modes:
- aufbruch (current focus): only mm:atomicArc, not nil by default (design), small stdlib, plugin-based metaprogramming, APIs prefer ErrorCode. The runtime aims to handle OOM gracefully; some parts are design-stage or early-stage, not all are implemented.
- compat: intended to be close to Nim 2 semantics but with limited implementation complexity. ABI differences are expected.
- Known interoperability differences even in compat:
- string, seq, and ref layouts are not ABI-compatible with Nim 2.
- Due to string COW, passing s[i] as var char is disallowed.
- seq <-> string casts are disallowed.
- string → cstring conversions may be more expensive (no guaranteed trailing zero).
Not in this release (planned or acknowledged gaps)
- Full exception hierarchy beyond ErrorCode.
- The effect system (no tags, no raises list, no sideEffect tags).
- Range subtype, user-definable pragmas, dirty templates, backtick identifier construction, overloading ().
- Closure iterators.
- Term-rewriting macros (superseded by plugins; module/type-attached plugins are the intended path).
- Circular dependency solution for modules is not implemented.
- Mode switch for default ref nilness is not implemented.
- CPS/async integration with try/finally/raise and the unified spawn/await story (design described; implementation partial).
- std/math and std/strutils are incomplete.
What’s next (no promises; this is the working queue)
- Finish std/strutils and round out std/math.
- Stabilize and document the plugin API; expand module/type-attached plugins.
- Integrate CPS later in the pipeline so .passive plays well with finally/raise; then expose structured parallelism (e.g., parallel for) via plugins.
- Implement the mode switch for ref nilness defaults.
- Continue compat-mode work and document ABI/FFI trade-offs.
- Investigate cycle collection for ARC (.cyclic pragma, if/when it’s viable).
If you have time, try things out, report bugs, ask questions, and send small, focused PRs—every bit of feedback helps shape the direction.
Links
- Design principles: https://nim-lang.org/araq/nimony.html
- Manual: https://nim-lang.github.io/nimony-website/index.html
- Deep overview: https://deepwiki.com/nim-lang/nimony
- Architecture notes: https://github.com/nim-lang/nimony/blob/master/doc/design.md
If your time is scarce but you want this direction to continue: Contributions are welcome via our Open Collective at https://opencollective.com/nim.