Author: Shamshura Egor
Date: 2025-06-07T09:55:40+02:00
New Revision: eec9431d37e22a627c78e2f345d41a9a40e0c1c0

URL: 
https://github.com/llvm/llvm-project/commit/eec9431d37e22a627c78e2f345d41a9a40e0c1c0
DIFF: 
https://github.com/llvm/llvm-project/commit/eec9431d37e22a627c78e2f345d41a9a40e0c1c0.diff

LOG: [Clang] Added explanation why a is trivial copyable evaluated to false. 
(#142341)

Added: 
    

Modified: 
    clang/include/clang/Basic/DiagnosticSemaKinds.td
    clang/lib/Sema/SemaTypeTraits.cpp
    clang/test/SemaCXX/type-traits-unsatisfied-diags-std.cpp
    clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 8ba3bb099d741..e963612f387ef 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
@@ -1774,8 +1775,10 @@ def note_unsatisfied_trait_reason
            "%HasArcLifetime{has an ARC lifetime qualifier}|"
            "%VLA{is a variably-modified type}|"
            "%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}|"
+           "%NTRBase{has a non-trivially-relocatable base %1}|"
+           "%NTRField{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 b849bbe94af62..330f2aa750a09 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"
@@ -1938,6 +1940,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);
 }
 
@@ -2013,15 +2016,15 @@ static void DiagnoseNonTriviallyRelocatableReason(Sema 
&SemaRef,
           << B.getSourceRange();
     if (!SemaRef.IsCXXTriviallyRelocatableType(B.getType()))
       SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
-          << diag::TraitNotSatisfiedReason::NRBase << B.getType()
+          << diag::TraitNotSatisfiedReason::NTRBase << B.getType()
           << B.getSourceRange();
   }
   for (const FieldDecl *Field : D->fields()) {
     if (!Field->getType()->isReferenceType() &&
         !SemaRef.IsCXXTriviallyRelocatableType(Field->getType()))
       SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
-          << diag::TraitNotSatisfiedReason::NRField << Field << 
Field->getType()
-          << Field->getSourceRange();
+          << diag::TraitNotSatisfiedReason::NTRField << Field
+          << Field->getType() << Field->getSourceRange();
   }
   if (D->hasDeletedDestructor())
     SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
@@ -2099,6 +2102,82 @@ 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();
+  }
+  CXXDestructorDecl *Dtr = D->getDestructor();
+  if (D->hasDeletedDestructor() || (Dtr && !Dtr->isTrivial()))
+    SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
+        << diag::TraitNotSatisfiedReason::DeletedDtr
+        << !D->hasDeletedDestructor() << D->getDestructor()->getSourceRange();
+
+  for (const CXXMethodDecl *Method : D->methods()) {
+    if (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)
+          << (IsAssignment ? diag::TraitNotSatisfiedReason::UserProvidedAssign
+                           : diag::TraitNotSatisfiedReason::UserProvidedCtr)
+          << IsMove << Method->getSourceRange();
+      break;
+    }
+    default:
+      break;
+    }
+  }
+}
+
+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;
+
+  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())
@@ -2113,6 +2192,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@-1 {{'int &' is not trivially relocatable}} \
 // expected-note@-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@-1 {{static assertion failed due to requirement 
'std::{{.*}}is_trivially_copyable<int &>::value'}} \
+// expected-note@-1 {{'int &' is not trivially copyable}} \
+// expected-note@-1 {{because it is a reference type}}
+static_assert(std::is_trivially_copyable_v<int&>);
+// expected-error@-1 {{static assertion failed due to requirement 
'std::is_trivially_copyable_v<int &>'}} \
+// expected-note@-1 {{'int &' is not trivially copyable}} \
+// expected-note@-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@-1 {{static assertion failed due to requirement 
'is_trivially_relocatable_v<int &>'}} \
     // expected-note@-1 {{'int &' is not trivially relocatable}} \
     // expected-note@-1 {{because it is a reference type}}
+
+    static_assert(is_trivially_copyable<int&>::value);
+    // expected-error-re@-1 {{static assertion failed due to requirement 
'{{.*}}is_trivially_copyable<int &>::value'}} \
+    // expected-note@-1 {{'int &' is not trivially copyable}} \
+    // expected-note@-1 {{because it is a reference type}}
+    static_assert(is_trivially_copyable_v<int&>);
+    // expected-error@-1 {{static assertion failed due to requirement 
'is_trivially_copyable_v<int &>'}} \
+    // expected-note@-1 {{'int &' is not trivially copyable}} \
+    // expected-note@-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@-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@-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@-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..0256569fcca5f 100644
--- a/clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp
+++ b/clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp
@@ -144,3 +144,149 @@ 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@-1 {{static assertion failed due to requirement 
'__is_trivially_copyable(trivially_copyable::S)'}} \
+// expected-note@-1 {{'S' is not trivially copyable}} \
+// expected-note@-1 {{because it has a virtual base 'B'}} \
+// expected-note@-1 {{because it has a non-trivially-copyable base 'B'}} \
+// expected-note@-1 {{because it has a non-trivially-copyable member 'c' of 
type 'B'}} \
+// expected-note@-1 {{because it has a non-trivially-copyable member 'b' of 
type 'B &'}} \
+// expected-note@-1 {{because it has a non-trivially-copyable member 'a' of 
type 'int &'}} \
+// expected-note@-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@-1 {{static assertion failed due to requirement 
'__is_trivially_copyable(trivially_copyable::S2)'}} \
+// expected-note@-1 {{'S2' is not trivially copyable}} \
+// expected-note@-1 {{because it has a user provided move constructor}} \
+// expected-note@-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));
+
+struct S4 { // #tc-S4
+    ~S4();
+    B b;
+};
+static_assert(__is_trivially_copyable(S4));
+// expected-error@-1 {{static assertion failed due to requirement 
'__is_trivially_copyable(trivially_copyable::S4)'}} \
+// expected-note@-1 {{'S4' is not trivially copyable}} \
+// expected-note@-1 {{because it has a non-trivially-copyable member 'b' of 
type 'B'}} \
+// expected-note@-1 {{because it has a user-provided destructor}} \
+// expected-note@#tc-S4 {{'S4' defined here}}
+
+struct B1 {
+    int & a;
+};
+
+struct B2 {
+    int & a;
+};
+
+struct S5 : virtual B1, virtual B2 { // #tc-S5
+};
+static_assert(__is_trivially_copyable(S5));
+// expected-error@-1 {{static assertion failed due to requirement 
'__is_trivially_copyable(trivially_copyable::S5)'}} \
+// expected-note@-1 {{'S5' is not trivially copyable}} \
+// expected-note@-1 {{because it has a virtual base 'B1'}} \
+// expected-note@-1 {{because it has a virtual base 'B2'}} \
+// expected-note@#tc-S5 {{'S5' defined here}}
+
+struct B3 {
+    ~B3();
+};
+
+struct B4 {
+    ~B4();
+};
+
+struct S6 : B3, B4 { // #tc-S6
+};
+static_assert(__is_trivially_copyable(S6));
+// expected-error@-1 {{static assertion failed due to requirement 
'__is_trivially_copyable(trivially_copyable::S6)'}} \
+// expected-note@-1 {{because it has a non-trivially-copyable base 'B3'}} \
+// expected-note@-1 {{because it has a non-trivially-copyable base 'B4'}} \
+// expected-note@-1 {{because it has a user-provided destructor}} \
+// expected-note@-1 {{'S6' is not trivially copyable}} \
+// expected-note@#tc-S6 {{'S6' defined here}}
+
+struct S7 { // #tc-S7
+    S7(const S7&);
+};
+static_assert(__is_trivially_copyable(S7));
+// expected-error@-1 {{static assertion failed due to requirement 
'__is_trivially_copyable(trivially_copyable::S7)'}} \
+// expected-note@-1 {{because it has a user provided copy constructor}} \
+// expected-note@-1 {{'S7' is not trivially copyable}} \
+// expected-note@#tc-S7 {{'S7' defined here}}
+
+struct S8 { // #tc-S8
+    S8(S8&&);
+};
+static_assert(__is_trivially_copyable(S8));
+// expected-error@-1 {{static assertion failed due to requirement 
'__is_trivially_copyable(trivially_copyable::S8)'}} \
+// expected-note@-1 {{because it has a user provided move constructor}} \
+// expected-note@-1 {{'S8' is not trivially copyable}} \
+// expected-note@#tc-S8 {{'S8' defined here}}
+
+struct S9 { // #tc-S9
+    S9& operator=(const S9&);
+};
+static_assert(__is_trivially_copyable(S9));
+// expected-error@-1 {{static assertion failed due to requirement 
'__is_trivially_copyable(trivially_copyable::S9)'}} \
+// expected-note@-1 {{because it has a user provided copy assignment 
operator}} \
+// expected-note@-1 {{'S9' is not trivially copyable}} \
+// expected-note@#tc-S9 {{'S9' defined here}}
+
+struct S10 { // #tc-S10
+    S10& operator=(S10&&);
+};
+static_assert(__is_trivially_copyable(S10));
+// expected-error@-1 {{static assertion failed due to requirement 
'__is_trivially_copyable(trivially_copyable::S10)'}} \
+// expected-note@-1 {{because it has a user provided move assignment 
operator}} \
+// expected-note@-1 {{'S10' is not trivially copyable}} \
+// expected-note@#tc-S10 {{'S10' defined here}}
+
+struct B5 : B4 {
+};
+
+struct B6 : B5  {
+};
+
+struct S11 : B6 { // #tc-S11
+};
+static_assert(__is_trivially_copyable(S11));
+// expected-error@-1 {{static assertion failed due to requirement 
'__is_trivially_copyable(trivially_copyable::S11)'}} \
+// expected-note@-1 {{because it has a non-trivially-copyable base 'B6'}} \
+// expected-note@-1 {{'S11' is not trivially copyable}} \
+// expected-note@#tc-S11 {{'S11' defined here}}
+
+struct S12 : B6 { // #tc-S12
+    ~S12() = delete;
+};
+static_assert(__is_trivially_copyable(S12));
+// expected-error@-1 {{static assertion failed due to requirement 
'__is_trivially_copyable(trivially_copyable::S12)'}} \
+// expected-note@-1 {{because it has a non-trivially-copyable base 'B6'}} \
+// expected-note@-1 {{because it has a deleted destructor}} \
+// expected-note@-1 {{'S12' is not trivially copyable}} \
+// expected-note@#tc-S12 {{'S12' defined here}}
+}


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

Reply via email to