Skip to content

Releases: alicelanguage/alice

Alice v1.0.0

Choose a tag to compare

@github-actions github-actions released this 04 Nov 12:04
  • fix: IL backend async methods and async iterators: a finally/ensure around an await or yield ran twice when the
    continuation resumed the machine on another thread before the suspending MoveNext left the try block (its guard
    re-read the state field, which the other thread had set to -1). The guard now reads a MoveNext local set before
    each suspension, as C# does with its cached num. Seen in CI as Il_async_iterator_matches_csharp printing
    "ticks done" three times; pinned deterministically by Il_async_finally_runs_once_when_the_await_resumes_before_the_suspend_returns
    and Il_async_iterator_finally_runs_once_when_the_next_MoveNextAsync_races_the_yield.

  • fix: dataflow graphs on few cores: an idle node no longer spins while other pool work items wait for a thread, and yields
    the core between re-checks instead of Thread.SpinWait. Chains of tiny rings crawled on 1-4 core machines (50k items
    through 15 nodes: 8-15 s per run on 1-2 cores, past the 30 s test bound on CI runners), now about 1 s; no wake was
    lost. Benchmarks/dataflow unchanged (serial and Replicate). Stress: DataflowStressTests (pinned to two cores).

  • tests: Windows CRLF normalization in dump comparisons (ClosureConvertTests, DynSitesTests)

  • tests: Windows file handle leak in CoreFsProcessTests (enumerator disposal)

  • tests: Windows cross-drive path handling in ExecutablesTests (GetRelativePath can return absolute paths)

  • tests: macOS path canonicalization in ProveCliTests (initialize dir from Directory.GetCurrentDirectory() after SetCurrentDirectory to resolve symlinks)

  • tests: allocation test flakes on macOS/Linux (extended warmup in CoreNetAllocationTests to trigger lazy thread-pool initialization)

  • compiler: Windows DLL export declaration (__declspec(dllexport) for @[Native] functions)

  • tests: allocation test flakes on macOS (extended warmup in ParKernelTests to trigger lazy thread-pool initialization)

  • sdk: MSBuild Exec task for alice gen now surfaces compiler output (StandardErrorImportance="high" in Alice.targets)

  • cli: with stdout redirected (MSBuild, CI, pipes) diagnostics are plain text on stderr; the "could not compile" summary names the first error; an alice gen crash is a canonical MSBuild error line and alice build shows the command output when MSBuild reports only MSB3073

  • sdk: Alice.targets runs the alice.dll that drives alice build (AliceRunningCompiler) in the repo layout, falls back to the configuration that exists, and fails with the paths tried when no compiler is found

  • tests: --native-lib without @[Native] functions writes no C file (no clang on any OS)

  • tests: Echo_with_256_connections_allocates_nothing_per_message measures in a child process and asserts 0 B: the echo allocates nothing; the 0.5-2.4 KB it saw came from the test host (about 1.7 KB/s with no server running)

  • fix: alice gen crashed with BadImageFormatException on Windows (web API sample): a type lookup followed a type forward into a reference assembly (Microsoft.Extensions.Logging.Abstractions). Type lookups now skip assemblies the runtime cannot load, and framework reference packs (ASP.NET Core) resolve to the installed shared framework for reflection

  • tests: the allocation tests that read a counter disturbed by other threads (echo, affine tree build, dataflow per item) measure in a child process running nothing else, with a negative check each; all three read exactly 0 B

  • fix: dataflow Graph.Run no longer allocates 24 B when it has to sleep until the graph finishes (its ManualResetEventSlim created a lock object only then); it waits on a monitor allocated with the graph

  • prove: alice prove --check now verifies PROOF.cert certificates when present (without needing --smt flag)

  • IL backend is the default target (ADR 0033); --backend csharp flag, [build] backend = "csharp" in alice.toml, and ALICE_BACKEND=csharp environment variable preserve C# printer access for differential testing and debugging.

  • Checkable proof certificates (ADR 0042): alice prove --smt produces independently verifiable certificates when a proof-emitting SAT solver (CaDiCaL 1.7+) is available. Each certificate contains the rule's symbolic IR (Alice.Check terms), the Tseitin-encoded CNF, and an LRAT proof from the SAT solver. The standalone alice-check tool (<600 lines, no compiler/runtime dependencies) verifies: (1) the LRAT proof refutes the CNF, (2) the CNF matches the re-derived bit-blast of the IR, (3) hashes match. alice prove --check re-validates all certificates. PROOF.cert (Brotli-compressed) accompanies PROOF.al. Trusted computing base for certificate mode: Alice's rule-to-IR translation + alice-check (vs. enumeration: compiler+runtime, --smt without certs: +Z3). docs/proofs.md, alice-check executable, tests/Alice.Check.Tests (46 tests), differential fuzz extended to certificates.

  • perf: Bend 2 runtime suite re-measured on Intel Xeon 8488C, .NET 10.0.114, commit 1746ce4 (benchmarks/bend2/results/summary.md). Serial geomean Alice/C 1.54x (16 workloads), parallel speedup 8.8-21.0x on 32 cores; all checksums match C. bend-vs suite unchanged (Bend not available on this machine).

  • core: Alice's own regex engine (ADR 0036, src/Alice.Runtime/Core/Text/Regex/): Onigmo/Ruby front-end (RegexSyntax to RegexNode + RegexFeatures), Tier R = literal prefilters (memchr/SearchValues, Teddy over AVX-512/AVX2, inner literals, ASCII-caseless) + lazy DFA forward and reverse + Pike VM for captures, Tier B = own memoizing backtracker (look-around, backreferences, atomic/possessive, large repeats); UTF-16 and UTF-8 without transcoding; match-only calls allocate nothing. Regex literals are now Alice.Core.Text.Regex with Ruby semantics (m = dot-all, ^ $ line anchors, ASCII \w, named groups turn off unnamed captures, \1 templates); =~, match, scan, gsub, sub, split run on it and record the last match (Prelude.last_match, lazy). Parity: 100k random patterns against the BCL oracle (Tier R and Tier B) and Tier R against Tier B, 0 divergences (tests/Alice.Tests/Runtime/RegexParityTests.cs). Internal users of System.Text.RegularExpressions (CLI, LSP, REPL, telemetry, compiler passes, expect match, format) run on it too. Forge, pinned: regex-redux 0.83 s on UTF-16 and 0.55 s on UTF-8 vs .NET Compiled 3.98 s and NonBacktracking 4.90 s; rebar-style counts on UTF-8 are faster than Compiled except \p{Lu}\p{Ll}+ (0.87-0.92x), and on UTF-16 four rows are 0.78-0.97x with the causes given (benchmarks/core/regex/results.md). Search paths: end-only Count, a vectorized start-state skip in the lazy DFA, two-offset literal scans (UTF-16 packed to bytes), exact UTF-16 caseless literals by simple case folding (tests/Alice.Tests/Runtime/RegexScanTests.cs).

  • IL optimizer round 2 (IL backend only): loop-carried store-to-load forwarding (Air/Passes/IlOpt/StoreForward.cs,
    switch forward): a load of the element the previous iteration stored reads a register instead of memory, for
    fresh non-escaping arrays with one unconditional store per iteration and proven-in-bounds loads (editdist IL / C#
    1.00 -> 0.75); forced inlining now bounds the size a body expands to with its inline callees (gameoflife 0.96 ->
    0.92). bend2 serial geomean IL / C# 0.96 (0.94 without the arena tree ports), warm and parallel tables in
    benchmarks/bend2/results/il-vs-csharp.md.

  • IL backend optimizer (il-opt; IL backend only, ALICE_IL_OPT switches, e.g. 0 or -inline,-inbounds): R9
    [AggressiveInlining] on small loop-free bodies with at most 3 call sites (never fork dispatchers or __fork_*
    helpers); proven-in-bounds array reads and writes (Air/Passes/IlOpt/InBounds.cs: interval analysis over
    exact-length local arrays, emitted as GetArrayDataReference + Unsafe.Add, ILVerify clean) and overflow checks
    whose exact result interval fits the type removed; peepholes (null tests, constant branches, compare-branch fusion,
    type tests in Roslyn's is T p shape). Wired through main's IL hooks (IlPipeline.AfterLowering / BeforeEncode,
    IlBody.IndexLoadExt / IndexStoreExt, IlBuilder.OptimizeExtra). bend2 serial, IL / C# wall geomean 0.98
    (1.00 without il-opt; raytrace 1.18 -> 0.98, hashmap 1.03 -> 0.98); the arena tree benchmarks stay at 1.02-1.08.
    Table: benchmarks/bend2/results/il-vs-csharp.md; docs/performance.md "IL backend optimizer".

  • CM loop shapes (ADR 0039, P2.3): counted loops that are a scan, segmented scan, combine (histogram / scatter-add),
    compaction or permutation lower to Alice.Runtime.Cm kernels in both backends (Lowering/Lowerer.Cm.cs, AIR nodes
    in Mir/MExpr.Cm.cs, Backend/Printer.Cm.cs, Backend/Il/AirToIl.Cm.cs); inferred only where the result equals the
    serial loop (wrapping/bitwise integer ops, 16K+ iterations, serial fallback otherwise). cm for / cm ordered for
    require a shape (AL0247), float combines need ordered (AL0248), permutations a proof (AL0249). alice explain
    rows; CM idiomatic programs in benchmarks/bend2/idiomatic/cm/, results in benchmarks/bend2/results/cm.md.

  • IL backend wiring (backend plan D1.6/D1.8/D1.9/D1.10, ADR 0033): alice run --backend il / alice emit --il now compile async/iterator/async-iterator methods (own state machines, Air/Passes/StateMachines), Any operations (Alice.Runtime.Dyn call sites, AL0232), records, struct records and sum types (Synthesize), and lib P/Invokes; the three stub tests Il_async/iterator/async_iterator_matches_csharp are real C#-vs-IL differential tests. Fixes found on the way: break inside for v in <IEnumerable> left the try with br, String == String was a reference compare under IL.

  • IL backend declaration kinds (D1.10): generic classes/methods (by monomorphization, incl. generic base classes), delegate types and method-group values, constant-expression enum members (B = 1 << 1, AB = A | B), default interface methods, interface properties, @[Stack] stru...

Read more

Alice v0.14.0

Choose a tag to compare

@github-actions github-actions released this 27 Oct 17:10
  • Telemetry ships disabled in this release: alice never asks, records or sends, and alice telemetry only reports
    that it is disabled. The opt-in machinery below stays in the code behind TelemetryPolicy.Available.

  • Higher-order functions are specialized per lambda literal (ADR 0026): apply(xs) { |x| x * k } calls a clone of
    apply whose yield is a direct call of the block body, with k passed as a parameter. No delegate, no closure
    allocation, and the JIT can inline the body (a HOF-heavy benchmark: about 2.3x faster). ALICE_NO_SPECIALIZE=1
    turns it off.

  • Borrow and lifetime checks (ADR 0027): passing one mutable value twice to a call that writes through one of the
    parameters is AL0217 (copy_into(a, a)); a @[Stack] value captured by a block, stored in a heap type's field
    or put in a collection is AL0218 (was a C# error at emit time); using a mutable value after sending it through a
    Channel/Actor is AL0219; writing a value a running spawn reads before the task is joined is AL0202.

  • Loop interchange and cache tiling (ADR 0025), the loop-tile MIR pass. It applies to 2-3 deep counted nests over
    flat arrays. An affine dependence analysis delinearizes i * n + j and computes direction vectors. Each nest gets
    the legal order with stride-1 innermost access (i-j-k matmul becomes i-k-j). 3-deep nests and transposes are
    tiled when every dependence is non-negative. Tiles: 64 for matmul (half of L2), 32 for transpose (half of L1).
    ALICE_TILE=n overrides the size. The new nest runs behind a once-per-call guard (in bounds at every corner, no
    aliasing between written and read arrays); otherwise the original runs. Results are bit-identical. Checked
    integer arithmetic is not reordered. benchmarks/tiling.sh measures before and after.

  • BEAM-style processes (ADR 0028). Actor(T) is now a supervised, linkable process: a crash kills only that actor,
    link/on_exit propagate exits, and Supervisor.one_for_one / one_for_all restart children in place with
    restart intensity (max restarts per window) and nested trees. Actor(S, T).new(init, handler) keeps isolated
    state built inside the actor. The mailbox is a lock-free queue and the actor is its own thread-pool work item:
    0 bytes per message in steady state, ~6 M msgs/s on one actor and ~11 M msgs/s over 64.
    Isolation is compiler-enforced: non-sendable message types are AL0230, actor blocks capturing mutable locals AL0231.

  • gpu for launches (ADR 0021): explicitly grouped kernels with an occupancy-sized group (a power of two, a multiple
    of the warp size) and a capped grid, and a grid-stride loop. Reductions add in registers, reduce per group (warp
    shuffles, then shared memory) and do one atomic add per group instead of one per iteration: a 10M Int64 sum on an
    Iris Xe (OpenCL) 18 ms -> 4-6 ms. The device accumulator is cached, so a launch allocates no new T[1] or buffer.
    Write-only arrays are not uploaded (only the launch's range comes back), read-only arrays are not copied back, and
    a region skips the upload of an array its first loop overwrites whole.

  • Opt-in CLI telemetry, disabled for now (ADR 0031, docs/telemetry.md): off by default, asked once on an interactive terminal (never in
    CI), and after alice self update only when no answer is stored. alice telemetry on|off|status|show|reset|perf;
    ALICE_TELEMETRY_OPTOUT=1 / DO_NOT_TRACK=1 always win. Command name, versions, OS/arch, exit code, duration,
    sanitized crash stacks and error-code counts; never source, paths or names, and never inside compiled programs. The
    default endpoint is disabled until a server is deployed. tools/telemetry-server is a self-hostable collector
    (ASP.NET Core + SQLite, strict validation, rate limit, 180-day retention, admin-only aggregates, Dockerfile).

  • Stream fusion (ADR 0029): take(n) fuses into map/select/reject chains (xs.map{}.select{}.take(n).sum is one loop, no intermediate arrays); to_a/map after maps and takes write an exact-size array for arrays, s.chars and Int32 ranges; effectful stage blocks are no longer interleaved (an effectful stage before another block, a take or an early-exit sink runs stage by stage, as written); small literal arrays inside an inlined block go to one stack buffer before the loop.

  • Tensor primitives in the runtime (ADR 0030): Tensor<T> (strided, zero-copy views over 64-byte aligned native
    memory), TensorArena, BFloat16 and Half conversions, QuantizedMatrix (int8/int4 blocks of 32 with a Half
    scale, GGML Q8_0/Q4_0 layout) with fused dequantize-dot, MappedFile (memory-mapped spans and tensors, no copy), and
    Nn kernels: dot, GEMV (f32, bf16, Half, int8, int4), tiled parallel GEMM, elementwise, sum/max, fast exp, softmax,
    RMS/layer norm, silu/gelu/tanh/relu. AVX-512/AVX2/Vector128/scalar tiers, a persistent allocation-free worker pool,
    0 bytes allocated per call. Callable from Alice with using Alice.Runtime. benchmarks/tensors: GEMV 4096x4096 f32
    2.0 ms (naive loop 17.7 ms), int8 0.67 ms, int4 0.59 ms on the 12-thread laptop.

  • WebAssembly packages (ADR 0023): kind = "wasm" in alice.toml, alice new <name> --wasm (page, boot script and an
    @[JSExport] example), alice build publishes the static site to dist/wwwroot, and alice run runs it under
    Node. @[JSExport] / @[JSImport] partial def call between Alice and JavaScript. No workload needed.

  • Parallelization soundness fixes (never parallelize unproven code):

    • for loops writing one array in two index shapes (a[i] and a[i * w + j]), or reading a[i] while writing
      a[i * w + j], stay serial; distinct arrays in different shapes get a runtime ReferenceEquals guard. Before,
      iteration 0's row overlapped iteration 1's cell.
    • A call to an instance method of a class with subclasses is pure only when every override is: a subclass's
      field-writing override could run inside a parallel loop or fork.
    • A loop whose body assigns a branch-declared local (t = ... if c, read later) stays serial: the local carries
      across iterations. Before, the parallel helper did not compile.
  • AL0202 catches races it missed: a spawned/parallel block calling a field-writing method on self (spawn { bump });
    index writes keyed by par_each values or Par.Range's hi (only Par.For/Parallel.For's index and
    Par.Range's lo are disjoint, and only when the block does not reassign them); methods that write fields from a
    block, or by calling a field-writing method on self, now count as mutating.

  • Par.Sum forks by index halves (ParReduce) instead of adding partials under a lock: no lock or closure, and the
    same float result on every run.

Archives built from c510b84. Build and package checks: https://github.com/alicelanguage/alice/actions/runs/40546663672

Alice nightly 0.14.1-nightly.20261028

Pre-release

Choose a tag to compare

@github-actions github-actions released this 28 Oct 09:56

Nightly 0.14.1-nightly.20261028 built from b82a902. Not for production. Install: alice toolchain install nightly. Build: https://github.com/alicelanguage/alice/actions/runs/40667086527

Alice v0.13.1

Choose a tag to compare

@github-actions github-actions released this 23 Oct 15:23
  • Errors that come from the generated C# point at the right Alice column: the name they quote (frob, or to_widget
    for .NET's ToWidget), or the line's first token. #line maps lines only, so these carried the C# column.
  • ADR 0022 records alice prove --smt.
  • alice prove --smt calls Z3's C API directly instead of the Microsoft.Z3 package (x64-only): it now runs on
    Apple Silicon and arm64 Linux, and the CLI has one dependency fewer.
  • find is nilable (T?), as the lowering already produced: if big = xs.find { ... } on numbers is a nil check
    instead of a C# error, and null safety applies to its result.

Archives built from b7dc30e. Build and package checks: https://github.com/alicelanguage/alice/actions/runs/39977055637

Alice v0.13.0

Choose a tag to compare

@github-actions github-actions released this 23 Oct 14:42
  • alice prove --smt: opt-in Z3 (system libz3 or ALICE_Z3) proves rules over full x : Int32 / x : Int64 ranges, with overflow, division by zero and floored / % semantics; untranslatable rules fall back to enumeration.
  • Package builds type-check once: alice gen skips its in-process Roslyn check (--no-check) and csc errors are reported as Alice diagnostics. A one-file edit rebuild: 3.7 → 2.2 s. AliceGenCheck=true restores the old check.
  • Version 0.13.0. This release was tracked as 0.12.3 until its scope (FFI, NuGet distribution, GPU, Avalonia, EF Core, source generators...) made a minor version the honest number. A bare alice prove skips nested directories that have their own alice.toml.
  • Integration tests delete their temporary projects, feeds and NuGet caches. A full session had filled a 7.7 GB /tmp RAM disk with 2021 leftover directories, 175 MB per SDK-package run.
  • gpu ... end regions keep the arrays of the gpu for loops inside on the device: each is uploaded once, and written ones are copied back at end. A CPU read inside the region is a compile error. 51 launches over 1M floats: 29 ms in a region against 132 ms with per-launch transfers.
  • gpu for reductions: t += e into an outer Int32/Int64/Float32/Float64 is an atomic add on the device (100k-element Int64 sum, exact on the OpenCL GPU and the CPU accelerator).
  • gpu for i in a...b (ADR 0021): the loop body is compiled to an ILGPU kernel and run on the preferred device. It ran on this machine's OpenCL GPU, with results identical to ILGPU's CPU accelerator, which ALICE_GPU=cpu forces. Admission is the par for independence proof plus GPU rules (arrays of numbers and numbers; arithmetic, indexing, Math.*, conversions; no calls, allocation or reductions), each rejection naming its cause. Kernel bodies are unchecked and not FMA-contracted: the device compiler contracts, and an explicit FusedMultiplyAdd was rejected by the OpenCL driver.
  • GPU kernels in Alice: examples/gpu/ilgpu runs an Alice module function as an ILGPU kernel on the preferred device (it ran on this machine's OpenCL GPU; CUDA works the same, and ILGPU's CPU accelerator is the fallback). Covered by an integration test. To get there: a method reference to a static method lowers to the method group, not a wrapping lambda (C# caches the delegate, and APIs that need the real method get it); C#-style type arguments ArrayView1D<Float32, Stride1D::Dense> are accepted inside generic calls, with >> split; explicit type arguments work on generic extension methods.
  • Avalonia UI: an .axaml window with its code-behind in Alice (examples/ui/avalonia, run headless in an integration test). Fixes on the way: the compiler reflected over reference assemblies (NuGet ref/, a project's obj/.../ref/), whose stripped private members crashed type loading (TypeLoadException on Avalonia's StyledElement), and now reflects over the implementation assemblies, while Roslyn still compiles against the references; src/**/*.axaml are AvaloniaXaml items in the generated project; extension methods inherited by a .NET base class are callable with implicit self (FindControl<TextBlock>("x")); a capitalized generic call without a receiver parses; and a paren-less member of unknown type standing alone as a statement is a call.
  • Type-aware references and rename. The language server binds the project and keeps only occurrences resolving to the symbol under the cursor, or to an override of it in the same class hierarchy: Square#area renames without touching Invoice#area. Unresolved code falls back to name matching.
  • Owned lists and hashes of any element type (List(String), Dictionary(String, Int32)), not only of numbers. Only the collection must stay local, and Clear on release drops element references, so the pool keeps nothing alive.
  • Class discovery: Controller.subclasses, Controller.descendants and ObjectSpace.each_object(typeof(Controller)) { |c| } over the program's own classes, which is what a framework needs to find controllers or plugins. Live-object enumeration is not offered: it has no safe .NET equivalent.
  • Refinements: refine String do ... end inside a module is a monkey patch visible only in files that write using <module>. In C# it is an extension class in namespace __Refine_<module>, imported only by those files, so the scope holds there too.
  • Forks of three calls in one expression (trib(n - 1) + trib(n - 2) + trib(n - 3)) via a three-way Par.Fork: trib(32) 335 -> 43 ms on 12 cores. A failing branch's exception surfaces once every branch has finished.
  • Fix: an assignment inside a branch of a block now assigns the enclosing method's variable, as in Ruby. Before, h.each { |k, v| best = v if v > best } declared a new block-local best, and the method's best silently stayed 0. Found while checking the owned-hash benchmark's checksum, which was 0 under the old compiler too.
  • Owned hashes (ADR 0016 amendment): a local Dictionary(K, V) of numbers that never escapes is pooled and its buckets reused (benchmarks/gc/hashes.al: 137-146 -> 97-104 ms, 2239 -> 0 MiB, 374 -> 0 gen0 collections). Keys/Values views count as escapes. h.each { |k, v| ... } on a Dictionary is inlined to foreach (var (k, v) in h), with no closure or delegate.
  • Owned lists (ADR 0016 amendment): a local List(T) of numbers that never escapes is rented from a per-thread pool and released at its last use, keeping its grown backing array. A scratch list built per call (benchmarks/gc/lists.al, 200k calls) took 59 ms, allocated 2926 MiB and ran 489 gen0 collections; now it takes 31 ms with 0 MiB and 0 collections. Views, spans, enumerators, storing and capturing keep a list on the GC, and a returned list moves out unreleased.
  • View models for XAML UIs: event PropertyChanged : PropertyChangedEventHandler (capitalized event names, required to implement .NET interface events) and Ruby's private @count beside a public def count (the field takes another C# name instead of colliding). An INotifyPropertyChanged + ObservableCollection view model is tested.
  • Language server: find references and rename. Matches come from the lexer, so strings and comments are left alone but #{...} holes are found. Top-level names are renamed across the project and locals within their enclosing def; an invalid new name is refused.
  • Incremental package builds: alice gen runs only when a source, the reference or analyzer list, the compiler or the project changed (MSBuild Inputs/Outputs on AliceGenerate, with a sources list so that added and deleted files rebuild). A no-op alice build takes 1.0 s instead of 2.35 s. Covered by an integration test for no-op, added-file and deleted-file builds.
  • partial def (no body): a declaration a source generator completes, for [GeneratedRegex] (regex compiled at build time) and [LibraryImport] (.NET's source-generated P/Invoke) next to Alice's own lib/fun.
  • docs/production.md: what is ready for production, the test behind each claim, recommended alice.toml settings per workload, and the known gaps.
  • F# consumes Alice libraries built with naming = "dotnet": Geometry.Distance, IsEmpty, records, and sum types matched with :? Shape.Circle as c -> c.R (Integration/FSharpInteropTests, a real .fsproj).
  • Roslyn source generators work on Alice code ([JsonSerializable] contexts for reflection-free, NativeAOT-safe JSON; [GeneratedRegex]; [LoggerMessage]; NuGet generators). alice gen's type check now runs the generators MSBuild supplies (@(Analyzer)) instead of failing on members they add, and single-file builds find the framework's generators in the targeting pack. Framework generators load only when their attribute appears: hello world compiles in 1.06 s as before, not the 1.55 s it took with every generator loaded.
  • Multi-line blocks inside call parentheses parse: Task.Run(->() { followed by statements on separate lines. The lexer dropped every newline inside (/[, so statements in a nested { } ran together. It now tracks the innermost bracket.
  • FFI stress tests (FfiStressTests): strings at the 85/86-char stack-buffer boundary with 2- and 4-byte UTF-8, lone surrogates (encoded as U+FFFD), 8 threads x 20k concurrent string calls, repeated callback exceptions, and empty and 16 MiB pinned arrays.
  • Entity Framework Core works from Alice. examples/data/efcore defines a DbContext subclass with an OnConfiguring override and DbSet properties, and runs LINQ queries that EF translates to SQL from the Alice lambdas (WHERE "b"."rating" > 3). It is covered by an integration test. To get there: extension methods from any referenced assembly are callable without parentheses (query.ToQueryString), not only LINQ's; obj.tap { |o| ... } and obj.then { |o| ... } were added; and a multi-statement block passed where an Action is expected drops its tail value.
  • x.try &.m(args) (Crystal): the safe call x&.m(args), no longer "planned".
  • Lambdas as blocks: xs.sum(&->sq), xs.select(&->(x : Int32) { x.even? }) and xs.map(&f). A lambda literal or method reference passed this way is inlined into the loop like a literal block, with no delegate.
  • Named tuples: {name: "ada", age: 36} and the type {min: Int32, max: Int32} are C# named tuples ((string name, int age), no allocation), read with t.name or t[:name], where an unknown element is a compile error. Hashes keep =>.
  • Assignment as a condition: if user = find(id) and while line = reader.ReadLine. The while form assigns before every test, and elsif x = f evaluates f only when the earlier conditions failed.
  • Flow narrowing for nullable numbers: inside if n and after a guard clause (return unless n, break/next unless n, raise ... unless n), an Int32? local reads as Int32 (n.Value). Narrowing stops where the branch reassigns the local. C# already narrows nullable references.
  • Co...
Read more

Alice v0.12.2

Choose a tag to compare

@github-actions github-actions released this 22 Oct 16:17

Alice 0.12.2: explicit contracts (pure, safe, par, own and borrow parameters, scope), rules and proofs (RULES.al, alice prove, PROOF.al), the built-in guide for people and AI agents, and faster objects, interop and start-up. Update with alice self update.

Highlights

  • Say what you want, the compiler proves it: pure def, safe def, inline def, par a, b = f(x), g(y), own/borrow parameters and scope ... end lifetime regions. A failed proof is a compile error (AL0210-AL0214) instead of silently slower code.
  • Rules and proofs: rule name(x in -1000..1000) ... end in RULES.al; alice prove checks every case and writes PROOF.al; alice prove --check fails in CI when a rule breaks or the code changed. Alice proves its own numeric semantics this way.
  • Guide and AI agents: alice guide [performance|proofs|interop], alice agents [--write], and alice new now writes AGENTS.md plus a CLAUDE.md that imports it.
  • Faster without code changes: fib(n - 1) + fib(n - 2) forks directly; returned arrays are recycled by the caller; immutable constant objects are shared (binary trees: half the allocation of C#); Bool C calls 2.7x faster; alice run caches compiled programs (1.2 s to 0.13 s for an unchanged file).
  • See what happened: alice explain file.al lists the optimizations each method received.

Changelog

  • Explicit, checked paradigms on top of inference (ADR 0017):
    • Effects: pure def (AL0210) and safe def (secure execution, no ambient authority, AL0212).
    • Parallel let: par a, b = f(x), g(y) (Bend's fork without the recursion rule, AL0211).
    • Inlining: inline def.
  • Ownership:
    • own parameters (the caller moves in).
    • borrow parameters (checked, AL0213).
    • scope ... end lifetime regions (arrays must be owned and are released at end).
    • In-place xs = xs.map { } for owned arrays. A 3-stage map over 1M Float64: 4.0 ms and 3 B per call, against 9.3 ms and 32 MB for hand-written C# loops.
  • Owned results (ADR 0016 amendment): an array returned by a method that returns only its own owned local is owned by the caller and recycled. benchmarks/gc scratch: 3.81 MiB per run down to under 400 B, 0 collections.
  • alice run file.al caches the compiled image by content hash (~/.cache/alice/run, ALICE_NO_CACHE=1 to disable): an unchanged program starts in 0.13 s instead of 1.2 s.
  • Shared immutable objects: T.new(constants) of an immutable class whose identity the program never observes is one static instance. benchmarks/gc trees: 68 to 34 MiB per run, half the collections, about 1.35x faster than C# (21.5 vs 28.9 ms).
  • Interop: fun signatures over numbers and Bool are blittable (a Bool crosses as one byte behind an inlined wrapper), with no marshalling stub; a Bool C call is 2.7x faster. @[Fast] on a Bool function no longer fails at run time. Imports use ExactSpelling.
  • Rules and proofs: rule name(x in lo..hi, b : Bool) ... end (RULES.al) declares a pure property over a finite domain. alice prove checks every case and writes PROOF.al, a certificate tied to a hash of the sources. alice prove --check fails when a rule fails or PROOF.al is stale. Domains over 2^32 cases are AL0214. The repository's RULES.al proves Alice's numeric semantics; dotnet test checks it.
  • divmod, tdiv, remainder and modulo count as pure.
  • alice explain file.al: per method, the optimizations applied (fork/join, parallel loops, owned arrays, in-place maps, shared objects, stack arrays, SIMD, fast native calls).
  • alice guide [performance|proofs|interop] prints the guide from guide/, embedded in the CLI, for people and AI agents; every alice code block in it is compiled by the tests. alice agents [--write] prints or merges an Alice block into a project's AGENTS.md: workflow, Ruby/Crystal habits that are wrong in Alice, error codes. alice new writes AGENTS.md with that block and a CLAUDE.md that imports it (@AGENTS.md).
  • Tests that run programs in ExplicitParadigmTests join the console collection.
  • Fork/join of independent redexes (HVM-style): a statement whose value is a pure arithmetic term with exactly two calls to pure user methods, one recursive, forks the two calls without named results (fib(n - 1) + fib(n - 2), 1 + l.count + r.count). Calls under &&, ||, ?? or ?: are never forked, and a fork never captures a nullable local, because the helper would lose flow narrowing. Naive fib(38) on 12 cores: 250 ms -> 145 ms.

Archives built from 35f2433. Build and package checks: https://github.com/alicelanguage/alice/actions/runs/39811840368

Full Changelog: v0.12.1...v0.12.2

Alice v0.12.1

Choose a tag to compare

@github-actions github-actions released this 22 Oct 14:31

Alice 0.12.1: faster loops over owned arrays and a new GC benchmark. Update with alice self update.

Changelog

  • Loops over an owned array bounded by its fixed length (for k in 0...n over Array(T).new(n)) compare against buf.Length, so the JIT removes their bounds checks as it does for C#'s new T[n] (ADR 0016 amendment).
  • README: mutability, ownership, fork/join and benchmark sections.
  • benchmarks/gc: allocation-heavy workloads (rolling rows, per-call scratch buffers, 4 MiB frames, object trees) in Alice with inferred ownership, Alice with explicit own, and C#, under workstation and server GC. Reports time, allocated bytes, collections, GC pause and peak memory, with charts. Alice runs rows and frames with 0 collections (C#: 74-1002) and is about 2x faster than C# on scratch and frames under server GC.

Full Changelog: v0.12.0...v0.12.1

Alice v0.12.0

Choose a tag to compare

@github-actions github-actions released this 22 Oct 13:29
5bb7f90

Alice 0.12.0 makes numbers exact (floored division, checked overflow), adds a MIR optimizer, ownership for GC-free local buffers, a lean fork/join scheduler, and new syntax: own, par for, unchecked ... end.

Upgrading from 0.11: see What changed in 0.12. Install: curl -fsSL https://alice-lang.org/install.sh | sh or alice self update.

Changelog

  • Breaking (ADR 0013): signed / and % are floored like divmod (-7 / 2 == -4); tdiv/remainder truncate. Float % follows the divisor.

  • Breaking (ADR 0013): integer overflow raises OverflowException; &+ &- &* wrap. 2 ** -1 raises instead of returning 1.

  • Breaking (ADR 0013): to_u8 and the other sized conversions raise when out of range (NaN too); to_X! wraps. 2.5.round is 3.0 (half away from zero).

  • Breaking: floats print with a decimal point and Ruby exponent rules everywhere (2.0, 1.0e-07, 1.0e+16).

  • Generated programs default to workstation GC (faster startup, lower RSS); [runtime] gc = "server" | "low-memory" in alice.toml.

  • Startup: the format regex is source-generated (no Reflection.Emit on every launch); an if/case with a raise branch no longer becomes dynamic (the C# runtime binder was loading at startup).

  • Fix (-5).abs and -7.5.abs returning negative values (the C# emitter dropped the parentheses).

  • Fix round(n)/floor(n)/ceil(n) on decimal fractions (0.29.floor(2) was 0.28).

  • [a, b, c].min/.max on a literal lower to Math.Min/Math.Max with no array allocation (editdist inner loop).

  • Compiler: MIR pass pipeline (ADR 0014) in src/Alice.Compiler/Optimizer: constant folding, strength reduction (x / 8 is x >> 3, x % 16 is x & 15 with floored semantics), literal [a, b].min/max without allocation, unchecked counted-loop steps. ALICE_DISABLE_PASSES for A/B runs.

  • Compiler: fork/join of independent pure recursive calls (ADR 0015): a = f(x) / b = f(y) run in parallel with lazy task creation (only the top log2(cores)+3 levels spawn tasks). Writes into arrays allocated in the method keep it pure. Suite tree-radix at 1M elements: 3226 ms → 319 ms (C# 665 ms).

  • Breaking: Channel(T).new is unbuffered (a rendezvous, as in Crystal): send waits for a receiver. It was silently unbounded. Channel(T).new(n) buffers n. The channel is a lock-guarded ring (no Task per blocking call, no allocation per message); send after close raises ClosedChannelError.

  • Row flattening: an array of fixed-width literal rows filled by a full for i in 0...n loop and never escaping (points[i] = [x, y]) is one flat array points[i * k + j] with the column still bounds-checked (suite kmeans at 1M points: 0.44 s / 95 MB → 0.38 s / 63 MB).

  • Fix Array(T).new(n) { |i| ... } ignoring its block.

  • Heap-free small arrays: a local array of literal size (<= 256 unmanaged elements, or a literal of <= 32 items) that never escapes lives in one stackalloc buffer at method entry, also when declared inside a loop.

  • Fix Array(T).new(n, v) ignoring v (it returned zeros); a non-default fill uses an uninitialized allocation plus one Fill.

  • Range analysis: counted loops bounded by .Length/.Count and their i ± k offsets drop provably dead overflow checks (editdist: 998 → 884 ms, C# 841 ms).

  • Stdlib fork/join: sort of a large primitive array is a parallel merge sort with a pooled buffer (10M Int32: 756 → 207 ms).

  • Docs: fork/join (idea credited to Victor Taelin's BendRT) and heap and GC.

  • Value-range analysis: integer + - * whose operand ranges (literals, masks, shifts, widened Int32, ^/| of non-negatives, floored % d) prove no overflow are emitted unchecked; a proven x >= 2^31 ? x - 2^32 : x (also 8/16 bits) is one sign extension instead of a data-dependent branch. Suite terrain at 1M: 345 → 212 ms (C# 189 ms).

  • Purity gate: Alice.Runtime.Pure marks runtime functions the compiler may parallelize through (Digest::*); static Module.f(x) receivers, string interpolation, array literals, min/max/sum and the new numeric primitives are pure reads. Suite merkle at 1M leaves: 1812 → 1489 ms (C# 1885 ms).

  • Stdlib fork/join: sum, min, max of primitive arrays of 2^20+ elements split into SIMD chunks across cores (32M Float64 sum + max: 68.7 → 16.4 ms).

  • 2.0 ** n lowers to Math.ScaleB and x ** 2 to x * x (both bit-identical to Math.Pow); widening conversions (to_i64 on Int32, .to_f) are plain casts, narrowing ones an inlined checked helper.

  • Fork/join also forks Bend's single-statement parallel call a, b = f(x), g(y) (pure, independent, recursive), including into existing variables.

  • Startup: Prelude's dynamic/reflection/regex caches are created on first use instead of when every program starts.

  • Compiler: a loop nested in a serial loop is no longer auto-parallelized with the "heavy" threshold of 2 (game of life dispatched its 3-cell neighbourhood loop to the thread pool per cell: 197 ms → 60 ms); constant short trip counts emit no parallel path.

  • Fix for k in 0...m with m : Int64 (and n.times on Int64) declaring an int counter that could never reach the bound.

  • Allocation-free: round(n)/floor(n)/ceil(n), q, r = a.divmod(b) (no array), a.step(limit, k) with a literal step (inlined loop), and puts/str/inspect of numbers (no boxing).

  • Digest::SHA256.hexdigest, Digest::SHA1, Digest::MD5 (Ruby API).

  • New primitives: to_i8..to_u64, to_i32, to_f64, fdiv, sign, popcount, leading_zeros_count, trailing_zeros_count, bit_length, finite?; Int32::MAX, UInt8::MIN, Float64::INFINITY, Float64::NAN; snake_case .NET statics (Math.sqrt, Math.atan2).

  • Ownership (ADR 0016): local arrays of unmanaged elements that provably never escape are owned; their buffers return to a per-thread exact-length pool (Own(T)) before reassignment and at every exit, so a loop building a fresh row per iteration stops allocating. Moves (prev = row), interprocedural borrows and returns keep ownership. own a = ... makes it a checked contract: escape AL0207, use after move AL0208. 12 of the 17 suite workloads use it; editdist RSS 40.7 MB (C# 46.9 MB).

  • Auto-parallel loops prove row-major writes disjoint: img[y * w + x] = ... (directly or through a local bound once) with x counting 0...w in a nested loop runs rows across cores, as game of life, mandelbrot and image loops are written.

  • Runtime: Par.Fork offers its left half as one thread-pool work item instead of a Task (no Task, wrapper closure or continuation per fork); the join runs an unclaimed half inline, spins briefly, then blocks on the job. When the right half throws, the left half finishes before the exception unwinds.

  • Syntax: par for i in a...b parallelizes at any trip count and is compile error AL0209 when iterations are not provably independent; unchecked ... end is a wrapping region for integer + - * (ADR 0013). alice fmt indents both.

  • Benchmarks: run.sh runs Alice with the same server GC as the C# baselines; run 8 recorded (Alice at or ahead of idiomatic C# on all eight). The recursive-kernel rule requires a primitive result, so object-building recursion keeps tier-1 PGO.

  • examples/verify.sh expects 0.12 float printing (30.0).

  • Version 0.12.0.

  • JIT tiering (ADR 0014): methods with a loop nest, and primitive-only static functions that loops call, compile fully optimized from the first call instead of running instrumented tier-0 code until OSR / delayed tier-up (suite nbody 151 → 78 ms, terrain at 512x512 143 → 115 ms).

  • Parallel loops probe before dispatching: the first two iterations run serially, and the rest goes to the pool only when it projects past the dispatch cost (25 ms cold, 50 us warm); the runtime scheduler samples a chunk the same way. Short loops in short programs no longer pay thread-pool startup (small mandelbrot 80 → 75 ms); large ones stay parallel (4M-pixel mandelbrot 247 → 157 ms vs serial). Parallel chunk bodies compile fully optimized.

  • Floored / and % by a positive literal are branch-free (floor_div_pos, floor_mod_pos).

  • Fix: a loop whose body assigns its own counter is never auto-parallelized.

  • Fix (ADR 0008): a counted loop whose body read the array it writes at another index (row[j] = row[j - 1] + ...) was auto-parallelized from 65,536 iterations and could compute wrong values. Such loops stay serial; stencils over a different array stay parallel behind a runtime ReferenceEquals alias check.

  • Fix: an exception raised inside an auto-parallelized loop reaches the caller as itself, as in the serial loop, not wrapped in AggregateException.

  • Runtime: Par.Range/Reduce/MapSum run on a lean chunk scheduler (ParJob) instead of Parallel.For: one allocation per loop, an atomic chunk counter, and the calling thread works through chunks itself, so nested parallel loops cannot starve the pool and no TPL machinery is JIT-compiled at startup.

  • Shift counts are modular (ADR 0013): the + - * computing x << n drop their overflow checks.

  • A self-recursive function over primitives and primitive arrays compiles fully optimized from its first call (AggressiveOptimization). Suite queens: 0.92 s → 0.81 s (C# 0.91 s).

  • q, r = 7.divmod(2): multiple assignment destructures arrays and lists.

  • Hex and binary literals accept _ separators and type suffixes (0xFFFF_FFFF_i64, 0xFF_u8).

  • samples/wonderland3d: Alice-authored procedural 3D Wonderland film, interactive preview, deterministic 30-second MP4 export, GLSL bloom and an original synthesized stereo score (ADR 0012).

  • Windows installer: launch Microsoft's .NET SDK installer with a child-process execution policy override, preserving the user's policy and reporting SDK installation failures.

  • Root npm commands build/test Alice and generate VSIX/Zed packages from the companion extensions/ checkout.

B...

Read more

Alice v0.11.2

Choose a tag to compare

@github-actions github-actions released this 17 Oct 01:19

Archives built from e7bc3d7. Build and package checks: https://github.com/alicelanguage/alice/actions/runs/38990008774

Full Changelog: v0.11.1...v0.11.2

Alice v0.11.1

Choose a tag to compare

@github-actions github-actions released this 17 Oct 01:03

Archives built from 1e810eb. Build and package checks: https://github.com/alicelanguage/alice/actions/runs/38988678163

Full Changelog: v0.11.0...v0.11.1