https://github.com/DougGregor created https://github.com/llvm/llvm-project/pull/151213
SwiftConformsTo specifies an additional conformance that should be applied on import. Allow this on typedefs, because those can be imported as wrapper types. >From 1073b67aba7358a10988b73096d9d623935cd0dd Mon Sep 17 00:00:00 2001 From: Doug Gregor <dgre...@apple.com> Date: Tue, 29 Jul 2025 12:46:01 -0700 Subject: [PATCH] [API notes] Allow SwiftConformsTo on Typedefs SwiftConformsTo specifies an additional conformance that should be applied on import. Allow this on typedefs, because those can be imported as wrapper types. --- clang/include/clang/APINotes/Types.h | 23 ++++++++++++------- clang/lib/APINotes/APINotesFormat.h | 2 +- clang/lib/APINotes/APINotesReader.cpp | 13 ++++++----- clang/lib/APINotes/APINotesWriter.cpp | 16 ++++++------- clang/lib/APINotes/APINotesYAMLCompiler.cpp | 8 +++++-- clang/lib/Sema/SemaAPINotes.cpp | 8 +++---- .../Simple.framework/Headers/Simple.apinotes | 3 +++ clang/test/APINotes/swift-import-as.cpp | 2 +- clang/test/APINotes/yaml-roundtrip.test | 2 +- 9 files changed, 46 insertions(+), 31 deletions(-) diff --git a/clang/include/clang/APINotes/Types.h b/clang/include/clang/APINotes/Types.h index 0f2e49613b514..8708b4b092f28 100644 --- a/clang/include/clang/APINotes/Types.h +++ b/clang/include/clang/APINotes/Types.h @@ -141,6 +141,9 @@ class CommonTypeInfo : public CommonEntityInfo { /// The NS error domain for this type. std::optional<std::string> NSErrorDomain; + /// The Swift protocol that this type should be automatically conformed to. + std::optional<std::string> SwiftConformance; + public: CommonTypeInfo() {} @@ -165,6 +168,14 @@ class CommonTypeInfo : public CommonEntityInfo { : std::nullopt; } + std::optional<std::string> getSwiftConformance() const { + return SwiftConformance; + } + + void setSwiftConformance(std::optional<std::string> conformance) { + SwiftConformance = conformance; + } + friend bool operator==(const CommonTypeInfo &, const CommonTypeInfo &); CommonTypeInfo &operator|=(const CommonTypeInfo &RHS) { @@ -175,6 +186,8 @@ class CommonTypeInfo : public CommonEntityInfo { setSwiftBridge(RHS.getSwiftBridge()); if (!NSErrorDomain) setNSErrorDomain(RHS.getNSErrorDomain()); + if (SwiftConformance) + setSwiftConformance(RHS.getSwiftConformance()); return *this; } @@ -185,7 +198,8 @@ class CommonTypeInfo : public CommonEntityInfo { inline bool operator==(const CommonTypeInfo &LHS, const CommonTypeInfo &RHS) { return static_cast<const CommonEntityInfo &>(LHS) == RHS && LHS.SwiftBridge == RHS.SwiftBridge && - LHS.NSErrorDomain == RHS.NSErrorDomain; + LHS.NSErrorDomain == RHS.NSErrorDomain && + LHS.SwiftConformance == RHS.SwiftConformance; } inline bool operator!=(const CommonTypeInfo &LHS, const CommonTypeInfo &RHS) { @@ -739,9 +753,6 @@ class TagInfo : public CommonTypeInfo { std::optional<std::string> SwiftReleaseOp; std::optional<std::string> SwiftDefaultOwnership; - /// The Swift protocol that this type should be automatically conformed to. - std::optional<std::string> SwiftConformance; - std::optional<EnumExtensibilityKind> EnumExtensibility; TagInfo() @@ -790,9 +801,6 @@ class TagInfo : public CommonTypeInfo { if (!SwiftDefaultOwnership) SwiftDefaultOwnership = RHS.SwiftDefaultOwnership; - if (!SwiftConformance) - SwiftConformance = RHS.SwiftConformance; - if (!HasFlagEnum) setFlagEnum(RHS.isFlagEnum()); @@ -819,7 +827,6 @@ inline bool operator==(const TagInfo &LHS, const TagInfo &RHS) { LHS.SwiftRetainOp == RHS.SwiftRetainOp && LHS.SwiftReleaseOp == RHS.SwiftReleaseOp && LHS.SwiftDefaultOwnership == RHS.SwiftDefaultOwnership && - LHS.SwiftConformance == RHS.SwiftConformance && LHS.isFlagEnum() == RHS.isFlagEnum() && LHS.isSwiftCopyable() == RHS.isSwiftCopyable() && LHS.isSwiftEscapable() == RHS.isSwiftEscapable() && diff --git a/clang/lib/APINotes/APINotesFormat.h b/clang/lib/APINotes/APINotesFormat.h index bb0c276e74964..1ac486a2dd94c 100644 --- a/clang/lib/APINotes/APINotesFormat.h +++ b/clang/lib/APINotes/APINotesFormat.h @@ -24,7 +24,7 @@ const uint16_t VERSION_MAJOR = 0; /// API notes file minor version number. /// /// When the format changes IN ANY WAY, this number should be incremented. -const uint16_t VERSION_MINOR = 35; // SwiftDefaultOwnership +const uint16_t VERSION_MINOR = 36; // Typedef SwiftConformsTo const uint8_t kSwiftConforms = 1; const uint8_t kSwiftDoesNotConform = 2; diff --git a/clang/lib/APINotes/APINotesReader.cpp b/clang/lib/APINotes/APINotesReader.cpp index 7cc4df2a99369..538b950f8712d 100644 --- a/clang/lib/APINotes/APINotesReader.cpp +++ b/clang/lib/APINotes/APINotesReader.cpp @@ -134,6 +134,13 @@ void ReadCommonTypeInfo(const uint8_t *&Data, CommonTypeInfo &Info) { reinterpret_cast<const char *>(Data), ErrorDomainLength - 1))); Data += ErrorDomainLength - 1; } + + if (unsigned ConformanceLength = + endian::readNext<uint16_t, llvm::endianness::little>(Data)) { + Info.setSwiftConformance(std::string(reinterpret_cast<const char *>(Data), + ConformanceLength - 1)); + Data += ConformanceLength - 1; + } } /// Used to deserialize the on-disk identifier table. @@ -629,12 +636,6 @@ class TagTableInfo reinterpret_cast<const char *>(Data), DefaultOwnershipLength - 1); Data += DefaultOwnershipLength - 1; } - if (unsigned ConformanceLength = - endian::readNext<uint16_t, llvm::endianness::little>(Data)) { - Info.SwiftConformance = std::string(reinterpret_cast<const char *>(Data), - ConformanceLength - 1); - Data += ConformanceLength - 1; - } ReadCommonTypeInfo(Data, Info); return Info; diff --git a/clang/lib/APINotes/APINotesWriter.cpp b/clang/lib/APINotes/APINotesWriter.cpp index ffc5473988735..c013201d677bf 100644 --- a/clang/lib/APINotes/APINotesWriter.cpp +++ b/clang/lib/APINotes/APINotesWriter.cpp @@ -536,7 +536,8 @@ unsigned getCommonEntityInfoSize(const CommonEntityInfo &CEI) { // in on-disk hash tables. unsigned getCommonTypeInfoSize(const CommonTypeInfo &CTI) { return 2 + (CTI.getSwiftBridge() ? CTI.getSwiftBridge()->size() : 0) + 2 + - (CTI.getNSErrorDomain() ? CTI.getNSErrorDomain()->size() : 0) + + (CTI.getNSErrorDomain() ? CTI.getNSErrorDomain()->size() : 0) + 2 + + (CTI.getSwiftConformance() ? CTI.getSwiftConformance()->size() : 0) + getCommonEntityInfoSize(CTI); } @@ -557,6 +558,12 @@ void emitCommonTypeInfo(raw_ostream &OS, const CommonTypeInfo &CTI) { } else { writer.write<uint16_t>(0); } + if (auto conformance = CTI.getSwiftConformance()) { + writer.write<uint16_t>(conformance->size() + 1); + OS.write(conformance->c_str(), conformance->size()); + } else { + writer.write<uint16_t>(0); + } } /// Used to serialize the on-disk Objective-C property table. @@ -1274,7 +1281,6 @@ class TagTableInfo : public CommonTypeTableInfo<TagTableInfo, TagInfo> { 2 + (TI.SwiftRetainOp ? TI.SwiftRetainOp->size() : 0) + 2 + (TI.SwiftReleaseOp ? TI.SwiftReleaseOp->size() : 0) + 2 + (TI.SwiftDefaultOwnership ? TI.SwiftDefaultOwnership->size() : 0) + - 2 + (TI.SwiftConformance ? TI.SwiftConformance->size() : 0) + 3 + getCommonTypeInfoSize(TI); // clang-format on } @@ -1328,12 +1334,6 @@ class TagTableInfo : public CommonTypeTableInfo<TagTableInfo, TagInfo> { } else { writer.write<uint16_t>(0); } - if (auto Conformance = TI.SwiftConformance) { - writer.write<uint16_t>(Conformance->size() + 1); - OS.write(Conformance->c_str(), Conformance->size()); - } else { - writer.write<uint16_t>(0); - } emitCommonTypeInfo(OS, TI); } diff --git a/clang/lib/APINotes/APINotesYAMLCompiler.cpp b/clang/lib/APINotes/APINotesYAMLCompiler.cpp index 803410c54c646..fcc1d9d54b1c6 100644 --- a/clang/lib/APINotes/APINotesYAMLCompiler.cpp +++ b/clang/lib/APINotes/APINotesYAMLCompiler.cpp @@ -251,6 +251,7 @@ struct Class { std::optional<StringRef> NSErrorDomain; std::optional<bool> SwiftImportAsNonGeneric; std::optional<bool> SwiftObjCMembers; + std::optional<std::string> SwiftConformance; MethodsSeq Methods; PropertiesSeq Properties; }; @@ -275,6 +276,7 @@ template <> struct MappingTraits<Class> { IO.mapOptional("NSErrorDomain", C.NSErrorDomain); IO.mapOptional("SwiftImportAsNonGeneric", C.SwiftImportAsNonGeneric); IO.mapOptional("SwiftObjCMembers", C.SwiftObjCMembers); + IO.mapOptional("SwiftConformsTo", C.SwiftConformance); IO.mapOptional("Methods", C.Methods); IO.mapOptional("Properties", C.Properties); } @@ -525,6 +527,7 @@ struct Typedef { std::optional<StringRef> SwiftBridge; std::optional<StringRef> NSErrorDomain; std::optional<SwiftNewTypeKind> SwiftType; + std::optional<std::string> SwiftConformance; }; typedef std::vector<Typedef> TypedefsSeq; @@ -553,6 +556,7 @@ template <> struct MappingTraits<Typedef> { IO.mapOptional("SwiftBridge", T.SwiftBridge); IO.mapOptional("NSErrorDomain", T.NSErrorDomain); IO.mapOptional("SwiftWrapper", T.SwiftType); + IO.mapOptional("SwiftConformsTo", T.SwiftConformance); } }; } // namespace yaml @@ -802,6 +806,8 @@ class YAMLConverter { if (Common.SwiftBridge) Info.setSwiftBridge(std::string(*Common.SwiftBridge)); Info.setNSErrorDomain(Common.NSErrorDomain); + if (auto conformance = Common.SwiftConformance) + Info.setSwiftConformance(conformance); } // Translate from Method into ObjCMethodInfo and write it out. @@ -990,8 +996,6 @@ class YAMLConverter { TI.SwiftRetainOp = T.SwiftRetainOp; if (T.SwiftReleaseOp) TI.SwiftReleaseOp = T.SwiftReleaseOp; - if (T.SwiftConformance) - TI.SwiftConformance = T.SwiftConformance; if (T.SwiftDefaultOwnership) TI.SwiftDefaultOwnership = T.SwiftDefaultOwnership; diff --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp index 044abb0ee08a8..272069e5fcc7d 100644 --- a/clang/lib/Sema/SemaAPINotes.cpp +++ b/clang/lib/Sema/SemaAPINotes.cpp @@ -336,6 +336,10 @@ static void ProcessAPINotes(Sema &S, Decl *D, }); } + if (auto ConformsTo = Info.getSwiftConformance()) + D->addAttr( + SwiftAttrAttr::Create(S.Context, "conforms_to:" + ConformsTo.value())); + ProcessAPINotes(S, D, static_cast<const api_notes::CommonEntityInfo &>(Info), Metadata); } @@ -698,10 +702,6 @@ static void ProcessAPINotes(Sema &S, TagDecl *D, const api_notes::TagInfo &Info, D->addAttr(SwiftAttrAttr::Create( S.Context, "returned_as_" + DefaultOwnership.value() + "_by_default")); - if (auto ConformsTo = Info.SwiftConformance) - D->addAttr( - SwiftAttrAttr::Create(S.Context, "conforms_to:" + ConformsTo.value())); - if (auto Copyable = Info.isSwiftCopyable()) { if (!*Copyable) D->addAttr(SwiftAttrAttr::Create(S.Context, "~Copyable")); diff --git a/clang/test/APINotes/Inputs/Frameworks/Simple.framework/Headers/Simple.apinotes b/clang/test/APINotes/Inputs/Frameworks/Simple.framework/Headers/Simple.apinotes index 8c915bd8b5913..9ba38edb8a6f1 100644 --- a/clang/test/APINotes/Inputs/Frameworks/Simple.framework/Headers/Simple.apinotes +++ b/clang/test/APINotes/Inputs/Frameworks/Simple.framework/Headers/Simple.apinotes @@ -26,3 +26,6 @@ Classes: - Name: scalarNewProperty PropertyKind: Instance Nullability: Scalar +Typedefs: + - Name: MyTypedef + SwiftConformsTo: Swift.Equatable diff --git a/clang/test/APINotes/swift-import-as.cpp b/clang/test/APINotes/swift-import-as.cpp index 179170fbc0994..747aa29e95761 100644 --- a/clang/test/APINotes/swift-import-as.cpp +++ b/clang/test/APINotes/swift-import-as.cpp @@ -51,8 +51,8 @@ // CHECK-OPAQUE-REF-COUNTED-NOT: SwiftAttrAttr {{.+}} <<invalid sloc>> "release: // CHECK-NON-COPYABLE: Dumping NonCopyableType: // CHECK-NON-COPYABLE-NEXT: CXXRecordDecl {{.+}} imported in SwiftImportAs {{.+}} struct NonCopyableType -// CHECK-NON-COPYABLE: SwiftAttrAttr {{.+}} <<invalid sloc>> "conforms_to:MySwiftModule.MySwiftNonCopyableProtocol" // CHECK-NON-COPYABLE: SwiftAttrAttr {{.+}} <<invalid sloc>> "~Copyable" +// CHECK-NON-COPYABLE: SwiftAttrAttr {{.+}} <<invalid sloc>> "conforms_to:MySwiftModule.MySwiftNonCopyableProtocol" // CHECK-COPYABLE: Dumping CopyableType: // CHECK-COPYABLE-NEXT: CXXRecordDecl {{.+}} imported in SwiftImportAs {{.+}} struct CopyableType diff --git a/clang/test/APINotes/yaml-roundtrip.test b/clang/test/APINotes/yaml-roundtrip.test index bcf84afda8df0..f69038ca828b1 100644 --- a/clang/test/APINotes/yaml-roundtrip.test +++ b/clang/test/APINotes/yaml-roundtrip.test @@ -24,7 +24,7 @@ CHECK-NEXT: 25c26 CHECK-NEXT: < Nullability: S CHECK-NEXT: --- CHECK-NEXT: > Nullability: Unspecified -CHECK-NEXT: 28c29,30 +CHECK-NEXT: 28c29 CHECK-NEXT: < Nullability: Scalar CHECK-NEXT: --- CHECK-NEXT: > Nullability: Unspecified _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits