Author: Shamshura Egor
Date: 2025-09-03T06:23:59-07:00
New Revision: 44b779526036a62aa2f7c5285a51f5c043805c33

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

LOG: [Clang] Add detailed notes explaining why is_aggregate evaluates to false 
(#152488)

This PR is part of
[#141911](https://github.com/llvm/llvm-project/issues/141911)

---------

Co-authored-by: Erich Keane <eke...@nvidia.com>

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 0f3aa9aea215f..3146f20da1424 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -1773,6 +1773,7 @@ def note_unsatisfied_trait
            "%TriviallyCopyable{trivially copyable}|"
            "%Empty{empty}|"
            "%StandardLayout{standard-layout}|"
+           "%Aggregate{aggregate}|"
            "%Final{final}"
            "}1">;
 
@@ -1794,6 +1795,7 @@ def note_unsatisfied_trait_reason
            "%NTCBase{has a non-trivially-copyable base %1}|"
            "%NTCField{has a non-trivially-copyable member %1 of type %2}|"
            "%NonEmptyMember{has a non-static data member %1 of type %2}|"
+           "%PolymorphicType{is a polymorphic type}|"
            "%VirtualFunction{has a virtual function %1}|"
            "%NonEmptyBase{has a base class %1 that is not empty}|"
            "%NonZeroLengthField{field %1 is a non-zero-length bit-field}|"
@@ -1806,6 +1808,8 @@ def note_unsatisfied_trait_reason
            "%DeletedDtr{has a %select{deleted|user-provided}1 destructor}|"
            "%UserProvidedCtr{has a user provided %select{copy|move}1 "
            "constructor}|"
+           "%UserDeclaredCtr{has a user-declared constructor}|"
+           "%InheritedCtr{has an inherited constructor}|"
            "%DeletedCtr{has a deleted %select{copy|move}1 "
            "constructor}|"
            "%UserProvidedAssign{has a user provided %select{copy|move}1 "
@@ -1817,6 +1821,8 @@ def note_unsatisfied_trait_reason
            "%FunctionType{is a function type}|"
            "%CVVoidType{is a cv void type}|"
            "%IncompleteArrayType{is an incomplete array type}|"
+           "%PrivateProtectedDirectDataMember{has a 
%select{private|protected}1 direct data member}|"
+           "%PrivateProtectedDirectBase{has a %select{private|protected}1 
direct base}|"
            "%NotClassOrUnion{is not a class or union type}|"
            "%NotMarkedFinal{is not marked 'final'}"
            "}0">;

diff  --git a/clang/lib/Sema/SemaTypeTraits.cpp 
b/clang/lib/Sema/SemaTypeTraits.cpp
index b77975945c18c..1ca769ebb50f0 100644
--- a/clang/lib/Sema/SemaTypeTraits.cpp
+++ b/clang/lib/Sema/SemaTypeTraits.cpp
@@ -16,6 +16,7 @@
 #include "clang/Basic/DiagnosticIDs.h"
 #include "clang/Basic/DiagnosticParse.h"
 #include "clang/Basic/DiagnosticSema.h"
+#include "clang/Basic/Specifiers.h"
 #include "clang/Basic/TypeTraits.h"
 #include "clang/Sema/EnterExpressionEvaluationContext.h"
 #include "clang/Sema/Initialization.h"
@@ -23,6 +24,7 @@
 #include "clang/Sema/Overload.h"
 #include "clang/Sema/Sema.h"
 #include "clang/Sema/SemaHLSL.h"
+#include "llvm/ADT/STLExtras.h"
 
 using namespace clang;
 
@@ -2009,6 +2011,7 @@ static std::optional<TypeTrait> 
StdNameToTypeTrait(StringRef Name) {
       .Case("is_assignable", TypeTrait::BTT_IsAssignable)
       .Case("is_empty", TypeTrait::UTT_IsEmpty)
       .Case("is_standard_layout", TypeTrait::UTT_IsStandardLayout)
+      .Case("is_aggregate", TypeTrait::UTT_IsAggregate)
       .Case("is_constructible", TypeTrait::TT_IsConstructible)
       .Case("is_final", TypeTrait::UTT_IsFinal)
       .Default(std::nullopt);
@@ -2685,6 +2688,92 @@ static void DiagnoseNonStandardLayoutReason(Sema 
&SemaRef, SourceLocation Loc,
   SemaRef.Diag(D->getLocation(), diag::note_defined_here) << D;
 }
 
+static void DiagnoseNonAggregateReason(Sema &SemaRef, SourceLocation Loc,
+                                       const CXXRecordDecl *D) {
+  for (const CXXConstructorDecl *Ctor : D->ctors()) {
+    if (Ctor->isUserProvided())
+      SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
+          << diag::TraitNotSatisfiedReason::UserDeclaredCtr;
+    if (Ctor->isInheritingConstructor())
+      SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
+          << diag::TraitNotSatisfiedReason::InheritedCtr;
+  }
+
+  if (llvm::any_of(D->decls(), [](auto const *Sub) {
+        return isa<ConstructorUsingShadowDecl>(Sub);
+      })) {
+    SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
+        << diag::TraitNotSatisfiedReason::InheritedCtr;
+  }
+
+  if (D->isPolymorphic())
+    SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
+        << diag::TraitNotSatisfiedReason::PolymorphicType
+        << D->getSourceRange();
+
+  for (const CXXBaseSpecifier &B : D->bases()) {
+    if (B.isVirtual()) {
+      SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
+          << diag::TraitNotSatisfiedReason::VBase << B.getType()
+          << B.getSourceRange();
+      continue;
+    }
+    auto AccessSpecifier = B.getAccessSpecifier();
+    switch (AccessSpecifier) {
+    case AS_private:
+    case AS_protected:
+      SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
+          << diag::TraitNotSatisfiedReason::PrivateProtectedDirectBase
+          << (AccessSpecifier == AS_protected);
+      break;
+    default:
+      break;
+    }
+  }
+
+  for (const CXXMethodDecl *Method : D->methods()) {
+    if (Method->isVirtual()) {
+      SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
+          << diag::TraitNotSatisfiedReason::VirtualFunction << Method
+          << Method->getSourceRange();
+    }
+  }
+
+  for (const FieldDecl *Field : D->fields()) {
+    auto AccessSpecifier = Field->getAccess();
+    switch (AccessSpecifier) {
+    case AS_private:
+    case AS_protected:
+      SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
+          << diag::TraitNotSatisfiedReason::PrivateProtectedDirectDataMember
+          << (AccessSpecifier == AS_protected);
+      break;
+    default:
+      break;
+    }
+  }
+
+  SemaRef.Diag(D->getLocation(), diag::note_defined_here) << D;
+}
+
+static void DiagnoseNonAggregateReason(Sema &SemaRef, SourceLocation Loc,
+                                       QualType T) {
+  SemaRef.Diag(Loc, diag::note_unsatisfied_trait)
+      << T << diag::TraitName::Aggregate;
+
+  if (T->isVoidType())
+    SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
+        << diag::TraitNotSatisfiedReason::CVVoidType;
+
+  T = T.getNonReferenceType();
+  const CXXRecordDecl *D = T->getAsCXXRecordDecl();
+  if (!D || D->isInvalidDecl())
+    return;
+
+  if (D->hasDefinition())
+    DiagnoseNonAggregateReason(SemaRef, Loc, D);
+}
+
 void Sema::DiagnoseTypeTraitDetails(const Expr *E) {
   E = E->IgnoreParenImpCasts();
   if (E->containsErrors())
@@ -2717,6 +2806,9 @@ void Sema::DiagnoseTypeTraitDetails(const Expr *E) {
   case TT_IsConstructible:
     DiagnoseNonConstructibleReason(*this, E->getBeginLoc(), Args);
     break;
+  case UTT_IsAggregate:
+    DiagnoseNonAggregateReason(*this, E->getBeginLoc(), Args[0]);
+    break;
   case UTT_IsFinal: {
     QualType QT = Args[0];
     if (QT->isDependentType())

diff  --git a/clang/test/SemaCXX/type-traits-unsatisfied-diags-std.cpp 
b/clang/test/SemaCXX/type-traits-unsatisfied-diags-std.cpp
index 7c6c9ea4dde80..ba9fe3d24a7e6 100644
--- a/clang/test/SemaCXX/type-traits-unsatisfied-diags-std.cpp
+++ b/clang/test/SemaCXX/type-traits-unsatisfied-diags-std.cpp
@@ -51,6 +51,14 @@ struct is_constructible {
 template <typename... Args>
 constexpr bool is_constructible_v = __is_constructible(Args...);
 
+template <typename T>
+struct is_aggregate {
+    static constexpr bool value = __is_aggregate(T);
+};
+
+template <typename T>
+constexpr bool is_aggregate_v = __is_aggregate(T);
+
 template <typename T>
 struct is_final {
     static constexpr bool value = __is_final(T);
@@ -96,7 +104,7 @@ constexpr bool is_assignable_v = __is_assignable(T, U);
 
 template <typename T>
 struct __details_is_empty {
-    static constexpr bool value = __is_empty(T);
+  static constexpr bool value = __is_empty(T);
 };
 template <typename T>
 using is_empty  = __details_is_empty<T>;
@@ -105,9 +113,7 @@ constexpr bool is_empty_v = __is_empty(T);
 
 template <typename T>
 struct __details_is_standard_layout {
-static constexpr bool value = __is_standard_layout(T);
-
-
+    static constexpr bool value = __is_standard_layout(T);
 };
 template <typename T>
 using is_standard_layout = __details_is_standard_layout<T>;
@@ -125,6 +131,17 @@ using is_constructible  = 
__details_is_constructible<Args...>;
 template <typename... Args>
 constexpr bool is_constructible_v = __is_constructible(Args...);
 
+template <typename T>
+struct __details_is_aggregate {
+    static constexpr bool value = __is_aggregate(T);
+};
+
+template <typename T>
+using is_aggregate  = __details_is_aggregate<T>;
+
+template <typename T>
+constexpr bool is_aggregate_v = __is_aggregate(T);
+
 template <typename T>
 struct __details_is_final {
     static constexpr bool value = __is_final(T);
@@ -191,11 +208,20 @@ template <typename... Args>
 struct __details_is_constructible : bool_constant<__is_constructible(Args...)> 
{};
 
 template <typename... Args>
-using is_constructible  = __details_is_constructible<Args...>;
+using is_constructible = __details_is_constructible<Args...>;
 
 template <typename... Args>
 constexpr bool is_constructible_v = is_constructible<Args...>::value;
 
+template <typename T>
+struct __details_is_aggregate : bool_constant<__is_aggregate(T)> {};
+
+template <typename T>
+using is_aggregate = __details_is_aggregate<T>;
+
+template <typename T>
+constexpr bool is_aggregate_v = is_aggregate<T>::value;
+
 template <typename T>
 struct __details_is_final : bool_constant<__is_final(T)> {};
 template <typename T>
@@ -204,7 +230,6 @@ template <typename T>
 constexpr bool is_final_v = is_final<T>::value;
 
 #endif
-
 }
 
 static_assert(std::is_trivially_relocatable<int>::value);
@@ -299,6 +324,18 @@ static_assert(std::is_final_v<Arr>);
 // expected-note@-1 {{'int[3]' is not final}} \
 // expected-note@-1 {{because it is not a class or union type}}
 
+
+static_assert(!std::is_aggregate<int>::value);
+
+static_assert(std::is_aggregate<void>::value);
+// expected-error-re@-1 {{static assertion failed due to requirement 
'std::{{.*}}is_aggregate<void>::value'}} \
+// expected-note@-1 {{'void' is not aggregate}} \
+// expected-note@-1 {{because it is a cv void type}}
+static_assert(std::is_aggregate_v<void>);
+// expected-error@-1 {{static assertion failed due to requirement 
'std::is_aggregate_v<void>'}} \
+// expected-note@-1 {{'void' is not aggregate}} \
+// expected-note@-1 {{because it is a cv void type}}
+
 namespace test_namespace {
     using namespace std;
     static_assert(is_trivially_relocatable<int&>::value);
@@ -351,6 +388,15 @@ namespace test_namespace {
     static_assert(is_constructible_v<void>);
     // expected-error@-1 {{static assertion failed due to requirement 
'is_constructible_v<void>'}} \
     // expected-note@-1 {{because it is a cv void type}}
+    
+    static_assert(std::is_aggregate<void>::value);
+    // expected-error-re@-1 {{static assertion failed due to requirement 
'std::{{.*}}is_aggregate<void>::value'}} \
+    // expected-note@-1 {{'void' is not aggregate}} \
+    // expected-note@-1 {{because it is a cv void type}}
+    static_assert(std::is_aggregate_v<void>);
+    // expected-error@-1 {{static assertion failed due to requirement 
'std::is_aggregate_v<void>'}} \
+    // expected-note@-1 {{'void' is not aggregate}} \
+    // expected-note@-1 {{because it is a cv void type}}
 
     static_assert(is_final<int&>::value);
     // expected-error-re@-1 {{static assertion failed due to requirement 
'{{.*}}is_final<int &>::value'}} \
@@ -413,6 +459,14 @@ concept C3 = std::is_constructible_v<Args...>; // #concept6
 template <C3 T> void g3();  // #cand6
 
 
+template <typename T>
+requires std::is_aggregate<T>::value void f5();  // #cand9
+
+template <typename T>
+concept C5 = std::is_aggregate_v<T>; // #concept10
+
+template <C5 T> void g5();  // #cand10
+
 void test() {
     f<int&>();
     // expected-error@-1 {{no matching function for call to 'f'}} \
@@ -469,6 +523,21 @@ void test() {
     // expected-note@#cand6 {{because 'void' does not satisfy 'C3'}} \
     // expected-note@#concept6 {{because 'std::is_constructible_v<void>' 
evaluated to false}} \
     // expected-note@#concept6 {{because it is a cv void type}}
+
+    f5<void>();
+    // expected-error@-1 {{no matching function for call to 'f5'}} \
+    // expected-note@#cand9 {{candidate template ignored: constraints not 
satisfied [with T = void]}} \
+    // expected-note-re@#cand9 {{because '{{.*}}is_aggregate<void>::value' 
evaluated to false}} \
+    // expected-note@#cand9 {{'void' is not aggregate}} \
+    // expected-note@#cand9 {{because it is a cv void type}}
+
+    g5<void>();
+    // expected-error@-1 {{no matching function for call to 'g5'}} \
+    // expected-note@#cand10 {{candidate template ignored: constraints not 
satisfied [with T = void]}} \
+    // expected-note@#cand10 {{because 'void' does not satisfy 'C5'}} \
+    // expected-note@#concept10 {{because 'std::is_aggregate_v<void>' 
evaluated to false}} \
+    // expected-note@#concept10 {{'void' is not aggregate}} \
+    // expected-note@#concept10 {{because it is a cv void type}}
 }
 }
 

diff  --git a/clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp 
b/clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp
index 1619b0b22b85f..6f504f2c8b931 100644
--- a/clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp
+++ b/clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp
@@ -872,3 +872,95 @@ namespace is_final_tests {
     // expected-note@-1 {{because it is not a class or union type}}
 
 }
+namespace is_aggregate {
+  struct S1 {  // #ag-S1
+    S1(int);
+  };
+
+  static_assert(__is_aggregate(S1));
+  // expected-error@-1 {{static assertion failed due to requirement 
'__is_aggregate(is_aggregate::S1)'}} \
+  // expected-note@-1 {{because it has a user-declared constructor}} \
+  // expected-note@-1 {{'S1' is not aggregate}} \
+  // expected-note@#ag-S1 {{'S1' defined here}}
+
+  struct B2 {
+    B2(int);
+  };
+
+  struct S2 : B2 { // #ag-S2
+    using B2::B2;
+  };
+
+  static_assert(__is_aggregate(S2));
+  // expected-error@-1 {{static assertion failed due to requirement 
'__is_aggregate(is_aggregate::S2)'}} \
+  // expected-note@-1 {{because it has an inherited constructor}} \
+  // expected-note@-1 {{'S2' is not aggregate}} \
+  // expected-note@#ag-S2 {{'S2' defined here}}
+
+  struct S3 { // #ag-S3
+    protected:
+      int x;
+  };
+  static_assert(__is_aggregate(S3));
+  // expected-error@-1 {{static assertion failed due to requirement 
'__is_aggregate(is_aggregate::S3)'}} \
+  // expected-note@-1 {{because it has a protected direct data member}} \
+  // expected-note@-1 {{'S3' is not aggregate}} \
+  // expected-note@#ag-S3 {{'S3' defined here}}
+
+  struct S4 { // #ag-S4
+    private:
+      int x;
+  };
+  static_assert(__is_aggregate(S4));
+  // expected-error@-1 {{static assertion failed due to requirement 
'__is_aggregate(is_aggregate::S4)'}} \
+  // expected-note@-1 {{because it has a private direct data member}} \
+  // expected-note@-1 {{'S4' is not aggregate}} \
+  // expected-note@#ag-S4 {{'S4' defined here}}
+
+  struct B5 {};
+
+  struct S5 : private B5 { // #ag-S5
+    private:
+      int x;
+  };
+  static_assert(__is_aggregate(S5));
+  // expected-error@-1 {{static assertion failed due to requirement 
'__is_aggregate(is_aggregate::S5)'}} \
+  // expected-note@-1 {{because it has a private direct base}} \
+  // expected-note@-1 {{because it has a private direct data member}} \
+  // expected-note@-1 {{'S5' is not aggregate}} \
+  // expected-note@#ag-S5 {{'S5' defined here}}
+
+  struct B6 {};
+
+  struct S6 : protected B6 { // #ag-S6
+    private:
+      int x;
+  };
+  static_assert(__is_aggregate(S6));
+  // expected-error@-1 {{static assertion failed due to requirement 
'__is_aggregate(is_aggregate::S6)'}} \
+  // expected-note@-1 {{because it has a protected direct base}} \
+  // expected-note@-1 {{because it has a private direct data member}} \
+  // expected-note@-1 {{'S6' is not aggregate}} \
+  // expected-note@#ag-S6 {{'S6' defined here}}
+
+  static_assert(__is_aggregate(S6[]));
+
+  struct B7 {};
+
+  struct S7 : virtual B7 { // #ag-S7
+    virtual void foo();
+
+    private:
+      int x;
+  };
+  static_assert(__is_aggregate(S7));
+  // expected-error@-1 {{static assertion failed due to requirement 
'__is_aggregate(is_aggregate::S7)'}} \
+  // expected-note@-1 {{because it has a private direct data member}} \
+  // expected-note@-1 {{because it has a virtual function 'foo'}} \
+  // expected-note@-1 {{because it has a virtual base}} \
+  // expected-note@-1 {{'S7' is not aggregate}} \
+  // expected-note@-1 {{because it is a polymorphic type}} \
+  // expected-note@#ag-S7 {{'S7' defined here}}
+
+  static_assert(__is_aggregate(S7[10]));
+}


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

Reply via email to