fix: error thunk propagation and std.round(-0) sign#1044
Open
He-Pin wants to merge 2 commits into
Open
Conversation
Motivation: Two bugs found via adversarial testing against go-jsonnet and jrsonnet: 1. When an error thunk was forced inside a binary operator, sjsonnet incorrectly reported "Infinite recursion detected (self-referential thunk)" instead of propagating the original error message. Root cause: tryEvalCatch caught the sjsonnet.Error (NonFatal), returned null, and the main dispatch re-evaluated the thunk which still had its `evaluating` flag set to true. 2. std.round(-0) returned 0 instead of -0, losing the IEEE 754 negative zero sign. Both go-jsonnet and jrsonnet correctly return -0. Modification: 1. Wrap LazyExpr.value's computation in try/finally to guarantee the `evaluating` flag is reset even when the expression throws an error. This allows the main dispatch path to properly re-evaluate the thunk and propagate the original error. 2. Change `x >= 0` to `x > 0` in std.round so that -0.0 takes the negative branch (math.ceil preserves -0.0 sign per IEEE 754). Result: - `local a = error "fail"; local b = a + 1; b` now correctly reports "fail" instead of "Infinite recursion" (all 8 operator patterns fixed). - True self-referential thunks (`local a = a + 1; a`) still correctly detected as infinite recursion. - `std.round(-0)` returns -0, matching go-jsonnet and jrsonnet. - All tests pass on JVM 2.12, 2.13, 3.3.
fe61a64 to
75c6cb9
Compare
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.
Motivation
Two bugs found via adversarial testing against go-jsonnet and jrsonnet:
Error thunk false "Infinite recursion": When an error thunk was forced inside a binary operator, sjsonnet incorrectly reported
"Infinite recursion detected (self-referential thunk)"instead of propagating the original error. Root cause:tryEvalCatchcaught thesjsonnet.Error(which isNonFatal), returnednull, and the main dispatch re-evaluated the thunk which still had itsevaluatingflag set totrue.std.round(-0)returned0:std.round(-0)returned positive zero instead of negative zero. Both go-jsonnet and jrsonnet correctly return-0. Root cause:x >= 0istruefor-0.0(IEEE 754:-0 == +0), so it tookmath.floor(-0 + 0.5) = 0.0(positive zero).local a = error "fail"; local b = a + 1; bInfinite recursion detectedfailfailfail✅std.round(-0)0-0-0-0✅std.round(0)0000✅Modification
Error thunk fix (
Val.scala):LazyExpr.value's computation intry/finallyto guarantee theevaluatingflag is reset even when the expression throws. This allows the main dispatch path to properly re-evaluate the thunk and propagate the original error.std.round fix (
MathModule.scala):else if (x == 0) xearly return before the positive/negative branches. Since0.0 == -0.0istruein IEEE 754, this catches both+0and-0and returnsxpreserving the original sign bit.Result
+,-,*,/, string concat,!,>, array concat) now correctly propagate the original error.local a = a + 1; a) still correctly detected.std.round(-0)returns-0,std.round(0)returns0— both signs preserved.std.atan2(std.round(0), -1) > 0istrue;std.atan2(std.round(-0), -1) < 0istrue.