Skip to content

Commit 396e484

Browse files
committed
SIL: Handle dynamic casts between ObjC generic classes.
The type parameters disappear at runtime, so consider this when classifying dynamic casts.
1 parent a52c6ba commit 396e484

File tree

4 files changed

+82
-7
lines changed

4 files changed

+82
-7
lines changed

lib/SIL/DynamicCasts.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,37 @@ swift::classifyDynamicCast(Module *M,
367367
auto targetClass = target.getClassOrBoundGenericClass();
368368
if (sourceClass) {
369369
if (targetClass) {
370+
// Imported Objective-C generics don't check the generic parameters, which
371+
// are lost at runtime.
372+
if (sourceClass->usesObjCGenericsModel()) {
373+
374+
if (sourceClass == targetClass)
375+
return DynamicCastFeasibility::WillSucceed;
376+
377+
if (targetClass->usesObjCGenericsModel()) {
378+
// If both classes are ObjC generics, the cast may succeed if the
379+
// classes are related, irrespective of their generic parameters.
380+
auto isDeclSuperclass = [&](ClassDecl *proposedSuper,
381+
ClassDecl *proposedSub) -> bool {
382+
do {
383+
if (proposedSuper == proposedSub)
384+
return true;
385+
} while ((proposedSub = proposedSub->getSuperclassDecl()));
386+
387+
return false;
388+
};
389+
390+
if (isDeclSuperclass(sourceClass, targetClass))
391+
return DynamicCastFeasibility::MaySucceed;
392+
393+
if (isDeclSuperclass(targetClass, sourceClass)) {
394+
return DynamicCastFeasibility::WillSucceed;
395+
}
396+
return DynamicCastFeasibility::WillFail;
397+
}
398+
}
399+
400+
370401
if (target->isExactSuperclassOf(source, nullptr))
371402
return DynamicCastFeasibility::WillSucceed;
372403
if (target->isBindableToSuperclassOf(source, nullptr))

lib/SIL/SILVerifier.cpp

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2219,10 +2219,18 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
22192219
"upcast operand must be a class or class metatype instance");
22202220
CanType opInstTy(UI->getOperand()->getType().castTo<MetatypeType>()
22212221
->getInstanceType());
2222-
require(instTy->getClassOrBoundGenericClass(),
2222+
auto instClass = instTy->getClassOrBoundGenericClass();
2223+
require(instClass,
22232224
"upcast must convert a class metatype to a class metatype");
2224-
require(instTy->isExactSuperclassOf(opInstTy, nullptr),
2225-
"upcast must cast to a superclass or an existential metatype");
2225+
2226+
if (instClass->usesObjCGenericsModel()) {
2227+
require(instClass->getDeclaredTypeInContext()
2228+
->isBindableToSuperclassOf(opInstTy, nullptr),
2229+
"upcast must cast to a superclass or an existential metatype");
2230+
} else {
2231+
require(instTy->isExactSuperclassOf(opInstTy, nullptr),
2232+
"upcast must cast to a superclass or an existential metatype");
2233+
}
22262234
return;
22272235
}
22282236

@@ -2245,10 +2253,18 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
22452253
FromTy.getSwiftRValueType().getAnyOptionalObjectType());
22462254
}
22472255

2248-
require(ToTy.getClassOrBoundGenericClass(),
2256+
auto ToClass = ToTy.getClassOrBoundGenericClass();
2257+
require(ToClass,
22492258
"upcast must convert a class instance to a class type");
2250-
require(ToTy.isExactSuperclassOf(FromTy),
2251-
"upcast must cast to a superclass");
2259+
if (ToClass->usesObjCGenericsModel()) {
2260+
require(ToClass->getDeclaredTypeInContext()
2261+
->isBindableToSuperclassOf(FromTy.getSwiftRValueType(),
2262+
nullptr),
2263+
"upcast must cast to a superclass or an existential metatype");
2264+
} else {
2265+
require(ToTy.isExactSuperclassOf(FromTy),
2266+
"upcast must cast to a superclass or an existential metatype");
2267+
}
22522268
}
22532269

22542270
void checkIsNonnullInst(IsNonnullInst *II) {
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -O -emit-sil %s | FileCheck %s
2+
// REQUIRES: objc_interop
3+
4+
import objc_generics
5+
6+
// CHECK-LABEL: sil [noinline] @_TF26cast_folding_objc_generics26testObjCGenericParamChange
7+
// CHECK: upcast
8+
// CHECK-NOT: int_trap
9+
@inline(never)
10+
public func testObjCGenericParamChange(_ a: GenericClass<NSMutableString>) -> GenericClass<NSString> {
11+
return a as! GenericClass<NSString>
12+
}
13+
14+
// CHECK-LABEL: sil [noinline] @_TF26cast_folding_objc_generics34testObjCGenericParamChangeSubclass
15+
// CHECK: unconditional_checked_cast
16+
// CHECK-NOT: int_trap
17+
@inline(never)
18+
public func testObjCGenericParamChangeSubclass(_ a: GenericClass<NSMutableString>) -> GenericSubclass<NSString> {
19+
return a as! GenericSubclass<NSString>
20+
}
21+
22+
// CHECK-LABEL: sil [noinline] @_TF26cast_folding_objc_generics36testObjCGenericParamChangeSuperclass
23+
// CHECK: upcast
24+
// CHECK-NOT: int_trap
25+
@inline(never)
26+
public func testObjCGenericParamChangeSuperclass(_ a: GenericSubclass<NSMutableString>) -> GenericClass<NSString> {
27+
return a as! GenericClass<NSString>
28+
}

test/SILOptimizer/cast_folding_objc_no_foundation.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-swift-frontend -O -emit-sil %s | FileCheck %s
1+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -O -emit-sil %s | FileCheck %s
22
// REQUIRES: objc_interop
33

44
// Note: no 'import Foundation'

0 commit comments

Comments
 (0)