Skip to content

Commit 747ec12

Browse files
authored
Merge pull request #82439 from vanvoorden/dictionary-identical
[SE-0494][StdLib] Add `isTriviallyIdentical(to:)` Methods to Dictionary and Set
2 parents 05d4565 + e8055f6 commit 747ec12

File tree

8 files changed

+246
-0
lines changed

8 files changed

+246
-0
lines changed

benchmark/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ set(SWIFT_BENCH_MODULES
8383
single-source/DictionaryCompactMapValues
8484
single-source/DictionaryCopy
8585
single-source/DictionaryGroup
86+
single-source/DictionaryIdentical
8687
single-source/DictionaryKeysContains
8788
single-source/DictionaryLiteralTest
8889
single-source/DictionaryOfAnyHashableStrings
@@ -178,6 +179,7 @@ set(SWIFT_BENCH_MODULES
178179
# Disabled while layout prespecializations are experimental
179180
#single-source/SimpleArraySpecialization
180181
single-source/SequenceAlgos
182+
single-source/SetIdentical
181183
single-source/SetTests
182184
single-source/SevenBoom
183185
single-source/Sim2DArray
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
//===--- DictionaryIdentical.swift ----------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2025 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import TestsUtils
14+
15+
public let benchmarks = [
16+
BenchmarkInfo(name: "DictionaryEqualUnique", runFunction: run_DictionaryEqualUnique, tags: [.validation, .api, .Dictionary]),
17+
BenchmarkInfo(name: "DictionaryEqualShared", runFunction: run_DictionaryEqualShared, tags: [.validation, .api, .Dictionary]),
18+
BenchmarkInfo(name: "DictionaryIdentical", runFunction: run_DictionaryIdentical, tags: [.validation, .api, .Dictionary]),
19+
]
20+
21+
@inline(never)
22+
public func run_DictionaryEqualUnique(_ n: Int) {
23+
let d1 = Dictionary(uniqueKeysWithValues: zip(1...n, 1...n))
24+
let d2 = Dictionary(uniqueKeysWithValues: zip(1...n, 1...n))
25+
for _ in 0 ..< 100_000 {
26+
check(d1 == d2)
27+
}
28+
}
29+
30+
@inline(never)
31+
public func run_DictionaryEqualShared(_ n: Int) {
32+
let d1 = Dictionary(uniqueKeysWithValues: zip(1...n, 1...n))
33+
let d2 = d1
34+
for _ in 0 ..< 100_000 {
35+
check(d1 == d2)
36+
}
37+
}
38+
39+
@inline(never)
40+
public func run_DictionaryIdentical(_ n: Int) {
41+
let d1 = Dictionary(uniqueKeysWithValues: zip(1...n, 1...n))
42+
let d2 = d1
43+
for _ in 0 ..< 100_000 {
44+
check(d1.isTriviallyIdentical(to: d2))
45+
}
46+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
//===--- SetIdentical.swift -----------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2025 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import TestsUtils
14+
15+
public let benchmarks = [
16+
BenchmarkInfo(name: "SetEqualUnique", runFunction: run_SetEqualUnique, tags: [.validation, .api, .Set]),
17+
BenchmarkInfo(name: "SetEqualShared", runFunction: run_SetEqualShared, tags: [.validation, .api, .Set]),
18+
BenchmarkInfo(name: "SetIdentical", runFunction: run_SetIdentical, tags: [.validation, .api, .Set]),
19+
]
20+
21+
@inline(never)
22+
public func run_SetEqualUnique(_ n: Int) {
23+
let s1 = Set(1...n)
24+
let s2 = Set(1...n)
25+
for _ in 0 ..< 100_000 {
26+
check(s1 == s2)
27+
}
28+
}
29+
30+
@inline(never)
31+
public func run_SetEqualShared(_ n: Int) {
32+
let s1 = Set(1...n)
33+
let s2 = s1
34+
for _ in 0 ..< 100_000 {
35+
check(s1 == s2)
36+
}
37+
}
38+
39+
@inline(never)
40+
public func run_SetIdentical(_ n: Int) {
41+
let s1 = Set(1...n)
42+
let s2 = s1
43+
for _ in 0 ..< 100_000 {
44+
check(s1.isTriviallyIdentical(to: s2))
45+
}
46+
}

benchmark/utils/main.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ import DictionaryBridgeToObjC
7878
import DictionaryCompactMapValues
7979
import DictionaryCopy
8080
import DictionaryGroup
81+
import DictionaryIdentical
8182
import DictionaryKeysContains
8283
import DictionaryLiteralTest
8384
import DictionaryOfAnyHashableStrings
@@ -182,6 +183,7 @@ import RomanNumbers
182183
import SIMDRandomMask
183184
import SIMDReduceInteger
184185
import SequenceAlgos
186+
import SetIdentical
185187
import SetTests
186188
import SevenBoom
187189
import Sim2DArray
@@ -283,6 +285,7 @@ register(DictionaryBridgeToObjC.benchmarks)
283285
register(DictionaryCompactMapValues.benchmarks)
284286
register(DictionaryCopy.benchmarks)
285287
register(DictionaryGroup.benchmarks)
288+
register(DictionaryIdentical.benchmarks)
286289
register(DictionaryKeysContains.benchmarks)
287290
register(DictionaryLiteralTest.benchmarks)
288291
register(DictionaryOfAnyHashableStrings.benchmarks)
@@ -388,6 +391,7 @@ register(RomanNumbers.benchmarks)
388391
register(SIMDRandomMask.benchmarks)
389392
register(SIMDReduceInteger.benchmarks)
390393
register(SequenceAlgos.benchmarks)
394+
register(SetIdentical.benchmarks)
391395
register(SetTests.benchmarks)
392396
register(SevenBoom.benchmarks)
393397
register(Sim2DArray.benchmarks)

stdlib/public/core/Dictionary.swift

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2154,3 +2154,63 @@ extension Dictionary.Index: @unchecked Sendable
21542154
where Key: Sendable, Value: Sendable {}
21552155
extension Dictionary.Iterator: @unchecked Sendable
21562156
where Key: Sendable, Value: Sendable {}
2157+
2158+
extension Dictionary {
2159+
/// Returns a boolean value indicating whether this dictionary is identical to
2160+
/// `other`.
2161+
///
2162+
/// Two dictionary values are identical if there is no way to distinguish
2163+
/// between them.
2164+
///
2165+
/// For any values `a`, `b`, and `c`:
2166+
///
2167+
/// - `a.isTriviallyIdentical(to: a)` is always `true`. (Reflexivity)
2168+
/// - `a.isTriviallyIdentical(to: b)` implies `b.isTriviallyIdentical(to: a)`.
2169+
/// (Symmetry)
2170+
/// - If `a.isTriviallyIdentical(to: b)` and `b.isTriviallyIdentical(to: c)`
2171+
/// are both `true`, then `a.isTriviallyIdentical(to: c)` is also `true`.
2172+
/// (Transitivity)
2173+
/// - If `a` and `b` are `Equatable`, then `a.isTriviallyIdentical(b)` implies
2174+
/// `a == b`. `a == b` does not imply `a.isTriviallyIdentical(b)`
2175+
///
2176+
/// Values produced by copying the same value, with no intervening mutations,
2177+
/// will compare identical:
2178+
///
2179+
/// ```swift
2180+
/// let d = c
2181+
/// print(c.isTriviallyIdentical(to: d))
2182+
/// // Prints true
2183+
/// ```
2184+
///
2185+
/// Comparing dictionaries this way includes comparing (normally) hidden
2186+
/// implementation details such as the memory location of any underlying
2187+
/// dictionary storage object. Therefore, identical dictionaries are
2188+
/// guaranteed to compare equal with `==`, but not all equal dictionaries are
2189+
/// considered identical.
2190+
///
2191+
/// - Complexity: O(1)
2192+
@_alwaysEmitIntoClient
2193+
public func isTriviallyIdentical(to other: Self) -> Bool {
2194+
#if _runtime(_ObjC)
2195+
if
2196+
self._variant.isNative,
2197+
other._variant.isNative,
2198+
unsafe (self._variant.asNative._storage === other._variant.asNative._storage)
2199+
{
2200+
return true
2201+
}
2202+
if
2203+
!self._variant.isNative,
2204+
!other._variant.isNative,
2205+
self._variant.asCocoa.object === other._variant.asCocoa.object
2206+
{
2207+
return true
2208+
}
2209+
#else
2210+
if unsafe (self._variant.asNative._storage === other._variant.asNative._storage) {
2211+
return true
2212+
}
2213+
#endif
2214+
return false
2215+
}
2216+
}

stdlib/public/core/Set.swift

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1658,3 +1658,61 @@ extension Set.Index: @unchecked Sendable
16581658
where Element: Sendable { }
16591659
extension Set.Iterator: @unchecked Sendable
16601660
where Element: Sendable { }
1661+
1662+
extension Set {
1663+
/// Returns a boolean value indicating whether this set is identical to
1664+
/// `other`.
1665+
///
1666+
/// Two set values are identical if there is no way to distinguish between
1667+
/// them.
1668+
///
1669+
/// For any values `a`, `b`, and `c`:
1670+
///
1671+
/// - `a.isTriviallyIdentical(to: a)` is always `true`. (Reflexivity)
1672+
/// - `a.isTriviallyIdentical(to: b)` implies `b.isTriviallyIdentical(to: a)`.
1673+
/// (Symmetry)
1674+
/// - If `a.isTriviallyIdentical(to: b)` and `b.isTriviallyIdentical(to: c)`
1675+
/// are both `true`, then `a.isTriviallyIdentical(to: c)` is also `true`.
1676+
/// (Transitivity)
1677+
/// - `a.isTriviallyIdentical(b)` implies `a == b`. `a == b` does not imply `a.isTriviallyIdentical(b)`
1678+
///
1679+
/// Values produced by copying the same value, with no intervening mutations,
1680+
/// will compare identical:
1681+
///
1682+
/// ```swift
1683+
/// let d = c
1684+
/// print(c.isTriviallyIdentical(to: d))
1685+
/// // Prints true
1686+
/// ```
1687+
///
1688+
/// Comparing sets this way includes comparing (normally) hidden
1689+
/// implementation details such as the memory location of any underlying set
1690+
/// storage object. Therefore, identical sets are guaranteed to compare equal
1691+
/// with `==`, but not all equal sets are considered identical.
1692+
///
1693+
/// - Complexity: O(1)
1694+
@_alwaysEmitIntoClient
1695+
public func isTriviallyIdentical(to other: Self) -> Bool {
1696+
#if _runtime(_ObjC)
1697+
if
1698+
self._variant.isNative,
1699+
other._variant.isNative,
1700+
unsafe (self._variant.asNative._storage === other._variant.asNative._storage)
1701+
{
1702+
return true
1703+
}
1704+
if
1705+
!self._variant.isNative,
1706+
!other._variant.isNative,
1707+
self._variant.asCocoa.object === other._variant.asCocoa.object
1708+
{
1709+
return true
1710+
}
1711+
#else
1712+
if unsafe (self._variant.asNative._storage === other._variant.asNative._storage) {
1713+
return true
1714+
}
1715+
#endif
1716+
return false
1717+
}
1718+
}

validation-test/stdlib/Dictionary.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5789,6 +5789,21 @@ DictionaryTestSuite.test("BulkLoadingInitializer.Nonunique") {
57895789
}
57905790
}
57915791

5792+
DictionaryTestSuite.test("Identical") {
5793+
let d1: Dictionary = ["a": 1, "b": 2, "c": 3]
5794+
expectTrue(d1.isTriviallyIdentical(to: d1))
5795+
5796+
let d2: Dictionary = d1
5797+
expectTrue(d1.isTriviallyIdentical(to: d2))
5798+
5799+
var d3: Dictionary = d2
5800+
d3.reserveCapacity(0)
5801+
expectFalse(d1.isTriviallyIdentical(to: d3))
5802+
5803+
let d4: Dictionary = ["a": 1, "b": 2, "c": 3]
5804+
expectFalse(d1.isTriviallyIdentical(to: d4))
5805+
}
5806+
57925807
DictionaryTestSuite.setUp {
57935808
#if _runtime(_ObjC)
57945809
// Exercise ARC's autoreleased return value optimization in Foundation.

validation-test/stdlib/Set.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4895,4 +4895,19 @@ if #available(SwiftStdlib 5.1, *) {
48954895
}
48964896
#endif
48974897

4898+
SetTestSuite.test("Identical") {
4899+
let s1: Set = [0, 1, 2, 3]
4900+
expectTrue(s1.isTriviallyIdentical(to: s1))
4901+
4902+
let s2: Set = s1
4903+
expectTrue(s1.isTriviallyIdentical(to: s2))
4904+
4905+
var s3: Set = s2
4906+
s3.reserveCapacity(0)
4907+
expectFalse(s1.isTriviallyIdentical(to: s3))
4908+
4909+
let s4: Set = [0, 1, 2, 3]
4910+
expectFalse(s1.isTriviallyIdentical(to: s4))
4911+
}
4912+
48984913
runAllTests()

0 commit comments

Comments
 (0)