philnik updated this revision to Diff 527704.
philnik marked 2 inline comments as done.
philnik added a comment.
Herald added a subscriber: jdoerfert.

- Address comments
- Try to fix CI


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D151625/new/

https://reviews.llvm.org/D151625

Files:
  clang/include/clang/Basic/Attr.td
  clang/include/clang/Basic/AttrDocs.td
  clang/lib/AST/Type.cpp
  clang/test/Misc/pragma-attribute-supported-attributes-list.test
  
clang/test/SemaCXX/attr-equality-operator-compares-members-lexicographically.cpp

Index: clang/test/SemaCXX/attr-equality-operator-compares-members-lexicographically.cpp
===================================================================
--- /dev/null
+++ clang/test/SemaCXX/attr-equality-operator-compares-members-lexicographically.cpp
@@ -0,0 +1,139 @@
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fsyntax-only -verify %s -std=c++20
+
+void __attribute__((equality_operator_compares_members_lexicographically)) foo(); // expected-warning {{'equality_operator_compares_members_lexicographically' attribute only applies to classes and enums}}
+
+struct [[clang::equality_operator_compares_members_lexicographically]] TriviallyEqualityComparabe1 {
+  int i;
+};
+static_assert(__is_trivially_equality_comparable(TriviallyEqualityComparabe1));
+
+struct __attribute__((equality_operator_compares_members_lexicographically)) TriviallyEqualityComparabe2 {
+  int i;
+};
+static_assert(__is_trivially_equality_comparable(TriviallyEqualityComparabe2));
+
+struct [[clang::equality_operator_compares_members_lexicographically(1)]] TestMessage {}; // expected-error {{'equality_operator_compares_members_lexicographically' attribute takes no arguments}}
+
+
+struct [[clang::equality_operator_compares_members_lexicographically]] NotTriviallyEqualityComparableHasPadding {
+  short i;
+  int j;
+};
+static_assert(!__is_trivially_equality_comparable(NotTriviallyEqualityComparableHasPadding), "");
+
+struct [[clang::equality_operator_compares_members_lexicographically]] NotTriviallyEqualityComparableHasFloat {
+  float i;
+  int j;
+};
+static_assert(!__is_trivially_equality_comparable(NotTriviallyEqualityComparableHasFloat), "");
+
+struct [[clang::equality_operator_compares_members_lexicographically]] NotTriviallyEqualityComparableHasTailPadding {
+  int i;
+  char j;
+};
+static_assert(!__is_trivially_equality_comparable(NotTriviallyEqualityComparableHasTailPadding), "");
+
+struct [[clang::equality_operator_compares_members_lexicographically]] NotTriviallyEqualityComparableBase : NotTriviallyEqualityComparableHasTailPadding {
+  char j;
+};
+static_assert(!__is_trivially_equality_comparable(NotTriviallyEqualityComparableBase), "");
+
+class [[clang::equality_operator_compares_members_lexicographically]] TriviallyEqualityComparablePaddedOutBase {
+  int i;
+  char c;
+};
+static_assert(!__is_trivially_equality_comparable(TriviallyEqualityComparablePaddedOutBase), "");
+
+struct [[clang::equality_operator_compares_members_lexicographically]] TriviallyEqualityComparablePaddedOut : TriviallyEqualityComparablePaddedOutBase {
+  char j[3];
+};
+static_assert(__is_trivially_equality_comparable(TriviallyEqualityComparablePaddedOut), "");
+
+struct [[clang::equality_operator_compares_members_lexicographically]] TriviallyEqualityComparable1 {
+  char i;
+};
+static_assert(__is_trivially_equality_comparable(TriviallyEqualityComparable1));
+
+struct [[clang::equality_operator_compares_members_lexicographically]] TriviallyEqualityComparable2 {
+  int i;
+};
+static_assert(__is_trivially_equality_comparable(TriviallyEqualityComparable2));
+
+struct [[clang::equality_operator_compares_members_lexicographically]] NotTriviallyEqualityComparableTriviallyEqualityComparableBases
+    : TriviallyEqualityComparable1, TriviallyEqualityComparable2 {
+};
+static_assert(!__is_trivially_equality_comparable(NotTriviallyEqualityComparableTriviallyEqualityComparableBases));
+
+struct [[clang::equality_operator_compares_members_lexicographically]] NotTriviallyEqualityComparableBitfield {
+  int i : 1;
+};
+static_assert(!__is_trivially_equality_comparable(NotTriviallyEqualityComparableBitfield));
+
+// TODO: This is trivially equality comparable
+struct [[clang::equality_operator_compares_members_lexicographically]] NotTriviallyEqualityComparableBitfieldFilled {
+  char i : __CHAR_BIT__;
+};
+static_assert(!__is_trivially_equality_comparable(NotTriviallyEqualityComparableBitfield));
+
+union [[clang::equality_operator_compares_members_lexicographically]] U {
+  int i;
+};
+
+struct [[clang::equality_operator_compares_members_lexicographically]] NotTriviallyEqualityComparableImplicitlyDeletedOperatorByUnion {
+  U u;
+};
+static_assert(!__is_trivially_equality_comparable(NotTriviallyEqualityComparableImplicitlyDeletedOperatorByUnion));
+
+struct NotTriviallyEqualityComparableExplicitlyDeleted {
+  int i;
+
+  friend bool operator==(const NotTriviallyEqualityComparableExplicitlyDeleted&, const NotTriviallyEqualityComparableExplicitlyDeleted&) = delete;
+};
+
+struct [[clang::equality_operator_compares_members_lexicographically]] NotTriviallyEqualityComparableImplicitlyDeletedOperatorByStruct {
+  NotTriviallyEqualityComparableExplicitlyDeleted u;
+};
+static_assert(!__is_trivially_equality_comparable(NotTriviallyEqualityComparableImplicitlyDeletedOperatorByStruct));
+
+struct [[clang::equality_operator_compares_members_lexicographically]] NotTriviallyEqualityComparableHasReferenceMember {
+  int& i;
+};
+static_assert(!__is_trivially_equality_comparable(NotTriviallyEqualityComparableHasReferenceMember));
+
+enum E {
+  a,
+  b
+};
+bool operator==(E, E) { return false; }
+static_assert(!__is_trivially_equality_comparable(E));
+
+struct [[clang::equality_operator_compares_members_lexicographically]] NotTriviallyEqualityComparableHasEnum {
+  E e;
+};
+static_assert(!__is_trivially_equality_comparable(NotTriviallyEqualityComparableHasEnum));
+
+enum [[clang::equality_operator_compares_members_lexicographically]] TriviallyEqualityComparableEnum {
+  c,
+  d,
+};
+bool operator==(TriviallyEqualityComparableEnum, TriviallyEqualityComparableEnum);
+
+static_assert(__is_trivially_equality_comparable(TriviallyEqualityComparableEnum));
+
+struct [[clang::equality_operator_compares_members_lexicographically]] TriviallyEqualityComparableHasTriviallyEqualityComparableEnum {
+  TriviallyEqualityComparableEnum e;
+};
+static_assert(__is_trivially_equality_comparable(TriviallyEqualityComparableHasTriviallyEqualityComparableEnum));
+
+enum class [[clang::equality_operator_compares_members_lexicographically]] TriviallyEqualityComparableEnumClass {
+  e,
+  f,
+};
+bool operator==(TriviallyEqualityComparableEnumClass, TriviallyEqualityComparableEnumClass);
+
+static_assert(__is_trivially_equality_comparable(TriviallyEqualityComparableEnumClass));
+
+struct [[clang::equality_operator_compares_members_lexicographically]] TriviallyEqualityComparableHasTriviallyEqualityComparableEnumClass {
+  TriviallyEqualityComparableEnumClass e;
+};
+static_assert(__is_trivially_equality_comparable(TriviallyEqualityComparableHasTriviallyEqualityComparableEnumClass));
Index: clang/test/Misc/pragma-attribute-supported-attributes-list.test
===================================================================
--- clang/test/Misc/pragma-attribute-supported-attributes-list.test
+++ clang/test/Misc/pragma-attribute-supported-attributes-list.test
@@ -187,6 +187,7 @@
 // CHECK-NEXT: TargetVersion (SubjectMatchRule_function)
 // CHECK-NEXT: TestTypestate (SubjectMatchRule_function_is_member)
 // CHECK-NEXT: TrivialABI (SubjectMatchRule_record)
+// CHECK-NEXT: EqualityOperatorComparesMembersLexicographically (SubjectMatchRule_function)
 // CHECK-NEXT: Uninitialized (SubjectMatchRule_variable_is_local)
 // CHECK-NEXT: UnsafeBufferUsage (SubjectMatchRule_function)
 // CHECK-NEXT: UseHandle (SubjectMatchRule_variable_is_parameter)
Index: clang/lib/AST/Type.cpp
===================================================================
--- clang/lib/AST/Type.cpp
+++ clang/lib/AST/Type.cpp
@@ -2649,7 +2649,8 @@
             Decl->isTriviallyCopyable());
   };
 
-  if (llvm::none_of(Decl->methods(), IsDefaultedOperatorEqualEqual) &&
+  if (!Decl->hasAttr<EqualityOperatorComparesMembersLexicographicallyAttr>() &&
+      llvm::none_of(Decl->methods(), IsDefaultedOperatorEqualEqual) &&
       llvm::none_of(Decl->friends(), [&](const FriendDecl *Friend) {
         if (NamedDecl *ND = Friend->getFriendDecl()) {
           return ND->isFunctionOrFunctionTemplate() &&
@@ -2667,7 +2668,11 @@
                       }) &&
          llvm::all_of(Decl->fields(), [](const FieldDecl *FD) {
            auto Type = FD->getType();
-           if (Type->isReferenceType() || Type->isEnumeralType())
+           if (Type->isReferenceType() ||
+               (Type->isEnumeralType() &&
+                !Type->getAsTagDecl()
+                     ->hasAttr<
+                         EqualityOperatorComparesMembersLexicographicallyAttr>()))
              return false;
            if (const auto *RD = Type->getAsCXXRecordDecl())
              return HasNonDeletedDefaultedEqualityComparison(RD);
@@ -2679,7 +2684,9 @@
     const ASTContext &Context) const {
   QualType CanonicalType = getCanonicalType();
   if (CanonicalType->isIncompleteType() || CanonicalType->isDependentType() ||
-      CanonicalType->isEnumeralType())
+      (CanonicalType->isEnumeralType() &&
+       !CanonicalType->getAsTagDecl()
+            ->hasAttr<EqualityOperatorComparesMembersLexicographicallyAttr>()))
     return false;
 
   if (const auto *RD = CanonicalType->getAsCXXRecordDecl()) {
Index: clang/include/clang/Basic/AttrDocs.td
===================================================================
--- clang/include/clang/Basic/AttrDocs.td
+++ clang/include/clang/Basic/AttrDocs.td
@@ -538,8 +538,8 @@
 def NoMergeDocs : Documentation {
   let Category = DocCatStmt;
   let Content = [{
-If a statement is marked ``nomerge`` and contains call expressions, those call
-expressions inside the statement will not be merged during optimization. This 
+If a statement is marked ``nomerge`` and contains call expressions, those call 
+expressions inside the statement will not be merged during optimization. This
 attribute can be used to prevent the optimizer from obscuring the source
 location of certain calls. For example, it will prevent tail merging otherwise
 identical code sequences that raise an exception or terminate the program. Tail
@@ -1584,7 +1584,7 @@
 ``watchos``
   Apple's watchOS operating system. The minimum deployment target is specified by
   the ``-mwatchos-version-min=*version*`` command-line argument.
-  
+
 ``driverkit``
   Apple's DriverKit userspace kernel extensions. The minimum deployment target
   is specified as part of the triple.
@@ -3519,6 +3519,21 @@
   }];
 }
 
+def EqualityOperatorComparesMembersLexicographicallyDocs : Documentation {
+  let Category = DocCatDecl;
+  let Content = [{
+On classes, the ``equality_operator_compares_members_lexicographically``
+attribute informs clang that a call to ``operator==(const T&, const T&)`` is
+equivalent to comparing the members lexicographically (i.e. the call is
+equivalent to a call to ``bool operator==(const T&) const = default``). This is
+useful for libraries which have strict requirements on ADL uses. If possible, a
+defaulted equality comparison operator should be preferred over this attribute.
+On enums, the attribute guarantees that there are either no custom comparison
+operators defined, or any that defined are quivalent to the builtin comparison
+operator.
+  }];
+}
+
 def MSInheritanceDocs : Documentation {
   let Category = DocCatDecl;
   let Heading = "__single_inhertiance, __multiple_inheritance, __virtual_inheritance";
Index: clang/include/clang/Basic/Attr.td
===================================================================
--- clang/include/clang/Basic/Attr.td
+++ clang/include/clang/Basic/Attr.td
@@ -1628,6 +1628,14 @@
   let SimpleHandler = 1;
 }
 
+def EqualityOperatorComparesMembersLexicographically : InheritableAttr {
+  let Spellings = [Clang<"equality_operator_compares_members_lexicographically", 0>];
+  let Subjects = SubjectList<[CXXRecord, Enum]>;
+  let Documentation = [EqualityOperatorComparesMembersLexicographicallyDocs];
+  let LangOpts = [CPlusPlus];
+  let SimpleHandler = 1;
+}
+
 def MaxFieldAlignment : InheritableAttr {
   // This attribute has no spellings as it is only ever created implicitly.
   let Spellings = [];
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
  • [PATCH] D151625: [clang] A... Nikolas Klauser via Phabricator via cfe-commits

Reply via email to