-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[APINotes] Allow annotating a C++ type as non-copyable in Swift #90064
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
You can test this locally with the following command:git-clang-format --diff 697fcd009855a579f756dfe34498a815ed9dc3fd 60191f2bc6cb191b5b256d220a8b1d07093bddd1 -- clang/include/clang/APINotes/Types.h clang/lib/APINotes/APINotesFormat.h clang/lib/APINotes/APINotesReader.cpp clang/lib/APINotes/APINotesWriter.cpp clang/lib/APINotes/APINotesYAMLCompiler.cpp clang/lib/Sema/SemaAPINotes.cpp clang/test/APINotes/Inputs/Headers/SwiftImportAs.h clang/test/APINotes/swift-import-as.cpp View the diff from clang-format here.diff --git a/clang/lib/APINotes/APINotesWriter.cpp b/clang/lib/APINotes/APINotesWriter.cpp
index 3e61597631..6bd96c0b81 100644
--- a/clang/lib/APINotes/APINotesWriter.cpp
+++ b/clang/lib/APINotes/APINotesWriter.cpp
@@ -1125,10 +1125,10 @@ public:
class TagTableInfo : public CommonTypeTableInfo<TagTableInfo, TagInfo> {
public:
unsigned getUnversionedInfoSize(const TagInfo &TI) {
- return 2 + (TI.SwiftImportAs ? TI.SwiftImportAs->size() : 0) +
- 2 + (TI.SwiftRetainOp ? TI.SwiftRetainOp->size() : 0) +
- 2 + (TI.SwiftReleaseOp ? TI.SwiftReleaseOp->size() : 0) +
- 2 + getCommonTypeInfoSize(TI);
+ return 2 + (TI.SwiftImportAs ? TI.SwiftImportAs->size() : 0) + 2 +
+ (TI.SwiftRetainOp ? TI.SwiftRetainOp->size() : 0) + 2 +
+ (TI.SwiftReleaseOp ? TI.SwiftReleaseOp->size() : 0) + 2 +
+ getCommonTypeInfoSize(TI);
}
void emitUnversionedInfo(raw_ostream &OS, const TagInfo &TI) {
|
a3ea8c9
to
9eac3a3
Compare
return 2 + (TI.SwiftImportAs ? TI.SwiftImportAs->size() : 0) + 2 + | ||
(TI.SwiftRetainOp ? TI.SwiftRetainOp->size() : 0) + 2 + | ||
(TI.SwiftReleaseOp ? TI.SwiftReleaseOp->size() : 0) + 2 + | ||
getCommonTypeInfoSize(TI); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This feels less readable :(
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, it's unfortunate that clang-format is overly strict about this. I can revert this and try to merge despite clang-format complaining, if you think that's better.
9eac3a3
to
f0394cf
Compare
@llvm/pr-subscribers-clang Author: Egor Zhdan (egorzhdan) ChangesCertain C++ types, such as 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 Full diff: https://github.com/llvm/llvm-project/pull/90064.diff 9 Files Affected:
diff --git a/clang/include/clang/APINotes/Types.h b/clang/include/clang/APINotes/Types.h
index 93bb045d6a6670..026a4a431e7349 100644
--- a/clang/include/clang/APINotes/Types.h
+++ b/clang/include/clang/APINotes/Types.h
@@ -675,6 +675,11 @@ class TagInfo : public CommonTypeInfo {
LLVM_PREFERRED_TYPE(bool)
unsigned IsFlagEnum : 1;
+ LLVM_PREFERRED_TYPE(bool)
+ unsigned SwiftCopyableSpecified : 1;
+ LLVM_PREFERRED_TYPE(bool)
+ unsigned SwiftCopyable : 1;
+
public:
std::optional<std::string> SwiftImportAs;
std::optional<std::string> SwiftRetainOp;
@@ -682,7 +687,9 @@ class TagInfo : public CommonTypeInfo {
std::optional<EnumExtensibilityKind> EnumExtensibility;
- TagInfo() : HasFlagEnum(0), IsFlagEnum(0) {}
+ TagInfo()
+ : HasFlagEnum(0), IsFlagEnum(0), SwiftCopyableSpecified(false),
+ SwiftCopyable(false) {}
std::optional<bool> isFlagEnum() const {
if (HasFlagEnum)
@@ -694,6 +701,15 @@ class TagInfo : public CommonTypeInfo {
IsFlagEnum = Value.value_or(false);
}
+ std::optional<bool> isSwiftCopyable() const {
+ return SwiftCopyableSpecified ? std::optional<bool>(SwiftCopyable)
+ : std::nullopt;
+ }
+ void setSwiftCopyable(std::optional<bool> Value) {
+ SwiftCopyableSpecified = Value.has_value();
+ SwiftCopyable = Value.value_or(false);
+ }
+
TagInfo &operator|=(const TagInfo &RHS) {
static_cast<CommonTypeInfo &>(*this) |= RHS;
@@ -710,6 +726,9 @@ class TagInfo : public CommonTypeInfo {
if (!EnumExtensibility)
EnumExtensibility = RHS.EnumExtensibility;
+ if (!SwiftCopyableSpecified)
+ setSwiftCopyable(RHS.isSwiftCopyable());
+
return *this;
}
@@ -724,6 +743,7 @@ inline bool operator==(const TagInfo &LHS, const TagInfo &RHS) {
LHS.SwiftRetainOp == RHS.SwiftRetainOp &&
LHS.SwiftReleaseOp == RHS.SwiftReleaseOp &&
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 615314c46f09ca..97e630e97fdcc2 100644
--- a/clang/lib/APINotes/APINotesFormat.h
+++ b/clang/lib/APINotes/APINotesFormat.h
@@ -24,7 +24,10 @@ 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 = 25; // SwiftImportAs
+const uint16_t VERSION_MINOR = 26; // SwiftCopyable
+
+const uint8_t kSwiftCopyable = 1;
+const uint8_t kSwiftNonCopyable = 2;
using IdentifierID = llvm::PointerEmbeddedInt<unsigned, 31>;
using IdentifierIDField = llvm::BCVBR<16>;
diff --git a/clang/lib/APINotes/APINotesReader.cpp b/clang/lib/APINotes/APINotesReader.cpp
index dfc3beb6fa13ee..b60ca685f62c98 100644
--- a/clang/lib/APINotes/APINotesReader.cpp
+++ b/clang/lib/APINotes/APINotesReader.cpp
@@ -527,6 +527,13 @@ class TagTableInfo
Info.EnumExtensibility =
static_cast<EnumExtensibilityKind>((Payload & 0x3) - 1);
+ uint8_t Copyable =
+ endian::readNext<uint8_t, llvm::endianness::little>(Data);
+ if (Copyable == kSwiftNonCopyable)
+ Info.setSwiftCopyable(std::optional(false));
+ else if (Copyable == kSwiftCopyable)
+ Info.setSwiftCopyable(std::optional(true));
+
unsigned ImportAsLength =
endian::readNext<uint16_t, llvm::endianness::little>(Data);
if (ImportAsLength > 0) {
diff --git a/clang/lib/APINotes/APINotesWriter.cpp b/clang/lib/APINotes/APINotesWriter.cpp
index e3f5d102fcd07f..6bd96c0b813cf7 100644
--- a/clang/lib/APINotes/APINotesWriter.cpp
+++ b/clang/lib/APINotes/APINotesWriter.cpp
@@ -1125,10 +1125,10 @@ class CommonTypeTableInfo
class TagTableInfo : public CommonTypeTableInfo<TagTableInfo, TagInfo> {
public:
unsigned getUnversionedInfoSize(const TagInfo &TI) {
- return 2 + (TI.SwiftImportAs ? TI.SwiftImportAs->size() : 0) +
- 2 + (TI.SwiftRetainOp ? TI.SwiftRetainOp->size() : 0) +
- 2 + (TI.SwiftReleaseOp ? TI.SwiftReleaseOp->size() : 0) +
- 1 + getCommonTypeInfoSize(TI);
+ return 2 + (TI.SwiftImportAs ? TI.SwiftImportAs->size() : 0) + 2 +
+ (TI.SwiftRetainOp ? TI.SwiftRetainOp->size() : 0) + 2 +
+ (TI.SwiftReleaseOp ? TI.SwiftReleaseOp->size() : 0) + 2 +
+ getCommonTypeInfoSize(TI);
}
void emitUnversionedInfo(raw_ostream &OS, const TagInfo &TI) {
@@ -1146,6 +1146,11 @@ class TagTableInfo : public CommonTypeTableInfo<TagTableInfo, TagInfo> {
writer.write<uint8_t>(Flags);
+ if (auto Copyable = TI.isSwiftCopyable())
+ writer.write<uint8_t>(*Copyable ? kSwiftCopyable : kSwiftNonCopyable);
+ else
+ writer.write<uint8_t>(0);
+
if (auto ImportAs = TI.SwiftImportAs) {
writer.write<uint16_t>(ImportAs->size() + 1);
OS.write(ImportAs->c_str(), ImportAs->size());
diff --git a/clang/lib/APINotes/APINotesYAMLCompiler.cpp b/clang/lib/APINotes/APINotesYAMLCompiler.cpp
index 57d6da7a177596..2295d769d344b8 100644
--- a/clang/lib/APINotes/APINotesYAMLCompiler.cpp
+++ b/clang/lib/APINotes/APINotesYAMLCompiler.cpp
@@ -419,6 +419,7 @@ struct Tag {
std::optional<EnumExtensibilityKind> EnumExtensibility;
std::optional<bool> FlagEnum;
std::optional<EnumConvenienceAliasKind> EnumConvenienceKind;
+ std::optional<bool> SwiftCopyable;
};
typedef std::vector<Tag> TagsSeq;
@@ -452,6 +453,7 @@ template <> struct MappingTraits<Tag> {
IO.mapOptional("EnumExtensibility", T.EnumExtensibility);
IO.mapOptional("FlagEnum", T.FlagEnum);
IO.mapOptional("EnumKind", T.EnumConvenienceKind);
+ IO.mapOptional("SwiftCopyable", T.SwiftCopyable);
}
};
} // namespace yaml
@@ -1009,6 +1011,9 @@ class YAMLConverter {
if (Tag.SwiftReleaseOp)
TI.SwiftReleaseOp = Tag.SwiftReleaseOp;
+ if (Tag.SwiftCopyable)
+ TI.setSwiftCopyable(Tag.SwiftCopyable);
+
if (Tag.EnumConvenienceKind) {
if (Tag.EnumExtensibility) {
emitError(
diff --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp
index 4c445f28bba8c6..c5998aca0d7211 100644
--- a/clang/lib/Sema/SemaAPINotes.cpp
+++ b/clang/lib/Sema/SemaAPINotes.cpp
@@ -594,6 +594,11 @@ static void ProcessAPINotes(Sema &S, TagDecl *D, const api_notes::TagInfo &Info,
D->addAttr(
SwiftAttrAttr::Create(S.Context, "release:" + ReleaseOp.value()));
+ if (auto Copyable = Info.isSwiftCopyable()) {
+ if (!*Copyable)
+ D->addAttr(SwiftAttrAttr::Create(S.Context, "~Copyable"));
+ }
+
if (auto Extensibility = Info.EnumExtensibility) {
using api_notes::EnumExtensibilityKind;
bool ShouldAddAttribute = (*Extensibility != EnumExtensibilityKind::None);
diff --git a/clang/test/APINotes/Inputs/Headers/SwiftImportAs.apinotes b/clang/test/APINotes/Inputs/Headers/SwiftImportAs.apinotes
index 5dbb83cab86bd7..b0eead42869a41 100644
--- a/clang/test/APINotes/Inputs/Headers/SwiftImportAs.apinotes
+++ b/clang/test/APINotes/Inputs/Headers/SwiftImportAs.apinotes
@@ -7,3 +7,7 @@ Tags:
SwiftImportAs: reference
SwiftReleaseOp: RCRelease
SwiftRetainOp: RCRetain
+- Name: NonCopyableType
+ SwiftCopyable: false
+- Name: CopyableType
+ SwiftCopyable: true
diff --git a/clang/test/APINotes/Inputs/Headers/SwiftImportAs.h b/clang/test/APINotes/Inputs/Headers/SwiftImportAs.h
index 82b8a6749c4fe2..a8f6d0248eae40 100644
--- a/clang/test/APINotes/Inputs/Headers/SwiftImportAs.h
+++ b/clang/test/APINotes/Inputs/Headers/SwiftImportAs.h
@@ -4,3 +4,6 @@ struct RefCountedType { int value; };
inline void RCRetain(RefCountedType *x) { x->value++; }
inline void RCRelease(RefCountedType *x) { x->value--; }
+
+struct NonCopyableType { int value; };
+struct CopyableType { int value; };
diff --git a/clang/test/APINotes/swift-import-as.cpp b/clang/test/APINotes/swift-import-as.cpp
index 904857e5859303..103cf02f431afb 100644
--- a/clang/test/APINotes/swift-import-as.cpp
+++ b/clang/test/APINotes/swift-import-as.cpp
@@ -2,6 +2,8 @@
// 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++
// 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
// 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
+// 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
+// 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
#include <SwiftImportAs.h>
@@ -14,3 +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-NON-COPYABLE: Dumping NonCopyableType:
+// CHECK-NON-COPYABLE-NEXT: CXXRecordDecl {{.+}} imported in SwiftImportAs {{.+}} struct NonCopyableType
+// CHECK-NON-COPYABLE: SwiftAttrAttr {{.+}} <<invalid sloc>> "~Copyable"
+
+// CHECK-COPYABLE: Dumping CopyableType:
+// CHECK-COPYABLE-NEXT: CXXRecordDecl {{.+}} imported in SwiftImportAs {{.+}} struct CopyableType
+// CHECK-COPYABLE-NOT: SwiftAttrAttr
|
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
f0394cf
to
60191f2
Compare
The test failure on Windows is not related to this PR. |
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