Skip to content

Commit c1e8ce6

Browse files
committed
SE-0139: Bridge Cocoa framework structs to NSValue.
For every struct type for which the frameworks provides an NSValue category for boxing and unboxing values of that type, provide an _ObjectiveCBridgeable conformance in the Swift overlay that bridges that struct to NSValue, allowing the structs to be used naturally with id-as-Any APIs and Cocoa container classes. This is mostly a matter of gyb-ing out boilerplate using `NSValue.init(bytes:objCType:)` to construct the instance, `NSValue.objCType` to check its type when casting, and `NSValue.getValue(_:)` to extract the unboxed value, though there are a number of special snowflake cases that need special accommodation: - To maintain proper layering, CoreGraphics structs need to be bridged in the Foundation overlay. - AVFoundation provides the NSValue boxing categories for structs owned by CoreMedia, but it does so using its own internal subclasses of NSValue, and these subclasses do not interop properly with the standard `NSValue` subclasses instantiated by Foundation. To do the right thing, we therefore have to let AVFoundation provide the bridging implementation for the CoreMedia types, and we have to use its category methods to do so. - SceneKit provides NSValue categories to box and unbox SCNVector3, SCNVector4, and SCNMatrix4; however, the methods it provides do so in an unusual way. SCNVector3 and SCNVector4 are packaged into `CGRect`s and then the CGRect is boxed using `valueWithCGRect:`. SCNMatrix4 is copied into a CATransform3D, which is then boxed using `valueWithCATransform3D:` from CoreAnimation. To be consistent with what SceneKit does, use its category methods for these types as well, and when casting, check the type against the type encoding SceneKit uses rather than the type encoding of the expected type.
1 parent 272163f commit c1e8ce6

36 files changed

+584
-111
lines changed

include/swift/AST/ASTContext.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -514,9 +514,9 @@ class ASTContext {
514514
ProtocolDecl *getProtocol(KnownProtocolKind kind) const;
515515

516516
/// Determine whether the given nominal type is one of the standard
517-
/// library types that is known a priori to be bridged to a
518-
/// Foundation.
519-
bool isStandardLibraryTypeBridgedInFoundation(NominalTypeDecl *nominal) const;
517+
/// library or Cocoa framework types that is known to be bridged by another
518+
/// module's overlay, for layering or implementation detail reasons.
519+
bool isTypeBridgedInExternalModule(NominalTypeDecl *nominal) const;
520520

521521
/// Get the Objective-C type that a Swift type bridges to, if any.
522522
///

include/swift/AST/KnownIdentifiers.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ IDENTIFIER(Any)
2929
IDENTIFIER(atIndexedSubscript)
3030
IDENTIFIER_(bridgeToObjectiveC)
3131
IDENTIFIER_WITH_NAME(code_, "_code")
32+
IDENTIFIER(CoreGraphics)
33+
IDENTIFIER(CoreMedia)
3234
IDENTIFIER(CGFloat)
3335
IDENTIFIER(CVarArg)
3436
IDENTIFIER(Darwin)

lib/AST/ASTContext.cpp

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3982,7 +3982,7 @@ ASTContext::getForeignRepresentationInfo(NominalTypeDecl *nominal,
39823982
}
39833983
}
39843984

3985-
bool ASTContext::isStandardLibraryTypeBridgedInFoundation(
3985+
bool ASTContext::isTypeBridgedInExternalModule(
39863986
NominalTypeDecl *nominal) const {
39873987
return (nominal == getBoolDecl() ||
39883988
nominal == getIntDecl() ||
@@ -3995,8 +3995,18 @@ bool ASTContext::isStandardLibraryTypeBridgedInFoundation(
39953995
nominal == getStringDecl() ||
39963996
nominal == getErrorDecl() ||
39973997
nominal == getAnyHashableDecl() ||
3998-
// Weird one-off case where CGFloat is bridged to NSNumber.
3999-
nominal->getName() == Id_CGFloat);
3998+
// Foundation's overlay depends on the CoreGraphics overlay, but
3999+
// CoreGraphics value types bridge to Foundation objects such as
4000+
// NSValue and NSNumber, so to avoid circular dependencies, the
4001+
// bridging implementations of CG types appear in the Foundation
4002+
// module.
4003+
nominal->getParentModule()->getName() == Id_CoreGraphics ||
4004+
// CoreMedia is a dependency of AVFoundation, but the bridged
4005+
// NSValue implementations for CMTime, CMTimeRange, and
4006+
// CMTimeMapping are provided by AVFoundation, and AVFoundation
4007+
// gets upset if you don't use the NSValue subclasses its factory
4008+
// methods instantiate.
4009+
nominal->getParentModule()->getName() == Id_CoreMedia);
40004010
}
40014011

40024012
Optional<Type>
@@ -4018,7 +4028,7 @@ ASTContext::getBridgedToObjC(const DeclContext *dc, Type type,
40184028
// optimizer will be guaranteed to see the conformance if it exists.
40194029
bool knownBridgedToObjC = false;
40204030
if (auto ntd = type->getAnyNominal())
4021-
knownBridgedToObjC = isStandardLibraryTypeBridgedInFoundation(ntd);
4031+
knownBridgedToObjC = isTypeBridgedInExternalModule(ntd);
40224032

40234033
// TODO: Under id-as-any, container bridging is unconstrained. This check can
40244034
// go away.

lib/SIL/DynamicCasts.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,11 @@ mustBridgeToSwiftValueBox(Module *M, CanType T) {
5555
if (T->isAnyExistentialType())
5656
return false;
5757

58-
// getBridgedToObjC() might return a null-type for bridged foundation types
59-
// during compiling the standard library. Exclude this case here.
58+
// getBridgedToObjC() might return a null-type for some types
59+
// whose bridging implementation is allowed to live elsewhere. Exclude this
60+
// case here.
6061
if (auto N = T->getAnyNominal())
61-
if (M->getASTContext().isStandardLibraryTypeBridgedInFoundation(N))
62+
if (M->getASTContext().isTypeBridgedInExternalModule(N))
6263
return false;
6364

6465
auto bridgeTy = M->getASTContext().getBridgedToObjC(M, T, nullptr);

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3904,7 +3904,7 @@ void ConformanceChecker::checkConformance() {
39043904
// between an imported Objective-C module and its overlay.
39053905
if (Proto->isSpecificProtocol(KnownProtocolKind::ObjectiveCBridgeable)) {
39063906
if (auto nominal = Adoptee->getAnyNominal()) {
3907-
if (!TC.Context.isStandardLibraryTypeBridgedInFoundation(nominal)) {
3907+
if (!TC.Context.isTypeBridgedInExternalModule(nominal)) {
39083908
auto nominalModule = nominal->getParentModule();
39093909
auto conformanceModule = DC->getParentModule();
39103910
if (nominalModule->getName() != conformanceModule->getName()) {

stdlib/private/StdlibUnittestFoundationExtras/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@ add_swift_library(swiftStdlibUnittestFoundationExtras ${SWIFT_STDLIB_LIBRARY_BUI
44
StdlibUnittestFoundationExtras.swift
55
UnavailableFoundationMethodThunks.mm
66

7-
SWIFT_MODULE_DEPENDS Foundation
7+
SWIFT_MODULE_DEPENDS Foundation StdlibUnittest
88
INSTALL_IN_COMPONENT stdlib-experimental)
99

stdlib/private/StdlibUnittestFoundationExtras/StdlibUnittestFoundationExtras.swift

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
import ObjectiveC
1414
import Foundation
15+
import StdlibUnittest
1516

1617
internal var _temporaryLocaleCurrentLocale: NSLocale? = nil
1718

@@ -111,3 +112,20 @@ extension NSDictionary {
111112
andKeys: keys)
112113
}
113114
}
115+
116+
public func expectBridgeToNSValue<T>(_ value: T,
117+
nsValueInitializer: ((T) -> NSValue)? = nil,
118+
nsValueGetter: ((NSValue) -> T)? = nil,
119+
equal: (T, T) -> Bool) {
120+
let object = value as AnyObject
121+
let nsValue = object as! NSValue
122+
if let nsValueInitializer = nsValueInitializer {
123+
expectEqual(nsValueInitializer(value), nsValue)
124+
}
125+
if let nsValueGetter = nsValueGetter {
126+
expectTrue(equal(value, nsValueGetter(nsValue)))
127+
}
128+
expectTrue(equal(value, object as! T))
129+
130+
}
131+

stdlib/public/SDK/AVFoundation/AVFoundation.swift renamed to stdlib/public/SDK/AVFoundation/AVFoundation.swift.gyb

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
@_exported import AVFoundation // Clang module
2+
import CoreMedia
23
import Foundation
34

5+
%{
6+
from gyb_foundation_support import \
7+
ObjectiveCBridgeableImplementationForNSValueWithCategoryMethods
8+
}%
49

510
extension AVError {
611
/// The device name.
@@ -39,3 +44,23 @@ extension AVError {
3944
}
4045
}
4146

47+
// Bridge CoreMedia structs to NSValue.
48+
// AVFoundation provides internal NSValue subclasses for these structures that
49+
// are incompatible with the NSConcreteValue subclasses you get using
50+
// -[NSValue valueWithBytes:objCType:].
51+
52+
${ ObjectiveCBridgeableImplementationForNSValueWithCategoryMethods(
53+
Type="CMTime",
54+
initializer="{ NSValue(time: $0) }",
55+
getter="{ $0.timeValue }",
56+
) }
57+
${ ObjectiveCBridgeableImplementationForNSValueWithCategoryMethods(
58+
Type="CMTimeRange",
59+
initializer="{ NSValue(timeRange: $0) }",
60+
getter="{ $0.timeRangeValue }",
61+
) }
62+
${ ObjectiveCBridgeableImplementationForNSValueWithCategoryMethods(
63+
Type="CMTimeMapping",
64+
initializer="{ NSValue(timeMapping: $0) }",
65+
getter="{ $0.timeMappingValue }",
66+
) }

stdlib/public/SDK/AVFoundation/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
add_swift_library(swiftAVFoundation ${SWIFT_SDK_OVERLAY_LIBRARY_BUILD_TYPES} IS_SDK_OVERLAY
2-
AVFoundation.swift
2+
AVFoundation.swift.gyb
33
TARGET_SDKS OSX IOS IOS_SIMULATOR TVOS TVOS_SIMULATOR
44
SWIFT_MODULE_DEPENDS Foundation CoreMedia
55
SWIFT_MODULE_DEPENDS_OSX AppKit

stdlib/public/SDK/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,12 @@ add_subdirectory(GLKit)
2929
add_subdirectory(HomeKit)
3030
add_subdirectory(IOKit)
3131
add_subdirectory(Intents)
32+
add_subdirectory(MapKit)
3233
add_subdirectory(ObjectiveC)
3334
add_subdirectory(OpenCL)
3435
add_subdirectory(os)
3536
add_subdirectory(Photos)
37+
add_subdirectory(QuartzCore)
3638
add_subdirectory(SafariServices)
3739
add_subdirectory(SceneKit)
3840
add_subdirectory(simd)

stdlib/public/SDK/CoreImage/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ add_swift_library(swiftCoreImage ${SWIFT_SDK_OVERLAY_LIBRARY_BUILD_TYPES} IS_SDK
22
CoreImage.swift
33

44
TARGET_SDKS OSX IOS IOS_SIMULATOR TVOS TVOS_SIMULATOR
5-
SWIFT_MODULE_DEPENDS Foundation ObjectiveC
5+
SWIFT_MODULE_DEPENDS Foundation ObjectiveC QuartzCore
66
SWIFT_MODULE_DEPENDS_IOS CoreMedia
77
SWIFT_MODULE_DEPENDS_TVOS CoreMedia
88
FRAMEWORK_DEPENDS_OSX QuartzCore
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
add_swift_library(swiftCoreLocation ${SWIFT_SDK_OVERLAY_LIBRARY_BUILD_TYPES} IS_SDK_OVERLAY
2-
CoreLocation.swift
2+
CoreLocation.swift.gyb
33
SWIFT_MODULE_DEPENDS Foundation
44
FRAMEWORK_DEPENDS CoreLocation)

stdlib/public/SDK/CoreLocation/CoreLocation.swift renamed to stdlib/public/SDK/CoreLocation/CoreLocation.swift.gyb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
@_exported import CoreLocation
22
import Foundation
33

4+
%{
5+
from gyb_foundation_support import ObjectiveCBridgeableImplementationForNSValue
6+
}%
7+
48
#if os(iOS)
59
extension CLError {
610
/// In a regionMonitoringResponseDelayed error, the region that the
@@ -10,3 +14,5 @@ extension CLError {
1014
}
1115
}
1216
#endif
17+
18+
${ ObjectiveCBridgeableImplementationForNSValue("CLLocationCoordinate2D") }

stdlib/public/SDK/Foundation/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ add_swift_library(swiftFoundation ${SWIFT_SDK_OVERLAY_LIBRARY_BUILD_TYPES} IS_SD
33
Boxing.swift
44
NSError.swift
55
NSStringAPI.swift
6-
NSValue.swift
6+
NSValue.swift.gyb
77
ExtraStringAPIs.swift
88
ReferenceConvertible.swift
99
AffineTransform.swift

stdlib/public/SDK/Foundation/NSValue.swift

Lines changed: 0 additions & 39 deletions
This file was deleted.
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
//===--- NSValue.swift - Bridging things in NSValue -----------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See http://swift.org/LICENSE.txt for license information
9+
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
%{
14+
from gyb_foundation_support import ObjectiveCBridgeableImplementationForNSValue
15+
}%
16+
17+
import CoreGraphics
18+
19+
${ ObjectiveCBridgeableImplementationForNSValue("NSRange") }
20+
${ ObjectiveCBridgeableImplementationForNSValue("CGRect") }
21+
${ ObjectiveCBridgeableImplementationForNSValue("CGPoint") }
22+
${ ObjectiveCBridgeableImplementationForNSValue("CGVector") }
23+
${ ObjectiveCBridgeableImplementationForNSValue("CGSize") }
24+
${ ObjectiveCBridgeableImplementationForNSValue("CGAffineTransform") }
25+
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
add_swift_library(swiftMapKit ${SWIFT_SDK_OVERLAY_LIBRARY_BUILD_TYPES} IS_SDK_OVERLAY
2+
MapKit.swift.gyb
3+
SWIFT_COMPILE_FLAGS "${SWIFT_RUNTIME_SWIFT_COMPILE_FLAGS}"
4+
LINK_FLAGS "${SWIFT_RUNTIME_SWIFT_LINK_FLAGS}"
5+
SWIFT_MODULE_DEPENDS ObjectiveC Foundation
6+
SWIFT_MODULE_DEPENDS_IOS QuartzCore
7+
SWIFT_MODULE_DEPENDS_OSX QuartzCore AppKit
8+
SWIFT_MODULE_DEPENDS_TVOS QuartzCore
9+
FRAMEWORK_DEPENDS MapKit)
10+
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
@_exported import MapKit // Clang module
2+
import Foundation
3+
4+
%{
5+
from gyb_foundation_support import ObjectiveCBridgeableImplementationForNSValue
6+
}%
7+
8+
${ ObjectiveCBridgeableImplementationForNSValue("MKCoordinateSpan") }
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
add_swift_library(swiftQuartzCore ${SWIFT_SDK_OVERLAY_LIBRARY_BUILD_TYPES} IS_SDK_OVERLAY
2+
QuartzCore.swift.gyb
3+
SWIFT_COMPILE_FLAGS "${SWIFT_RUNTIME_SWIFT_COMPILE_FLAGS}"
4+
LINK_FLAGS "${SWIFT_RUNTIME_SWIFT_LINK_FLAGS}"
5+
TARGET_SDKS OSX IOS IOS_SIMULATOR TVOS TVOS_SIMULATOR
6+
SWIFT_MODULE_DEPENDS ObjectiveC Foundation
7+
FRAMEWORK_DEPENDS QuartzCore)
8+
9+
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//===--- NSValue.swift - Bridging things in NSValue -----------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See http://swift.org/LICENSE.txt for license information
9+
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
%{
14+
from gyb_foundation_support import ObjectiveCBridgeableImplementationForNSValue
15+
}%
16+
17+
@_exported import QuartzCore // Clang module
18+
import Foundation
19+
20+
${ ObjectiveCBridgeableImplementationForNSValue("CATransform3D") }
21+

stdlib/public/SDK/SceneKit/CMakeLists.txt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,13 @@ SET(SWIFT_SCENEKIT_DEPENDENCIES_NON_WATCHOS
55
CoreImage)
66

77
add_swift_library(swiftSceneKit ${SWIFT_SDK_OVERLAY_LIBRARY_BUILD_TYPES} IS_SDK_OVERLAY
8-
SceneKit.swift
8+
SceneKit.swift.gyb
99
Thunks.mm
1010

1111
TARGET_SDKS OSX IOS IOS_SIMULATOR TVOS TVOS_SIMULATOR WATCHOS WATCHOS_SIMULATOR
1212
SWIFT_MODULE_DEPENDS_OSX ${SWIFT_SCENEKIT_DEPENDENCIES_NON_WATCHOS} AppKit
1313
SWIFT_MODULE_DEPENDS_IOS ${SWIFT_SCENEKIT_DEPENDENCIES_NON_WATCHOS} UIKit
1414
SWIFT_MODULE_DEPENDS_TVOS ${SWIFT_SCENEKIT_DEPENDENCIES_NON_WATCHOS} UIKit
15-
SWIFT_MODULE_DEPENDS_WATCHOS Foundation
16-
SWIFT_MODULE_DEPENDS simd
15+
SWIFT_MODULE_DEPENDS Foundation simd
1716
FRAMEWORK_DEPENDS_WEAK SceneKit)
1817

0 commit comments

Comments
 (0)