fix(semantics): correct i256 error type, rem_euclid domain and 256-bit object tags - #128
Open
mihaieremia wants to merge 1 commit into
Open
mihaieremia wants to merge 1 commit into
mihaieremia wants to merge 1 commit into
Conversation
mihaieremia
force-pushed
the
feat/i256-host-support
branch
from
September 15, 2026 19:01
9ba94b7 to
1f570c8
Compare
…t object tags Three defects in the I256 support added by runtimeverification#127, each checked against the Soroban host rather than against the surrounding rules. - The i256 arithmetic host functions report `(Object, ArithDomain)`, not `(Value, ArithDomain)`. The 6-argument arm of `impl_bignum_host_fns!` builds its error with `ScErrorType::Object` (soroban-env-host/src/host/num.rs:39-45), and `i256_add`, `_sub`, `_mul`, `_div` and `_rem_euclid` all expand through that arm (soroban-env-host/src/host.rs:1568-1578). - `i256_rem_euclid` accepted `i256::MIN` by `-1` and returned `0`. `I256` is `ethnum::I256` (soroban-env-common/src/num.rs:10), whose `checked_rem_euclid` rejects `rhs == 0 || (self == MIN && rhs == -1)` (ethnum-1.5.2/src/int/api.rs:635-641) -- the same two pairs as `checked_div`, even though the mathematical remainder is representable. This was a silent wrong answer, not a stuck term. The comment above the rule was also wrong about `modInt`: K's `modInt` is e-division and always lands in `[0, absInt(B))` (domains.md:1262), so `absInt(B)` is redundant rather than load-bearing. Left the expression alone, corrected the claim. - `getTagWithFlag(true, _)` was missing both 256-bit types. Under `alwaysAllocate`, `addObject` tags the handle with `getTagWithFlag(AA, SCV)`; with no entry, a value that fits the small encoding fell through to `owise` and took `getTag`, so an object stored in `<hostObjects>` was tagged `U256Small` (12) / `I256Small` (13). `isObject` is tag 64..77, so such a handle then failed `isObject` and `loadObject` took the `-small` branch, making `fromSmall` decode the object index as the value. U64, I64, U128, I128 and Symbol all carry this line for that reason; U256 and I256 were the only two small-capable types missing it. Tag numbers confirmed against `Tag` in soroban-env-common/src/val.rs (`U256Small = 12`, `U256Object = 70`, `I256Small = 13`, `I256Object = 71`). i256.wast pins the first two: the six existing error assertions move to `ErrObject`, and `i256::MIN rem_euclid -1` is added. The tag rules are only reachable with `alwaysAllocate` set, which the .wast harness does not do; they were checked by building with the cell forced to `true`, under which u256.wast, double_u256.wast and i256.wast all fail before the change and pass after.
mihaieremia
force-pushed
the
feat/i256-host-support
branch
from
September 15, 2026 19:21
1f570c8 to
80e07f1
Compare
mihaieremia
marked this pull request as ready for review
September 15, 2026 20:41
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.
Follow up fix to #127, not a feature.
This branch was opened as "add I256 support" against
089b3aa. #127 landed that support first, so the original diff is obsolete, and merging it would have regressedmaster: it had noi256_sub, its smalleri256.wastwould have replaced yours, and it carried a stalejson-utils.mdreverting theValMap2JSONrename from #126. Rebased ontoa078e29and reduced to the three defects the comparison exposed in #127's rules.Fixes
Citations are
rs-soroban-envata7e15b43, which is the current tip ofmain. Re-checked against thev28.0.2tag as well; every cited line is identical there.1. Wrong error type, 7 sites.
i256_add,i256_sub,i256_mul,i256_divandi256_rem_euclidall expand through the 6 argument arm ofimpl_bignum_host_fns!(host.rs:1568-1579), which raisesScErrorType::Object(host/num.rs:39-45). The rules threwErrValue. NowErrObject.2.
i256_rem_euclidacceptedi256::MINby-1.I256isethnum::I256, whosechecked_rem_euclidrejects a zero divisor andMIN / -1alike (ethnum 1.5.3, src/int/api.rs:635-641). The rule guarded onlyB =/=Int 0. Run against the unfixed build it returnedI256(0)withexitCode 0, so this was a silent wrong answer rather than a stuck term.The comment above the rule was also wrong: K's
modIntis e-division (domains.md:1262), soabsInt(B)is redundant rather than load bearing. Expression left alone, claim corrected.3.
getTagWithFlag(true, _)missing for both 256 bit types. UnderalwaysAllocate,addObjecttags a handle withgetTagWithFlag(AA, SCV)(configuration.md:324). With no entry for the type, a small range value falls through toowiseand takesgetTag, so an object in<hostObjects>gets tag 12 or 13.isObjectis64..77(data.md:123), so the handle fails it,loadObjecttakes its-smallbranch, andfromSmalldecodes the object index as the value.U64,I64,U128,I128andSymbolalready carry this line. U256 and I256 were the only remaining small capable types missing it, so both are added.TimepointandDurationhave noScValconstructor in komet (data.md:53-70) and cannot reach the rule.Verification
K v7.1.313 on macOS/arm64.
kdist build 'soroban-semantics.*'--warnings-to-errorspytest -k "test_run or tracing_smoke"make check,make test-unitkrunover all 36.wastArithmetic, both directions executed:
mastersemantics with this branch'si256.wastgiveskrunexit 1, stuck at the firstexpectResult(Error(ErrObject, 0)). This branch gives exit 0.Tags: no committed test reaches these rules, because the
.wastharness runs withalwaysAllocate = false. Forcing the cell true in a local build, which is not part of this PR,u256.wast,double_u256.wastandi256.wastall fail onmaster'sdata.mdand all pass with both rules. Reading theU256(123)handle out of<locals>at the same step in each build:data.mdHostValmasterU256SmallU256ObjectSame handle, tag differing by 58. Unfixed, the run ends with
U256(50331648)on the host stack whereU256(246)is expected. Both runs print<alwaysAllocate>in their own output configuration, so the mode is self attested rather than asserted.Constants were re-read from source rather than recall: the four tags (
val.rs), every host function letter for module"i"(env.json, all already correct),ScValTypeOrd(I256) = 12, and the/IntandmodIntdivision semantics (domains.md:1255,1262).Not included
The
soroban-sdk22 to 27 bump from the original diff is dropped. 22.0.11 already has everyI256method the tests use, and dropping it removes a 419 lineCargo.lockdiff.The comparison also surfaced five pre-existing gaps unrelated to I256, mostly missing error rules that leave a term stuck where the host errors. Out of scope here, happy to file them separately.
Limits
CI has not run.
gh pr checks 128reports no checks, since the integration jobs need a maintainer to approve the workflow for a fork PR. Everything above is local.No symbolic proof was run. The Haskell backend compiles, but only the concrete and tracing definitions were executed, and fix 3 was reached by forcing the configuration cell rather than through
komet prove --always-allocate.make test-integrationwas not run in full, becausetest_kometneeds the CI Docker image.