Skip to content

Commit 1ddf99a

Browse files
committed
[stdlib] Set, Dictionary: Unify casting operations
1 parent ceb703b commit 1ddf99a

File tree

4 files changed

+148
-123
lines changed

4 files changed

+148
-123
lines changed

stdlib/public/core/DictionaryCasting.swift

Lines changed: 56 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,30 @@
1212

1313
//===--- Compiler conversion/casting entry points for Dictionary<K, V> ----===//
1414

15+
extension Dictionary {
16+
@_alwaysEmitIntoClient @inlinable // Introduced in 5.1
17+
@inline(__always)
18+
internal init?<C: Collection>(
19+
_mapping source: C,
20+
allowingDuplicates: Bool,
21+
transform: (C.Element) -> (key: Key, value: Value)?
22+
) {
23+
var target = _NativeDictionary<Key, Value>(capacity: source.count)
24+
if allowingDuplicates {
25+
for member in source {
26+
guard let (key, value) = transform(member) else { return nil }
27+
target._unsafeUpdate(key: key, value: value)
28+
}
29+
} else {
30+
for member in source {
31+
guard let (key, value) = transform(member) else { return nil }
32+
target._unsafeInsertNew(key: key, value: value)
33+
}
34+
}
35+
self.init(_native: target)
36+
}
37+
}
38+
1539
/// Perform a non-bridged upcast that always succeeds.
1640
///
1741
/// - Precondition: `BaseKey` and `BaseValue` are base classes or base `@objc`
@@ -21,18 +45,15 @@
2145
public func _dictionaryUpCast<DerivedKey, DerivedValue, BaseKey, BaseValue>(
2246
_ source: Dictionary<DerivedKey, DerivedValue>
2347
) -> Dictionary<BaseKey, BaseValue> {
24-
// String and NSString have different concepts of equality, so
25-
// NSString-keyed Dictionaries may generate key collisions when "upcasted"
26-
// to String. See rdar://problem/35995647
27-
let allowDuplicates = (BaseKey.self == String.self)
28-
var builder = _NativeDictionary<BaseKey, BaseValue>(capacity: source.count)
29-
for (k, v) in source {
30-
builder.insertWithGuaranteedCapacity(
31-
key: k as! BaseKey,
32-
value: v as! BaseValue,
33-
allowingDuplicates: allowDuplicates)
34-
}
35-
return Dictionary(_native: builder)
48+
return Dictionary(
49+
_mapping: source,
50+
// String and NSString have different concepts of equality, so
51+
// NSString-keyed Dictionaries may generate key collisions when "upcasted"
52+
// to String. See rdar://problem/35995647
53+
allowingDuplicates: (BaseKey.self == String.self)
54+
) { k, v in
55+
(k as! BaseKey, v as! BaseValue)
56+
}!
3657
}
3758

3859
/// Called by the casting machinery.
@@ -77,19 +98,15 @@ public func _dictionaryDownCast<BaseKey, BaseValue, DerivedKey, DerivedValue>(
7798
// because we rely on as! to generate nice runtime errors when the downcast
7899
// fails.
79100

80-
// String and NSString have different concepts of equality, so
81-
// NSString-keyed Dictionaries may generate key collisions when downcasted
82-
// to String. See rdar://problem/35995647
83-
let allowDuplicates = (DerivedKey.self == String.self)
84-
var builder = _NativeDictionary<DerivedKey, DerivedValue>(
85-
capacity: source.count)
86-
for (k, v) in source {
87-
builder.insertWithGuaranteedCapacity(
88-
key: k as! DerivedKey,
89-
value: v as! DerivedValue,
90-
allowingDuplicates: allowDuplicates)
91-
}
92-
return Dictionary(_native: builder)
101+
return Dictionary(
102+
_mapping: source,
103+
// String and NSString have different concepts of equality, so
104+
// NSString-keyed Dictionaries may generate key collisions when downcasted
105+
// to String. See rdar://problem/35995647
106+
allowingDuplicates: (DerivedKey.self == String.self)
107+
) { k, v in
108+
(k as! DerivedKey, v as! DerivedValue)
109+
}!
93110
}
94111

95112
/// Called by the casting machinery.
@@ -120,21 +137,19 @@ public func _dictionaryDownCastConditional<
120137
>(
121138
_ source: Dictionary<BaseKey, BaseValue>
122139
) -> Dictionary<DerivedKey, DerivedValue>? {
123-
124-
// String and NSString have different concepts of equality, so
125-
// NSString-keyed Dictionaries may generate key collisions when downcasted
126-
// to String. See rdar://problem/35995647
127-
let allowDuplicates = (DerivedKey.self == String.self)
128-
129-
var builder = _NativeDictionary<DerivedKey, DerivedValue>(
130-
capacity: source.count)
131-
for (k, v) in source {
132-
guard let k1 = k as? DerivedKey, let v1 = v as? DerivedValue
133-
else { return nil }
134-
builder.insertWithGuaranteedCapacity(
135-
key: k1,
136-
value: v1,
137-
allowingDuplicates: allowDuplicates)
140+
return Dictionary(
141+
_mapping: source,
142+
// String and NSString have different concepts of equality, so
143+
// NSString-keyed Dictionaries may generate key collisions when downcasted
144+
// to String. See rdar://problem/35995647
145+
allowingDuplicates: (DerivedKey.self == String.self)
146+
) { k, v in
147+
guard
148+
let key = k as? DerivedKey,
149+
let value = v as? DerivedValue
150+
else {
151+
return nil
152+
}
153+
return (key, value)
138154
}
139-
return Dictionary(_native: builder)
140155
}

stdlib/public/core/NativeDictionary.swift

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -460,28 +460,6 @@ internal func KEY_TYPE_OF_DICTIONARY_VIOLATES_HASHABLE_REQUIREMENTS(
460460
}
461461

462462
extension _NativeDictionary { // Insertions
463-
/// Insert a new element into uniquely held storage. Storage must be uniquely
464-
/// referenced with adequate capacity. If `allowingDuplicates` is false,
465-
/// `element` must not be already present in the dictionary.
466-
@_alwaysEmitIntoClient @inlinable // Introduced in 5.1
467-
internal mutating func insertWithGuaranteedCapacity(
468-
key: __owned Key,
469-
value: __owned Value,
470-
allowingDuplicates: Bool
471-
) {
472-
_precondition(count < capacity)
473-
if !allowingDuplicates {
474-
_unsafeInsertNew(key: key, value: value)
475-
return
476-
}
477-
let (bucket, found) = find(key)
478-
if found {
479-
(_values + bucket.offset).pointee = value
480-
} else {
481-
_insert(at: bucket, key: key, value: value)
482-
}
483-
}
484-
485463
/// Insert a new element into uniquely held storage.
486464
/// Storage must be uniquely referenced with adequate capacity.
487465
/// The `key` must not be already present in the Dictionary.
@@ -507,6 +485,28 @@ extension _NativeDictionary { // Insertions
507485
_storage._count &+= 1
508486
}
509487

488+
/// Insert a new element into uniquely held storage, replacing an existing
489+
/// value (if any). Storage must be uniquely referenced with adequate
490+
/// capacity.
491+
@_alwaysEmitIntoClient @inlinable // Introduced in 5.1
492+
internal mutating func _unsafeUpdate(
493+
key: __owned Key,
494+
value: __owned Value
495+
) {
496+
let (bucket, found) = find(key)
497+
if found {
498+
// Note that we also update the key here. This method is used to handle
499+
// collisions arising from equality transitions during bridging, and in
500+
// that case it is desirable to keep values paired with their original
501+
// keys. This is not how `updateValue(_:, forKey:)` works.
502+
(_keys + bucket.offset).pointee = key
503+
(_values + bucket.offset).pointee = value
504+
} else {
505+
_precondition(count < capacity)
506+
_insert(at: bucket, key: key, value: value)
507+
}
508+
}
509+
510510
/// Insert a new entry into uniquely held storage.
511511
/// Storage must be uniquely referenced.
512512
/// The `key` must not be already present in the Dictionary.

stdlib/public/core/NativeSet.swift

Lines changed: 17 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ extension _NativeSet { // Low-level unchecked operations
124124
}
125125

126126
@_alwaysEmitIntoClient @inlinable // Introduced in 5.1
127+
@inline(__always)
127128
internal func uncheckedAssign(
128129
at bucket: Bucket,
129130
to element: __owned Element
@@ -356,33 +357,12 @@ internal func ELEMENT_TYPE_OF_SET_VIOLATES_HASHABLE_REQUIREMENTS(
356357
}
357358

358359
extension _NativeSet { // Insertions
359-
/// Insert a new element into uniquely held storage. Storage must be uniquely
360-
/// referenced with adequate capacity. If `allowingDuplicates` is false,
361-
/// `element` must not be already present in the set.
362-
@_alwaysEmitIntoClient @inlinable // Introduced in 5.1
363-
internal mutating func insertWithGuaranteedCapacity(
364-
_ element: __owned Element,
365-
allowingDuplicates: Bool
366-
) {
367-
_precondition(count < capacity)
368-
if !allowingDuplicates {
369-
_unsafeInsertNew(element)
370-
return
371-
}
372-
let (bucket, found) = find(element)
373-
if found {
374-
uncheckedAssign(at: bucket, to: element)
375-
} else {
376-
_unsafeInsertNew(element, at: bucket)
377-
}
378-
}
379-
380360
/// Insert a new element into uniquely held storage.
381361
/// Storage must be uniquely referenced with adequate capacity.
382362
/// The `element` must not be already present in the Set.
383363
@inlinable
384364
internal func _unsafeInsertNew(_ element: __owned Element) {
385-
_internalInvariant(count + 1 <= capacity)
365+
_precondition(count < capacity)
386366
let hashValue = self.hashValue(for: element)
387367
if _isDebugAssertConfiguration() {
388368
// In debug builds, perform a full lookup and trap if we detect duplicate
@@ -461,6 +441,21 @@ extension _NativeSet { // Insertions
461441
_unsafeInsertNew(element, at: bucket)
462442
return nil
463443
}
444+
445+
/// Insert an element into uniquely held storage, replacing an existing value
446+
/// (if any). Storage must be uniquely referenced with adequate capacity.
447+
@_alwaysEmitIntoClient @inlinable // Introduced in 5.1
448+
internal mutating func _unsafeUpdate(
449+
with element: __owned Element
450+
) {
451+
let (bucket, found) = find(element)
452+
if found {
453+
uncheckedAssign(at: bucket, to: element)
454+
} else {
455+
_precondition(count < capacity)
456+
_unsafeInsertNew(element, at: bucket)
457+
}
458+
}
464459
}
465460

466461
extension _NativeSet {

stdlib/public/core/SetCasting.swift

Lines changed: 53 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -12,25 +12,47 @@
1212

1313
//===--- Compiler conversion/casting entry points for Set<Element> --------===//
1414

15+
extension Set {
16+
@_alwaysEmitIntoClient @inlinable // Introduced in 5.1
17+
@inline(__always)
18+
internal init?<C: Collection>(
19+
_mapping source: C,
20+
allowingDuplicates: Bool,
21+
transform: (C.Element) -> Element?
22+
) {
23+
var target = _NativeSet<Element>(capacity: source.count)
24+
if allowingDuplicates {
25+
for m in source {
26+
guard let member = transform(m) else { return nil }
27+
target._unsafeUpdate(with: member)
28+
}
29+
} else {
30+
for m in source {
31+
guard let member = transform(m) else { return nil }
32+
target._unsafeInsertNew(member)
33+
}
34+
}
35+
self.init(_native: target)
36+
}
37+
}
38+
1539
/// Perform a non-bridged upcast that always succeeds.
1640
///
1741
/// - Precondition: `BaseValue` is a base class or base `@objc`
1842
/// protocol (such as `AnyObject`) of `DerivedValue`.
1943
@inlinable
20-
public func _setUpCast<DerivedValue, BaseValue>(_ source: Set<DerivedValue>)
21-
-> Set<BaseValue> {
22-
// String and NSString have different concepts of equality, so Set<NSString>
23-
// may generate key collisions when "upcasted" to Set<String>.
24-
// See rdar://problem/35995647
25-
let allowDuplicates = (BaseValue.self == String.self)
26-
27-
var builder = _NativeSet<BaseValue>(capacity: source.count)
28-
for member in source {
29-
_ = builder.insertWithGuaranteedCapacity(
30-
member as! BaseValue,
31-
allowingDuplicates: allowDuplicates)
32-
}
33-
return Set(_native: builder)
44+
public func _setUpCast<DerivedValue, BaseValue>(
45+
_ source: Set<DerivedValue>
46+
) -> Set<BaseValue> {
47+
return Set(
48+
_mapping: source,
49+
// String and NSString have different concepts of equality, so Set<NSString>
50+
// may generate key collisions when "upcasted" to Set<String>.
51+
// See rdar://problem/35995647
52+
allowingDuplicates: (BaseValue.self == String.self)
53+
) { member in
54+
(member as! BaseValue)
55+
}!
3456
}
3557

3658
/// Called by the casting machinery.
@@ -64,18 +86,15 @@ public func _setDownCast<BaseValue, DerivedValue>(_ source: Set<BaseValue>)
6486
// We can't just delegate to _setDownCastConditional here because we rely on
6587
// `as!` to generate nice runtime errors when the downcast fails.
6688

67-
// String and NSString have different concepts of equality, so
68-
// NSString-keyed Sets may generate key collisions when downcasted
69-
// to String. See rdar://problem/35995647
70-
let allowDuplicates = (DerivedValue.self == String.self)
71-
72-
var builder = _NativeSet<DerivedValue>(capacity: source.count)
73-
for member in source {
74-
builder.insertWithGuaranteedCapacity(
75-
member as! DerivedValue,
76-
allowingDuplicates: allowDuplicates)
77-
}
78-
return Set(_native: builder)
89+
return Set(
90+
_mapping: source,
91+
// String and NSString have different concepts of equality, so
92+
// NSString-keyed Sets may generate key collisions when downcasted
93+
// to String. See rdar://problem/35995647
94+
allowingDuplicates: (DerivedValue.self == String.self)
95+
) { member in
96+
(member as! DerivedValue)
97+
}!
7998
}
8099

81100
/// Called by the casting machinery.
@@ -102,17 +121,13 @@ internal func _setDownCastConditionalIndirect<SourceValue, TargetValue>(
102121
public func _setDownCastConditional<BaseValue, DerivedValue>(
103122
_ source: Set<BaseValue>
104123
) -> Set<DerivedValue>? {
105-
// String and NSString have different concepts of equality, so
106-
// NSString-keyed Sets may generate key collisions when downcasted
107-
// to String. See rdar://problem/35995647
108-
let allowDuplicates = (DerivedValue.self == String.self)
109-
110-
var builder = _NativeSet<DerivedValue>(capacity: source.count)
111-
for member in source {
112-
guard let derivedMember = member as? DerivedValue else { return nil }
113-
_ = builder.insertWithGuaranteedCapacity(
114-
derivedMember,
115-
allowingDuplicates: allowDuplicates)
124+
return Set(
125+
_mapping: source,
126+
// String and NSString have different concepts of equality, so
127+
// NSString-keyed Sets may generate key collisions when downcasted
128+
// to String. See rdar://problem/35995647
129+
allowingDuplicates: (DerivedValue.self == String.self)
130+
) { member in
131+
member as? DerivedValue
116132
}
117-
return Set(_native: builder)
118133
}

0 commit comments

Comments
 (0)