[clang] [C23] Implement WG14 N3037 (PR #132939)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Mar 25 08:08:57 PDT 2025
================
@@ -450,6 +453,116 @@ class StmtComparer {
};
} // namespace
+static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
+ const Attr *Attr1, const Attr *Attr2) {
+ // Two attributes are structurally equivalent if they are the same kind
+ // of attribute, spelled with the same spelling kind, and have the same
+ // arguments. This means that [[noreturn]] and __attribute__((noreturn)) are
+ // not structurally equivalent, nor are [[nodiscard("foo")]] and
+ // [[nodiscard("bar")]].
+ if (Attr1->getKind() != Attr2->getKind())
+ return false;
+
+ if (Attr1->getSyntax() != Attr2->getSyntax())
+ return false;
+
+ if (Attr1->getSpellingListIndex() != Attr2->getSpellingListIndex())
+ return false;
+
+ auto GetAttrName = [](const Attr *A) {
+ if (const IdentifierInfo *II = A->getAttrName())
+ return II->getName();
+ return StringRef{};
+ };
+
+ if (GetAttrName(Attr1) != GetAttrName(Attr2))
+ return false;
+
+ // FIXME: check the attribute arguments. Attr does not track the arguments on
+ // the base class, which makes this awkward. We may want to tablegen a
+ // comparison function for attributes? In the meantime, we're doing this the
+ // cheap way by pretty printing the attributes and checking they produce
+ // equivalent string representations.
+ std::string AttrStr1, AttrStr2;
+ PrintingPolicy DefaultPolicy(Context.LangOpts);
+ llvm::raw_string_ostream SS1(AttrStr1), SS2(AttrStr2);
+ Attr1->printPretty(SS1, DefaultPolicy);
+ Attr2->printPretty(SS2, DefaultPolicy);
+
+ return SS1.str() == SS2.str();
+}
+
+static bool
+CheckStructurallyEquivalentAttributes(StructuralEquivalenceContext &Context,
+ const Decl *D1, const Decl *D2,
+ const Decl *PrimaryDecl = nullptr) {
+ // Gather the attributes and sort them by name so that they're in equivalent
+ // orders. This means that __attribute__((foo, bar)) is equivalent to
+ // __attribute__((bar, foo)).
+ llvm::SmallVector<const Attr *, 2> Attrs1, Attrs2;
+ auto CopyAttrs = [](auto &&Range, llvm::SmallVectorImpl<const Attr *> &Cont) {
+ for (const Attr *A : Range)
+ Cont.push_back(A);
+ };
+ CopyAttrs(D1->attrs(), Attrs1);
+ CopyAttrs(D2->attrs(), Attrs2);
+
+ auto Sorter = [](const Attr *LHS, const Attr *RHS) {
+ const IdentifierInfo *II1 = LHS->getAttrName(), *II2 = RHS->getAttrName();
+ if (!II1 || !II2)
+ return II1 == II2;
+ return *II1 < *II2;
+ };
+ llvm::sort(Attrs1, Sorter);
+ llvm::sort(Attrs2, Sorter);
+
+ auto A2 = Attrs2.begin(), A2End = Attrs2.end();
+ const auto *DiagnoseDecl = cast<TypeDecl>(PrimaryDecl ? PrimaryDecl : D2);
+ for (auto A1 = Attrs1.begin(), A1End = Attrs1.end(); A1 != A1End;
+ ++A1, ++A2) {
+ if (A2 == A2End) {
----------------
Sirraide wrote:
It might be easier to bail out on `Attrs1.size() != Atrrs2.size()` first and then use `llvm::zip` here? I haven’t thought too hard about this thought.
https://github.com/llvm/llvm-project/pull/132939
More information about the cfe-commits
mailing list