Skip to content

Commit 15c0202

Browse files
committed
[stdlib] dictionary identical
1 parent df3aa1e commit 15c0202

File tree

8 files changed

+248
-0
lines changed

8 files changed

+248
-0
lines changed

benchmark/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ set(SWIFT_BENCH_MODULES
8080
single-source/DictionaryCompactMapValues
8181
single-source/DictionaryCopy
8282
single-source/DictionaryGroup
83+
single-source/DictionaryIdentical
8384
single-source/DictionaryKeysContains
8485
single-source/DictionaryLiteralTest
8586
single-source/DictionaryOfAnyHashableStrings
@@ -175,6 +176,7 @@ set(SWIFT_BENCH_MODULES
175176
# Disabled while layout prespecializations are experimental
176177
#single-source/SimpleArraySpecialization
177178
single-source/SequenceAlgos
179+
single-source/SetIdentical
178180
single-source/SetTests
179181
single-source/SevenBoom
180182
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
@@ -75,6 +75,7 @@ import DictionaryBridgeToObjC
7575
import DictionaryCompactMapValues
7676
import DictionaryCopy
7777
import DictionaryGroup
78+
import DictionaryIdentical
7879
import DictionaryKeysContains
7980
import DictionaryLiteralTest
8081
import DictionaryOfAnyHashableStrings
@@ -179,6 +180,7 @@ import RomanNumbers
179180
import SIMDRandomMask
180181
import SIMDReduceInteger
181182
import SequenceAlgos
183+
import SetIdentical
182184
import SetTests
183185
import SevenBoom
184186
import Sim2DArray
@@ -277,6 +279,7 @@ register(DictionaryBridgeToObjC.benchmarks)
277279
register(DictionaryCompactMapValues.benchmarks)
278280
register(DictionaryCopy.benchmarks)
279281
register(DictionaryGroup.benchmarks)
282+
register(DictionaryIdentical.benchmarks)
280283
register(DictionaryKeysContains.benchmarks)
281284
register(DictionaryLiteralTest.benchmarks)
282285
register(DictionaryOfAnyHashableStrings.benchmarks)
@@ -382,6 +385,7 @@ register(RomanNumbers.benchmarks)
382385
register(SIMDRandomMask.benchmarks)
383386
register(SIMDReduceInteger.benchmarks)
384387
register(SequenceAlgos.benchmarks)
388+
register(SetIdentical.benchmarks)
385389
register(SetTests.benchmarks)
386390
register(SevenBoom.benchmarks)
387391
register(Sim2DArray.benchmarks)

stdlib/public/core/Dictionary.swift

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2154,3 +2154,64 @@ 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`
2175+
/// - `a == b` does not imply `a.isTriviallyIdentical(b)`
2176+
///
2177+
/// Values produced by copying the same value, with no intervening mutations,
2178+
/// will compare identical:
2179+
///
2180+
/// ```swift
2181+
/// let d = c
2182+
/// print(c.isTriviallyIdentical(to: d))
2183+
/// // Prints true
2184+
/// ```
2185+
///
2186+
/// Comparing dictionaries this way includes comparing (normally) hidden
2187+
/// implementation details such as the memory location of any underlying
2188+
/// dictionary storage object. Therefore, identical dictionaries are
2189+
/// guaranteed to compare equal with `==`, but not all equal dictionaries are
2190+
/// considered identical.
2191+
///
2192+
/// - Complexity: O(1)
2193+
@_alwaysEmitIntoClient
2194+
public func isTriviallyIdentical(to other: Self) -> Bool {
2195+
#if _runtime(_ObjC)
2196+
if
2197+
self._variant.isNative,
2198+
other._variant.isNative,
2199+
unsafe (self._variant.asNative._storage === other._variant.asNative._storage)
2200+
{
2201+
return true
2202+
}
2203+
if
2204+
!self._variant.isNative,
2205+
!other._variant.isNative,
2206+
self._variant.asCocoa.object === other._variant.asCocoa.object
2207+
{
2208+
return true
2209+
}
2210+
#else
2211+
if unsafe (self._variant.asNative._storage === other._variant.asNative._storage) {
2212+
return true
2213+
}
2214+
#endif
2215+
return false
2216+
}
2217+
}

stdlib/public/core/Set.swift

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1658,3 +1658,62 @@ 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`
1678+
/// - `a == b` does not imply `a.isTriviallyIdentical(b)`
1679+
///
1680+
/// Values produced by copying the same value, with no intervening mutations,
1681+
/// will compare identical:
1682+
///
1683+
/// ```swift
1684+
/// let d = c
1685+
/// print(c.isTriviallyIdentical(to: d))
1686+
/// // Prints true
1687+
/// ```
1688+
///
1689+
/// Comparing sets this way includes comparing (normally) hidden
1690+
/// implementation details such as the memory location of any underlying set
1691+
/// storage object. Therefore, identical sets are guaranteed to compare equal
1692+
/// with `==`, but not all equal sets are considered identical.
1693+
///
1694+
/// - Complexity: O(1)
1695+
@_alwaysEmitIntoClient
1696+
public func isTriviallyIdentical(to other: Self) -> Bool {
1697+
#if _runtime(_ObjC)
1698+
if
1699+
self._variant.isNative,
1700+
other._variant.isNative,
1701+
unsafe (self._variant.asNative._storage === other._variant.asNative._storage)
1702+
{
1703+
return true
1704+
}
1705+
if
1706+
!self._variant.isNative,
1707+
!other._variant.isNative,
1708+
self._variant.asCocoa.object === other._variant.asCocoa.object
1709+
{
1710+
return true
1711+
}
1712+
#else
1713+
if unsafe (self._variant.asNative._storage === other._variant.asNative._storage) {
1714+
return true
1715+
}
1716+
#endif
1717+
return false
1718+
}
1719+
}

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)