Comparison operators are not allowed to be defaulted if they were previously declared outside the class.
Pretty low-impact, but it's nice to reject this without a linking error.
Fixes https://github.com/llvm/llvm-project/issues/51227.
Reviewed By: #clang-language-wg, ChuanqiXu
Differential Revision: https://reviews.llvm.org/D141803
- Fix issue that the standard C++ modules importer will call global
constructor/destructor for the global varaibles in the importing modules.
This fixes `Issue 59765 <https://github.com/llvm/llvm-project/issues/59765>`_
+- Reject in-class defaulting of previosly declared comparison operators. Fixes
+ `Issue 51227 <https://github.com/llvm/llvm-project/issues/51227>`_.
Improvements to Clang's diagnostics
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
"defaulting %select{this %sub{select_defaulted_comparison_kind}1|"
"the corresponding implicit 'operator==' for this defaulted 'operator<=>'}0 "
"would delete it after its first declaration">;
+def err_non_first_default_compare_in_class : Error<
+ "defaulting this %sub{select_defaulted_comparison_kind}0 "
+ "is not allowed because it was already declared outside the class">;
def note_defaulted_comparison_union : Note<
"defaulted %0 is implicitly deleted because "
"%2 is a %select{union-like class|union}1 with variant members">;
bool First = FD == FD->getCanonicalDecl();
- // If we want to delete the function, then do so; there's nothing else to
- // check in that case.
- if (Info.Deleted) {
- if (!First) {
+ if (!First) {
+ if (Info.Deleted) {
// C++11 [dcl.fct.def.default]p4:
// [For a] user-provided explicitly-defaulted function [...] if such a
// function is implicitly defined as deleted, the program is ill-formed.
.visit();
return true;
}
+ if (isa<CXXRecordDecl>(FD->getLexicalDeclContext())) {
+ // C++20 [class.compare.default]p1:
+ // [...] A definition of a comparison operator as defaulted that appears
+ // in a class shall be the first declaration of that function.
+ Diag(FD->getLocation(), diag::err_non_first_default_compare_in_class)
+ << (int)DCK;
+ Diag(FD->getCanonicalDecl()->getLocation(),
+ diag::note_previous_declaration);
+ return true;
+ }
+ }
+ // If we want to delete the function, then do so; there's nothing else to
+ // check in that case.
+ if (Info.Deleted) {
SetDeclDeleted(FD, FD->getLocation());
if (!inTemplateInstantiation() && !FD->isImplicit()) {
Diag(FD->getLocation(), diag::warn_defaulted_comparison_deleted)
struct S5; // expected-note 3{{forward declaration}}
bool operator==(S5, S5) = default; // expected-error{{not a friend}} expected-error 2{{has incomplete type}}
+struct S6;
+bool operator==(const S6&, const S6&); // expected-note {{previous declaration}}
+struct S6 {
+ friend bool operator==(const S6&, const S6&) = default; // expected-error {{because it was already declared outside}}
+};
+
enum e {};
bool operator==(e, int) = default; // expected-error{{expected class or reference to a constant class}}
struct F;
bool operator==(const F&, const F&);
-bool operator!=(const F&, const F&);
+bool operator!=(const F&, const F&); // expected-note {{previous declaration}}
bool operator<=>(const F&, const F&);
-bool operator<(const F&, const F&);
+bool operator<(const F&, const F&); // expected-note {{previous declaration}}
struct F {
union { int a; };
friend bool operator==(const F&, const F&) = default; // expected-error {{defaulting this equality comparison operator would delete it after its first declaration}} expected-note {{implicitly deleted because 'F' is a union-like class}}
- friend bool operator!=(const F&, const F&) = default;
+ friend bool operator!=(const F&, const F&) = default; // expected-error {{because it was already declared outside}}
friend bool operator<=>(const F&, const F&) = default; // expected-error {{defaulting this three-way comparison operator would delete it after its first declaration}} expected-note {{implicitly deleted because 'F' is a union-like class}}
- friend bool operator<(const F&, const F&) = default;
+ friend bool operator<(const F&, const F&) = default; // expected-error {{because it was already declared outside}}
};
// FIXME: This rule creates problems for reordering of declarations; is this
// really the right model?
struct G;
-bool operator==(const G&, const G&);
-bool operator!=(const G&, const G&);
-std::strong_ordering operator<=>(const G&, const G&);
-bool operator<(const G&, const G&);
-bool operator<=(const G&, const G&);
-bool operator>(const G&, const G&);
-bool operator>=(const G&, const G&);
+bool operator==(const G&, const G&); // expected-note {{previous declaration}}
+bool operator!=(const G&, const G&); // expected-note {{previous declaration}}
+std::strong_ordering operator<=>(const G&, const G&); // expected-note {{previous declaration}}
+bool operator<(const G&, const G&); // expected-note {{previous declaration}}
+bool operator<=(const G&, const G&); // expected-note {{previous declaration}}
+bool operator>(const G&, const G&); // expected-note {{previous declaration}}
+bool operator>=(const G&, const G&); // expected-note {{previous declaration}}
struct G {
- friend bool operator==(const G&, const G&) = default;
- friend bool operator!=(const G&, const G&) = default;
-
- friend std::strong_ordering operator<=>(const G&, const G&) = default;
- friend bool operator<(const G&, const G&) = default;
- friend bool operator<=(const G&, const G&) = default;
- friend bool operator>(const G&, const G&) = default;
- friend bool operator>=(const G&, const G&) = default;
+ friend bool operator==(const G&, const G&) = default; // expected-error {{because it was already declared outside}}
+ friend bool operator!=(const G&, const G&) = default; // expected-error {{because it was already declared outside}}
+
+ friend std::strong_ordering operator<=>(const G&, const G&) = default; // expected-error {{because it was already declared outside}}
+ friend bool operator<(const G&, const G&) = default; // expected-error {{because it was already declared outside}}
+ friend bool operator<=(const G&, const G&) = default; // expected-error {{because it was already declared outside}}
+ friend bool operator>(const G&, const G&) = default; // expected-error {{because it was already declared outside}}
+ friend bool operator>=(const G&, const G&) = default; // expected-error {{because it was already declared outside}}
};
bool operator==(const G&, const G&);
bool operator!=(const G&, const G&);