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'}} +}; _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits