Skip to content

Commit 29de582

Browse files
committed
Explain why 'is_empty' evaluates to false
Add tests for various cases of 'is_empty' evaluating to false // and ensure that the diagnostics are correct.
1 parent 01174ff commit 29de582

File tree

4 files changed

+175
-1
lines changed

4 files changed

+175
-1
lines changed

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1767,7 +1767,8 @@ def note_unsatisfied_trait
17671767
: Note<"%0 is not %enum_select<TraitName>{"
17681768
"%TriviallyRelocatable{trivially relocatable}|"
17691769
"%Replaceable{replaceable}|"
1770-
"%TriviallyCopyable{trivially copyable}"
1770+
"%TriviallyCopyable{trivially copyable}|"
1771+
"%Empty{empty}"
17711772
"}1">;
17721773

17731774
def note_unsatisfied_trait_reason
@@ -1787,6 +1788,11 @@ def note_unsatisfied_trait_reason
17871788
"%NonReplaceableField{has a non-replaceable member %1 of type %2}|"
17881789
"%NTCBase{has a non-trivially-copyable base %1}|"
17891790
"%NTCField{has a non-trivially-copyable member %1 of type %2}|"
1791+
"%NonEmptyMember{has a non-static data member %1 of type %2}|"
1792+
"%VirtualFunction{has a virtual function %1}|"
1793+
"%VirtualBase{has a virtual base class %1}|"
1794+
"%NonEmptyBase{has a base class %1 that is not empty}|"
1795+
"%ZeroLengthField{field %1 is a non-zero-length bit-field}|"
17901796
"%DeletedDtr{has a %select{deleted|user-provided}1 destructor}|"
17911797
"%UserProvidedCtr{has a user provided %select{copy|move}1 "
17921798
"constructor}|"

clang/lib/Sema/SemaTypeTraits.cpp

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1958,6 +1958,7 @@ static std::optional<TypeTrait> StdNameToTypeTrait(StringRef Name) {
19581958
.Case("is_replaceable", TypeTrait::UTT_IsReplaceable)
19591959
.Case("is_trivially_copyable", TypeTrait::UTT_IsTriviallyCopyable)
19601960
.Case("is_assignable", TypeTrait::BTT_IsAssignable)
1961+
.Case("is_empty", TypeTrait::UTT_IsEmpty)
19611962
.Default(std::nullopt);
19621963
}
19631964

@@ -2313,6 +2314,68 @@ static void DiagnoseNonAssignableReason(Sema &SemaRef, SourceLocation Loc,
23132314
SemaRef.Diag(D->getLocation(), diag::note_defined_here) << D;
23142315
}
23152316

2317+
static void DiagnoseIsEmptyReason(Sema &S, SourceLocation Loc,
2318+
const CXXRecordDecl *D) {
2319+
// Non-static data members (ignore zero-width bit‐fields).
2320+
for (auto *Field : D->fields()) {
2321+
if (Field->isBitField() && Field->getBitWidthValue() == 0)
2322+
continue;
2323+
S.Diag(Loc, diag::note_unsatisfied_trait_reason)
2324+
<< diag::TraitNotSatisfiedReason::NonEmptyMember << Field
2325+
<< Field->getType() << Field->getSourceRange();
2326+
}
2327+
2328+
// Virtual functions.
2329+
for (auto *M : D->methods()) {
2330+
if (M->isVirtual()) {
2331+
S.Diag(Loc, diag::note_unsatisfied_trait_reason)
2332+
<< diag::TraitNotSatisfiedReason::VirtualFunction << M->getDeclName()
2333+
<< M->getSourceRange();
2334+
break;
2335+
}
2336+
}
2337+
2338+
// Virtual bases and non-empty bases.
2339+
for (auto &B : D->bases()) {
2340+
auto *BR = B.getType()->getAsCXXRecordDecl();
2341+
if (!BR || BR->isInvalidDecl())
2342+
continue;
2343+
if (B.isVirtual()) {
2344+
S.Diag(Loc, diag::note_unsatisfied_trait_reason)
2345+
<< diag::TraitNotSatisfiedReason::VirtualBase << B.getType()
2346+
<< B.getSourceRange();
2347+
}
2348+
if (!BR->isEmpty()) {
2349+
S.Diag(Loc, diag::note_unsatisfied_trait_reason)
2350+
<< diag::TraitNotSatisfiedReason::NonEmptyBase << B.getType()
2351+
<< B.getSourceRange();
2352+
}
2353+
}
2354+
}
2355+
2356+
static void DiagnoseIsEmptyReason(Sema &S, SourceLocation Loc, QualType T) {
2357+
// Emit primary "not empty" diagnostic.
2358+
S.Diag(Loc, diag::note_unsatisfied_trait) << T << diag::TraitName::Empty;
2359+
2360+
// While diagnosing is_empty<T>, we want to look at the actual type, not a
2361+
// reference or an array of it. So we need to massage the QualType param to
2362+
// strip refs and arrays.
2363+
if (T->isReferenceType())
2364+
S.Diag(Loc, diag::note_unsatisfied_trait_reason)
2365+
<< diag::TraitNotSatisfiedReason::Ref;
2366+
T = T.getNonReferenceType();
2367+
2368+
if (auto *AT = S.Context.getAsArrayType(T))
2369+
T = AT->getElementType();
2370+
2371+
if (auto *D = T->getAsCXXRecordDecl()) {
2372+
if (D->hasDefinition()) {
2373+
DiagnoseIsEmptyReason(S, Loc, D);
2374+
S.Diag(D->getLocation(), diag::note_defined_here) << D;
2375+
}
2376+
}
2377+
}
2378+
23162379
void Sema::DiagnoseTypeTraitDetails(const Expr *E) {
23172380
E = E->IgnoreParenImpCasts();
23182381
if (E->containsErrors())
@@ -2336,6 +2399,9 @@ void Sema::DiagnoseTypeTraitDetails(const Expr *E) {
23362399
case BTT_IsAssignable:
23372400
DiagnoseNonAssignableReason(*this, E->getBeginLoc(), Args[0], Args[1]);
23382401
break;
2402+
case UTT_IsEmpty:
2403+
DiagnoseIsEmptyReason(*this, E->getBeginLoc(), Args[0]);
2404+
break;
23392405
default:
23402406
break;
23412407
}

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

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,13 @@ struct is_assignable {
2828

2929
template <typename T, typename U>
3030
constexpr bool is_assignable_v = __is_assignable(T, U);
31+
32+
template <typename T>
33+
struct is_empty {
34+
static constexpr bool value = __is_empty(T);
35+
};
36+
template <typename T>
37+
constexpr bool is_empty_v = __is_empty(T);
3138
#endif
3239

3340
#ifdef STD2
@@ -63,6 +70,15 @@ using is_assignable = __details_is_assignable<T, U>;
6370

6471
template <typename T, typename U>
6572
constexpr bool is_assignable_v = __is_assignable(T, U);
73+
74+
template <typename T>
75+
struct __details_is_empty {
76+
static constexpr bool value = __is_empty(T);
77+
};
78+
template <typename T>
79+
using is_empty = __details_is_empty<T>;
80+
template <typename T>
81+
constexpr bool is_empty_v = __is_empty(T);
6682
#endif
6783

6884

@@ -101,6 +117,13 @@ using is_assignable = __details_is_assignable<T, U>;
101117

102118
template <typename T, typename U>
103119
constexpr bool is_assignable_v = is_assignable<T, U>::value;
120+
121+
template <typename T>
122+
struct __details_is_empty : bool_constant<__is_empty(T)> {};
123+
template <typename T>
124+
using is_empty = __details_is_empty<T>;
125+
template <typename T>
126+
constexpr bool is_empty_v = is_empty<T>::value;
104127
#endif
105128

106129
}
@@ -127,6 +150,18 @@ static_assert(std::is_trivially_copyable_v<int&>);
127150
// expected-note@-1 {{'int &' is not trivially copyable}} \
128151
// expected-note@-1 {{because it is a reference type}}
129152

153+
static_assert(!std::is_empty<int>::value);
154+
155+
static_assert(std::is_empty<int&>::value);
156+
// expected-error-re@-1 {{static assertion failed due to requirement 'std::{{.*}}is_empty<int &>::value'}} \
157+
// expected-note@-1 {{'int &' is not empty}} \
158+
// expected-note@-1 {{because it is a reference type}}
159+
static_assert(std::is_empty_v<int&>);
160+
// expected-error@-1 {{static assertion failed due to requirement 'std::is_empty_v<int &>'}} \
161+
// expected-note@-1 {{'int &' is not empty}} \
162+
// expected-note@-1 {{because it is a reference type}}
163+
164+
130165
static_assert(std::is_assignable<int&, int>::value);
131166

132167
static_assert(std::is_assignable<int&, void>::value);
@@ -162,6 +197,15 @@ namespace test_namespace {
162197
static_assert(is_assignable_v<int&, void>);
163198
// expected-error@-1 {{static assertion failed due to requirement 'is_assignable_v<int &, void>'}} \
164199
// expected-error@-1 {{assigning to 'int' from incompatible type 'void'}}
200+
201+
static_assert(is_empty<int&>::value);
202+
// expected-error-re@-1 {{static assertion failed due to requirement '{{.*}}is_empty<int &>::value'}} \
203+
// expected-note@-1 {{'int &' is not empty}} \
204+
// expected-note@-1 {{because it is a reference type}}
205+
static_assert(is_empty_v<int&>);
206+
// expected-error@-1 {{static assertion failed due to requirement 'is_empty_v<int &>'}} \
207+
// expected-note@-1 {{'int &' is not empty}} \
208+
// expected-note@-1 {{because it is a reference type}}
165209
}
166210

167211

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

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -559,3 +559,61 @@ static_assert(__is_assignable(C1, C1));
559559
// expected-note@#ama-C1 {{implicitly declared private here}} \
560560
// expected-note@#a-C1 {{'C1' defined here}}
561561
}
562+
563+
namespace is_empty_tests {
564+
// Non-static data member.
565+
struct A { int x; }; // #e-A
566+
static_assert(__is_empty(A));
567+
// expected-error@-1 {{static assertion failed due to requirement '__is_empty(is_empty_tests::A)'}} \
568+
// expected-note@-1 {{'A' is not empty}} \
569+
// expected-note@-1 {{because it has a non-static data member 'x' of type 'int'}} \
570+
// expected-note@#e-A {{'A' defined here}}
571+
572+
// Reference member.
573+
struct R {int &r; }; // #e-R
574+
static_assert(__is_empty(R));
575+
// expected-error@-1 {{static assertion failed due to requirement '__is_empty(is_empty_tests::R)'}} \
576+
// expected-note@-1 {{'R' is not empty}} \
577+
// expected-note@-1 {{because it has a non-static data member 'r' of type 'int &'}} \
578+
// expected-note@#e-R {{'R' defined here}}
579+
580+
// Virtual function.
581+
struct VirtualFunc {virtual void f(); }; // #e-VirtualFunc
582+
static_assert(__is_empty(VirtualFunc));
583+
// expected-error@-1 {{static assertion failed due to requirement '__is_empty(is_empty_tests::VirtualFunc)'}} \
584+
// expected-note@-1 {{'VirtualFunc' is not empty}} \
585+
// expected-note@-1 {{because it has a virtual function 'f'}} \
586+
// expected-note@#e-VirtualFunc {{'VirtualFunc' defined here}}
587+
588+
// Virtual base class.
589+
struct EB {};
590+
struct VB: virtual EB {}; // #e-VB
591+
static_assert(__is_empty(VB));
592+
// expected-error@-1 {{static assertion failed due to requirement '__is_empty(is_empty_tests::VB)'}} \
593+
// expected-note@-1 {{'VB' is not empty}} \
594+
// expected-note@-1 {{because it has a virtual base class 'EB'}} \
595+
// expected-note@#e-VB {{'VB' defined here}}
596+
597+
// Non-empty base class.
598+
struct Base { int b; }; // #e-Base
599+
struct Derived : Base {}; // #e-Derived
600+
static_assert(__is_empty(Derived));
601+
// expected-error@-1 {{static assertion failed due to requirement '__is_empty(is_empty_tests::Derived)'}} \
602+
// expected-note@-1 {{'Derived' is not empty}} \
603+
// expected-note@-1 {{because it has a base class 'Base' that is not empty}} \
604+
// expected-note@#e-Derived {{'Derived' defined here}}
605+
606+
// Combination of the above.
607+
struct Multi : Base, virtual EB { // #e-Multi
608+
int z;
609+
virtual void g();
610+
};
611+
static_assert(__is_empty(Multi));
612+
// expected-error@-1 {{static assertion failed due to requirement '__is_empty(is_empty_tests::Multi)'}} \
613+
// expected-note@-1 {{'Multi' is not empty}} \
614+
// expected-note@-1 {{because it has a non-static data member 'z' of type 'int'}} \
615+
// expected-note@-1 {{because it has a virtual function 'g'}} \
616+
// expected-note@-1 {{because it has a base class 'Base' that is not empty}} \
617+
// expected-note@-1 {{because it has a virtual base class 'EB'}} \
618+
// expected-note@#e-Multi {{'Multi' defined here}}
619+
}

0 commit comments

Comments
 (0)