[clang] [Clang] Added explanation why a is trivial copyable evaluated to false. (PR #142341)
Shamshura Egor via cfe-commits
cfe-commits at lists.llvm.org
Mon Jun 2 04:50:31 PDT 2025
https://github.com/egorshamshura updated https://github.com/llvm/llvm-project/pull/142341
>From 2e423a75e5ee8b5ecc450d4457fc677fd0c08ad2 Mon Sep 17 00:00:00 2001
From: Shamshura Egor <shamshuraegor at gmail.com>
Date: Mon, 2 Jun 2025 07:25:26 +0000
Subject: [PATCH 1/3] [Clang] Added explanation why a is trivial copyable
evaluated to false.
---
.../clang/Basic/DiagnosticSemaKinds.td | 5 +-
clang/lib/Sema/SemaTypeTraits.cpp | 97 +++++++++++++++++++
.../type-traits-unsatisfied-diags-std.cpp | 72 ++++++++++++++
.../SemaCXX/type-traits-unsatisfied-diags.cpp | 80 +++++++++++++++
4 files changed, 253 insertions(+), 1 deletion(-)
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index efc842bb4c42e..748e0720c5ef5 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -1764,7 +1764,8 @@ def err_user_defined_msg_constexpr : Error<
// Type traits explanations
def note_unsatisfied_trait : Note<"%0 is not %enum_select<TraitName>{"
- "%TriviallyRelocatable{trivially relocatable}"
+ "%TriviallyRelocatable{trivially relocatable}|"
+ "%TriviallyCopyable{trivially copyable}"
"}1">;
def note_unsatisfied_trait_reason
@@ -1776,6 +1777,8 @@ def note_unsatisfied_trait_reason
"%VBase{has a virtual base %1}|"
"%NRBase{has a non-trivially-relocatable base %1}|"
"%NRField{has a non-trivially-relocatable member %1 of type %2}|"
+ "%NTCBase{has a non-trivially-copyable base %1}|"
+ "%NTCField{has a non-trivially-copyable member %1 of type %2}|"
"%DeletedDtr{has a %select{deleted|user-provided}1 destructor}|"
"%UserProvidedCtr{has a user provided %select{copy|move}1 "
"constructor}|"
diff --git a/clang/lib/Sema/SemaTypeTraits.cpp b/clang/lib/Sema/SemaTypeTraits.cpp
index 7bf3c8eaabf4b..aaa5aff53fbc5 100644
--- a/clang/lib/Sema/SemaTypeTraits.cpp
+++ b/clang/lib/Sema/SemaTypeTraits.cpp
@@ -11,8 +11,10 @@
//===----------------------------------------------------------------------===//
#include "clang/AST/DeclCXX.h"
+#include "clang/AST/Type.h"
#include "clang/Basic/DiagnosticParse.h"
#include "clang/Basic/DiagnosticSema.h"
+#include "clang/Basic/TypeTraits.h"
#include "clang/Sema/EnterExpressionEvaluationContext.h"
#include "clang/Sema/Initialization.h"
#include "clang/Sema/Lookup.h"
@@ -1922,6 +1924,7 @@ static std::optional<TypeTrait> StdNameToTypeTrait(StringRef Name) {
return llvm::StringSwitch<std::optional<TypeTrait>>(Name)
.Case("is_trivially_relocatable",
TypeTrait::UTT_IsCppTriviallyRelocatable)
+ .Case("is_trivially_copyable", TypeTrait::UTT_IsTriviallyCopyable)
.Default(std::nullopt);
}
@@ -2083,6 +2086,97 @@ static void DiagnoseNonTriviallyRelocatableReason(Sema &SemaRef,
SemaRef.Diag(D->getLocation(), diag::note_defined_here) << D;
}
+static void DiagnoseNonTriviallyCopyableReason(Sema &SemaRef,
+ SourceLocation Loc,
+ const CXXRecordDecl *D) {
+ for (const CXXBaseSpecifier &B : D->bases()) {
+ assert(B.getType()->getAsCXXRecordDecl() && "invalid base?");
+ if (B.isVirtual())
+ SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
+ << diag::TraitNotSatisfiedReason::VBase << B.getType()
+ << B.getSourceRange();
+ if (!B.getType().isTriviallyCopyableType(D->getASTContext())) {
+ SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
+ << diag::TraitNotSatisfiedReason::NTCBase << B.getType()
+ << B.getSourceRange();
+ }
+ }
+ for (const FieldDecl *Field : D->fields()) {
+ if (!Field->getType().isTriviallyCopyableType(Field->getASTContext()))
+ SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
+ << diag::TraitNotSatisfiedReason::NTCField << Field
+ << Field->getType() << Field->getSourceRange();
+ }
+ if (D->hasDeletedDestructor())
+ SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
+ << diag::TraitNotSatisfiedReason::DeletedDtr << 0
+ << D->getDestructor()->getSourceRange();
+
+ if (D->isUnion()) {
+ auto DiagSPM = [&](CXXSpecialMemberKind K, bool Has) {
+ if (Has)
+ SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
+ << diag::TraitNotSatisfiedReason::UnionWithUserDeclaredSMF << K;
+ };
+ DiagSPM(CXXSpecialMemberKind::CopyConstructor,
+ D->hasUserDeclaredCopyConstructor());
+ DiagSPM(CXXSpecialMemberKind::CopyAssignment,
+ D->hasUserDeclaredCopyAssignment());
+ DiagSPM(CXXSpecialMemberKind::MoveConstructor,
+ D->hasUserDeclaredMoveConstructor());
+ DiagSPM(CXXSpecialMemberKind::MoveAssignment,
+ D->hasUserDeclaredMoveAssignment());
+ return;
+ }
+
+ if (!D->hasSimpleMoveConstructor() && !D->hasSimpleCopyConstructor()) {
+ const auto *Decl = cast<CXXConstructorDecl>(
+ LookupSpecialMemberFromXValue(SemaRef, D, /*Assign=*/false));
+ if (Decl && Decl->isUserProvided())
+ SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
+ << diag::TraitNotSatisfiedReason::UserProvidedCtr
+ << Decl->isMoveConstructor() << Decl->getSourceRange();
+ }
+ if (!D->hasSimpleMoveAssignment() && !D->hasSimpleCopyAssignment()) {
+ CXXMethodDecl *Decl =
+ LookupSpecialMemberFromXValue(SemaRef, D, /*Assign=*/true);
+ if (Decl && Decl->isUserProvided())
+ SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
+ << diag::TraitNotSatisfiedReason::UserProvidedAssign
+ << Decl->isMoveAssignmentOperator() << Decl->getSourceRange();
+ }
+ CXXDestructorDecl *Dtr = D->getDestructor();
+ if (Dtr && Dtr->isUserProvided() && !Dtr->isDefaulted())
+ SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
+ << diag::TraitNotSatisfiedReason::DeletedDtr << 1
+ << Dtr->getSourceRange();
+}
+
+static void DiagnoseNonTriviallyCopyableReason(Sema &SemaRef,
+ SourceLocation Loc, QualType T) {
+ SemaRef.Diag(Loc, diag::note_unsatisfied_trait)
+ << T << diag::TraitName::TriviallyCopyable;
+
+ if (T->isReferenceType())
+ SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
+ << diag::TraitNotSatisfiedReason::Ref;
+
+ T = T.getNonReferenceType();
+
+ if (T.hasNonTrivialObjCLifetime())
+ SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
+ << diag::TraitNotSatisfiedReason::HasArcLifetime;
+
+ const CXXRecordDecl *D = T->getAsCXXRecordDecl();
+ if (!D || D->isInvalidDecl())
+ return;
+
+ if (D->hasDefinition())
+ DiagnoseNonTriviallyCopyableReason(SemaRef, Loc, D);
+
+ SemaRef.Diag(D->getLocation(), diag::note_defined_here) << D;
+}
+
void Sema::DiagnoseTypeTraitDetails(const Expr *E) {
E = E->IgnoreParenImpCasts();
if (E->containsErrors())
@@ -2097,6 +2191,9 @@ void Sema::DiagnoseTypeTraitDetails(const Expr *E) {
case UTT_IsCppTriviallyRelocatable:
DiagnoseNonTriviallyRelocatableReason(*this, E->getBeginLoc(), Args[0]);
break;
+ case UTT_IsTriviallyCopyable:
+ DiagnoseNonTriviallyCopyableReason(*this, E->getBeginLoc(), Args[0]);
+ break;
default:
break;
}
diff --git a/clang/test/SemaCXX/type-traits-unsatisfied-diags-std.cpp b/clang/test/SemaCXX/type-traits-unsatisfied-diags-std.cpp
index 90cff1e66000c..498e202e26265 100644
--- a/clang/test/SemaCXX/type-traits-unsatisfied-diags-std.cpp
+++ b/clang/test/SemaCXX/type-traits-unsatisfied-diags-std.cpp
@@ -12,6 +12,14 @@ struct is_trivially_relocatable {
template <typename T>
constexpr bool is_trivially_relocatable_v = __builtin_is_cpp_trivially_relocatable(T);
+
+template <typename T>
+struct is_trivially_copyable {
+ static constexpr bool value = __is_trivially_copyable(T);
+};
+
+template <typename T>
+constexpr bool is_trivially_copyable_v = __is_trivially_copyable(T);
#endif
#ifdef STD2
@@ -25,6 +33,17 @@ using is_trivially_relocatable = __details_is_trivially_relocatable<T>;
template <typename T>
constexpr bool is_trivially_relocatable_v = __builtin_is_cpp_trivially_relocatable(T);
+
+template <typename T>
+struct __details_is_trivially_copyable {
+ static constexpr bool value = __is_trivially_copyable(T);
+};
+
+template <typename T>
+using is_trivially_copyable = __details_is_trivially_copyable<T>;
+
+template <typename T>
+constexpr bool is_trivially_copyable_v = __is_trivially_copyable(T);
#endif
@@ -45,6 +64,15 @@ using is_trivially_relocatable = __details_is_trivially_relocatable<T>;
template <typename T>
constexpr bool is_trivially_relocatable_v = is_trivially_relocatable<T>::value;
+
+template <typename T>
+struct __details_is_trivially_copyable : bool_constant<__is_trivially_copyable(T)> {};
+
+template <typename T>
+using is_trivially_copyable = __details_is_trivially_copyable<T>;
+
+template <typename T>
+constexpr bool is_trivially_copyable_v = is_trivially_copyable<T>::value;
#endif
}
@@ -60,6 +88,18 @@ static_assert(std::is_trivially_relocatable_v<int&>);
// expected-note at -1 {{'int &' is not trivially relocatable}} \
// expected-note at -1 {{because it is a reference type}}
+static_assert(std::is_trivially_copyable<int>::value);
+
+static_assert(std::is_trivially_copyable<int&>::value);
+// expected-error-re at -1 {{static assertion failed due to requirement 'std::{{.*}}is_trivially_copyable<int &>::value'}} \
+// expected-note at -1 {{'int &' is not trivially copyable}} \
+// expected-note at -1 {{because it is a reference type}}
+static_assert(std::is_trivially_copyable_v<int&>);
+// expected-error at -1 {{static assertion failed due to requirement 'std::is_trivially_copyable_v<int &>'}} \
+// expected-note at -1 {{'int &' is not trivially copyable}} \
+// expected-note at -1 {{because it is a reference type}}
+
+
namespace test_namespace {
using namespace std;
static_assert(is_trivially_relocatable<int&>::value);
@@ -70,6 +110,15 @@ namespace test_namespace {
// expected-error at -1 {{static assertion failed due to requirement 'is_trivially_relocatable_v<int &>'}} \
// expected-note at -1 {{'int &' is not trivially relocatable}} \
// expected-note at -1 {{because it is a reference type}}
+
+ static_assert(is_trivially_copyable<int&>::value);
+ // expected-error-re at -1 {{static assertion failed due to requirement '{{.*}}is_trivially_copyable<int &>::value'}} \
+ // expected-note at -1 {{'int &' is not trivially copyable}} \
+ // expected-note at -1 {{because it is a reference type}}
+ static_assert(is_trivially_copyable_v<int&>);
+ // expected-error at -1 {{static assertion failed due to requirement 'is_trivially_copyable_v<int &>'}} \
+ // expected-note at -1 {{'int &' is not trivially copyable}} \
+ // expected-note at -1 {{because it is a reference type}}
}
@@ -82,6 +131,14 @@ concept C = std::is_trivially_relocatable_v<T>; // #concept2
template <C T> void g(); // #cand2
+template <typename T>
+requires std::is_trivially_copyable<T>::value void f2(); // #cand3
+
+template <typename T>
+concept C2 = std::is_trivially_copyable_v<T>; // #concept4
+
+template <C2 T> void g2(); // #cand4
+
void test() {
f<int&>();
// expected-error at -1 {{no matching function for call to 'f'}} \
@@ -97,5 +154,20 @@ void test() {
// expected-note@#concept2 {{because 'std::is_trivially_relocatable_v<int &>' evaluated to false}} \
// expected-note@#concept2 {{'int &' is not trivially relocatable}} \
// expected-note@#concept2 {{because it is a reference type}}
+
+ f2<int&>();
+ // expected-error at -1 {{no matching function for call to 'f2'}} \
+ // expected-note@#cand3 {{candidate template ignored: constraints not satisfied [with T = int &]}} \
+ // expected-note-re@#cand3 {{because '{{.*}}is_trivially_copyable<int &>::value' evaluated to false}} \
+ // expected-note@#cand3 {{'int &' is not trivially copyable}} \
+ // expected-note@#cand3 {{because it is a reference type}}
+
+ g2<int&>();
+ // expected-error at -1 {{no matching function for call to 'g2'}} \
+ // expected-note@#cand4 {{candidate template ignored: constraints not satisfied [with T = int &]}} \
+ // expected-note@#cand4 {{because 'int &' does not satisfy 'C2'}} \
+ // expected-note@#concept4 {{because 'std::is_trivially_copyable_v<int &>' evaluated to false}} \
+ // expected-note@#concept4 {{'int &' is not trivially copyable}} \
+ // expected-note@#concept4 {{because it is a reference type}}
}
}
diff --git a/clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp b/clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp
index d9cab20f4febd..97510fe2eca9f 100644
--- a/clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp
+++ b/clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp
@@ -144,3 +144,83 @@ static_assert(__builtin_is_cpp_trivially_relocatable(U2));
// expected-note@#tr-U2 {{'U2' defined here}}
}
+
+namespace trivially_copyable {
+struct B {
+ virtual ~B();
+};
+struct S : virtual B { // #tc-S
+ S();
+ int & a;
+ const int ci;
+ B & b;
+ B c;
+ ~S();
+};
+static_assert(__is_trivially_copyable(S));
+// expected-error at -1 {{static assertion failed due to requirement '__is_trivially_copyable(trivially_copyable::S)'}} \
+// expected-note at -1 {{'S' is not trivially copyable}} \
+// expected-note at -1 {{because it has a virtual base 'B'}} \
+// expected-note at -1 {{because it has a non-trivially-copyable base 'B'}} \
+// expected-note at -1 {{because it has a non-trivially-copyable member 'c' of type 'B'}} \
+// expected-note at -1 {{because it has a non-trivially-copyable member 'b' of type 'B &'}} \
+// expected-note at -1 {{because it has a non-trivially-copyable member 'a' of type 'int &'}} \
+// expected-note at -1 {{because it has a user-provided destructor}}
+// expected-note@#tc-S {{'S' defined here}}
+
+struct S2 { // #tc-S2
+ S2(S2&&);
+ S2& operator=(const S2&);
+};
+static_assert(__is_trivially_copyable(S2));
+// expected-error at -1 {{static assertion failed due to requirement '__is_trivially_copyable(trivially_copyable::S2)'}} \
+// expected-note at -1 {{'S2' is not trivially copyable}} \
+// expected-note at -1 {{because it has a user provided move constructor}} \
+// expected-note at -1 {{because it has a user provided copy assignment operator}} \
+// expected-note@#tc-S2 {{'S2' defined here}}
+
+struct S3 {
+ ~S3() = delete;
+};
+static_assert(__is_trivially_copyable(S3));
+
+union U { // #tc-U
+ U(const U&);
+ U(U&&);
+ U& operator=(const U&);
+ U& operator=(U&&);
+};
+static_assert(__is_trivially_copyable(U));
+// expected-error at -1 {{static assertion failed due to requirement '__is_trivially_copyable(trivially_copyable::U)'}} \
+// expected-note at -1 {{'U' is not trivially copyable}} \
+// expected-note at -1 {{because it is a union with a user-declared copy constructor}} \
+// expected-note at -1 {{because it is a union with a user-declared copy assignment operator}} \
+// expected-note at -1 {{because it is a union with a user-declared move constructor}} \
+// expected-note at -1 {{because it is a union with a user-declared move assignment operator}}
+// expected-note@#tc-U {{'U' defined here}}
+
+struct S4 { // #tc-S4
+ ~S4();
+ B b;
+};
+static_assert(__is_trivially_copyable(S4));
+// expected-error at -1 {{static assertion failed due to requirement '__is_trivially_copyable(trivially_copyable::S4)'}} \
+// expected-note at -1 {{'S4' is not trivially copyable}} \
+// expected-note at -1 {{because it has a non-trivially-copyable member 'b' of type 'B'}} \
+// expected-note at -1 {{because it has a user-provided destructor}} \
+// expected-note@#tc-S4 {{'S4' defined here}}
+
+union U2 { // #tc-U2
+ U2(const U2&);
+ U2(U2&&);
+ B b;
+};
+static_assert(__is_trivially_copyable(U2));
+// expected-error at -1 {{static assertion failed due to requirement '__is_trivially_copyable(trivially_copyable::U2)'}} \
+// expected-note at -1 {{'U2' is not trivially copyable}} \
+// expected-note at -1 {{because it is a union with a user-declared copy constructor}} \
+// expected-note at -1 {{because it is a union with a user-declared move constructor}} \
+// expected-note at -1 {{because it has a deleted destructor}} \
+// expected-note at -1 {{because it has a non-trivially-copyable member 'b' of type 'B'}} \
+// expected-note@#tc-U2 {{'U2' defined here}}
+}
>From 8edd53d0554046a50320cfab9e0db163fb86268b Mon Sep 17 00:00:00 2001
From: Shamshura Egor <shamshuraegor at gmail.com>
Date: Mon, 2 Jun 2025 09:41:32 +0000
Subject: [PATCH 2/3] Fixed check for trivial destructor using Dtr->isTrivial()
now, removed union
---
clang/lib/Sema/SemaTypeTraits.cpp | 19 +-----------
.../SemaCXX/type-traits-unsatisfied-diags.cpp | 29 -------------------
2 files changed, 1 insertion(+), 47 deletions(-)
diff --git a/clang/lib/Sema/SemaTypeTraits.cpp b/clang/lib/Sema/SemaTypeTraits.cpp
index aaa5aff53fbc5..231b10efb6afe 100644
--- a/clang/lib/Sema/SemaTypeTraits.cpp
+++ b/clang/lib/Sema/SemaTypeTraits.cpp
@@ -2112,23 +2112,6 @@ static void DiagnoseNonTriviallyCopyableReason(Sema &SemaRef,
<< diag::TraitNotSatisfiedReason::DeletedDtr << 0
<< D->getDestructor()->getSourceRange();
- if (D->isUnion()) {
- auto DiagSPM = [&](CXXSpecialMemberKind K, bool Has) {
- if (Has)
- SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
- << diag::TraitNotSatisfiedReason::UnionWithUserDeclaredSMF << K;
- };
- DiagSPM(CXXSpecialMemberKind::CopyConstructor,
- D->hasUserDeclaredCopyConstructor());
- DiagSPM(CXXSpecialMemberKind::CopyAssignment,
- D->hasUserDeclaredCopyAssignment());
- DiagSPM(CXXSpecialMemberKind::MoveConstructor,
- D->hasUserDeclaredMoveConstructor());
- DiagSPM(CXXSpecialMemberKind::MoveAssignment,
- D->hasUserDeclaredMoveAssignment());
- return;
- }
-
if (!D->hasSimpleMoveConstructor() && !D->hasSimpleCopyConstructor()) {
const auto *Decl = cast<CXXConstructorDecl>(
LookupSpecialMemberFromXValue(SemaRef, D, /*Assign=*/false));
@@ -2146,7 +2129,7 @@ static void DiagnoseNonTriviallyCopyableReason(Sema &SemaRef,
<< Decl->isMoveAssignmentOperator() << Decl->getSourceRange();
}
CXXDestructorDecl *Dtr = D->getDestructor();
- if (Dtr && Dtr->isUserProvided() && !Dtr->isDefaulted())
+ if (Dtr && !Dtr->isTrivial())
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
<< diag::TraitNotSatisfiedReason::DeletedDtr << 1
<< Dtr->getSourceRange();
diff --git a/clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp b/clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp
index 97510fe2eca9f..dc2a4b4adb9d0 100644
--- a/clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp
+++ b/clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp
@@ -184,21 +184,6 @@ struct S3 {
};
static_assert(__is_trivially_copyable(S3));
-union U { // #tc-U
- U(const U&);
- U(U&&);
- U& operator=(const U&);
- U& operator=(U&&);
-};
-static_assert(__is_trivially_copyable(U));
-// expected-error at -1 {{static assertion failed due to requirement '__is_trivially_copyable(trivially_copyable::U)'}} \
-// expected-note at -1 {{'U' is not trivially copyable}} \
-// expected-note at -1 {{because it is a union with a user-declared copy constructor}} \
-// expected-note at -1 {{because it is a union with a user-declared copy assignment operator}} \
-// expected-note at -1 {{because it is a union with a user-declared move constructor}} \
-// expected-note at -1 {{because it is a union with a user-declared move assignment operator}}
-// expected-note@#tc-U {{'U' defined here}}
-
struct S4 { // #tc-S4
~S4();
B b;
@@ -209,18 +194,4 @@ static_assert(__is_trivially_copyable(S4));
// expected-note at -1 {{because it has a non-trivially-copyable member 'b' of type 'B'}} \
// expected-note at -1 {{because it has a user-provided destructor}} \
// expected-note@#tc-S4 {{'S4' defined here}}
-
-union U2 { // #tc-U2
- U2(const U2&);
- U2(U2&&);
- B b;
-};
-static_assert(__is_trivially_copyable(U2));
-// expected-error at -1 {{static assertion failed due to requirement '__is_trivially_copyable(trivially_copyable::U2)'}} \
-// expected-note at -1 {{'U2' is not trivially copyable}} \
-// expected-note at -1 {{because it is a union with a user-declared copy constructor}} \
-// expected-note at -1 {{because it is a union with a user-declared move constructor}} \
-// expected-note at -1 {{because it has a deleted destructor}} \
-// expected-note at -1 {{because it has a non-trivially-copyable member 'b' of type 'B'}} \
-// expected-note@#tc-U2 {{'U2' defined here}}
}
>From 65ca243f7cc3a41f3fe16f5c87401c4db4a32f5b Mon Sep 17 00:00:00 2001
From: Shamshura Egor <shamshuraegor at gmail.com>
Date: Mon, 2 Jun 2025 11:49:47 +0000
Subject: [PATCH 3/3] Fixed for eligible special member functions
---
clang/lib/Sema/SemaTypeTraits.cpp | 42 ++++++++++++++++++++-----------
1 file changed, 28 insertions(+), 14 deletions(-)
diff --git a/clang/lib/Sema/SemaTypeTraits.cpp b/clang/lib/Sema/SemaTypeTraits.cpp
index 231b10efb6afe..75dc0558425e2 100644
--- a/clang/lib/Sema/SemaTypeTraits.cpp
+++ b/clang/lib/Sema/SemaTypeTraits.cpp
@@ -2112,21 +2112,35 @@ static void DiagnoseNonTriviallyCopyableReason(Sema &SemaRef,
<< diag::TraitNotSatisfiedReason::DeletedDtr << 0
<< D->getDestructor()->getSourceRange();
- if (!D->hasSimpleMoveConstructor() && !D->hasSimpleCopyConstructor()) {
- const auto *Decl = cast<CXXConstructorDecl>(
- LookupSpecialMemberFromXValue(SemaRef, D, /*Assign=*/false));
- if (Decl && Decl->isUserProvided())
- SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
- << diag::TraitNotSatisfiedReason::UserProvidedCtr
- << Decl->isMoveConstructor() << Decl->getSourceRange();
- }
- if (!D->hasSimpleMoveAssignment() && !D->hasSimpleCopyAssignment()) {
- CXXMethodDecl *Decl =
- LookupSpecialMemberFromXValue(SemaRef, D, /*Assign=*/true);
- if (Decl && Decl->isUserProvided())
+ for (const CXXMethodDecl *Method : D->methods()) {
+ if (Method->isIneligibleOrNotSelected() || Method->isTrivial() ||
+ !Method->isUserProvided()) {
+ continue;
+ }
+ auto SpecialMemberKind =
+ SemaRef.getDefaultedFunctionKind(Method).asSpecialMember();
+ switch (SpecialMemberKind) {
+ case CXXSpecialMemberKind::CopyConstructor:
+ case CXXSpecialMemberKind::MoveConstructor:
+ case CXXSpecialMemberKind::CopyAssignment:
+ case CXXSpecialMemberKind::MoveAssignment: {
+ bool IsAssignment =
+ SpecialMemberKind == CXXSpecialMemberKind::CopyAssignment ||
+ SpecialMemberKind == CXXSpecialMemberKind::MoveAssignment;
+ bool IsMove =
+ SpecialMemberKind == CXXSpecialMemberKind::MoveConstructor ||
+ SpecialMemberKind == CXXSpecialMemberKind::MoveAssignment;
+
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
- << diag::TraitNotSatisfiedReason::UserProvidedAssign
- << Decl->isMoveAssignmentOperator() << Decl->getSourceRange();
+ << (IsAssignment ? diag::TraitNotSatisfiedReason::UserProvidedAssign
+ : diag::TraitNotSatisfiedReason::UserProvidedCtr)
+ << IsMove << Method->getSourceRange();
+ break;
+ }
+ default: {
+ break;
+ }
+ }
}
CXXDestructorDecl *Dtr = D->getDestructor();
if (Dtr && !Dtr->isTrivial())
More information about the cfe-commits
mailing list