Skip to content

Commit 4082a5c

Browse files
committed
Merge pull request #2621 from jckarter/dont-warn-on-objc-generic-casts-master
Sema: Don't warn on casts that change an ObjC generic's parameters.
2 parents e63eaa8 + 396e484 commit 4082a5c

File tree

7 files changed

+117
-16
lines changed

7 files changed

+117
-16
lines changed

include/swift/AST/Decl.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2255,7 +2255,7 @@ class ValueDecl : public Decl {
22552255
bool isObjC() const {
22562256
return getAttrs().hasAttribute<ObjCAttr>();
22572257
}
2258-
2258+
22592259
void setIsObjC(bool Value);
22602260

22612261
/// Is this declaration marked with 'final'?
@@ -3343,6 +3343,13 @@ class ClassDecl : public NominalTypeDecl {
33433343
auto NTD = dyn_cast<NominalTypeDecl>(C);
33443344
return NTD && classof(NTD);
33453345
}
3346+
3347+
/// Returns true if the decl uses the Objective-C generics model.
3348+
///
3349+
/// This is true of imported Objective-C classes.
3350+
bool usesObjCGenericsModel() const {
3351+
return isObjC() && hasClangNode() && isGenericContext();
3352+
}
33463353
};
33473354

33483355

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) {

lib/Sema/TypeCheckConstraints.cpp

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2746,7 +2746,7 @@ CheckedCastKind TypeChecker::typeCheckCheckedCast(Type fromType,
27462746
if (toExistential || fromExistential || fromArchetype || toArchetype)
27472747
return CheckedCastKind::ValueCast;
27482748

2749-
// Reality check casts between concrete types.
2749+
// Check for casts between concrete types that cannot succeed.
27502750

27512751
ConstraintSystem cs(*this, dc, ConstraintSystemOptions());
27522752

@@ -2821,14 +2821,25 @@ CheckedCastKind TypeChecker::typeCheckCheckedCast(Type fromType,
28212821
// This "always fails" diagnosis makes no sense when paired with the CF
28222822
// one.
28232823
auto clas = toType->getClassOrBoundGenericClass();
2824-
if (!clas || !clas->isForeign()) {
2825-
if (suppressDiagnostics) {
2826-
return CheckedCastKind::Unresolved;
2827-
}
2828-
diagnose(diagLoc, diag::downcast_to_unrelated, origFromType, origToType)
2829-
.highlight(diagFromRange)
2830-
.highlight(diagToRange);
2824+
if (clas && clas->isForeign())
2825+
return CheckedCastKind::ValueCast;
2826+
2827+
// Don't warn on casts that change the generic parameters of ObjC generic
2828+
// classes. This may be necessary to force-fit ObjC APIs that depend on
2829+
// covariance, or for APIs where the generic parameter annotations in the
2830+
// ObjC headers are inaccurate.
2831+
if (clas && clas->usesObjCGenericsModel()) {
2832+
if (fromType->getClassOrBoundGenericClass() == clas)
2833+
return CheckedCastKind::ValueCast;
28312834
}
2835+
2836+
if (suppressDiagnostics) {
2837+
return CheckedCastKind::Unresolved;
2838+
}
2839+
diagnose(diagLoc, diag::downcast_to_unrelated, origFromType, origToType)
2840+
.highlight(diagFromRange)
2841+
.highlight(diagToRange);
2842+
28322843
return CheckedCastKind::ValueCast;
28332844
}
28342845

test/ClangModules/objc_bridging_generics.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,3 +308,11 @@ class SwiftConcreteSubclassC<T>: GenericClass<NSString> {
308308
override func arrayOfThings() -> [NSString] {}
309309
}
310310

311+
// FIXME: Some generic ObjC APIs rely on covariance. We don't handle this well
312+
// in Swift yet, but ensure we don't emit spurious warnings when
313+
// `as!` is used to force types to line up.
314+
func foo(x: GenericClass<NSMutableString>) {
315+
let x2 = x as! GenericClass<NSString>
316+
takeGenericClass(x2)
317+
takeGenericClass(unsafeBitCast(x, to: GenericClass<NSString>.self))
318+
}
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)