diff --git a/clang/docs/APINotes.rst b/clang/docs/APINotes.rst index e5ec154237b0d..dec4b186ff72f 100644 --- a/clang/docs/APINotes.rst +++ b/clang/docs/APINotes.rst @@ -206,6 +206,17 @@ declaration kind), all of which are optional: - Name: tzdb SwiftCopyable: false + A non-copyable type can have a "destroy" operation, specified with + `SwiftDestroyOp`, which will be invoked on the instance when it is no + longer in use to free up resources. + + :: + + Tags: + - Name: WGPUAdapterInfo + SwiftCopyable: false + SwiftDestroyOp: wgpuAdapterInfoFreeMembers + :SwiftConformsTo: Allows annotating a C++ class as conforming to a Swift protocol. Equivalent diff --git a/clang/include/clang/APINotes/Types.h b/clang/include/clang/APINotes/Types.h index 925fd019ddf8c..44ff7bf23f096 100644 --- a/clang/include/clang/APINotes/Types.h +++ b/clang/include/clang/APINotes/Types.h @@ -838,6 +838,7 @@ class TagInfo : public CommonTypeInfo { std::optional SwiftRetainOp; std::optional SwiftReleaseOp; std::optional SwiftDefaultOwnership; + std::optional SwiftDestroyOp; std::optional EnumExtensibility; @@ -886,6 +887,8 @@ class TagInfo : public CommonTypeInfo { SwiftReleaseOp = RHS.SwiftReleaseOp; if (!SwiftDefaultOwnership) SwiftDefaultOwnership = RHS.SwiftDefaultOwnership; + if (!SwiftDestroyOp) + SwiftDestroyOp = RHS.SwiftDestroyOp; if (!HasFlagEnum) setFlagEnum(RHS.isFlagEnum()); @@ -913,6 +916,7 @@ inline bool operator==(const TagInfo &LHS, const TagInfo &RHS) { LHS.SwiftRetainOp == RHS.SwiftRetainOp && LHS.SwiftReleaseOp == RHS.SwiftReleaseOp && LHS.SwiftDefaultOwnership == RHS.SwiftDefaultOwnership && + LHS.SwiftDestroyOp == RHS.SwiftDestroyOp && LHS.isFlagEnum() == RHS.isFlagEnum() && LHS.isSwiftCopyable() == RHS.isSwiftCopyable() && LHS.isSwiftEscapable() == RHS.isSwiftEscapable() && diff --git a/clang/lib/APINotes/APINotesFormat.h b/clang/lib/APINotes/APINotesFormat.h index e93237e91ca99..d21eb1beb9a4d 100644 --- a/clang/lib/APINotes/APINotesFormat.h +++ b/clang/lib/APINotes/APINotesFormat.h @@ -24,7 +24,7 @@ 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 = 37; // Typedef SwiftConformsTo +const uint16_t VERSION_MINOR = 38; // SwiftDestroyOp const uint8_t kSwiftConforms = 1; const uint8_t kSwiftDoesNotConform = 2; diff --git a/clang/lib/APINotes/APINotesReader.cpp b/clang/lib/APINotes/APINotesReader.cpp index 7bce99656ba9c..536a16ee3ad6b 100644 --- a/clang/lib/APINotes/APINotesReader.cpp +++ b/clang/lib/APINotes/APINotesReader.cpp @@ -675,6 +675,13 @@ class TagTableInfo reinterpret_cast(Data), DefaultOwnershipLength - 1); Data += DefaultOwnershipLength - 1; } + unsigned DestroyOpLength = + endian::readNext(Data); + if (DestroyOpLength > 0) { + Info.SwiftDestroyOp = std::string(reinterpret_cast(Data), + DestroyOpLength - 1); + Data += DestroyOpLength - 1; + } ReadCommonTypeInfo(Data, Info); return Info; diff --git a/clang/lib/APINotes/APINotesWriter.cpp b/clang/lib/APINotes/APINotesWriter.cpp index 44426030d771e..a2b3d7cca1b0e 100644 --- a/clang/lib/APINotes/APINotesWriter.cpp +++ b/clang/lib/APINotes/APINotesWriter.cpp @@ -1338,6 +1338,7 @@ class TagTableInfo : public CommonTypeTableInfo { 2 + (TI.SwiftRetainOp ? TI.SwiftRetainOp->size() : 0) + 2 + (TI.SwiftReleaseOp ? TI.SwiftReleaseOp->size() : 0) + 2 + (TI.SwiftDefaultOwnership ? TI.SwiftDefaultOwnership->size() : 0) + + 2 + (TI.SwiftDestroyOp ? TI.SwiftDestroyOp->size() : 0) + 3 + getCommonTypeInfoSize(TI); // clang-format on } @@ -1391,6 +1392,12 @@ class TagTableInfo : public CommonTypeTableInfo { } else { writer.write(0); } + if (auto DestroyOp = TI.SwiftDestroyOp) { + writer.write(DestroyOp->size() + 1); + OS.write(DestroyOp->c_str(), DestroyOp->size()); + } else { + writer.write(0); + } emitCommonTypeInfo(OS, TI); } diff --git a/clang/lib/APINotes/APINotesYAMLCompiler.cpp b/clang/lib/APINotes/APINotesYAMLCompiler.cpp index 925c75d5028a7..3620bad6dfcd6 100644 --- a/clang/lib/APINotes/APINotesYAMLCompiler.cpp +++ b/clang/lib/APINotes/APINotesYAMLCompiler.cpp @@ -631,6 +631,7 @@ struct Tag { std::optional SwiftRetainOp; std::optional SwiftReleaseOp; std::optional SwiftDefaultOwnership; + std::optional SwiftDestroyOp; std::optional SwiftConformance; std::optional EnumExtensibility; std::optional FlagEnum; @@ -672,6 +673,7 @@ template <> struct MappingTraits { IO.mapOptional("SwiftReleaseOp", T.SwiftReleaseOp); IO.mapOptional("SwiftRetainOp", T.SwiftRetainOp); IO.mapOptional("SwiftDefaultOwnership", T.SwiftDefaultOwnership); + IO.mapOptional("SwiftDestroyOp", T.SwiftDestroyOp); IO.mapOptional("SwiftConformsTo", T.SwiftConformance); IO.mapOptional("EnumExtensibility", T.EnumExtensibility); IO.mapOptional("FlagEnum", T.FlagEnum); @@ -1184,6 +1186,8 @@ class YAMLConverter { TI.SwiftReleaseOp = T.SwiftReleaseOp; if (T.SwiftDefaultOwnership) TI.SwiftDefaultOwnership = T.SwiftDefaultOwnership; + if (T.SwiftDestroyOp) + TI.SwiftDestroyOp = T.SwiftDestroyOp; if (T.SwiftCopyable) TI.setSwiftCopyable(T.SwiftCopyable); diff --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp index e1029a4bae836..5007d66f276dd 100644 --- a/clang/lib/Sema/SemaAPINotes.cpp +++ b/clang/lib/Sema/SemaAPINotes.cpp @@ -780,6 +780,9 @@ static void ProcessAPINotes(Sema &S, TagDecl *D, const api_notes::TagInfo &Info, if (auto DefaultOwnership = Info.SwiftDefaultOwnership) D->addAttr(SwiftAttrAttr::Create( S.Context, "returned_as_" + DefaultOwnership.value() + "_by_default")); + if (auto DestroyOp = Info.SwiftDestroyOp) + D->addAttr( + SwiftAttrAttr::Create(S.Context, "destroy:" + DestroyOp.value())); if (auto Copyable = Info.isSwiftCopyable()) { if (!*Copyable) diff --git a/clang/test/APINotes/Inputs/Headers/SwiftImportAs.apinotes b/clang/test/APINotes/Inputs/Headers/SwiftImportAs.apinotes index c096822fb92b5..15c806842d08f 100644 --- a/clang/test/APINotes/Inputs/Headers/SwiftImportAs.apinotes +++ b/clang/test/APINotes/Inputs/Headers/SwiftImportAs.apinotes @@ -32,6 +32,9 @@ Tags: SwiftEscapable: false - Name: EscapableType SwiftEscapable: true +- Name: NoncopyableWithDestroyType + SwiftCopyable: false + SwiftDestroyOp: NCDDestroy Functions: - Name: functionReturningFrt__ @@ -39,3 +42,7 @@ Functions: SwiftReturnOwnership: unretained - Name: functionReturningFrt_returns_retained SwiftReturnOwnership: retained +Typedefs: + - Name: WrappedOptions + SwiftWrapper: struct + SwiftConformsTo: Swift.OptionSet diff --git a/clang/test/APINotes/Inputs/Headers/SwiftImportAs.h b/clang/test/APINotes/Inputs/Headers/SwiftImportAs.h index 5f817ac89bdd3..978b4fbbb3b00 100644 --- a/clang/test/APINotes/Inputs/Headers/SwiftImportAs.h +++ b/clang/test/APINotes/Inputs/Headers/SwiftImportAs.h @@ -29,3 +29,10 @@ struct OpaqueRefCountedType; // redeclaration inline void ORCRetain(struct OpaqueRefCountedType *x); inline void ORCRelease(struct OpaqueRefCountedType *x); + +typedef unsigned WrappedOptions; + +struct NoncopyableWithDestroyType { +}; + +void NCDDestroy(NoncopyableWithDestroyType instance); diff --git a/clang/test/APINotes/swift-import-as.cpp b/clang/test/APINotes/swift-import-as.cpp index 747aa29e95761..f5d08df7c6a1b 100644 --- a/clang/test/APINotes/swift-import-as.cpp +++ b/clang/test/APINotes/swift-import-as.cpp @@ -14,6 +14,8 @@ // RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers %s -x c++ -ast-dump -ast-dump-filter methodReturningFrt__ | FileCheck -check-prefix=CHECK-METHOD-RETURNING-FRT %s // RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers %s -x c++ -ast-dump -ast-dump-filter methodReturningFrt_returns_unretained | FileCheck -check-prefix=CHECK-METHOD-RETURNING-FRT-UNRETAINED %s // RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers %s -x c++ -ast-dump -ast-dump-filter methodReturningFrt_returns_retained | FileCheck -check-prefix=CHECK-METHOD-RETURNING-FRT-RETAINED %s +// RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers %s -x c++ -ast-dump -ast-dump-filter WrappedOptions | FileCheck -check-prefix=CHECK-WRAPPED-OPTIONS %s +// RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers %s -x c++ -ast-dump -ast-dump-filter NoncopyableWithDestroyType | FileCheck -check-prefix=CHECK-NONCOPYABLE-WITH-DESTROY %s #include @@ -91,3 +93,13 @@ // CHECK-METHOD-RETURNING-FRT-RETAINED: Dumping ImmortalRefType::methodReturningFrt_returns_retained: // CHECK-METHOD-RETURNING-FRT-RETAINED: CXXMethodDecl {{.+}} imported in SwiftImportAs methodReturningFrt_returns_retained 'ImmortalRefType *()' // CHECK-METHOD-RETURNING-FRT-RETAINED: `-SwiftAttrAttr {{.+}} "returns_retained" + +// CHECK-WRAPPED-OPTIONS: Dumping WrappedOptions +// CHECK-WRAPPED-OPTIONS: TypedefDecl{{.*}}WrappedOptions 'unsigned int' +// CHECK-WRAPPED-OPTIONS: SwiftNewTypeAttr {{.*}} swift_wrapper NK_Struct +// CHECK-WRAPPED-OPTIONS: SwiftAttrAttr {{.*}} "conforms_to:Swift.OptionSet" + +// CHECK-NONCOPYABLE-WITH-DESTROY: Dumping NoncopyableWithDestroyType +// CHECK-NONCOPYABLE-WITH-DESTROY: RecordDecl {{.*}}struct NoncopyableWithDestroyType +// CHECK-NONCOPYABLE-WITH-DESTROY: SwiftAttrAttr {{.+}} "destroy:NCDDestroy" +// CHECK-NONCOPYABLE-WITH-DESTROY: SwiftAttrAttr {{.+}} "~Copyable"