Fix four process layer v2 bugs found porting follow - #1424
Merged
Merged
Conversation
- Directory check: an answer module under a subdirectory, walked as the entry of its own program, lent the batch its bare module name. Every program lowered against that name lost its loop imports, and a process calling the capability failed with unknown-ident. Such a pair is now left to the programs that import the module by its real name. - wasm-gc: the seed walker demanded an eq helper for every list and vector element type, including the eager List<Tuple<State, Result>> a tuple of every answer function gets. A state with no equality (one holding a provider resource) then failed with "carrier eq inner type has no eq dispatch". An element is now seeded only when its list helpers compare elements at all; a real comparison still demands and fails on its own. - Generated loop: the answer module's state is held as an Option in the run and handed out (__take<Module>) for the length of one answer, and the Rust backend takes a record param by value when the function consumes it (updates it at its last use, or passes it at its last use to a callee that takes it by value). The run now moves down the serve chain, so the answer function holds the only reference to the state and Map/Vector updates in it are in place instead of a full copy per request. - aver effects --write now adds the in-place effects a process is missing, and lowers each program against its own answer modules, as check does. - An answer-shape warning is reported by the answer module only, not again by every module that imports it. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This was referenced Sep 25, 2026
jasisz
added a commit
that referenced
this pull request
Sep 25, 2026
* Release a consumed tuple or Option box on the VM The generated loop hands an answer module's state out of the run in an Option, inside a tuple, and takes the answer back in another tuple. On the VM the Option box and the tuples kept no holder count, so after a match had taken them apart they still counted as holders of the state, and every answer copied the module's Maps. #1424 fixed this on the Rust side only. Tuples and the boxes of Option.Some, Result.Ok and Result.Err now count their off-stack holders the way maps, vectors and records do: every entry, global and constant that stores one registers itself. A match whose subject nothing reads afterwards (a temporary, or a local at its last use) now releases what it takes apart: POP_CONSUMED after a tuple arm empties the tuple, and MATCH_UNWRAP with the consume bit empties the box, each only when nothing off the stack and no stack cell holds the tuple or box, and only when a Map or Vector inside makes it worth the walk. `?` does the same for an Ok box through PROPAGATE_ERR_CONSUMED. aver run --profile now also prints how many map entries the writes that were not in place copied. Release build without LTO, run_owned_answer_state scaled to 2000 requests against a 100k-entry Map: 1.84 s -> 0.13 s. The fixture as checked in (200 requests, 1k entries) is 0.06 s either way. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Close the release helper the merge left open Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
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.
Four compiler bugs found while moving btc-listener's
followonto process layer v2 (#1421), plus the doubledanswer-shapewarning.B1:
aver check .reportedunknown-ident 'Lib'for a process calling a capability under a subdirectoryaver check main.avpassed, and so didaver run. A directory check walkslib/ticks.avas the entry of its own program. There it is named by itsmodule Ticksline, so it added the pair(Lib.Clock, Ticks)to the answers the batch shares.main.avwas then lowered against aTicksthat nothing can import, the loop's generated imports failed, and the process lost theLibnamespace.Fix:
prime_batch_answersskips a pair whose answer module is the walk's own entry unless that name resolves from the module root to the same file. The programs that import the module by its real name (Lib.Ticks) still add it.Test:
run_all_spec::a_directory_check_names_an_answer_module_under_a_subdirectory_the_way_its_importer_does(fixturerun_process_subdir_capability) checksmain.avand.and runs the program.B2: wasm-gc failed with
carrier eq inner type 'App.Owner.State' has no eq dispatchEvery tuple gets an eager
List<Tuple<..>>, in caseList.zipbuilds one, so every answer function'sTuple<State, Result<R, Run.Wake>>has one. The seed walker inmodule.rsregistered an eq helper for every list and vector element with no resolvability check. When the state has no equality, the tuple's eq body could not be emitted. In btc the state reaches aKv.Handleresource.Fix: an element is seeded only when its list/vector helpers compare elements at all (
list_eq_kindisSome, the same test that gives the helpers theircontains/eq/hash slots). If nothing compares the element, no eq carrier is registered. A real comparison still registers its own helper at the comparison site, and still fails there.Test:
wasm_work_spec::an_answer_state_without_equality_compiles_on_wasm_gc(fixturerun_answer_state_without_eq, where the state holds a List, a Map and anOptionof a capability resource). Before the fix it failed with the carrier-eq error; now it compiles.B3: the answer module's state was copied on every change (Rust backend)
The loop called
Owner.heard(run.owner, …)whilerunstill held the state, and every__Runparam down the serve chain was borrowed. The Map inside was never uniquely owned, so eachMap.setdidRc::make_mutand copied the whole Map.Fix, in two parts:
__Runholds each answer state asOption<State>. A new__take<Module>(run) -> Tuple<__Run, Option<State>>hands the state out, and the serve arm matches on it and passes the taken state to the answer function.__serve<P><Kind>writes backOption.Some(__next). Keyed seating reads the keys through__keysOf<P>(run).__workHostStepbinds the slot ids before calling__serveEach, so the run is passed at its last use.CodegenContext::rust_owned_record_params), and signatures and call sites both read it. With this,__serveIf,__serve,__serve<P>,__take<Module>,__turnand__workHostSteptakerunby value, so the run moves from__serveEachto the answer function.Measurement,
aver compile --target rust, release build: the owner holds aMap<Int, Int>of 100,000 entries and one process makes 2,000 requests, each doingMap.set.The output is the same (
total 4000). The ~0.37 ms per request before is one clone of the 100k-entry map.The VM and wasm-gc do not have this pattern fixed:
aver): 15.0 s before and 15.6 s after for the same program, and 3.1 s of that is building the map. The VM does an in-placeMap.setonly on a slotown_paramproves unique.Map.set(state.counts, …)updates a field of a param, so it copies whatever the loop does.aver run --wasm-gc). The Map there copies on every set, and the loop shape does not change that.Tests:
rust_work_spec::an_answer_modules_state_reaches_its_answer_function_uniquely_owned(fixturerun_owned_answer_state) checks the by-value chain in the generated crate (__serveIf/__serve/__serveTicker/__takeOwnertakemut run: __Run, and the answer function gets__taken). It also builds the crate and compares its output with the VM.run_all_specdump expectations now include__takeLedgerand__keysOfScorer.run_schedule_casesreads the ledger throughservedBy, because the state field is now anOption.B4:
aver effects --writesaid "already minimal" whilecheckreported a missingConsole.printin a processThe rewriter left every yielding function alone. It now adds what the body performs in place, and never removes anything, because what a process reaches through its stops belongs to the lowering.
load_surfacenow also lowers each program against that program's own answer modules (program.marked()) instead of the manifest alone, the same waycheckdoes.Test:
effects_command_spec::write_adds_what_a_process_performs_in_place. It stripsConsole.printfrom the fixture, checks thatcheckfails, that the report saysmissing: Console.print, and that--writerestores both lists. Thencheckpasses.G3:
answer-shapewarnings printed twiceThe warning was raised once by the answer module (
Owner) and once by the entry that imports it (App.Owner). An answer-shape warning is now reported only when the checked module is the answer module itself. Errors are unchanged.Test:
answer_shape_spec::an_answer_function_with_effects_is_allowed_and_said_soasserts the warning appears once.Not fixed here
With these fixes,
aver compile main.av --module-root . --target wasm-gcon the btcfeat/follow-processesworktree now stops at a different error:capability ABI lacks List<Infra.BlockJobs.Bytes> slot. The job kindInfra.BlockJobsnamesBytesthrough its dependencies' types, and the ABI qualifies it as a type of the capability. That is a separate bug in the job-kind boundary (#1401 area) and needs its own issue.Checks run locally
cargo fmt --all -- --checkcargo clippy -p aver-lang --lib --bin aver --benches -- -D warnings, and the same with--features wasm,wasip2cargo test -p aver-lang --lib --features wasm,wasip2: 1702 passed--features wasm,wasip2:run_all_spec32,effects_command_spec20,answer_shape_spec18,typechecker_spec349,yield_spec38,yield_verify_spec16,wasm_work_spec26,wasip2_coordinator_spec7,guide_example_spec10,rust_work_spec23,own_param_graduation13,own_param_soundness27,rust_codegen_regression4. All pass.Lean and certification were not run locally. The generated loop changed shape and the wasm-gc seeding registers fewer eq helpers when an element has no equality, so the Proof and Certification workflows are the ones to watch.