Skip to content

Commit df995de

Browse files
[stdlib] Make KeyValuePairs fully inlinable (swiftlang#19502)
* Move KVP into its own Swift file. Make it fully inlineable. * Make _makeCollectionDescription an extension. Add KeyValue equivalent.
1 parent 5a29812 commit df995de

File tree

10 files changed

+194
-156
lines changed

10 files changed

+194
-156
lines changed

stdlib/public/core/Array.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1302,14 +1302,14 @@ extension Array: CustomReflectable {
13021302
extension Array: CustomStringConvertible, CustomDebugStringConvertible {
13031303
/// A textual representation of the array and its elements.
13041304
public var description: String {
1305-
return _makeCollectionDescription(for: self, withTypeName: nil)
1305+
return _makeCollectionDescription()
13061306
}
13071307

13081308
/// A textual representation of the array and its elements, suitable for
13091309
/// debugging.
13101310
public var debugDescription: String {
13111311
// Always show sugared representation for Arrays.
1312-
return _makeCollectionDescription(for: self, withTypeName: nil)
1312+
return _makeCollectionDescription()
13131313
}
13141314
}
13151315

stdlib/public/core/ArrayShared.swift

Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -57,29 +57,32 @@ func _deallocateUninitializedArray<Element>(
5757
}
5858

5959

60-
61-
// Utility method for collections that wish to implement CustomStringConvertible
62-
// and CustomDebugStringConvertible using a bracketed list of elements,
63-
// like an array.
64-
internal func _makeCollectionDescription<C: Collection>
65-
(for items: C, withTypeName type: String?) -> String {
66-
var result = ""
67-
if let type = type {
68-
result += "\(type)(["
69-
} else {
70-
result += "["
71-
}
72-
var first = true
73-
for item in items {
74-
if first {
75-
first = false
60+
extension Collection {
61+
// Utility method for collections that wish to implement
62+
// CustomStringConvertible and CustomDebugStringConvertible using a bracketed
63+
// list of elements, like an array.
64+
internal func _makeCollectionDescription(
65+
withTypeName type: String? = nil
66+
) -> String {
67+
var result = ""
68+
if let type = type {
69+
result += "\(type)(["
7670
} else {
77-
result += ", "
71+
result += "["
72+
}
73+
74+
var first = true
75+
for item in self {
76+
if first {
77+
first = false
78+
} else {
79+
result += ", "
80+
}
81+
debugPrint(item, terminator: "", to: &result)
7882
}
79-
debugPrint(item, terminator: "", to: &result)
83+
result += type != nil ? "])" : "]"
84+
return result
8085
}
81-
result += type != nil ? "])" : "]"
82-
return result
8386
}
8487

8588
extension _ArrayBufferProtocol {

stdlib/public/core/ArraySlice.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1121,13 +1121,13 @@ extension ArraySlice: CustomReflectable {
11211121
extension ArraySlice: CustomStringConvertible, CustomDebugStringConvertible {
11221122
/// A textual representation of the array and its elements.
11231123
public var description: String {
1124-
return _makeCollectionDescription(for: self, withTypeName: nil)
1124+
return _makeCollectionDescription()
11251125
}
11261126

11271127
/// A textual representation of the array and its elements, suitable for
11281128
/// debugging.
11291129
public var debugDescription: String {
1130-
return _makeCollectionDescription(for: self, withTypeName: "ArraySlice")
1130+
return _makeCollectionDescription(withTypeName: "ArraySlice")
11311131
}
11321132
}
11331133

stdlib/public/core/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ set(SWIFTLIB_ESSENTIAL
8686
IntegerTypes.swift.gyb
8787
Join.swift
8888
KeyPath.swift
89+
KeyValuePairs.swift
8990
LazyCollection.swift
9091
LazySequence.swift
9192
LifetimeManager.swift

stdlib/public/core/ContiguousArray.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -947,13 +947,13 @@ extension ContiguousArray: CustomReflectable {
947947
extension ContiguousArray: CustomStringConvertible, CustomDebugStringConvertible {
948948
/// A textual representation of the array and its elements.
949949
public var description: String {
950-
return _makeCollectionDescription(for: self, withTypeName: nil)
950+
return _makeCollectionDescription()
951951
}
952952

953953
/// A textual representation of the array and its elements, suitable for
954954
/// debugging.
955955
public var debugDescription: String {
956-
return _makeCollectionDescription(for: self, withTypeName: "ContiguousArray")
956+
return _makeCollectionDescription(withTypeName: "ContiguousArray")
957957
}
958958
}
959959

stdlib/public/core/Dictionary.swift

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1391,13 +1391,11 @@ extension Dictionary {
13911391
}
13921392

13931393
public var description: String {
1394-
return _makeCollectionDescription(for: self, withTypeName: nil)
1394+
return _makeCollectionDescription()
13951395
}
13961396

13971397
public var debugDescription: String {
1398-
return _makeCollectionDescription(
1399-
for: self,
1400-
withTypeName: "Dictionary.Keys")
1398+
return _makeCollectionDescription(withTypeName: "Dictionary.Keys")
14011399
}
14021400
}
14031401

@@ -1463,13 +1461,11 @@ extension Dictionary {
14631461
}
14641462

14651463
public var description: String {
1466-
return _makeCollectionDescription(for: self, withTypeName: nil)
1464+
return _makeCollectionDescription()
14671465
}
14681466

14691467
public var debugDescription: String {
1470-
return _makeCollectionDescription(
1471-
for: self,
1472-
withTypeName: "Dictionary.Values")
1468+
return _makeCollectionDescription(withTypeName: "Dictionary.Values")
14731469
}
14741470
}
14751471
}
@@ -1604,12 +1600,18 @@ internal struct _DictionaryAnyHashableBox<Key: Hashable, Value: Hashable>
16041600
}
16051601
}
16061602

1607-
extension Dictionary: CustomStringConvertible, CustomDebugStringConvertible {
1608-
internal func _makeDescription() -> String {
1609-
if count == 0 {
1603+
extension Collection {
1604+
// Utility method for KV collections that wish to implement
1605+
// CustomStringConvertible and CustomDebugStringConvertible using a bracketed
1606+
// list of elements.
1607+
// FIXME: Doesn't use the withTypeName argument yet
1608+
internal func _makeKeyValuePairDescription<K, V>(
1609+
withTypeName type: String? = nil
1610+
) -> String where Element == (key: K, value: V) {
1611+
if self.count == 0 {
16101612
return "[:]"
16111613
}
1612-
1614+
16131615
var result = "["
16141616
var first = true
16151617
for (k, v) in self {
@@ -1625,16 +1627,18 @@ extension Dictionary: CustomStringConvertible, CustomDebugStringConvertible {
16251627
result += "]"
16261628
return result
16271629
}
1630+
}
16281631

1632+
extension Dictionary: CustomStringConvertible, CustomDebugStringConvertible {
16291633
/// A string that represents the contents of the dictionary.
16301634
public var description: String {
1631-
return _makeDescription()
1635+
return _makeKeyValuePairDescription()
16321636
}
16331637

16341638
/// A string that represents the contents of the dictionary, suitable for
16351639
/// debugging.
16361640
public var debugDescription: String {
1637-
return _makeDescription()
1641+
return _makeKeyValuePairDescription()
16381642
}
16391643
}
16401644

stdlib/public/core/GroupInfo.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@
7676
"WriteBackMutableSlice.swift",
7777
"UnfoldSequence.swift",
7878
"UIntBuffer.swift",
79+
"KeyValuePairs.swift",
7980
{
8081
"Type-erased": [
8182
"ExistentialCollection.swift"
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
//===--- KeyValuePairs.swift ----------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2018 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+
/// A lightweight collection of key-value pairs.
14+
///
15+
/// Use a `KeyValuePairs` instance when you need an ordered collection of
16+
/// key-value pairs and don't require the fast key lookup that the
17+
/// `Dictionary` type provides. Unlike key-value pairs in a true dictionary,
18+
/// neither the key nor the value of a `KeyValuePairs` instance must
19+
/// conform to the `Hashable` protocol.
20+
///
21+
/// You initialize a `KeyValuePairs` instance using a Swift dictionary
22+
/// literal. Besides maintaining the order of the original dictionary literal,
23+
/// `KeyValuePairs` also allows duplicates keys. For example:
24+
///
25+
/// let recordTimes: KeyValuePairs = ["Florence Griffith-Joyner": 10.49,
26+
/// "Evelyn Ashford": 10.76,
27+
/// "Evelyn Ashford": 10.79,
28+
/// "Marlies Gohr": 10.81]
29+
/// print(recordTimes.first!)
30+
/// // Prints "("Florence Griffith-Joyner", 10.49)"
31+
///
32+
/// Some operations that are efficient on a dictionary are slower when using
33+
/// `KeyValuePairs`. In particular, to find the value matching a key, you
34+
/// must search through every element of the collection. The call to
35+
/// `firstIndex(where:)` in the following example must traverse the whole
36+
/// collection to find the element that matches the predicate:
37+
///
38+
/// let runner = "Marlies Gohr"
39+
/// if let index = recordTimes.firstIndex(where: { $0.0 == runner }) {
40+
/// let time = recordTimes[index].1
41+
/// print("\(runner) set a 100m record of \(time) seconds.")
42+
/// } else {
43+
/// print("\(runner) couldn't be found in the records.")
44+
/// }
45+
/// // Prints "Marlies Gohr set a 100m record of 10.81 seconds."
46+
///
47+
/// Key-Value Pairs as a Function Parameter
48+
/// ---------------------------------------
49+
///
50+
/// When calling a function with a `KeyValuePairs` parameter, you can pass
51+
/// a Swift dictionary literal without causing a `Dictionary` to be created.
52+
/// This capability can be especially important when the order of elements in
53+
/// the literal is significant.
54+
///
55+
/// For example, you could create an `IntPairs` structure that holds a list of
56+
/// two-integer tuples and use an initializer that accepts a
57+
/// `KeyValuePairs` instance.
58+
///
59+
/// struct IntPairs {
60+
/// var elements: [(Int, Int)]
61+
///
62+
/// init(_ elements: KeyValuePairs<Int, Int>) {
63+
/// self.elements = Array(elements)
64+
/// }
65+
/// }
66+
///
67+
/// When you're ready to create a new `IntPairs` instance, use a dictionary
68+
/// literal as the parameter to the `IntPairs` initializer. The
69+
/// `KeyValuePairs` instance preserves the order of the elements as
70+
/// passed.
71+
///
72+
/// let pairs = IntPairs([1: 2, 1: 1, 3: 4, 2: 1])
73+
/// print(pairs.elements)
74+
/// // Prints "[(1, 2), (1, 1), (3, 4), (2, 1)]"
75+
@_fixed_layout // trivial-implementation
76+
public struct KeyValuePairs<Key, Value> : ExpressibleByDictionaryLiteral {
77+
@usableFromInline // trivial-implementation
78+
internal let _elements: [(Key, Value)]
79+
80+
/// Creates a new `KeyValuePairs` instance from the given dictionary
81+
/// literal.
82+
///
83+
/// The order of the key-value pairs is kept intact in the resulting
84+
/// `KeyValuePairs` instance.
85+
@inlinable // trivial-implementation
86+
public init(dictionaryLiteral elements: (Key, Value)...) {
87+
self._elements = elements
88+
}
89+
}
90+
91+
/// `Collection` conformance that allows `KeyValuePairs` to
92+
/// interoperate with the rest of the standard library.
93+
extension KeyValuePairs : RandomAccessCollection {
94+
/// The element type of a `KeyValuePairs`: a tuple containing an
95+
/// individual key-value pair.
96+
public typealias Element = (key: Key, value: Value)
97+
public typealias Index = Int
98+
public typealias Indices = Range<Int>
99+
public typealias SubSequence = Slice<KeyValuePairs>
100+
101+
/// The position of the first element in a nonempty collection.
102+
///
103+
/// If the `KeyValuePairs` instance is empty, `startIndex` is equal to
104+
/// `endIndex`.
105+
@inlinable // trivial-implementation
106+
public var startIndex: Index { return 0 }
107+
108+
/// The collection's "past the end" position---that is, the position one
109+
/// greater than the last valid subscript argument.
110+
///
111+
/// If the `KeyValuePairs` instance is empty, `endIndex` is equal to
112+
/// `startIndex`.
113+
@inlinable // trivial-implementation
114+
public var endIndex: Index { return _elements.endIndex }
115+
116+
/// Accesses the element at the specified position.
117+
///
118+
/// - Parameter position: The position of the element to access. `position`
119+
/// must be a valid index of the collection that is not equal to the
120+
/// `endIndex` property.
121+
/// - Returns: The key-value pair at position `position`.
122+
@inlinable // trivial-implementation
123+
public subscript(position: Index) -> Element {
124+
return _elements[position]
125+
}
126+
}
127+
128+
extension KeyValuePairs: CustomStringConvertible {
129+
/// A string that represents the contents of the dictionary.
130+
public var description: String {
131+
return _makeKeyValuePairDescription()
132+
}
133+
}
134+
135+
extension KeyValuePairs: CustomDebugStringConvertible {
136+
/// A string that represents the contents of the dictionary, suitable for
137+
/// debugging.
138+
public var debugDescription: String {
139+
return _makeKeyValuePairDescription()
140+
}
141+
}

0 commit comments

Comments
 (0)