[clang] [clang][AST] Hash `AttributedType`'s `Attr` by Content (PR #200961)
Qiongsi Wu via cfe-commits
cfe-commits at lists.llvm.org
Tue Jun 2 10:52:39 PDT 2026
https://github.com/qiongsiwu updated https://github.com/llvm/llvm-project/pull/200961
>From b2ffa916c4b4f43183fb90a47548f08e2986882b Mon Sep 17 00:00:00 2001
From: Qiongsi Wu <qiongsi_wu at apple.com>
Date: Mon, 1 Jun 2026 14:54:21 -0700
Subject: [PATCH 1/2] Fixing attributed type deduplication.
---
clang/include/clang/AST/Attr.h | 12 ++++++++++++
clang/include/clang/AST/TypeBase.h | 8 +-------
clang/include/clang/Basic/Attr.td | 5 +++++
clang/lib/AST/Type.cpp | 11 +++++++++++
.../AST/attributed-type-dedup-nullability.m | 18 ++++++++++++++++++
.../AST/attributed-type-dedup-swift-attr.m | 15 +++++++++++++++
6 files changed, 62 insertions(+), 7 deletions(-)
create mode 100644 clang/test/AST/attributed-type-dedup-nullability.m
create mode 100644 clang/test/AST/attributed-type-dedup-swift-attr.m
diff --git a/clang/include/clang/AST/Attr.h b/clang/include/clang/AST/Attr.h
index 0f9fc01391c30..df2da85cf6402 100644
--- a/clang/include/clang/AST/Attr.h
+++ b/clang/include/clang/AST/Attr.h
@@ -81,6 +81,8 @@ class Attr : public AttributeCommonInfo {
return ::operator delete(Ptr, C, Alignment);
}
+ virtual ~Attr() = default;
+
protected:
Attr(ASTContext &Context, const AttributeCommonInfo &CommonInfo,
attr::Kind AK, bool IsLateParsed)
@@ -120,6 +122,16 @@ class Attr : public AttributeCommonInfo {
void printPretty(raw_ostream &OS, const PrintingPolicy &Policy) const;
static StringRef getDocumentation(attr::Kind);
+
+ // Contribute Attr-specific content to a FoldingSetNodeID for callers like
+ // AttributedType::Profile that dedupe nodes by Attr identity. The default
+ // is empty: AttributedType::Profile already hashes attrKind (and
+ // asserts attr->getKind() == attrKind), so a no-op default makes stateless
+ // Attrs (nullability, ARC ownership, calling conventions, ...) dedupe by
+ // (kind, modified, equivalent) — pointer identity of the Attr is ignored.
+ // Override this in subclasses whose arguments meaningfully distinguish
+ // the wrapped type (e.g. SwiftAttrAttr hashes its string payload).
+ virtual void Profile(llvm::FoldingSetNodeID &ID) const {}
};
class TypeAttr : public Attr {
diff --git a/clang/include/clang/AST/TypeBase.h b/clang/include/clang/AST/TypeBase.h
index c64eee11fd91e..cce7af88181cd 100644
--- a/clang/include/clang/AST/TypeBase.h
+++ b/clang/include/clang/AST/TypeBase.h
@@ -6746,13 +6746,7 @@ class AttributedType : public Type, public llvm::FoldingSetNode {
}
static void Profile(llvm::FoldingSetNodeID &ID, Kind attrKind,
- QualType modified, QualType equivalent,
- const Attr *attr) {
- ID.AddInteger(attrKind);
- ID.AddPointer(modified.getAsOpaquePtr());
- ID.AddPointer(equivalent.getAsOpaquePtr());
- ID.AddPointer(attr);
- }
+ QualType modified, QualType equivalent, const Attr *attr);
static bool classof(const Type *T) {
return T->getTypeClass() == Attributed;
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 32f72e8da960e..533f13d2ca879 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -3196,6 +3196,11 @@ def SwiftAttr : DeclOrTypeAttr {
let Args = [StringArgument<"Attribute">];
let Documentation = [SwiftAttrDocs];
let PragmaAttributeSupport = 1;
+ let AdditionalMembers = [{
+ void Profile(llvm::FoldingSetNodeID &ID) const override {
+ ID.AddString(getAttribute());
+ }
+ }];
}
def SwiftBridge : InheritableAttr {
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index 96a398aa21dad..711b216d81de7 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -5338,6 +5338,17 @@ NullabilityKindOrNone AttributedType::stripOuterNullability(QualType &T) {
return std::nullopt;
}
+void AttributedType::Profile(llvm::FoldingSetNodeID &ID, Kind attrKind,
+ QualType modified, QualType equivalent,
+ const Attr *attr) {
+
+ ID.AddInteger(attrKind);
+ ID.AddPointer(modified.getAsOpaquePtr());
+ ID.AddPointer(equivalent.getAsOpaquePtr());
+ if (attr)
+ attr->Profile(ID);
+}
+
bool Type::isSignableIntegerType(const ASTContext &Ctx) const {
if (!isIntegralType(Ctx) || isEnumeralType())
return false;
diff --git a/clang/test/AST/attributed-type-dedup-nullability.m b/clang/test/AST/attributed-type-dedup-nullability.m
new file mode 100644
index 0000000000000..9904ec00913d0
--- /dev/null
+++ b/clang/test/AST/attributed-type-dedup-nullability.m
@@ -0,0 +1,18 @@
+// RUN: %clang_cc1 -fobjc-arc -emit-pch -o %t.pch %s
+// RUN: llvm-bcanalyzer --dump --disable-histogram %t.pch | FileCheck %s
+
+// All three non-null types below should share a single AttributedType node and
+// serialize as exactly one TYPE_ATTRIBUTED record.
+
+ at class NSString;
+
+#pragma clang assume_nonnull begin
+ at interface T
+- (NSString *)a;
+- (NSString *)b;
+- (NSString *)c;
+ at end
+#pragma clang assume_nonnull end
+
+// CHECK-COUNT-1: <TYPE_ATTRIBUTED
+// CHECK-NOT: <TYPE_ATTRIBUTED
diff --git a/clang/test/AST/attributed-type-dedup-swift-attr.m b/clang/test/AST/attributed-type-dedup-swift-attr.m
new file mode 100644
index 0000000000000..d39778572eda5
--- /dev/null
+++ b/clang/test/AST/attributed-type-dedup-swift-attr.m
@@ -0,0 +1,15 @@
+// RUN: %clang_cc1 -fobjc-arc -emit-pch -o %t.pch %s
+// RUN: llvm-bcanalyzer --dump --disable-histogram %t.pch | FileCheck %s
+
+// Checks that swift_attr deduplication works correctly.
+// The following test should generate two attributed types, not three.
+
+#define ATTR_A __attribute__((swift_attr("@A")))
+#define ATTR_B __attribute__((swift_attr("@B")))
+
+void f1(int * ATTR_A p);
+void f2(int * ATTR_A p);
+void f3(int * ATTR_B p);
+
+// CHECK-COUNT-2: <TYPE_ATTRIBUTED
+// CHECK-NOT: <TYPE_ATTRIBUTED
>From 90f72abfe49e55b1c93eaa93640e9ba05ebd00f6 Mon Sep 17 00:00:00 2001
From: Qiongsi Wu <qiongsi_wu at apple.com>
Date: Tue, 2 Jun 2026 10:52:23 -0700
Subject: [PATCH 2/2] Revise to make the fix more focused. Removed the virtual
method Profile from the base Attr class.
---
clang/include/clang/AST/Attr.h | 12 ------------
clang/include/clang/Basic/Attr.td | 2 +-
clang/lib/AST/Type.cpp | 6 +++---
3 files changed, 4 insertions(+), 16 deletions(-)
diff --git a/clang/include/clang/AST/Attr.h b/clang/include/clang/AST/Attr.h
index df2da85cf6402..0f9fc01391c30 100644
--- a/clang/include/clang/AST/Attr.h
+++ b/clang/include/clang/AST/Attr.h
@@ -81,8 +81,6 @@ class Attr : public AttributeCommonInfo {
return ::operator delete(Ptr, C, Alignment);
}
- virtual ~Attr() = default;
-
protected:
Attr(ASTContext &Context, const AttributeCommonInfo &CommonInfo,
attr::Kind AK, bool IsLateParsed)
@@ -122,16 +120,6 @@ class Attr : public AttributeCommonInfo {
void printPretty(raw_ostream &OS, const PrintingPolicy &Policy) const;
static StringRef getDocumentation(attr::Kind);
-
- // Contribute Attr-specific content to a FoldingSetNodeID for callers like
- // AttributedType::Profile that dedupe nodes by Attr identity. The default
- // is empty: AttributedType::Profile already hashes attrKind (and
- // asserts attr->getKind() == attrKind), so a no-op default makes stateless
- // Attrs (nullability, ARC ownership, calling conventions, ...) dedupe by
- // (kind, modified, equivalent) — pointer identity of the Attr is ignored.
- // Override this in subclasses whose arguments meaningfully distinguish
- // the wrapped type (e.g. SwiftAttrAttr hashes its string payload).
- virtual void Profile(llvm::FoldingSetNodeID &ID) const {}
};
class TypeAttr : public Attr {
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 533f13d2ca879..44f8ea6d16532 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -3197,7 +3197,7 @@ def SwiftAttr : DeclOrTypeAttr {
let Documentation = [SwiftAttrDocs];
let PragmaAttributeSupport = 1;
let AdditionalMembers = [{
- void Profile(llvm::FoldingSetNodeID &ID) const override {
+ void Profile(llvm::FoldingSetNodeID &ID) const {
ID.AddString(getAttribute());
}
}];
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index 711b216d81de7..c9d038ec308ef 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -14,6 +14,7 @@
#include "Linkage.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Attr.h"
+#include "clang/AST/Attrs.inc"
#include "clang/AST/CharUnits.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclBase.h"
@@ -5341,12 +5342,11 @@ NullabilityKindOrNone AttributedType::stripOuterNullability(QualType &T) {
void AttributedType::Profile(llvm::FoldingSetNodeID &ID, Kind attrKind,
QualType modified, QualType equivalent,
const Attr *attr) {
-
ID.AddInteger(attrKind);
ID.AddPointer(modified.getAsOpaquePtr());
ID.AddPointer(equivalent.getAsOpaquePtr());
- if (attr)
- attr->Profile(ID);
+ if (const auto *A = dyn_cast_or_null<SwiftAttrAttr>(attr))
+ A->Profile(ID);
}
bool Type::isSignableIntegerType(const ASTContext &Ctx) const {
More information about the cfe-commits
mailing list