Skip to content

Commit f0394cf

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
1 parent 697fcd0 commit f0394cf

File tree

9 files changed

+68
-6
lines changed

9 files changed

+68
-6
lines changed

clang/include/clang/APINotes/Types.h

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -675,14 +675,21 @@ class TagInfo : public CommonTypeInfo {
675675
LLVM_PREFERRED_TYPE(bool)
676676
unsigned IsFlagEnum : 1;
677677

678+
LLVM_PREFERRED_TYPE(bool)
679+
unsigned SwiftCopyableSpecified : 1;
680+
LLVM_PREFERRED_TYPE(bool)
681+
unsigned SwiftCopyable : 1;
682+
678683
public:
679684
std::optional<std::string> SwiftImportAs;
680685
std::optional<std::string> SwiftRetainOp;
681686
std::optional<std::string> SwiftReleaseOp;
682687

683688
std::optional<EnumExtensibilityKind> EnumExtensibility;
684689

685-
TagInfo() : HasFlagEnum(0), IsFlagEnum(0) {}
690+
TagInfo()
691+
: HasFlagEnum(0), IsFlagEnum(0), SwiftCopyableSpecified(false),
692+
SwiftCopyable(false) {}
686693

687694
std::optional<bool> isFlagEnum() const {
688695
if (HasFlagEnum)
@@ -694,6 +701,15 @@ class TagInfo : public CommonTypeInfo {
694701
IsFlagEnum = Value.value_or(false);
695702
}
696703

704+
std::optional<bool> isSwiftCopyable() const {
705+
return SwiftCopyableSpecified ? std::optional<bool>(SwiftCopyable)
706+
: std::nullopt;
707+
}
708+
void setSwiftCopyable(std::optional<bool> Value) {
709+
SwiftCopyableSpecified = Value.has_value();
710+
SwiftCopyable = Value.value_or(false);
711+
}
712+
697713
TagInfo &operator|=(const TagInfo &RHS) {
698714
static_cast<CommonTypeInfo &>(*this) |= RHS;
699715

@@ -710,6 +726,9 @@ class TagInfo : public CommonTypeInfo {
710726
if (!EnumExtensibility)
711727
EnumExtensibility = RHS.EnumExtensibility;
712728

729+
if (!SwiftCopyableSpecified)
730+
setSwiftCopyable(RHS.isSwiftCopyable());
731+
713732
return *this;
714733
}
715734

@@ -724,6 +743,7 @@ inline bool operator==(const TagInfo &LHS, const TagInfo &RHS) {
724743
LHS.SwiftRetainOp == RHS.SwiftRetainOp &&
725744
LHS.SwiftReleaseOp == RHS.SwiftReleaseOp &&
726745
LHS.isFlagEnum() == RHS.isFlagEnum() &&
746+
LHS.isSwiftCopyable() == RHS.isSwiftCopyable() &&
727747
LHS.EnumExtensibility == RHS.EnumExtensibility;
728748
}
729749

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
@@ -527,6 +527,13 @@ class TagTableInfo
527527
Info.EnumExtensibility =
528528
static_cast<EnumExtensibilityKind>((Payload & 0x3) - 1);
529529

530+
uint8_t Copyable =
531+
endian::readNext<uint8_t, llvm::endianness::little>(Data);
532+
if (Copyable == kSwiftNonCopyable)
533+
Info.setSwiftCopyable(std::optional(false));
534+
else if (Copyable == kSwiftCopyable)
535+
Info.setSwiftCopyable(std::optional(true));
536+
530537
unsigned ImportAsLength =
531538
endian::readNext<uint16_t, llvm::endianness::little>(Data);
532539
if (ImportAsLength > 0) {

clang/lib/APINotes/APINotesWriter.cpp

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1125,10 +1125,10 @@ class CommonTypeTableInfo
11251125
class TagTableInfo : public CommonTypeTableInfo<TagTableInfo, TagInfo> {
11261126
public:
11271127
unsigned getUnversionedInfoSize(const TagInfo &TI) {
1128-
return 2 + (TI.SwiftImportAs ? TI.SwiftImportAs->size() : 0) +
1129-
2 + (TI.SwiftRetainOp ? TI.SwiftRetainOp->size() : 0) +
1130-
2 + (TI.SwiftReleaseOp ? TI.SwiftReleaseOp->size() : 0) +
1131-
1 + getCommonTypeInfoSize(TI);
1128+
return 2 + (TI.SwiftImportAs ? TI.SwiftImportAs->size() : 0) + 2 +
1129+
(TI.SwiftRetainOp ? TI.SwiftRetainOp->size() : 0) + 2 +
1130+
(TI.SwiftReleaseOp ? TI.SwiftReleaseOp->size() : 0) + 2 +
1131+
getCommonTypeInfoSize(TI);
11321132
}
11331133

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

11471147
writer.write<uint8_t>(Flags);
11481148

1149+
if (auto Copyable = TI.isSwiftCopyable())
1150+
writer.write<uint8_t>(*Copyable ? kSwiftCopyable : kSwiftNonCopyable);
1151+
else
1152+
writer.write<uint8_t>(0);
1153+
11491154
if (auto ImportAs = TI.SwiftImportAs) {
11501155
writer.write<uint16_t>(ImportAs->size() + 1);
11511156
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
@@ -419,6 +419,7 @@ struct Tag {
419419
std::optional<EnumExtensibilityKind> EnumExtensibility;
420420
std::optional<bool> FlagEnum;
421421
std::optional<EnumConvenienceAliasKind> EnumConvenienceKind;
422+
std::optional<bool> SwiftCopyable;
422423
};
423424

424425
typedef std::vector<Tag> TagsSeq;
@@ -452,6 +453,7 @@ template <> struct MappingTraits<Tag> {
452453
IO.mapOptional("EnumExtensibility", T.EnumExtensibility);
453454
IO.mapOptional("FlagEnum", T.FlagEnum);
454455
IO.mapOptional("EnumKind", T.EnumConvenienceKind);
456+
IO.mapOptional("SwiftCopyable", T.SwiftCopyable);
455457
}
456458
};
457459
} // namespace yaml
@@ -1009,6 +1011,9 @@ class YAMLConverter {
10091011
if (Tag.SwiftReleaseOp)
10101012
TI.SwiftReleaseOp = Tag.SwiftReleaseOp;
10111013

1014+
if (Tag.SwiftCopyable)
1015+
TI.setSwiftCopyable(Tag.SwiftCopyable);
1016+
10121017
if (Tag.EnumConvenienceKind) {
10131018
if (Tag.EnumExtensibility) {
10141019
emitError(

clang/lib/Sema/SemaAPINotes.cpp

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

597+
if (auto Copyable = Info.isSwiftCopyable()) {
598+
if (!*Copyable)
599+
D->addAttr(SwiftAttrAttr::Create(S.Context, "~Copyable"));
600+
}
601+
597602
if (auto Extensibility = Info.EnumExtensibility) {
598603
using api_notes::EnumExtensibilityKind;
599604
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)