Repository navigation
A copy met after an unfold converts before either copy unfolds (arguments compared rigid first, failures remembered on the cell) - #1232
Giulio2002 wants to merge 1 commit into
Conversation
The full conversion pass asks the rigid comparison (no def unfolds) of the arguments of two calls of one def before unfolding them. A share-cell pair the rigid pass found unequal is marked on the cell, so no failing walk is repeated; a success unifies the cells as before. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
An independent measurement, with a minimal case that has no large numerals. The cost comes from a slow closed computation inside a type, compared after an unrelated part of the type needed one unfolding step: The control is the same file with
Both checkers print macOS arm64, bun 1.2.9. |
|
Sorry for not answering this earlier. While I'm not in favour of the approach here, adding an extra field to Var, i think we could do a similar optimization in the same spirit. I've tried it a couple times but didn't get to a design I liked. |
|
you can close it and do the optimization. whatever you need |
|
Closed in favor of #1832 |
Disclosure: I wrote this change with Claude Code (an AI coding agent), like #1142 and #1209.
bend2/bend.tsis touched (+45/−3), whichAGENTS.mdreserves for humans: if you would rather write the idea yourself, please close this and take it. It replaces #1210 (closed) with a smaller change: no side table, no wrapper frames.The problem
A conversion whose two sides become identical only after a def unfolds evaluates both closed sub-terms. A big numeral is then a unary chain, and the checker overflows its stack.
Stock
term_compareruns the rigid pass (the empty book: no def unfolds, calls compare by head and arguments) once, on the whole goal.boxed()againstBox{big()}is not rigidly equal, so the full pass unfoldsboxed()toBox{big()}. It then evaluatesbig()on both sides (2^30Succcells each). It never asks the cheap question again. In a real project (a verified SSZ library whose schemas carry list limits of 2^30 and above) this blocks every proof that unfolds a spec definition next to its limit.The change
Before the full pass unfolds two calls of one filled def, compare their arguments with the rigid pass (
compare_call, first to last, stopping at the first unequal argument). If they are equal the calls are equal and neither unfolds.rhs.v = lhs.v) runs on success, as it does today.u?: object: the right-hand side the rigid pass found it unequal to. It is read only in rigid EQ mode and written in the block that does the unification. No table, no wrapper function, no extra stack frame, so conversions recurse exactly as deep as on stock.-1cell (book !== RIGID): otherwise the rigid retry would stop the full pass's own evaluation of that cell.A step budget on the retry did not help.
compute_1600makes about 475k retries and all of them fail, and even 4 steps per retry cost about as much as the unbounded walk. The failure marker brings rigid steps from 6.6M to 1.46M.Measurements
All on a shared Linux server, so times are noisy. Stock is
01875127.tests/check/conversion_copy_after_unfold.bend)nwith{U32.to_nat(n) == n'n}by{==}tests/(1,526 files, one run each)bench/checkercompute_1600 (median CPU of 10, interleaved)I also ran a verified SSZ proof tree of about 6,100 files (48 import-only umbrella checks) on this change: all 48 pass at the pinned stack (423 s wall on the shared box).
Soundness probes, same verdict as stock on each: equal arguments to different defs; different arguments under a box; partial application and arity; 2^30 literals (also inside a box); different lambdas; an unfilled hole; an unfilled law; a non-terminating def with equal and with different arguments; an
@unsafedef through an import. Every rejection stays a rejection. The controls stock overflows on (copy after unfold, nested,twice(big())) are accepted.Tests
tests/check/conversion_copy_after_unfold.bend(the repro, its mirror,twice(big()), and a call nested in an unfolded def) andtests/check/conversion_copy_after_unfold_mismatch.bend(arguments that differ must still fail to convert, including under a box).🤖 Generated with Claude Code