Skip to content

[Clang] Ensure "=default"ed function can be deleted when used as an extension in C++03 #90725

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

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

MitalAshok
Copy link
Contributor

Fixes #90605

@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" labels May 1, 2024
@llvmbot
Copy link
Member

llvmbot commented May 1, 2024

@llvm/pr-subscribers-clang

Author: Mital Ashok (MitalAshok)

Changes

Fixes #90605


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

3 Files Affected:

  • (modified) clang/lib/Sema/SemaDeclCXX.cpp (+3-1)
  • (modified) clang/test/SemaCXX/cxx0x-cursory-default-delete.cpp (+31-3)
  • (modified) clang/test/SemaCXX/cxx0x-defaulted-functions.cpp (+13)
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 157d42c09cfcd8..e3c90c8ee1d644 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -9767,7 +9767,9 @@ bool Sema::ShouldDeleteSpecialMember(CXXMethodDecl *MD,
     return false;
   CXXRecordDecl *RD = MD->getParent();
   assert(!RD->isDependentType() && "do deletion after instantiation");
-  if (!LangOpts.CPlusPlus || (!LangOpts.CPlusPlus11 && !RD->isLambda()) ||
+  if (!LangOpts.CPlusPlus ||
+      (!LangOpts.CPlusPlus11 && !RD->isLambda() &&
+       !MD->isExplicitlyDefaulted()) ||
       RD->isInvalidDecl())
     return false;
 
diff --git a/clang/test/SemaCXX/cxx0x-cursory-default-delete.cpp b/clang/test/SemaCXX/cxx0x-cursory-default-delete.cpp
index 6ae146f0d08c7d..f884725a234671 100644
--- a/clang/test/SemaCXX/cxx0x-cursory-default-delete.cpp
+++ b/clang/test/SemaCXX/cxx0x-cursory-default-delete.cpp
@@ -1,4 +1,11 @@
 // RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -std=c++03 -Wno-c++11-extensions -fsyntax-only -verify %s
+
+#if __cplusplus < 201103L
+#define static_assert _Static_assert
+#define nullptr 0
+#define noexcept throw()
+#endif
 
 struct non_copiable {
   non_copiable(const non_copiable&) = delete; // expected-note {{marked deleted here}}
@@ -25,10 +32,12 @@ void fn1 () {
   non_const_copy ncc;
   non_const_copy ncc2 = ncc;
   ncc = ncc2;
+#if __cplusplus >= 201103L
   const non_const_copy cncc{};
+#endif
   const non_const_copy cncc1; // expected-error {{default initialization of an object of const type 'const non_const_copy' without a user-provided default constructor}}
-  non_const_copy ncc3 = cncc; // expected-error {{no matching}}
-  ncc = cncc; // expected-error {{no viable overloaded}}
+  non_const_copy ncc3 = cncc1; // expected-error {{no matching}}
+  ncc = cncc1; // expected-error {{no viable overloaded}}
 };
 
 struct no_fields { };
@@ -196,7 +205,7 @@ struct except_spec_d_match : except_spec_a, except_spec_b {
 struct S { S(); };
 S::S() __attribute((noreturn)) = default;
 
-using size_t = decltype(sizeof(0));
+using size_t = __SIZE_TYPE__;
 void *operator new(size_t) = delete; // expected-error {{deleted definition must be first declaration}} expected-note {{implicit}}
 void operator delete(void *) noexcept = delete; // expected-error {{deleted definition must be first declaration}} expected-note {{implicit}}
 
@@ -217,3 +226,22 @@ namespace deleted_overrides_deleted {
   template<typename T> struct B : A { virtual void f() = delete; };
   template struct B<int>;
 }
+
+namespace GH90605 {
+struct Element {
+    Element& operator=(const Element&) = delete; // #GH90605-Element-assign
+};
+
+struct S {
+    Element i; // #GH90605-i
+
+    S& operator=(const S&) = default;
+// expected-warning@-1 {{explicitly defaulted copy assignment operator is implicitly deleted}}
+//   expected-note@#GH90605-i {{copy assignment operator of 'S' is implicitly deleted because field 'i' has a deleted copy assignment operator}}
+//   expected-note@#GH90605-Element-assign {{'operator=' has been explicitly marked deleted here}}
+//   expected-note@-4 {{replace 'default' with 'delete'}}
+};
+
+static_assert(!__is_trivially_assignable(S&, const S&), "");
+static_assert(!__is_assignable(S&, const S&), "");
+}
diff --git a/clang/test/SemaCXX/cxx0x-defaulted-functions.cpp b/clang/test/SemaCXX/cxx0x-defaulted-functions.cpp
index 0c3dd1ea7aa274..30d54920a1a686 100644
--- a/clang/test/SemaCXX/cxx0x-defaulted-functions.cpp
+++ b/clang/test/SemaCXX/cxx0x-defaulted-functions.cpp
@@ -1,4 +1,9 @@
 // RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify -fcxx-exceptions -Wno-deprecated-builtins %s
+// RUN: %clang_cc1 -std=c++03 -Wno-c++11-extensions -fsyntax-only -verify -fcxx-exceptions -Wno-deprecated-builtins %s
+
+#if __cplusplus < 201103L
+#define static_assert _Static_assert
+#endif
 
 void fn() = default; // expected-error {{only special member}}
 struct foo {
@@ -43,6 +48,7 @@ void tester() {
   b = c;
 }
 
+#if __cplusplus >= 201103L
 template<typename T> struct S : T {
   constexpr S() = default;         // expected-note {{previous declaration is here}}
   constexpr S(const S&) = default; // expected-note {{previous declaration is here}}
@@ -118,6 +124,7 @@ namespace DefaultedFnExceptionSpec {
     *p = *p; // expected-note {{instantiation of}}
   }
 }
+#endif
 
 namespace PR13527 {
   struct X {
@@ -135,6 +142,7 @@ namespace PR13527 {
   X &X::operator=(X&&) = default; // expected-error {{redefinition}}
   X::~X() = default; // expected-error {{redefinition}}
 
+#if __cplusplus >= 201103L
   struct Y {
     Y() = default;
     Y(const Y&) = default;
@@ -149,6 +157,7 @@ namespace PR13527 {
   Y &Y::operator=(const Y&) noexcept = default; // expected-error {{definition of explicitly defaulted}}
   Y &Y::operator=(Y&&) noexcept = default; // expected-error {{definition of explicitly defaulted}}
   Y::~Y() = default; // expected-error {{definition of explicitly defaulted}}
+#endif
 }
 
 namespace PR27699 {
@@ -185,6 +194,7 @@ extern "C" { // expected-note {{extern "C" language linkage specification begins
  void PR13573(const _Tp&) = delete;
 }
 
+#if __cplusplus >= 201103L
 namespace PR15597 {
   template<typename T> struct A {
     A() noexcept(true) = default;
@@ -197,6 +207,7 @@ namespace PR15597 {
   A<int> a;
   B<int> b;
 }
+#endif
 
 namespace PR27941 {
 struct ExplicitBool {
@@ -245,6 +256,7 @@ E<Type>::E(const int&) {}  // expected-error {{definition of explicitly defaulte
 
 }
 
+#if __cplusplus >= 201103L
 namespace P1286R2 {
   struct X {
     X();
@@ -286,3 +298,4 @@ struct B {
   auto operator = (RM<B>) -> RV<B> = delete;
 };
 }
+#endif

Copy link
Contributor

@cor3ntin cor3ntin left a comment

Choose a reason for hiding this comment

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

Can you add a changelog entry?

@@ -9767,7 +9767,9 @@ bool Sema::ShouldDeleteSpecialMember(CXXMethodDecl *MD,
return false;
CXXRecordDecl *RD = MD->getParent();
assert(!RD->isDependentType() && "do deletion after instantiation");
if (!LangOpts.CPlusPlus || (!LangOpts.CPlusPlus11 && !RD->isLambda()) ||
if (!LangOpts.CPlusPlus ||
Copy link
Contributor

Choose a reason for hiding this comment

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

are there cases where !LangOpts.CPlusPlus is true 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.

I wouldn't think so since we have a CXXMethodDecl and CXXRecordDecl. It was added with #73376 which changed it from!CPlusPlus11 when adding C++03 lambdas

Copy link
Contributor

Choose a reason for hiding this comment

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

I would try to remove it and see if tests pass (which i suspect they will)

@philnik777
Copy link
Contributor

Note that this is also a problem with an implicit assignment operator: https://godbolt.org/z/jrh5novMo. I'm not 100% sure it's not a bug, but it definitely looks to me like one. It's definitely a mismatch between C++03 and C++11, which I wouldn't expect given that = delete a C++11 extension.

Now that `= default` will be deleted when ill-formed when used as an extension
@MitalAshok MitalAshok requested a review from Endilll as a code owner May 1, 2024 14:51
@MitalAshok MitalAshok marked this pull request as draft May 1, 2024 14:51
@MitalAshok MitalAshok force-pushed the fix-cxx03-defaulted-extension branch from ca63e41 to 5df6f72 Compare May 1, 2024 16:06
@Endilll Endilll removed their request for review January 15, 2025 18:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
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.

[Clang] __is_trivially_assignable returning true with deleted member
4 participants