Skip to content

Commit 86fbeee

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 b318763 commit 86fbeee

36 files changed

+583
-143
lines changed

include/swift/AST/ASTContext.h

+3-3
Original file line numberDiff line numberDiff line change
@@ -510,9 +510,9 @@ class ASTContext {
510510
ProtocolDecl *getProtocol(KnownProtocolKind kind) const;
511511

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

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

include/swift/AST/KnownIdentifiers.def

+2
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

+13-3
Original file line numberDiff line numberDiff line change
@@ -3938,7 +3938,7 @@ ASTContext::getForeignRepresentationInfo(NominalTypeDecl *nominal,
39383938
}
39393939
}
39403940

3941-
bool ASTContext::isStandardLibraryTypeBridgedInFoundation(
3941+
bool ASTContext::isTypeBridgedInExternalModule(
39423942
NominalTypeDecl *nominal) const {
39433943
return (nominal == getBoolDecl() ||
39443944
nominal == getIntDecl() ||
@@ -3951,8 +3951,18 @@ bool ASTContext::isStandardLibraryTypeBridgedInFoundation(
39513951
nominal == getStringDecl() ||
39523952
nominal == getErrorDecl() ||
39533953
nominal == getAnyHashableDecl() ||
3954-
// Weird one-off case where CGFloat is bridged to NSNumber.
3955-
nominal->getName() == Id_CGFloat);
3954+
// Foundation's overlay depends on the CoreGraphics overlay, but
3955+
// CoreGraphics value types bridge to Foundation objects such as
3956+
// NSValue and NSNumber, so to avoid circular dependencies, the
3957+
// bridging implementations of CG types appear in the Foundation
3958+
// module.
3959+
nominal->getParentModule()->getName() == Id_CoreGraphics ||
3960+
// CoreMedia is a dependency of AVFoundation, but the bridged
3961+
// NSValue implementations for CMTime, CMTimeRange, and
3962+
// CMTimeMapping are provided by AVFoundation, and AVFoundation
3963+
// gets upset if you don't use the NSValue subclasses its factory
3964+
// methods instantiate.
3965+
nominal->getParentModule()->getName() == Id_CoreMedia);
39563966
}
39573967

39583968
Type ASTContext::getBridgedToObjC(const DeclContext *dc, Type type,

lib/SIL/DynamicCasts.cpp

+4-3
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,11 @@ mustBridgeToSwiftValueBox(Module *M, CanType T) {
4444
if (T->isAnyExistentialType())
4545
return false;
4646

47-
// getBridgedToObjC() might return a null-type for bridged foundation types
48-
// during compiling the standard library. Exclude this case here.
47+
// getBridgedToObjC() might return a null-type for some types
48+
// whose bridging implementation is allowed to live elsewhere. Exclude this
49+
// case here.
4950
if (auto N = T->getAnyNominal())
50-
if (M->getASTContext().isStandardLibraryTypeBridgedInFoundation(N))
51+
if (M->getASTContext().isTypeBridgedInExternalModule(N))
5152
return false;
5253

5354
return !M->getASTContext().getBridgedToObjC(M, T);

lib/Sema/TypeCheckProtocol.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -3974,7 +3974,7 @@ void ConformanceChecker::checkConformance() {
39743974
// between an imported Objective-C module and its overlay.
39753975
if (Proto->isSpecificProtocol(KnownProtocolKind::ObjectiveCBridgeable)) {
39763976
if (auto nominal = Adoptee->getAnyNominal()) {
3977-
if (!TC.Context.isStandardLibraryTypeBridgedInFoundation(nominal)) {
3977+
if (!TC.Context.isTypeBridgedInExternalModule(nominal)) {
39783978
auto nominalModule = nominal->getParentModule();
39793979
auto conformanceModule = DC->getParentModule();
39803980
if (nominalModule->getName() != conformanceModule->getName()) {

stdlib/private/StdlibUnittestFoundationExtras/CMakeLists.txt

+1-1
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

+18
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?
1718

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

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

+25
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

+1-1
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_COMPILE_FLAGS "${SWIFT_RUNTIME_SWIFT_COMPILE_FLAGS}"
55
LINK_FLAGS "${SWIFT_RUNTIME_SWIFT_LINK_FLAGS}"

stdlib/public/SDK/CMakeLists.txt

+2
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

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ add_swift_library(swiftCoreImage ${SWIFT_SDK_OVERLAY_LIBRARY_BUILD_TYPES} IS_SDK
44
SWIFT_COMPILE_FLAGS "${SWIFT_RUNTIME_SWIFT_COMPILE_FLAGS}"
55
LINK_FLAGS "${SWIFT_RUNTIME_SWIFT_LINK_FLAGS}"
66
TARGET_SDKS OSX IOS IOS_SIMULATOR TVOS TVOS_SIMULATOR
7-
SWIFT_MODULE_DEPENDS Foundation ObjectiveC
7+
SWIFT_MODULE_DEPENDS Foundation ObjectiveC QuartzCore
88
SWIFT_MODULE_DEPENDS_IOS CoreMedia
99
SWIFT_MODULE_DEPENDS_TVOS CoreMedia
1010
FRAMEWORK_DEPENDS_OSX QuartzCore

stdlib/public/SDK/CoreLocation/CMakeLists.txt

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
add_swift_library(swiftCoreLocation ${SWIFT_SDK_OVERLAY_LIBRARY_BUILD_TYPES} IS_SDK_OVERLAY
2-
CoreLocation.swift
3-
2+
CoreLocation.swift.gyb
43
SWIFT_COMPILE_FLAGS "${SWIFT_RUNTIME_SWIFT_COMPILE_FLAGS}"
54
LINK_FLAGS "${SWIFT_RUNTIME_SWIFT_LINK_FLAGS}"
65
SWIFT_MODULE_DEPENDS Foundation

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

+6
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

+1-1
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

-39
This file was deleted.
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+
+10
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+
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") }
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+
NSValue.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+
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

+2-3
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ 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
SWIFT_COMPILE_FLAGS "${SWIFT_RUNTIME_SWIFT_COMPILE_FLAGS}"
@@ -14,7 +14,6 @@ add_swift_library(swiftSceneKit ${SWIFT_SDK_OVERLAY_LIBRARY_BUILD_TYPES} IS_SDK_
1414
SWIFT_MODULE_DEPENDS_OSX ${SWIFT_SCENEKIT_DEPENDENCIES_NON_WATCHOS} AppKit
1515
SWIFT_MODULE_DEPENDS_IOS ${SWIFT_SCENEKIT_DEPENDENCIES_NON_WATCHOS} UIKit
1616
SWIFT_MODULE_DEPENDS_TVOS ${SWIFT_SCENEKIT_DEPENDENCIES_NON_WATCHOS} UIKit
17-
SWIFT_MODULE_DEPENDS_WATCHOS Foundation
18-
SWIFT_MODULE_DEPENDS simd
17+
SWIFT_MODULE_DEPENDS Foundation simd
1918
FRAMEWORK_DEPENDS_WEAK SceneKit)
2019

0 commit comments

Comments
 (0)