From 039bb9592a7c9a197932d7d23e66c2980018921a Mon Sep 17 00:00:00 2001 From: He-Pin Date: Wed, 17 Jun 2026 15:33:37 +0800 Subject: [PATCH 1/2] fix: tryEagerEval does not force unevaluated lazy thunks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Motivation: The resolveAsDouble optimization called binding.value on scope bindings, which forced lazy thunk evaluation even when the result would be discarded (e.g., dead branches). This violated Jsonnet's lazy evaluation semantics — side effects like std.trace or error in unused bindings would execute when they should not. Modification: In resolveAsDouble, pattern match on the binding directly instead of calling binding.value. Only Val.Num (already evaluated numbers) are used; unevaluated lazy thunks return NaN (skip), preserving lazy semantics. Result: `local a = error "x"; local b = a + 1; if false then b else 0` now returns 0 without evaluating 'a', matching go-jsonnet and jrsonnet behavior. --- sjsonnet/src/sjsonnet/Evaluator.scala | 3 ++- .../new_test_suite/tryEagerEval_lazy_thunks.jsonnet | 6 ++++++ .../new_test_suite/tryEagerEval_lazy_thunks.jsonnet.golden | 1 + 3 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 sjsonnet/test/resources/new_test_suite/tryEagerEval_lazy_thunks.jsonnet create mode 100644 sjsonnet/test/resources/new_test_suite/tryEagerEval_lazy_thunks.jsonnet.golden diff --git a/sjsonnet/src/sjsonnet/Evaluator.scala b/sjsonnet/src/sjsonnet/Evaluator.scala index bb7632df9..b5dcfe079 100644 --- a/sjsonnet/src/sjsonnet/Evaluator.scala +++ b/sjsonnet/src/sjsonnet/Evaluator.scala @@ -223,8 +223,9 @@ class Evaluator( val idx = v.nameIdx if (idx < scope.length) { val binding = scope.bindings(idx) - if (binding != null) binding.value match { + if (binding != null) binding match { case n: Val.Num => n.rawDouble + case _: Val => Double.NaN case _ => Double.NaN } else Double.NaN diff --git a/sjsonnet/test/resources/new_test_suite/tryEagerEval_lazy_thunks.jsonnet b/sjsonnet/test/resources/new_test_suite/tryEagerEval_lazy_thunks.jsonnet new file mode 100644 index 000000000..1336ac49f --- /dev/null +++ b/sjsonnet/test/resources/new_test_suite/tryEagerEval_lazy_thunks.jsonnet @@ -0,0 +1,6 @@ +// Test that tryEagerEval preserves lazy semantics: unused local bindings with side effects +// should not be forced. This matches go-jsonnet and jrsonnet behavior. +std.assertEqual( + (local a = error "should not be evaluated"; local b = a + 1; if false then b else 0), + 0 +) diff --git a/sjsonnet/test/resources/new_test_suite/tryEagerEval_lazy_thunks.jsonnet.golden b/sjsonnet/test/resources/new_test_suite/tryEagerEval_lazy_thunks.jsonnet.golden new file mode 100644 index 000000000..27ba77dda --- /dev/null +++ b/sjsonnet/test/resources/new_test_suite/tryEagerEval_lazy_thunks.jsonnet.golden @@ -0,0 +1 @@ +true From c7f7ecf6ee8fe827471e51baf0aaf99d6534c146 Mon Sep 17 00:00:00 2001 From: He-Pin Date: Thu, 18 Jun 2026 11:26:33 +0800 Subject: [PATCH 2/2] chore: retrigger CI for cancelled native build