Skip to content

Commit 8d854d2

Browse files
authored
Merge pull request #3918 from jckarter/printasobjc-crash
PrintAsObjC: Handle new upper bounds of Array/Dictionary/Set correctly.
2 parents 31b04da + cfcb6ea commit 8d854d2

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)