Skip to content

[Clang] Diagnose defaulted assignment operator with incompatible object parameter #70176

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Oct 30, 2023

Conversation

cor3ntin
Copy link
Contributor

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

@cor3ntin cor3ntin requested a review from AaronBallman October 25, 2023 08:13
@cor3ntin cor3ntin added the c++23 label Oct 25, 2023
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" labels Oct 25, 2023
@llvmbot
Copy link
Member

llvmbot commented Oct 25, 2023

@llvm/pr-subscribers-clang

Author: cor3ntin (cor3ntin)

Changes

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


Full diff: https://github.com/llvm/llvm-project/pull/70176.diff

3 Files Affected:

  • (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+3)
  • (modified) clang/lib/Sema/SemaDeclCXX.cpp (+15)
  • (modified) clang/test/SemaCXX/cxx2b-deducing-this.cpp (+31)
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index a673ce726d6c220..6e138683334c2c3 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -9481,6 +9481,9 @@ def err_defaulted_special_member_return_type : Error<
 def err_defaulted_special_member_quals : Error<
   "an explicitly-defaulted %select{copy|move}0 assignment operator may not "
   "have 'const'%select{, 'constexpr'|}1 or 'volatile' qualifiers">;
+def err_defaulted_special_member_explicit_object_mismatch : Error<
+  "the type of the explicit object parameter of an explicitly-defaulted "
+  "%select{copy|move}0 assignment operator should match the type of the class %1">;
 def err_defaulted_special_member_volatile_param : Error<
   "the parameter for an explicitly-defaulted %sub{select_special_member_kind}0 "
   "may not be volatile">;
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index a3f68d4ffc0f6ec..35554fca5776f2c 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -7748,6 +7748,21 @@ bool Sema::CheckExplicitlyDefaultedSpecialMember(CXXMethodDecl *MD,
         HadError = true;
       }
     }
+    // [C++23][dcl.fct.def.default]/p2.2
+    // if F2 has an implicit object parameter of type “reference to C”,
+    // F1 may be an explicit object member function whose explicit object
+    // parameter is of (possibly different) type “reference to C”,
+    // in which case the type of F1 would differ from the type of F2
+    // in that the type of F1 has an additional parameter;
+    if(!Context.hasSameType(ThisType.getNonReferenceType().getUnqualifiedType(), Context.getRecordType(RD))) {
+      if (DeleteOnTypeMismatch)
+        ShouldDeleteForTypeMismatch = true;
+      else {
+        Diag(MD->getLocation(), diag::err_defaulted_special_member_explicit_object_mismatch)
+            << (CSM == CXXMoveAssignment) << RD;
+        HadError = true;
+      }
+    }
   }
 
   // Check for parameter type matching.
diff --git a/clang/test/SemaCXX/cxx2b-deducing-this.cpp b/clang/test/SemaCXX/cxx2b-deducing-this.cpp
index 535381e876da9c7..f9e73b41e2c330f 100644
--- a/clang/test/SemaCXX/cxx2b-deducing-this.cpp
+++ b/clang/test/SemaCXX/cxx2b-deducing-this.cpp
@@ -585,3 +585,34 @@ class Server : public Thing {
     S name_;
 };
 }
+
+namespace GH69233 {
+struct Base {};
+struct S : Base {
+    int j;
+    S& operator=(this Base& self, const S&) = default;
+    // expected-warning@-1 {{explicitly defaulted copy assignment operator is implicitly deleted}}
+    // expected-note@-2 {{function is implicitly deleted because its declared type does not match the type of an implicit copy assignment operator}}
+    // expected-note@-3 {{explicitly defaulted function was implicitly deleted here}}
+};
+
+struct S2 {
+    S2& operator=(this int&& self, const S2&);
+    S2& operator=(this int&& self, S2&&);
+    operator int();
+};
+
+S2& S2::operator=(this int&& self, const S2&) = default;
+// 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'}}
+
+S2& S2::operator=(this int&& self, S2&&) = default;
+// 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'}}
+
+void test() {
+    S s;
+    s = s; // expected-error {{object of type 'S' cannot be assigned because its copy assignment operator is implicitly deleted}}
+    S2 s2;
+    s2 = s2;
+}
+
+}

@github-actions
Copy link

github-actions bot commented Oct 25, 2023

✅ With the latest revision this PR passed the C/C++ code formatter.

else {
Diag(MD->getLocation(),
diag::err_defaulted_special_member_explicit_object_mismatch)
<< (CSM == CXXMoveAssignment) << RD;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a useful source range we can supply here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you have in mind? the first parameter? I am not sure how useful it is though - we certainly don't do it for other warnings here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing specific. In line 7709 above, we pass MD->getSourceRange().

@cor3ntin cor3ntin force-pushed the corentin/gh69233 branch 2 times, most recently from 9277312 to e5f6815 Compare October 26, 2023 16:48

void test() {
S s;
s = s; // expected-error {{object of type 'S' cannot be assigned because its copy assignment operator is implicitly deleted}}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we get an example where move assignment fails as well.

@AaronBallman
Copy link
Collaborator

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.

I could use an explanation as to how they must be the same type when 2.2 says "...may be an explicit object member function whose explicit object parameter is of (possibly different) type “reference to C”, in which case..."

@cor3ntin
Copy link
Contributor Author

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.

I could use an explanation as to how they must be the same type when 2.2 says "...may be an explicit object member function whose explicit object parameter is of (possibly different) type “reference to C”, in which case..."

That wording is terrible, but it's hard to phrase it better.
The type may be different but it is always a reference to C where C is the same type.

Ie it can be either C& or C&&. this is consistent with the fact that we allow ref-qualified functions.
There is some context here https://lists.isocpp.org/core/2023/07/14608.php

I had a chat with core on mattermost and there is agreement that the wording could be made clearer editorially.

@AaronBallman
Copy link
Collaborator

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.

I could use an explanation as to how they must be the same type when 2.2 says "...may be an explicit object member function whose explicit object parameter is of (possibly different) type “reference to C”, in which case..."

That wording is terrible, but it's hard to phrase it better. The type may be different but it is always a reference to C where C is the same type.

Ie it can be either C& or C&&. this is consistent with the fact that we allow ref-qualified functions. There is some context here https://lists.isocpp.org/core/2023/07/14608.php

I had a chat with core on mattermost and there is agreement that the wording could be made clearer editorially.

Thank you for the explanation!

Copy link
Collaborator

@AaronBallman AaronBallman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM though something seems to be amiss with precommit CI and so it might be nice to get a clean run of that before landing (I don't insist).

…ct parameter.

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 llvm#69233
Note that if there is an invalid move constructor,
overload resolution will try to pick a copy constructor instead.
@cor3ntin cor3ntin merged commit 273ceb1 into llvm:main Oct 30, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
c++23 clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

ICE Instantiating defaulted assignment with explicit object parameter of base type
5 participants