Repository navigation
Land the checkThrows library surface; repair the guard's constant propagation (EH-16) - #154
Merged
Merged
Conversation
…pagation (EH-16) The library surface of the exception-handling adoption: the checked psrs:runtime-exception#checkThrows contract publishes through CC and the stdlib's adapted Test.Assert implements checkThrows over it, making the guard real end to end. The corpus case's own execution is blocked by the backend's constant-stack tail-call treatment, diagnosed with evidence below; row EH-16 is recorded Blocked, not Verified. - stdlib.lock.json pins the psrs-stdlib check-throws branch commit 728a82f94af6e7c54ba10124af54644e8c364d19 (fingerprint fnv1a64-v1:5dfa6f2ce6b3092c): lib/Test/Assert.purs implements checkThrows :: forall a. (Unit -> a) -> Effect Boolean as Target.perform \_ -> checkThrowsImpl fn over the checked foreign, with its adaptation record (docs/assert-throws.md in the stdlib repo) and refreshed vendored_sha256; exports and assertImpl's storage-trap protocol unchanged. - Publication evidence: bindings/runtime/call/tests.rs canonical_exception_call_publishes_the_checked_source_signature_without_a_host_abi — canonical_signature keeps the callable operand and Boolean result unchanged (the provider has no host ABI), checked accepts the published external, and malformed shapes are rejected with the binding's module. - mir/opt/constants.rs + mir/opt/cfg.rs: the scalar-propagation lattice collected block-parameter predecessors over normal-edges-only reachability, the third such analysis after slice 4's two; a join shared by a protected path and a catch path saw only the normal predecessor's constants, so every checkThrows join materialized false and the handler's true argument was stripped from its jump — the guard observed false even when its catch path fired. Predecessor collection now uses reachable_blocks_with_unwind; psrs-backend 587 passed unchanged. - psrs-driver/src/program: the trusted-prelude compile path takes an explicit TargetCapabilities (default profile for every existing caller; tests::exception_library selects the enabled profile until the EH-17 flip); tests/mod.rs factors run_artifact_with_wasmtime so the corpus evidence runs the pinned engine limit. - tests/exception_library.rs, executed under PSRS_REQUIRE_WASMTIME=1 and -W max-wasm-stack=524288: the guard is interpreted and a genuinely non-tail depth-100000 recursion exhausts the budget inside the guarded call, catching true (200 then true, exit 0); a returning argument observes false (1009 then false); the corpus source's four depth-100 assertEqual halves log Done and exit 0 under the enabled profile; the full TCOMutRec is rejected under the disabled profile with the channel's diagnostic. - checklist row EH-16: Blocked with the evidence record, the observed TCOMutRec trap (stdout empty, exit 134, 1.25 s) and the diagnosis: the corpus's tco/ntco distinction is upstream's tail-call classification; our mandated closure-tail reuse makes both constant-stack, so the budget never fires for the ntco halves while the tco halves require that same reuse. Validation: cargo fmt --all --check; cargo clippy --workspace --all-targets -- -D warnings; PSRS_STDLIB_ROOT=… PSRS_REQUIRE_WASMTIME=1 cargo test --workspace — 2194 passed, 0 failed, 5 ignored (the scoreboard suites, out of scope).
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.
What lands
Slice 5 of the exception-handling adoption (row EH-16), spanning both repositories:
Compiler side:
psrs:runtime-exceptionCC publication is now evidenced through the real frontend: publication keeps the callable operand + Boolean result unchanged (the provider has no host ABI;plan_importplans no import),checked()accepts the published external, three malformed shapes rejected with module attribution.mir/opt/constants.rsbuilt its block-parameter predecessor lattice from normal-edges-only reachability — the third normal-CFG-only analysis (slice 4 fixed two). For a join shared by a protected path and a catch path, the join's parameter merged to the normal path's constant, was materialized away, and the removal stripped the handler'strueargument from its jump — everycheckThrowscall observedfalseeven on the catch path. Fixed viareachable_blocks_with_unwindoverunwind_successors.stdlib.lock.jsonpin updated to the stdlib change728a82f9(fingerprintfnv1a64-v1:5dfa6f2ce6b3092c— the hasher was verified by recomputing the old value7d96f1c44d8d52ccon the pre-change checkout).Stdlib side (independent repo, merged to its master as
728a82f9before this PR):Test/Assert.checkThrowsimplemented overTarget.perform+ the checkedpsrs:runtime-exception#checkThrowsbinding (10-line diff, surface unchanged,assert''s storage-trap protocol untouched); adaptation recorddocs/assert-throws.mdwith the pinned upstream declaration and target justification;upstream-lock.jsonrefreshed.EH-16 is recorded Blocked — honestly, and here is why
TCOMutRec compiles under the enabled profile but its first
assertThrowstraps (exit 134):\_ -> ntcoN 100000returns normally at depth 100000 — the backend's mandated closure-tail reuse (mark_tail→ReturnCallRef, CF-07's verified row) runs arity-mismatched mutual recursion in constant stack, so the budget never fires. The corpus's own comment names the distinction: upstream's TCO pass loopifies arity-matched named mutual-recursion groups and leaves arity-mismatched closure-mediated cycles growing the stack; the same program requires both behaviors (tcoNreuse,ntcoNexhaust). Reproducing it needs a recursion-classification/loopification topic of its own (cross-function loopification + declining physical tail reuse through closure-mediated cycles) — a normative decision, not a slice repair. The record names the owning decision; nothing was faked or weakened.What IS verified end to end (real frontend, pinned engine, all executed): depth-100000 genuinely-non-tail recursion under the guard exhausts the budget and the catch delivers
true(exit 0); a returning argument reportsfalse; the corpus source's four depth-100assertEqualhalves logDoneand exit 0; the full TCOMutRec is rejected under the disabled profile with the channel's diagnostic.Validation
cargo fmt --all --check✅ ·cargo clippy --workspace --all-targets -- -D warnings✅ ·cargo test -p psrs-backend588/0 (coordinator); full workspace suite run by the slice ✅. Stdlibnpm test: 12 passed / 1 pre-existing environmental failure (record-unsafe needs pinned upstream checkouts absent on this host — unrelated). D-04 untouched; profile default unchanged; L6/M7 board deliberately NOT run (that is EH-17's evidence, and its number depends on the tail-call decision above).