[clang] [APINotes] Support C++ tag conformances to Swift protocols (PR #102664)
Egor Zhdan via cfe-commits
cfe-commits at lists.llvm.org
Mon Aug 12 07:22:17 PDT 2024
https://github.com/egorzhdan updated https://github.com/llvm/llvm-project/pull/102664
>From 4735b4c7bad13dee4bbeb819aba8ac555f842322 Mon Sep 17 00:00:00 2001
From: Egor Zhdan <e_zhdan at apple.com>
Date: Fri, 9 Aug 2024 20:08:43 +0100
Subject: [PATCH] [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
---
clang/docs/APINotes.rst | 12 ++++++++++++
clang/include/clang/APINotes/Types.h | 7 +++++++
clang/lib/APINotes/APINotesFormat.h | 2 +-
clang/lib/APINotes/APINotesReader.cpp | 6 ++++++
clang/lib/APINotes/APINotesWriter.cpp | 7 +++++++
clang/lib/APINotes/APINotesYAMLCompiler.cpp | 4 ++++
clang/lib/Sema/SemaAPINotes.cpp | 4 ++++
.../APINotes/Inputs/Headers/SwiftImportAs.apinotes | 2 ++
clang/test/APINotes/swift-import-as.cpp | 2 ++
9 files changed, 45 insertions(+), 1 deletion(-)
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