Skip to content

Commit 273ceb1

Browse files
authored
[Clang] Diagnose defaulted assignment operator with incompatible object parameter (#70176)
Per https://eel.is/c++draft/dcl.fct.def.default#2.2, the explicit object parameter of a defaulted special member function must be of the same type as the one of an equivalent implicitly defaulted function, ignoring references. Fixes #69233
1 parent 6a62707 commit 273ceb1

File tree

3 files changed

+62
-0
lines changed

3 files changed

+62
-0
lines changed

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9483,6 +9483,9 @@ def err_defaulted_special_member_return_type : Error<
94839483
def err_defaulted_special_member_quals : Error<
94849484
"an explicitly-defaulted %select{copy|move}0 assignment operator may not "
94859485
"have 'const'%select{, 'constexpr'|}1 or 'volatile' qualifiers">;
9486+
def err_defaulted_special_member_explicit_object_mismatch : Error<
9487+
"the type of the explicit object parameter of an explicitly-defaulted "
9488+
"%select{copy|move}0 assignment operator should match the type of the class %1">;
94869489
def err_defaulted_special_member_volatile_param : Error<
94879490
"the parameter for an explicitly-defaulted %sub{select_special_member_kind}0 "
94889491
"may not be volatile">;

clang/lib/Sema/SemaDeclCXX.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7748,6 +7748,24 @@ bool Sema::CheckExplicitlyDefaultedSpecialMember(CXXMethodDecl *MD,
77487748
HadError = true;
77497749
}
77507750
}
7751+
// [C++23][dcl.fct.def.default]/p2.2
7752+
// if F2 has an implicit object parameter of type “reference to C”,
7753+
// F1 may be an explicit object member function whose explicit object
7754+
// parameter is of (possibly different) type “reference to C”,
7755+
// in which case the type of F1 would differ from the type of F2
7756+
// in that the type of F1 has an additional parameter;
7757+
if (!Context.hasSameType(
7758+
ThisType.getNonReferenceType().getUnqualifiedType(),
7759+
Context.getRecordType(RD))) {
7760+
if (DeleteOnTypeMismatch)
7761+
ShouldDeleteForTypeMismatch = true;
7762+
else {
7763+
Diag(MD->getLocation(),
7764+
diag::err_defaulted_special_member_explicit_object_mismatch)
7765+
<< (CSM == CXXMoveAssignment) << RD << MD->getSourceRange();
7766+
HadError = true;
7767+
}
7768+
}
77517769
}
77527770

77537771
// Check for parameter type matching.

clang/test/SemaCXX/cxx2b-deducing-this.cpp

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -585,3 +585,44 @@ class Server : public Thing {
585585
S name_;
586586
};
587587
}
588+
589+
namespace GH69233 {
590+
struct Base {};
591+
struct S : Base {
592+
int j;
593+
S& operator=(this Base& self, const S&) = default;
594+
// expected-warning@-1 {{explicitly defaulted copy assignment operator is implicitly deleted}}
595+
// expected-note@-2 {{function is implicitly deleted because its declared type does not match the type of an implicit copy assignment operator}}
596+
// expected-note@-3 {{explicitly defaulted function was implicitly deleted here}}
597+
};
598+
599+
struct S2 {
600+
S2& operator=(this int&& self, const S2&);
601+
S2& operator=(this int&& self, S2&&);
602+
operator int();
603+
};
604+
605+
S2& S2::operator=(this int&& self, const S2&) = default;
606+
// expected-error@-1 {{the type of the explicit object parameter of an explicitly-defaulted copy assignment operator should match the type of the class 'S2'}}
607+
608+
S2& S2::operator=(this int&& self, S2&&) = default;
609+
// expected-error@-1 {{the type of the explicit object parameter of an explicitly-defaulted move assignment operator should match the type of the class 'S2'}}
610+
611+
struct Move {
612+
Move& operator=(this int&, Move&&) = default;
613+
// expected-warning@-1 {{explicitly defaulted move assignment operator is implicitly deleted}}
614+
// expected-note@-2 {{function is implicitly deleted because its declared type does not match the type of an implicit move assignment operator}}
615+
// expected-note@-3 {{copy assignment operator is implicitly deleted because 'Move' has a user-declared move assignment operator}}
616+
};
617+
618+
void test() {
619+
S s;
620+
s = s; // expected-error {{object of type 'S' cannot be assigned because its copy assignment operator is implicitly deleted}}
621+
S2 s2;
622+
s2 = s2;
623+
624+
Move m;
625+
m = Move{}; // expected-error {{object of type 'Move' cannot be assigned because its copy assignment operator is implicitly deleted}}
626+
}
627+
628+
}

0 commit comments

Comments
 (0)