[clang] 06a26f2 - [clang][APINotes] Apply API notes 'swift_attr's idempotently (#222526)
via cfe-commits
cfe-commits at lists.llvm.org
Thu Sep 10 22:46:29 PDT 2026
Author: John Hui
Date: 2026-09-11T06:46:24+01:00
New Revision: 06a26f2ff3a2c5464a5dca8cf42314bae5e635cc
URL: https://github.com/llvm/llvm-project/commit/06a26f2ff3a2c5464a5dca8cf42314bae5e635cc
DIFF: https://github.com/llvm/llvm-project/commit/06a26f2ff3a2c5464a5dca8cf42314bae5e635cc.diff
LOG: [clang][APINotes] Apply API notes 'swift_attr's idempotently (#222526)
Added:
clang/test/APINotes/swift-attr-redeclaration.cpp
Modified:
clang/lib/Sema/SemaAPINotes.cpp
clang/test/APINotes/swift-import-as.cpp
Removed:
################################################################################
diff --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp
index 78153d9ddf39d..4c7e5ea16cfd7 100644
--- a/clang/lib/Sema/SemaAPINotes.cpp
+++ b/clang/lib/Sema/SemaAPINotes.cpp
@@ -247,6 +247,15 @@ static void handleAPINotedRetainCountConvention(
}
}
+/// Add a 'swift_attr' unless \p D already carries that exact annotation.
+static void addSwiftAttrIfAbsent(Sema &S, Decl *D, StringRef Attribute) {
+ for (const auto *A : D->specific_attrs<SwiftAttrAttr>())
+ if (A->getAttribute() == Attribute)
+ return;
+
+ D->addAttr(SwiftAttrAttr::Create(S.Context, Attribute));
+}
+
static void ProcessAPINotes(Sema &S, Decl *D,
const api_notes::CommonEntityInfo &Info,
VersionedInfoMetadata Metadata) {
@@ -362,8 +371,7 @@ static void ProcessAPINotes(Sema &S, Decl *D,
}
if (auto ConformsTo = Info.getSwiftConformance())
- D->addAttr(
- SwiftAttrAttr::Create(S.Context, "conforms_to:" + ConformsTo.value()));
+ addSwiftAttrIfAbsent(S, D, "conforms_to:" + ConformsTo.value());
ProcessAPINotes(S, D, static_cast<const api_notes::CommonEntityInfo &>(Info),
Metadata);
@@ -607,8 +615,7 @@ static void ProcessAPINotes(Sema &S, FunctionOrMethod AnyFunc,
// returns_(un)retained
if (!Info.SwiftReturnOwnership.empty())
- D->addAttr(SwiftAttrAttr::Create(S.Context,
- "returns_" + Info.SwiftReturnOwnership));
+ addSwiftAttrIfAbsent(S, D, "returns_" + Info.SwiftReturnOwnership);
// Result type override.
QualType OverriddenResultType;
@@ -725,32 +732,22 @@ static void ProcessAPINotes(Sema &S, ObjCMethodDecl *D,
static_cast<const api_notes::FunctionInfo &>(Info), Metadata);
}
-static void addSwiftAttrIfAbsent(Sema &S, Decl *D, StringRef Attribute) {
- for (const auto *A : D->specific_attrs<SwiftAttrAttr>())
- if (A->getAttribute() == Attribute)
- return;
-
- D->addAttr(SwiftAttrAttr::Create(S.Context, Attribute));
-}
-
/// Process API notes for a tag.
static void ProcessAPINotes(Sema &S, TagDecl *D, const api_notes::TagInfo &Info,
VersionedInfoMetadata Metadata) {
if (auto ImportAs = Info.SwiftImportAs)
- D->addAttr(SwiftAttrAttr::Create(S.Context, "import_" + ImportAs.value()));
+ addSwiftAttrIfAbsent(S, D, "import_" + ImportAs.value());
if (auto RetainOp = Info.SwiftRetainOp)
- D->addAttr(SwiftAttrAttr::Create(S.Context, "retain:" + RetainOp.value()));
+ addSwiftAttrIfAbsent(S, D, "retain:" + RetainOp.value());
if (auto ReleaseOp = Info.SwiftReleaseOp)
- D->addAttr(
- SwiftAttrAttr::Create(S.Context, "release:" + ReleaseOp.value()));
+ addSwiftAttrIfAbsent(S, D, "release:" + ReleaseOp.value());
if (auto DestroyOp = Info.SwiftDestroyOp)
- D->addAttr(
- SwiftAttrAttr::Create(S.Context, "destroy:" + DestroyOp.value()));
+ addSwiftAttrIfAbsent(S, D, "destroy:" + DestroyOp.value());
if (auto DefaultOwnership = Info.SwiftDefaultOwnership)
- D->addAttr(SwiftAttrAttr::Create(
- S.Context, "returned_as_" + DefaultOwnership.value() + "_by_default"));
+ addSwiftAttrIfAbsent(
+ S, D, "returned_as_" + DefaultOwnership.value() + "_by_default");
if (auto Copyable = Info.isSwiftCopyable()) {
if (!*Copyable)
diff --git a/clang/test/APINotes/swift-attr-redeclaration.cpp b/clang/test/APINotes/swift-attr-redeclaration.cpp
new file mode 100644
index 0000000000000..f2e9e58f61bd6
--- /dev/null
+++ b/clang/test/APINotes/swift-attr-redeclaration.cpp
@@ -0,0 +1,46 @@
+// An API-noted tag that is forward-declared before its definition must not
+// accumulate duplicate 'swift_attr's on the definition.
+// RUN: rm -rf %t && split-file %s %t
+//
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/cache \
+// RUN: -fapinotes-modules -I %t/Inputs -x c++ %t/test.cpp \
+// RUN: -ast-dump -ast-dump-filter FwdThenDefined \
+// RUN: | FileCheck --check-prefix=FWD --implicit-check-not=SwiftAttrAttr %s
+
+// The forward declaration carries one copy of each annotation.
+//
+// FWD: Dumping FwdThenDefined:
+// FWD: CXXRecordDecl {{.*}} struct FwdThenDefined
+// FWD-NEXT: SwiftAttrAttr {{.*}} "import_reference"
+// FWD-NEXT: SwiftAttrAttr {{.*}} "retain:FTDRetain"
+// FWD-NEXT: SwiftAttrAttr {{.*}} "release:FTDRelease"
+
+// So must the definition: the copies it inherits and the copies API notes
+// applies to it are the same three annotations.
+//
+// FWD: Dumping FwdThenDefined:
+// FWD: CXXRecordDecl {{.*}} prev {{.*}} struct FwdThenDefined definition
+// FWD: SwiftAttrAttr {{.*}} "import_reference"
+// FWD-NEXT: SwiftAttrAttr {{.*}} "retain:FTDRetain"
+// FWD-NEXT: SwiftAttrAttr {{.*}} "release:FTDRelease"
+
+//--- Inputs/module.modulemap
+module Redecl {
+ header "Redecl.h"
+}
+
+//--- Inputs/Redecl.apinotes
+---
+Name: Redecl
+Tags:
+- Name: FwdThenDefined
+ SwiftImportAs: reference
+ SwiftRetainOp: FTDRetain
+ SwiftReleaseOp: FTDRelease
+
+//--- Inputs/Redecl.h
+struct FwdThenDefined;
+struct FwdThenDefined {};
+
+//--- test.cpp
+#include "Redecl.h"
diff --git a/clang/test/APINotes/swift-import-as.cpp b/clang/test/APINotes/swift-import-as.cpp
index 1ff09900350e2..212d5a7b4c8e5 100644
--- a/clang/test/APINotes/swift-import-as.cpp
+++ b/clang/test/APINotes/swift-import-as.cpp
@@ -46,15 +46,17 @@
// CHECK-OPAQUE-REF-COUNTED: SwiftAttrAttr {{.+}} <<invalid sloc>> "import_reference"
// CHECK-OPAQUE-REF-COUNTED: SwiftAttrAttr {{.+}} <<invalid sloc>> "retain:ORCRetain"
// CHECK-OPAQUE-REF-COUNTED: SwiftAttrAttr {{.+}} <<invalid sloc>> "release:ORCRelease"
-// CHECK-OPAQUE-REF-COUNTED-NOT: SwiftAttrAttr {{.+}} <<invalid sloc>> "release:ORCRelease"
+// CHECK-OPAQUE-REF-COUNTED-NOT: SwiftAttrAttr {{.*}}"release:ORCRelease"
+// The redeclaration inherits the annotations rather than having API notes
+// applied a second time, so it carries one copy of each, marked Inherited.
// CHECK-OPAQUE-REF-COUNTED: Dumping OpaqueRefCountedType:
// CHECK-OPAQUE-REF-COUNTED-NEXT: CXXRecordDecl {{.+}} imported in SwiftImportAs{{.*}}struct OpaqueRefCountedType
-// CHECK-OPAQUE-REF-COUNTED: SwiftAttrAttr {{.+}} <<invalid sloc>> "import_reference"
-// CHECK-OPAQUE-REF-COUNTED: SwiftAttrAttr {{.+}} <<invalid sloc>> "retain:ORCRetain"
-// CHECK-OPAQUE-REF-COUNTED: SwiftAttrAttr {{.+}} <<invalid sloc>> "release:ORCRelease"
+// CHECK-OPAQUE-REF-COUNTED: SwiftAttrAttr {{.+}} <<invalid sloc>> Inherited "import_reference"
+// CHECK-OPAQUE-REF-COUNTED: SwiftAttrAttr {{.+}} <<invalid sloc>> Inherited "retain:ORCRetain"
+// CHECK-OPAQUE-REF-COUNTED: SwiftAttrAttr {{.+}} <<invalid sloc>> Inherited "release:ORCRelease"
-// CHECK-OPAQUE-REF-COUNTED-NOT: SwiftAttrAttr {{.+}} <<invalid sloc>> "release:
+// CHECK-OPAQUE-REF-COUNTED-NOT: SwiftAttrAttr {{.*}}"release:
// CHECK-NON-COPYABLE: Dumping NonCopyableType:
// CHECK-NON-COPYABLE-NEXT: CXXRecordDecl {{.+}} imported in SwiftImportAs {{.+}} struct NonCopyableType
// CHECK-NON-COPYABLE: SwiftAttrAttr {{.+}} <<invalid sloc>> "~Copyable"
More information about the cfe-commits
mailing list