[clang] dc8c217 - [APINotes] Support C++ tag conformances to Swift protocols

via cfe-commits cfe-commits at lists.llvm.org
Tue Aug 13 04:47:27 PDT 2024


Author: Egor Zhdan
Date: 2024-08-13T12:47:23+01:00
New Revision: dc8c217db6329f03a116ef63dafa6de02913d311

URL: https://github.com/llvm/llvm-project/commit/dc8c217db6329f03a116ef63dafa6de02913d311
DIFF: https://github.com/llvm/llvm-project/commit/dc8c217db6329f03a116ef63dafa6de02913d311.diff

LOG: [APINotes] Support C++ tag conformances to Swift protocols

This allows adding a Clang attribute
`swift_attr("conforms_to:ModuleName.ProtocolName")` to C++ structs via
API Notes.

The Swift compiler respects this attribute when importing C++ types into
Swift by automatically declaring the C++ type as a conforming type to
the given Swift protocol.

rdar://131388824

Added: 
    

Modified: 
    clang/docs/APINotes.rst
    clang/include/clang/APINotes/Types.h
    clang/lib/APINotes/APINotesFormat.h
    clang/lib/APINotes/APINotesReader.cpp
    clang/lib/APINotes/APINotesWriter.cpp
    clang/lib/APINotes/APINotesYAMLCompiler.cpp
    clang/lib/Sema/SemaAPINotes.cpp
    clang/test/APINotes/Inputs/Headers/SwiftImportAs.apinotes
    clang/test/APINotes/swift-import-as.cpp

Removed: 
    


################################################################################
diff  --git a/clang/docs/APINotes.rst b/clang/docs/APINotes.rst
index bc09b16bab5d27..dcefa6810dac67 100644
--- a/clang/docs/APINotes.rst
+++ b/clang/docs/APINotes.rst
@@ -188,6 +188,18 @@ declaration kind), all of which are optional:
     - Name: tzdb
       SwiftCopyable: false
 
+:SwiftConformsTo:
+
+  Allows annotating a C++ class as conforming to a Swift protocol. Equivalent
+  to ``SWIFT_CONFORMS_TO_PROTOCOL``. The value is a module-qualified name of a
+  Swift protocol.
+
+  ::
+
+    Tags:
+    - Name: vector
+      SwiftConformsTo: Cxx.CxxSequence
+
 :Availability, AvailabilityMsg:
 
   A value of "nonswift" is equivalent to ``NS_SWIFT_UNAVAILABLE``. A value of

diff  --git a/clang/include/clang/APINotes/Types.h b/clang/include/clang/APINotes/Types.h
index c8e5e4df25d173..f972d0cf26640d 100644
--- a/clang/include/clang/APINotes/Types.h
+++ b/clang/include/clang/APINotes/Types.h
@@ -685,6 +685,9 @@ class TagInfo : public CommonTypeInfo {
   std::optional<std::string> SwiftRetainOp;
   std::optional<std::string> SwiftReleaseOp;
 
+  /// The Swift protocol that this type should be automatically conformed to.
+  std::optional<std::string> SwiftConformance;
+
   std::optional<EnumExtensibilityKind> EnumExtensibility;
 
   TagInfo()
@@ -720,6 +723,9 @@ class TagInfo : public CommonTypeInfo {
     if (!SwiftReleaseOp)
       SwiftReleaseOp = RHS.SwiftReleaseOp;
 
+    if (!SwiftConformance)
+      SwiftConformance = RHS.SwiftConformance;
+
     if (!HasFlagEnum)
       setFlagEnum(RHS.isFlagEnum());
 
@@ -742,6 +748,7 @@ inline bool operator==(const TagInfo &LHS, const TagInfo &RHS) {
          LHS.SwiftImportAs == RHS.SwiftImportAs &&
          LHS.SwiftRetainOp == RHS.SwiftRetainOp &&
          LHS.SwiftReleaseOp == RHS.SwiftReleaseOp &&
+         LHS.SwiftConformance == RHS.SwiftConformance &&
          LHS.isFlagEnum() == RHS.isFlagEnum() &&
          LHS.isSwiftCopyable() == RHS.isSwiftCopyable() &&
          LHS.EnumExtensibility == RHS.EnumExtensibility;

diff  --git a/clang/lib/APINotes/APINotesFormat.h b/clang/lib/APINotes/APINotesFormat.h
index 9d254dcc1c9eff..fba5f4e8907dae 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 = 28; // nested tags
+const uint16_t VERSION_MINOR = 29; // SwiftConformsTo
 
 const uint8_t kSwiftCopyable = 1;
 const uint8_t kSwiftNonCopyable = 2;

diff  --git a/clang/lib/APINotes/APINotesReader.cpp b/clang/lib/APINotes/APINotesReader.cpp
index 871f782511d5f1..c05fdffe4a071b 100644
--- a/clang/lib/APINotes/APINotesReader.cpp
+++ b/clang/lib/APINotes/APINotesReader.cpp
@@ -572,6 +572,12 @@ class TagTableInfo
                                         ReleaseOpLength - 1);
       Data += ReleaseOpLength - 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 2a71922746ac5d..cf3a0bee393eee 100644
--- a/clang/lib/APINotes/APINotesWriter.cpp
+++ b/clang/lib/APINotes/APINotesWriter.cpp
@@ -1189,6 +1189,7 @@ class TagTableInfo : public CommonTypeTableInfo<TagTableInfo, TagInfo> {
     return 2 + (TI.SwiftImportAs ? TI.SwiftImportAs->size() : 0) +
            2 + (TI.SwiftRetainOp ? TI.SwiftRetainOp->size() : 0) +
            2 + (TI.SwiftReleaseOp ? TI.SwiftReleaseOp->size() : 0) +
+           2 + (TI.SwiftConformance ? TI.SwiftConformance->size() : 0) +
            2 + getCommonTypeInfoSize(TI);
   }
 
@@ -1230,6 +1231,12 @@ 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 11cccc94a15f03..2205686c4d15c3 100644
--- a/clang/lib/APINotes/APINotesYAMLCompiler.cpp
+++ b/clang/lib/APINotes/APINotesYAMLCompiler.cpp
@@ -419,6 +419,7 @@ struct Tag {
   std::optional<std::string> SwiftImportAs;
   std::optional<std::string> SwiftRetainOp;
   std::optional<std::string> SwiftReleaseOp;
+  std::optional<std::string> SwiftConformance;
   std::optional<EnumExtensibilityKind> EnumExtensibility;
   std::optional<bool> FlagEnum;
   std::optional<EnumConvenienceAliasKind> EnumConvenienceKind;
@@ -456,6 +457,7 @@ template <> struct MappingTraits<Tag> {
     IO.mapOptional("SwiftImportAs", T.SwiftImportAs);
     IO.mapOptional("SwiftReleaseOp", T.SwiftReleaseOp);
     IO.mapOptional("SwiftRetainOp", T.SwiftRetainOp);
+    IO.mapOptional("SwiftConformsTo", T.SwiftConformance);
     IO.mapOptional("EnumExtensibility", T.EnumExtensibility);
     IO.mapOptional("FlagEnum", T.FlagEnum);
     IO.mapOptional("EnumKind", T.EnumConvenienceKind);
@@ -920,6 +922,8 @@ class YAMLConverter {
       TI.SwiftRetainOp = T.SwiftRetainOp;
     if (T.SwiftReleaseOp)
       TI.SwiftReleaseOp = T.SwiftReleaseOp;
+    if (T.SwiftConformance)
+      TI.SwiftConformance = T.SwiftConformance;
 
     if (T.SwiftCopyable)
       TI.setSwiftCopyable(T.SwiftCopyable);

diff  --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp
index 2c49c1f64b2da8..65b56bd1c8efc7 100644
--- a/clang/lib/Sema/SemaAPINotes.cpp
+++ b/clang/lib/Sema/SemaAPINotes.cpp
@@ -605,6 +605,10 @@ static void ProcessAPINotes(Sema &S, TagDecl *D, const api_notes::TagInfo &Info,
     D->addAttr(
         SwiftAttrAttr::Create(S.Context, "release:" + ReleaseOp.value()));
 
+  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/Headers/SwiftImportAs.apinotes b/clang/test/APINotes/Inputs/Headers/SwiftImportAs.apinotes
index b0eead42869a41..f4f9c7a244e0a3 100644
--- a/clang/test/APINotes/Inputs/Headers/SwiftImportAs.apinotes
+++ b/clang/test/APINotes/Inputs/Headers/SwiftImportAs.apinotes
@@ -7,7 +7,9 @@ Tags:
   SwiftImportAs: reference
   SwiftReleaseOp: RCRelease
   SwiftRetainOp: RCRetain
+  SwiftConformsTo: MySwiftModule.MySwiftRefCountedProtocol
 - Name: NonCopyableType
   SwiftCopyable: false
+  SwiftConformsTo: MySwiftModule.MySwiftNonCopyableProtocol
 - Name: CopyableType
   SwiftCopyable: true

diff  --git a/clang/test/APINotes/swift-import-as.cpp b/clang/test/APINotes/swift-import-as.cpp
index 62e6450e94e113..6457e1557618de 100644
--- a/clang/test/APINotes/swift-import-as.cpp
+++ b/clang/test/APINotes/swift-import-as.cpp
@@ -16,9 +16,11 @@
 // CHECK-REF-COUNTED: SwiftAttrAttr {{.+}} <<invalid sloc>> "import_reference"
 // CHECK-REF-COUNTED: SwiftAttrAttr {{.+}} <<invalid sloc>> "retain:RCRetain"
 // CHECK-REF-COUNTED: SwiftAttrAttr {{.+}} <<invalid sloc>> "release:RCRelease"
+// CHECK-REF-COUNTED: SwiftAttrAttr {{.+}} <<invalid sloc>> "conforms_to:MySwiftModule.MySwiftRefCountedProtocol"
 
 // 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-COPYABLE: Dumping CopyableType:


        


More information about the cfe-commits mailing list