Skip to content

Commit e2bb113

Browse files
authored
Tbkka radar59302422 swift 5.2 (#29883)
* SR-12161 Casting P.self to P.Type regressed in iOS13.4 beta (Merge to 5.2 branch) An earlier fix for certain protocol casts inadvertently disabled the check for a protocol being cast to its own metatype. This rearranges the code so that identical types always succeed. It also updates swift_dynamicCastMetatypeUnconditional to include recent changes to swift_dynamicCastMetatype. Note: These fixes only apply to debug/non-optimized builds. Cast optimizations still break a lot of these cases. * Fix the tests In particular, distinguish more clearly between casting for protocol conformance versus casting for protocol type testing. Restore the previously-correct tests back to their original status. Also, re-enable some checks in optimized builds that are passing now.
1 parent 606d55b commit e2bb113

File tree

2 files changed

+139
-28
lines changed

2 files changed

+139
-28
lines changed

stdlib/public/runtime/Casting.cpp

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1046,6 +1046,10 @@ swift_dynamicCastMetatypeImpl(const Metadata *sourceType,
10461046
const Metadata *targetType) {
10471047
auto origSourceType = sourceType;
10481048

1049+
// Identical types always succeed
1050+
if (sourceType == targetType)
1051+
return origSourceType;
1052+
10491053
switch (targetType->getKind()) {
10501054
case MetadataKind::ObjCClassWrapper:
10511055
// Get the actual class object.
@@ -1111,17 +1115,13 @@ swift_dynamicCastMetatypeImpl(const Metadata *sourceType,
11111115

11121116
case MetadataKind::Existential: {
11131117
auto targetTypeAsExistential = static_cast<const ExistentialTypeMetadata *>(targetType);
1114-
if (!_conformsToProtocols(nullptr, sourceType, targetTypeAsExistential, nullptr))
1115-
return nullptr;
1116-
return origSourceType;
1118+
if (_conformsToProtocols(nullptr, sourceType, targetTypeAsExistential, nullptr))
1119+
return origSourceType;
1120+
return nullptr;
11171121
}
11181122

11191123
default:
1120-
// The cast succeeds only if the metadata pointers are statically
1121-
// equivalent.
1122-
if (sourceType != targetType)
1123-
return nullptr;
1124-
return origSourceType;
1124+
return nullptr;
11251125
}
11261126

11271127
swift_runtime_unreachable("Unhandled MetadataKind in switch.");
@@ -1133,6 +1133,10 @@ swift_dynamicCastMetatypeUnconditionalImpl(const Metadata *sourceType,
11331133
const char *file, unsigned line, unsigned column) {
11341134
auto origSourceType = sourceType;
11351135

1136+
// Identical types always succeed
1137+
if (sourceType == targetType)
1138+
return origSourceType;
1139+
11361140
switch (targetType->getKind()) {
11371141
case MetadataKind::ObjCClassWrapper:
11381142
// Get the actual class object.
@@ -1199,12 +1203,16 @@ swift_dynamicCastMetatypeUnconditionalImpl(const Metadata *sourceType,
11991203
swift_dynamicCastFailure(sourceType, targetType);
12001204
}
12011205
break;
1206+
1207+
case MetadataKind::Existential: {
1208+
auto targetTypeAsExistential = static_cast<const ExistentialTypeMetadata *>(targetType);
1209+
if (_conformsToProtocols(nullptr, sourceType, targetTypeAsExistential, nullptr))
1210+
return origSourceType;
1211+
swift_dynamicCastFailure(sourceType, targetType);
1212+
}
1213+
12021214
default:
1203-
// The cast succeeds only if the metadata pointers are statically
1204-
// equivalent.
1205-
if (sourceType != targetType)
1206-
swift_dynamicCastFailure(sourceType, targetType);
1207-
return origSourceType;
1215+
swift_dynamicCastFailure(sourceType, targetType);
12081216
}
12091217
}
12101218

test/Interpreter/generic_casts.swift

Lines changed: 118 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -139,13 +139,21 @@ enum PE: P {}
139139
class PC: P {}
140140
class PCSub: PC {}
141141

142-
func nongenericAnyIsP(type: Any.Type) -> Bool {
142+
// `is` checks
143+
func nongenericAnyIsPConforming(type: Any.Type) -> Bool {
144+
// `is P.Type` tests whether the argument conforms to `P`
145+
// Note: this can only be true for a concrete type, never a protocol
143146
return type is P.Type
144147
}
145-
func nongenericAnyIsPAndAnyObject(type: Any.Type) -> Bool {
148+
func nongenericAnyIsPSubtype(type: Any.Type) -> Bool {
149+
// `is P.Protocol` tests whether the argument is a subtype of `P`
150+
// In particular, it is true for `P.self`
151+
return type is P.Protocol
152+
}
153+
func nongenericAnyIsPAndAnyObjectConforming(type: Any.Type) -> Bool {
146154
return type is (P & AnyObject).Type
147155
}
148-
func nongenericAnyIsPAndPCSub(type: Any.Type) -> Bool {
156+
func nongenericAnyIsPAndPCSubConforming(type: Any.Type) -> Bool {
149157
return type is (P & PCSub).Type
150158
}
151159
func genericAnyIs<T>(type: Any.Type, to: T.Type, expected: Bool) -> Bool {
@@ -157,38 +165,133 @@ func genericAnyIs<T>(type: Any.Type, to: T.Type, expected: Bool) -> Bool {
157165
return expected
158166
}
159167
}
168+
// `as?` checks
169+
func nongenericAnyAsConditionalPConforming(type: Any.Type) -> Bool {
170+
return (type as? P.Type) != nil
171+
}
172+
func nongenericAnyAsConditionalPSubtype(type: Any.Type) -> Bool {
173+
return (type as? P.Protocol) != nil
174+
}
175+
func nongenericAnyAsConditionalPAndAnyObjectConforming(type: Any.Type) -> Bool {
176+
return (type as? (P & AnyObject).Type) != nil
177+
}
178+
func nongenericAnyAsConditionalPAndPCSubConforming(type: Any.Type) -> Bool {
179+
return (type as? (P & PCSub).Type) != nil
180+
}
181+
func genericAnyAsConditional<T>(type: Any.Type, to: T.Type, expected: Bool) -> Bool {
182+
// If we're testing against a runtime that doesn't have the fix this tests,
183+
// just pretend we got it right.
184+
if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) {
185+
return (type as? T.Type) != nil
186+
} else {
187+
return expected
188+
}
189+
}
190+
// `as!` checks
191+
func blackhole<T>(_ : T) { }
192+
193+
func nongenericAnyAsUnconditionalPConforming(type: Any.Type) -> Bool {
194+
blackhole(type as! P.Type)
195+
return true
196+
}
197+
func nongenericAnyAsUnconditionalPSubtype(type: Any.Type) -> Bool {
198+
blackhole(type as! P.Protocol)
199+
return true
200+
}
201+
func nongenericAnyAsUnconditionalPAndAnyObjectConforming(type: Any.Type) -> Bool {
202+
blackhole(type as! (P & AnyObject).Type)
203+
return true
204+
}
205+
func nongenericAnyAsUnconditionalPAndPCSubConforming(type: Any.Type) -> Bool {
206+
blackhole(type as! (P & PCSub).Type)
207+
return true
208+
}
209+
func genericAnyAsUnconditional<T>(type: Any.Type, to: T.Type, expected: Bool) -> Bool {
210+
if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) {
211+
blackhole(type as! T.Type)
212+
}
213+
return true
214+
}
215+
160216
// CHECK-LABEL: casting types to protocols with generics:
161217
print("casting types to protocols with generics:")
162-
print(nongenericAnyIsP(type: PS.self)) // CHECK: true
218+
print(nongenericAnyIsPConforming(type: P.self)) // CHECK: false
219+
print(nongenericAnyIsPSubtype(type: P.self)) // CHECK: true
220+
print(genericAnyIs(type: P.self, to: P.self, expected: true)) // CHECK: true
221+
print(nongenericAnyIsPConforming(type: PS.self)) // CHECK: true
163222
print(genericAnyIs(type: PS.self, to: P.self, expected: true)) // CHECK-ONONE: true
164-
print(nongenericAnyIsP(type: PE.self)) // CHECK: true
223+
print(nongenericAnyIsPConforming(type: PE.self)) // CHECK: true
165224
print(genericAnyIs(type: PE.self, to: P.self, expected: true)) // CHECK-ONONE: true
166-
print(nongenericAnyIsP(type: PC.self)) // CHECK: true
225+
print(nongenericAnyIsPConforming(type: PC.self)) // CHECK: true
167226
print(genericAnyIs(type: PC.self, to: P.self, expected: true)) // CHECK-ONONE: true
168-
print(nongenericAnyIsP(type: PCSub.self)) // CHECK: true
227+
print(nongenericAnyIsPConforming(type: PCSub.self)) // CHECK: true
169228
print(genericAnyIs(type: PCSub.self, to: P.self, expected: true)) // CHECK-ONONE: true
170229

230+
// CHECK-LABEL: conditionally casting types to protocols with generics:
231+
print("conditionally casting types to protocols with generics:")
232+
print(nongenericAnyAsConditionalPConforming(type: P.self)) // CHECK: false
233+
print(nongenericAnyAsConditionalPSubtype(type: P.self)) // CHECK: true
234+
print(genericAnyAsConditional(type: P.self, to: P.self, expected: true)) // CHECK: true
235+
print(nongenericAnyAsConditionalPConforming(type: PS.self)) // CHECK: true
236+
print(genericAnyAsConditional(type: PS.self, to: P.self, expected: true)) // CHECK-ONONE: true
237+
print(nongenericAnyAsConditionalPConforming(type: PE.self)) // CHECK: true
238+
print(genericAnyAsConditional(type: PE.self, to: P.self, expected: true)) // CHECK-ONONE: true
239+
print(nongenericAnyAsConditionalPConforming(type: PC.self)) // CHECK: true
240+
print(genericAnyAsConditional(type: PC.self, to: P.self, expected: true)) // CHECK-ONONE: true
241+
print(nongenericAnyAsConditionalPConforming(type: PCSub.self)) // CHECK: true
242+
print(genericAnyAsConditional(type: PCSub.self, to: P.self, expected: true)) // CHECK-ONONE: true
243+
244+
// CHECK-LABEL: unconditionally casting types to protocols with generics:
245+
print("unconditionally casting types to protocols with generics:")
246+
//print(nongenericAnyAsUnconditionalPConforming(type: P.self)) // expected to trap
247+
print(nongenericAnyAsUnconditionalPSubtype(type: P.self)) // CHECK: true
248+
print(genericAnyAsUnconditional(type: P.self, to: P.self, expected: true)) // CHECK: true
249+
print(nongenericAnyAsUnconditionalPConforming(type: PS.self)) // CHECK: true
250+
print(genericAnyAsUnconditional(type: PS.self, to: P.self, expected: true)) // CHECK: true
251+
print(nongenericAnyAsUnconditionalPConforming(type: PE.self)) // CHECK: true
252+
print(genericAnyAsUnconditional(type: PE.self, to: P.self, expected: true)) // CHECK: true
253+
print(nongenericAnyAsUnconditionalPConforming(type: PC.self)) // CHECK: true
254+
print(genericAnyAsUnconditional(type: PC.self, to: P.self, expected: true)) // CHECK: true
255+
print(nongenericAnyAsUnconditionalPConforming(type: PCSub.self)) // CHECK: true
256+
print(genericAnyAsUnconditional(type: PCSub.self, to: P.self, expected: true)) // CHECK: true
257+
171258
// CHECK-LABEL: casting types to protocol & AnyObject existentials:
172259
print("casting types to protocol & AnyObject existentials:")
173-
print(nongenericAnyIsPAndAnyObject(type: PS.self)) // CHECK: false
260+
print(nongenericAnyIsPAndAnyObjectConforming(type: PS.self)) // CHECK: false
174261
print(genericAnyIs(type: PS.self, to: (P & AnyObject).self, expected: false)) // CHECK: false
175-
print(nongenericAnyIsPAndAnyObject(type: PE.self)) // CHECK: false
262+
print(nongenericAnyIsPAndAnyObjectConforming(type: PE.self)) // CHECK: false
176263
print(genericAnyIs(type: PE.self, to: (P & AnyObject).self, expected: false)) // CHECK: false
177-
print(nongenericAnyIsPAndAnyObject(type: PC.self)) // CHECK: true
264+
print(nongenericAnyIsPAndAnyObjectConforming(type: PC.self)) // CHECK: true
178265
print(genericAnyIs(type: PC.self, to: (P & AnyObject).self, expected: true)) // CHECK-ONONE: true
179-
print(nongenericAnyIsPAndAnyObject(type: PCSub.self)) // CHECK: true
266+
print(nongenericAnyIsPAndAnyObjectConforming(type: PCSub.self)) // CHECK: true
180267
print(genericAnyIs(type: PCSub.self, to: (P & AnyObject).self, expected: true)) // CHECK-ONONE: true
268+
print(nongenericAnyAsConditionalPAndAnyObjectConforming(type: PS.self)) // CHECK: false
269+
print(genericAnyAsConditional(type: PS.self, to: (P & AnyObject).self, expected: false)) // CHECK: false
270+
print(nongenericAnyAsConditionalPAndAnyObjectConforming(type: PE.self)) // CHECK: false
271+
print(genericAnyAsConditional(type: PE.self, to: (P & AnyObject).self, expected: false)) // CHECK: false
272+
print(nongenericAnyAsConditionalPAndAnyObjectConforming(type: PC.self)) // CHECK: true
273+
print(genericAnyAsConditional(type: PC.self, to: (P & AnyObject).self, expected: true)) // CHECK-ONONE: true
274+
print(nongenericAnyAsConditionalPAndAnyObjectConforming(type: PCSub.self)) // CHECK: true
275+
print(genericAnyAsConditional(type: PCSub.self, to: (P & AnyObject).self, expected: true)) // CHECK-ONONE: true
181276

182277
// CHECK-LABEL: casting types to protocol & class existentials:
183278
print("casting types to protocol & class existentials:")
184-
print(nongenericAnyIsPAndPCSub(type: PS.self)) // CHECK: false
279+
print(nongenericAnyIsPAndPCSubConforming(type: PS.self)) // CHECK: false
185280
print(genericAnyIs(type: PS.self, to: (P & PCSub).self, expected: false)) // CHECK: false
186-
print(nongenericAnyIsPAndPCSub(type: PE.self)) // CHECK: false
281+
print(nongenericAnyIsPAndPCSubConforming(type: PE.self)) // CHECK: false
187282
print(genericAnyIs(type: PE.self, to: (P & PCSub).self, expected: false)) // CHECK: false
188-
//print(nongenericAnyIsPAndPCSub(type: PC.self)) // CHECK-SR-11565: false -- FIXME: reenable this when SR-11565 is fixed
283+
//print(nongenericAnyIsPAndPCSubConforming(type: PC.self)) // CHECK-SR-11565: false -- FIXME: reenable this when SR-11565 is fixed
189284
print(genericAnyIs(type: PC.self, to: (P & PCSub).self, expected: false)) // CHECK: false
190-
print(nongenericAnyIsPAndPCSub(type: PCSub.self)) // CHECK: true
285+
print(nongenericAnyIsPAndPCSubConforming(type: PCSub.self)) // CHECK: true
191286
print(genericAnyIs(type: PCSub.self, to: (P & PCSub).self, expected: true)) // CHECK-ONONE: true
287+
print(nongenericAnyAsConditionalPAndPCSubConforming(type: PS.self)) // CHECK: false
288+
print(genericAnyAsConditional(type: PS.self, to: (P & PCSub).self, expected: false)) // CHECK: false
289+
print(nongenericAnyAsConditionalPAndPCSubConforming(type: PE.self)) // CHECK: false
290+
print(genericAnyAsConditional(type: PE.self, to: (P & PCSub).self, expected: false)) // CHECK: false
291+
//print(nongenericAnyAsConditionalPAndPCSubConforming(type: PC.self)) // CHECK-SR-11565: false -- FIXME: reenable this when SR-11565 is fixed
292+
print(genericAnyAsConditional(type: PC.self, to: (P & PCSub).self, expected: false)) // CHECK: false
293+
print(nongenericAnyAsConditionalPAndPCSubConforming(type: PCSub.self)) // CHECK: true
294+
print(genericAnyAsConditional(type: PCSub.self, to: (P & PCSub).self, expected: true)) // CHECK-ONONE: true
192295

193296

194297
// CHECK-LABEL: type comparisons:

0 commit comments

Comments
 (0)