Skip to content

Commit 6821aca

Browse files
author
Dave Abrahams
committed
[stdlib] Generalize array bridge/cast machinery
Now we support casting and bridging to/from [Any], not just [AnyObject] Note that the typechecker still doesn't allow all the casts we'd like; see the FIXMEs in test/1_stdlib/ArrayBridge.swift.gyb.
1 parent ebbc643 commit 6821aca

File tree

3 files changed

+97
-203
lines changed

3 files changed

+97
-203
lines changed

stdlib/public/core/ArrayCast.swift

Lines changed: 19 additions & 151 deletions
Original file line numberDiff line numberDiff line change
@@ -16,181 +16,49 @@
1616
//
1717
//===----------------------------------------------------------------------===//
1818

19-
#if _runtime(_ObjC)
20-
// FIXME: These need to be implemented even for non-objc:
21-
// rdar://problem/18881196
22-
23-
internal enum _ValueOrReference {
24-
case reference, value
25-
26-
internal init<T>(_: T.Type) {
27-
self = _isClassOrObjCExistential(T.self) ? .reference : .value
28-
}
29-
}
30-
31-
internal enum _BridgeStyle {
32-
case verbatim, explicit
33-
34-
internal init<T>(_: T.Type) {
35-
self = _isBridgedVerbatimToObjectiveC(T.self) ? .verbatim : .explicit
36-
}
37-
}
38-
39-
//===--- Forced casts: [T] as! [U] ----------------------------------------===//
40-
4119
/// Implements `source as! [TargetElement]`.
4220
///
43-
/// - Precondition: At least one of `SourceElement` and `TargetElement` is a
44-
/// class type or ObjC existential. May trap for other "valid" inputs when
45-
/// `TargetElement` is not bridged verbatim, if an element can't be converted.
21+
/// - Note: When SourceElement and TargetElement are both bridged verbatim, type
22+
/// checking is deferred until elements are actually accessed.
4623
public func _arrayForceCast<SourceElement, TargetElement>(
4724
_ source: Array<SourceElement>
4825
) -> Array<TargetElement> {
49-
switch (
50-
_ValueOrReference(SourceElement.self), _BridgeStyle(TargetElement.self)
51-
) {
52-
case (.reference, .verbatim):
53-
let native = source._buffer.requestNativeBuffer()
54-
55-
if _fastPath(native != nil) {
56-
if _fastPath(native!.storesOnlyElementsOfType(TargetElement.self)) {
26+
#if _runtime(_ObjC)
27+
if _isClassOrObjCExistential(SourceElement.self)
28+
&& _isClassOrObjCExistential(TargetElement.self) {
29+
let src = source._buffer
30+
if let native = src.requestNativeBuffer() {
31+
if native.storesOnlyElementsOfType(TargetElement.self) {
5732
// A native buffer that is known to store only elements of the
5833
// TargetElement can be used directly
59-
return Array(_buffer: source._buffer.cast(toBufferOf: TargetElement.self))
34+
return Array(_buffer: src.cast(toBufferOf: TargetElement.self))
6035
}
6136
// Other native buffers must use deferred element type checking
6237
return Array(_buffer:
63-
source._buffer.downcast(
64-
toBufferWithDeferredTypeCheckOf: TargetElement.self))
38+
src.downcast(toBufferWithDeferredTypeCheckOf: TargetElement.self))
6539
}
66-
// All non-native buffers use deferred element typechecking
6740
return Array(_immutableCocoaArray: source._buffer._asCocoaArray())
68-
69-
case (.reference, .explicit):
70-
let result: [TargetElement]? = _arrayConditionalBridgeElements(source)
71-
_precondition(result != nil, "array cannot be bridged from Objective-C")
72-
return result!
73-
74-
case (.value, .verbatim):
75-
if source.isEmpty {
76-
return Array()
77-
}
78-
79-
var buf = _ContiguousArrayBuffer<TargetElement>(
80-
uninitializedCount: source.count, minimumCapacity: 0)
81-
82-
let _: Void = buf.withUnsafeMutableBufferPointer {
83-
var p = $0.baseAddress!
84-
for value in source {
85-
let bridged: AnyObject? = _bridgeToObjectiveC(value)
86-
_precondition(
87-
bridged != nil, "array element cannot be bridged to Objective-C")
88-
// FIXME: should be an unsafeDowncast.
89-
p.initialize(with: unsafeBitCast(bridged!, to: TargetElement.self))
90-
p += 1
91-
}
92-
}
93-
return Array(_buffer: _ArrayBuffer(buf, shiftedToStartIndex: 0))
94-
95-
case (.value, .explicit):
96-
_sanityCheckFailure(
97-
"Force-casting between Arrays of value types not prevented at compile-time"
98-
)
9941
}
42+
#endif
43+
return _arrayConditionalCast(source)!
10044
}
10145

102-
//===--- Conditional casts: [T] as? [U] -----------------------------------===//
46+
internal struct _UnwrappingFailed : Error {}
10347

104-
/// Implements the semantics of `x as? [TargetElement]` where `x` has type
105-
/// `[SourceElement]` and `TargetElement` is a verbatim-bridged trivial subtype of
106-
/// `SourceElement`.
107-
///
108-
/// Returns an Array<TargetElement> containing the same elements as a
109-
///
110-
/// O(1) if a's buffer elements are dynamically known to have type
111-
/// TargetElement or a type derived from TargetElement. O(N)
112-
/// otherwise.
113-
internal func _arrayConditionalDownCastElements<SourceElement, TargetElement>(
114-
_ a: Array<SourceElement>
115-
) -> [TargetElement]? {
116-
_sanityCheck(_isBridgedVerbatimToObjectiveC(SourceElement.self))
117-
_sanityCheck(_isBridgedVerbatimToObjectiveC(TargetElement.self))
118-
119-
if _fastPath(!a.isEmpty) {
120-
let native = a._buffer.requestNativeBuffer()
121-
122-
if _fastPath(native != nil) {
123-
if native!.storesOnlyElementsOfType(TargetElement.self) {
124-
return Array(_buffer: a._buffer.cast(toBufferOf: TargetElement.self))
125-
}
126-
return nil
127-
}
128-
129-
// slow path: we store an NSArray
130-
131-
// We can skip the check if TargetElement happens to be AnyObject
132-
if !(AnyObject.self is TargetElement.Type) {
133-
for element in a {
134-
if !(element is TargetElement) {
135-
return nil
136-
}
137-
}
138-
}
139-
return Array(_buffer: a._buffer.cast(toBufferOf: TargetElement.self))
48+
extension Optional {
49+
internal func unwrappedOrError() throws -> Wrapped {
50+
if let x = self { return x }
51+
throw _UnwrappingFailed()
14052
}
141-
return []
142-
}
143-
144-
/// Try to convert the source array of objects to an array of values
145-
/// produced by bridging the objects from Objective-C to `TargetElement`.
146-
///
147-
/// - Precondition: SourceElement is a class type.
148-
/// - Precondition: TargetElement is bridged non-verbatim to Objective-C.
149-
/// O(n), because each element must be bridged separately.
150-
internal func _arrayConditionalBridgeElements<
151-
SourceElement,
152-
TargetElement
153-
>(_ source: Array<SourceElement>) -> Array<TargetElement>? {
154-
155-
_sanityCheck(_isBridgedVerbatimToObjectiveC(SourceElement.self))
156-
_sanityCheck(!_isBridgedVerbatimToObjectiveC(TargetElement.self))
157-
158-
let buf = _ContiguousArrayBuffer<TargetElement>(
159-
uninitializedCount: source.count, minimumCapacity: 0)
160-
161-
var p = buf.firstElementAddress
162-
163-
// Make sure the resulting array owns anything that is successfully stored
164-
// there.
165-
defer { buf.count = p - buf.firstElementAddress }
166-
167-
for object: SourceElement in source {
168-
guard let value = object as? TargetElement else {
169-
return nil
170-
}
171-
p.initialize(with: value)
172-
p += 1
173-
}
174-
return Array(_buffer: _ArrayBuffer(buf, shiftedToStartIndex: 0))
17553
}
17654

17755
/// Implements `source as? [TargetElement]`: convert each element of
17856
/// `source` to a `TargetElement` and return the resulting array, or
17957
/// return `nil` if any element fails to convert.
18058
///
181-
/// - Precondition: `SourceElement` is a class or ObjC existential type.
182-
/// O(n), because each element must be checked.
59+
/// - Complexity: O(n), because each element must be checked.
18360
public func _arrayConditionalCast<SourceElement, TargetElement>(
18461
_ source: [SourceElement]
18562
) -> [TargetElement]? {
186-
switch (_ValueOrReference(SourceElement.self), _BridgeStyle(TargetElement.self)) {
187-
case (.value, _):
188-
_sanityCheckFailure(
189-
"Conditional cast from array of value types not prevented at compile-time")
190-
case (.reference, .verbatim):
191-
return _arrayConditionalDownCastElements(source)
192-
case (.reference, .explicit):
193-
return _arrayConditionalBridgeElements(source)
194-
}
63+
return try? source.map { try ($0 as? TargetElement).unwrappedOrError() }
19564
}
196-
#endif

0 commit comments

Comments
 (0)