Skip to content

Commit 8c418a2

Browse files
committed
[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 (cherry picked from commit dc8c217)
1 parent 9e885d1 commit 8c418a2

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
@@ -688,6 +688,9 @@ class TagInfo : public CommonTypeInfo {
688688
std::optional<std::string> SwiftRetainOp;
689689
std::optional<std::string> SwiftReleaseOp;
690690

691+
/// The Swift protocol that this type should be automatically conformed to.
692+
std::optional<std::string> SwiftConformance;
693+
691694
std::optional<EnumExtensibilityKind> EnumExtensibility;
692695

693696
TagInfo()
@@ -723,6 +726,9 @@ class TagInfo : public CommonTypeInfo {
723726
if (!SwiftReleaseOp)
724727
SwiftReleaseOp = RHS.SwiftReleaseOp;
725728

729+
if (!SwiftConformance)
730+
SwiftConformance = RHS.SwiftConformance;
731+
726732
if (!HasFlagEnum)
727733
setFlagEnum(RHS.isFlagEnum());
728734

@@ -745,6 +751,7 @@ inline bool operator==(const TagInfo &LHS, const TagInfo &RHS) {
745751
LHS.SwiftImportAs == RHS.SwiftImportAs &&
746752
LHS.SwiftRetainOp == RHS.SwiftRetainOp &&
747753
LHS.SwiftReleaseOp == RHS.SwiftReleaseOp &&
754+
LHS.SwiftConformance == RHS.SwiftConformance &&
748755
LHS.isFlagEnum() == RHS.isFlagEnum() &&
749756
LHS.isSwiftCopyable() == RHS.isSwiftCopyable() &&
750757
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
@@ -1196,6 +1196,7 @@ class TagTableInfo : public CommonTypeTableInfo<TagTableInfo, TagInfo> {
11961196
return 2 + (TI.SwiftImportAs ? TI.SwiftImportAs->size() : 0) +
11971197
2 + (TI.SwiftRetainOp ? TI.SwiftRetainOp->size() : 0) +
11981198
2 + (TI.SwiftReleaseOp ? TI.SwiftReleaseOp->size() : 0) +
1199+
2 + (TI.SwiftConformance ? TI.SwiftConformance->size() : 0) +
11991200
2 + getCommonTypeInfoSize(TI);
12001201
}
12011202

@@ -1237,6 +1238,12 @@ class TagTableInfo : public CommonTypeTableInfo<TagTableInfo, TagInfo> {
12371238
} else {
12381239
writer.write<uint16_t>(0);
12391240
}
1241+
if (auto Conformance = TI.SwiftConformance) {
1242+
writer.write<uint16_t>(Conformance->size() + 1);
1243+
OS.write(Conformance->c_str(), Conformance->size());
1244+
} else {
1245+
writer.write<uint16_t>(0);
1246+
}
12401247

12411248
emitCommonTypeInfo(OS, TI);
12421249
}

clang/lib/APINotes/APINotesYAMLCompiler.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -532,6 +532,7 @@ struct Tag {
532532
std::optional<std::string> SwiftImportAs;
533533
std::optional<std::string> SwiftRetainOp;
534534
std::optional<std::string> SwiftReleaseOp;
535+
std::optional<std::string> SwiftConformance;
535536
std::optional<EnumExtensibilityKind> EnumExtensibility;
536537
std::optional<bool> FlagEnum;
537538
std::optional<EnumConvenienceAliasKind> EnumConvenienceKind;
@@ -569,6 +570,7 @@ template <> struct MappingTraits<Tag> {
569570
IO.mapOptional("SwiftImportAs", T.SwiftImportAs);
570571
IO.mapOptional("SwiftReleaseOp", T.SwiftReleaseOp);
571572
IO.mapOptional("SwiftRetainOp", T.SwiftRetainOp);
573+
IO.mapOptional("SwiftConformsTo", T.SwiftConformance);
572574
IO.mapOptional("EnumExtensibility", T.EnumExtensibility);
573575
IO.mapOptional("FlagEnum", T.FlagEnum);
574576
IO.mapOptional("EnumKind", T.EnumConvenienceKind);
@@ -1033,6 +1035,8 @@ class YAMLConverter {
10331035
TI.SwiftRetainOp = T.SwiftRetainOp;
10341036
if (T.SwiftReleaseOp)
10351037
TI.SwiftReleaseOp = T.SwiftReleaseOp;
1038+
if (T.SwiftConformance)
1039+
TI.SwiftConformance = T.SwiftConformance;
10361040

10371041
if (T.SwiftCopyable)
10381042
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)