From b1b36b09accb02267c86713a7ad16595c76a6505 Mon Sep 17 00:00:00 2001 From: Daniel Chao Date: Fri, 30 Oct 2026 10:31:06 -0700 Subject: [PATCH] Fix computation of hash codes for Listing, Duration, DataSize (#1922) These are currently breaking the principle of: if two objects are `equals`, then they have the same hash code. --- .../java/org/pkl/core/runtime/VmDataSize.java | 3 +- .../java/org/pkl/core/runtime/VmDuration.java | 3 +- .../java/org/pkl/core/runtime/VmListing.java | 12 +----- .../LanguageSnippetTests/input/basic/set2.pkl | 38 +++++++++++++++++++ .../output/basic/set2.pcf | 14 +++++++ 5 files changed, 58 insertions(+), 12 deletions(-) create mode 100644 pkl-core/src/test/files/LanguageSnippetTests/input/basic/set2.pkl create mode 100644 pkl-core/src/test/files/LanguageSnippetTests/output/basic/set2.pcf diff --git a/pkl-core/src/main/java/org/pkl/core/runtime/VmDataSize.java b/pkl-core/src/main/java/org/pkl/core/runtime/VmDataSize.java index 3d1c89f25..566a4d72a 100644 --- a/pkl-core/src/main/java/org/pkl/core/runtime/VmDataSize.java +++ b/pkl-core/src/main/java/org/pkl/core/runtime/VmDataSize.java @@ -152,7 +152,8 @@ public final class VmDataSize extends VmValue implements Comparable @Override public int hashCode() { - return Double.hashCode(convertValueTo(DataSizeUnit.BYTES)); + // adding 0.0 normalizes -0.0 to 0.0, because -0.0 == 0.0 but their hash codes differ + return Double.hashCode(convertValueTo(DataSizeUnit.BYTES) + 0.0); } @Override diff --git a/pkl-core/src/main/java/org/pkl/core/runtime/VmDuration.java b/pkl-core/src/main/java/org/pkl/core/runtime/VmDuration.java index 99d6d2836..bba31a1af 100644 --- a/pkl-core/src/main/java/org/pkl/core/runtime/VmDuration.java +++ b/pkl-core/src/main/java/org/pkl/core/runtime/VmDuration.java @@ -149,7 +149,8 @@ public final class VmDuration extends VmValue implements Comparable @Override public int hashCode() { - return Double.hashCode(getValue(DurationUnit.NANOS)); + // adding 0.0 normalizes -0.0 to 0.0, because -0.0 == 0.0 but their hash codes differ + return Double.hashCode(getValue(DurationUnit.NANOS) + 0.0); } @Override diff --git a/pkl-core/src/main/java/org/pkl/core/runtime/VmListing.java b/pkl-core/src/main/java/org/pkl/core/runtime/VmListing.java index aa6a39a36..374d0cd13 100644 --- a/pkl-core/src/main/java/org/pkl/core/runtime/VmListing.java +++ b/pkl-core/src/main/java/org/pkl/core/runtime/VmListing.java @@ -212,18 +212,10 @@ public final class VmListing extends VmListingOrMapping { force(false); var result = 0; - var cursor = cachedValues.getEntries(); - - while (cursor.advance()) { - var key = cursor.getKey(); - if (key instanceof Identifier) continue; - - var value = cursor.getValue(); - //noinspection ConstantValue - assert value != null; + for (var cursor = elements(); cursor.advance(); ) { + var value = cursor.cachedValue(); result = 31 * result + value.hashCode(); } - cachedHash = result; return result; } diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input/basic/set2.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input/basic/set2.pkl new file mode 100644 index 000000000..5e734a34e --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/input/basic/set2.pkl @@ -0,0 +1,38 @@ +local l1: Listing = new { + new { x = 1 } + new { x = 2 } +} + +local l2: Listing = new { + new { x = 1 } + new { x = 2 } +} + +local m1: Mapping = new { + ["a"] { x = 1 } + ["b"] { x = 2 } +} + +local m2: Mapping = new { + ["a"] { x = 1 } + ["b"] { x = 2 } +} + +res1 { + first = l2[1] + // https://github.com/apple/pkl/issues/1920 + size = Set(l1, l2).length +} + +res2 { + first = m2["b"] + size = Set(m1, m2).length +} + +local bigList = IntSeq(1, 200).toList().map((i) -> i.b) + +// https://github.com/apple/pkl/issues/1921 +res3 = (bigList + List(0.b, -0.b)).toSet().length - bigList.length + +// https://github.com/apple/pkl/issues/1921 +res4 = (bigList + List(0.ms, -0.ms)).toSet().length - bigList.length diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/basic/set2.pcf b/pkl-core/src/test/files/LanguageSnippetTests/output/basic/set2.pcf new file mode 100644 index 000000000..6b547a54e --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/basic/set2.pcf @@ -0,0 +1,14 @@ +res1 { + first { + x = 2 + } + size = 1 +} +res2 { + first { + x = 2 + } + size = 1 +} +res3 = 1 +res4 = 1