Skip to content

Commit 78557ae

Browse files
authored
Merge pull request #4024 from jckarter/metatype-universal-bridging-3.0
rdar://problem/27526826 Handle universal bridging of metatypes.
2 parents 60033a2 + c8659ca commit 78557ae

File tree

2 files changed

+71
-7
lines changed

2 files changed

+71
-7
lines changed

stdlib/public/runtime/Casting.cpp

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2772,7 +2772,28 @@ static id bridgeAnythingNonVerbatimToObjectiveC(OpaqueValue *src,
27722772
return result;
27732773
}
27742774

2775-
if (auto srcBridgeWitness = findBridgeWitness(srcType)) {
2775+
// Handle metatypes.
2776+
if (isa<ExistentialMetatypeMetadata>(srcType)
2777+
|| isa<MetatypeMetadata>(srcType)) {
2778+
const Metadata *srcMetatypeValue;
2779+
memcpy(&srcMetatypeValue, src, sizeof(srcMetatypeValue));
2780+
2781+
// Class metatypes bridge to their class object.
2782+
if (isa<ClassMetadata>(srcMetatypeValue)
2783+
|| isa<ObjCClassWrapperMetadata>(srcMetatypeValue)) {
2784+
return (id)srcMetatypeValue->getClassObject();
2785+
2786+
// ObjC protocols bridge to their Protocol object.
2787+
} else if (auto existential
2788+
= dyn_cast<ExistentialTypeMetadata>(srcMetatypeValue)) {
2789+
if (existential->isObjC() && existential->Protocols.NumProtocols == 1) {
2790+
// Though they're statically-allocated globals, Protocol inherits
2791+
// NSObject's default refcounting behavior so must be retained.
2792+
auto protocolObj = (id)existential->Protocols[0];
2793+
return objc_retain(protocolObj);
2794+
}
2795+
}
2796+
} else if (auto srcBridgeWitness = findBridgeWitness(srcType)) {
27762797
// Bridge the source value to an object.
27772798
auto srcBridgedObject =
27782799
srcBridgeWitness->bridgeToObjectiveC(src, srcType, srcBridgeWitness);

test/1_stdlib/BridgeIdAsAny.swift.gyb

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,17 @@ func metatypeCanBeDynamicallyCast(original: Int.Type,
103103
expectTrue(original == bridged as! Int.Type)
104104
expectTrue(original == bridged as! Any.Type)
105105
}
106+
func metatypeCanBeDynamicallyCast(original: CFString.Type,
107+
bridged: AnyObject) {
108+
expectTrue(original == bridged as! CFString.Type)
109+
expectTrue(original == bridged as! Any.Type)
110+
}
111+
func metatypeCanBeDynamicallyCastGenerically<T>(original: T.Type,
112+
bridged: AnyObject) {
113+
expectTrue(original == bridged as! T.Type)
114+
expectTrue(original == bridged as! Any.Type)
115+
}
116+
106117

107118
func guineaPigFunction() -> Int {
108119
return 1738
@@ -114,17 +125,49 @@ func functionCanBeDynamicallyCast(original: () -> Int,
114125
expectEqual(original(), try! (bridged as! () throws -> Int)())
115126
}
116127

128+
func classMetatypePreservesIdentity<T: AnyObject>(original: T.Type,
129+
bridged: AnyObject) {
130+
expectTrue(original as AnyObject === bridged)
131+
expectTrue(original as AnyObject.Type as AnyObject === bridged)
132+
expectTrue(original as Any.Type as AnyObject === bridged)
133+
}
134+
135+
func classMetatypePreservesIdentityGenerically<T>(original: T.Type,
136+
bridged: AnyObject) {
137+
expectTrue(original as AnyObject === bridged)
138+
expectTrue(original as Any.Type as AnyObject === bridged)
139+
}
140+
141+
func protocolObjectPreservesIdentity(original: NSCopying.Protocol,
142+
bridged: AnyObject) {
143+
let proto: Protocol = original
144+
expectTrue(proto === bridged)
145+
}
146+
147+
protocol P {}
148+
117149
// We want to exhaustively check all paths through the bridging and dynamic
118150
// casting infrastructure, so expand out test cases that wrap the different
119151
// interesting bridging cases in different kinds of existential container.
120152
%{
121153
testCases = [
122-
("classes", "LifetimeTracked(0)", "bridgedObjectPreservesIdentity", True),
123-
("strings", '"vitameatavegamin"', "stringBridgesToEqualNSString", True),
124-
("unbridged type", "KnownUnbridged()", "boxedTypeRoundTripsThroughDynamicCasting", True),
125-
("tuple", '(1, "2")', "tupleCanBeDynamicallyCast", False),
126-
("metatype", 'Int.self', "metatypeCanBeDynamicallyCast", False),
127-
("function", 'guineaPigFunction', "functionCanBeDynamicallyCast", False),
154+
("classes", "LifetimeTracked(0)", "bridgedObjectPreservesIdentity", True),
155+
("strings", '"vitameatavegamin"', "stringBridgesToEqualNSString", True),
156+
("unbridged type", "KnownUnbridged()", "boxedTypeRoundTripsThroughDynamicCasting", True),
157+
("tuple", '(1, "2")', "tupleCanBeDynamicallyCast", False),
158+
("metatype", 'Int.self', "metatypeCanBeDynamicallyCast", False),
159+
("generic metatype", 'Int.self', "metatypeCanBeDynamicallyCastGenerically", False),
160+
("CF metatype", 'CFString.self', "metatypeCanBeDynamicallyCast", False),
161+
("generic CF metatype", 'CFString.self', "metatypeCanBeDynamicallyCastGenerically", False),
162+
("class metatype", 'LifetimeTracked.self', "classMetatypePreservesIdentity", False),
163+
("objc metatype", 'NSObject.self', "classMetatypePreservesIdentity", False),
164+
("protocol", 'P.self', "metatypeCanBeDynamicallyCastGenerically", False),
165+
("objc protocol", 'NSCopying.self', "protocolObjectPreservesIdentity", False),
166+
("objc protocol composition", '(NSCopying & NSCoding).self', "metatypeCanBeDynamicallyCastGenerically", False),
167+
("mixed protocol composition", '(NSCopying & P).self', "metatypeCanBeDynamicallyCastGenerically", False),
168+
("generic class metatype", 'LifetimeTracked.self', "classMetatypePreservesIdentityGenerically", False),
169+
("generic objc metatype", 'NSObject.self', "classMetatypePreservesIdentityGenerically", False),
170+
("function", 'guineaPigFunction', "functionCanBeDynamicallyCast", False),
128171
]
129172
}%
130173

0 commit comments

Comments
 (0)