Skip to content

Commit dc8c217

Browse files
authored
[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
1 parent d550ada commit dc8c217

File tree

9 files changed

+45
-1
lines changed

9 files changed

+45
-1
lines changed

clang/docs/APINotes.rst

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,18 @@ declaration kind), all of which are optional:
188188
- Name: tzdb
189189
SwiftCopyable: false
190190

191+
:SwiftConformsTo:
192+
193+
Allows annotating a C++ class as conforming to a Swift protocol. Equivalent
194+
to ``SWIFT_CONFORMS_TO_PROTOCOL``. The value is a module-qualified name of a
195+
Swift protocol.
196+
197+
::
198+
199+
Tags:
200+
- Name: vector
201+
SwiftConformsTo: Cxx.CxxSequence
202+
191203
:Availability, AvailabilityMsg:
192204

193205
A value of "nonswift" is equivalent to ``NS_SWIFT_UNAVAILABLE``. A value of

clang/include/clang/APINotes/Types.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -685,6 +685,9 @@ class TagInfo : public CommonTypeInfo {
685685
std::optional<std::string> SwiftRetainOp;
686686
std::optional<std::string> SwiftReleaseOp;
687687

688+
/// The Swift protocol that this type should be automatically conformed to.
689+
std::optional<std::string> SwiftConformance;
690+
688691
std::optional<EnumExtensibilityKind> EnumExtensibility;
689692

690693
TagInfo()
@@ -720,6 +723,9 @@ class TagInfo : public CommonTypeInfo {
720723
if (!SwiftReleaseOp)
721724
SwiftReleaseOp = RHS.SwiftReleaseOp;
722725

726+
if (!SwiftConformance)
727+
SwiftConformance = RHS.SwiftConformance;
728+
723729
if (!HasFlagEnum)
724730
setFlagEnum(RHS.isFlagEnum());
725731

@@ -742,6 +748,7 @@ inline bool operator==(const TagInfo &LHS, const TagInfo &RHS) {
742748
LHS.SwiftImportAs == RHS.SwiftImportAs &&
743749
LHS.SwiftRetainOp == RHS.SwiftRetainOp &&
744750
LHS.SwiftReleaseOp == RHS.SwiftReleaseOp &&
751+
LHS.SwiftConformance == RHS.SwiftConformance &&
745752
LHS.isFlagEnum() == RHS.isFlagEnum() &&
746753
LHS.isSwiftCopyable() == RHS.isSwiftCopyable() &&
747754
LHS.EnumExtensibility == RHS.EnumExtensibility;

clang/lib/APINotes/APINotesFormat.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ const uint16_t VERSION_MAJOR = 0;
2424
/// API notes file minor version number.
2525
///
2626
/// When the format changes IN ANY WAY, this number should be incremented.
27-
const uint16_t VERSION_MINOR = 28; // nested tags
27+
const uint16_t VERSION_MINOR = 29; // SwiftConformsTo
2828

2929
const uint8_t kSwiftCopyable = 1;
3030
const uint8_t kSwiftNonCopyable = 2;

clang/lib/APINotes/APINotesReader.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -572,6 +572,12 @@ class TagTableInfo
572572
ReleaseOpLength - 1);
573573
Data += ReleaseOpLength - 1;
574574
}
575+
if (unsigned ConformanceLength =
576+
endian::readNext<uint16_t, llvm::endianness::little>(Data)) {
577+
Info.SwiftConformance = std::string(reinterpret_cast<const char *>(Data),
578+
ConformanceLength - 1);
579+
Data += ConformanceLength - 1;
580+
}
575581

576582
ReadCommonTypeInfo(Data, Info);
577583
return Info;

clang/lib/APINotes/APINotesWriter.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1189,6 +1189,7 @@ class TagTableInfo : public CommonTypeTableInfo<TagTableInfo, TagInfo> {
11891189
return 2 + (TI.SwiftImportAs ? TI.SwiftImportAs->size() : 0) +
11901190
2 + (TI.SwiftRetainOp ? TI.SwiftRetainOp->size() : 0) +
11911191
2 + (TI.SwiftReleaseOp ? TI.SwiftReleaseOp->size() : 0) +
1192+
2 + (TI.SwiftConformance ? TI.SwiftConformance->size() : 0) +
11921193
2 + getCommonTypeInfoSize(TI);
11931194
}
11941195

@@ -1230,6 +1231,12 @@ class TagTableInfo : public CommonTypeTableInfo<TagTableInfo, TagInfo> {
12301231
} else {
12311232
writer.write<uint16_t>(0);
12321233
}
1234+
if (auto Conformance = TI.SwiftConformance) {
1235+
writer.write<uint16_t>(Conformance->size() + 1);
1236+
OS.write(Conformance->c_str(), Conformance->size());
1237+
} else {
1238+
writer.write<uint16_t>(0);
1239+
}
12331240

12341241
emitCommonTypeInfo(OS, TI);
12351242
}

clang/lib/APINotes/APINotesYAMLCompiler.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,7 @@ struct Tag {
419419
std::optional<std::string> SwiftImportAs;
420420
std::optional<std::string> SwiftRetainOp;
421421
std::optional<std::string> SwiftReleaseOp;
422+
std::optional<std::string> SwiftConformance;
422423
std::optional<EnumExtensibilityKind> EnumExtensibility;
423424
std::optional<bool> FlagEnum;
424425
std::optional<EnumConvenienceAliasKind> EnumConvenienceKind;
@@ -456,6 +457,7 @@ template <> struct MappingTraits<Tag> {
456457
IO.mapOptional("SwiftImportAs", T.SwiftImportAs);
457458
IO.mapOptional("SwiftReleaseOp", T.SwiftReleaseOp);
458459
IO.mapOptional("SwiftRetainOp", T.SwiftRetainOp);
460+
IO.mapOptional("SwiftConformsTo", T.SwiftConformance);
459461
IO.mapOptional("EnumExtensibility", T.EnumExtensibility);
460462
IO.mapOptional("FlagEnum", T.FlagEnum);
461463
IO.mapOptional("EnumKind", T.EnumConvenienceKind);
@@ -920,6 +922,8 @@ class YAMLConverter {
920922
TI.SwiftRetainOp = T.SwiftRetainOp;
921923
if (T.SwiftReleaseOp)
922924
TI.SwiftReleaseOp = T.SwiftReleaseOp;
925+
if (T.SwiftConformance)
926+
TI.SwiftConformance = T.SwiftConformance;
923927

924928
if (T.SwiftCopyable)
925929
TI.setSwiftCopyable(T.SwiftCopyable);

clang/lib/Sema/SemaAPINotes.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -605,6 +605,10 @@ static void ProcessAPINotes(Sema &S, TagDecl *D, const api_notes::TagInfo &Info,
605605
D->addAttr(
606606
SwiftAttrAttr::Create(S.Context, "release:" + ReleaseOp.value()));
607607

608+
if (auto ConformsTo = Info.SwiftConformance)
609+
D->addAttr(
610+
SwiftAttrAttr::Create(S.Context, "conforms_to:" + ConformsTo.value()));
611+
608612
if (auto Copyable = Info.isSwiftCopyable()) {
609613
if (!*Copyable)
610614
D->addAttr(SwiftAttrAttr::Create(S.Context, "~Copyable"));

clang/test/APINotes/Inputs/Headers/SwiftImportAs.apinotes

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ Tags:
77
SwiftImportAs: reference
88
SwiftReleaseOp: RCRelease
99
SwiftRetainOp: RCRetain
10+
SwiftConformsTo: MySwiftModule.MySwiftRefCountedProtocol
1011
- Name: NonCopyableType
1112
SwiftCopyable: false
13+
SwiftConformsTo: MySwiftModule.MySwiftNonCopyableProtocol
1214
- Name: CopyableType
1315
SwiftCopyable: true

clang/test/APINotes/swift-import-as.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,11 @@
1616
// CHECK-REF-COUNTED: SwiftAttrAttr {{.+}} <<invalid sloc>> "import_reference"
1717
// CHECK-REF-COUNTED: SwiftAttrAttr {{.+}} <<invalid sloc>> "retain:RCRetain"
1818
// CHECK-REF-COUNTED: SwiftAttrAttr {{.+}} <<invalid sloc>> "release:RCRelease"
19+
// CHECK-REF-COUNTED: SwiftAttrAttr {{.+}} <<invalid sloc>> "conforms_to:MySwiftModule.MySwiftRefCountedProtocol"
1920

2021
// CHECK-NON-COPYABLE: Dumping NonCopyableType:
2122
// CHECK-NON-COPYABLE-NEXT: CXXRecordDecl {{.+}} imported in SwiftImportAs {{.+}} struct NonCopyableType
23+
// CHECK-NON-COPYABLE: SwiftAttrAttr {{.+}} <<invalid sloc>> "conforms_to:MySwiftModule.MySwiftNonCopyableProtocol"
2224
// CHECK-NON-COPYABLE: SwiftAttrAttr {{.+}} <<invalid sloc>> "~Copyable"
2325

2426
// CHECK-COPYABLE: Dumping CopyableType:

0 commit comments

Comments
 (0)