diff --git a/src/komet/kdist/soroban-semantics/data.md b/src/komet/kdist/soroban-semantics/data.md index 842d0bf..0b830f6 100644 --- a/src/komet/kdist/soroban-semantics/data.md +++ b/src/komet/kdist/soroban-semantics/data.md @@ -179,6 +179,8 @@ module HOST-OBJECT rule getTagWithFlag(true, I64(_)) => 65 rule getTagWithFlag(true, U128(_)) => 68 rule getTagWithFlag(true, I128(_)) => 69 + rule getTagWithFlag(true, U256(_)) => 70 + rule getTagWithFlag(true, I256(_)) => 71 rule getTagWithFlag(true, Symbol(_)) => 74 rule getTagWithFlag(_, SCV) => getTag(SCV) [owise] diff --git a/src/komet/kdist/soroban-semantics/host/integer.md b/src/komet/kdist/soroban-semantics/host/integer.md index aca8f0e..d09397e 100644 --- a/src/komet/kdist/soroban-semantics/host/integer.md +++ b/src/komet/kdist/soroban-semantics/host/integer.md @@ -351,7 +351,7 @@ negative value is converted to its unsigned form before shifting. The i256 arithmetic host functions are *checked*: the result must be representable, and the divisor must be non-zero. Otherwise the host fails the -call with `(ErrValue, ArithDomain)` rather than wrapping around. +call with `(ErrObject, ArithDomain)` rather than wrapping around. The first Wasm argument is on top of the host stack (`loadArgs` pushes the arguments in reverse), so `A` is the left-hand side. @@ -367,7 +367,7 @@ arguments in reverse), so `A` is the left-hand side. requires inRangeInt(i256, Signed, A +Int B) rule [hostCallAux-i256-add-overflow]: - hostCallAux ( "i" , "v" ) => #throw(ErrValue, ArithDomain) ... + hostCallAux ( "i" , "v" ) => #throw(ErrObject, ArithDomain) ... I256(A) : I256(B) : S => S requires notBool inRangeInt(i256, Signed, A +Int B) ``` @@ -385,7 +385,7 @@ arguments in reverse), so `A` is the left-hand side. requires inRangeInt(i256, Signed, A -Int B) rule [hostCallAux-i256-sub-overflow]: - hostCallAux ( "i" , "w" ) => #throw(ErrValue, ArithDomain) ... + hostCallAux ( "i" , "w" ) => #throw(ErrObject, ArithDomain) ... I256(A) : I256(B) : S => S requires notBool inRangeInt(i256, Signed, A -Int B) ``` @@ -403,7 +403,7 @@ arguments in reverse), so `A` is the left-hand side. requires inRangeInt(i256, Signed, A *Int B) rule [hostCallAux-i256-mul-overflow]: - hostCallAux ( "i" , "x" ) => #throw(ErrValue, ArithDomain) ... + hostCallAux ( "i" , "x" ) => #throw(ErrObject, ArithDomain) ... I256(A) : I256(B) : S => S requires notBool inRangeInt(i256, Signed, A *Int B) ``` @@ -427,12 +427,12 @@ rather than range-checking `A /Int B`, so no side condition divides. [preserves-definedness] // 'A /Int B' is defined for non-zero B rule [hostCallAux-i256-div-by-zero]: - hostCallAux ( "i" , "y" ) => #throw(ErrValue, ArithDomain) ... + hostCallAux ( "i" , "y" ) => #throw(ErrObject, ArithDomain) ... I256(_A) : I256(B) : S => S requires B ==Int 0 rule [hostCallAux-i256-div-overflow]: - hostCallAux ( "i" , "y" ) => #throw(ErrValue, ArithDomain) ... + hostCallAux ( "i" , "y" ) => #throw(ErrObject, ArithDomain) ... I256(A) : I256(B) : S => S requires A ==Int minInt(i256, Signed) andBool B ==Int -1 ``` @@ -440,9 +440,9 @@ rather than range-checking `A /Int B`, so no side condition divides. ## i256_rem_euclid Euclidean modulo: the result is always non-negative, whatever the signs of the -operands. K's `modInt` takes the sign of its divisor, so dividing by `absInt(B)` -gives exactly Rust's `rem_euclid`. The quotient never leaves the range, so a -zero divisor is the only failure. +operands. K's `modInt` is e-division, so it already lands in `[0, absInt(B))` +and agrees with Rust's `rem_euclid`. `checked_rem_euclid` rejects the same two +operand pairs as `checked_div`: a zero divisor, and `i256::MIN` by `-1`. ```k rule [hostCallAux-i256-rem-euclid]: @@ -453,12 +453,14 @@ zero divisor is the only failure. I256(A) : I256(B) : S => S requires B =/=Int 0 + andBool notBool (A ==Int minInt(i256, Signed) andBool B ==Int -1) [preserves-definedness] // 'A modInt absInt(B)' is defined for non-zero B rule [hostCallAux-i256-rem-euclid-error]: - hostCallAux ( "i" , "z" ) => #throw(ErrValue, ArithDomain) ... - I256(_A) : I256(B) : S => S + hostCallAux ( "i" , "z" ) => #throw(ErrObject, ArithDomain) ... + I256(A) : I256(B) : S => S requires B ==Int 0 + orBool (A ==Int minInt(i256, Signed) andBool B ==Int -1) ``` ```k diff --git a/src/tests/integration/data/i256.wast b/src/tests/integration/data/i256.wast index c51108d..10806a5 100644 --- a/src/tests/integration/data/i256.wast +++ b/src/tests/integration/data/i256.wast @@ -268,7 +268,7 @@ callTx( Contract(b"test-sc"), "add", ListItem(I256(2 ^Int 255 -Int 1)) ListItem(I256(1)), - Error(ErrValue, ArithDomain) + Error(ErrObject, ArithDomain) ) callTx( @@ -292,7 +292,7 @@ callTx( Contract(b"test-sc"), "sub", ListItem(I256(0 -Int 2 ^Int 255)) ListItem(I256(1)), - Error(ErrValue, ArithDomain) + Error(ErrObject, ArithDomain) ) callTx( @@ -317,7 +317,7 @@ callTx( Contract(b"test-sc"), "mul", ListItem(I256(2 ^Int 200)) ListItem(I256(2 ^Int 200)), - Error(ErrValue, ArithDomain) + Error(ErrObject, ArithDomain) ) ;; Division truncates toward zero. @@ -358,7 +358,7 @@ callTx( Contract(b"test-sc"), "div", ListItem(I256(1)) ListItem(I256(0)), - Error(ErrValue, ArithDomain) + Error(ErrObject, ArithDomain) ) ;; i256::MIN / -1 is the one division that overflows. @@ -367,7 +367,7 @@ callTx( Contract(b"test-sc"), "div", ListItem(I256(0 -Int 2 ^Int 255)) ListItem(I256(0 -Int 1)), - Error(ErrValue, ArithDomain) + Error(ErrObject, ArithDomain) ) ;; Euclidean modulo is never negative, whatever the signs of the operands. @@ -408,7 +408,17 @@ callTx( Contract(b"test-sc"), "rem", ListItem(I256(1)) ListItem(I256(0)), - Error(ErrValue, ArithDomain) + Error(ErrObject, ArithDomain) +) + +;; `checked_rem_euclid` rejects i256::MIN % -1 as well, even though the +;; mathematical remainder (0) is representable. +callTx( + Account(b"test-caller"), + Contract(b"test-sc"), + "rem", + ListItem(I256(0 -Int 2 ^Int 255)) ListItem(I256(0 -Int 1)), + Error(ErrObject, ArithDomain) ) setExitCode(0)