Skip to content

🍒[APINotes] Allow annotating a C++ type as non-copyable in Swift #8671

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

Merged
merged 1 commit into from
May 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion clang/include/clang/APINotes/Types.h
Original file line number Diff line number Diff line change
Expand Up @@ -678,14 +678,21 @@ 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;
std::optional<std::string> SwiftReleaseOp;

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)
Expand All @@ -697,6 +704,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;

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

if (!SwiftCopyableSpecified)
setSwiftCopyable(RHS.isSwiftCopyable());

return *this;
}

Expand All @@ -727,6 +746,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;
}

Expand Down
5 changes: 4 additions & 1 deletion clang/lib/APINotes/APINotesFormat.h
Original file line number Diff line number Diff line change
Expand Up @@ -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>;
Expand Down
7 changes: 7 additions & 0 deletions clang/lib/APINotes/APINotesReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -552,6 +552,13 @@ class TagTableInfo
Info.EnumExtensibility =
static_cast<EnumExtensibilityKind>((Payload & 0x3) - 1);

uint8_t Copyable =
endian::readNext<uint8_t, llvm::endianness::little, unaligned>(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, unaligned>(Data);
if (ImportAsLength > 0) {
Expand Down
7 changes: 6 additions & 1 deletion clang/lib/APINotes/APINotesWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1135,7 +1135,7 @@ class TagTableInfo : public CommonTypeTableInfo<TagTableInfo, TagInfo> {
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);
2 + getCommonTypeInfoSize(TI);
}

void emitUnversionedInfo(raw_ostream &OS, const TagInfo &TI) {
Expand All @@ -1153,6 +1153,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());
Expand Down
5 changes: 5 additions & 0 deletions clang/lib/APINotes/APINotesYAMLCompiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -532,6 +532,7 @@ struct Tag {
std::optional<EnumExtensibilityKind> EnumExtensibility;
std::optional<bool> FlagEnum;
std::optional<EnumConvenienceAliasKind> EnumConvenienceKind;
std::optional<bool> SwiftCopyable;
};

typedef std::vector<Tag> TagsSeq;
Expand Down Expand Up @@ -565,6 +566,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
Expand Down Expand Up @@ -1122,6 +1124,9 @@ class YAMLConverter {
if (Tag.SwiftReleaseOp)
TI.SwiftReleaseOp = Tag.SwiftReleaseOp;

if (Tag.SwiftCopyable)
TI.setSwiftCopyable(Tag.SwiftCopyable);

if (Tag.EnumConvenienceKind) {
if (Tag.EnumExtensibility) {
emitError(
Expand Down
5 changes: 5 additions & 0 deletions clang/lib/Sema/SemaAPINotes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -592,6 +592,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);
Expand Down
4 changes: 4 additions & 0 deletions clang/test/APINotes/Inputs/Headers/SwiftImportAs.apinotes
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,7 @@ Tags:
SwiftImportAs: reference
SwiftReleaseOp: RCRelease
SwiftRetainOp: RCRetain
- Name: NonCopyableType
SwiftCopyable: false
- Name: CopyableType
SwiftCopyable: true
3 changes: 3 additions & 0 deletions clang/test/APINotes/Inputs/Headers/SwiftImportAs.h
Original file line number Diff line number Diff line change
Expand Up @@ -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; };
10 changes: 10 additions & 0 deletions clang/test/APINotes/swift-import-as.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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>

Expand All @@ -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