fix: harden NaN defense in arithmetic fast paths + add float modulo-by-zero test#915
Open
He-Pin wants to merge 1 commit into
Open
fix: harden NaN defense in arithmetic fast paths + add float modulo-by-zero test#915He-Pin wants to merge 1 commit into
He-Pin wants to merge 1 commit into
Conversation
be1f38a to
1c47422
Compare
Collaborator
|
please rebase |
Motivation: sjsonnet silently produced NaN for modulo-by-zero (e.g. `42 % 0`) instead of throwing "division by zero" like go-jsonnet. The modulo operator lacked zero-divisor guards in 4 out of 6 evaluation paths, and the static optimizer constant-folded `x % 0` into Val.Num(NaN). Modification: - Evaluator: add rd==0 checks to evalBinaryOpNumNum, visitBinaryOpAsDouble, visitBinaryOp, and tryInlineArith for OP_% - Evaluator: add isNaN defense check to OP_/ in visitBinaryOp (catches NaN from stdlib math functions like std.sqrt(-1)) - StaticOptimizer: add r==0 guard to tryFoldAsDouble and main transform constant-folding for OP_% (consistent with OP_/) - Fix percent_mod_int5.jsonnet.golden to expect "division by zero" (verified against go-jsonnet binary) - Add regression tests for modulo-by-zero error cases Result: All modulo evaluation paths now reject division by zero, matching go-jsonnet behavior. NaN can no longer silently leak through.
1c47422 to
d262c37
Compare
ef3b0b4 to
d262c37
Compare
Contributor
Author
|
@stephenamar-db rebased now. |
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.
fix: harden NaN defense in arithmetic fast paths + add float modulo-by-zero test
Motivation:
After #910 landed the modulo-by-zero guards across all evaluator paths and
StaticOptimizer, there remained a gap:tryInlineArith(the eager-eval fast path invisitAsLazy) and the outervisitBinaryOpdid not check whether the result of/or%is NaN. This meant that if a NaN ever reached these paths (e.g. from stdlib math functions likestd.sqrt(-1)being folded into a lazy thunk), it could leak as aVal.Num(NaN)instead of producing an error — diverging from go-jsonnet'smakeDoubleCheckbehavior.Additionally, the
error.modulo_by_zerogolden file had a trailing blank line thatgit diff --checkflagged, andarithmetic_nan_handling.jsonnethad a misleading comment ("Test NaN handling") while only testing normal arithmetic.Modification:
Evaluator.tryInlineArith: addr.isNaNchecks forOP_/andOP_%results (fallback toLazyExpron NaN, matching existingisInfinitefallback pattern)Evaluator.visitBinaryOp: addresult.isNaN→Error.fail("not a number", pos)forOP_/andOP_%(consistent with go-jsonnet'smakeDoubleCheck)error.modulo_by_zero.jsonnet.goldentrailing blank linearithmetic_nan_handling.jsonnetcomment to accurately describe its purpose (regression tests ensuring NaN guards don't break normal arithmetic)error.modulo_by_zero_float.jsonnet+ golden:0.0 % 0.0→"Division by zero."Note: The modulo-by-zero guards in
evalBinaryOpNumNum,visitBinaryOpAsDouble, andStaticOptimizer.tryFoldBinaryOp/tryFoldAsDoubleare already in master via #910. This PR only adds the result-level NaN defense that #910 didn't cover.Result:
NaN can no longer leak through
tryInlineArithorvisitBinaryOpfor division/modulo results. All modulo-by-zero cases (integer and float) now correctly produce"Division by zero."or"division by zero"errors, matching go-jsonnet behavior. Golden file passesgit diff --check.References:
builtins.go:108-135:builtinDiv/builtinModulochecky.value == 0→"Division by zero."builtins.go:1108-1116:makeDoubleCheckguards NaN/Infinity on all arithmetic resultsevalBinaryOpNumNum,visitBinaryOpAsDouble,StaticOptimizer