Skip to content

Commit eaa69e9

Browse files
committed
fix(#624): fence Lean/Why3 backends to fail loud on early return
The experimental Lean/Why3 emitters silently dropped early `return` statements (gen_block skipped them, lowering `fn f(){ return 1; … }` to `:= ()`). Add an Ast.fn_body_contains_return fence so codegen_lean/codegen_why3 return Error instead. The hole moves residual(pinned) -> loud-fail; the xfail pin flipped to XPASS (verified by running the harness) and is converted to a positive fence-check (test_stub_backend_return_fenced). Also (the "small calls"): - Content-bind suite-file anchors too (whole-file hash) — the manifest now seals test/test_borrow_polonius.ml; verified property 3 catches a mutation to it. Ledger's "each anchor content-bound" claim is now fully true. - Add a best-effort branch-freshness check to property 4 (origin/main-guarded): if main moved a soundness path under the branch, the gate asks for a rebase. - Fix a gate false-positive: pinned_row_pins now matches the STATUS CELL, not a prose mention of the status word (caught by running the gate, not eyeballing). - #555-stub row cites #624 (not #560 — that's variable-string wasm ops). Verified: dune build + dune runtest green (534 tests); all 5 gate properties + the three sibling gates green; self-tests for content-binding (incl. suite) red as expected. #623 (interp non-tail resume) left for owner-steer — it needs a CPS rewrite of eval, not a small fix. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BbxKhXQwTvVgkYDgBMLJoa
1 parent 65c2ae3 commit eaa69e9

7 files changed

Lines changed: 156 additions & 77 deletions

File tree

docs/SOUNDNESS.adoc

Lines changed: 26 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
// Freshness stamp — a real main-ancestor commit (survives squash-merge); advance
55
// it (and --reseal) in any change that touches a soundness path. See "Keeping
66
// this honest" and tools/check-soundness-ledger.sh (property 4).
7-
:ground-truth-sha: dd6c19e
7+
:ground-truth-sha: 65c2ae3
88
:ground-truth-date: 2026-06-21
99
:stamp-verified-by: tools/check-soundness-ledger.sh
1010
:toc: macro
@@ -59,13 +59,11 @@ Anchored to tests:: Each claim cites the executable artefact that proves it. The
5959
gate verifies the artefact *exists*.
6060

6161
Content-bound:: The gate also verifies the artefact still *says what it said*:
62-
each fixture and pinned-test anchor is pinned to a content digest in
63-
`tools/soundness-anchors.sha256`, so a fixture cannot have its assertion eroded —
64-
weakened, re-baselined, or silently re-scoped — without the digest mismatching
65-
and the build failing. (Suite-file anchors such as `test/test_borrow_polonius.ml`
66-
are existence- and stamp-checked but not digest-bound: a whole-file hash is too
67-
coarse to be a meaningful content seal.) Prose nailed to a fixture *by name only*
68-
can still rot if the fixture's meaning drifts; prose nailed *by content* cannot.
62+
each anchor — fixture, pinned-test body, and suite file — is pinned to a content
63+
digest in `tools/soundness-anchors.sha256`, so an anchor cannot have its assertion
64+
eroded — weakened, re-baselined, or silently re-scoped — without the digest
65+
mismatching and the build failing. Prose nailed to a fixture *by name only* can
66+
still rot if the fixture's meaning drifts; prose nailed *by content* cannot.
6967

7068
Stamped, and the stamp is enforced:: The freshness attribute records the
7169
main-ancestor commit this was last reconciled against. The gate fails if `HEAD`
@@ -200,21 +198,19 @@ corpus with a shrinking allowlist (7 known divergences).
200198
count pinned at 7)
201199

202200
| #555-stub / #624
203-
| The Lean and Why3 experimental backends drop `return` statements wholesale
204-
(`lean_codegen.ml` `gen_block` skips `StmtReturn`, so `fn f(){ return 1; … }`
205-
lowers to `:= ()`) — a silent-wrong-value shape, the same _category_ as the #555
206-
codegen hole but on the stub-backend path. Not yet fenced. The backends are
207-
`experimental` so no production verdict depends on them; the agreed remedy (#624)
208-
is to fail loud. (NB: #628 tracked this as "#560"; that issue is actually
209-
variable-string wasm ops — #624 is the correct tracker, and this change supplies
210-
the pin #628 could not.)
211-
| `residual (pinned)`
201+
| The Lean and Why3 experimental backends now FAIL LOUD on an early `return`
202+
instead of silently dropping it. Previously `gen_block` skipped the `return`
203+
statements, so `fn f(){ return 1; … }` lowered to `:= ()`; now a
204+
`Ast.fn_body_contains_return` fence makes `codegen_lean` / `codegen_why3` return
205+
`Error`. Fixed via #624. (NB: #628 tracked this as "#560"; that issue is actually
206+
variable-string wasm ops — #624 is the correct tracker. This change supplies the
207+
loud fence #628 could not, so the row is now `loud-fail` (it was previously a
208+
pinned residual).)
209+
| `loud-fail`
212210
| `test/e2e/fixtures/stub_backend_return_dropped.affine` +
213-
`test_stub_backend_return_xfail` (xfail: asserts the desired loud failure —
214-
`Lean_codegen.codegen_lean` returning `Error` — on an early-`return` program;
215-
currently fails-as-expected because the backend silently returns `Ok`; flips to
216-
an unexpected pass the day the backend fails loud or is removed). See _Still
217-
open_.
211+
`test_stub_backend_return_fenced` (positive check: asserts
212+
`Lean_codegen.codegen_lean` returns `Error` on the early-`return` program — it was
213+
`Ok`; the check would fail if the silent drop regressed).
218214
|===
219215

220216
== Still open — read before claiming "sound"
@@ -226,14 +222,14 @@ row in the table above, restated for the hurried reader — not a separate list:
226222
* *Interpreter non-tail resume* (#555 / #623, `residual (pinned)`) — the
227223
`5`-not-`105` shape. Silent on the interpreter path; pinned by
228224
`test_resume_nontail_xfail`.
229-
* *Stub backends drop `return`* (#555-stub / #624, `residual (pinned)`) — Lean and
230-
Why3 drop `return` wholesale. Silent-wrong-value, broader than #555 codegen,
231-
fenced only by `experimental` gating and a pinning xfail, not by a loud failure.
232-
Treat all non-reference backends as experimental (see
233-
`docs/CAPABILITY-MATRIX.adoc`).
234-
235-
If you are deciding whether AffineScript is "sound enough" for a use, these two
236-
rows are the answer, and the metatheory caveat below applies to *both*.
225+
(Stub backends dropping `return` (#555-stub / #624) is now `loud-fail` — Lean and
226+
Why3 reject an early `return` instead of silently dropping it — so it is no longer
227+
a "still open" residual. Treat all non-reference backends as experimental anyway,
228+
see `docs/CAPABILITY-MATRIX.adoc`.)
229+
230+
If you are deciding whether AffineScript is "sound enough" for a use, the
231+
interpreter non-tail resume row above is the one remaining residual, and the
232+
metatheory caveat below applies to it.
237233

238234
=== Pinned-residual discipline
239235

lib/ast.ml

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -499,3 +499,44 @@ type program = {
499499
prog_decls : top_level list;
500500
}
501501
[@@deriving show, eq]
502+
503+
(** Does a function body contain a `return`? Used by the experimental Lean/Why3
504+
emitters to FAIL LOUD rather than silently drop control flow (Refs #624):
505+
their [gen_block] skips non-`let` statements, so an early `return` would
506+
otherwise vanish. Covers the standard nesting (block / if / let / binary /
507+
unary / app / span / while / for / assign); unlisted exotic variants default
508+
to [false] (conservative — they are not generated by the fixtures this
509+
fences, and the backends are experimental). *)
510+
let rec expr_contains_return (e : expr) : bool =
511+
match e with
512+
| ExprReturn _ -> true
513+
| ExprSpan (inner, _) -> expr_contains_return inner
514+
| ExprBlock blk -> block_contains_return blk
515+
| ExprIf { ei_cond; ei_then; ei_else } ->
516+
expr_contains_return ei_cond
517+
|| expr_contains_return ei_then
518+
|| (match ei_else with Some e -> expr_contains_return e | None -> false)
519+
| ExprLet { el_value; el_body; _ } ->
520+
expr_contains_return el_value
521+
|| (match el_body with Some e -> expr_contains_return e | None -> false)
522+
| ExprBinary (a, _, b) -> expr_contains_return a || expr_contains_return b
523+
| ExprUnary (_, x) -> expr_contains_return x
524+
| ExprApp (f, args) ->
525+
expr_contains_return f || List.exists expr_contains_return args
526+
| _ -> false
527+
528+
and block_contains_return (blk : block) : bool =
529+
List.exists stmt_contains_return blk.blk_stmts
530+
|| (match blk.blk_expr with Some e -> expr_contains_return e | None -> false)
531+
532+
and stmt_contains_return (s : stmt) : bool =
533+
match s with
534+
| StmtExpr e -> expr_contains_return e
535+
| StmtLet { sl_value; _ } -> expr_contains_return sl_value
536+
| StmtAssign (a, _, b) -> expr_contains_return a || expr_contains_return b
537+
| StmtWhile (c, b) -> expr_contains_return c || block_contains_return b
538+
| StmtFor (_, e, b) -> expr_contains_return e || block_contains_return b
539+
540+
let fn_body_contains_return : fn_body -> bool = function
541+
| FnExpr e -> expr_contains_return e
542+
| FnBlock b -> block_contains_return b

lib/lean_codegen.ml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,15 @@ and gen_block (blk : block) : string =
8484

8585
let gen_function (fd : fn_decl) : string =
8686
let name = mangle fd.fd_name.name in
87+
(* #624: fail loud on `return` rather than silently drop it. The block emitter
88+
skips non-`let` statements, so an early `return` would vanish (the body
89+
would lower to `:= ()`). Refuse instead — these are experimental backends. *)
90+
if fn_body_contains_return fd.fd_body then
91+
failwith
92+
(Printf.sprintf
93+
"Lean backend does not support `return` (in `%s`, Refs #624): early \
94+
returns would silently drop control flow; rewrite as a tail expression"
95+
fd.fd_name.name);
8796
let params = match fd.fd_params with
8897
| [] -> ""
8998
| _ -> " " ^ String.concat " " (List.map (fun (p : param) ->

lib/why3_codegen.ml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,13 @@ and gen_block (blk : block) : string =
9191

9292
let gen_function (fd : fn_decl) : string =
9393
let name = mangle fd.fd_name.name in
94+
(* #624: fail loud on `return` rather than silently drop it (see lean_codegen). *)
95+
if fn_body_contains_return fd.fd_body then
96+
failwith
97+
(Printf.sprintf
98+
"Why3 backend does not support `return` (in `%s`, Refs #624): early \
99+
returns would silently drop control flow; rewrite as a tail expression"
100+
fd.fd_name.name);
94101
let params = match fd.fd_params with
95102
| [] -> "()"
96103
| _ -> String.concat " " (List.map (fun (p : param) ->

test/xfail/test_xfail_pins.ml

Lines changed: 43 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,32 @@
11
(* SPDX-License-Identifier: MPL-2.0 *)
22
(* SPDX-FileCopyrightText: 2024-2026 hyperpolymath (Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk>) *)
33

4-
(** xfail pin harness for the soundness ledger (docs/SOUNDNESS.adoc, property 5).
5-
6-
Each pin asserts the DESIRED behaviour of a known soundness hole. Because the
7-
hole is still present, the assertion currently FAILS — which is the expected
8-
state, reported as [XFAIL-OK]. The day the hole is fixed, the assertion
9-
PASSES, which is unexpected and reported as [XPASS]: the process exits
10-
non-zero (so [dune runtest] and the gate both go red) with a message telling
11-
the engineer to update the ledger row rather than silence the pin.
12-
13-
Polarity / fail-closed contract:
14-
- pin body raises [Xfail] when the desired behaviour is NOT met (hole
15-
present) -> XFAIL-OK (exit 0 contribution)
16-
- pin body returns normally (desired behaviour met, hole gone)
17-
-> XPASS (exit 1)
18-
- pin body raises ANY OTHER exception (parse error, missing fixture, …)
19-
-> XERROR (exit 1) — pin infrastructure is broken,
20-
so we fail closed rather than mistake it for an
21-
expected failure.
4+
(** Soundness pin harness for docs/SOUNDNESS.adoc (property 5).
5+
6+
Two kinds of executable soundness-row check live here:
7+
8+
- [pins] — *xfail* pins for `residual (pinned)` rows. Each asserts the
9+
DESIRED behaviour; because the hole is still present the assertion fails,
10+
which is expected and reported [XFAIL-OK]. The day the hole is fixed it
11+
passes — reported [XPASS], non-zero exit — telling the engineer to update
12+
the ledger row rather than silence the pin. (Fail-closed: any OTHER
13+
exception is [XERROR], also non-zero — broken pin infrastructure must not
14+
masquerade as an expected failure.)
15+
16+
- [fences] — positive checks for `loud-fail` rows: the hole is closed by a
17+
loud failure, and the check asserts that loud failure still holds (it
18+
passes now and would fail if the silent behaviour regressed).
2219
2320
The shell gate (tools/check-soundness-ledger.sh, property 5) runs this
24-
executable, asserts every pin named by a `residual (pinned)` / `open
25-
(tracked)` ledger row reports XFAIL-OK, and surfaces XPASS as the distinct
26-
"is the hole fixed?" alarm. *)
21+
executable and asserts every pin named by a `residual (pinned)` / `open
22+
(tracked)` row reports XFAIL-OK; XPASS is surfaced as the distinct "is the
23+
hole fixed?" alarm. The exe exits non-zero on any XPASS / XERROR / fence
24+
failure, so `dune runtest` catches them too. *)
2725

2826
open Affinescript
2927

3028
exception Xfail of string
3129

32-
(* Locate test/e2e/fixtures across the run contexts we care about:
33-
AFFINE_FIXTURES override (the gate sets this), the dune-test sandbox
34-
(cwd = _build/default/test/xfail, fixtures at ../e2e/fixtures via the
35-
source_tree dep), cwd = test/, and cwd = repo root. Fail closed if none. *)
3630
let fixtures_dir =
3731
let cands =
3832
(match Sys.getenv_opt "AFFINE_FIXTURES" with Some d -> [ d ] | None -> [])
@@ -56,7 +50,7 @@ let resolve_symbols prog =
5650
| Ok (rctx, _) -> rctx.symbols
5751
| Error (e, _) -> failwith ("resolve: " ^ Resolve.show_resolve_error e)
5852

59-
(* ---- Pin: #555 / #623 — interpreter non-tail single-shot resume ----------
53+
(* ---- xfail pin: #555 / #623 — interpreter non-tail single-shot resume -----
6054
`let x = op(); x + 100` with `op() => resume(5)` should yield 105 once the
6155
continuation is reified; the shallow tree-walking interpreter yields 5. *)
6256
let test_resume_nontail_xfail () =
@@ -82,25 +76,29 @@ let test_resume_nontail_xfail () =
8276
raise
8377
(Xfail ("non-tail resume returned " ^ Value.show_value other ^ ", not 105"))
8478

85-
(* ---- Pin: #555-stub / #624 — Lean backend drops `return` ------------------
86-
The agreed remedy (#624) is to fail loud on early `return`; today
87-
`codegen_lean` returns Ok and silently lowers the body to `:= ()`. *)
88-
let test_stub_backend_return_xfail () =
79+
(* ---- fence: #555-stub / #624 — Lean backend fails loud on `return` ---------
80+
Fixed: `codegen_lean` now returns Error on an early `return` (the
81+
fn_body_contains_return fence) instead of silently lowering to `:= ()`. This
82+
positive check asserts the loud failure holds; it would fail if the silent
83+
drop regressed. *)
84+
let test_stub_backend_return_fenced () =
8985
let path = fixture "stub_backend_return_dropped.affine" in
9086
let prog = parse path in
9187
let symbols = resolve_symbols prog in
9288
match Lean_codegen.codegen_lean prog symbols with
93-
| Error _ -> () (* desired met -> XPASS: the backend now fails loud *)
89+
| Error _ -> () (* desired loud failure holds *)
9490
| Ok _ ->
95-
raise
96-
(Xfail
97-
"Lean_codegen.codegen_lean returned Ok; early `return` silently dropped")
91+
failwith
92+
"Lean_codegen.codegen_lean returned Ok on an early `return` — the #624 \
93+
loud fence regressed to a silent drop"
9894

9995
let pins =
10096
[ ("test_resume_nontail_xfail",
101-
"#555/#623 interp non-tail resume -> 5 not 105", test_resume_nontail_xfail);
102-
("test_stub_backend_return_xfail",
103-
"#555-stub/#624 Lean drops `return`", test_stub_backend_return_xfail) ]
97+
"#555/#623 interp non-tail resume -> 5 not 105", test_resume_nontail_xfail) ]
98+
99+
let fences =
100+
[ ("test_stub_backend_return_fenced",
101+
"#555-stub/#624 Lean fails loud on `return`", test_stub_backend_return_fenced) ]
104102

105103
let () =
106104
let bad = ref false in
@@ -117,5 +115,13 @@ let () =
117115
bad := true;
118116
Printf.printf "XERROR %s -- pin infrastructure failed: %s\n" name m)
119117
pins;
118+
List.iter
119+
(fun (name, reason, body) ->
120+
match try `Ok (body ()) with e -> `Err (Printexc.to_string e) with
121+
| `Ok () -> Printf.printf "FENCE-OK %s -- %s\n" name reason
122+
| `Err m ->
123+
bad := true;
124+
Printf.printf "FENCE-FAIL %s -- loud failure regressed: %s\n" name m)
125+
fences;
120126
flush stdout;
121127
if !bad then exit 1 else exit 0

tools/check-soundness-ledger.sh

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,8 @@
2222
# Anchors are named in the ledger table's column 4 ("Proven in code"). From the
2323
# table region of docs/SOUNDNESS.adoc we extract three anchor kinds:
2424
# FIXTURE test/e2e/fixtures/<name>.affine -> digest = sha256 of the file
25-
# SUITE test/<name>.ml -> existence + stamp only
26-
# (a whole-file digest is too
27-
# coarse to content-bind)
25+
# SUITE test/<name>.ml -> digest = sha256 of the whole
26+
# file (a suite is its own unit)
2827
# TEST backtick-quoted `test_<name>` -> digest = sha256 of the test's
2928
# function body, extracted from its `let <name> ` line to the next
3029
# top-level `let ` in whichever single test/**/*.ml defines it.
@@ -114,16 +113,19 @@ hash_anchor() {
114113
grab && index($0,"let ")==1 {exit}
115114
grab {print}
116115
' "$file" | sha256sum | cut -d' ' -f1 ;;
116+
suite)
117+
[ -f "$loc" ] || die "anchor ${a}: suite file not found"
118+
sha256sum "$loc" | cut -d' ' -f1 ;;
117119
*) die "internal: hash_anchor on non-digest kind: ${a}" ;;
118120
esac
119121
}
120122

121-
# --- the digest manifest (fixtures + test bodies; not suite files) ----------
123+
# --- the digest manifest (fixtures + test bodies + suite files) -------------
122124
generate_manifest() {
123125
local a
124126
while IFS= read -r a; do
125127
[ -z "$a" ] && continue
126-
case "$a" in fixture:*|test:*) printf '%s %s\n' "$(hash_anchor "$a")" "$a" ;; esac
128+
case "$a" in fixture:*|test:*|suite:*) printf '%s %s\n' "$(hash_anchor "$a")" "$a" ;; esac
127129
done < <(extract_anchors) | LC_ALL=C sort
128130
}
129131

@@ -137,7 +139,7 @@ if [ "${1:-}" = "--reseal" ]; then
137139
echo "# Soundness anchor digests for docs/SOUNDNESS.adoc — DO NOT hand-edit."
138140
echo "# Regenerate: tools/check-soundness-ledger.sh --reseal"
139141
echo "# Format: <sha256> <kind:locator>. FIXTURE = file hash; TEST = the"
140-
echo "# test's function-body hash. SUITE anchors are existence-checked only."
142+
echo "# test's function-body hash; SUITE = whole-file hash."
141143
generate_manifest
142144
} > "$MANIFEST"
143145
echo "OK: resealed ${MANIFEST} ($(grep -cvE '^#' "$MANIFEST") anchors)."
@@ -208,6 +210,21 @@ check_stamp() {
208210
return 1
209211
fi
210212
fi
213+
# Freshness vs main (best-effort; skipped if origin/main is absent, e.g. a
214+
# shallow CI checkout): if this branch is BEHIND main on a soundness path, the
215+
# ledger may be stale relative to a change not yet rebased in. Fires only when
216+
# main actually moved a soundness path under you — never for a fresh branch.
217+
if git rev-parse --verify -q origin/main >/dev/null 2>&1; then
218+
local mb; mb="$(git merge-base HEAD origin/main 2>/dev/null || true)"
219+
if [ -n "$mb" ]; then
220+
local behind; behind="$(git diff --name-only "$mb" origin/main -- "$LEDGER" "${SOUNDNESS_LIB_PATHS[@]}" 2>/dev/null || true)"
221+
if [ -n "$behind" ]; then
222+
note "ERROR (property 4): main moved a soundness path under this branch; rebase onto origin/main and re-verify the ledger:"
223+
printf '%s\n' "$behind" | sed 's/^/ /' >&2
224+
return 1
225+
fi
226+
fi
227+
fi
211228
}
212229

213230
# ---- Property 5: pin-liveness (xfail harness) ------------------------------
@@ -221,7 +238,9 @@ pinned_row_pins() {
221238
END { emit() }
222239
function emit() {
223240
if (row=="") return
224-
if (row ~ /residual \(pinned\)/ || row ~ /open \(tracked\)/) {
241+
# Match the STATUS CELL (a line that is `| `status``), not a prose mention
242+
# of the status word elsewhere in the row.
243+
if (row ~ /\n\| `residual \(pinned\)`/ || row ~ /\n\| `open \(tracked\)`/) {
225244
if (match(row, /test_[A-Za-z0-9_]+_xfail/)) print substr(row, RSTART, RLENGTH)
226245
else if (match(row, /test_[A-Za-z0-9_]+/)) print substr(row, RSTART, RLENGTH)
227246
else print "NOPIN"

tools/soundness-anchors.sha256

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@
22
# Soundness anchor digests for docs/SOUNDNESS.adoc — DO NOT hand-edit.
33
# Regenerate: tools/check-soundness-ledger.sh --reseal
44
# Format: <sha256> <kind:locator>. FIXTURE = file hash; TEST = the
5-
# test's function-body hash. SUITE anchors are existence-checked only.
5+
# test's function-body hash; SUITE = whole-file hash.
66
0d3623cf36a67860106d64bb32d30633978bb426d4ddd77ecc2bf6e48e1b7263 fixture:test/e2e/fixtures/borrow_callee_returned_borrow_uam.affine
7-
20f7fd48892f586fccba9bf549ff4db8ccaf97304b37052f45c0f283c324da41 test:test_resume_nontail_xfail
87
5b84a4f71aa65f9aa9383e31c0cb624291c4f9d0de7cca4a0ea6a76fe85aea35 fixture:test/e2e/fixtures/handle_resume_nontail.affine
98
68f0f4ad2462f146b87f491b7456aff9a56b77397344d22c62f4f4ff5ca6e3ae fixture:test/e2e/fixtures/assume_rejected.affine
10-
a391d19e0190938ed0ccb4a495d8aac2f178bbe2b15f9723f64b16064f973bb6 test:test_stub_backend_return_xfail
9+
7f416f186a15511ec5c64d2b96bfffbdf9e7c8c0879951d56c80e46129e2ea48 test:test_stub_backend_return_fenced
10+
80541ee00580b4dccee4657cbd31d11a1ddaa18cc49813b48f686c9509f314dc suite:test/test_borrow_polonius.ml
11+
874a5531cd8bc402adf4b66277b21b15d921efe06a7128ea6583bcd6e55781ba test:test_resume_nontail_xfail
1112
ac1da10c3206605304f11e869f6aa07acba57f6f8248bb2dfacca19984f86c31 test:test_coherence_duplicate_rejected
1213
b76c7824a9b1570d66752359a28d75ebdfaedb5de5ad68c343a0a2c26ad4221c fixture:test/e2e/fixtures/async_sync_fallback.affine
1314
c743b86cfc50512e5867ddd792b4b4273b5bfb6f40205b4a07019031d7e7b102 fixture:test/e2e/fixtures/handle_resume_multishot.affine

0 commit comments

Comments
 (0)