Conversation
(cherry picked from commit f039146f88e3b166a2cf4b50948a365dc68e5bcc)
Use normalizeInt_uint256_word for legacy uint256 decoding, as the corresponding End and Dog proofs already do. Preserve the theorem statement and reuse the normalization abstraction rather than unfolding its modulo implementation. Verified that Clipper.FileDecode fails before this change and builds successfully afterward at the default Lean stack.
Cover all 32 Solidity integer widths at signed, unsigned, and wrap boundaries across explicit casts, word interpretation, legacy ABI cleaning, modern/Vyper validation, and ABI encoding. Check nested widening versus signedness-conversion order, range assertions, and invalid operand types. Build Solm.CastTests explicitly in CI because the Solm root does not import standalone regression modules. Focused CastTests and Clipper.FileDecode validation passes (3475 jobs). Full combined cast/negation corpus replay follows on the dependent branch.
(cherry picked from commit 1cc56aa4ab48c42d57dd910bddf4ef523ce330d5)
Cover signed minima and neighboring values across all supported integer widths, double negation, operand errors and reverts, and typed surface notation. Document that UnaryOp.neg models wrapping semantics and label the explicit CI regression target accordingly.
(cherry picked from commit 8979706200701d6bd2a337e0d4c0f49acfe46808)
Annotate remaining integer operators and reuse checked/wrapping evaluation lemmas. Supply canonical integer and decoded bytes32 evidence at typed declarations and parameter binding, deriving bounds from the existing ABI and packed storage proofs. Preserve public constructor/runtime equivalence claims and validate the full 4796-job library, example, benchmark, and integer regression corpus at the default Lean stack.
Generalize wrapping evaluation and execution helpers by removing 46 obsolete order premises. Propagate the smaller interfaces through callers and remove dead derivations and two orphan bound lemmas. Validate the complete 4796-job Lean corpus at the default stack. Audit retained theorem conclusions and confirm identical Clipper runtime and contract capstone axiom sets.
Replace divisor-text heuristics with lexical unchecked expressions and blocks, and migrate existing wrapping specifications. Preserve the V3 oracle's uint160 accumulator and uint256 interpolation boundaries in the affected migration. Add scope, rename-invariance, and oracle boundary regressions. The full 4798-job corpus passes at default stack; Clipper correctness axiom sets are unchanged. Rust formatting and Clippy do not apply to this Lean-only repository (no Cargo manifest).
Fold surface literal expressions with exact rational precision and use literal representability for integer promotion. Infer shift and power types from the left/base operand, checking and evaluating the unsigned right operand independently. Preserve genuinely typed narrow subexpressions. Add surface AST, evaluator, and rejection regressions checked against solc 0.8.35. Keep the minimal existing-spec migrations required by literal folding; defer the V3-only arithmetic tests and FullMath repair to a separate follow-up. Validation: complete 4800-job default-stack corpus replay and focused integer tests pass. Retained theorem statements and both Clipper correctness axiom sets are unchanged, with no sorryAx.
Adapt new Burn, Mint, Swap, and update-call consumers to typed locals and parameter validation. Derive reserve bounds from existing packed-storage invariants and retain the public correctness statements. Exercise arithmetic boundaries across all 32 Solidity integer widths, simplify explicit wrapping notation, and document the supported semantics. Full default-stack corpus and integer regressions pass (4998 jobs); V2 and Clipper capstones introduce no new non-native axioms or proof placeholders.
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.
Summary
Fix integer casts, wrapping negation, binary arithmetic, and expression typing in Solm, and migrate the affected specifications and proofs.
Values remain represented by Lean’s unbounded
Int, but integer operations now carry the width, signedness, and overflow policy needed to interpret them correctly. This avoids relying on storage writes or ABI encoding to repair an incorrect intermediate value.Semantic fixes
1. Integer casts normalize to the target type
Integer-to-integer casts previously returned their input unchanged, so narrowing followed by widening could preserve bits that should have been discarded.
The shared
normalizeIntfunction now:2^N.castValue?, integer storage-word interpretation, and legacy ABI cleaning share this implementation. For example, narrowing511touint8produces255, even when subsequently widened.Modern ABI decoding still rejects noncanonical words. ABI encoding and explicit range assertions remain validation operations, not truncating conversions.
2. Unary negation preserves the declared width
Unbounded negation produced an out-of-range positive result when negating a signed minimum.
UnaryOp.negnow carries anIntType; evaluation negates the mathematical value and normalizes the result at that type. Wrappingint8negation therefore maps-128back to-128.The corresponding Cat proofs retain their permitted signed-minimum boundary rather than excluding it with a stronger hypothesis.
This operator models legacy/unchecked wrapping negation only. Modern checked-overflow negation is not implemented. Surface negation requires an explicitly typed operand, such as
-int256(x).3. Arithmetic has explicit checked and wrapping semantics
Arithmetic operators carry both
IntTypeandIntArithMode. A sharedevalIntArithResulteither checks the mathematical result against the declared signed/unsigned range or normalizes it for wrapping arithmetic.This applies to addition, subtraction, multiplication, division, and exponentiation. Overflow is handled at the operation itself, including narrow intermediate expressions—not deferred until assignment or encoding.
Surface binary arithmetic is checked by default. The elaborator supports lexical
unchecked { ... }blocks and the specification-onlyunchecked(expr)form. It records the selected mode on generated operators; the mode does not propagate into called functions.Ordinary
%never selects wrapping behavior. The final implementation has no divisor-spelling heuristic. Explicitas Trange assertions remain checked.4. Signed division and remainder use truncation toward zero
Runtime division and remainder use
Int.tdivandInt.tmod, replacing Lean’s Euclidean division/remainder behavior.For example, signed
-5 / 2evaluates to-2, and-5 % 2evaluates to-1.The signed-minimum divided by
-1case passes through the shared overflow handler: checked division reverts, while wrapping division returns the signed minimum. Division and remainder by zero revert regardless of overflow mode.5. Bitwise operations and shifts respect width and signedness
Integer bitwise operations first interpret operands as unsigned bit patterns of the declared width, perform the operation, and reinterpret the result at the declared signedness. Integer complement follows the same width-bounded approach.
Left shifts truncate to the declared width. Right shifts normalize the operand first and preserve its sign for signed types:
int8(-3) >> 1produces-2. Oversized shifts produce zero, or-1for a negative signed right shift. Negative shift amounts are rejected.Fixed-bytes complement, bitwise operations, and shifts have separate AST constructors, preserving their behavior without conflating them with typed integers.
6. Literal expressions retain exact values during elaboration
Literal-only expressions are evaluated with exact rational precision before conversion into runtime integer expressions.
This permits intermediate values outside the destination width and preserves fractional intermediates:
(255 + 1) - 1folds to255.(5 / 2) * 2folds to5.Literal values also participate in type selection. A representable literal can use the other operand’s type; otherwise, its smallest representable integer type participates in promotion or rejection. For example, a
uint32operand plus4294967296selectsuint40.Genuinely typed narrow subexpressions retain their widths. Typed
type(T).min/maxexpressions and conditional expressions are not incorrectly treated as arbitrary-precision literals. Opaque Lean escapes are not assumed to be Solidity constant expressions.7. Shift and exponentiation typing follows the left operand
The result type of shifts and exponentiation comes from the left/base operand, not a common type formed with the right operand.
Thus a wide shift count does not widen a
uint8value, and a wide exponent does not removeuint8multiplication overflow.Literal bases with typed right operands use the appropriate full-word signed or unsigned type. The right operand is checked independently as unsigned and elaborated without imposing the left operand’s width on its subtree.
8. Typed bindings require valid values
Previously, local declarations and parameter binding could ignore their declared ABI types.
The recursive
valueMatchesABITypechecks integer ranges, fixed-byte widths, array lengths and elements, and tuple shapes. Parameter binding uses this check; successful typed local-declaration proofs now require corresponding validation evidence.These checks do not silently truncate values. Invalid local bindings are excluded from valid execution derivations; this change does not introduce an EVM revert rule for malformed specifications.
Specification and proof migration
The migration adds reusable normalization, typed-arithmetic, and binding lemmas, and repairs consumers at their actual source invariants:
UInt256values throughout evaluation and storage.observeSingleinterpolation repair makes uint160 accumulator arithmetic, uint256 intermediate arithmetic, and narrowing explicit.Limitations
Unary negation remains wrapping-only (no checked/unchecked behavior difference). The uniswap v3 scaffold still has known full-precision and source-width/signedness issues deferred to separate work.