Repository navigation
One way to learn Aether, and main's red Linux leg: the path, batch 9's examples, a glossary, Rosetta Code, #3432 and #3481 - #3477
Merged
Conversation
#3432 asked that a root's pathLenConstraint count, as OpenSSL and Go count it. validate_chain does already: since the rewrite for #3303 the anchor joins the path before path_code runs, and path_code checks every CA from index 1 to the end, the anchor last. Nothing tested it. crypto_tls13_chain gets a PKI made with openssl 3.6: rootpl0, a self-signed root with pathLenConstraint 0; icapl, a CA under it, with plleaf under icapl; and pldirect, an end entity of rootpl0. openssl verify refuses plleaf ("path length constraint exceeded") and takes pldirect, and so do the two new cases. A root without the extension is the existing "the same leaf under its root". Negative control: with path_code's loop stopped before the anchor (while j < n - 1), "a CA below a pathLenConstraint 0 root" is accepted and the test fails; restored, all 39 checks pass. path_code's comment now says the anchor is checked, so the loop is not trimmed.
The path stopped before batch 9. Six new examples, each with its output checked by make check-examples (30 of 30 runs alike for each, and ten whole check-examples runs clean): - 02-actors/11-ending.ae: stop, kill and a panic, and the reason a monitor's Down carries; a send to an ended actor is counted. - 02-actors/12-links.ae: a failure climbs links to an actor that traps exits; a "normal" stop ends nobody. - 02-actors/14-bounded-mailbox.ae: @mailbox(bound: 3), try_send's three answers, and a plain ! past the bound. The actor fills its own mailbox from its handler, where it can take nothing, so the counts are exact on every run; main's sends to it raced the cores (18 of 30). main prints with print, which flushes, so the runtime's one stderr report of a drop lands where a reader sees it. - 02-actors/15-streams.ae: std.stream holds a producer to a window of 3 over 20 lines; the peak in flight is the window. - 03-std/10-tcp.ae: a server and a client in one program, reading the byte stream up to the newline that ends a message. - 03-std/11-cryptography.ae: SHA-256 (the standard "abc" vector), an HMAC, an Ed25519 key, signature and check, all pure Aether; the public key in the Output block is the one openssl derives from the same seed. - 04-interop/07-limits: a C host runs script.ae's exports under a 50 ms deadline, a cancel from another thread, and a 64 KiB execution account (10 of 10 runs alike). Links come before the supervisor built on them, so 12-supervisor.ae is 13-supervisor.ae and 11-sharded-map.ae is 16-sharded-map.ae, with every reference moved (Makefile, the cache benchmark, three docs, and 10-crash-isolation's pointer). The path README links the tutorial and the glossary, and the result-type anchor it and 10-errors.ae use had two hyphens where GitHub's slug has three.
Getting started, docs/tutorial.md, docs/tutorials/01-03 and examples/ taught hello, control flow and actors four times, in four orders, and partly in an older style: getting started taught the deprecated `export` keyword and modules that are std.fs now, the tutorials answered with chains of print calls, and nothing linked docs/tutorials/. Each now has one job: - getting-started.md installs, runs a first program, starts a project, and covers the tools, versions, other targets and troubleshooting; its language sections are gone (the path teaches them), and the details of publishing release artifacts and binary packages moved to package-fetching.md, which pointed back at them; - the examples are the path; - tutorial.md is the path told in prose: a chapter per part, why each step comes where it does, common mistakes, and exercises with answers; its 11 programs with output are run and compared by make check-docs; - docs/tutorials/ is folded into it and removed. The README's documentation list starts with the path, in order. The FIFO regression test quoted the old tutorial; it quotes the new one.
#1604's third item: the words the tutorial, the examples and the reference use, each defined in a few lines with where to read more: actor, actor_ref, ask, borrowed, bounded mailbox, handle, link, safe point, sum type and the rest. The path, the tutorial and getting started link it. Its anchors were checked against the headings they name.
This was referenced Oct 30, 2026
#1191) examples/08-rosetta/ holds 22 classic tasks as Rosetta Code states them: FizzBuzz, Ackermann, Fibonacci (negative n too), factorial, GCD, the sieve, Hanoi, N queens (1 to 10, 92 for eight), string reversal by grapheme, palindromes, Roman encoding, binary digits, quicksort, binary search, hailstone (27 has 112 terms; 77031 the longest below 100,000), Levenshtein, happy and perfect numbers, look-and-say, Josephus, Pascal's triangle, and prime decomposition of 2^100 - 1 with std.bignum. Each is a whole program, formatted, with no warning, its Output block checked (make check-examples: 93 examples, 0 problems), its values those the task states. The path's README gives each task's length beside the first Go and Python solutions in RosettaCodeData (1d475861): non-blank lines that are not only a comment, Aether's header and Output block left out. Over the 22, Aether 455, Go 642, Python 233; the README says the other languages' first files are not always whole programs. The Aether counts were recounted, and the Go FizzBuzz count checked against the data. Roman numerals/Decode, Leap year and Caesar cipher are left out rather than written around two compiler bugs, filed for the next batch: #3478 (an if value whose condition ends in a call does not parse) and #3479 (c = s[i] warns, its type left to codegen). The issue stays open for the rest of Rosetta Code. The tutorial's last chapter points at the tasks.
cpu_recommend_cores caps the default at 16 (MAX_CORES), and the cores follow the CPUs the process may use; the entry said one per CPU.
…#3432) One fresh review read every change against the code. Fixed here: - A make buffer has one owner, the local that releases it; the glossary and the tutorial taught it as a handle the program frees, as did 11-arrays-and-slices and 17-ownership on the path, and five path examples and four Rosetta ones freed one by hand. The tutorial now gives three rules (counted values, a slice's buffer, handles), the examples say and do what the runtime does, and 11-arrays-and-slices' Output names the line its panic moved to. - 03-ask-reply said an ask without a reply waits out the 5-second timeout; it ends at once, "the handler returned without replying". The glossary's ask entry says both. - 07-limits: aether_set_call_deadline(ms) sets one instant, ms from then, not a budget per call; host.c and script.ae say so, and sleep_ms is valid for a second or more. - 11-cryptography checks an HMAC tag with ct.bytes_eq_slice, not == on hex strings, and says why. - 10-tcp keeps what follows a newline for the next read (a LineReader struct), checks every error, plays both sides on one thread instead of blocking a core in a handler, and calls TCP the layer under HTTP (std.http does not use std.tcp). - The tutorial promised exercises in every chapter (the first two have them; README and getting-started said the same), told a newcomer to give blocking work "its own actor" (std.worker is the answer), claimed a host never sees a crash and that Go runs in-process, said conditions "take" no parentheses, and that reading an actor's state is impossible rather than racy. The syntax quick reference the old tutorials ended with is back, at the end. - Glossary: link (a normal end ends nobody), safe point (--emit=obj too, and the JSON parse), counted value, outcome, contrib. - 11-ending: three common ways an actor ends, not "the first of three"; 14-bounded-mailbox, 15-streams and 07-limits use for and break where they hand-rolled loops and a flag. - getting-started: the aether.toml ae init writes, word for word; a missing pthread.h is libc6-dev (build-essential), not libpthread-stubs0-dev. bootstrap-from-source.md and cic-help.md described the old getting-started. - The Rosetta caveat cited Go's leap year, a task the tranche leaves out; the changelog counted six examples, not seven. - crypto_tls13_chain asserts the refusal's reason, so a rotted fixture refused for something else fails. Outside the diff and filed for the next batch: #3480 (the reference's reply section, the stdlib reference's constant-time advice, and emit-lib's deadline wording). check-examples (93, 0 problems), check-docs, check-changelog, check-tests, the chain test and ae fmt --check pass; the changed examples ran 5 of 5 alike (07-limits 3 of 3).
The second and last review, of d9dec8a alone, found: - The slice rule had no exceptions: a buffer put in a struct's field, a module var, a tuple or a message, or held by a local a closure captures, is not released for the program (language reference, "Slice ownership"; memory-management.md). 17-ownership, the tutorial's rule and its common mistake, and the glossary now say so, so a reader who stores a buffer in a struct frees it. - Removing the frees changed four Rosetta line counts (sieve 21, N queens 48, Levenshtein 26, Josephus 26) and the total (450). - The in-process Go host is a Go library built as a C shared library, not a TinyGo one. - Chapter 2 opened with "nothing outside an actor touches its state", which its own common mistake now contradicts; it says other code reaches an actor through messages. - 15-streams stops on any offer other than 1 (-1 after finish too). - 10-tcp sends with write_all, since one write may send part of a buffer. - The tutorial's counted values include sum values; std.tcp is the layer HTTP runs on, not a layer under std.http; the standard library follows two of chapter 1's rules; the path README says only the first two chapters have exercises. check-examples (93, 0 problems), check-docs and check-changelog pass, and the changed examples ran 5 of 5 alike.
…3481) Main's Linux / GCC leg aborts both Aether-consumer cases of the matrix since 0.861.0, and the log shows only "Aborted (core dumped)": run_app sent the program's stderr to app.err and never printed it. A failed case now prints the last lines of what the program wrote there.
#3481) Main's Linux / GCC leg failed the cross-version ABI matrix since 0.861.0, both Aether-consumer cases aborting. The stderr the previous commit prints: aether: the library was made for Aether runtime 0.860.0/abi1 and runs on 0.863.0/abi1: rebuild it with the Aether of the runtime it runs on aether: panic outside any try/catch or actor: ... An `ae build` program is linked -rdynamic (std.dl and script_gateway plugins resolve runtime symbols against it), so on ELF it exports its runtime, and a library's calls into the runtime it carries bound to the program's instead. The library's code ran on a runtime it was not made for; across versions the runtime ABI check of #2957 says so and refuses, and the importer ended on the panic. Equal versions (0.860.0 against 0.860.0) and a library older than the check (against 0.859.0) passed, which is why it showed only once main moved past the release. macOS (two-level namespace) and Windows (PE) keep the copies apart and pass, and docs/abi-policy.md says a self-contained library carries its own runtime. The owner chose that model for Linux too. So: - ae links a library with a runtime of its own -Bsymbolic on ELF: its references bind to what it defines, and it still exports them for a C host. A --shared-runtime library defines no runtime and is unchanged. - The import refusal of what needs one runtime (a struct holding strings, a builder using its config, #2931) and the warning that a library's actors are not drained at exit apply on every platform; both name --shared-runtime, as on macOS and Windows. - A library of another runtime ABI linked before this (no DT_SYMBOLIC or DF_SYMBOLIC in its dynamic section, read from the file) is refused at import on ELF, naming the rebuild, instead of ending the program on its first call. The reader was checked on x86_64, aarch64 and 32-bit ARM libraries built with and without -Bsymbolic. Tests: the ABI matrix checks that this ae links SYMBOLIC on ELF, expects the refusal for an old library linked without it, and still runs the old importer with this library (which now keeps its own runtime); binary_import_actors, binary_import_ownership, binary_import_structs and sandbox_nested_libraries expect on Linux and FreeBSD what they expected on macOS and Windows. Docs: emit-lib.md, abi-policy.md and ownership-model.md. Checked here: ae builds with -Werror on Windows and compiles for x86_64 Linux with zig (-Wall -Wextra -Werror); the scripts parse. Linux and FreeBSD behaviour is CI's to show.
# Conflicts: # tests/integration/abi_matrix/test_abi_matrix.sh
One review of the two #3481 commits found: - An importer of 0.864.0 or before took ELF's runtime copies as merged: it never refused an import that needs one runtime there, so with a new library keeping its own runtime, a struct holding strings or a builder using its config would cross unrefused and the two runtimes free each other's values, and a library's in-flight actor messages be dropped unwarned. A static-runtime library with such an export, builder or actors now names the `own_runtime` duty in its catalog's `requires`; importers since catalog 1.11 refuse a duty they do not know, and this ae performs it. Built here: a struct-holding export and an actor library name it, a plain one and a --shared-runtime one do not. - Cross-built ELF libraries (ae_cross.c, the Tier-A and FreeBSD links) are linked -Bsymbolic too, so a --target rebuild keeps its runtime as the refusal message promises; build-system.md says so. - The ELF reader needs the whole header of its class, a named byte order and a real program-header count (not PN_XNUM), leaves an empty dynamic segment unknown rather than "not symbolic", and keeps offsets within fseek's range. - sandbox_nested_libraries keeps Linux coverage of the interposed path of #3438 (still reachable by libraries linked without -Bsymbolic): case 2f links the two libraries by hand from aetherc's C, checks they are not SYMBOLIC, and runs them under the -rdynamic host. - A library's failure still reaches a `try` around the call (the import stub raises it again, #2956): emit-lib.md, app_panic.ae and the changelog said a panic never reached the program's catch. - The script gateway's docs (module.ae, the header and .c comments, the stdlib reference) said a POSIX script binds to the host's runtime; a default-built script keeps its own on Linux and FreeBSD now, as on macOS. - Stale comments and docs: emit-lib.md (the copies stay apart, its test table), containment-sandbox.md, the Makefile's shared-runtime note, ae.c's binary-import link note, aether_sandbox.h, and emit_lib_failed_result's app.ae. The matrix notes when it cannot check the -Bsymbolic link. Not done: a CI step pinned to an older release, which the review proposed on the belief that 0.864.0 was published; the latest published release is 0.860.0, so the existing step already crosses runtime ABIs. Checked here: the build with -Werror; ae.c and ae_cross.c compile for x86_64 Linux with zig (-Wall -Wextra -Werror); check-api-docs, check-docs, check-changelog, check-tests; binary_import_structs, binary_import_ownership, binary_import_actors, abi_policy, emit_lib_accounts, sandbox_nested_libraries, and the ABI matrix against the 0.860.0 Windows release, all passing on Windows. Linux is CI's.
This was referenced Oct 30, 2026
The review of b65fb05 found the duty missing for shapes ae refuses (a builder passing a struct with strings, a struct reaching one through a typed-pointer field, a @c_callback export) and named for shapes it imports (a struct of closures). The compiler and ae now decide from the catalog's own spellings through compiler/aether_lib_strings.h, which also finds a string inside a tuple (ae let one cross before) and enters each struct once. The duty is named under __ELF__ only: importers of 0.860.0 to 0.864.0 on macOS and Windows kept the copies apart and refuse per module and warn of actors themselves, as before. --shared-runtime with --target is refused, since a cross-built library carries the target's runtime. The ELF reader refuses a program header offset past LONG_MAX up front. The ownership and actors tests check which libraries name the duty; the docs list it.
…ero value Found by this PR's first macOS ARM64 run to finish: check-examples saw clang's -Wreturn-type warning for 07-limits' spin() in the example's output. Under --emit=lib the safe point at each loop head leaves the loop when a deadline or a cancel ends the call, and the C then fell off the end of a function that returns a value (undefined for the export's wrapper). A body whose last statement is a loop that cannot complete now returns its result's zero value at its end, as the wrapper does; a closure result is a zeroed _AeClosure. emit_lib_deadline compiles such functions with -Werror=return-type and checks the call returns 0 at its deadline (failed before, under GCC too).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
One way to learn Aether: a single path from installing it to whole programs, the examples batch 9 left out, a learner's glossary, a first tranche of Rosetta Code tasks, and #3432.
Scope
Fixed at the start. Anything found on the way that is outside it is filed for the next batch, unless it is a security, crash or data-loss problem.
std.cryptography: tls13_cert ignores a trust anchor's own pathLenConstraint #3432, a trust anchor's own pathLenConstraint.
validate_chainalready enforces it: since the rewrite for std.cryptography: tls13 chain verification checks only names and signatures (a CA's leaf can sign any host) #3303 the anchor joins the path thatpath_codechecks. Nothing tested it. crypto_tls13_chain now has an openssl-made root with pathLenConstraint 0: a CA below it is refused and an end entity under it is taken, asopenssl verifydoes. With the anchor taken out of the loop, the first case fails.examples/ teaches nothing batch 9 added: links, bounded mailboxes, streams, the embedding contract, std.tcp, std.cryptography #3476, examples for what batch 9 added, each with its output checked:
Downcarries);try_send;Links now come before the supervisor built on them, so 12-supervisor is 13 and 11-sharded-map is 16, with every reference updated.
One way to learn Aether: getting-started, tutorial.md, tutorials/ and examples/ teach the same things four times #3475, one learning path:
docs/tutorial.mdis the path told in prose: a chapter per part, common mistakes, and exercises with answers, every program checked bymake check-docs;docs/tutorials/(orphaned, older style) is folded into it;docs/package-fetching.md;Lessons from Roc: multi-stage snapshot tests, a rule-shaped agent doc, and a compiler Glossary #1604, its third item:
docs/glossary.md, linked from the path, the tutorial and getting started. The issue's other two items stay open.Rosetta Code examples #1191, a first tranche: 22 classic Rosetta Code tasks in
examples/08-rosetta/, each checked, with their length beside the first Go and Python solutions in RosettaCodeData (Aether 455 lines, Go 642, Python 233; the README says why that is not like for like). The issue stays open for the rest.Found on the way and filed for the next batch, not fixed here: #3478 (an
ifvalue whose condition ends in a call does not parse) and #3479 (c = s[i]warns that its type was left to codegen). Three Rosetta tasks wait for them instead of being written around them.Reviews
Two rounds, then stop:
makebuffer has one owner that releases it. The path taught it as a handle to free, in the new glossary and tutorial and in four existing examples (11-arrays-and-slices, 17-ownership, 03-ask-reply's no-reply wording among them), and nine examples freed one by hand. One way to learn Aether: getting-started, tutorial.md, tutorials/ and examples/ teach the same things four times #3475's target, "no claim the runtime does not keep", covers fixing the old examples too. The round also fixed:var, a tuple, a message or a captured local is the program's to free), the Rosetta counts the removed frees changed (Aether 450 lines now), and smaller wording.Found outside the scope and filed for the next batch: #3478, #3479 (compiler; three Rosetta tasks wait for them), #3480 (three reference statements the review found wrong), #3483 (a timing-bound runtime test that failed on macOS ASan and Valgrind here; re-run), #3485 (a refused binary import leaves its stub directory), #3486 (an unused function in
aethat clang-Werrorrejects).Checks
Local, Windows x86_64 (MSYS2 MINGW64), on 06e8b78:
make check-examples: 93 examples, 84 built, 81 run and matched, 0 problems. Ten whole runs in a row were clean before the Rosetta tranche. Each new example ran 30 of 30 alike, and each changed one 5 of 5.make check-docs: 121 complete blocks compile, 198 run and match.make check-changelog,make check-testsandae fmt --checkon every changed file pass.#3481: main's red Linux leg, fixed here
Main's Linux / GCC leg has been red since the 0.861.0 release, in the cross-version ABI matrix, and so was this PR's. The fix is here too: one PR, as asked; it was #3482 for an hour, now closed.
The cause:
ae buildprogram is linked-rdynamic, so on ELF a library's calls into the runtime it carries bound to the program's runtime.The owner chose that model for Linux and FreeBSD too (682c917):
aelinks a library with its own runtime-Bsymbolicon ELF.--shared-runtime.-Bsymbolic, is refused at import, naming the rebuild, instead of ending the program. The flag is read from its dynamic section.own_runtimeduty in its catalog. An importer of 0.860.0 to 0.864.0, which treated ELF's copies as merged, therefore refuses it instead of letting the two runtimes free each other's values. Cross-built ELF libraries are linked-Bsymbolictoo. And Linux keeps a test of the interposed path, which hand-linked libraries can still take (sandbox_nested_libraries 2f).aerefuses and named for some it imports. That is fixed in the last commit:aenow read the catalog's own spellings through one header (compiler/aether_lib_strings.h), so they decide alike. It also finds a string inside a tuple, whichaelet cross before, and visits each struct once.__ELF__), so importers of 0.860.0 to 0.864.0 on macOS and Windows keep refusing per module and warning about actors, as before.--shared-runtimewith--targetis refused: a cross-built library carries the target's runtime, while its catalog said the runtime was shared.The tests follow:
On Windows
aebuilds with-Werror, and it compiles for x86_64 Linux with zig. The ELF reader was checked on x86_64, aarch64 and 32-bit ARM libraries built with and without-Bsymbolic. Linux and FreeBSD behaviour is CI's to show. The review of the fixes was the last one: its findings are fixed here, and no further review round runs.Found by this PR's CI and fixed here
The first macOS ARM64 run to finish failed in check-examples. In
07-limits, clang warned thatspin(), which returns an int and ends inwhile true, does not return a value, and the warning reached the example's output.The cause is on main. Under
--emit=libthe safe point at each loop head leaves the loop when a deadline or a cancel ends the call. The C then fell off the end of a function that returns a value, so the value the export's wrapper read was undefined.Such a function now returns its result's zero value at its end, as the wrapper does.
emit_lib_deadlinegains a case that compiles it with-Werror=return-typeand checks the call returns 0 at its deadline. That case failed before the fix, under GCC too.Closes #3432, closes #3475, closes #3476, closes #3481