Skip to content

Commit 83b7524

Browse files
committed
[APINotes] Allow annotating a C++ type as non-copyable in Swift
Certain C++ types, such as `std::chrono::tzdb` in libstdc++, are non-copyable, but don't explicitly delete their copy constructor. Instead, they trigger template instantiation errors when trying to call their implicit copy constructor. The Swift compiler inserts implicit copies of value types in some cases, which trigger compiler errors for such types. This adds a Clang API Notes attribute that allows annotating C++ types as non-copyable in Swift. This lets the Swift compiler know that it should not try to instantiate the implicit copy constructor for a C++ struct. rdar://127049438 (cherry picked from commit b2098db)
1 parent db53966 commit 83b7524

File tree

9 files changed

+65
-3
lines changed

9 files changed

+65
-3
lines changed

clang/include/clang/APINotes/Types.h

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -678,14 +678,21 @@ class TagInfo : public CommonTypeInfo {
678678
LLVM_PREFERRED_TYPE(bool)
679679
unsigned IsFlagEnum : 1;
680680

681+
LLVM_PREFERRED_TYPE(bool)
682+
unsigned SwiftCopyableSpecified : 1;
683+
LLVM_PREFERRED_TYPE(bool)
684+
unsigned SwiftCopyable : 1;
685+
681686
public:
682687
std::optional<std::string> SwiftImportAs;
683688
std::optional<std::string> SwiftRetainOp;
684689
std::optional<std::string> SwiftReleaseOp;
685690

686691
std::optional<EnumExtensibilityKind> EnumExtensibility;
687692

688-
TagInfo() : HasFlagEnum(0), IsFlagEnum(0) {}
693+
TagInfo()
694+
: HasFlagEnum(0), IsFlagEnum(0), SwiftCopyableSpecified(false),
695+
SwiftCopyable(false) {}
689696

690697
std::optional<bool> isFlagEnum() const {
691698
if (HasFlagEnum)
@@ -697,6 +704,15 @@ class TagInfo : public CommonTypeInfo {
697704
IsFlagEnum = Value.value_or(false);
698705
}
699706

707+
std::optional<bool> isSwiftCopyable() const {
708+
return SwiftCopyableSpecified ? std::optional<bool>(SwiftCopyable)
709+
: std::nullopt;
710+
}
711+
void setSwiftCopyable(std::optional<bool> Value) {
712+
SwiftCopyableSpecified = Value.has_value();
713+
SwiftCopyable = Value.value_or(false);
714+
}
715+
700716
TagInfo &operator|=(const TagInfo &RHS) {
701717
static_cast<CommonTypeInfo &>(*this) |= RHS;
702718

@@ -713,6 +729,9 @@ class TagInfo : public CommonTypeInfo {
713729
if (!EnumExtensibility)
714730
EnumExtensibility = RHS.EnumExtensibility;
715731

732+
if (!SwiftCopyableSpecified)
733+
setSwiftCopyable(RHS.isSwiftCopyable());
734+
716735
return *this;
717736
}
718737

@@ -727,6 +746,7 @@ inline bool operator==(const TagInfo &LHS, const TagInfo &RHS) {
727746
LHS.SwiftRetainOp == RHS.SwiftRetainOp &&
728747
LHS.SwiftReleaseOp == RHS.SwiftReleaseOp &&
729748
LHS.isFlagEnum() == RHS.isFlagEnum() &&
749+
LHS.isSwiftCopyable() == RHS.isSwiftCopyable() &&
730750
LHS.EnumExtensibility == RHS.EnumExtensibility;
731751
}
732752

clang/lib/APINotes/APINotesFormat.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,10 @@ 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 = 25; // SwiftImportAs
27+
const uint16_t VERSION_MINOR = 26; // SwiftCopyable
28+
29+
const uint8_t kSwiftCopyable = 1;
30+
const uint8_t kSwiftNonCopyable = 2;
2831

2932
using IdentifierID = llvm::PointerEmbeddedInt<unsigned, 31>;
3033
using IdentifierIDField = llvm::BCVBR<16>;

clang/lib/APINotes/APINotesReader.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -552,6 +552,13 @@ class TagTableInfo
552552
Info.EnumExtensibility =
553553
static_cast<EnumExtensibilityKind>((Payload & 0x3) - 1);
554554

555+
uint8_t Copyable =
556+
endian::readNext<uint8_t, llvm::endianness::little, unaligned>(Data);
557+
if (Copyable == kSwiftNonCopyable)
558+
Info.setSwiftCopyable(std::optional(false));
559+
else if (Copyable == kSwiftCopyable)
560+
Info.setSwiftCopyable(std::optional(true));
561+
555562
unsigned ImportAsLength =
556563
endian::readNext<uint16_t, llvm::endianness::little, unaligned>(Data);
557564
if (ImportAsLength > 0) {

clang/lib/APINotes/APINotesWriter.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1135,7 +1135,7 @@ class TagTableInfo : public CommonTypeTableInfo<TagTableInfo, TagInfo> {
11351135
return 2 + (TI.SwiftImportAs ? TI.SwiftImportAs->size() : 0) +
11361136
2 + (TI.SwiftRetainOp ? TI.SwiftRetainOp->size() : 0) +
11371137
2 + (TI.SwiftReleaseOp ? TI.SwiftReleaseOp->size() : 0) +
1138-
1 + getCommonTypeInfoSize(TI);
1138+
2 + getCommonTypeInfoSize(TI);
11391139
}
11401140

11411141
void emitUnversionedInfo(raw_ostream &OS, const TagInfo &TI) {
@@ -1153,6 +1153,11 @@ class TagTableInfo : public CommonTypeTableInfo<TagTableInfo, TagInfo> {
11531153

11541154
writer.write<uint8_t>(Flags);
11551155

1156+
if (auto Copyable = TI.isSwiftCopyable())
1157+
writer.write<uint8_t>(*Copyable ? kSwiftCopyable : kSwiftNonCopyable);
1158+
else
1159+
writer.write<uint8_t>(0);
1160+
11561161
if (auto ImportAs = TI.SwiftImportAs) {
11571162
writer.write<uint16_t>(ImportAs->size() + 1);
11581163
OS.write(ImportAs->c_str(), ImportAs->size());

clang/lib/APINotes/APINotesYAMLCompiler.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -532,6 +532,7 @@ struct Tag {
532532
std::optional<EnumExtensibilityKind> EnumExtensibility;
533533
std::optional<bool> FlagEnum;
534534
std::optional<EnumConvenienceAliasKind> EnumConvenienceKind;
535+
std::optional<bool> SwiftCopyable;
535536
};
536537

537538
typedef std::vector<Tag> TagsSeq;
@@ -565,6 +566,7 @@ template <> struct MappingTraits<Tag> {
565566
IO.mapOptional("EnumExtensibility", T.EnumExtensibility);
566567
IO.mapOptional("FlagEnum", T.FlagEnum);
567568
IO.mapOptional("EnumKind", T.EnumConvenienceKind);
569+
IO.mapOptional("SwiftCopyable", T.SwiftCopyable);
568570
}
569571
};
570572
} // namespace yaml
@@ -1122,6 +1124,9 @@ class YAMLConverter {
11221124
if (Tag.SwiftReleaseOp)
11231125
TI.SwiftReleaseOp = Tag.SwiftReleaseOp;
11241126

1127+
if (Tag.SwiftCopyable)
1128+
TI.setSwiftCopyable(Tag.SwiftCopyable);
1129+
11251130
if (Tag.EnumConvenienceKind) {
11261131
if (Tag.EnumExtensibility) {
11271132
emitError(

clang/lib/Sema/SemaAPINotes.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -592,6 +592,11 @@ static void ProcessAPINotes(Sema &S, TagDecl *D, const api_notes::TagInfo &Info,
592592
D->addAttr(
593593
SwiftAttrAttr::Create(S.Context, "release:" + ReleaseOp.value()));
594594

595+
if (auto Copyable = Info.isSwiftCopyable()) {
596+
if (!*Copyable)
597+
D->addAttr(SwiftAttrAttr::Create(S.Context, "~Copyable"));
598+
}
599+
595600
if (auto Extensibility = Info.EnumExtensibility) {
596601
using api_notes::EnumExtensibilityKind;
597602
bool ShouldAddAttribute = (*Extensibility != EnumExtensibilityKind::None);

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,7 @@ Tags:
77
SwiftImportAs: reference
88
SwiftReleaseOp: RCRelease
99
SwiftRetainOp: RCRetain
10+
- Name: NonCopyableType
11+
SwiftCopyable: false
12+
- Name: CopyableType
13+
SwiftCopyable: true

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,6 @@ struct RefCountedType { int value; };
44

55
inline void RCRetain(RefCountedType *x) { x->value++; }
66
inline void RCRelease(RefCountedType *x) { x->value--; }
7+
8+
struct NonCopyableType { int value; };
9+
struct CopyableType { int value; };

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
// RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fdisable-module-hash -fapinotes-modules -fsyntax-only -I %S/Inputs/Headers %s -x c++
33
// RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fdisable-module-hash -fapinotes-modules -fsyntax-only -I %S/Inputs/Headers %s -x c++ -ast-dump -ast-dump-filter ImmortalRefType | FileCheck -check-prefix=CHECK-IMMORTAL %s
44
// RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fdisable-module-hash -fapinotes-modules -fsyntax-only -I %S/Inputs/Headers %s -x c++ -ast-dump -ast-dump-filter RefCountedType | FileCheck -check-prefix=CHECK-REF-COUNTED %s
5+
// RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fdisable-module-hash -fapinotes-modules -fsyntax-only -I %S/Inputs/Headers %s -x c++ -ast-dump -ast-dump-filter NonCopyableType | FileCheck -check-prefix=CHECK-NON-COPYABLE %s
6+
// RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fdisable-module-hash -fapinotes-modules -fsyntax-only -I %S/Inputs/Headers %s -x c++ -ast-dump -ast-dump-filter CopyableType | FileCheck -check-prefix=CHECK-COPYABLE %s
57

68
#include <SwiftImportAs.h>
79

@@ -14,3 +16,11 @@
1416
// CHECK-REF-COUNTED: SwiftAttrAttr {{.+}} <<invalid sloc>> "import_reference"
1517
// CHECK-REF-COUNTED: SwiftAttrAttr {{.+}} <<invalid sloc>> "retain:RCRetain"
1618
// CHECK-REF-COUNTED: SwiftAttrAttr {{.+}} <<invalid sloc>> "release:RCRelease"
19+
20+
// CHECK-NON-COPYABLE: Dumping NonCopyableType:
21+
// CHECK-NON-COPYABLE-NEXT: CXXRecordDecl {{.+}} imported in SwiftImportAs {{.+}} struct NonCopyableType
22+
// CHECK-NON-COPYABLE: SwiftAttrAttr {{.+}} <<invalid sloc>> "~Copyable"
23+
24+
// CHECK-COPYABLE: Dumping CopyableType:
25+
// CHECK-COPYABLE-NEXT: CXXRecordDecl {{.+}} imported in SwiftImportAs {{.+}} struct CopyableType
26+
// CHECK-COPYABLE-NOT: SwiftAttrAttr

0 commit comments

Comments
 (0)