Skip to content

Commit cfcb6ea

Browse files
committed
PrintAsObjC: Handle new upper bounds of Array/Dictionary/Set correctly.
We would crash because 'Any' doesn't have a corresponding bridged type through the normal bridging mechanism. Handle this correctly, and correctly recognize 'AnyHashable' and 'Any' as the upper bounds of Dictionary, Set, and Array so we present the unqualified NS types in the generated header.
1 parent 6b1210e commit cfcb6ea

File tree

3 files changed

+98
-20
lines changed

3 files changed

+98
-20
lines changed

lib/PrintAsObjC/PrintAsObjC.cpp

Lines changed: 50 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "swift/AST/AST.h"
1616
#include "swift/AST/ASTVisitor.h"
1717
#include "swift/AST/ForeignErrorConvention.h"
18+
#include "swift/AST/NameLookup.h"
1819
#include "swift/AST/PrettyStackTrace.h"
1920
#include "swift/AST/TypeVisitor.h"
2021
#include "swift/AST/Comment.h"
@@ -37,16 +38,24 @@
3738

3839
using namespace swift;
3940

40-
static bool isNSObject(ASTContext &ctx, Type type) {
41+
static bool isNSObjectOrAnyHashable(ASTContext &ctx, Type type) {
4142
if (auto classDecl = type->getClassOrBoundGenericClass()) {
4243
return classDecl->getName()
4344
== ctx.getSwiftId(KnownFoundationEntity::NSObject) &&
4445
classDecl->getModuleContext()->getName() == ctx.Id_ObjectiveC;
4546
}
47+
if (auto nomDecl = type->getAnyNominal()) {
48+
return nomDecl->getName() == ctx.getIdentifier("AnyHashable") &&
49+
nomDecl->getModuleContext() == ctx.getStdlibModule();
50+
}
4651

4752
return false;
4853
}
4954

55+
static bool isAnyObjectOrAny(Type type) {
56+
return type->isAnyObject() || type->isAny();
57+
}
58+
5059
/// Returns true if \p name matches a keyword in any Clang language mode.
5160
static bool isClangKeyword(Identifier name) {
5261
static const llvm::StringSet<> keywords = []{
@@ -125,6 +134,8 @@ class ObjCPrinter : private DeclVisitor<ObjCPrinter>,
125134
Accessibility minRequiredAccess;
126135
bool protocolMembersOptional = false;
127136

137+
Optional<Type> NSCopyingType;
138+
128139
friend ASTVisitor<ObjCPrinter>;
129140
friend TypeVisitor<ObjCPrinter>;
130141

@@ -859,6 +870,8 @@ class ObjCPrinter : private DeclVisitor<ObjCPrinter>,
859870
auto &ctx = swiftNominal->getASTContext();
860871
assert(objcClass);
861872

873+
Type rewrittenArgsBuf[2];
874+
862875
// Detect when the type arguments correspond to the unspecialized
863876
// type, and clear them out. There is some type-specific hackery
864877
// here for:
@@ -867,13 +880,38 @@ class ObjCPrinter : private DeclVisitor<ObjCPrinter>,
867880
// NSDictionary<NSObject *, id> --> NSDictionary
868881
// NSSet<id> --> NSSet
869882
if (!typeArgs.empty() &&
870-
(!hasGenericObjCType(objcClass) ||
871-
(swiftNominal == ctx.getArrayDecl() && typeArgs[0]->isAnyObject()) ||
872-
(swiftNominal == ctx.getDictionaryDecl() &&
873-
isNSObject(ctx, typeArgs[0]) && typeArgs[1]->isAnyObject()) ||
874-
(swiftNominal == ctx.getSetDecl() && isNSObject(ctx, typeArgs[0])))) {
883+
(!hasGenericObjCType(objcClass)
884+
|| (swiftNominal == ctx.getArrayDecl() &&
885+
isAnyObjectOrAny(typeArgs[0]))
886+
|| (swiftNominal == ctx.getDictionaryDecl() &&
887+
isNSObjectOrAnyHashable(ctx, typeArgs[0]) &&
888+
isAnyObjectOrAny(typeArgs[1]))
889+
|| (swiftNominal == ctx.getSetDecl() &&
890+
isNSObjectOrAnyHashable(ctx, typeArgs[0])))) {
875891
typeArgs = {};
876892
}
893+
894+
// Use the proper upper id<NSCopying> bound for Dictionaries with
895+
// upper-bounded keys.
896+
else if (swiftNominal == ctx.getDictionaryDecl() &&
897+
isNSObjectOrAnyHashable(ctx, typeArgs[0])) {
898+
if (Module *M = ctx.getLoadedModule(ctx.Id_Foundation)) {
899+
if (!NSCopyingType) {
900+
UnqualifiedLookup lookup(ctx.getIdentifier("NSCopying"), M, nullptr);
901+
auto type = lookup.getSingleTypeResult();
902+
if (type && isa<ProtocolDecl>(type)) {
903+
NSCopyingType = type->getDeclaredType();
904+
} else {
905+
NSCopyingType = Type();
906+
}
907+
}
908+
if (*NSCopyingType) {
909+
rewrittenArgsBuf[0] = *NSCopyingType;
910+
rewrittenArgsBuf[1] = typeArgs[1];
911+
typeArgs = rewrittenArgsBuf;
912+
}
913+
}
914+
}
877915

878916
// Print the class type.
879917
SmallString<32> objcNameScratch;
@@ -1119,12 +1157,17 @@ class ObjCPrinter : private DeclVisitor<ObjCPrinter>,
11191157
// Use the type as bridged to Objective-C unless the element type is itself
11201158
// an imported type or a collection.
11211159
const StructDecl *SD = ty->getStructOrBoundGenericStruct();
1122-
if (SD != ctx.getArrayDecl() &&
1160+
if (ty->isAny()) {
1161+
ty = ctx.getProtocol(KnownProtocolKind::AnyObject)
1162+
->getDeclaredType();
1163+
} else if (SD != ctx.getArrayDecl() &&
11231164
SD != ctx.getDictionaryDecl() &&
11241165
SD != ctx.getSetDecl() &&
11251166
!isSwiftNewtype(SD)) {
11261167
ty = *ctx.getBridgedToObjC(&M, ty, /*resolver*/nullptr);
11271168
}
1169+
1170+
assert(ty && "unknown bridged type");
11281171

11291172
print(ty, None);
11301173
}

test/Inputs/clang-importer-sdk/swift-modules/Foundation.swift

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,24 @@ internal func _convertNSSetToSet<T : Hashable>(_ s: NSSet?) -> Set<T> {
4545
return Set<T>()
4646
}
4747

48+
extension AnyHashable : _ObjectiveCBridgeable {
49+
public func _bridgeToObjectiveC() -> NSObject {
50+
return NSObject()
51+
}
52+
public static func _forceBridgeFromObjectiveC(_ x: NSObject,
53+
result: inout AnyHashable?) {
54+
}
55+
public static func _conditionallyBridgeFromObjectiveC(
56+
_ x: NSObject,
57+
result: inout AnyHashable?
58+
) -> Bool {
59+
return true
60+
}
61+
public static func _unconditionallyBridgeFromObjectiveC(_ x: NSObject?) -> AnyHashable {
62+
return AnyHashable("")
63+
}
64+
}
65+
4866
extension String : _ObjectiveCBridgeable {
4967
public func _bridgeToObjectiveC() -> NSString {
5068
return NSString()

test/PrintAsObjC/any_as_id.swift

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
// RUN: FileCheck %s < %t/any_as_id.h
1717

1818
// RUN: %check-in-clang %t/any_as_id.h
19-
// RUN: %check-in-clang -fno-modules -Qunused-arguments %t/any_as_id.h
2019

2120
import Foundation
2221

@@ -25,33 +24,51 @@ import Foundation
2524
// CHECK-NEXT: @interface AnyAsIdTest : NSObject
2625
class AnyAsIdTest : NSObject {
2726

28-
// CHECK-NEXT: - (void)takesId:(id _Nonnull)x;
29-
func takesId(_ x: Any) {}
27+
// CHECK-NEXT: - (NSArray * _Nonnull)arrayOfAny:(NSArray * _Nonnull)x;
28+
func arrayOfAny(_ x: [Any]) -> [Any] { return x }
29+
// CHECK-NEXT: - (NSArray * _Nullable)arrayOfAnyPerhaps:(NSArray * _Nonnull)x;
30+
func arrayOfAnyPerhaps(_ x: [Any]) -> [Any]? { return x }
31+
32+
// CHECK-NEXT: - (NSDictionary * _Nonnull)dictionaryOfAny:(NSDictionary * _Nonnull)x;
33+
func dictionaryOfAny(_ x: [AnyHashable: Any]) -> [AnyHashable: Any] { return x }
34+
// CHECK-NEXT: - (void)dictionaryOfAnyKeys:(NSDictionary<id <NSCopying>, NSString *> * _Nonnull)x;
35+
func dictionaryOfAnyKeys(_ x: [AnyHashable: String]) {}
36+
// CHECK-NEXT: - (NSDictionary * _Nullable)dictionaryOfAnyMayhap:(NSDictionary * _Nonnull)x;
37+
func dictionaryOfAnyMayhap(_ x: [AnyHashable: Any]) -> [AnyHashable: Any]? { return x }
38+
// CHECK-NEXT: - (void)dictionaryOfAnyValues:(NSDictionary<NSString *, id> * _Nonnull)x;
39+
func dictionaryOfAnyValues(_ x: [String: Any]) {}
3040

3141
// CHECK-NEXT: - (id _Nonnull)getAny;
3242
func getAny() -> Any { return 1 as Any }
43+
// CHECK-NEXT: - (id _Nullable)getAnyConstructively;
44+
func getAnyConstructively() -> Any? { return Optional<Any>(1 as Any) }
45+
// CHECK-NEXT: - (id _Nullable)getAnyMaybe;
46+
func getAnyMaybe() -> Any? { return nil }
47+
// CHECK-NEXT: - (id _Nullable)getAnyProbably;
48+
func getAnyProbably() -> Any? { return 1 as Any }
3349

3450
// CHECK-NEXT: - (id _Nonnull)passThroughAny:(id _Nonnull)x;
3551
func passThroughAny(_ x: Any) -> Any { return x }
52+
// CHECK-NEXT: - (id _Nullable)passThroughAnyMaybe:(id _Nullable)x;
53+
func passThroughAnyMaybe(_ x: Any?) -> Any? { return x }
54+
55+
// CHECK-NEXT: - (void)setOfAny:(NSSet * _Nonnull)x;
56+
func setOfAny(_ x: Set<AnyHashable>) {}
57+
58+
// CHECK-NEXT: - (void)takesId:(id _Nonnull)x;
59+
func takesId(_ x: Any) {}
3660

3761
// CHECK-NEXT: - (id _Nonnull)unwrapAny:(id _Nullable)x;
3862
func unwrapAny(_ x : Any?) -> Any { return x! }
3963

40-
// CHECK-NEXT: - (id _Nullable)getAnyMaybe;
41-
func getAnyMaybe() -> Any? { return nil }
42-
// CHECK-NEXT: - (id _Nullable)getAnyProbably;
43-
func getAnyProbably() -> Any? { return 1 as Any }
44-
// CHECK-NEXT: - (id _Nullable)passThroughAnyMaybe:(id _Nullable)x;
45-
func passThroughAnyMaybe(_ x: Any?) -> Any? { return x }
46-
// CHECK-NEXT: - (id _Nullable)getAnyConstructively;
47-
func getAnyConstructively() -> Any? { return Optional<Any>(1 as Any) }
4864
// CHECK-NEXT: - (id _Nullable)wrapAny:(id _Nonnull)x;
4965
func wrapAny(_ x : Any) -> Any? { return x }
5066

5167
// CHECK-NEXT: - (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER;
52-
/* implicit inherited init() */
68+
/* implicit inherited init() */
69+
5370
}
5471
// CHECK-NEXT: @end
5572

56-
73+
extension NSArray { func forceToExist() {} }
5774

0 commit comments

Comments
 (0)