Skip to content

Releases: dj-lumiere/RazorForge

RazorForge v0.4.0

RazorForge v0.4.0 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 28 Aug 11:30

RazorForge v0.4.0

The realm release. v0.2.0 shipped the execution model, v0.3.0 the communication layer. v0.4.0 is a
language-maturity release built around one idea that turned out to connect everything else:
realms. A realm is the namespace a name lives in — RF:: for RazorForge, C:: for foreign C,
LLVM:: for intrinsics, and SF:: for the new sister language Suflae. Foreign calls, compiler
intrinsics, and cross-language interop all became one mechanism instead of three ad-hoc ones.

On top of that foundation this release lands a reworked ownership & copy model, a compile-time
reflection surface
, a matured C FFI (struct-by-value including float structs, platform-width C
types, conditional compilation), and native SIMD vectors. Suflae ships as its own v0.1 preview
release alongside this one — see the Suflae release notes.

🌐 Realms

Foreign and intrinsic calls are now realm-qualified rather than string-tagged.

  • routine C::malloc(...) / routine LLVM::... replace the old external("C"|"llvm") form. A
    realm is part of a name's identity, not a comment on it — so a foreign name resolves, mangles, and
    type-checks like any other.
  • Realm-qualified references in expression and type position — RF::Core.List, C::size_t — so a
    program can name a type or routine from a specific world when it matters (mixed RF/SF, FFI boundaries).
  • Strict-realm checking at foreign call sites (RF-S460): crossing a realm boundary is deliberate,
    never accidental.
  • Realms are the substrate the whole Suflae "world-line" model rides on: SF's List is RF::Core.List,
    wrapped per-instance so it obeys Suflae's sharing semantics without forking the standard library.

🔗 C FFI

The FFI surface matured enough to bind real C libraries.

  • Struct-by-value, including float structs. Aggregates pass and return by value following each
    platform's ABI — per-eightbyte SSE/INTEGER classification (SysV x86-64), HFA (AArch64/AAPCS64), and
    by-size GP-register packing (Windows x64). Struct layout matches the C default, so a Hijacked[record]
    hits the right field offsets (flat and nested).
  • Platform-width C types: CLong / CULong / CWChar (per-target via @target) and CWStr
    (wchar_t*), plus the existing CStr for char*.
  • Conditional compilation: @target(os: "linux", "macos") gates whole files and declarations
    (Go-style build constraints), so per-platform bindings live side by side.
  • Library linking from the manifest: [target] c_libraries / library_paths (clang -l/-L), and a
    @link("lib") annotation on C:: externs, threaded into the link step.
  • Callbacks (bare routine name → C function pointer), opaque pointers, typed pointers (Hijacked[T]),
    and choice → int32 all work and are exercised end-to-end.

🧬 Ownership & copy model

The value/copy rules were reworked into one coherent model.

  • Copyable and Storable are orthogonal. Being copyable (duplicable) and being storable (movable
    into a slot) are separate capabilities on separate axes — record types auto-derive Copyable through
    a needs P everywhere structural gate, while reference-counted handles are storable but not copyable
    (a duplicate is an explicit .share(), never an implicit copy).
  • Three-rules parameter model: a record parameter borrows; a value moved to a destination
    (constructor field, store primitive) transfers; the caller tears down rvalue arguments. Container and
    aggregate element reads copy on keep (var x = a[i] copies), and taking a bare entity out of a
    container without a copy is a compile error rather than a silent alias/double-free.
  • Construction verbs are gone. The three entity→wrapper "constructors" that masqueraded as methods
    (.retain(), .share[P](), .roam()) are abolished — you write Retained(from: steal n) /
    Roamed(from: n), and the definition reads as the type.
  • One canonical RC vocabulary: share (mint a co-owner), hold / unhold (strong count),
    observe / unobserve (weak count), access (read-only coercion), control (deref), destroy
    (which now folds in the old release). Aggregate-steal holes (steal l[i], steal o.field) are
    rejected (RF-S622).

🪞 Buildtime reflection

A first buildtime reflection surface, for serialization, FFI layout, and GPU-vertex-style codegen.

  • Member listings: openmemvarof(T) / allmemvarof(T) enumerate a type's members at build time.
  • Metadata intrinsics: nameof / orderof / typeof / placeof (offset) / sizeof / valueof
    of a member, with full repr-C offset and size folding.
  • The $primary splice injects a reflected member as code (me.$nameof(m) → me.x), and expand m in openmemvarof(T) unrolls a body per member at monomorphization.
  • SoA collections: SplitList / SplitArray store a record's fields as parallel arrays, gated on
    needs T is SplittableType.
  • Buildtime-value const generics (${...}) for sizes and counts.
  • represent / diagnose / serialize are now universal built-ins (every type has them) rather
    than opt-in protocols — the Representable / Diagnosable / Serializable protocols are removed.

🔢 Numerics & SIMD

  • Native SIMD vectors: Vector2D / Vector3D / Vector4D (F32 lanes) with elementwise
    arithmetic, reductions, and geometry helpers, lowered to hardware vector ops.
  • Rational types Q64 / Q32 for exact fractional arithmetic.

✍️ Naming & ceremony

Wide renames that make the surface read as intended. Same programs, clearer words.

  • A generic parameter's identity is its slot, not its name. A user type whose name collides with a
    stdlib generic's parameter (record T vs List[T]) no longer breaks that generic — the whole
    record T / record N / record M collision class is fixed.
  • @migratable → @reshaping (the container-mutation marker driving the each-loop reshaping ban).
  • Field → MemberVariable, Method → MemberRoutine, Copy → Store, Blank → None.
  • Routine names are canonically bare: failability (!) and type arguments ([...]) are structured
    attributes, not part of the name string.

🧹 Under the hood

Internal-only, but load-bearing for correctness and future speed.

  • Carriers lowered to records. try_ / check_ / lookup_ results (Maybe / Result / Lookup)
    are now ordinary record constructions produced by a lowering pass; the codegen special-cases for
    carrier construction and payload layout are deleted (#carrier is 0 across the whole program). Wide
    payloads (U128 / D128 / big records) are stored at full width instead of being pointer-truncated.
  • Monomorphization completes before codegen. Type substitution, RC retain bumps, Roamed locks, and
    roam/free hook wiring all moved out of the code generator into explicit passes — codegen no longer
    substitutes types or encroaches on stdlib layout, and errors instead of silently falling back.
  • Scope teardown is emitted before the terminator (spilling the returned value to a temp), fixing a
    class of temporary-destroy leaks, and trivial destroys are skipped.

✅ Tests

Full suite green — 1,511 unit + analyzer tests and the single-build stdlib harness (188
fixtures
, including the C-FFI, ABI struct-by-value, buildtime-reflection, realm, and Suflae-equivalence
fixtures). CI green on Linux, macOS, and Windows.

🔮 Suflae arrives

Suflae — the sharing-first sister language that hides RazorForge's memory-management surface behind a
biased-refcount Roamed runtime — ships its first preview as a separate sf-v0.1.0 release. The
same compiler binary runs it (suflae hello.sf, or any .sf file). See the Suflae v0.1.0 release notes
for scope.

⚠️ Not yet

Async networking remains unimplemented (async file I/O and subprocess orchestration are the async
I/O surface). Custom FFI calling conventions beyond ccc, @repr(C) / @packed / @align, and
callback-in-struct-field are not yet supported. The persistent-daemon / JIT fast-rebuild path is in
progress and not part of this release.

⬆️ Upgrading from v0.3.0

  • Foreign routines: replace external("C") / external("llvm") with routine C::name(...) /
    routine LLVM::name(...).
  • Construction: replace n.retain() / n.share[P]() / n.roam() with Retained(from: steal n) /
    Shared[T, P](from: steal n) / Roamed(from: n).
  • RC verbs: retain/release → hold/unhold, weak track/watch → observe, the wrapper copy
    verb → share, refer() → access(); a wrapper's release() is now just destroy().
  • Annotations & keywords: @migratable → @reshaping; in your own compiler-adjacent code/prose,
    Field/Method/Copy/Blank → MemberVariable/MemberRoutine/Store/None.
  • Protocols removed: drop obeys Representable / Diagnosable / Serializable — represent /
    diagnose / serialize are universal now.
  • FastSet / FastDict are removed. Use Set / Dict (secure-by-default hashing); they are the
    only hash collections now.

v0.3.0

v0.3.0 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 01 Aug 03:52
e365d2c

RazorForge v0.3.0

The channels release. v0.2.0 shipped the execution model — coroutines, threads, and a single owned
Agent[T] handle over both. v0.3.0 adds the communication layer that was promised next: typed
channels
for streaming values between concurrent work, a SignalCaster condition-variable monitor
for hand-built coordination, and an M:N work-stealing scheduler so coroutines actually run in
parallel across cores.

It also carries a ceremony pass over the language. A marker earns its keystrokes only when the
danger it guards is silent — so ownership transfer stays marked (steal, or your variable vanishes
under you) and memory-unsafety stays marked (dangerous, narrowed this release to only the ops that can
actually corrupt memory or race). Markers that merely restated something already loud or derivable
are gone: the failable ! is now optional (a failure crashes with a message on its own), the wired $
sigil is removed, and the redundant isonly flags operator is dropped.

📡 Channels

Typed streaming conduits between producers and consumers — the piece v0.2.0 explicitly deferred.

  • Sender[T] — the producer end; cloneable (sender.clone()), so many producers can feed one
    channel (fan-in via steal sender.clone()). Send with send!(item:); close() / is_closed()
    manage the lifecycle.
  • Receiver[T] — a single-consumer receiver, directly iterable (obeys Iterable[T]).
  • SharedReceiver[T] — a multi-consumer (MPMC) receiver for worker-pool patterns, also iterable
    and cloneable.
  • ChannelDrain[T] — the emittable iterator (emit) that iteration drains a receiver through.
  • Factories: make_channel[T](capacity:) -> (Sender, Receiver) and
    make_shared_channel[T](capacity:) -> (Sender, SharedReceiver).
  • send! is failable — a closed channel is a marked failure you handle with when / try_, never
    a silent drop. Bounded capacity gives natural backpressure: a full channel parks the producing
    coroutine (or blocks the producing thread) until a consumer makes room.

📶 SignalCaster

A condition-variable monitor with its own internal mutex, for coordination patterns channels don't cover.

  • lock / unlock / wait / wait_within(deadline) / cast_one / cast_all / clone.
  • wait is uncolored — a coroutine parks, a thread blocks, same call site (the same contract as
    retrieve! and waitfor). wait_within adds a timed variant.
  • Predicate-style waits and timeouts are covered end-to-end.

⚙️ M:N work-stealing scheduler

The v0.2.0 scheduler was a per-thread, caller-driven event loop — a coroutine only advanced while its
owning thread was inside an await. v0.3.0 replaces it with a process-global pool of N daemon worker
threads
(N = host cores by default) with per-worker work-stealing, so independent coroutines make
progress on multiple cores at once.

  • Per-worker local deques (owner pushes/pops one end, idle workers steal from the other) plus a
    shared injector queue for off-pool spawns and wakes.
  • RF_WORKERS environment knob pins the worker count (RF_WORKERS=1 for deterministic,
    single-worker execution); otherwise it tracks host cores.
  • A worker-safe park/wake state machine keeps a wake that races a park from ever resuming a
    coroutine on two workers at once, and the deadlock detector is N-aware — it only flags a genuine
    stall once every worker is idle with work outstanding.
  • Coroutines may migrate between workers across a park; single-thread-only access tokens
    (Viewing/Modifying) and bare entities are held to their thread, while the thread-shareable set
    (Atomic/Shared/Watched/Inspecting/Claiming) may cross.

🛡️ Thread-crossing soundness

Passing a value into a threaded or suspended routine now checks at compile time that it is safe
to share across the boundary (RF-S632). A bare, single-owner entity may cross only when moved with
steal (the move is provably exclusive); shared-ownership handles must cross as Shared/Watched.
Single-thread tokens are rejected at the boundary rather than racing at runtime.

✍️ Less ceremony

The language cleanup that ships alongside channels — same programs, fewer required sigils.

  • Constructors read as the type. routine T(from: X) replaces routine T.create(...); you write
    Point(x: 1, y: 2) / S64(from: n) and the definition site matches the call site. The internal
    create name is gone from the surface.
  • The failable ! is optional. A routine's failability is inferred from its body (throw /
    absent), and a bare call to a failable routine propagates the crash on its own — no more threading
    ! through every declaration and call site just to restate that a failure is possible. ! still
    reads fine where you want it explicit (send!, retrieve!), and recovery is unchanged: the
    generated try_ → Maybe[T], check_ → Result[T], and lookup_ → Lookup[T] variants plus
    when are how you handle a failure instead of crashing.
  • The wired $ sigil is removed. Operator hooks and lifecycle methods are plain names (add, eq,
    iter, destroy, …); a method's wired-ness is inferred from the protocol its owner obeys, and
    a + b now requires the type to actually implement the operator's protocol (a missing hook is a
    compile error, not a codegen surprise).
  • dangerous narrowed. The unsafe gate now sits only on operations that genuinely deref raw
    pointers, hand-manage memory (destroy), or touch concurrency-fatal primitives — not on every
    token-passing container routine. danger is also a plain keyword now (was danger!).
  • flags isonly X → flags == X. One redundant keyword removed; the codegen was already identical.

📦 Prefix / package import

  • import A/B now pulls in every submodule under A/B. A single import brings in every module
    whose declared path is a strict descendant (A/B/Sub, A/B/Sub/Deep, …), instead of one import
    line per module. Resolution keys on the declared module path, not the directory layout.
  • Each submodule's leaf stays callable leaf-qualified (XxxApi.start()); a cross-module leaf clash
    surfaces as a compile error (RF-S513), disambiguated by importing the specific module. (Multi-segment
    call-site qualification like Foo/Alpha.greet() is not spellable — / is division in expression
    position.)

🩹 Runtime stability

  • Per-thread coroutine context on the M:N pool. The stackful-coroutine backend's active-context
    pointer is now correctly thread-local under the multi-worker scheduler, fixing an intermittent
    crash that surfaced only once coroutines ran on more than one worker thread.

✅ Tests

Full stdlib end-to-end suite green — 163 fixtures, including the channel_* (backpressure,
fan-in, rendezvous, try-feed, worker-pool, introspection), signalcaster_* (predicate, timeout), and
coro_* scheduler fixtures (migration, parallel, work-steal) — alongside the analyzer and unit suites
(1,475 tests total). CI green on Linux, macOS, and Windows.

⚠️ Not yet

Async networking is still not implemented (async file I/O and subprocess orchestration from v0.2.0
remain the async I/O surface). The Suflae sister language is in progress and not part of this
release.

v0.2.0

v0.2.0 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 30 Jun 10:25

RazorForge v0.2.0

The concurrency release. RazorForge gains a full asynchronous execution model — stackful coroutines, a
cooperative scheduler, OS threads, and a single owned handle that unifies both — plus structured
concurrency, subprocess orchestration, and async file I/O. The design goal throughout: concurrency you
write as straight-line code, with ownership and failure stayed explicit.

🧵 Concurrency model

  • suspended routine and threaded routine. Calling one starts the work and hands back an
    owned Agent[T] — no spawn keyword, the call is the spawn. A suspended routine runs as a
    stackful coroutine on this thread's implicit scheduler; a threaded routine runs on an OS thread.
    One handle type backs both, so a mixed set can be awaited together.
  • agent.retrieve!() — uncolored await. Drives the work and returns its value. Inside a
    scheduler-driven coroutine it parks (siblings keep running); on a plain thread it blocks. No
    function coloring: the same call site works in either context.
  • waitfor(duration) — uncolored timed wait (parks under the scheduler, sleeps on a thread), and
    agent.waitfor(d).retrieve!() for a per-agent timeout.
  • Drop = abandon. An un-retrieved Agent that goes out of scope is cleanly torn down: a parked
    coroutine unwinds its cancellation shadow stack; a running worker thread is joined then discarded.

🪢 Structured concurrency

A List[Agent[T]] is the scope — its ownership already guarantees no child outlives it. The scope
operations are member routines on that list:

  • agents.gather!() — drive all concurrently, return every result in input order, fail-fast.
  • agents.race!() — drive all, return the first finisher's value; losers are abandoned.
  • agents.cancel_all!() — request cooperative cancellation of every agent, then wait out the
    wind-down. Cancellation is request-only and never frees (teardown stays at scope exit); an agent
    observes it via cancellation_requested() (the only way to halt a worker thread, which cannot be
    killed) or via an interruptible waitfor.

⚙️ Runtime

  • Stackful coroutines backed by native fibers on Windows (CreateFiberEx) and libco elsewhere, so
    a coroutine can suspend from any call depth — including deep C-runtime calls like fopen.
  • Cooperative scheduler (ready FIFO + timer list) with cross-thread wake, the bridge that lets
    a coroutine await a worker thread without blocking and lets parked work resume from any thread.
  • Demand-committed coroutine stacks — reserve large, commit on touch — so very many coroutines
    coexist (≈14 KB resident per live coroutine, not 1 MiB); allocation failure raises a diagnosed
    OutOfMemoryError instead of crashing.

🔌 Subprocess & async I/O

  • run_process(command) -> ProcessResult — run an external program (shell), capturing stdout,
    stderr, exit code, and signal, while parking the calling coroutine. Orchestrate programs
    concurrently with gather!.
  • Uncolored file I/O — read_text(path) and write_text(path, content) carry no _async
    variant and no function color. Inside a suspended routine the calling coroutine parks while a
    vendored libuv loop on its own thread does the blocking transfer (siblings keep progressing);
    outside one it runs inline. Same call site, either context — the same contract as retrieve! and
    waitfor. For whole-file work prefer these to opening a FileHandle and calling the blocking
    read_all inside a coroutine.

🛠️ Language & compiler

  • Member routines on specialized generic receivers — e.g. routine List[Agent[V]].gather!(),
    where me is typed as the specialized receiver. This is what lets the structured-concurrency
    operations live directly on List[Agent[T]].
  • Named-argument evaluation order fixed — named arguments are bound to parameters by name (not
    source order) across every call path; order-independent named calls now evaluate and bind correctly.

⚠️ Not yet

Honesty about scope: channels (typed streaming conduits) are designed but land in v0.3.0, and
async networking is not yet implemented. v0.2.0 is the execution model; streaming/communication
come next.

✅ Tests

Full stdlib end-to-end suite green — 146 fixtures across coroutines, threads, Agent/race!/
gather!/cancel_all!, subprocess, and async I/O — alongside the analyzer and unit suites (1615
tests total). CI green on Linux, macOS, and Windows.

v0.1.1

v0.1.1 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 30 Jun 10:18
d87917a

RazorForge v0.1.1

An optimization-and-cleanup release: faster decimal transcendentals (results unchanged, bit-for-bit)
plus a new DPD decimal interchange surface and dead-code removal.

⚡ Faster decimal transcendentals

Every speedup below is byte-identical to the previous result — validated by exact-match sweeps
against the full-precision path plus round-trip checks, and locked in by committed fixtures. The wins
come from sizing each decimal type's work to the precision it actually needs rather than the shared
softfloat engine's full width: D64 needs ~54 bits but runs on the 113-bit F128 engine; D128 needs 113
bits but runs on the 237-bit F256 engine.

Measured on an Intel Core i9-14900HX (release-time / LLVM -O3, 20k-iteration microbench).
Numbers are machine-specific and not comparable to the collection benchmarks in
internal-wiki/bench_results.md (different machine).

routine before after speedup
D64 pow 2614 ns 1324 ns ~2.0×
D64 erf 10198 ns 5175 ns ~2.0×
D64 erfc 9943 ns 5199 ns ~1.9×
D64 tgamma 6841 ns 4451 ns ~1.5×
D64 atan 3191 ns 2427 ns ~1.3×
D64 asin 3850 ns 3157 ns ~1.2×
D128 erf 16308 ns 11176 ns ~1.5×
D128 erfc 16385 ns 11014 ns ~1.5×

acos/atan2 improve alongside asin/atan (shared core). pow now evaluates exp(you·ln me) at
F128 width instead of octuple; erf/erfc run the continued fraction at F128 width with the level
count sized to the target; tgamma, asin/atan reduce their Stirling / half-angle / Horner degrees
to the target precision. D32 already delegates to native libm and is unchanged.

✨ DPD decimal interchange

  • to_dpd() / from_dpd(bits:) on D32/D64/D128 (→ U32/U64/U128). RazorForge stores
    decimals in BID (to_bits/from_bits); these convert to and from the IEEE 754-2008
    densely-packed-decimal interchange encoding, for interoperating with DPD-based systems (IBM
    POWER hardware decimals, decNumber, DPD wire/file formats).
  • Validated against the canonical IEEE/Speleotrove encodings (e.g. decimal64 −7.50 =
    0xA2300000000003D0) and full round-trip across cohorts, signs, exponents, and Inf/NaN.
  • Codec tables live in Standard/RazorForge/Core/Numerics/DPDTables.rf, generated by
    scripts/gen_dpd.py directly from the canonical Cowlishaw BCD↔DPD equations — self-contained, no
    external dependency.

🧹 Cleanup

  • Removed dead references to the rf_d32_to_f64 / rf_f64_to_d32 FFI (a decNumber-layout shim) now
    that decNumber is no longer in the tree.

✅ Tests

  • Added D64/D128 transcendental correctness fixtures (tcheck_d64_transcendentals extended,
    tcheck_d128_transcendentals new) and a DPD encode/round-trip fixture (dpd_api). Full stdlib
    end-to-end suite green (130 fixtures).

v0.1.0

v0.1.0 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 23 Jun 08:16

RazorForge v0.1.0

The first release with threaded routines and a ground-up overhaul of the numeric stack. This is
the largest release since the initial alphas (134 commits since v0.0.4-alpha).

✨ Highlights

  • Threaded routines (Phase 1–3). Concurrency with safety enforced at compile time: a
    readers-writers conflict checker (RF-S630) that tracks controller identity across .share()
    aliases, a thread-argument shareability gate (RF-S632), and a re-entrant exclusive-lock guard that
    fails fast instead of deadlocking.
  • Numerics rebuilt. Dropped the decNumber/TLFloat dependencies in favor of an in-house
    softfloat engine plus libbf. Checked arithmetic is failable by default — overflow, divide-by-zero,
    and domain errors are now typed and must be handled, so numeric failure is explicit at the call
    site rather than silent.
  • IEEE 754 surface. total_order/total_order_mag, signaling vs quiet NaN, nextUp/nextDown,
    decimal normalize, and domain-vs-overflow separation in division across the floating-point and
    decimal types.
  • Wider numeric API. Decimal (full IEEE surface + 70-digit text parse), Integer
    (modpow/divmod/bit ops), Real (rounding + selection via libbf), and Complex/Cxx
    (log2/reciprocal/to_polar).
  • Faster softfloat (measured, x86-64): F128 multiply 14.9 → 10.1 ns, D64 true-division
    119.6 → 39 ns (~3×), and sqrt ~3.3× faster — speedups that carry into the transcendentals built
    on them. F16 now works correctly on x86-64.

🛡️ Correctness & robustness

  • Fixed several double-free and memory-leak paths in record-copy lowering, synthesized destructors,
    and owned-temporary teardown.
  • Codegen now emits only reachable routines, backed by an over-prune tripwire and a declare/define
    signature-match invariant.
  • Deterministic, OS-independent method resolution (fixes Linux/macOS-only resolution differences).
  • Bare member access (x.name) now reads a field and no longer silently auto-calls a zero-arg method
    — write x.name() to call.

📦 Packaging

  • Tests moved to a dedicated project; the shipped compiler no longer carries test code or
    test/tooling assemblies, slimming the distribution.

⚠️ Upgrading from v0.0.4-alpha (source-breaking)

Code written against v0.0.4-alpha may need these adjustments:

  1. Checked numeric ops are now failable (!). Checked fixed-width arithmetic, division, and the
    overflow/domain-prone methods (e.g. divmod, modpow, logb/quantize/scaleb, tgamma) now
    throw typed errors (NumericOverflow / DivisionByZero / NumericDomain). Callers must be
    failable themselves, handle with when, or call the generated try_/check_ variants.
    Arbitrary-precision Integer add/sub/mul stay non-failable (they cannot overflow); their division
    family does not.

    # before
    var q = a.divmod(other: b)
    # after — handle the failure
    var q = a.divmod!(other: b)        # in a failable routine
    var q = a.try_divmod(other: b)     # or recover explicitly
    
  2. Bare member access no longer auto-calls a zero-arg method (RF-S450). x.name reads a member
    variable; call methods explicitly with ().

    var p = cstr.ptr      # before (silently called ptr())
    var p = cstr.ptr()    # after
    
  3. Error type rename: ValueError / ParseError → InvalidValueError. Update when arms and
    any explicit references.

  4. Decimal.$sub is now $sub! (subtraction can overflow). Decimal subtraction in non-failable
    contexts must be handled.

  5. New concurrency static checks may reject previously-accepted code: RXW conflicts (RF-S630),
    unshareable thread arguments (RF-S632). These reject data-racing patterns that compiled before but
    were unsafe; re-entrant exclusive-lock acquisition now crashes (ReentrantLockError) rather than
    deadlocking. The fix is usually to share through Atomic/Shared/Watched or restructure the
    using scopes.

✅ Platforms

Verified in CI on Linux x86-64, Windows x86-64, and macOS arm64 (Apple Silicon).

v0.0.4-alpha

v0.0.4-alpha Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 13 Jun 16:59
c7c28e8

What's new in v0.0.4-alpha

This release makes value types mutate in place. A method on a storage-backed
record now receives its me receiver by reference, so it can change the
caller's storage instead of a throwaway copy. It's a small surface change with a
large consequence: it's the foundation the upcoming concurrency tier (lock-free
atomics) and C FFI out-parameters are built on.

Language

  • Storage-backed record methods now take me by reference. A method on a
    value-type record can mutate its fields and the change is visible to the caller.
    Previously the receiver arrived as a hidden copy, so me.field = … (and
    me.field.hijack()) operated on a temporary that was discarded the moment the
    method returned — the mutation silently vanished. Now those operations reach the
    caller's real storage, so in-place mutation works. This applies to every
    storage-backed record:

    • struct records (records with no @llvm backend type), and
    • inline aggregate records — Array[T, N] and BitArray[N], whose backend
      is an inline [N x T] buffer.

    The rule is purely type-level — there is no per-method or per-name special case.
    Array.$setitem! is by-reference because Array is storage-backed, exactly like
    every other Array method.

    • Behavioral note: any code that relied on the old "mutation is a no-op"
      behavior will now see the mutation take effect. In practice that pattern was a
      latent bug (a value-type method that appeared to mutate but didn't).
  • Nested and indexed in-place mutation now works. Assigning through a field or
    element chain mutates the target directly:

    • me.inner.field = … and me.inner.field += 1 (nested struct fields)
    • arr[i] = x and a.b[i] = x (index assignment, including through a field)

    Array / BitArray index assignment writes the single element in place — it no
    longer rebuilds the whole buffer on every write.

  • @llvm-primitive (scalar) records are unchanged. The numeric types
    (S8…U128, F16…F128, the decimals and complexes), Bool, Hijacked,
    Retained, and friends keep value-passing semantics — their value is the
    machine register their operators feed to intrinsics, so they were never affected
    by the copy problem and are untouched here. (Such types are pure values and never
    mutate me in place, so "needs by-value" and "mutates in place" never overlap.)
    Use with when you want a fresh, independent copy of a value record.

Compiler fixes

  • Address-of an rvalue receiver now works. Taking the address of (or calling a
    by-reference method on) a temporary — a call result, a constructor expression, a
    literal — now spills the value to a temporary and uses its address, instead of
    failing with "cannot take address of expression".
  • Address-of through a crashable's fields is now supported (it previously
    rejected crashable parents), so returning or copying a Text/record field of a
    crashable from crash_message/$diagnose works.
  • Address-of on inline aggregate records (Array/BitArray) now generates
    valid IR for the universal get_address/hijack machinery, where it previously
    emitted an illegal [N x T] → ptr cast.

Internal

  • The dead set_element_at intrinsic — superseded by the new in-place element
    store path — was removed.
  • Storing an owned value through the in-place element store is now correctly
    treated as an ownership move, so the stored value is no longer torn down twice.

Groundwork

  • This is the prerequisite for two v0.1.0 features: value-representation atomics
    (AtomicS64 is one machine word like S64, differing only in its atomic-only
    API) and C FFI out-parameters (passing a struct's address to a C function).
    Both require a method to reach its receiver's real storage, which is exactly what
    by-reference me provides.

The change is locked by the full end-to-end fixture suite and the analyzer test
suite (1382 tests), all green, with every generated module verified by LLVM.

v0.0.3-alpha

v0.0.3-alpha Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 12 Jun 15:47
2899c5e

What's new in v0.0.3-alpha

A bugfix release that closes three correctness gaps: a parser misread of
subjectless when, a memory leak on entity-variable reassignment, and protocol
conformance that ignored category membership.

Compiler fixes

  • Subjectless when arms no longer misparse as lambdas. A when with no
    subject whose arm condition started with an identifier (e.g. x > 0) could
    be mistaken for a lambda parameter list, producing a spurious syntax error.
    Arm conditions now parse correctly.
  • Reassigning a plain entity variable no longer leaks. Assigning a new
    value to an entity-typed var now destroys the previous contents first,
    matching the teardown that already ran at end of scope. Branch- and
    loop-driven reassignment no longer leaks the old value.

Language

  • Records satisfy category protocols by membership. A record now conforms
    to category protocols such as RecordType purely by being a record — no
    explicit obeys clause required. Conformance is decided by category
    membership rather than only by declared protocol lists.

All three fixes are locked by end-to-end fixtures that run green on every
commit.


RazorForge is a natively compiled, statically typed language built around
single-ownership memory management (no borrow checker, no GC) and
compiler-generated error handling. This is an early alpha: the compiler,
runtime, and standard library work — 1,400+ tests and 90+ end-to-end fixtures
run green on every commit — but APIs will change and you will find bugs.

Install & hello world

Each package is self-contained: compiler, standard library, native runtime,
and the LLVM 22 toolchain (clang/opt/lld) are all inside — no separate LLVM
install needed.

  1. Download the package for your platform below and extract it.

  2. Run the installer to put razorforge (and rf) on your PATH:

    • Windows: install.cmd
    • Linux / macOS: ./install.sh
  3. Write hello.rf:

    module Hello
    
    import IO/Console
    
    routine start()
      show("Hello from RazorForge!")
      return
    
  4. razorforge buildandrun hello.rf

See QUICKSTART.md inside the package for projects, razorforge.toml, and the
full CLI reference.

Platform notes

Package Needs from the system
win-x64 Nothing — fully self-contained (mingw-based linking)
linux-x64 glibc development files for linking (apt install libc6-dev / dnf install glibc-devel)
osx-arm64 Apple Command Line Tools for linker stubs (xcode-select --install)

macOS Gatekeeper: this alpha is not notarized by Apple, so browser
downloads are quarantined and macOS will refuse to load the bundled
libraries ("libhostfxr.dylib cannot be opened because the developer cannot
be verified"). ./install.sh fixes this automatically — it clears the
quarantine attribute and ad-hoc re-signs the bundled binaries (quarantine
removal alone isn't always enough: Apple Silicon requires valid signatures
and newer macOS caches Gatekeeper verdicts). Manual equivalent, run once
inside the extracted folder:
xattr -dr com.apple.quarantine . && find . -type f \( -name '*.dylib' -o -perm -u+x \) -exec codesign --force --sign - {} \;
Downloading with curl -LO avoids the quarantine flag entirely.

Checksums for every artifact are attached as checksums-<platform>.txt.

Docs & feedback

Language guide: https://razorforge.lumi-dev.xyz/ ·
Issues and design feedback: https://github.com/dj-lumiere/razorforge-suflae/issues

v0.0.2-alpha

v0.0.2-alpha Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 12 Jun 06:57
35d18fc

What's new in v0.0.2-alpha

This release is about decimal precision you can trust — every decimal type
now has a complete, correctly-rounded transcendental surface — plus a rework of
deferred initialization.

Numerics

  • F128 is now backed by TLFloat (correctly rounded quad-precision
    software float). LLVM's fp128 type is gone from emitted IR entirely, which
    removes the platform-specific compiler-rt shims (notably on macOS).
    Behavioral fixes that come with it: subnormals are computed correctly (they
    were flushed before), F128→F64 conversion rounds (it truncated), and
    large-integer→F128 conversion rounds to nearest.
  • D32/D64/D128 gain the full transcendental surface — sin cos
    tan asin acos atan atan2, the hyperbolics and their inverses,
    exp exp2 expm1 log log2 log10 log1p, pow/$pow!, cbrt,
    hypot. Results are correctly rounded to the type's last digit and
    byte-identical on every platform
    , via tiered routing through the
    next-size-up TLFloat binary format (D32 → binary64, D64 → quad, D128 →
    octuple).
  • Arbitrary-precision Decimal trig now actually exists. sin … tanh,
    atan2, Decimal.pi(precision), and Decimal.e(precision) were declared
    in the stdlib but had no implementation — any call failed at link. They are
    now LibBF-backed with working precision that scales with the request
    (default 50 significant digits, up to 1000), so a 1000-digit result is as
    trustworthy as a 50-digit one.
  • Canonical special values everywhere: every float and decimal type now
    prints inf, -inf, and NaN (NaN is always unsigned). Previously the
    spellings differed by family (-nan, Inf, Infinity).

Language

  • Breaking: the uninit keyword is removed. Use lateinit var instead.

  • lateinit reworked — eager allocation, late initialization. Storage is
    allocated at the declaration, so a lateinit entity is valid and borrowable
    immediately (the out-parameter pattern now works instead of crashing), and
    reassignment destroys the previous contents, so branch-initialization no
    longer leaks.

  • Typed suffixes now work on integer-form literals. 1f64, 1d32, 1dn,
    1j, 1jn, … all tokenize — a literal no longer needs a decimal point just
    to carry a float/decimal/imaginary suffix.

  • Complex values print as a+bj, matching the imaginary literal suffixes,
    so output round-trips as literal syntax. The fixed-width C32/C64/C128
    gain this representation too (they previously printed the memberwise debug
    form), and the arbitrary-precision Complex switches from 1+1i to 1+1j.

Compiler fixes

  • Default arguments on method calls were silently broken. Calling any
    method without an argument that has a declared default (e.g. d.sin(),
    Decimal.pi()) emitted a call with the argument missing — the callee read
    whatever happened to be in the register, producing nondeterministic results
    that varied by platform. Defaults are now materialized at every call site.

All numeric results above are locked by end-to-end fixtures whose expected
outputs were generated from the runtime itself and cross-checked against
reference constants.


RazorForge is a natively compiled, statically typed language built around
single-ownership memory management (no borrow checker, no GC) and
compiler-generated error handling. This is an early alpha: the compiler,
runtime, and standard library work — 1,400+ tests and 90+ end-to-end fixtures
run green on every commit — but APIs will change and you will find bugs.

Install & hello world

Each package is self-contained: compiler, standard library, native runtime,
and the LLVM 22 toolchain (clang/opt/lld) are all inside — no separate LLVM
install needed.

  1. Download the package for your platform below and extract it.

  2. Run the installer to put razorforge (and rf) on your PATH:

    • Windows: install.cmd
    • Linux / macOS: ./install.sh
  3. Write hello.rf:

    module Hello
    
    import IO/Console
    
    routine start()
      show("Hello from RazorForge!")
      return
    
  4. razorforge buildandrun hello.rf

See QUICKSTART.md inside the package for projects, razorforge.toml, and the
full CLI reference.

Platform notes

Package Needs from the system
win-x64 Nothing — fully self-contained (mingw-based linking)
linux-x64 glibc development files for linking (apt install libc6-dev / dnf install glibc-devel)
osx-arm64 Apple Command Line Tools for linker stubs (xcode-select --install)

macOS Gatekeeper: this alpha is not notarized by Apple, so browser
downloads are quarantined and macOS will refuse to load the bundled
libraries ("libhostfxr.dylib cannot be opened because the developer cannot
be verified"). ./install.sh fixes this automatically — it clears the
quarantine attribute and ad-hoc re-signs the bundled binaries (quarantine
removal alone isn't always enough: Apple Silicon requires valid signatures
and newer macOS caches Gatekeeper verdicts). Manual equivalent, run once
inside the extracted folder:
xattr -dr com.apple.quarantine . && find . -type f \( -name '*.dylib' -o -perm -u+x \) -exec codesign --force --sign - {} \;
Downloading with curl -LO avoids the quarantine flag entirely.

Checksums for every artifact are attached as checksums-<platform>.txt.

Docs & feedback

Language guide: https://razorforge.lumi-dev.xyz/ ·
Issues and design feedback: https://github.com/dj-lumiere/razorforge-suflae/issues

v0.0.1-alpha

v0.0.1-alpha Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 11 Jun 15:00

RazorForge is a natively compiled, statically typed language built around
single-ownership memory management (no borrow checker, no GC) and
compiler-generated error handling. This is an early alpha: the compiler,
runtime, and standard library work — 1,400+ tests and 90+ end-to-end fixtures
run green on every commit — but APIs will change and you will find bugs.

Install & hello world

Each package is self-contained: compiler, standard library, native runtime,
and the LLVM 22 toolchain (clang/opt/lld) are all inside — no separate LLVM
install needed.

  1. Download the package for your platform below and extract it.

  2. Run the installer to put razorforge (and rf) on your PATH:

    • Windows: install.cmd
    • Linux / macOS: ./install.sh
  3. Write hello.rf:

    module Hello
    
    import IO/Console
    
    routine start()
      show("Hello from RazorForge!")
      return
    
  4. razorforge buildandrun hello.rf

See QUICKSTART.md inside the package for projects, razorforge.toml, and the
full CLI reference.

Platform notes

Package Needs from the system
win-x64 Nothing — fully self-contained (mingw-based linking)
linux-x64 glibc development files for linking (apt install libc6-dev / dnf install glibc-devel)
osx-arm64 Apple Command Line Tools for linker stubs (xcode-select --install)

Checksums for every artifact are attached as checksums-<platform>.txt.

Docs & feedback

Language guide: https://razorforge.lumi-dev.xyz/ ·
Issues and design feedback: https://github.com/dj-lumiere/razorforge-suflae/issues