[clang] a4facd3 - [c++20] Enforce rule that a union-like class or class with reference

Richard Smith via cfe-commits cfe-commits at lists.llvm.org
Sun Oct 27 23:26:54 PDT 2019


Author: Richard Smith
Date: 2019-10-27T23:26:44-07:00
New Revision: a4facd355dc36bc83d5c2402856f5a3741890c9a

URL: https://github.com/llvm/llvm-project/commit/a4facd355dc36bc83d5c2402856f5a3741890c9a
DIFF: https://github.com/llvm/llvm-project/commit/a4facd355dc36bc83d5c2402856f5a3741890c9a.diff

LOG: [c++20] Enforce rule that a union-like class or class with reference
members cannot have defaulted comparisons.

Added: 
    clang/test/CXX/class/class.compare/class.compare.default/p2.cpp

Modified: 
    clang/include/clang/Basic/DiagnosticGroups.td
    clang/include/clang/Basic/DiagnosticSemaKinds.td
    clang/lib/Sema/SemaDeclCXX.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index 11218ccaeee7..29d27ec681f5 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -113,6 +113,7 @@ def UndefinedVarTemplate : DiagGroup<"undefined-var-template">;
 def UndefinedFuncTemplate : DiagGroup<"undefined-func-template">;
 def MissingNoEscape : DiagGroup<"missing-noescape">;
 
+def DefaultedComparison : DiagGroup<"defaulted-comparison">;
 def DeleteIncomplete : DiagGroup<"delete-incomplete">;
 def DeleteNonAbstractNonVirtualDtor : DiagGroup<"delete-non-abstract-non-virtual-dtor">;
 def DeleteAbstractNonVirtualDtor : DiagGroup<"delete-abstract-non-virtual-dtor">;

diff  --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 385be4b44923..db877a46c300 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -8155,6 +8155,21 @@ def err_defaulted_comparison_non_const : Error<
 def err_defaulted_comparison_return_type_not_bool : Error<
   "return type for defaulted %sub{select_defaulted_comparison_kind}0 "
   "must be 'bool', not %1">;
+def err_defaulted_comparison_reference_member : Error<
+  "cannot default %0 in class %1 with reference member">;
+def ext_defaulted_comparison_reference_member : ExtWarn<
+  "ISO C++2a does not allow defaulting %0 in class %1 with reference member">,
+  InGroup<DefaultedComparison>;
+def note_reference_member : Note<"reference member %0 declared here">;
+def err_defaulted_comparison_union : Error<
+  "cannot default %0 in %select{union-like class|union}1 %2">;
+def ext_defaulted_comparison_union : ExtWarn<
+  "ISO C++2a does not allow defaulting %0 in "
+  "%select{union-like class|union}1 %2">, InGroup<DefaultedComparison>;
+def ext_defaulted_comparison_empty_union : ExtWarn<
+  "ISO C++2a does not allow defaulting %0 in "
+  "%select{union-like class|union}1 %2 despite it having no variant members">,
+  InGroup<DefaultedComparison>;
 
 def ext_implicit_exception_spec_mismatch : ExtWarn<
   "function previously declared with an %select{explicit|implicit}0 exception "

diff  --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index ccf6c0a604b2..6a718db6c553 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -7096,8 +7096,40 @@ bool Sema::CheckExplicitlyDefaultedComparison(FunctionDecl *FD,
   //   A defaulted comparison operator function for class C is defined as
   //   deleted if any non-static data member of C is of reference type or C is
   //   a union-like class.
-  // FIXME: Applying this to cases other than == and <=> is unreasonable.
-  // FIXME: Implement.
+  llvm::SmallVector<CXXRecordDecl*, 4> Classes(1, RD);
+  FieldDecl *ReferenceMember = nullptr;
+  bool UnionLike = RD->isUnion();
+  while (!Classes.empty()) {
+    if (Classes.back()->isUnion())
+      UnionLike = true;
+    for (FieldDecl *FD : Classes.pop_back_val()->fields()) {
+      if (FD->getType()->isReferenceType())
+        ReferenceMember = FD;
+      if (FD->isAnonymousStructOrUnion())
+        Classes.push_back(FD->getType()->getAsCXXRecordDecl());
+    }
+  }
+  // For non-memberwise comparisons, this rule is unjustified, so we permit
+  // those cases as an extension.
+  bool Memberwise = DCK == DefaultedComparisonKind::Equal ||
+                    DCK == DefaultedComparisonKind::ThreeWay;
+  if (ReferenceMember) {
+    Diag(FD->getLocation(),
+         Memberwise ? diag::err_defaulted_comparison_reference_member
+                    : diag::ext_defaulted_comparison_reference_member)
+        << FD << RD;
+    Diag(ReferenceMember->getLocation(), diag::note_reference_member)
+        << ReferenceMember;
+  } else if (UnionLike) {
+    // If the class actually has no variant members, this rule similarly
+    // is unjustified, so we permit those cases too.
+    Diag(FD->getLocation(),
+         !Memberwise ? diag::ext_defaulted_comparison_union
+                     : !RD->hasVariantMembers()
+                           ? diag::ext_defaulted_comparison_empty_union
+                           : diag::err_defaulted_comparison_union)
+        << FD << RD->isUnion() << RD;
+  }
 
   // C++2a [class.eq]p1, [class.rel]p1:
   //   A [defaulted comparison other than <=>] shall have a declared return
@@ -7122,7 +7154,8 @@ bool Sema::CheckExplicitlyDefaultedComparison(FunctionDecl *FD,
   //   the requirements for a constexpr function.
   // FIXME: Apply this rule to all defaulted comparisons. The only way this
   // can fail is if the return type of a defaulted operator<=> is not a literal
-  // type.
+  // type. We should additionally consider whether any of the operations
+  // performed by the comparison invokes a non-constexpr function.
   return false;
 }
 

diff  --git a/clang/test/CXX/class/class.compare/class.compare.default/p2.cpp b/clang/test/CXX/class/class.compare/class.compare.default/p2.cpp
new file mode 100644
index 000000000000..eb4789e31376
--- /dev/null
+++ b/clang/test/CXX/class/class.compare/class.compare.default/p2.cpp
@@ -0,0 +1,70 @@
+// RUN: %clang_cc1 -std=c++2a -verify %s
+
+struct A {
+  int x;
+  int &y; // expected-note 7{{reference member 'y' declared here}}
+
+  bool operator==(const A&) const = default; // expected-error {{cannot default 'operator==' in class 'A' with reference member}}
+  bool operator!=(const A&) const = default; // expected-warning {{ISO C++2a does not allow defaulting 'operator!=' in class 'A' with reference member}}
+
+  bool operator<=>(const A&) const = default; // expected-error {{cannot default 'operator<=>' in class 'A' with reference member}}
+  bool operator<(const A&) const = default; // expected-warning {{ISO C++2a does not allow defaulting 'operator<' in class 'A' with reference member}}
+  bool operator<=(const A&) const = default; // expected-warning {{ISO C++2a does not allow defaulting 'operator<=' in class 'A' with reference member}}
+  bool operator>(const A&) const = default; // expected-warning {{ISO C++2a does not allow defaulting 'operator>' in class 'A' with reference member}}
+  bool operator>=(const A&) const = default; // expected-warning {{ISO C++2a does not allow defaulting 'operator>=' in class 'A' with reference member}}
+};
+
+struct B {
+  struct {
+    int x;
+    int &y; // expected-note 7{{reference member 'y' declared here}}
+  };
+
+  bool operator==(const B&) const = default; // expected-error {{cannot default 'operator==' in class 'B' with reference member}}
+  bool operator!=(const B&) const = default; // expected-warning {{ISO C++2a does not allow defaulting 'operator!=' in class 'B' with reference member}}
+
+  bool operator<=>(const B&) const = default; // expected-error {{cannot default 'operator<=>' in class 'B' with reference member}}
+  bool operator<(const B&) const = default; // expected-warning {{ISO C++2a does not allow defaulting 'operator<' in class 'B' with reference member}}
+  bool operator<=(const B&) const = default; // expected-warning {{ISO C++2a does not allow defaulting 'operator<=' in class 'B' with reference member}}
+  bool operator>(const B&) const = default; // expected-warning {{ISO C++2a does not allow defaulting 'operator>' in class 'B' with reference member}}
+  bool operator>=(const B&) const = default; // expected-warning {{ISO C++2a does not allow defaulting 'operator>=' in class 'B' with reference member}}
+};
+
+union C {
+  int a;
+
+  bool operator==(const C&) const = default; // expected-error {{cannot default 'operator==' in union 'C'}}
+  bool operator!=(const C&) const = default; // expected-warning {{ISO C++2a does not allow defaulting 'operator!=' in union 'C'}}
+
+  bool operator<=>(const C&) const = default; // expected-error {{cannot default 'operator<=>' in union 'C'}}
+  bool operator<(const C&) const = default; // expected-warning {{ISO C++2a does not allow defaulting 'operator<' in union 'C'}}
+  bool operator<=(const C&) const = default; // expected-warning {{ISO C++2a does not allow defaulting 'operator<=' in union 'C'}}
+  bool operator>(const C&) const = default; // expected-warning {{ISO C++2a does not allow defaulting 'operator>' in union 'C'}}
+  bool operator>=(const C&) const = default; // expected-warning {{ISO C++2a does not allow defaulting 'operator>=' in union 'C'}}
+};
+
+struct D {
+  union {
+    int a;
+  };
+
+  bool operator==(const D&) const = default; // expected-error {{cannot default 'operator==' in union-like class 'D'}}
+  bool operator!=(const D&) const = default; // expected-warning {{ISO C++2a does not allow defaulting 'operator!=' in union-like class 'D'}}
+
+  bool operator<=>(const D&) const = default; // expected-error {{cannot default 'operator<=>' in union-like class 'D'}}
+  bool operator<(const D&) const = default; // expected-warning {{ISO C++2a does not allow defaulting 'operator<' in union-like class 'D'}}
+  bool operator<=(const D&) const = default; // expected-warning {{ISO C++2a does not allow defaulting 'operator<=' in union-like class 'D'}}
+  bool operator>(const D&) const = default; // expected-warning {{ISO C++2a does not allow defaulting 'operator>' in union-like class 'D'}}
+  bool operator>=(const D&) const = default; // expected-warning {{ISO C++2a does not allow defaulting 'operator>=' in union-like class 'D'}}
+};
+
+union E {
+  bool operator==(const E&) const = default; // expected-warning {{ISO C++2a does not allow defaulting 'operator==' in union 'E' despite it having no variant members}}
+  bool operator!=(const E&) const = default; // expected-warning {{ISO C++2a does not allow defaulting 'operator!=' in union 'E'}}
+
+  bool operator<=>(const E&) const = default; // expected-warning {{ISO C++2a does not allow defaulting 'operator<=>' in union 'E' despite it having no variant members}}
+  bool operator<(const E&) const = default; // expected-warning {{ISO C++2a does not allow defaulting 'operator<' in union 'E'}}
+  bool operator<=(const E&) const = default; // expected-warning {{ISO C++2a does not allow defaulting 'operator<=' in union 'E'}}
+  bool operator>(const E&) const = default; // expected-warning {{ISO C++2a does not allow defaulting 'operator>' in union 'E'}}
+  bool operator>=(const E&) const = default; // expected-warning {{ISO C++2a does not allow defaulting 'operator>=' in union 'E'}}
+};


        


More information about the cfe-commits mailing list