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 <shamshurae...@gmail.com> Date: Mon, 2 Jun 2025 07:25:26 +0000 Subject: [PATCH 1/7] [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@-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..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@-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)); + +union U { // #tc-U + U(const U&); + U(U&&); + U& operator=(const U&); + U& operator=(U&&); +}; +static_assert(__is_trivially_copyable(U)); +// expected-error@-1 {{static assertion failed due to requirement '__is_trivially_copyable(trivially_copyable::U)'}} \ +// expected-note@-1 {{'U' is not trivially copyable}} \ +// expected-note@-1 {{because it is a union with a user-declared copy constructor}} \ +// expected-note@-1 {{because it is a union with a user-declared copy assignment operator}} \ +// expected-note@-1 {{because it is a union with a user-declared move constructor}} \ +// expected-note@-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@-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}} + +union U2 { // #tc-U2 + U2(const U2&); + U2(U2&&); + B b; +}; +static_assert(__is_trivially_copyable(U2)); +// expected-error@-1 {{static assertion failed due to requirement '__is_trivially_copyable(trivially_copyable::U2)'}} \ +// expected-note@-1 {{'U2' is not trivially copyable}} \ +// expected-note@-1 {{because it is a union with a user-declared copy constructor}} \ +// expected-note@-1 {{because it is a union with a user-declared move constructor}} \ +// expected-note@-1 {{because it has a deleted destructor}} \ +// expected-note@-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 <shamshurae...@gmail.com> Date: Mon, 2 Jun 2025 09:41:32 +0000 Subject: [PATCH 2/7] 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@-1 {{static assertion failed due to requirement '__is_trivially_copyable(trivially_copyable::U)'}} \ -// expected-note@-1 {{'U' is not trivially copyable}} \ -// expected-note@-1 {{because it is a union with a user-declared copy constructor}} \ -// expected-note@-1 {{because it is a union with a user-declared copy assignment operator}} \ -// expected-note@-1 {{because it is a union with a user-declared move constructor}} \ -// expected-note@-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@-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}} - -union U2 { // #tc-U2 - U2(const U2&); - U2(U2&&); - B b; -}; -static_assert(__is_trivially_copyable(U2)); -// expected-error@-1 {{static assertion failed due to requirement '__is_trivially_copyable(trivially_copyable::U2)'}} \ -// expected-note@-1 {{'U2' is not trivially copyable}} \ -// expected-note@-1 {{because it is a union with a user-declared copy constructor}} \ -// expected-note@-1 {{because it is a union with a user-declared move constructor}} \ -// expected-note@-1 {{because it has a deleted destructor}} \ -// expected-note@-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 <shamshurae...@gmail.com> Date: Mon, 2 Jun 2025 11:49:47 +0000 Subject: [PATCH 3/7] 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()) >From 9880d4f52c7d2d62fafc2732f7fb429485802465 Mon Sep 17 00:00:00 2001 From: Shamshura Egor <shamshurae...@gmail.com> Date: Mon, 2 Jun 2025 11:59:09 +0000 Subject: [PATCH 4/7] Removed Arc lifetime --- clang/lib/Sema/SemaTypeTraits.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/clang/lib/Sema/SemaTypeTraits.cpp b/clang/lib/Sema/SemaTypeTraits.cpp index 75dc0558425e2..8612c2f9c8977 100644 --- a/clang/lib/Sema/SemaTypeTraits.cpp +++ b/clang/lib/Sema/SemaTypeTraits.cpp @@ -2158,12 +2158,6 @@ static void DiagnoseNonTriviallyCopyableReason(Sema &SemaRef, 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; >From 2401565e26aa2677fda9d66b23dea414510b9814 Mon Sep 17 00:00:00 2001 From: Shamshura Egor <shamshurae...@gmail.com> Date: Mon, 2 Jun 2025 14:38:01 +0000 Subject: [PATCH 5/7] Changed NR to NTR --- clang/include/clang/Basic/DiagnosticSemaKinds.td | 4 ++-- clang/lib/Sema/SemaTypeTraits.cpp | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 748e0720c5ef5..fd1be3aafb8aa 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -1775,8 +1775,8 @@ 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}|" diff --git a/clang/lib/Sema/SemaTypeTraits.cpp b/clang/lib/Sema/SemaTypeTraits.cpp index 8612c2f9c8977..f053934ab8eeb 100644 --- a/clang/lib/Sema/SemaTypeTraits.cpp +++ b/clang/lib/Sema/SemaTypeTraits.cpp @@ -2000,15 +2000,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) >From 94bec75372ff4be437041442153ae8440b00d5fa Mon Sep 17 00:00:00 2001 From: Shamshura Egor <shamshurae...@gmail.com> Date: Mon, 2 Jun 2025 17:09:14 +0000 Subject: [PATCH 6/7] Added tests for non-trvial-copyable bases, for special members --- .../SemaCXX/type-traits-unsatisfied-diags.cpp | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp b/clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp index dc2a4b4adb9d0..f84d25d7b4ac2 100644 --- a/clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp +++ b/clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp @@ -194,4 +194,75 @@ static_assert(__is_trivially_copyable(S4)); // 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}} } >From 8636714f7df406ec9d1e62c69b06031ae65a0df4 Mon Sep 17 00:00:00 2001 From: Shamshura Egor <shamshurae...@gmail.com> Date: Mon, 2 Jun 2025 18:08:34 +0000 Subject: [PATCH 7/7] Removed extra isIneligibleOrNotSelected check --- clang/lib/Sema/SemaTypeTraits.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/clang/lib/Sema/SemaTypeTraits.cpp b/clang/lib/Sema/SemaTypeTraits.cpp index f053934ab8eeb..ba41e4b125a9e 100644 --- a/clang/lib/Sema/SemaTypeTraits.cpp +++ b/clang/lib/Sema/SemaTypeTraits.cpp @@ -2113,8 +2113,7 @@ static void DiagnoseNonTriviallyCopyableReason(Sema &SemaRef, << D->getDestructor()->getSourceRange(); for (const CXXMethodDecl *Method : D->methods()) { - if (Method->isIneligibleOrNotSelected() || Method->isTrivial() || - !Method->isUserProvided()) { + if (Method->isTrivial() || !Method->isUserProvided()) { continue; } auto SpecialMemberKind = _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits