Author: Doug Gregor
Date: 2025-08-15T08:02:14+01:00
New Revision: ae90b60fc8661956250a0b8185be37e8fb92c0d7

URL: 
https://github.com/llvm/llvm-project/commit/ae90b60fc8661956250a0b8185be37e8fb92c0d7
DIFF: 
https://github.com/llvm/llvm-project/commit/ae90b60fc8661956250a0b8185be37e8fb92c0d7.diff

LOG: [APINotes] Add SwiftDestroyOp API note to map to the "destroy" function 
(#153261)

Like retain/release for reference types, "destroy" lets us specify an
operation that is used to deinitialize an instance of a noncopyable
type.

Added: 
    

Modified: 
    clang/docs/APINotes.rst
    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.apinotes
    clang/test/APINotes/Inputs/Headers/SwiftImportAs.h
    clang/test/APINotes/swift-import-as.cpp

Removed: 
    


################################################################################
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 8708b4b092f28..71625715bda19 100644
--- a/clang/include/clang/APINotes/Types.h
+++ b/clang/include/clang/APINotes/Types.h
@@ -751,6 +751,7 @@ class TagInfo : public CommonTypeInfo {
   std::optional<std::string> SwiftImportAs;
   std::optional<std::string> SwiftRetainOp;
   std::optional<std::string> SwiftReleaseOp;
+  std::optional<std::string> SwiftDestroyOp;
   std::optional<std::string> SwiftDefaultOwnership;
 
   std::optional<EnumExtensibilityKind> EnumExtensibility;
@@ -798,6 +799,8 @@ class TagInfo : public CommonTypeInfo {
       SwiftRetainOp = RHS.SwiftRetainOp;
     if (!SwiftReleaseOp)
       SwiftReleaseOp = RHS.SwiftReleaseOp;
+    if (!SwiftDestroyOp)
+      SwiftDestroyOp = RHS.SwiftDestroyOp;
     if (!SwiftDefaultOwnership)
       SwiftDefaultOwnership = RHS.SwiftDefaultOwnership;
 
@@ -826,6 +829,7 @@ inline bool operator==(const TagInfo &LHS, const TagInfo 
&RHS) {
          LHS.SwiftImportAs == RHS.SwiftImportAs &&
          LHS.SwiftRetainOp == RHS.SwiftRetainOp &&
          LHS.SwiftReleaseOp == RHS.SwiftReleaseOp &&
+         LHS.SwiftDestroyOp == RHS.SwiftDestroyOp &&
          LHS.SwiftDefaultOwnership == RHS.SwiftDefaultOwnership &&
          LHS.isFlagEnum() == RHS.isFlagEnum() &&
          LHS.isSwiftCopyable() == RHS.isSwiftCopyable() &&

diff  --git a/clang/lib/APINotes/APINotesFormat.h 
b/clang/lib/APINotes/APINotesFormat.h
index 1ac486a2dd94c..69d180e7b3eb5 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 = 36; // Typedef SwiftConformsTo
+const uint16_t VERSION_MINOR = 37; // 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 8b3812613b166..573356f97ff73 100644
--- a/clang/lib/APINotes/APINotesReader.cpp
+++ b/clang/lib/APINotes/APINotesReader.cpp
@@ -636,6 +636,13 @@ class TagTableInfo
           reinterpret_cast<const char *>(Data), DefaultOwnershipLength - 1);
       Data += DefaultOwnershipLength - 1;
     }
+    unsigned DestroyOpLength =
+        endian::readNext<uint16_t, llvm::endianness::little>(Data);
+    if (DestroyOpLength > 0) {
+      Info.SwiftDestroyOp = std::string(reinterpret_cast<const char *>(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 c013201d677bf..cf88d118d0979 100644
--- a/clang/lib/APINotes/APINotesWriter.cpp
+++ b/clang/lib/APINotes/APINotesWriter.cpp
@@ -1280,6 +1280,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) +
+           2 + (TI.SwiftDestroyOp ? TI.SwiftDestroyOp->size() : 0) +
            2 + (TI.SwiftDefaultOwnership ? TI.SwiftDefaultOwnership->size() : 
0) +
            3 + getCommonTypeInfoSize(TI);
     // clang-format on
@@ -1334,6 +1335,12 @@ class TagTableInfo : public 
CommonTypeTableInfo<TagTableInfo, TagInfo> {
     } else {
       writer.write<uint16_t>(0);
     }
+    if (auto DestroyOp = TI.SwiftDestroyOp) {
+      writer.write<uint16_t>(DestroyOp->size() + 1);
+      OS.write(DestroyOp->c_str(), DestroyOp->size());
+    } else {
+      writer.write<uint16_t>(0);
+    }
 
     emitCommonTypeInfo(OS, TI);
   }

diff  --git a/clang/lib/APINotes/APINotesYAMLCompiler.cpp 
b/clang/lib/APINotes/APINotesYAMLCompiler.cpp
index fcc1d9d54b1c6..a91a1eea03d81 100644
--- a/clang/lib/APINotes/APINotesYAMLCompiler.cpp
+++ b/clang/lib/APINotes/APINotesYAMLCompiler.cpp
@@ -462,6 +462,7 @@ struct Tag {
   std::optional<std::string> SwiftImportAs;
   std::optional<std::string> SwiftRetainOp;
   std::optional<std::string> SwiftReleaseOp;
+  std::optional<std::string> SwiftDestroyOp;
   std::optional<std::string> SwiftDefaultOwnership;
   std::optional<std::string> SwiftConformance;
   std::optional<EnumExtensibilityKind> EnumExtensibility;
@@ -503,6 +504,7 @@ template <> struct MappingTraits<Tag> {
     IO.mapOptional("SwiftImportAs", T.SwiftImportAs);
     IO.mapOptional("SwiftReleaseOp", T.SwiftReleaseOp);
     IO.mapOptional("SwiftRetainOp", T.SwiftRetainOp);
+    IO.mapOptional("SwiftDestroyOp", T.SwiftDestroyOp);
     IO.mapOptional("SwiftDefaultOwnership", T.SwiftDefaultOwnership);
     IO.mapOptional("SwiftConformsTo", T.SwiftConformance);
     IO.mapOptional("EnumExtensibility", T.EnumExtensibility);
@@ -996,6 +998,8 @@ class YAMLConverter {
       TI.SwiftRetainOp = T.SwiftRetainOp;
     if (T.SwiftReleaseOp)
       TI.SwiftReleaseOp = T.SwiftReleaseOp;
+    if (T.SwiftDestroyOp)
+      TI.SwiftDestroyOp = T.SwiftDestroyOp;
     if (T.SwiftDefaultOwnership)
       TI.SwiftDefaultOwnership = T.SwiftDefaultOwnership;
 

diff  --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp
index 272069e5fcc7d..4cc1b76264340 100644
--- a/clang/lib/Sema/SemaAPINotes.cpp
+++ b/clang/lib/Sema/SemaAPINotes.cpp
@@ -698,6 +698,9 @@ static void ProcessAPINotes(Sema &S, TagDecl *D, const 
api_notes::TagInfo &Info,
   if (auto ReleaseOp = Info.SwiftReleaseOp)
     D->addAttr(
         SwiftAttrAttr::Create(S.Context, "release:" + ReleaseOp.value()));
+  if (auto DestroyOp = Info.SwiftDestroyOp)
+    D->addAttr(
+        SwiftAttrAttr::Create(S.Context, "destroy:" + DestroyOp.value()));
   if (auto DefaultOwnership = Info.SwiftDefaultOwnership)
     D->addAttr(SwiftAttrAttr::Create(
         S.Context, "returned_as_" + DefaultOwnership.value() + "_by_default"));

diff  --git a/clang/test/APINotes/Inputs/Headers/SwiftImportAs.apinotes 
b/clang/test/APINotes/Inputs/Headers/SwiftImportAs.apinotes
index cf739ee81c0a9..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__

diff  --git a/clang/test/APINotes/Inputs/Headers/SwiftImportAs.h 
b/clang/test/APINotes/Inputs/Headers/SwiftImportAs.h
index 4ff45c754fe88..978b4fbbb3b00 100644
--- a/clang/test/APINotes/Inputs/Headers/SwiftImportAs.h
+++ b/clang/test/APINotes/Inputs/Headers/SwiftImportAs.h
@@ -31,3 +31,8 @@ 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 64c8c6f202f98..f5d08df7c6a1b 100644
--- a/clang/test/APINotes/swift-import-as.cpp
+++ b/clang/test/APINotes/swift-import-as.cpp
@@ -15,6 +15,7 @@
 // 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 <SwiftImportAs.h>
 
@@ -97,3 +98,8 @@
 // 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"


        
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to