diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 62c8c0130c3d0..00c8b18826212 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -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 ^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/lib/Sema/SemaTypeTraits.cpp b/clang/lib/Sema/SemaTypeTraits.cpp index 38877967af05e..d861181fea164 100644 --- a/clang/lib/Sema/SemaTypeTraits.cpp +++ b/clang/lib/Sema/SemaTypeTraits.cpp @@ -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(Result.get()); + if (!CallExpr) + return isa(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(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 +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(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 +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()->getDecl()->getDefinitionOrSelf(); + return EqualityComparisonIsDefaulted(S, ED, KeyLoc); + } else if (const auto *RD = Type->getAsCXXRecordDecl()) return HasNonDeletedDefaultedEqualityComparison(S, RD, KeyLoc); return true; }); @@ -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()->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 9ef44d0346b48..107dd72590420 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,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 +struct TriviallyEqualityComparableContains { + T t; + + bool operator==(const TriviallyEqualityComparableContains&) const = default; +}; + +static_assert(__is_trivially_equality_comparable(TriviallyEqualityComparableContains)); +static_assert(!__is_trivially_equality_comparable(TriviallyEqualityComparableContains)); +static_assert(!__is_trivially_equality_comparable(TriviallyEqualityComparableContains)); +static_assert(!__is_trivially_equality_comparable(TriviallyEqualityComparableContains)); +static_assert(!__is_trivially_equality_comparable(TriviallyEqualityComparableContains)); +static_assert(__is_trivially_equality_comparable(TriviallyEqualityComparableContains)); +static_assert(__is_trivially_equality_comparable(TriviallyEqualityComparableContains)); +static_assert(__is_trivially_equality_comparable(TriviallyEqualityComparableContains)); +static_assert(__is_trivially_equality_comparable(TriviallyEqualityComparableContains)); +static_assert(__is_trivially_equality_comparable(TriviallyEqualityComparableContains)); +static_assert(!__is_trivially_equality_comparable(TriviallyEqualityComparableContains)); +static_assert(!__is_trivially_equality_comparable(TriviallyEqualityComparableContains)); +static_assert(!__is_trivially_equality_comparable(TriviallyEqualityComparableContains)); auto GetNonCapturingLambda() { return [](){ return 42; }; } @@ -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; @@ -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; @@ -4311,6 +4289,38 @@ struct TriviallyEqualityComparable { }; static_assert(__is_trivially_equality_comparable(TriviallyEqualityComparable)); +template +struct TriviallyEqualityComparableContains { + T t; + + friend bool operator==(const TriviallyEqualityComparableContains&, const TriviallyEqualityComparableContains&) = default; +}; + +static_assert(__is_trivially_equality_comparable(TriviallyEqualityComparableContains)); +static_assert(!__is_trivially_equality_comparable(TriviallyEqualityComparableContains)); +static_assert(!__is_trivially_equality_comparable(TriviallyEqualityComparableContains)); +static_assert(!__is_trivially_equality_comparable(TriviallyEqualityComparableContains)); +static_assert(!__is_trivially_equality_comparable(TriviallyEqualityComparableContains)); +static_assert(__is_trivially_equality_comparable(TriviallyEqualityComparableContains)); +static_assert(__is_trivially_equality_comparable(TriviallyEqualityComparableContains)); +static_assert(__is_trivially_equality_comparable(TriviallyEqualityComparableContains)); +static_assert(__is_trivially_equality_comparable(TriviallyEqualityComparableContains)); +static_assert(__is_trivially_equality_comparable(TriviallyEqualityComparableContains)); +static_assert(!__is_trivially_equality_comparable(TriviallyEqualityComparableContains)); +static_assert(!__is_trivially_equality_comparable(TriviallyEqualityComparableContains)); +static_assert(!__is_trivially_equality_comparable(TriviallyEqualityComparableContains)); + +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(); @@ -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&); @@ -4465,7 +4455,7 @@ static_assert(__is_trivially_equality_comparable(TriviallyEqualityComparableRefC } #endif // __cplusplus >= 202002L -}; +} namespace can_pass_in_regs {