https://github.com/halbi2 updated https://github.com/llvm/llvm-project/pull/169079
>From cf66bc88862b47fd7bc97a71f0c5ca81d3f60a8a Mon Sep 17 00:00:00 2001 From: halbi2 <[email protected]> Date: Fri, 21 Nov 2025 13:22:30 -0500 Subject: [PATCH 1/4] [Clang] make most enums trivially equality comparable 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 --- clang/lib/Sema/SemaTypeTraits.cpp | 83 ++++++++++++++++++------------ clang/test/SemaCXX/type-traits.cpp | 46 +++++++---------- 2 files changed, 70 insertions(+), 59 deletions(-) diff --git a/clang/lib/Sema/SemaTypeTraits.cpp b/clang/lib/Sema/SemaTypeTraits.cpp index 38877967af05e..581989e6d0069 100644 --- a/clang/lib/Sema/SemaTypeTraits.cpp +++ b/clang/lib/Sema/SemaTypeTraits.cpp @@ -591,6 +591,43 @@ static bool HasNoThrowOperator(CXXRecordDecl *RD, OverloadedOperatorKind Op, return false; } +static bool EqualityComparisonIsDefaulted(Sema &S, const TypeDecl *Decl, + SourceLocation KeyLoc) { + 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 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) { @@ -599,36 +636,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) { @@ -643,9 +652,12 @@ 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>()->getOriginalDecl()->getDefinitionOrSelf(); + return EqualityComparisonIsDefaulted(S, ED, KeyLoc); + } else if (const auto *RD = Type->getAsCXXRecordDecl()) return HasNonDeletedDefaultedEqualityComparison(S, RD, KeyLoc); return true; }); @@ -655,9 +667,14 @@ 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>()->getOriginalDecl()->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 9ef44d0346b48..76fa4a9c2b936 100644 --- a/clang/test/SemaCXX/type-traits.cpp +++ b/clang/test/SemaCXX/type-traits.cpp @@ -3993,6 +3993,10 @@ 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[])); @@ -4000,6 +4004,8 @@ 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; @@ -4033,19 +4039,21 @@ struct TriviallyEqualityComparable { }; static_assert(__is_trivially_equality_comparable(TriviallyEqualityComparable)); -struct TriviallyEqualityComparableContainsArray { - int a[4]; +template <class T> +struct TriviallyEqualityComparableContains { + T t; - bool operator==(const TriviallyEqualityComparableContainsArray&) const = default; + bool operator==(const TriviallyEqualityComparableContains&) 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)); +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<EnumWithOpEq>)); auto GetNonCapturingLambda() { return [](){ return 42; }; } @@ -4196,13 +4204,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; @@ -4434,15 +4435,8 @@ struct NotTriviallyEqualityComparableHasReferenceMember { }; 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; + Enum e; friend bool operator==(const NotTriviallyEqualityComparableHasEnum&, const NotTriviallyEqualityComparableHasEnum&) = default; }; static_assert(!__is_trivially_equality_comparable(NotTriviallyEqualityComparableHasEnum)); @@ -4465,7 +4459,7 @@ static_assert(__is_trivially_equality_comparable(TriviallyEqualityComparableRefC } #endif // __cplusplus >= 202002L -}; +} namespace can_pass_in_regs { >From 702699243051f408e0615de24f19169cb445e5d7 Mon Sep 17 00:00:00 2001 From: halbi2 <[email protected]> Date: Sat, 22 Nov 2025 11:45:45 -0500 Subject: [PATCH 2/4] fix clang-format and build error --- clang/lib/Sema/SemaTypeTraits.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/clang/lib/Sema/SemaTypeTraits.cpp b/clang/lib/Sema/SemaTypeTraits.cpp index 581989e6d0069..c816b900bef1d 100644 --- a/clang/lib/Sema/SemaTypeTraits.cpp +++ b/clang/lib/Sema/SemaTypeTraits.cpp @@ -591,19 +591,17 @@ static bool HasNoThrowOperator(CXXRecordDecl *RD, OverloadedOperatorKind Op, return false; } -static bool EqualityComparisonIsDefaulted(Sema &S, const TypeDecl *Decl, +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, /*ForValidityCheck=*/true); + Sema::SFINAETrap SFINAE(S, /*WithAccessChecking=*/true); Sema::ContextRAII TUContext(S, S.Context.getTranslationUnitDecl()); // const ClassT& obj; - OpaqueValueExpr Operand( - KeyLoc, T.withConst(), - ExprValueKind::VK_LValue); + OpaqueValueExpr Operand(KeyLoc, T.withConst(), ExprValueKind::VK_LValue); UnresolvedSet<16> Functions; // obj == obj; S.LookupBinOp(S.TUScope, {}, BinaryOperatorKind::BO_EQ, Functions); @@ -671,7 +669,9 @@ static bool isTriviallyEqualityComparableType(Sema &S, QualType Type, return false; if (CanonicalType->isEnumeralType()) { - EnumDecl *ED = CanonicalType->castAs<EnumType>()->getOriginalDecl()->getDefinitionOrSelf(); + EnumDecl *ED = CanonicalType->castAs<EnumType>() + ->getOriginalDecl() + ->getDefinitionOrSelf(); return EqualityComparisonIsDefaulted(S, ED, KeyLoc); } >From a76689f7c21d1fb7283df8e7df587d1251f755d3 Mon Sep 17 00:00:00 2001 From: halbi2 <[email protected]> Date: Sat, 22 Nov 2025 12:07:51 -0500 Subject: [PATCH 3/4] fix clang-format and build error --- clang/lib/Sema/SemaTypeTraits.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/clang/lib/Sema/SemaTypeTraits.cpp b/clang/lib/Sema/SemaTypeTraits.cpp index c816b900bef1d..d861181fea164 100644 --- a/clang/lib/Sema/SemaTypeTraits.cpp +++ b/clang/lib/Sema/SemaTypeTraits.cpp @@ -653,7 +653,8 @@ static bool HasNonDeletedDefaultedEqualityComparison(Sema &S, if (Type->isReferenceType()) return false; if (Type->isEnumeralType()) { - EnumDecl *ED = Type->castAs<EnumType>()->getOriginalDecl()->getDefinitionOrSelf(); + EnumDecl *ED = + Type->castAs<EnumType>()->getDecl()->getDefinitionOrSelf(); return EqualityComparisonIsDefaulted(S, ED, KeyLoc); } else if (const auto *RD = Type->getAsCXXRecordDecl()) return HasNonDeletedDefaultedEqualityComparison(S, RD, KeyLoc); @@ -669,9 +670,8 @@ static bool isTriviallyEqualityComparableType(Sema &S, QualType Type, return false; if (CanonicalType->isEnumeralType()) { - EnumDecl *ED = CanonicalType->castAs<EnumType>() - ->getOriginalDecl() - ->getDefinitionOrSelf(); + EnumDecl *ED = + CanonicalType->castAs<EnumType>()->getDecl()->getDefinitionOrSelf(); return EqualityComparisonIsDefaulted(S, ED, KeyLoc); } >From 5e95710693d541addcf13c33250c824b5343f332 Mon Sep 17 00:00:00 2001 From: halbi2 <[email protected]> Date: Sat, 22 Nov 2025 12:39:04 -0500 Subject: [PATCH 4/4] fix more tests --- clang/test/SemaCXX/type-traits.cpp | 56 +++++++++++++++++++----------- 1 file changed, 35 insertions(+), 21 deletions(-) diff --git a/clang/test/SemaCXX/type-traits.cpp b/clang/test/SemaCXX/type-traits.cpp index 76fa4a9c2b936..15c3bea18afc8 100644 --- a/clang/test/SemaCXX/type-traits.cpp +++ b/clang/test/SemaCXX/type-traits.cpp @@ -4053,7 +4053,11 @@ static_assert(!__is_trivially_equality_comparable(TriviallyEqualityComparableCon 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; }; } @@ -4204,27 +4208,6 @@ struct NotTriviallyEqualityComparableNonTriviallyComparableBase : NotTriviallyEq }; static_assert(!__is_trivially_equality_comparable(NotTriviallyEqualityComparableNonTriviallyComparableBase)); -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; @@ -4312,6 +4295,37 @@ 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<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(); _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
