[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