Skip to content

Commit 9a2783f

Browse files
authored
Merge pull request #3671 from apple/stdlib-id-as-any
2 parents 38104f2 + b5d2e7b commit 9a2783f

File tree

3 files changed

+294
-125
lines changed

3 files changed

+294
-125
lines changed

stdlib/public/core/HashedCollections.swift.gyb

Lines changed: 78 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -1255,23 +1255,30 @@ internal func _stdlib_NSSet_allObjects(_ nss: _NSSet) ->
12551255

12561256
//===--- Compiler conversion/casting entry points for Set<Element> --------===//
12571257

1258-
#if _runtime(_ObjC)
1258+
func _impossible<T>(_:T.Type) -> T {
1259+
Builtin.unreachable()
1260+
}
1261+
1262+
func _unsafeUpcast<T, U>(_ x: T, to: U.Type) -> U {
1263+
_sanityCheck(x is U)
1264+
return x as? U ?? _impossible(U.self)
1265+
}
1266+
12591267
/// Perform a non-bridged upcast that always succeeds.
12601268
///
12611269
/// - Precondition: `BaseValue` is a base class or base `@objc`
12621270
/// protocol (such as `AnyObject`) of `DerivedValue`.
12631271
public func _setUpCast<DerivedValue, BaseValue>(_ source: Set<DerivedValue>)
12641272
-> Set<BaseValue> {
1265-
_sanityCheck(_isClassOrObjCExistential(BaseValue.self))
1266-
_sanityCheck(_isClassOrObjCExistential(DerivedValue.self))
1267-
12681273
var builder = _SetBuilder<BaseValue>(count: source.count)
1269-
for member in source {
1270-
builder.add(member: unsafeBitCast(member, to: BaseValue.self))
1274+
for x in source {
1275+
builder.add(member: _unsafeUpcast(x, to: BaseValue.self))
12711276
}
12721277
return builder.take()
12731278
}
12741279

1280+
#if _runtime(_ObjC)
1281+
12751282
/// Implements an unconditional upcast that involves bridging.
12761283
///
12771284
/// The cast can fail if bridging fails.
@@ -1288,6 +1295,7 @@ public func _setBridgeToObjectiveC<SwiftValue, ObjCValue>(
12881295
let valueBridgesDirectly =
12891296
_isBridgedVerbatimToObjectiveC(SwiftValue.self) ==
12901297
_isBridgedVerbatimToObjectiveC(ObjCValue.self)
1298+
12911299
for member in source {
12921300
var bridgedMember: ObjCValue
12931301
if valueBridgesDirectly {
@@ -1303,6 +1311,8 @@ public func _setBridgeToObjectiveC<SwiftValue, ObjCValue>(
13031311
return result
13041312
}
13051313

1314+
#endif
1315+
13061316
/// Implements a forced downcast. This operation should have O(1) complexity.
13071317
///
13081318
/// The cast can fail if bridging fails. The actual checks and bridging can be
@@ -1313,20 +1323,23 @@ public func _setBridgeToObjectiveC<SwiftValue, ObjCValue>(
13131323
public func _setDownCast<BaseValue, DerivedValue>(_ source: Set<BaseValue>)
13141324
-> Set<DerivedValue> {
13151325

1316-
_sanityCheck(_isClassOrObjCExistential(BaseValue.self))
1317-
_sanityCheck(_isClassOrObjCExistential(DerivedValue.self))
1318-
1319-
switch source._variantStorage {
1320-
case _VariantSetStorage.native(let nativeOwner):
1321-
return Set(
1322-
_immutableCocoaSet:
1323-
unsafeBitCast(nativeOwner, to: _NSSet.self))
1324-
1325-
case _VariantSetStorage.cocoa(let cocoaStorage):
1326-
return Set(
1327-
_immutableCocoaSet:
1328-
unsafeBitCast(cocoaStorage, to: _NSSet.self))
1326+
#if _runtime(_ObjC)
1327+
if _isClassOrObjCExistential(BaseValue.self)
1328+
&& _isClassOrObjCExistential(DerivedValue.self) {
1329+
switch source._variantStorage {
1330+
case _VariantSetStorage.native(let nativeOwner):
1331+
return Set(
1332+
_immutableCocoaSet:
1333+
unsafeBitCast(nativeOwner, to: _NSSet.self))
1334+
1335+
case _VariantSetStorage.cocoa(let cocoaStorage):
1336+
return Set(
1337+
_immutableCocoaSet:
1338+
unsafeBitCast(cocoaStorage, to: _NSSet.self))
1339+
}
13291340
}
1341+
#endif
1342+
return _setDownCastConditional(source)!
13301343
}
13311344

13321345
/// Implements a conditional downcast.
@@ -1339,20 +1352,14 @@ public func _setDownCast<BaseValue, DerivedValue>(_ source: Set<BaseValue>)
13391352
public func _setDownCastConditional<BaseValue, DerivedValue>(
13401353
_ source: Set<BaseValue>
13411354
) -> Set<DerivedValue>? {
1342-
_sanityCheck(_isClassOrObjCExistential(BaseValue.self))
1343-
_sanityCheck(_isClassOrObjCExistential(DerivedValue.self))
1344-
1345-
var result = Set<DerivedValue>(minimumCapacity: source.count)
1346-
for member in source {
1347-
if let derivedMember = member as? DerivedValue {
1348-
result.insert(derivedMember)
1349-
continue
1350-
}
1351-
return nil
1352-
}
1353-
return result
1355+
return try? Set(
1356+
source.lazy.map {
1357+
try ($0 as? DerivedValue).unwrappedOrError()
1358+
})
13541359
}
13551360

1361+
#if _runtime(_ObjC)
1362+
13561363
/// Implements an unconditional downcast that involves bridging.
13571364
///
13581365
/// - Precondition: At least one of `SwiftValue` is a bridged value
@@ -1406,23 +1413,6 @@ public func _setBridgeFromObjectiveCConditional<
14061413
}
14071414
return result
14081415
}
1409-
#else
1410-
1411-
public func _setUpCast<DerivedValue, BaseValue>(_ source: Set<DerivedValue>)
1412-
-> Set<BaseValue> {
1413-
fatalError("_setUpCast is unimplemented")
1414-
}
1415-
1416-
public func _setDownCast<BaseValue, DerivedValue>(_ source: Set<BaseValue>)
1417-
-> Set<DerivedValue> {
1418-
fatalError("_setDownCast is unimplemented")
1419-
}
1420-
1421-
public func _setDownCastConditional<BaseValue, DerivedValue>(
1422-
_ source: Set<BaseValue>
1423-
) -> Set<DerivedValue>? {
1424-
fatalError("_setDownCastConditional is unimplemented")
1425-
}
14261416

14271417
#endif
14281418

@@ -2179,7 +2169,6 @@ internal func _stdlib_NSDictionary_allKeys(_ nsd: _NSDictionary)
21792169

21802170
//===--- Compiler conversion/casting entry points for Dictionary<K, V> ----===//
21812171

2182-
#if _runtime(_ObjC)
21832172
/// Perform a non-bridged upcast that always succeeds.
21842173
///
21852174
/// - Precondition: `BaseKey` and `BaseValue` are base classes or base `@objc`
@@ -2188,22 +2177,17 @@ internal func _stdlib_NSDictionary_allKeys(_ nsd: _NSDictionary)
21882177
public func _dictionaryUpCast<DerivedKey, DerivedValue, BaseKey, BaseValue>(
21892178
_ source: Dictionary<DerivedKey, DerivedValue>
21902179
) -> Dictionary<BaseKey, BaseValue> {
2191-
// FIXME: This crappy implementation is O(n) because it copies the
2192-
// data; a proper implementation would be O(1).
2193-
2194-
_sanityCheck(_isClassOrObjCExistential(BaseKey.self))
2195-
_sanityCheck(_isClassOrObjCExistential(BaseValue.self))
2196-
_sanityCheck(_isClassOrObjCExistential(DerivedKey.self))
2197-
_sanityCheck(_isClassOrObjCExistential(DerivedValue.self))
2198-
21992180
var result = Dictionary<BaseKey, BaseValue>(minimumCapacity: source.count)
2181+
22002182
for (k, v) in source {
2201-
result[unsafeBitCast(k, to: BaseKey.self)] =
2202-
unsafeBitCast(v, to: BaseValue.self)
2183+
result[_unsafeUpcast(k, to: BaseKey.self)]
2184+
= _unsafeUpcast(v, to: BaseValue.self)
22032185
}
22042186
return result
22052187
}
22062188

2189+
#if _runtime(_ObjC)
2190+
22072191
/// Implements an unconditional upcast that involves bridging.
22082192
///
22092193
/// The cast can fail if bridging fails.
@@ -2263,6 +2247,7 @@ public func _dictionaryBridgeToObjectiveC<
22632247

22642248
return result
22652249
}
2250+
#endif
22662251

22672252
/// Implements a forced downcast. This operation should have O(1) complexity.
22682253
///
@@ -2274,33 +2259,38 @@ public func _dictionaryBridgeToObjectiveC<
22742259
public func _dictionaryDownCast<BaseKey, BaseValue, DerivedKey, DerivedValue>(
22752260
_ source: Dictionary<BaseKey, BaseValue>
22762261
) -> Dictionary<DerivedKey, DerivedValue> {
2277-
_sanityCheck(_isClassOrObjCExistential(BaseKey.self))
2278-
_sanityCheck(_isClassOrObjCExistential(BaseValue.self))
2279-
_sanityCheck(_isClassOrObjCExistential(DerivedKey.self))
2280-
_sanityCheck(_isClassOrObjCExistential(DerivedValue.self))
2281-
2282-
switch source._variantStorage {
2283-
case .native(let nativeOwner):
2284-
// FIXME(performance): this introduces an indirection through Objective-C
2285-
// runtime, even though we access native storage. But we cannot
2286-
// unsafeBitCast the owner object, because that would change the generic
2287-
// arguments.
2288-
//
2289-
// One way to solve this is to add a third, read-only, representation to
2290-
// variant storage: like _NativeDictionaryStorageOwner, but it would
2291-
// perform casts when accessing elements.
2292-
//
2293-
// Note: it is safe to treat the storage as immutable here because
2294-
// Dictionary will not mutate storage with reference count greater than 1.
2295-
return Dictionary(
2296-
_immutableCocoaDictionary:
2262+
2263+
#if _runtime(_ObjC)
2264+
if _isClassOrObjCExistential(BaseKey.self)
2265+
&& _isClassOrObjCExistential(BaseValue.self)
2266+
&& _isClassOrObjCExistential(DerivedKey.self)
2267+
&& _isClassOrObjCExistential(DerivedValue.self) {
2268+
2269+
switch source._variantStorage {
2270+
case .native(let nativeOwner):
2271+
// FIXME(performance): this introduces an indirection through Objective-C
2272+
// runtime, even though we access native storage. But we cannot
2273+
// unsafeBitCast the owner object, because that would change the generic
2274+
// arguments.
2275+
//
2276+
// One way to solve this is to add a third, read-only, representation to
2277+
// variant storage: like _NativeDictionaryStorageOwner, but it would
2278+
// perform casts when accessing elements.
2279+
//
2280+
// Note: it is safe to treat the storage as immutable here because
2281+
// Dictionary will not mutate storage with reference count greater than 1.
2282+
return Dictionary(
2283+
_immutableCocoaDictionary:
22972284
unsafeBitCast(nativeOwner, to: _NSDictionary.self))
22982285

2299-
case .cocoa(let cocoaStorage):
2300-
return Dictionary(
2301-
_immutableCocoaDictionary:
2286+
case .cocoa(let cocoaStorage):
2287+
return Dictionary(
2288+
_immutableCocoaDictionary:
23022289
unsafeBitCast(cocoaStorage, to: _NSDictionary.self))
2290+
}
23032291
}
2292+
#endif
2293+
return _dictionaryDownCastConditional(source)!
23042294
}
23052295

23062296
/// Implements a conditional downcast.
@@ -2315,27 +2305,17 @@ public func _dictionaryDownCastConditional<
23152305
>(
23162306
_ source: Dictionary<BaseKey, BaseValue>
23172307
) -> Dictionary<DerivedKey, DerivedValue>? {
2318-
_sanityCheck(_isClassOrObjCExistential(BaseKey.self))
2319-
_sanityCheck(_isClassOrObjCExistential(BaseValue.self))
2320-
_sanityCheck(_isClassOrObjCExistential(DerivedKey.self))
2321-
_sanityCheck(_isClassOrObjCExistential(DerivedValue.self))
2322-
2308+
23232309
var result = Dictionary<DerivedKey, DerivedValue>()
2324-
for (key, value) in source {
2325-
if let derivedKey = key as? DerivedKey {
2326-
if let derivedValue = value as? DerivedValue {
2327-
result[derivedKey] = derivedValue
2328-
continue
2329-
}
2330-
}
2331-
2332-
// Either the key or the value wasn't of the appropriate derived
2333-
// type. Fail.
2334-
return nil
2310+
for (k, v) in source {
2311+
guard let k1 = k as? DerivedKey, let v1 = v as? DerivedValue
2312+
else { return nil }
2313+
result[k1] = v1
23352314
}
23362315
return result
23372316
}
23382317

2318+
#if _runtime(_ObjC)
23392319
/// Implements an unconditional downcast that involves bridging.
23402320
///
23412321
/// - Precondition: At least one of `SwiftKey` or `SwiftValue` is a bridged value
@@ -2417,30 +2397,7 @@ public func _dictionaryBridgeFromObjectiveCConditional<
24172397
}
24182398
return result
24192399
}
2420-
#else
2421-
2422-
public func _dictionaryUpCast<DerivedKey, DerivedValue, BaseKey, BaseValue>(
2423-
_ source: Dictionary<DerivedKey, DerivedValue>
2424-
) -> Dictionary<BaseKey, BaseValue> {
2425-
fatalError("_dictionaryUpCast is unimplemented")
2426-
}
2427-
2428-
public func _dictionaryDownCast<BaseKey, BaseValue, DerivedKey, DerivedValue>(
2429-
_ source: Dictionary<BaseKey, BaseValue>
2430-
) -> Dictionary<DerivedKey, DerivedValue> {
2431-
fatalError("_dictionaryDownCast is unimplemented")
2432-
}
2433-
2434-
public func _dictionaryDownCastConditional<
2435-
BaseKey, BaseValue, DerivedKey, DerivedValue
2436-
>(
2437-
_ source: Dictionary<BaseKey, BaseValue>
2438-
) -> Dictionary<DerivedKey, DerivedValue>? {
2439-
fatalError("_dictionaryDownCastConditional is unimplemented")
2440-
}
2441-
24422400
#endif
2443-
24442401
//===--- APIs templated for Dictionary and Set ----------------------------===//
24452402

24462403
%{

test/1_stdlib/ArrayBridge.swift.gyb

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
//
1616
// RUN: %gyb %s -o %t/ArrayBridge.swift
1717
// RUN: %target-clang %S/Inputs/ArrayBridge/ArrayBridge.m -c -o %t/ArrayBridgeObjC.o -g
18-
// RUN: %line-directive %t/ArrayBridge.swift -- %target-build-swift %t/ArrayBridge.swift -I %S/Inputs/ArrayBridge/ -Xlinker %t/ArrayBridgeObjC.o -o %t/ArrayBridge --
18+
// RUN: %line-directive %t/ArrayBridge.swift -- %target-build-swift -Xfrontend -enable-experimental-collection-casts %t/ArrayBridge.swift -I %S/Inputs/ArrayBridge/ -Xlinker %t/ArrayBridgeObjC.o -o %t/ArrayBridge --
1919

2020
// RUN: %target-run %t/ArrayBridge
2121
// REQUIRES: executable_test
@@ -240,7 +240,8 @@ tests.test("Another/${Any}") {
240240

241241
let nsArrayOfBaseConvertedToAnyArray = nsArrayOfBase
242242
% if Any == 'Any':
243-
// FIXME: nsArrayOfBase as [Any] doesn't typecheck
243+
// FIXME: nsArrayOfBase as [Any] doesn't
244+
// typecheck. -enable-experimental-collection-casts doesn't change that.
244245
as [AnyObject]
245246
% end
246247
as [${Any}]
@@ -316,7 +317,8 @@ tests.test("testExplicitlyBridged/${Any}") {
316317
let cocoaBridgeableValues = NSArray(
317318
array: bridgeableValuesAsNSArray
318319
%if Any == 'Any':
319-
// FIXME: should just be "as [Any]" but the typechecker doesn't allow it yet
320+
// FIXME: should just be "as [Any]" but the typechecker doesn't allow it
321+
// yet. -enable-experimental-collection-casts doesn't change that.
320322
as [AnyObject] as [Any] as! [AnyObject]
321323
%else:
322324
as [${Any}]
@@ -379,7 +381,8 @@ tests.test("testExplicitlyBridged/${Any}") {
379381
// Downcast of Cocoa array to an array of classes.
380382
let wrappedCocoaBridgeableValues = cocoaBridgeableValues
381383
%if Any == 'Any':
382-
// FIXME: should just be "as [Any]" but typechecker doesn't allow it yet.
384+
// FIXME: should just be "as [Any]" but typechecker doesn't allow it
385+
// yet. -enable-experimental-collection-casts doesn't change that.
383386
as [AnyObject]
384387
%end
385388
as [${Any}]

0 commit comments

Comments
 (0)