Skip to content

Commit

Permalink
SIL: Handle dynamic casts between ObjC generic classes.
Browse files Browse the repository at this point in the history
The type parameters disappear at runtime, so consider this when classifying dynamic casts.
  • Loading branch information
jckarter committed May 23, 2016
1 parent a52c6ba commit 396e484
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 7 deletions.
31 changes: 31 additions & 0 deletions lib/SIL/DynamicCasts.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,37 @@ swift::classifyDynamicCast(Module *M,
auto targetClass = target.getClassOrBoundGenericClass();
if (sourceClass) {
if (targetClass) {
// Imported Objective-C generics don't check the generic parameters, which
// are lost at runtime.
if (sourceClass->usesObjCGenericsModel()) {

if (sourceClass == targetClass)
return DynamicCastFeasibility::WillSucceed;

if (targetClass->usesObjCGenericsModel()) {
// If both classes are ObjC generics, the cast may succeed if the
// classes are related, irrespective of their generic parameters.
auto isDeclSuperclass = [&](ClassDecl *proposedSuper,
ClassDecl *proposedSub) -> bool {
do {
if (proposedSuper == proposedSub)
return true;
} while ((proposedSub = proposedSub->getSuperclassDecl()));

return false;
};

if (isDeclSuperclass(sourceClass, targetClass))
return DynamicCastFeasibility::MaySucceed;

if (isDeclSuperclass(targetClass, sourceClass)) {
return DynamicCastFeasibility::WillSucceed;
}
return DynamicCastFeasibility::WillFail;
}
}


if (target->isExactSuperclassOf(source, nullptr))
return DynamicCastFeasibility::WillSucceed;
if (target->isBindableToSuperclassOf(source, nullptr))
Expand Down
28 changes: 22 additions & 6 deletions lib/SIL/SILVerifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2219,10 +2219,18 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
"upcast operand must be a class or class metatype instance");
CanType opInstTy(UI->getOperand()->getType().castTo<MetatypeType>()
->getInstanceType());
require(instTy->getClassOrBoundGenericClass(),
auto instClass = instTy->getClassOrBoundGenericClass();
require(instClass,
"upcast must convert a class metatype to a class metatype");
require(instTy->isExactSuperclassOf(opInstTy, nullptr),
"upcast must cast to a superclass or an existential metatype");

if (instClass->usesObjCGenericsModel()) {
require(instClass->getDeclaredTypeInContext()
->isBindableToSuperclassOf(opInstTy, nullptr),
"upcast must cast to a superclass or an existential metatype");
} else {
require(instTy->isExactSuperclassOf(opInstTy, nullptr),
"upcast must cast to a superclass or an existential metatype");
}
return;
}

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

require(ToTy.getClassOrBoundGenericClass(),
auto ToClass = ToTy.getClassOrBoundGenericClass();
require(ToClass,
"upcast must convert a class instance to a class type");
require(ToTy.isExactSuperclassOf(FromTy),
"upcast must cast to a superclass");
if (ToClass->usesObjCGenericsModel()) {
require(ToClass->getDeclaredTypeInContext()
->isBindableToSuperclassOf(FromTy.getSwiftRValueType(),
nullptr),
"upcast must cast to a superclass or an existential metatype");
} else {
require(ToTy.isExactSuperclassOf(FromTy),
"upcast must cast to a superclass or an existential metatype");
}
}

void checkIsNonnullInst(IsNonnullInst *II) {
Expand Down
28 changes: 28 additions & 0 deletions test/SILOptimizer/cast_folding_objc_generics.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -O -emit-sil %s | FileCheck %s
// REQUIRES: objc_interop

import objc_generics

// CHECK-LABEL: sil [noinline] @_TF26cast_folding_objc_generics26testObjCGenericParamChange
// CHECK: upcast
// CHECK-NOT: int_trap
@inline(never)
public func testObjCGenericParamChange(_ a: GenericClass<NSMutableString>) -> GenericClass<NSString> {
return a as! GenericClass<NSString>
}

// CHECK-LABEL: sil [noinline] @_TF26cast_folding_objc_generics34testObjCGenericParamChangeSubclass
// CHECK: unconditional_checked_cast
// CHECK-NOT: int_trap
@inline(never)
public func testObjCGenericParamChangeSubclass(_ a: GenericClass<NSMutableString>) -> GenericSubclass<NSString> {
return a as! GenericSubclass<NSString>
}

// CHECK-LABEL: sil [noinline] @_TF26cast_folding_objc_generics36testObjCGenericParamChangeSuperclass
// CHECK: upcast
// CHECK-NOT: int_trap
@inline(never)
public func testObjCGenericParamChangeSuperclass(_ a: GenericSubclass<NSMutableString>) -> GenericClass<NSString> {
return a as! GenericClass<NSString>
}
2 changes: 1 addition & 1 deletion test/SILOptimizer/cast_folding_objc_no_foundation.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// RUN: %target-swift-frontend -O -emit-sil %s | FileCheck %s
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -O -emit-sil %s | FileCheck %s
// REQUIRES: objc_interop

// Note: no 'import Foundation'
Expand Down

0 comments on commit 396e484

Please sign in to comment.