https://github.com/egorshamshura updated https://github.com/llvm/llvm-project/pull/152488
>From 5a49d52bf57474c3e2f65058c8fbd1203934524b Mon Sep 17 00:00:00 2001 From: Shamshura Egor <shamshurae...@gmail.com> Date: Tue, 5 Aug 2025 11:22:32 +0000 Subject: [PATCH 1/3] merged --- clang/lib/Sema/SemaTypeTraits.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/clang/lib/Sema/SemaTypeTraits.cpp b/clang/lib/Sema/SemaTypeTraits.cpp index 1d8687e4bf1c1..2406b517e56a5 100644 --- a/clang/lib/Sema/SemaTypeTraits.cpp +++ b/clang/lib/Sema/SemaTypeTraits.cpp @@ -1965,6 +1965,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) .Default(std::nullopt); } @@ -2595,6 +2596,11 @@ static void DiagnoseNonStandardLayoutReason(Sema &SemaRef, SourceLocation Loc, 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; +} + void Sema::DiagnoseTypeTraitDetails(const Expr *E) { E = E->IgnoreParenImpCasts(); if (E->containsErrors()) @@ -2627,6 +2633,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; default: break; } >From a6b90f38d67d8aa1319ceb4dc0b14fd75d14aaae Mon Sep 17 00:00:00 2001 From: Shamshura Egor <shamshurae...@gmail.com> Date: Thu, 7 Aug 2025 12:36:37 +0000 Subject: [PATCH 2/3] Added diagnostic for is_aggregate. --- .../clang/Basic/DiagnosticSemaKinds.td | 11 ++- clang/lib/Sema/SemaTypeTraits.cpp | 70 +++++++++++++++ .../type-traits-unsatisfied-diags-std.cpp | 79 +++++++++++++++-- .../SemaCXX/type-traits-unsatisfied-diags.cpp | 87 +++++++++++++++++++ 4 files changed, 239 insertions(+), 8 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index f903b7f4dacd0..65cb5e809cce0 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -1772,7 +1772,8 @@ def note_unsatisfied_trait "%Replaceable{replaceable}|" "%TriviallyCopyable{trivially copyable}|" "%Empty{empty}|" - "%StandardLayout{standard-layout}" + "%StandardLayout{standard-layout}|" + "%Aggregate{aggregate}" "}1">; def note_unsatisfied_trait_reason @@ -1805,6 +1806,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 " @@ -1815,7 +1818,11 @@ def note_unsatisfied_trait_reason "%sub{select_special_member_kind}1}|" "%FunctionType{is a function type}|" "%CVVoidType{is a cv void type}|" - "%IncompleteArrayType{is an incomplete array type}" + "%IncompleteArrayType{is an incomplete array type}|" + "%PrivateDirectDataMember{has a private direct data member}|" + "%ProtectedDirectDataMember{has a protected direct data member}|" + "%PrivateDirectBase{has a private direct base}|" + "%ProtectedDirectBase{has a protected direct base}" "}0">; def warn_consteval_if_always_true : Warning< diff --git a/clang/lib/Sema/SemaTypeTraits.cpp b/clang/lib/Sema/SemaTypeTraits.cpp index 2406b517e56a5..868de469d91c8 100644 --- a/clang/lib/Sema/SemaTypeTraits.cpp +++ b/clang/lib/Sema/SemaTypeTraits.cpp @@ -2596,9 +2596,79 @@ 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; + } + + for (const FieldDecl *Field : D->fields()) { + switch (Field->getAccess()) { + case AS_private: + SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason) + << diag::TraitNotSatisfiedReason::PrivateDirectDataMember; + break; + case AS_protected: + SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason) + << diag::TraitNotSatisfiedReason::ProtectedDirectDataMember; + break; + default: + break; + } + } + + 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; + } + switch (B.getAccessSpecifier()) { + case AS_private: + SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason) + << diag::TraitNotSatisfiedReason::PrivateDirectBase; + break; + case AS_protected: + SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason) + << diag::TraitNotSatisfiedReason::ProtectedDirectBase; + 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(); + } + } + + 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) { diff --git a/clang/test/SemaCXX/type-traits-unsatisfied-diags-std.cpp b/clang/test/SemaCXX/type-traits-unsatisfied-diags-std.cpp index f3ddbbfe15bdc..aaf19ce67ac50 100644 --- a/clang/test/SemaCXX/type-traits-unsatisfied-diags-std.cpp +++ b/clang/test/SemaCXX/type-traits-unsatisfied-diags-std.cpp @@ -50,6 +50,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); #endif #ifdef STD2 @@ -88,7 +96,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>; @@ -97,9 +105,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>; @@ -116,6 +122,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); #endif @@ -173,12 +190,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; -#endif +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; +#endif } static_assert(std::is_trivially_relocatable<int>::value); @@ -248,6 +273,16 @@ static_assert(std::is_constructible_v<void>); // expected-error@-1 {{static assertion failed due to requirement 'std::is_constructible_v<void>'}} \ // expected-note@-1 {{because it is a cv void 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); @@ -300,6 +335,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}} } @@ -337,6 +381,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'}} \ @@ -393,6 +445,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 54806a93ddf80..7612b46994699 100644 --- a/clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp +++ b/clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp @@ -829,3 +829,90 @@ static_assert(__is_standard_layout(H)); // no diagnostics static_assert(__is_standard_layout(I)); // no diagnostics } +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}} + + 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@#ag-S7 {{'S7' defined here}} +} >From f2088018dc86a1ef06a1dce1bc936c51fb5f5084 Mon Sep 17 00:00:00 2001 From: Shamshura Egor <shamshurae...@gmail.com> Date: Thu, 7 Aug 2025 12:48:44 +0000 Subject: [PATCH 3/3] Clang-formatted --- clang/lib/Sema/SemaTypeTraits.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/clang/lib/Sema/SemaTypeTraits.cpp b/clang/lib/Sema/SemaTypeTraits.cpp index 868de469d91c8..a0b5d6284d277 100644 --- a/clang/lib/Sema/SemaTypeTraits.cpp +++ b/clang/lib/Sema/SemaTypeTraits.cpp @@ -2654,7 +2654,8 @@ static void DiagnoseNonAggregateReason(Sema &SemaRef, SourceLocation Loc, SemaRef.Diag(D->getLocation(), diag::note_defined_here) << D; } -static void DiagnoseNonAggregateReason(Sema &SemaRef, SourceLocation Loc, QualType T) { +static void DiagnoseNonAggregateReason(Sema &SemaRef, SourceLocation Loc, + QualType T) { SemaRef.Diag(Loc, diag::note_unsatisfied_trait) << T << diag::TraitName::Aggregate; _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits