diff --git a/benchmark/CMakeLists.txt b/benchmark/CMakeLists.txt index be40f43902092..a591a6a56ffc3 100644 --- a/benchmark/CMakeLists.txt +++ b/benchmark/CMakeLists.txt @@ -80,6 +80,7 @@ set(SWIFT_BENCH_MODULES single-source/DictionaryCompactMapValues single-source/DictionaryCopy single-source/DictionaryGroup + single-source/DictionaryIdentical single-source/DictionaryKeysContains single-source/DictionaryLiteralTest single-source/DictionaryOfAnyHashableStrings @@ -173,6 +174,7 @@ set(SWIFT_BENCH_MODULES # Disabled while layout prespecializations are experimental #single-source/SimpleArraySpecialization single-source/SequenceAlgos + single-source/SetIdentical single-source/SetTests single-source/SevenBoom single-source/Sim2DArray diff --git a/benchmark/single-source/DictionaryIdentical.swift b/benchmark/single-source/DictionaryIdentical.swift new file mode 100644 index 0000000000000..05226dfc2ed2c --- /dev/null +++ b/benchmark/single-source/DictionaryIdentical.swift @@ -0,0 +1,46 @@ +//===--- DictionaryIdentical.swift ----------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2025 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import TestsUtils + +public let benchmarks = [ + BenchmarkInfo(name: "DictionaryEqualUnique", runFunction: run_DictionaryEqualUnique, tags: [.validation, .api, .Dictionary]), + BenchmarkInfo(name: "DictionaryEqualShared", runFunction: run_DictionaryEqualShared, tags: [.validation, .api, .Dictionary]), + BenchmarkInfo(name: "DictionaryIdentical", runFunction: run_DictionaryIdentical, tags: [.validation, .api, .Dictionary]), +] + +@inline(never) +public func run_DictionaryEqualUnique(_ n: Int) { + let d1 = Dictionary(uniqueKeysWithValues: zip(1...n, 1...n)) + let d2 = Dictionary(uniqueKeysWithValues: zip(1...n, 1...n)) + for _ in 0 ..< 100_000 { + check(d1 == d2) + } +} + +@inline(never) +public func run_DictionaryEqualShared(_ n: Int) { + let d1 = Dictionary(uniqueKeysWithValues: zip(1...n, 1...n)) + let d2 = d1 + for _ in 0 ..< 100_000 { + check(d1 == d2) + } +} + +@inline(never) +public func run_DictionaryIdentical(_ n: Int) { + let d1 = Dictionary(uniqueKeysWithValues: zip(1...n, 1...n)) + let d2 = d1 + for _ in 0 ..< 100_000 { + check(d1.isIdentical(to: d2)) + } +} diff --git a/benchmark/single-source/SetIdentical.swift b/benchmark/single-source/SetIdentical.swift new file mode 100644 index 0000000000000..f975f81f243aa --- /dev/null +++ b/benchmark/single-source/SetIdentical.swift @@ -0,0 +1,46 @@ +//===--- SetIdentical.swift -----------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2025 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import TestsUtils + +public let benchmarks = [ + BenchmarkInfo(name: "SetEqualUnique", runFunction: run_SetEqualUnique, tags: [.validation, .api, .Set]), + BenchmarkInfo(name: "SetEqualShared", runFunction: run_SetEqualShared, tags: [.validation, .api, .Set]), + BenchmarkInfo(name: "SetIdentical", runFunction: run_SetIdentical, tags: [.validation, .api, .Set]), +] + +@inline(never) +public func run_SetEqualUnique(_ n: Int) { + let s1 = Set(1...n) + let s2 = Set(1...n) + for _ in 0 ..< 100_000 { + check(s1 == s2) + } +} + +@inline(never) +public func run_SetEqualShared(_ n: Int) { + let s1 = Set(1...n) + let s2 = s1 + for _ in 0 ..< 100_000 { + check(s1 == s2) + } +} + +@inline(never) +public func run_SetIdentical(_ n: Int) { + let s1 = Set(1...n) + let s2 = s1 + for _ in 0 ..< 100_000 { + check(s1.isIdentical(to: s2)) + } +} diff --git a/benchmark/utils/main.swift b/benchmark/utils/main.swift index 6d866f8092825..6538260df585e 100644 --- a/benchmark/utils/main.swift +++ b/benchmark/utils/main.swift @@ -75,6 +75,7 @@ import DictionaryBridgeToObjC import DictionaryCompactMapValues import DictionaryCopy import DictionaryGroup +import DictionaryIdentical import DictionaryKeysContains import DictionaryLiteralTest import DictionaryOfAnyHashableStrings @@ -177,6 +178,7 @@ import RomanNumbers import SIMDRandomMask import SIMDReduceInteger import SequenceAlgos +import SetIdentical import SetTests import SevenBoom import Sim2DArray @@ -276,6 +278,7 @@ register(DictionaryBridgeToObjC.benchmarks) register(DictionaryCompactMapValues.benchmarks) register(DictionaryCopy.benchmarks) register(DictionaryGroup.benchmarks) +register(DictionaryIdentical.benchmarks) register(DictionaryKeysContains.benchmarks) register(DictionaryLiteralTest.benchmarks) register(DictionaryOfAnyHashableStrings.benchmarks) @@ -379,6 +382,7 @@ register(RomanNumbers.benchmarks) register(SIMDRandomMask.benchmarks) register(SIMDReduceInteger.benchmarks) register(SequenceAlgos.benchmarks) +register(SetIdentical.benchmarks) register(SetTests.benchmarks) register(SevenBoom.benchmarks) register(Sim2DArray.benchmarks) diff --git a/stdlib/public/core/Dictionary.swift b/stdlib/public/core/Dictionary.swift index 6e867aedec522..ef67c73bb9b14 100644 --- a/stdlib/public/core/Dictionary.swift +++ b/stdlib/public/core/Dictionary.swift @@ -2154,3 +2154,43 @@ extension Dictionary.Index: @unchecked Sendable where Key: Sendable, Value: Sendable {} extension Dictionary.Iterator: @unchecked Sendable where Key: Sendable, Value: Sendable {} + +extension Dictionary { + /// Returns a boolean value indicating whether this dictionary is identical to + /// `other`. + /// + /// Two dictionary values are identical if there is no way to distinguish + /// between them. + /// + /// Comparing dictionaries this way includes comparing (normally) hidden + /// implementation details such as the memory location of any underlying + /// dictionary storage object. Therefore, identical dictionaries are + /// guaranteed to compare equal with `==`, but not all equal dictionaries are + /// considered identical. + /// + /// - Performance: O(1) + @_alwaysEmitIntoClient + public func isIdentical(to other: Self) -> Bool { +#if _runtime(_ObjC) + if + self._variant.isNative, + other._variant.isNative, + unsafe (self._variant.asNative._storage === other._variant.asNative._storage) + { + return true + } + if + !self._variant.isNative, + !other._variant.isNative, + self._variant.asCocoa.object === other._variant.asCocoa.object + { + return true + } +#else + if unsafe (self._variant.asNative._storage === other._variant.asNative._storage) { + return true + } +#endif + return false + } +} diff --git a/stdlib/public/core/Set.swift b/stdlib/public/core/Set.swift index fa79ca2858d0a..90ff27f5d0443 100644 --- a/stdlib/public/core/Set.swift +++ b/stdlib/public/core/Set.swift @@ -1658,3 +1658,42 @@ extension Set.Index: @unchecked Sendable where Element: Sendable { } extension Set.Iterator: @unchecked Sendable where Element: Sendable { } + +extension Set { + /// Returns a boolean value indicating whether this set is identical to + /// `other`. + /// + /// Two set values are identical if there is no way to distinguish between + /// them. + /// + /// Comparing sets this way includes comparing (normally) hidden + /// implementation details such as the memory location of any underlying set + /// storage object. Therefore, identical sets are guaranteed to compare equal + /// with `==`, but not all equal sets are considered identical. + /// + /// - Performance: O(1) + @_alwaysEmitIntoClient + public func isIdentical(to other: Self) -> Bool { +#if _runtime(_ObjC) + if + self._variant.isNative, + other._variant.isNative, + unsafe (self._variant.asNative._storage === other._variant.asNative._storage) + { + return true + } + if + !self._variant.isNative, + !other._variant.isNative, + self._variant.asCocoa.object === other._variant.asCocoa.object + { + return true + } +#else + if unsafe (self._variant.asNative._storage === other._variant.asNative._storage) { + return true + } +#endif + return false + } +} diff --git a/test/stdlib/DictionaryTests.swift b/test/stdlib/DictionaryTests.swift new file mode 100644 index 0000000000000..f30210616ce46 --- /dev/null +++ b/test/stdlib/DictionaryTests.swift @@ -0,0 +1,43 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2025 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// RUN: %target-run-simple-swift(-parse-as-library) +// REQUIRES: executable_test +// END. +// +//===----------------------------------------------------------------------===// + +import StdlibUnittest + +@main +enum DictionaryTests { + static func main() { + let testSuite = TestSuite("DictionaryTests") + testSuite.test("Identical", testIdentical) + runAllTests() + } + + static func testIdentical() { + let d1: Dictionary = ["a": 1, "b": 2, "c": 3] + expectTrue(d1.isIdentical(to: d1)) + + let d2: Dictionary = d1 + expectTrue(d1.isIdentical(to: d2)) + + var d3: Dictionary = d2 + d3.reserveCapacity(0) + expectFalse(d1.isIdentical(to: d3)) + + let d4: Dictionary = ["a": 1, "b": 2, "c": 3] + expectFalse(d1.isIdentical(to: d4)) + } +} diff --git a/test/stdlib/SetTests.swift b/test/stdlib/SetTests.swift new file mode 100644 index 0000000000000..d1ed0e6847922 --- /dev/null +++ b/test/stdlib/SetTests.swift @@ -0,0 +1,43 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2025 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// RUN: %target-run-simple-swift(-parse-as-library) +// REQUIRES: executable_test +// END. +// +//===----------------------------------------------------------------------===// + +import StdlibUnittest + +@main +enum SetTests { + static func main() { + let testSuite = TestSuite("SetTests") + testSuite.test("Identical", testIdentical) + runAllTests() + } + + static func testIdentical() { + let s1: Set = [0, 1, 2, 3] + expectTrue(s1.isIdentical(to: s1)) + + let s2: Set = s1 + expectTrue(s1.isIdentical(to: s2)) + + var s3: Set = s2 + s3.reserveCapacity(0) + expectFalse(s1.isIdentical(to: s3)) + + let s4: Set = [0, 1, 2, 3] + expectFalse(s1.isIdentical(to: s4)) + } +}