Skip to content

Commit 44b7795

Browse files
[Clang] Add detailed notes explaining why is_aggregate evaluates to false (#152488)
This PR is part of [#141911](#141911) --------- Co-authored-by: Erich Keane <[email protected]>
1 parent 913d44d commit 44b7795

File tree

4 files changed

+265
-6
lines changed

4 files changed

+265
-6
lines changed

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1773,6 +1773,7 @@ def note_unsatisfied_trait
17731773
"%TriviallyCopyable{trivially copyable}|"
17741774
"%Empty{empty}|"
17751775
"%StandardLayout{standard-layout}|"
1776+
"%Aggregate{aggregate}|"
17761777
"%Final{final}"
17771778
"}1">;
17781779

@@ -1794,6 +1795,7 @@ def note_unsatisfied_trait_reason
17941795
"%NTCBase{has a non-trivially-copyable base %1}|"
17951796
"%NTCField{has a non-trivially-copyable member %1 of type %2}|"
17961797
"%NonEmptyMember{has a non-static data member %1 of type %2}|"
1798+
"%PolymorphicType{is a polymorphic type}|"
17971799
"%VirtualFunction{has a virtual function %1}|"
17981800
"%NonEmptyBase{has a base class %1 that is not empty}|"
17991801
"%NonZeroLengthField{field %1 is a non-zero-length bit-field}|"
@@ -1806,6 +1808,8 @@ def note_unsatisfied_trait_reason
18061808
"%DeletedDtr{has a %select{deleted|user-provided}1 destructor}|"
18071809
"%UserProvidedCtr{has a user provided %select{copy|move}1 "
18081810
"constructor}|"
1811+
"%UserDeclaredCtr{has a user-declared constructor}|"
1812+
"%InheritedCtr{has an inherited constructor}|"
18091813
"%DeletedCtr{has a deleted %select{copy|move}1 "
18101814
"constructor}|"
18111815
"%UserProvidedAssign{has a user provided %select{copy|move}1 "
@@ -1817,6 +1821,8 @@ def note_unsatisfied_trait_reason
18171821
"%FunctionType{is a function type}|"
18181822
"%CVVoidType{is a cv void type}|"
18191823
"%IncompleteArrayType{is an incomplete array type}|"
1824+
"%PrivateProtectedDirectDataMember{has a %select{private|protected}1 direct data member}|"
1825+
"%PrivateProtectedDirectBase{has a %select{private|protected}1 direct base}|"
18201826
"%NotClassOrUnion{is not a class or union type}|"
18211827
"%NotMarkedFinal{is not marked 'final'}"
18221828
"}0">;

clang/lib/Sema/SemaTypeTraits.cpp

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,15 @@
1616
#include "clang/Basic/DiagnosticIDs.h"
1717
#include "clang/Basic/DiagnosticParse.h"
1818
#include "clang/Basic/DiagnosticSema.h"
19+
#include "clang/Basic/Specifiers.h"
1920
#include "clang/Basic/TypeTraits.h"
2021
#include "clang/Sema/EnterExpressionEvaluationContext.h"
2122
#include "clang/Sema/Initialization.h"
2223
#include "clang/Sema/Lookup.h"
2324
#include "clang/Sema/Overload.h"
2425
#include "clang/Sema/Sema.h"
2526
#include "clang/Sema/SemaHLSL.h"
27+
#include "llvm/ADT/STLExtras.h"
2628

2729
using namespace clang;
2830

@@ -2009,6 +2011,7 @@ static std::optional<TypeTrait> StdNameToTypeTrait(StringRef Name) {
20092011
.Case("is_assignable", TypeTrait::BTT_IsAssignable)
20102012
.Case("is_empty", TypeTrait::UTT_IsEmpty)
20112013
.Case("is_standard_layout", TypeTrait::UTT_IsStandardLayout)
2014+
.Case("is_aggregate", TypeTrait::UTT_IsAggregate)
20122015
.Case("is_constructible", TypeTrait::TT_IsConstructible)
20132016
.Case("is_final", TypeTrait::UTT_IsFinal)
20142017
.Default(std::nullopt);
@@ -2685,6 +2688,92 @@ static void DiagnoseNonStandardLayoutReason(Sema &SemaRef, SourceLocation Loc,
26852688
SemaRef.Diag(D->getLocation(), diag::note_defined_here) << D;
26862689
}
26872690

2691+
static void DiagnoseNonAggregateReason(Sema &SemaRef, SourceLocation Loc,
2692+
const CXXRecordDecl *D) {
2693+
for (const CXXConstructorDecl *Ctor : D->ctors()) {
2694+
if (Ctor->isUserProvided())
2695+
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
2696+
<< diag::TraitNotSatisfiedReason::UserDeclaredCtr;
2697+
if (Ctor->isInheritingConstructor())
2698+
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
2699+
<< diag::TraitNotSatisfiedReason::InheritedCtr;
2700+
}
2701+
2702+
if (llvm::any_of(D->decls(), [](auto const *Sub) {
2703+
return isa<ConstructorUsingShadowDecl>(Sub);
2704+
})) {
2705+
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
2706+
<< diag::TraitNotSatisfiedReason::InheritedCtr;
2707+
}
2708+
2709+
if (D->isPolymorphic())
2710+
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
2711+
<< diag::TraitNotSatisfiedReason::PolymorphicType
2712+
<< D->getSourceRange();
2713+
2714+
for (const CXXBaseSpecifier &B : D->bases()) {
2715+
if (B.isVirtual()) {
2716+
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
2717+
<< diag::TraitNotSatisfiedReason::VBase << B.getType()
2718+
<< B.getSourceRange();
2719+
continue;
2720+
}
2721+
auto AccessSpecifier = B.getAccessSpecifier();
2722+
switch (AccessSpecifier) {
2723+
case AS_private:
2724+
case AS_protected:
2725+
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
2726+
<< diag::TraitNotSatisfiedReason::PrivateProtectedDirectBase
2727+
<< (AccessSpecifier == AS_protected);
2728+
break;
2729+
default:
2730+
break;
2731+
}
2732+
}
2733+
2734+
for (const CXXMethodDecl *Method : D->methods()) {
2735+
if (Method->isVirtual()) {
2736+
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
2737+
<< diag::TraitNotSatisfiedReason::VirtualFunction << Method
2738+
<< Method->getSourceRange();
2739+
}
2740+
}
2741+
2742+
for (const FieldDecl *Field : D->fields()) {
2743+
auto AccessSpecifier = Field->getAccess();
2744+
switch (AccessSpecifier) {
2745+
case AS_private:
2746+
case AS_protected:
2747+
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
2748+
<< diag::TraitNotSatisfiedReason::PrivateProtectedDirectDataMember
2749+
<< (AccessSpecifier == AS_protected);
2750+
break;
2751+
default:
2752+
break;
2753+
}
2754+
}
2755+
2756+
SemaRef.Diag(D->getLocation(), diag::note_defined_here) << D;
2757+
}
2758+
2759+
static void DiagnoseNonAggregateReason(Sema &SemaRef, SourceLocation Loc,
2760+
QualType T) {
2761+
SemaRef.Diag(Loc, diag::note_unsatisfied_trait)
2762+
<< T << diag::TraitName::Aggregate;
2763+
2764+
if (T->isVoidType())
2765+
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
2766+
<< diag::TraitNotSatisfiedReason::CVVoidType;
2767+
2768+
T = T.getNonReferenceType();
2769+
const CXXRecordDecl *D = T->getAsCXXRecordDecl();
2770+
if (!D || D->isInvalidDecl())
2771+
return;
2772+
2773+
if (D->hasDefinition())
2774+
DiagnoseNonAggregateReason(SemaRef, Loc, D);
2775+
}
2776+
26882777
void Sema::DiagnoseTypeTraitDetails(const Expr *E) {
26892778
E = E->IgnoreParenImpCasts();
26902779
if (E->containsErrors())
@@ -2717,6 +2806,9 @@ void Sema::DiagnoseTypeTraitDetails(const Expr *E) {
27172806
case TT_IsConstructible:
27182807
DiagnoseNonConstructibleReason(*this, E->getBeginLoc(), Args);
27192808
break;
2809+
case UTT_IsAggregate:
2810+
DiagnoseNonAggregateReason(*this, E->getBeginLoc(), Args[0]);
2811+
break;
27202812
case UTT_IsFinal: {
27212813
QualType QT = Args[0];
27222814
if (QT->isDependentType())

clang/test/SemaCXX/type-traits-unsatisfied-diags-std.cpp

Lines changed: 75 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,14 @@ struct is_constructible {
5151
template <typename... Args>
5252
constexpr bool is_constructible_v = __is_constructible(Args...);
5353

54+
template <typename T>
55+
struct is_aggregate {
56+
static constexpr bool value = __is_aggregate(T);
57+
};
58+
59+
template <typename T>
60+
constexpr bool is_aggregate_v = __is_aggregate(T);
61+
5462
template <typename T>
5563
struct is_final {
5664
static constexpr bool value = __is_final(T);
@@ -96,7 +104,7 @@ constexpr bool is_assignable_v = __is_assignable(T, U);
96104

97105
template <typename T>
98106
struct __details_is_empty {
99-
static constexpr bool value = __is_empty(T);
107+
static constexpr bool value = __is_empty(T);
100108
};
101109
template <typename T>
102110
using is_empty = __details_is_empty<T>;
@@ -105,9 +113,7 @@ constexpr bool is_empty_v = __is_empty(T);
105113

106114
template <typename T>
107115
struct __details_is_standard_layout {
108-
static constexpr bool value = __is_standard_layout(T);
109-
110-
116+
static constexpr bool value = __is_standard_layout(T);
111117
};
112118
template <typename T>
113119
using is_standard_layout = __details_is_standard_layout<T>;
@@ -125,6 +131,17 @@ using is_constructible = __details_is_constructible<Args...>;
125131
template <typename... Args>
126132
constexpr bool is_constructible_v = __is_constructible(Args...);
127133

134+
template <typename T>
135+
struct __details_is_aggregate {
136+
static constexpr bool value = __is_aggregate(T);
137+
};
138+
139+
template <typename T>
140+
using is_aggregate = __details_is_aggregate<T>;
141+
142+
template <typename T>
143+
constexpr bool is_aggregate_v = __is_aggregate(T);
144+
128145
template <typename T>
129146
struct __details_is_final {
130147
static constexpr bool value = __is_final(T);
@@ -191,11 +208,20 @@ template <typename... Args>
191208
struct __details_is_constructible : bool_constant<__is_constructible(Args...)> {};
192209

193210
template <typename... Args>
194-
using is_constructible = __details_is_constructible<Args...>;
211+
using is_constructible = __details_is_constructible<Args...>;
195212

196213
template <typename... Args>
197214
constexpr bool is_constructible_v = is_constructible<Args...>::value;
198215

216+
template <typename T>
217+
struct __details_is_aggregate : bool_constant<__is_aggregate(T)> {};
218+
219+
template <typename T>
220+
using is_aggregate = __details_is_aggregate<T>;
221+
222+
template <typename T>
223+
constexpr bool is_aggregate_v = is_aggregate<T>::value;
224+
199225
template <typename T>
200226
struct __details_is_final : bool_constant<__is_final(T)> {};
201227
template <typename T>
@@ -204,7 +230,6 @@ template <typename T>
204230
constexpr bool is_final_v = is_final<T>::value;
205231

206232
#endif
207-
208233
}
209234

210235
static_assert(std::is_trivially_relocatable<int>::value);
@@ -299,6 +324,18 @@ static_assert(std::is_final_v<Arr>);
299324
// expected-note@-1 {{'int[3]' is not final}} \
300325
// expected-note@-1 {{because it is not a class or union type}}
301326

327+
328+
static_assert(!std::is_aggregate<int>::value);
329+
330+
static_assert(std::is_aggregate<void>::value);
331+
// expected-error-re@-1 {{static assertion failed due to requirement 'std::{{.*}}is_aggregate<void>::value'}} \
332+
// expected-note@-1 {{'void' is not aggregate}} \
333+
// expected-note@-1 {{because it is a cv void type}}
334+
static_assert(std::is_aggregate_v<void>);
335+
// expected-error@-1 {{static assertion failed due to requirement 'std::is_aggregate_v<void>'}} \
336+
// expected-note@-1 {{'void' is not aggregate}} \
337+
// expected-note@-1 {{because it is a cv void type}}
338+
302339
namespace test_namespace {
303340
using namespace std;
304341
static_assert(is_trivially_relocatable<int&>::value);
@@ -351,6 +388,15 @@ namespace test_namespace {
351388
static_assert(is_constructible_v<void>);
352389
// expected-error@-1 {{static assertion failed due to requirement 'is_constructible_v<void>'}} \
353390
// expected-note@-1 {{because it is a cv void type}}
391+
392+
static_assert(std::is_aggregate<void>::value);
393+
// expected-error-re@-1 {{static assertion failed due to requirement 'std::{{.*}}is_aggregate<void>::value'}} \
394+
// expected-note@-1 {{'void' is not aggregate}} \
395+
// expected-note@-1 {{because it is a cv void type}}
396+
static_assert(std::is_aggregate_v<void>);
397+
// expected-error@-1 {{static assertion failed due to requirement 'std::is_aggregate_v<void>'}} \
398+
// expected-note@-1 {{'void' is not aggregate}} \
399+
// expected-note@-1 {{because it is a cv void type}}
354400

355401
static_assert(is_final<int&>::value);
356402
// 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
413459
template <C3 T> void g3(); // #cand6
414460

415461

462+
template <typename T>
463+
requires std::is_aggregate<T>::value void f5(); // #cand9
464+
465+
template <typename T>
466+
concept C5 = std::is_aggregate_v<T>; // #concept10
467+
468+
template <C5 T> void g5(); // #cand10
469+
416470
void test() {
417471
f<int&>();
418472
// expected-error@-1 {{no matching function for call to 'f'}} \
@@ -469,6 +523,21 @@ void test() {
469523
// expected-note@#cand6 {{because 'void' does not satisfy 'C3'}} \
470524
// expected-note@#concept6 {{because 'std::is_constructible_v<void>' evaluated to false}} \
471525
// expected-note@#concept6 {{because it is a cv void type}}
526+
527+
f5<void>();
528+
// expected-error@-1 {{no matching function for call to 'f5'}} \
529+
// expected-note@#cand9 {{candidate template ignored: constraints not satisfied [with T = void]}} \
530+
// expected-note-re@#cand9 {{because '{{.*}}is_aggregate<void>::value' evaluated to false}} \
531+
// expected-note@#cand9 {{'void' is not aggregate}} \
532+
// expected-note@#cand9 {{because it is a cv void type}}
533+
534+
g5<void>();
535+
// expected-error@-1 {{no matching function for call to 'g5'}} \
536+
// expected-note@#cand10 {{candidate template ignored: constraints not satisfied [with T = void]}} \
537+
// expected-note@#cand10 {{because 'void' does not satisfy 'C5'}} \
538+
// expected-note@#concept10 {{because 'std::is_aggregate_v<void>' evaluated to false}} \
539+
// expected-note@#concept10 {{'void' is not aggregate}} \
540+
// expected-note@#concept10 {{because it is a cv void type}}
472541
}
473542
}
474543

0 commit comments

Comments
 (0)