Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,8 @@ C++ Language Changes
``<=>``. This makes it possible to optimize certain facilities by using the ``<=>`` operation directly instead of
doing multiple comparisons.

- ``__is_trivially_equality_comparable`` no longer returns false for all enum types. (#GH132672)

C++2c Feature Support
^^^^^^^^^^^^^^^^^^^^^

Expand Down
83 changes: 50 additions & 33 deletions clang/lib/Sema/SemaTypeTraits.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -591,6 +591,41 @@ static bool HasNoThrowOperator(CXXRecordDecl *RD, OverloadedOperatorKind Op,
return false;
}

static bool EqualityComparisonIsDefaulted(Sema &S, const TagDecl *Decl,
SourceLocation KeyLoc) {
CanQualType T = S.Context.getCanonicalTagType(Decl);

EnterExpressionEvaluationContext UnevaluatedContext(
S, Sema::ExpressionEvaluationContext::Unevaluated);
Sema::SFINAETrap SFINAE(S, /*WithAccessChecking=*/true);
Sema::ContextRAII TUContext(S, S.Context.getTranslationUnitDecl());

// const ClassT& obj;
OpaqueValueExpr Operand(KeyLoc, T.withConst(), ExprValueKind::VK_LValue);
UnresolvedSet<16> Functions;
// obj == obj;
S.LookupBinOp(S.TUScope, {}, BinaryOperatorKind::BO_EQ, Functions);

auto Result = S.CreateOverloadedBinOp(KeyLoc, BinaryOperatorKind::BO_EQ,
Functions, &Operand, &Operand);
if (Result.isInvalid() || SFINAE.hasErrorOccurred())
return false;

const auto *CallExpr = dyn_cast<CXXOperatorCallExpr>(Result.get());
if (!CallExpr)
return isa<EnumDecl>(Decl);
const auto *Callee = CallExpr->getDirectCallee();
auto ParamT = Callee->getParamDecl(0)->getType();
if (!Callee->isDefaulted())
return false;
if (!ParamT->isReferenceType()) {
const CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(Decl);
if (!RD->isTriviallyCopyable())
return false;
}
return S.Context.hasSameUnqualifiedType(ParamT.getNonReferenceType(), T);
}

static bool HasNonDeletedDefaultedEqualityComparison(Sema &S,
const CXXRecordDecl *Decl,
SourceLocation KeyLoc) {
Expand All @@ -599,36 +634,8 @@ static bool HasNonDeletedDefaultedEqualityComparison(Sema &S,
if (Decl->isLambda())
return Decl->isCapturelessLambda();

CanQualType T = S.Context.getCanonicalTagType(Decl);
{
EnterExpressionEvaluationContext UnevaluatedContext(
S, Sema::ExpressionEvaluationContext::Unevaluated);
Sema::SFINAETrap SFINAE(S, /*ForValidityCheck=*/true);
Sema::ContextRAII TUContext(S, S.Context.getTranslationUnitDecl());

// const ClassT& obj;
OpaqueValueExpr Operand(KeyLoc, T.withConst(), ExprValueKind::VK_LValue);
UnresolvedSet<16> Functions;
// obj == obj;
S.LookupBinOp(S.TUScope, {}, BinaryOperatorKind::BO_EQ, Functions);

auto Result = S.CreateOverloadedBinOp(KeyLoc, BinaryOperatorKind::BO_EQ,
Functions, &Operand, &Operand);
if (Result.isInvalid() || SFINAE.hasErrorOccurred())
return false;

const auto *CallExpr = dyn_cast<CXXOperatorCallExpr>(Result.get());
if (!CallExpr)
return false;
const auto *Callee = CallExpr->getDirectCallee();
auto ParamT = Callee->getParamDecl(0)->getType();
if (!Callee->isDefaulted())
return false;
if (!ParamT->isReferenceType() && !Decl->isTriviallyCopyable())
return false;
if (!S.Context.hasSameUnqualifiedType(ParamT.getNonReferenceType(), T))
return false;
}
if (!EqualityComparisonIsDefaulted(S, Decl, KeyLoc))
return false;

return llvm::all_of(Decl->bases(),
[&](const CXXBaseSpecifier &BS) {
Expand All @@ -643,9 +650,13 @@ static bool HasNonDeletedDefaultedEqualityComparison(Sema &S,
Type = Type->getBaseElementTypeUnsafe()
->getCanonicalTypeUnqualified();

if (Type->isReferenceType() || Type->isEnumeralType())
if (Type->isReferenceType())
return false;
if (const auto *RD = Type->getAsCXXRecordDecl())
if (Type->isEnumeralType()) {
EnumDecl *ED =
Type->castAs<EnumType>()->getDecl()->getDefinitionOrSelf();
return EqualityComparisonIsDefaulted(S, ED, KeyLoc);
} else if (const auto *RD = Type->getAsCXXRecordDecl())
return HasNonDeletedDefaultedEqualityComparison(S, RD, KeyLoc);
return true;
});
Expand All @@ -655,9 +666,15 @@ static bool isTriviallyEqualityComparableType(Sema &S, QualType Type,
SourceLocation KeyLoc) {
QualType CanonicalType = Type.getCanonicalType();
if (CanonicalType->isIncompleteType() || CanonicalType->isDependentType() ||
CanonicalType->isEnumeralType() || CanonicalType->isArrayType())
CanonicalType->isArrayType())
return false;

if (CanonicalType->isEnumeralType()) {
EnumDecl *ED =
CanonicalType->castAs<EnumType>()->getDecl()->getDefinitionOrSelf();
return EqualityComparisonIsDefaulted(S, ED, KeyLoc);
}

if (const auto *RD = CanonicalType->getAsCXXRecordDecl()) {
if (!HasNonDeletedDefaultedEqualityComparison(S, RD, KeyLoc))
return false;
Expand Down
128 changes: 59 additions & 69 deletions clang/test/SemaCXX/type-traits.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3993,13 +3993,19 @@ namespace is_trivially_equality_comparable {
struct ForwardDeclared; // expected-note {{forward declaration of 'is_trivially_equality_comparable::ForwardDeclared'}}
static_assert(!__is_trivially_equality_comparable(ForwardDeclared)); // expected-error {{incomplete type 'ForwardDeclared' used in type trait expression}}

enum Enum {};
enum EnumWithOpEq {};
bool operator==(EnumWithOpEq, EnumWithOpEq);

static_assert(!__is_trivially_equality_comparable(void));
static_assert(__is_trivially_equality_comparable(int));
static_assert(!__is_trivially_equality_comparable(int[]));
static_assert(!__is_trivially_equality_comparable(int[3]));
static_assert(!__is_trivially_equality_comparable(float));
static_assert(!__is_trivially_equality_comparable(double));
static_assert(!__is_trivially_equality_comparable(long double));
static_assert(__is_trivially_equality_comparable(Enum));
static_assert(!__is_trivially_equality_comparable(EnumWithOpEq));

struct NonTriviallyEqualityComparableNoComparator {
int i;
Expand Down Expand Up @@ -4033,19 +4039,26 @@ struct TriviallyEqualityComparable {
};
static_assert(__is_trivially_equality_comparable(TriviallyEqualityComparable));

struct TriviallyEqualityComparableContainsArray {
int a[4];

bool operator==(const TriviallyEqualityComparableContainsArray&) const = default;
};
static_assert(__is_trivially_equality_comparable(TriviallyEqualityComparableContainsArray));

struct TriviallyEqualityComparableContainsMultiDimensionArray {
int a[4][4];

bool operator==(const TriviallyEqualityComparableContainsMultiDimensionArray&) const = default;
};
static_assert(__is_trivially_equality_comparable(TriviallyEqualityComparableContainsMultiDimensionArray));
template <class T>
struct TriviallyEqualityComparableContains {
T t;

bool operator==(const TriviallyEqualityComparableContains&) const = default;
};

static_assert(__is_trivially_equality_comparable(TriviallyEqualityComparableContains<int>));
static_assert(!__is_trivially_equality_comparable(TriviallyEqualityComparableContains<int&>));
static_assert(!__is_trivially_equality_comparable(TriviallyEqualityComparableContains<float>));
static_assert(!__is_trivially_equality_comparable(TriviallyEqualityComparableContains<double>));
static_assert(!__is_trivially_equality_comparable(TriviallyEqualityComparableContains<long double>));
static_assert(__is_trivially_equality_comparable(TriviallyEqualityComparableContains<int[4]>));
static_assert(__is_trivially_equality_comparable(TriviallyEqualityComparableContains<int[4][4]>));
static_assert(__is_trivially_equality_comparable(TriviallyEqualityComparableContains<Enum>));
static_assert(__is_trivially_equality_comparable(TriviallyEqualityComparableContains<Enum[2]>));
static_assert(__is_trivially_equality_comparable(TriviallyEqualityComparableContains<Enum[2][2]>));
static_assert(!__is_trivially_equality_comparable(TriviallyEqualityComparableContains<EnumWithOpEq>));
static_assert(!__is_trivially_equality_comparable(TriviallyEqualityComparableContains<EnumWithOpEq[2]>));
static_assert(!__is_trivially_equality_comparable(TriviallyEqualityComparableContains<EnumWithOpEq[2][2]>));

auto GetNonCapturingLambda() { return [](){ return 42; }; }

Expand Down Expand Up @@ -4174,13 +4187,6 @@ struct NotTriviallyEqualityComparableImplicitlyDeletedOperatorByStruct {
};
static_assert(!__is_trivially_equality_comparable(NotTriviallyEqualityComparableImplicitlyDeletedOperatorByStruct));

struct NotTriviallyEqualityComparableHasReferenceMember {
int& i;

bool operator==(const NotTriviallyEqualityComparableHasReferenceMember&) const = default;
};
static_assert(!__is_trivially_equality_comparable(NotTriviallyEqualityComparableHasReferenceMember));

struct NotTriviallyEqualityComparableNonTriviallyComparableBaseBase {
int i;

Expand All @@ -4196,34 +4202,6 @@ struct NotTriviallyEqualityComparableNonTriviallyComparableBase : NotTriviallyEq
};
static_assert(!__is_trivially_equality_comparable(NotTriviallyEqualityComparableNonTriviallyComparableBase));

enum E {
a,
b
};
bool operator==(E, E) { return false; }
static_assert(!__is_trivially_equality_comparable(E));

struct NotTriviallyEqualityComparableHasEnum {
E e;
bool operator==(const NotTriviallyEqualityComparableHasEnum&) const = default;
};
static_assert(!__is_trivially_equality_comparable(NotTriviallyEqualityComparableHasEnum));

struct NotTriviallyEqualityComparableNonTriviallyEqualityComparableArrs {
E e[1];

bool operator==(const NotTriviallyEqualityComparableNonTriviallyEqualityComparableArrs&) const = default;
};
static_assert(!__is_trivially_equality_comparable(NotTriviallyEqualityComparableNonTriviallyEqualityComparableArrs));

struct NotTriviallyEqualityComparableNonTriviallyEqualityComparableArrs2 {
E e[1][1];

bool operator==(const NotTriviallyEqualityComparableNonTriviallyEqualityComparableArrs2&) const = default;
};

static_assert(!__is_trivially_equality_comparable(NotTriviallyEqualityComparableNonTriviallyEqualityComparableArrs2));

struct NotTriviallyEqualityComparablePrivateComparison {
int i;

Expand Down Expand Up @@ -4311,6 +4289,38 @@ struct TriviallyEqualityComparable {
};
static_assert(__is_trivially_equality_comparable(TriviallyEqualityComparable));

template <class T>
struct TriviallyEqualityComparableContains {
T t;

friend bool operator==(const TriviallyEqualityComparableContains&, const TriviallyEqualityComparableContains&) = default;
};

static_assert(__is_trivially_equality_comparable(TriviallyEqualityComparableContains<int>));
static_assert(!__is_trivially_equality_comparable(TriviallyEqualityComparableContains<int&>));
static_assert(!__is_trivially_equality_comparable(TriviallyEqualityComparableContains<float>));
static_assert(!__is_trivially_equality_comparable(TriviallyEqualityComparableContains<double>));
static_assert(!__is_trivially_equality_comparable(TriviallyEqualityComparableContains<long double>));
static_assert(__is_trivially_equality_comparable(TriviallyEqualityComparableContains<int[4]>));
static_assert(__is_trivially_equality_comparable(TriviallyEqualityComparableContains<int[4][4]>));
static_assert(__is_trivially_equality_comparable(TriviallyEqualityComparableContains<Enum>));
static_assert(__is_trivially_equality_comparable(TriviallyEqualityComparableContains<Enum[2]>));
static_assert(__is_trivially_equality_comparable(TriviallyEqualityComparableContains<Enum[2][2]>));
static_assert(!__is_trivially_equality_comparable(TriviallyEqualityComparableContains<EnumWithOpEq>));
static_assert(!__is_trivially_equality_comparable(TriviallyEqualityComparableContains<EnumWithOpEq[2]>));
static_assert(!__is_trivially_equality_comparable(TriviallyEqualityComparableContains<EnumWithOpEq[2][2]>));

auto GetNonCapturingLambda() { return [](){ return 42; }; }

struct TriviallyEqualityComparableContainsLambda {
[[no_unique_address]] decltype(GetNonCapturingLambda()) l;
int i;

friend bool operator==(const TriviallyEqualityComparableContainsLambda&, const TriviallyEqualityComparableContainsLambda&) = default;
};
static_assert(!__is_trivially_equality_comparable(decltype(GetNonCapturingLambda()))); // padding
static_assert(__is_trivially_equality_comparable(TriviallyEqualityComparableContainsLambda));

struct TriviallyEqualityComparableNonTriviallyCopyable {
TriviallyEqualityComparableNonTriviallyCopyable(const TriviallyEqualityComparableNonTriviallyCopyable&);
~TriviallyEqualityComparableNonTriviallyCopyable();
Expand Down Expand Up @@ -4427,26 +4437,6 @@ struct NotTriviallyEqualityComparableImplicitlyDeletedOperatorByStruct {
};
static_assert(!__is_trivially_equality_comparable(NotTriviallyEqualityComparableImplicitlyDeletedOperatorByStruct));

struct NotTriviallyEqualityComparableHasReferenceMember {
int& i;

friend bool operator==(const NotTriviallyEqualityComparableHasReferenceMember&, const NotTriviallyEqualityComparableHasReferenceMember&) = default;
};
static_assert(!__is_trivially_equality_comparable(NotTriviallyEqualityComparableHasReferenceMember));

enum E {
a,
b
};
bool operator==(E, E) { return false; }
static_assert(!__is_trivially_equality_comparable(E));

struct NotTriviallyEqualityComparableHasEnum {
E e;
friend bool operator==(const NotTriviallyEqualityComparableHasEnum&, const NotTriviallyEqualityComparableHasEnum&) = default;
};
static_assert(!__is_trivially_equality_comparable(NotTriviallyEqualityComparableHasEnum));

struct NonTriviallyEqualityComparableValueComparisonNonTriviallyCopyable {
int i;
NonTriviallyEqualityComparableValueComparisonNonTriviallyCopyable(const NonTriviallyEqualityComparableValueComparisonNonTriviallyCopyable&);
Expand All @@ -4465,7 +4455,7 @@ static_assert(__is_trivially_equality_comparable(TriviallyEqualityComparableRefC
}

#endif // __cplusplus >= 202002L
};
}

namespace can_pass_in_regs {

Expand Down