Skip to content

Commit ef3da9b

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 f280d3b commit ef3da9b

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
@@ -1956,6 +1956,7 @@ static std::optional<TypeTrait> StdNameToTypeTrait(StringRef Name) {
19561956
TypeTrait::UTT_IsCppTriviallyRelocatable)
19571957
.Case("is_replaceable", TypeTrait::UTT_IsReplaceable)
19581958
.Case("is_trivially_copyable", TypeTrait::UTT_IsTriviallyCopyable)
1959+
.Case("is_empty", TypeTrait::UTT_IsEmpty)
19591960
.Default(std::nullopt);
19601961
}
19611962

@@ -2285,6 +2286,68 @@ static void DiagnoseNonTriviallyCopyableReason(Sema &SemaRef,
22852286
SemaRef.Diag(D->getLocation(), diag::note_defined_here) << D;
22862287
}
22872288

2289+
static void DiagnoseIsEmptyReason(Sema &S, SourceLocation Loc,
2290+
const CXXRecordDecl *D) {
2291+
// Non-static data members (ignore zero-width bit‐fields).
2292+
for (auto *Field : D->fields()) {
2293+
if (Field->isBitField() && Field->getBitWidthValue() == 0)
2294+
continue;
2295+
S.Diag(Loc, diag::note_unsatisfied_trait_reason)
2296+
<< diag::TraitNotSatisfiedReason::NonEmptyMember << Field
2297+
<< Field->getType() << Field->getSourceRange();
2298+
}
2299+
2300+
// Virtual functions.
2301+
for (auto *M : D->methods()) {
2302+
if (M->isVirtual()) {
2303+
S.Diag(Loc, diag::note_unsatisfied_trait_reason)
2304+
<< diag::TraitNotSatisfiedReason::VirtualFunction << M->getDeclName()
2305+
<< M->getSourceRange();
2306+
break;
2307+
}
2308+
}
2309+
2310+
// Virtual bases and non-empty bases.
2311+
for (auto &B : D->bases()) {
2312+
auto *BR = B.getType()->getAsCXXRecordDecl();
2313+
if (!BR || BR->isInvalidDecl())
2314+
continue;
2315+
if (B.isVirtual()) {
2316+
S.Diag(Loc, diag::note_unsatisfied_trait_reason)
2317+
<< diag::TraitNotSatisfiedReason::VirtualBase << B.getType()
2318+
<< B.getSourceRange();
2319+
}
2320+
if (!BR->isEmpty()) {
2321+
S.Diag(Loc, diag::note_unsatisfied_trait_reason)
2322+
<< diag::TraitNotSatisfiedReason::NonEmptyBase << B.getType()
2323+
<< B.getSourceRange();
2324+
}
2325+
}
2326+
}
2327+
2328+
static void DiagnoseIsEmptyReason(Sema &S, SourceLocation Loc, QualType T) {
2329+
// Emit primary "not empty" diagnostic.
2330+
S.Diag(Loc, diag::note_unsatisfied_trait) << T << diag::TraitName::Empty;
2331+
2332+
// While diagnosing is_empty<T>, we want to look at the actual type, not a
2333+
// reference or an array of it. So we need to massage the QualType param to
2334+
// strip refs and arrays.
2335+
if (T->isReferenceType())
2336+
S.Diag(Loc, diag::note_unsatisfied_trait_reason)
2337+
<< diag::TraitNotSatisfiedReason::Ref;
2338+
T = T.getNonReferenceType();
2339+
2340+
if (auto *AT = S.Context.getAsArrayType(T))
2341+
T = AT->getElementType();
2342+
2343+
if (auto *D = T->getAsCXXRecordDecl()) {
2344+
if (D->hasDefinition()) {
2345+
DiagnoseIsEmptyReason(S, Loc, D);
2346+
S.Diag(D->getLocation(), diag::note_defined_here) << D;
2347+
}
2348+
}
2349+
}
2350+
22882351
void Sema::DiagnoseTypeTraitDetails(const Expr *E) {
22892352
E = E->IgnoreParenImpCasts();
22902353
if (E->containsErrors())
@@ -2305,6 +2368,9 @@ void Sema::DiagnoseTypeTraitDetails(const Expr *E) {
23052368
case UTT_IsTriviallyCopyable:
23062369
DiagnoseNonTriviallyCopyableReason(*this, E->getBeginLoc(), Args[0]);
23072370
break;
2371+
case UTT_IsEmpty:
2372+
DiagnoseIsEmptyReason(*this, E->getBeginLoc(), Args[0]);
2373+
break;
23082374
default:
23092375
break;
23102376
}

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

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,13 @@ struct is_trivially_copyable {
2020

2121
template <typename T>
2222
constexpr bool is_trivially_copyable_v = __is_trivially_copyable(T);
23+
24+
template <typename T>
25+
struct is_empty {
26+
static constexpr bool value = __is_empty(T);
27+
};
28+
template <typename T>
29+
constexpr bool is_empty_v = __is_empty(T);
2330
#endif
2431

2532
#ifdef STD2
@@ -44,6 +51,15 @@ using is_trivially_copyable = __details_is_trivially_copyable<T>;
4451

4552
template <typename T>
4653
constexpr bool is_trivially_copyable_v = __is_trivially_copyable(T);
54+
55+
template <typename T>
56+
struct __details_is_empty {
57+
static constexpr bool value = __is_empty(T);
58+
};
59+
template <typename T>
60+
using is_empty = __details_is_empty<T>;
61+
template <typename T>
62+
constexpr bool is_empty_v = __is_empty(T);
4763
#endif
4864

4965

@@ -73,6 +89,13 @@ using is_trivially_copyable = __details_is_trivially_copyable<T>;
7389

7490
template <typename T>
7591
constexpr bool is_trivially_copyable_v = is_trivially_copyable<T>::value;
92+
93+
template <typename T>
94+
struct __details_is_empty : bool_constant<__is_empty(T)> {};
95+
template <typename T>
96+
using is_empty = __details_is_empty<T>;
97+
template <typename T>
98+
constexpr bool is_empty_v = is_empty<T>::value;
7699
#endif
77100

78101
}
@@ -99,6 +122,18 @@ static_assert(std::is_trivially_copyable_v<int&>);
99122
// expected-note@-1 {{'int &' is not trivially copyable}} \
100123
// expected-note@-1 {{because it is a reference type}}
101124

125+
static_assert(!std::is_empty<int>::value);
126+
127+
static_assert(std::is_empty<int&>::value);
128+
// expected-error-re@-1 {{static assertion failed due to requirement 'std::{{.*}}is_empty<int &>::value'}} \
129+
// expected-note@-1 {{'int &' is not empty}} \
130+
// expected-note@-1 {{because it is a reference type}}
131+
static_assert(std::is_empty_v<int&>);
132+
// expected-error@-1 {{static assertion failed due to requirement 'std::is_empty_v<int &>'}} \
133+
// expected-note@-1 {{'int &' is not empty}} \
134+
// expected-note@-1 {{because it is a reference type}}
135+
136+
102137

103138
namespace test_namespace {
104139
using namespace std;
@@ -119,6 +154,15 @@ namespace test_namespace {
119154
// expected-error@-1 {{static assertion failed due to requirement 'is_trivially_copyable_v<int &>'}} \
120155
// expected-note@-1 {{'int &' is not trivially copyable}} \
121156
// expected-note@-1 {{because it is a reference type}}
157+
158+
static_assert(is_empty<int&>::value);
159+
// expected-error-re@-1 {{static assertion failed due to requirement '{{.*}}is_empty<int &>::value'}} \
160+
// expected-note@-1 {{'int &' is not empty}} \
161+
// expected-note@-1 {{because it is a reference type}}
162+
static_assert(is_empty_v<int&>);
163+
// expected-error@-1 {{static assertion failed due to requirement 'is_empty_v<int &>'}} \
164+
// expected-note@-1 {{'int &' is not empty}} \
165+
// expected-note@-1 {{because it is a reference type}}
122166
}
123167

124168

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

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -488,3 +488,61 @@ static_assert(__is_trivially_copyable(S12));
488488
// expected-note@-1 {{'S12' is not trivially copyable}} \
489489
// expected-note@#tc-S12 {{'S12' defined here}}
490490
}
491+
492+
namespace is_empty_tests {
493+
// Non-static data member.
494+
struct A { int x; }; // #e-A
495+
static_assert(__is_empty(A));
496+
// expected-error@-1 {{static assertion failed due to requirement '__is_empty(is_empty_tests::A)'}} \
497+
// expected-note@-1 {{'A' is not empty}} \
498+
// expected-note@-1 {{because it has a non-static data member 'x' of type 'int'}} \
499+
// expected-note@#e-A {{'A' defined here}}
500+
501+
// Reference member.
502+
struct R {int &r; }; // #e-R
503+
static_assert(__is_empty(R));
504+
// expected-error@-1 {{static assertion failed due to requirement '__is_empty(is_empty_tests::R)'}} \
505+
// expected-note@-1 {{'R' is not empty}} \
506+
// expected-note@-1 {{because it has a non-static data member 'r' of type 'int &'}} \
507+
// expected-note@#e-R {{'R' defined here}}
508+
509+
// Virtual function.
510+
struct VirtualFunc {virtual void f(); }; // #e-VirtualFunc
511+
static_assert(__is_empty(VirtualFunc));
512+
// expected-error@-1 {{static assertion failed due to requirement '__is_empty(is_empty_tests::VirtualFunc)'}} \
513+
// expected-note@-1 {{'VirtualFunc' is not empty}} \
514+
// expected-note@-1 {{because it has a virtual function 'f'}} \
515+
// expected-note@#e-VirtualFunc {{'VirtualFunc' defined here}}
516+
517+
// Virtual base class.
518+
struct EB {};
519+
struct VB: virtual EB {}; // #e-VB
520+
static_assert(__is_empty(VB));
521+
// expected-error@-1 {{static assertion failed due to requirement '__is_empty(is_empty_tests::VB)'}} \
522+
// expected-note@-1 {{'VB' is not empty}} \
523+
// expected-note@-1 {{because it has a virtual base class 'EB'}} \
524+
// expected-note@#e-VB {{'VB' defined here}}
525+
526+
// Non-empty base class.
527+
struct Base { int b; }; // #e-Base
528+
struct Derived : Base {}; // #e-Derived
529+
static_assert(__is_empty(Derived));
530+
// expected-error@-1 {{static assertion failed due to requirement '__is_empty(is_empty_tests::Derived)'}} \
531+
// expected-note@-1 {{'Derived' is not empty}} \
532+
// expected-note@-1 {{because it has a base class 'Base' that is not empty}} \
533+
// expected-note@#e-Derived {{'Derived' defined here}}
534+
535+
// Combination of the above.
536+
struct Multi : Base, virtual EB { // #e-Multi
537+
int z;
538+
virtual void g();
539+
};
540+
static_assert(__is_empty(Multi));
541+
// expected-error@-1 {{static assertion failed due to requirement '__is_empty(is_empty_tests::Multi)'}} \
542+
// expected-note@-1 {{'Multi' is not empty}} \
543+
// expected-note@-1 {{because it has a non-static data member 'z' of type 'int'}} \
544+
// expected-note@-1 {{because it has a virtual function 'g'}} \
545+
// expected-note@-1 {{because it has a base class 'Base' that is not empty}} \
546+
// expected-note@-1 {{because it has a virtual base class 'EB'}} \
547+
// expected-note@#e-Multi {{'Multi' defined here}}
548+
}

0 commit comments

Comments
 (0)