summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRichard Smith <richard@metafoo.co.uk>2019-10-27 23:10:50 -0700
committerRichard Smith <richard@metafoo.co.uk>2019-10-27 23:26:44 -0700
commita4facd355dc36bc83d5c2402856f5a3741890c9a (patch)
treef7d21cbd8a381c36c739b166fa41bc32d8eced8c
parent1a9c01c7f6c7e88676440869bbbe9f43fa45b109 (diff)
downloadbcm5719-llvm-a4facd355dc36bc83d5c2402856f5a3741890c9a.tar.gz
bcm5719-llvm-a4facd355dc36bc83d5c2402856f5a3741890c9a.zip
[c++20] Enforce rule that a union-like class or class with reference
members cannot have defaulted comparisons.
-rw-r--r--clang/include/clang/Basic/DiagnosticGroups.td1
-rw-r--r--clang/include/clang/Basic/DiagnosticSemaKinds.td15
-rw-r--r--clang/lib/Sema/SemaDeclCXX.cpp39
-rw-r--r--clang/test/CXX/class/class.compare/class.compare.default/p2.cpp70
4 files changed, 122 insertions, 3 deletions
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index 11218ccaeee..29d27ec681f 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 385be4b4492..db877a46c30 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 ccf6c0a604b..6a718db6c55 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 00000000000..eb4789e3137
--- /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'}}
+};
OpenPOWER on IntegriCloud