https://github.com/Xazax-hun updated 
https://github.com/llvm/llvm-project/pull/217187

From 021c6ef27a942457773ada57756c859b2a30855b Mon Sep 17 00:00:00 2001
From: Gabor Horvath <[email protected]>
Date: Wed, 19 Aug 2026 02:32:03 +0100
Subject: [PATCH] [clang] Redeclarations should inherit all unique SwiftAttrs

SwiftAttrs are inherited by redeclarations but only the first attribute
was inherited, the rest was dropped. This behavior is confusing and
inconsistent that lead to some subtle bugs consuming these annotations.
This PR makes sure every instance with a unique argument is inherited.
---
 clang/include/clang/Sema/SemaSwift.h          |  3 +
 clang/lib/Sema/SemaDecl.cpp                   |  2 +
 clang/lib/Sema/SemaSwift.cpp                  | 10 +++
 .../test/AST/attr-swift_attr-redeclaration.c  | 70 +++++++++++++++++++
 4 files changed, 85 insertions(+)
 create mode 100644 clang/test/AST/attr-swift_attr-redeclaration.c

diff --git a/clang/include/clang/Sema/SemaSwift.h 
b/clang/include/clang/Sema/SemaSwift.h
index 8d8f1467054ac..2bd2b87d1a36d 100644
--- a/clang/include/clang/Sema/SemaSwift.h
+++ b/clang/include/clang/Sema/SemaSwift.h
@@ -23,6 +23,7 @@ class AttributeCommonInfo;
 class Decl;
 enum class ParameterABI;
 class ParsedAttr;
+class SwiftAttrAttr;
 class SwiftNameAttr;
 
 class SemaSwift : public SemaBase {
@@ -32,6 +33,8 @@ class SemaSwift : public SemaBase {
   SwiftNameAttr *mergeNameAttr(Decl *D, const SwiftNameAttr &SNA,
                                StringRef Name);
 
+  SwiftAttrAttr *mergeAttrAttr(Decl *D, const SwiftAttrAttr &SAA);
+
   void handleAttrAttr(Decl *D, const ParsedAttr &AL);
   void handleAsyncAttr(Decl *D, const ParsedAttr &AL);
   void handleBridge(Decl *D, const ParsedAttr &AL);
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index c62b6fab1b45e..cc1eb5c2eff96 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -2969,6 +2969,8 @@ static bool mergeDeclAttribute(Sema &S, NamedDecl *D,
     NewAttr = S.mergeMinSizeAttr(D, *MA);
   else if (const auto *SNA = dyn_cast<SwiftNameAttr>(Attr))
     NewAttr = S.Swift().mergeNameAttr(D, *SNA, SNA->getName());
+  else if (const auto *SAA = dyn_cast<SwiftAttrAttr>(Attr))
+    NewAttr = S.Swift().mergeAttrAttr(D, *SAA);
   else if (const auto *OA = dyn_cast<OptimizeNoneAttr>(Attr))
     NewAttr = S.mergeOptimizeNoneAttr(D, *OA);
   else if (const auto *InternalLinkageA = dyn_cast<InternalLinkageAttr>(Attr))
diff --git a/clang/lib/Sema/SemaSwift.cpp b/clang/lib/Sema/SemaSwift.cpp
index 687e1c76045b0..51fa6411d83f1 100644
--- a/clang/lib/Sema/SemaSwift.cpp
+++ b/clang/lib/Sema/SemaSwift.cpp
@@ -39,6 +39,16 @@ SwiftNameAttr *SemaSwift::mergeNameAttr(Decl *D, const 
SwiftNameAttr &SNA,
   return ::new (getASTContext()) SwiftNameAttr(getASTContext(), SNA, Name);
 }
 
+SwiftAttrAttr *SemaSwift::mergeAttrAttr(Decl *D, const SwiftAttrAttr &SAA) {
+  // A declaration may carry any number of 'swift_attr's; the string argument
+  // identifies each one, so only an identical one is a duplicate.
+  for (const auto *A : D->specific_attrs<SwiftAttrAttr>())
+    if (A->getAttribute() == SAA.getAttribute())
+      return nullptr;
+  return ::new (getASTContext())
+      SwiftAttrAttr(getASTContext(), SAA, SAA.getAttribute());
+}
+
 /// Pointer-like types in the default address space.
 static bool isValidSwiftContextType(QualType Ty) {
   if (!Ty->hasPointerRepresentation())
diff --git a/clang/test/AST/attr-swift_attr-redeclaration.c 
b/clang/test/AST/attr-swift_attr-redeclaration.c
new file mode 100644
index 0000000000000..cc8ddfecf8c07
--- /dev/null
+++ b/clang/test/AST/attr-swift_attr-redeclaration.c
@@ -0,0 +1,70 @@
+// RUN: %clang_cc1 -ast-dump -ast-dump-filter=two_on_first %s \
+// RUN:   | FileCheck --check-prefix=TWO --implicit-check-not=SwiftAttrAttr %s
+// RUN: %clang_cc1 -ast-dump -ast-dump-filter=split %s \
+// RUN:   | FileCheck --check-prefix=SPLIT --implicit-check-not=SwiftAttrAttr 
%s
+// RUN: %clang_cc1 -ast-dump -ast-dump-filter=dedup %s \
+// RUN:   | FileCheck --check-prefix=DEDUP --implicit-check-not=SwiftAttrAttr 
%s
+// RUN: %clang_cc1 -ast-dump -ast-dump-filter=overlap %s \
+// RUN:   | FileCheck --check-prefix=OVERLAP 
--implicit-check-not=SwiftAttrAttr %s
+
+// 'swift_attr' is a bag of independent annotations identified by its string
+// argument, so a redeclaration inherits all of them, not just the first.
+
+__attribute__((swift_attr("@a"))) __attribute__((swift_attr("@b")))
+void two_on_first(void);
+void two_on_first(void);
+
+// TWO:      FunctionDecl {{.*}} two_on_first
+// TWO-NEXT:   SwiftAttrAttr {{.*}} "@a"
+// TWO-NEXT:   SwiftAttrAttr {{.*}} "@b"
+// TWO:      FunctionDecl {{.*}} prev {{.*}} two_on_first
+// TWO-NEXT:   SwiftAttrAttr {{.*}} Inherited "@a"
+// TWO-NEXT:   SwiftAttrAttr {{.*}} Inherited "@b"
+
+// A 'swift_attr' on the redeclaration must not suppress inheriting the
+// different ones from the previous declaration.
+__attribute__((swift_attr("@x1"))) __attribute__((swift_attr("@x2")))
+void split(void);
+__attribute__((swift_attr("@y1"))) __attribute__((swift_attr("@y2")))
+void split(void);
+
+// SPLIT:      FunctionDecl {{.*}} split
+// SPLIT-NEXT:   SwiftAttrAttr {{.*}} "@x1"
+// SPLIT-NEXT:   SwiftAttrAttr {{.*}} "@x2"
+// SPLIT:      FunctionDecl {{.*}} prev {{.*}} split
+// SPLIT-NEXT:   SwiftAttrAttr {{.*}} Inherited "@x1"
+// SPLIT-NEXT:   SwiftAttrAttr {{.*}} Inherited "@x2"
+// SPLIT-NEXT:   SwiftAttrAttr {{.*}} "@y1"
+// SPLIT-NEXT:   SwiftAttrAttr {{.*}} "@y2"
+
+// Identical annotations are still deduplicated, so a chain of redeclarations
+// does not accumulate copies.
+__attribute__((swift_attr("@same1"))) __attribute__((swift_attr("@same2")))
+void dedup(void);
+__attribute__((swift_attr("@same1"))) __attribute__((swift_attr("@same2")))
+void dedup(void);
+void dedup(void);
+
+// DEDUP:      FunctionDecl {{.*}} dedup
+// DEDUP-NEXT:   SwiftAttrAttr {{.*}} "@same1"
+// DEDUP-NEXT:   SwiftAttrAttr {{.*}} "@same2"
+// DEDUP:      FunctionDecl {{.*}} prev {{.*}} dedup
+// DEDUP-NEXT:   SwiftAttrAttr {{.*}} "@same1"
+// DEDUP-NEXT:   SwiftAttrAttr {{.*}} "@same2"
+// DEDUP:      FunctionDecl {{.*}} prev {{.*}} dedup
+// DEDUP-NEXT:   SwiftAttrAttr {{.*}} Inherited "@same1"
+// DEDUP-NEXT:   SwiftAttrAttr {{.*}} Inherited "@same2"
+
+// Partially overlapping sets are merged into their union.
+__attribute__((swift_attr("@p"))) __attribute__((swift_attr("@q")))
+void overlap(void);
+__attribute__((swift_attr("@q"))) __attribute__((swift_attr("@r")))
+void overlap(void);
+
+// OVERLAP:      FunctionDecl {{.*}} overlap
+// OVERLAP-NEXT:   SwiftAttrAttr {{.*}} "@p"
+// OVERLAP-NEXT:   SwiftAttrAttr {{.*}} "@q"
+// OVERLAP:      FunctionDecl {{.*}} prev {{.*}} overlap
+// OVERLAP-NEXT:   SwiftAttrAttr {{.*}} Inherited "@p"
+// OVERLAP-NEXT:   SwiftAttrAttr {{.*}} "@q"
+// OVERLAP-NEXT:   SwiftAttrAttr {{.*}} "@r"

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to