By a wide margin, the fastest Acton to date! Massive compilation speedups,
mostly from optimized data structures (HashMaps, IntSets etc) rather than
linear list searches, concurrent type checking and concurrent parsing etc -
which makes the compiler over 5000x for a particularly large test module
(~300MB .act file). A new back pass compilation technique, which we call
Deferred Back Passes (DBP), selects only the interesting parts of a module for
compilation, providing another massive speedup.
Compiler & Build
- Add concurrent top-level type checking for total statements, allowing the
compiler to check independent top-level definitions in parallel while keeping
source-order semantics for non-total statements. [#2773, #2782]- Verbose and timing builds now report inferred signatures for non-total
statements, making it easier to add explicit signatures and unlock more
concurrent checking. - Type errors from independent total statements are collected into multiple
diagnostics instead of stopping at the first failing statement. - Use a fixed worker pool with bounded lookahead, improving memory behavior
and performance for modules with many independent top-level definitions.
- Verbose and timing builds now report inferred signatures for non-total
- Parse modules in parallel by splitting top-level statement chunks across
parser workers, reducing parser latency for large source files while
preserving source-order results. [#2787]--parse-serialkeeps the whole-file parser available for debugging and
fallback comparisons.- Module-suite validation now uses a set-based duplicate check, avoiding
quadratic validation time in large projects.
- Report parser progress percentages during builds, giving useful feedback for
very large files before type checking starts. [#2784] - Report hashing as its own front-pass progress phase in CLI and LSP builds, so
large modules show source, implementation, interface, and dependency hashing
work instead of appearing stuck after type checking. [#2905]- Type-checking and hashing progress updates are paced to keep detailed
progress reporting from slowing large-module builds through excessive UI
churn.
- Type-checking and hashing progress updates are paced to keep detailed
- Report active back-pass progress for normalize, deactorize, CPS, lambda
lifting, boxing, code generation, render, and write steps, so CLI and LSP
builds show which generated-code phase is currently running. [#2808, #2853] - Generate unboxed code for bounded integer and float values through the boxing
and code-generation passes, a major reduction in wrapper allocation and
box/unbox traffic for numeric Acton programs. Fewer boxes also means less
memory traffic and less garbage for the runtime to collect, so numeric-heavy
code can improve both by doing faster primitive work and by putting less
pressure on the memory subsystem and garbage collector. [#2824, #2890]- Builtin, base, and standard-library C implementations now use raw C values
for unboxed integer and float operations where the Acton type is known. - Cached module interfaces from older boxed-primitive compiler versions are
treated as stale and rebuilt instead of being reused across the
representation change. - As one example, the existing
examples/sumto.actbenchmark at 30 million
integer additions dropped from about 960 MB of GC heap allocation to about
54 KB, with reported GC time falling from about 2.05 seconds to 0 ms and
wall time from about 1.47 seconds to about 0.12 seconds. - In a standalone run of the DCT performance fixture at size 2,000, GC heap
allocation dropped from about 768 MB to about 384 MB, reported GC time from
about 1.58 seconds to about 0.79 seconds, and wall time from about 1.14
seconds to about 0.65 seconds.
- Builtin, base, and standard-library C implementations now use raw C values
- Preserve each module's import context in cached module interfaces, so builds,
acton sig, documentation, and completion can reuse cached interfaces without
losing imported class and protocol information. [#2779] - Store cached module interfaces in LMDB-backed
.tydbdirectories instead of
monolithic.tyfiles, keeping corrupt or version-mismatched entries as safe
cache misses while adding keyed records for headers, imports, names, hashes,
dependency hashes, and typed statements. [#2819, #2855, #2858, #2889, #2896,
#2907]- Full reads reconstruct source-order environments from explicit order keys,
and cache copy and cleanup paths now handle directory artifacts. - Cache values are encoded with
persist, improving.tydbwrite and read
time for large generated interfaces while treating older binary-encoded
caches as stale rebuilds. - Dependency hashes are indexed by provider module and name, so stale-cache
checks can skip unchanged modules cheaply and only inspect the dependency
names a cached module actually used when an upstream module changed. - Preparing
.tydbentries for names, hashes, extensions, and typed
statements now happens in strict concurrent chunks before the LMDB write
transaction, and CLI progress distinguishes preparation from writing. - Read transactions retry transient LMDB lock-table setup errors with a fresh
environment and a longer backoff window, matching the existing retry path
for environment opens. - Build and statically link LMDB from a pinned bundled dependency so compiler
builds do not depend on the host LMDB installation.
- Full reads reconstruct source-order environments from explicit order keys,
- Run front-output writes asynchronously after front passes, so cached
interface writes,.tydbcopies, and documentation rendering can overlap with
downstream work while watch and LSP builds discard stale-generation writes.
[#2851] - Add deferred back passes for very large modules, letting the compiler run the
normal front passes first and then generate code only for declarations that
are reachable from the program, similar to tree shaking, dead-code
elimination, or link-time optimization for generated Acton modules. [#2843,
#2884]- Automatic selection starts at modules with at least 1,000 top-level names;
--dbp MOD[:NAME,...]can force focused experiments, and--no-dbp
disables both heuristic and forced selection. - Deferred back passes prune typed modules to interested names, root actors,
local dependency closures, and class/protocol extension dependencies before
running the existing back-pass chain. - Modules at explicit
Build.actlibrary boundaries compile normally so
declared library outputs keep their full generated surface.
- Automatic selection starts at modules with at least 1,000 top-level names;
- Skip build-time HTML documentation pages for modules with more than 10,000
top-level names while keeping compilation and cached interfaces intact. The
generated project documentation index still lists those modules without
linking to missing pages, and explicitacton doc FILE.actrequests still
render the requested source file. [#2884] - Fix concurrent
.tydbreads so shared interface caches retry transient LMDB
lock-file setup races and propagate environmental errors with their real
cause instead of reporting a missing interface. [#2841] - Speed up compiler environment lookups, scans, module-environment rewrites,
attribute queries, solver candidate enumeration, substitutions, and witness
resolution in large modules by indexing active names, signature and
definition locations, state-variable and type-variable names, solver
candidate sets, term substitutions, and type witnesses; separating closed
imports and finalized top-level names from live local bindings; avoiding
temporary inherited-attribute lists for single-attribute queries; reading
substitution state once per traversal; narrowing used-substitution tracking to
active entries; and deduplicating reserved names with a hash index while
preserving source-order semantics. [#2789, #2796, #2797, #2799, #2800, #2816,
#2833, #2837, #2839, #2840, #2924] - Speed up documentation rendering and generated-name hashing by indexing
documentation type lookups and hashing internal/generated names without
rendering prefix strings. [#2830, #2831] - Report more detailed front-pass timing for type reconstruction, result
forcing, hash computation, cached interface writes, and documentation output,
and speed up signature validation by checking bindings through a set. [#2848]- Build progress output now keeps long five-digit second durations aligned
with shorter timings, using the same width for active progress lines and
completed timing logs. [#2901, #2908] - Multi-line type-check result blocks, including timing details and inferred
signatures, are emitted atomically so concurrent module builds cannot
interleave another module's progress between detail lines. [#2925] - Build progress labels avoid repeating the project prefix when module names
are already project-qualified, keeping dependency build output shorter and
easier to scan. [#2929]
- Build progress output now keeps long five-digit second durations aligned
- Speed up back-end work on large modules by indexing code-generation name
membership and boxed variable lookups, and by tracking boxing-pass witness
names with a hash set, reducing repeated scans during boxing and generated
C/H rendering. [#2806, #2810, #2895] - Hash implementation fragments and normalized source/implementation AST
fragments through direct byte and structural streams, making cached change
detection cheaper while preserving per-name build hashes and ignoring
location-only source changes. [#2811, #2897, #2900] - Hash per-name source/implementation and interface fragments in parallel, and
deduplicate dependency lists before unaliasing or writing.tydb
dependency-user rows, speeding the hash phase and large generated interface
writes while preserving stable cache contents. [#2921] - Fix quantified type-variable scope cleanup so leaving an inner quantifier only
drops witnesses associated with the removed variables, preserving unrelated
witnesses for later type checking. [#2802] - Fix actor state-variable inference so unannotated
varfields keep their
inferred type when actor assumptions are matched instead of being widened to
valueafter ordinary assignments. [#2894] - Fix generated C declarations so parameters, return values, and local
declarations all keepvolatilewhen the compiler environment marks the
variable as volatile. [#2898] - Fix generated protocol witness method tables so inherited abstract slots are
filled through forwarding wrappers only from concrete ancestor witness
classes, avoiding both empty slots across multiple protocol inheritance paths
and unrelated classes that merely share a method name and ABI. [#2887,
#2927] - Bump the GHC toolchain to 9.8.4 (Stackage lts-23.28), fixing a rare arm64
segfault in the compiler. GHC 9.6.6's runtime could crash in the garbage
collector by following a NULL closure pointer (GHC #24791, black holes in
large objects); the static LMDB interface cache shifted the binary layout
enough to expose it, producing intermittent SIGSEGVs on arm64 (Linux and
macOS) duringpkgoperations and when compiling some programs. 9.6.7
reduced the crash rate and 9.8.4 eliminates it (0 crashes across thousands of
builds that reproduced it on 9.6.x). [#2832, #2834]
CLI & Project Workflow
- Resolve project modules under their
Build.actproject name, so dependency
modules are imported through the name declared in thedependenciestable.
For example, a dependency listed as"analytics": (...)is imported with
import analytics.events, while local project modules still support
unprefixed imports. [#2914]- Importing a dependency name resolves to that dependency's
lib.act, so
import analyticsworks for the dependency root module and
import analytics.eventsworks for other dependency modules. acton sig, root actor selection,--modules, deferred-back-pass module
selectors, library outputs, generated file paths, and incremental rebuild
traces understand project-qualified module names.- Projects now reject source trees whose first module path segment overlaps a
declared dependency name, avoiding ambiguous imports between local source
modules and dependency projects.
- Importing a dependency name resolves to that dependency's
- Add
acton repl, a live interactive Acton shell for trying code, inspecting
values, and getting immediate feedback as you explore. [#2842]- It uses the normal compiler pipeline inside a scratch REPL directory, so
inputs are parsed, type checked, code generated, and run like ordinary
Acton code; session and evaluation modules are compiled as dynamic
libraries so most evaluations avoid the long runner relink step. - Because each interaction produces and reruns a normal executable, the REPL
does not keep a live interpreted runtime. Retained imports, declarations,
and setup assignments are replayed for each evaluation, unlike REPLs that
keep an interpreted process alive between inputs. - The REPL retains imports, declarations, and setup assignments as session
source, prints non-Noneexpression results, supports multi-line blocks,
and provides:show,:reset,:dep, and:quitcommands.
- It uses the normal compiler pipeline inside a scratch REPL directory, so
- It is now possible to customize project library output in
Build.actby
declaring selected modules as named static or dynamic libraries that
executables can link against. [#2809]- Dynamic libraries are installed in the normal output layout with executable
rpaths, so a project can rebuild a library module and keep running an
existing executable against the updated shared library. - Incremental builds keep every module in a declared library together, so
library artifacts stay complete even when only one member module changed.
- Dynamic libraries are installed in the normal output layout with executable
- Use the canonical
actonbinary in Makefile build, test, distribution, and
release recipes while keepingactoncas a compatibility symlink and install
alias. [#2882] - Let
Build.actZig dependency options refer to the compiler toolchain's
bundled dependency directory with the{sysdeps}sentinel, written as
"{{sysdeps}}"in Acton strings, so project wrappers can use bundled headers
and libraries without hardcoding the installeddistpath. [#2915, #2917]- Commands that rewrite
Build.act, such asacton pkg add, now re-escape
literal braces in strings so sentinel values and other brace-containing
names, descriptions, dependency keys, and option values round-trip safely. - Unescaped interpolation in Zig dependency option values now reports a clear
project error naming the dependency and option instead of silently dropping
the option from the generated Zig build.
- Commands that rewrite
Runtime & Standard Library
- Add an RTS sync-pause primitive for worker threads, allowing a worker to
cooperatively park the rest of the worker pool around synchronized runtime
operations such as dynamic library code reload. [#2815] - Expose standard library modules through a bundled
stdproject and namespace,
so applications can import modules such asstd.json,std.argparse, and
std.xmlwithout declaring an external dependency. [#2817]- Project builds add an implicit
stddependency unless one is declared, and
compiler lookups include bundledstdinterfaces alongside base. - The distribution builds and ships
dist/stdseparately fromdist/base,
keeping base focused on builtins and runtime support.
- Project builds add an implicit
- Fix XML decoding so comments between text/CDATA nodes and child elements are
ignored while preserving the surrounding text and tail content. [#2886] - Fix macOS crash handling so the RTS can print a backtrace without relying on
Linux/proc/self/exe, then restore the platform crash handler before
re-raising the original signal. [#2909] - Move TLS and DNS native I/O allocations to explicit ownership, keeping
mbedtls and tlsuv state off the GC heap while preserving GC roots for libuv
handles. TLS listeners and connections now close native streams through
explicit close paths with actor cleanup as a safety net, queued TLS writes
keep their payloads alive until completion, and immediate TLS connect setup
errors are reported to callbacks instead of being ignored. [#2911]TLSListenernow exposesclose(), accepted TLS connections can outlive
the listener actor, and accepted connections stay pinned to the worker
thread that owns their libuv stream.
- Fix
list.indexon empty lists so it raisesKeyErrorfor a missing element
instead of rejecting the default stop position. [#2801]
Packages & Distribution
- Change x86_64 Linux builds from statically linked GNU libc to dynamically
linked GNU libc while keeping other libraries statically linked. [#2774,
#2781]- The x86_64 Linux release job and Debian package builds run on Debian 11 to
provide a glibc 2.31 floor. - Nightly CI still covers Debian 11, 12, and 13 on x86_64.
- CI now validates the dynamic library set and maximum required GLIBC version
foracton,lsp-server-acton, andactondb.
- The x86_64 Linux release job and Debian package builds run on Debian 11 to
- Build the compiler's host-linked C libraries with bundled Zig projects
instead of host static archives, so release builds can linkzlib,gmp, and
tinfo, and LMDB against the selected platform target. [#2819, #2822,
#2827]- Linux compiler links prefer the bundled static archives and Zig's libc++ /
libunwind over host libraries, preserving the chosen glibc target. - macOS compiler links can use the same bundled static archive path for zlib,
with Mach-O archive alignment and a stable macOS deployment target.
- Linux compiler links prefer the bundled static archives and Zig's libc++ /
- Vendor the
diagnosecompiler diagnostic library from upstream after
diagnoseremoved its staletext <=2.0dependency, so compiler builds can
use the resolver'stextpackage instead of pinningtext-2.0just for
diagnostics. [#2783] - Keep Acton-owned Zig build files for bundled dependencies in this repository
and overlay them during dependency extraction, making Zig build glue easier to
update while allowing dependencies without source changes to use upstream
source archives. [#2805] - Port Acton and bundled dependency build files to Zig 0.16, including remote
dependency fetching and cache archive extraction foracton pkg add,
acton fetch, and project builds with remote dependencies. [#2807] - Fix release tarball naming to use the stamped
VERSION_INFOdirectly, so
make releasekeeps the tarball version aligned with the compiler version
and tagged releases keep plain version numbers. [#2928] - Fix Windows Acton program execution by building the vendored libuv Windows C
sources without Zig/Clang's null sanitizer, avoiding a sanitizer trap in
libuv's async wakeup path. [#2902]
Documentation
- Document when and how to use the Acton container image, including
copy-pastable Docker commands for checking the compiler version and building
the current project. [#2767] - Document dependency import prefixes across the user and developer guides,
includingBuild.actdependency-name examples, dependencysrc/lib.act
imports through the dependency name, and other dependency modules under the
same prefix. [#2932]
Testing & CI
- Add make targets for accepting compiler golden outputs, including separate
targets for Sydtest goldens, Acton Tasty goldens, and the combined compiler
golden workflow documented in the developer guide. [#2821] - Assert incremental rebuild behavior directly in compiler tests, checking
stale and fresh modules, type checking, code generation, public-hash
propagation, doc-only edits, and codegen-only stale outputs instead of
comparing fragile concurrent progress-output goldens. [#2923, #2926] - Add dedicated compiler benchmark drivers for performance work. [#2794, #2813]
- The type-checking benchmark reports parse, environment, kind-checking, and
type-checking timings, with optional RTS allocation and GC statistics. - The compiler pipeline benchmark can stop after parsing, kind checking, type
checking, front passes, documentation-aware front passes, or the full
front/back pipeline.
- The type-checking benchmark reports parse, environment, kind-checking, and
- Refresh Acton compile-cache snapshots from nightly and manual workflow runs,
while pull request and push builds restore those snapshots without saving new
cache entries. [#2823, #2826]- Haskell dependency caches are keyed separately from the Acton/Zig compile
cache, and cache saves require successful build and release steps so failed
builds cannot publish partial snapshots. - The Acton/Zig compile cache uses one stable per-platform key refreshed by
producer runs, with stale entries evicted before saving so CI does not keep
a week of duplicate snapshots. - The nightly producer workflow now runs at 02:00 UTC. [#2931]
- Haskell dependency caches are keyed separately from the Acton/Zig compile
- Run cross-compilation checks in a dedicated CI job after Debian packages are
built, covering macOS, Windows, GNU/Linux, and musl targets without bloating
the default test job's compile cache. [#2850, #2852, #2902]- Windows cross-compilation uses
--no-threads, matching the supported
Windows target mode, and nightly/manual runs refresh a separate
cross-compilation cache. - Cross-compiled Windows
hello.exeartifacts are preserved and run on
matching x86_64 and arm64 Windows runners. - Linux container jobs free host disk before restoring compile caches, and
release, container, apt, and Homebrew publishing jobs now wait for the
cross-compilation job. - The cross-compile job installs the freshly built Debian package with
dpkg -i, avoiding unrelated hosted-runner apt repository failures while
still testing the produced artifact. [#2904]
- Windows cross-compilation uses
- Keep CI cache restores clean after Stack/GHC changes by removing broad Stack
restore-key fallbacks, and make the standard-library time test tolerate slow
CI startup while still catching gross clock errors. [#2836, #2838] - Cache downloaded source archives in source-build and Debian package-build CI
jobs with keys based on dependency build inputs, so transient archive download
failures are less likely to break unchanged release builds. [#2856] - Retry network-bound package-manager installs and flaky distributed-database
test binaries in CI, and print Linux container disk usage around cache restore
and test execution to make out-of-space failures easier to diagnose. [#2846] - Run REPL command tests against the normal persistent scratch path and add
in-process renderer checks, while keeping explicit--tempdircoverage for
scratch-directory isolation behavior. [#2881] - Avoid running the compiler standard-library test groups twice in
make test
by leaving those checks totest-stdlib. [#2870] - Switch external application CI coverage from the old Orchestron repository to
StratoWeave while keeping the reusable app-test workflow. [#2888] - Add a generated class-heavy type-checking fixture to exercise concurrent
type-checking scheduler performance on large recursive class structures. [#2777]
Compatibility Notes
- Projects that import dependency modules by bare source module names may need
to qualify those imports with the dependency name fromBuild.act;
dependencylib.actmodules can be imported as that dependency name itself.
[#2914] - C extension bindings that implement functions taking or returning Acton
numeric primitive types may need to follow the new unboxed C signatures for
bounded integers and floats instead of passing boxed wrapper pointers. [#2824]