Carry waits keyed by a program's own type beside the generated loop - #1431
Merged
Merged
Conversation
The generated loop keys its one wait by Int. A program that also waited by hand, keyed by a sum of its own, then keyed waits two ways: the Rust door refused it, and wasm-gc failed validation, because every backend names the wait's boundary from one key type. In a program that answers a capability of its own, every wait keyed by another type than Int, in the entry or in a dependency, is now rewritten to call helpers generated for its key type in the module's __ namespace (__waitPollBy<Key> and four small recursive helpers). They number the keys in the order the map puts them in, wait on the Int-keyed set, and answer the keys the ready numbers stand for, in the same order. The entry is carried inside the yield lowering, after the loop is generated; a module with no process is carried in the front door from a checked copy, the same two-phase shape the lowering uses. No backend changes: every backend sees Int keys, so the wasm-gc wait ABI (aver.wait_poll, __rt_result_wait_keys_ok/_err and the rest) keeps its names and shape, and --target wasip2, which only lowers Int-keyed waits, runs these programs too. A program that answers no capability keeps the old rule and its own key type. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Brings in nested literal and list patterns (#1434), Run.fail (#1430), #1428, #1429 and #1432. A module with no process of its own used to go down one of two exclusive branches of the front pipeline: compile its nested patterns, or carry its waits keyed by a type other than Int. A dependency can need both, so the branch now does both in order: check the module as written, so errors name the patterns the user wrote; compile the nested patterns and check again; carry the waits with the key types read off that check of the lowered module; and check what the carrying wrote once more. The entry already did both: the yield lowering carries its waits over the module as written, and the nested patterns are compiled after it. Nested patterns inside a yield function are still refused. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A second copy of the wait fixture whose dependency matches the answer of its own Wait.poll with nested patterns in the same function that waits, and whose entry matches its empty wait's answer with nested patterns too. The run, the dump of the generated helpers, generated Rust and both wasm targets all do the same work as the VM. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
# Conflicts: # CHANGELOG.md
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.
Bug
A program with the generated loop could not also key its own
Wait.pollby a sum type. The loop keys its wait byInt, and every backend names the wait's boundary from one key type per program. Repro: the probe withtype Watchplus a hand-writtenWait.poll(idle: Map<Watch, Wait.Item>, 10)besideRun.all().aver compile --target rustrefused it: "this program keys one wait set by 'Int' and another by 'Watch'…".aver run --wasm-gcfailed module validation (type mismatch: expected (ref null $type), found (ref null $type)). The registry pickedWatchas the one key, and the loop's wait passedMap<Int, Wait.Item>.The limit lives in
capability::work::wait_key_type/wait_key_conflict, read by the wasm-gc type registry (wait_set_key, theaver.wait_pollimport signature, the__rt_wait_*and__rt_result_wait_keys_ok/_errfactories) and by the Rust door (theWait.pollinstantiation and the key codec).Fix
The waits are generated per key type rather than making the boundary generic. In a program that answers a capability of its own, every wait keyed by a type other than
Intis rewritten to call helpers generated for that key type in the waiting module's__namespace:The keys are numbered in the order the map puts them in, the wait runs on the
Int-keyed set, and the ready numbers map back to their keys in the same order. The answer is the same one the program would have got.pipeline::frontfrom a checked copy, the same two-phase shape the lowering uses. This runs only when the program answers a capability and the module callsWait.poll.ABI
There is no ABI change. Every backend sees
Intkeys only, soaver.wait_poll,__rt_wait_set_order,__rt_wait_set_key_at,__rt_wait_keys_cons/_niland__rt_result_wait_keys_ok/_errkeep their names and theirIntinstantiation. The vendored JS adapters (tools/wasm-work/host.mjs, Robin's copy) need nothing. A recording of such a wait holds theInt-keyed wait.--target wasip2, which lowers onlyInt-keyed waits, now runs these programs too.A program that answers no capability keeps the old rule and its own key type: the socket-only JS test still sees
Map<Watch, Wait.Item>.Tests
The new fixture
tests/fixtures/run_wait_own_keyhas a loop, one wait in the entry keyed byCollecting.Watch, and a job collection keyed byCollecting.Watchin a dependency.run_all_spec: both paths run on the VM, the helpers are generated for both modules, and a recording of the hand-written waits replays toMATCH.rust_work_spec: VM/Rust parity on both paths.wasm_work_spec: VM parity on wasm-gc and wasip2, both paths.