Skip to content

Commit d83db61

Browse files
authored
[PrintAsObjC] Put "copy" in the property decls for value types. (#3141)
Bridged value types are implicitly copied as part of bridging. This isn't 100% correct because it doesn't handle bridged types that /don't/ conform to NSCopying, but there aren't any of those today and there probably shouldn't be. rdar://problem/26917017
1 parent 6f22343 commit d83db61

File tree

2 files changed

+75
-38
lines changed

2 files changed

+75
-38
lines changed

lib/PrintAsObjC/PrintAsObjC.cpp

Lines changed: 72 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,8 @@ class ObjCPrinter : private DeclVisitor<ObjCPrinter>,
112112
friend ASTVisitor;
113113
friend TypeVisitor;
114114

115-
llvm::DenseMap<std::pair<Identifier, Identifier>, std::pair<StringRef, bool>>
115+
using NameAndOptional = std::pair<StringRef, bool>;
116+
llvm::DenseMap<std::pair<Identifier, Identifier>, NameAndOptional>
116117
specialNames;
117118
Identifier ID_CFTypeRef;
118119

@@ -628,7 +629,9 @@ class ObjCPrinter : private DeclVisitor<ObjCPrinter>,
628629
if (nominal == ctx.getArrayDecl() ||
629630
nominal == ctx.getDictionaryDecl() ||
630631
nominal == ctx.getSetDecl() ||
631-
nominal == ctx.getStringDecl()) {
632+
nominal == ctx.getStringDecl() ||
633+
(!getKnownTypeInfo(nominal) && getObjCBridgedClass(nominal))) {
634+
// We fast-path the most common cases in the condition above.
632635
os << ", copy";
633636
} else if (nominal == ctx.getUnmanagedDecl()) {
634637
os << ", unsafe_unretained";
@@ -798,25 +801,25 @@ class ObjCPrinter : private DeclVisitor<ObjCPrinter>,
798801
return true;
799802
}
800803

801-
/// If the nominal type is bridged to Objective-C (via a conformance
802-
/// to _ObjectiveCBridgeable), print the bridged type.
803-
bool printIfObjCBridgeable(const NominalTypeDecl *nominal,
804-
ArrayRef<Type> typeArgs,
805-
Optional<OptionalTypeKind> optionalKind) {
804+
/// If \p nominal is bridged to an Objective-C class (via a conformance to
805+
/// _ObjectiveCBridgeable), return that class.
806+
///
807+
/// Otherwise returns null.
808+
const ClassDecl *getObjCBridgedClass(const NominalTypeDecl *nominal) {
806809
// Print imported bridgeable decls as their unbridged type.
807810
if (nominal->hasClangNode())
808-
return false;
811+
return nullptr;
809812

810813
auto &ctx = nominal->getASTContext();
811814

812815
// Dig out the ObjectiveCBridgeable protocol.
813816
auto proto = ctx.getProtocol(KnownProtocolKind::ObjectiveCBridgeable);
814-
if (!proto) return false;
817+
if (!proto) return nullptr;
815818

816819
// Determine whether this nominal type is _ObjectiveCBridgeable.
817820
SmallVector<ProtocolConformance *, 2> conformances;
818821
if (!nominal->lookupConformance(&M, proto, conformances))
819-
return false;
822+
return nullptr;
820823

821824
// Dig out the Objective-C type.
822825
auto conformance = conformances.front();
@@ -825,15 +828,20 @@ class ObjCPrinter : private DeclVisitor<ObjCPrinter>,
825828
conformance,
826829
ctx.Id_ObjectiveCType,
827830
nullptr);
828-
if (!objcType) return false;
831+
if (!objcType) return nullptr;
829832

830833
// Dig out the Objective-C class.
831-
auto classDecl = objcType->getClassOrBoundGenericClass();
832-
if (!classDecl) return false;
834+
return objcType->getClassOrBoundGenericClass();
835+
}
833836

834-
// Determine the Objective-C name of the class.
835-
SmallString<32> objcNameScratch;
836-
StringRef objcName = classDecl->getObjCRuntimeName(objcNameScratch);
837+
/// If the nominal type is bridged to Objective-C (via a conformance
838+
/// to _ObjectiveCBridgeable), print the bridged type.
839+
void printObjCBridgeableType(const NominalTypeDecl *swiftNominal,
840+
const ClassDecl *objcClass,
841+
ArrayRef<Type> typeArgs,
842+
Optional<OptionalTypeKind> optionalKind) {
843+
auto &ctx = swiftNominal->getASTContext();
844+
assert(objcClass);
837845

838846
// Detect when the type arguments correspond to the unspecialized
839847
// type, and clear them out. There is some type-specific hackery
@@ -843,15 +851,17 @@ class ObjCPrinter : private DeclVisitor<ObjCPrinter>,
843851
// NSDictionary<NSObject *, id> --> NSDictionary
844852
// NSSet<id> --> NSSet
845853
if (!typeArgs.empty() &&
846-
(!hasGenericObjCType(classDecl) ||
847-
(objcName == "NSArray" && typeArgs[0]->isAnyObject()) ||
848-
(objcName == "NSDictionary" && isNSObject(ctx, typeArgs[0]) &&
849-
typeArgs[1]->isAnyObject()) ||
850-
(objcName == "NSSet" && isNSObject(ctx, typeArgs[0]))))
854+
(!hasGenericObjCType(objcClass) ||
855+
(swiftNominal == ctx.getArrayDecl() && typeArgs[0]->isAnyObject()) ||
856+
(swiftNominal == ctx.getDictionaryDecl() &&
857+
isNSObject(ctx, typeArgs[0]) && typeArgs[1]->isAnyObject()) ||
858+
(swiftNominal == ctx.getSetDecl() && isNSObject(ctx, typeArgs[0])))) {
851859
typeArgs = {};
860+
}
852861

853862
// Print the class type.
854-
os << objcName;
863+
SmallString<32> objcNameScratch;
864+
os << objcClass->getObjCRuntimeName(objcNameScratch);
855865

856866
// Print the type arguments, if present.
857867
if (!typeArgs.empty()) {
@@ -866,17 +876,29 @@ class ObjCPrinter : private DeclVisitor<ObjCPrinter>,
866876

867877
os << " *";
868878
printNullability(optionalKind);
869-
return true;
870879
}
871880

872-
/// If "name" is one of the standard library types used to map in Clang
873-
/// primitives and basic types, print out the appropriate spelling and
874-
/// return true.
881+
/// If the nominal type is bridged to Objective-C (via a conformance to
882+
/// _ObjectiveCBridgeable), print the bridged type. Otherwise, nothing is
883+
/// printed.
875884
///
876-
/// This handles typealiases and structs provided by the standard library
877-
/// for interfacing with C and Objective-C.
878-
bool printIfKnownTypeName(Identifier moduleName, Identifier name,
879-
Optional<OptionalTypeKind> optionalKind) {
885+
/// \returns true iff printed something.
886+
bool printIfObjCBridgeable(const NominalTypeDecl *nominal,
887+
ArrayRef<Type> typeArgs,
888+
Optional<OptionalTypeKind> optionalKind) {
889+
if (const ClassDecl *objcClass = getObjCBridgedClass(nominal)) {
890+
printObjCBridgeableType(nominal, objcClass, typeArgs, optionalKind);
891+
return true;
892+
}
893+
return false;
894+
}
895+
896+
/// If \p typeDecl is one of the standard library types used to map in Clang
897+
/// primitives and basic types, return the address of the info in
898+
/// \c specialNames containing the Clang name and whether it can be optional.
899+
///
900+
/// Returns null if the name is not one of these known types.
901+
const NameAndOptional *getKnownTypeInfo(const TypeDecl *typeDecl) {
880902
if (specialNames.empty()) {
881903
ASTContext &ctx = M.getASTContext();
882904
#define MAP(SWIFT_NAME, CLANG_REPR, NEEDS_NULLABILITY) \
@@ -956,12 +978,27 @@ class ObjCPrinter : private DeclVisitor<ObjCPrinter>,
956978
"SIMD elements is changed");
957979
}
958980

981+
Identifier moduleName = typeDecl->getModuleContext()->getName();
982+
Identifier name = typeDecl->getName();
959983
auto iter = specialNames.find({moduleName, name});
960984
if (iter == specialNames.end())
961-
return false;
985+
return nullptr;
986+
return &iter->second;
987+
}
962988

963-
os << iter->second.first;
964-
if (iter->second.second)
989+
/// If \p typeDecl is one of the standard library types used to map in Clang
990+
/// primitives and basic types, print out the appropriate spelling and
991+
/// return true.
992+
///
993+
/// This handles typealiases and structs provided by the standard library
994+
/// for interfacing with C and Objective-C.
995+
bool printIfKnownSimpleType(const TypeDecl *typeDecl,
996+
Optional<OptionalTypeKind> optionalKind) {
997+
auto *knownTypeInfo = getKnownTypeInfo(typeDecl);
998+
if (!knownTypeInfo)
999+
return false;
1000+
os << knownTypeInfo->first;
1001+
if (knownTypeInfo->second)
9651002
printNullability(optionalKind);
9661003
return true;
9671004
}
@@ -984,9 +1021,7 @@ class ObjCPrinter : private DeclVisitor<ObjCPrinter>,
9841021
void visitNameAliasType(NameAliasType *aliasTy,
9851022
Optional<OptionalTypeKind> optionalKind) {
9861023
const TypeAliasDecl *alias = aliasTy->getDecl();
987-
if (printIfKnownTypeName(alias->getModuleContext()->getName(),
988-
alias->getName(),
989-
optionalKind))
1024+
if (printIfKnownSimpleType(alias, optionalKind))
9901025
return;
9911026

9921027
if (alias->hasClangNode()) {
@@ -1032,8 +1067,7 @@ class ObjCPrinter : private DeclVisitor<ObjCPrinter>,
10321067
const StructDecl *SD = ST->getStructOrBoundGenericStruct();
10331068

10341069
// Handle known type names.
1035-
if (printIfKnownTypeName(SD->getModuleContext()->getName(), SD->getName(),
1036-
optionalKind))
1070+
if (printIfKnownSimpleType(SD, optionalKind))
10371071
return;
10381072

10391073
// Handle bridged types.

test/PrintAsObjC/classes.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -437,6 +437,7 @@ public class NonObjCClass { }
437437
// CHECK-NEXT: - (BOOL)initGetter SWIFT_METHOD_FAMILY(none);
438438
// CHECK-NEXT: @property (nonatomic, setter=initSetter:) BOOL setterIsInit;
439439
// CHECK-NEXT: - (void)initSetter:(BOOL)newValue SWIFT_METHOD_FAMILY(none);
440+
// CHECK-NEXT: @property (nonatomic, copy) NSURL * _Nullable customValueTypeProp;
440441
// CHECK-NEXT: init
441442
// CHECK-NEXT: @end
442443
@objc class Properties {
@@ -542,6 +543,8 @@ public class NonObjCClass { }
542543
get { return true }
543544
@objc(initSetter:) set {}
544545
}
546+
547+
var customValueTypeProp: URL?
545548
}
546549

547550
// CHECK-LABEL: @interface PropertiesOverridden

0 commit comments

Comments
 (0)