https://github.com/cor3ntin created 
https://github.com/llvm/llvm-project/pull/143661

When the definition of a special member function was defaulted we would not 
consider it user-provided, even when the first declaration was not defaulted.

Fixes #143599

>From b12415e6d4bfab4d97a8a416c5f924e0bb8d62ce Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinja...@gmail.com>
Date: Wed, 11 Jun 2025 09:21:05 +0200
Subject: [PATCH] [Clang] _default-movable_ should be based on the first
 declaration

When the definition of a special member function was defaulted
we would not consider it user-provided, even when the first
declaration was not defaulted.

Fixes #143599
---
 clang/lib/Sema/SemaTypeTraits.cpp             | 19 +++++++++------
 .../SemaCXX/cxx2c-trivially-relocatable.cpp   | 21 +++++++++++++++++
 .../SemaCXX/type-traits-unsatisfied-diags.cpp | 23 +++++++++++++++++++
 3 files changed, 56 insertions(+), 7 deletions(-)

diff --git a/clang/lib/Sema/SemaTypeTraits.cpp 
b/clang/lib/Sema/SemaTypeTraits.cpp
index d663e5581093e..bba0099b6fcb5 100644
--- a/clang/lib/Sema/SemaTypeTraits.cpp
+++ b/clang/lib/Sema/SemaTypeTraits.cpp
@@ -121,8 +121,10 @@ static bool hasSuitableConstructorForRelocation(Sema 
&SemaRef,
 
   CXXMethodDecl *Decl =
       LookupSpecialMemberFromXValue(SemaRef, D, /*Assign=*/false);
-  return Decl && Decl->isUserProvided() == AllowUserDefined &&
-         !Decl->isDeleted();
+  if (!Decl)
+    return false;
+  Decl = Decl->getCanonicalDecl();
+  return Decl->isUserProvided() == AllowUserDefined && !Decl->isDeleted();
 }
 
 static bool hasSuitableMoveAssignmentOperatorForRelocation(
@@ -136,9 +138,8 @@ static bool hasSuitableMoveAssignmentOperatorForRelocation(
       LookupSpecialMemberFromXValue(SemaRef, D, /*Assign=*/true);
   if (!Decl)
     return false;
-
-  return Decl && Decl->isUserProvided() == AllowUserDefined &&
-         !Decl->isDeleted();
+  Decl = Decl->getCanonicalDecl();
+  return Decl->isUserProvided() == AllowUserDefined && !Decl->isDeleted();
 }
 
 // [C++26][class.prop]
@@ -164,6 +165,8 @@ static bool IsDefaultMovable(Sema &SemaRef, const 
CXXRecordDecl *D) {
   if (!Dtr)
     return true;
 
+  Dtr = Dtr->getCanonicalDecl();
+
   if (Dtr->isUserProvided() && (!Dtr->isDefaulted() || Dtr->isDeleted()))
     return false;
 
@@ -2031,7 +2034,7 @@ static void DiagnoseNonDefaultMovable(Sema &SemaRef, 
SourceLocation Loc,
   if (!D->hasSimpleMoveConstructor() && !D->hasSimpleCopyConstructor()) {
     const auto *Decl = cast_or_null<CXXConstructorDecl>(
         LookupSpecialMemberFromXValue(SemaRef, D, /*Assign=*/false));
-    if (Decl && Decl->isUserProvided())
+    if (Decl && Decl->getCanonicalDecl()->isUserProvided())
       SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
           << diag::TraitNotSatisfiedReason::UserProvidedCtr
           << Decl->isMoveConstructor() << Decl->getSourceRange();
@@ -2039,12 +2042,14 @@ static void DiagnoseNonDefaultMovable(Sema &SemaRef, 
SourceLocation Loc,
   if (!D->hasSimpleMoveAssignment() && !D->hasSimpleCopyAssignment()) {
     CXXMethodDecl *Decl =
         LookupSpecialMemberFromXValue(SemaRef, D, /*Assign=*/true);
-    if (Decl && Decl->isUserProvided())
+    if (Decl && Decl->getCanonicalDecl()->isUserProvided())
       SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
           << diag::TraitNotSatisfiedReason::UserProvidedAssign
           << Decl->isMoveAssignmentOperator() << Decl->getSourceRange();
   }
   CXXDestructorDecl *Dtr = D->getDestructor();
+  if (Dtr)
+    Dtr = Dtr->getCanonicalDecl();
   if (Dtr && Dtr->isUserProvided() && !Dtr->isDefaulted())
     SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
         << diag::TraitNotSatisfiedReason::DeletedDtr << /*User Provided*/ 1
diff --git a/clang/test/SemaCXX/cxx2c-trivially-relocatable.cpp 
b/clang/test/SemaCXX/cxx2c-trivially-relocatable.cpp
index aff172e0bc70a..9d43994ee7661 100644
--- a/clang/test/SemaCXX/cxx2c-trivially-relocatable.cpp
+++ b/clang/test/SemaCXX/cxx2c-trivially-relocatable.cpp
@@ -388,3 +388,24 @@ void do_test__builtin_trivially_relocate() {
     // expected-note@-1 {{'test__builtin_trivially_relocate<S *, S *, int>' 
requested here}}
     // expected-error@#reloc1 {{first argument to 
'__builtin_trivially_relocate' must be relocatable}}
 }
+
+
+namespace GH143599 {
+struct A { ~A (); };
+A::~A () = default;
+
+static_assert (!__builtin_is_cpp_trivially_relocatable(A));
+static_assert (!__builtin_is_replaceable(A));
+
+struct B { B(const B&); };
+B::B (const B&) = default;
+
+static_assert (!__builtin_is_cpp_trivially_relocatable(B));
+static_assert (!__builtin_is_replaceable(B));
+
+struct C { C& operator=(const C&); };
+C& C::operator=(const C&) = default;
+
+static_assert (!__builtin_is_cpp_trivially_relocatable(C));
+static_assert (!__builtin_is_replaceable(C));
+}
diff --git a/clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp 
b/clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp
index 9e053034acda4..a8c78f6304ca9 100644
--- a/clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp
+++ b/clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp
@@ -320,6 +320,29 @@ 
static_assert(__builtin_is_cpp_trivially_relocatable(UnionOfPolymorphic));
 
 }
 
+struct GH143599 {  // expected-note 2 {{'GH143599' defined here}}
+    ~GH143599 ();
+     GH143599(const GH143599&);
+     GH143599& operator=(const GH143599&);
+};
+GH143599::~GH143599 () = default;
+GH143599::GH143599 (const GH143599&) = default;
+GH143599& GH143599::operator=(const GH143599&) = default;
+
+static_assert (__builtin_is_cpp_trivially_relocatable(GH143599));
+// expected-error@-1 {{static assertion failed due to requirement 
'__builtin_is_cpp_trivially_relocatable(GH143599)'}} \
+// expected-note@-1 {{'GH143599' is not trivially relocatable}} \
+// expected-note@-1 {{because it has a user provided copy constructor}} \
+// expected-note@-1 {{because it has a user provided copy assignment 
operator}} \
+// expected-note@-1 {{because it has a user-provided destructor}}
+
+static_assert (__builtin_is_replaceable(GH143599));
+// expected-error@-1 {{static assertion failed due to requirement 
'__builtin_is_replaceable(GH143599)'}} \
+// expected-note@-1 {{'GH143599' is not replaceable}} \
+// expected-note@-1 {{because it has a user provided copy constructor}} \
+// expected-note@-1 {{because it has a user provided copy assignment 
operator}} \
+// expected-note@-1 {{because it has a user-provided destructor}}
+
 namespace trivially_copyable {
 struct B {
  virtual ~B();

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to