[clang] 1f65d4d - [Clang] make most enums trivially equality comparable (#169079)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Feb 16 02:52:18 PST 2026
Author: halbi2
Date: 2026-02-16T11:52:13+01:00
New Revision: 1f65d4dda14cfea4323fd7139e222d26c7dc365d
URL: https://github.com/llvm/llvm-project/commit/1f65d4dda14cfea4323fd7139e222d26c7dc365d
DIFF: https://github.com/llvm/llvm-project/commit/1f65d4dda14cfea4323fd7139e222d26c7dc365d.diff
LOG: [Clang] make most enums trivially equality comparable (#169079)
std::equal(std::byte) currently has sub-optimal codegen due to enum
types not being recognized as trivially equality comparable. In order to
fix this we make them trivially comparable. In the process I factored
out into a standalone function EqualityComparisonIsDefaulted and
refactored the test cases.
Enum types cannot have operator== which is a hidden friend.
Fixes #132672
Added:
Modified:
clang/docs/ReleaseNotes.rst
clang/lib/Sema/SemaTypeTraits.cpp
clang/test/SemaCXX/type-traits.cpp
Removed:
################################################################################
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index c9bedb87c6a79..5ac5ae4a7d37e 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -103,6 +103,8 @@ What's New in Clang |release|?
C++ Language Changes
--------------------
+- ``__is_trivially_equality_comparable`` no longer returns false for all enum types. (#GH132672)
+
C++2c Feature Support
^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/lib/Sema/SemaTypeTraits.cpp b/clang/lib/Sema/SemaTypeTraits.cpp
index be2c8853d6433..a94a59e8add7b 100644
--- a/clang/lib/Sema/SemaTypeTraits.cpp
+++ b/clang/lib/Sema/SemaTypeTraits.cpp
@@ -503,6 +503,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);
+
+ ExprResult 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 && !RD->isTriviallyCopyable())
+ return false;
+ }
+ return S.Context.hasSameUnqualifiedType(ParamT.getNonReferenceType(), T);
+}
+
static bool HasNonDeletedDefaultedEqualityComparison(Sema &S,
const CXXRecordDecl *Decl,
SourceLocation KeyLoc) {
@@ -511,36 +546,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) {
@@ -555,9 +562,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;
});
@@ -567,9 +578,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;
diff --git a/clang/test/SemaCXX/type-traits.cpp b/clang/test/SemaCXX/type-traits.cpp
index 561c9ca8286b9..65c0729571f99 100644
--- a/clang/test/SemaCXX/type-traits.cpp
+++ b/clang/test/SemaCXX/type-traits.cpp
@@ -4003,6 +4003,14 @@ 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 Byte : unsigned char {};
+enum ByteWithOpEq : unsigned char {};
+bool operator==(ByteWithOpEq, ByteWithOpEq);
+
+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[]));
@@ -4010,6 +4018,10 @@ 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(Byte));
+static_assert(!__is_trivially_equality_comparable(ByteWithOpEq));
+static_assert(__is_trivially_equality_comparable(Enum));
+static_assert(!__is_trivially_equality_comparable(EnumWithOpEq));
struct NonTriviallyEqualityComparableNoComparator {
int i;
@@ -4043,19 +4055,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; }; }
@@ -4184,13 +4203,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;
@@ -4206,34 +4218,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;
@@ -4321,6 +4305,27 @@ 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]>));
+
struct TriviallyEqualityComparableNonTriviallyCopyable {
TriviallyEqualityComparableNonTriviallyCopyable(const TriviallyEqualityComparableNonTriviallyCopyable&);
~TriviallyEqualityComparableNonTriviallyCopyable();
@@ -4437,26 +4442,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&);
@@ -4475,7 +4460,7 @@ static_assert(__is_trivially_equality_comparable(TriviallyEqualityComparableRefC
}
#endif // __cplusplus >= 202002L
-};
+}
namespace can_pass_in_regs {
More information about the cfe-commits
mailing list