Skip to content

Commit 31e3346

Browse files
committed
[Clang] Fix __is_trivially_equality_comparable returning true with ineligebile defaulted overloads
1 parent 465bfd4 commit 31e3346

File tree

5 files changed

+101
-64
lines changed

5 files changed

+101
-64
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -606,6 +606,9 @@ Bug Fixes in This Version
606606
- ``__is_array`` and ``__is_bounded_array`` no longer return ``true`` for
607607
zero-sized arrays. Fixes (#GH54705).
608608

609+
- ``__is_trivially_equality_comparable`` no longer returns true for types which
610+
have a constrained defaulted comparison operator (#GH89293).
611+
609612
Bug Fixes to Compiler Builtins
610613
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
611614

clang/include/clang/AST/Type.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1126,9 +1126,6 @@ class QualType {
11261126
/// Return true if this is a trivially relocatable type.
11271127
bool isTriviallyRelocatableType(const ASTContext &Context) const;
11281128

1129-
/// Return true if this is a trivially equality comparable type.
1130-
bool isTriviallyEqualityComparableType(const ASTContext &Context) const;
1131-
11321129
/// Returns true if it is a class and it might be dynamic.
11331130
bool mayBeDynamicClass() const;
11341131

clang/lib/AST/Type.cpp

Lines changed: 0 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -2768,66 +2768,6 @@ bool QualType::isTriviallyRelocatableType(const ASTContext &Context) const {
27682768
}
27692769
}
27702770

2771-
static bool
2772-
HasNonDeletedDefaultedEqualityComparison(const CXXRecordDecl *Decl) {
2773-
if (Decl->isUnion())
2774-
return false;
2775-
if (Decl->isLambda())
2776-
return Decl->isCapturelessLambda();
2777-
2778-
auto IsDefaultedOperatorEqualEqual = [&](const FunctionDecl *Function) {
2779-
return Function->getOverloadedOperator() ==
2780-
OverloadedOperatorKind::OO_EqualEqual &&
2781-
Function->isDefaulted() && Function->getNumParams() > 0 &&
2782-
(Function->getParamDecl(0)->getType()->isReferenceType() ||
2783-
Decl->isTriviallyCopyable());
2784-
};
2785-
2786-
if (llvm::none_of(Decl->methods(), IsDefaultedOperatorEqualEqual) &&
2787-
llvm::none_of(Decl->friends(), [&](const FriendDecl *Friend) {
2788-
if (NamedDecl *ND = Friend->getFriendDecl()) {
2789-
return ND->isFunctionOrFunctionTemplate() &&
2790-
IsDefaultedOperatorEqualEqual(ND->getAsFunction());
2791-
}
2792-
return false;
2793-
}))
2794-
return false;
2795-
2796-
return llvm::all_of(Decl->bases(),
2797-
[](const CXXBaseSpecifier &BS) {
2798-
if (const auto *RD = BS.getType()->getAsCXXRecordDecl())
2799-
return HasNonDeletedDefaultedEqualityComparison(RD);
2800-
return true;
2801-
}) &&
2802-
llvm::all_of(Decl->fields(), [](const FieldDecl *FD) {
2803-
auto Type = FD->getType();
2804-
if (Type->isArrayType())
2805-
Type = Type->getBaseElementTypeUnsafe()->getCanonicalTypeUnqualified();
2806-
2807-
if (Type->isReferenceType() || Type->isEnumeralType())
2808-
return false;
2809-
if (const auto *RD = Type->getAsCXXRecordDecl())
2810-
return HasNonDeletedDefaultedEqualityComparison(RD);
2811-
return true;
2812-
});
2813-
}
2814-
2815-
bool QualType::isTriviallyEqualityComparableType(
2816-
const ASTContext &Context) const {
2817-
QualType CanonicalType = getCanonicalType();
2818-
if (CanonicalType->isIncompleteType() || CanonicalType->isDependentType() ||
2819-
CanonicalType->isEnumeralType() || CanonicalType->isArrayType())
2820-
return false;
2821-
2822-
if (const auto *RD = CanonicalType->getAsCXXRecordDecl()) {
2823-
if (!HasNonDeletedDefaultedEqualityComparison(RD))
2824-
return false;
2825-
}
2826-
2827-
return Context.hasUniqueObjectRepresentations(
2828-
CanonicalType, /*CheckIfTriviallyCopyable=*/false);
2829-
}
2830-
28312771
bool QualType::isNonWeakInMRRWithObjCWeak(const ASTContext &Context) const {
28322772
return !Context.getLangOpts().ObjCAutoRefCount &&
28332773
Context.getLangOpts().ObjCWeak &&

clang/lib/Sema/SemaExprCXX.cpp

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5199,6 +5199,75 @@ static bool HasNoThrowOperator(const RecordType *RT, OverloadedOperatorKind Op,
51995199
return false;
52005200
}
52015201

5202+
static bool
5203+
HasNonDeletedDefaultedEqualityComparison(Sema& S, const CXXRecordDecl *Decl) {
5204+
if (Decl->isUnion())
5205+
return false;
5206+
if (Decl->isLambda())
5207+
return Decl->isCapturelessLambda();
5208+
5209+
{
5210+
EnterExpressionEvaluationContext UnevaluatedContext(
5211+
S, Sema::ExpressionEvaluationContext::Unevaluated);
5212+
Sema::SFINAETrap SFINAE(S, /*AccessCheckingSFINAE=*/true);
5213+
Sema::ContextRAII TUContext(S, S.Context.getTranslationUnitDecl());
5214+
5215+
// const ClassT& obj;
5216+
OpaqueValueExpr Operand(
5217+
{}, Decl->getTypeForDecl()->getCanonicalTypeUnqualified().withConst(),
5218+
ExprValueKind::VK_LValue);
5219+
UnresolvedSet<16> Functions;
5220+
// obj == obj;
5221+
S.LookupBinOp(S.TUScope, {}, BinaryOperatorKind::BO_EQ, Functions);
5222+
5223+
auto Result = S.CreateOverloadedBinOp({}, BinaryOperatorKind::BO_EQ,
5224+
Functions, &Operand, &Operand);
5225+
if (Result.isInvalid() || SFINAE.hasErrorOccurred())
5226+
return false;
5227+
auto Callee = Result.getAs<CXXOperatorCallExpr>()->getDirectCallee();
5228+
auto ParamT = Callee->getParamDecl(0)->getType();
5229+
if (!Callee->isDefaulted() ||
5230+
(!ParamT->isReferenceType() && !Decl->isTriviallyCopyable()))
5231+
return false;
5232+
}
5233+
5234+
return llvm::all_of(Decl->bases(),
5235+
[&](const CXXBaseSpecifier &BS) {
5236+
if (const auto *RD = BS.getType()->getAsCXXRecordDecl())
5237+
return HasNonDeletedDefaultedEqualityComparison(S,
5238+
RD);
5239+
return true;
5240+
}) &&
5241+
llvm::all_of(Decl->fields(), [&](const FieldDecl *FD) {
5242+
auto Type = FD->getType();
5243+
if (Type->isArrayType())
5244+
Type = Type->getBaseElementTypeUnsafe()
5245+
->getCanonicalTypeUnqualified();
5246+
5247+
if (Type->isReferenceType() || Type->isEnumeralType())
5248+
return false;
5249+
if (const auto *RD = Type->getAsCXXRecordDecl())
5250+
return HasNonDeletedDefaultedEqualityComparison(S, RD);
5251+
return true;
5252+
});
5253+
}
5254+
5255+
static bool isTriviallyEqualityComparableType(
5256+
Sema& S, QualType Type) {
5257+
QualType CanonicalType = Type.getCanonicalType();
5258+
if (CanonicalType->isIncompleteType() || CanonicalType->isDependentType() ||
5259+
CanonicalType->isEnumeralType() || CanonicalType->isArrayType())
5260+
return false;
5261+
5262+
if (const auto *RD = CanonicalType->getAsCXXRecordDecl()) {
5263+
if (!HasNonDeletedDefaultedEqualityComparison(S, RD))
5264+
return false;
5265+
}
5266+
5267+
return S.getASTContext().hasUniqueObjectRepresentations(
5268+
CanonicalType, /*CheckIfTriviallyCopyable=*/false);
5269+
}
5270+
52025271
static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT,
52035272
SourceLocation KeyLoc,
52045273
TypeSourceInfo *TInfo) {
@@ -5629,7 +5698,7 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT,
56295698
Self.Diag(KeyLoc, diag::err_builtin_pass_in_regs_non_class) << T;
56305699
return false;
56315700
case UTT_IsTriviallyEqualityComparable:
5632-
return T.isTriviallyEqualityComparableType(C);
5701+
return isTriviallyEqualityComparableType(Self, T);
56335702
}
56345703
}
56355704

clang/test/SemaCXX/type-traits.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3885,8 +3885,36 @@ struct NotTriviallyEqualityComparableNonTriviallyEqualityComparableArrs2 {
38853885

38863886
bool operator==(const NotTriviallyEqualityComparableNonTriviallyEqualityComparableArrs2&) const = default;
38873887
};
3888+
38883889
static_assert(!__is_trivially_equality_comparable(NotTriviallyEqualityComparableNonTriviallyEqualityComparableArrs2));
38893890

3891+
template<bool B>
3892+
struct MaybeTriviallyEqualityComparable {
3893+
int i;
3894+
bool operator==(const MaybeTriviallyEqualityComparable&) const requires B = default;
3895+
bool operator==(const MaybeTriviallyEqualityComparable& rhs) const { return (i % 3) == (rhs.i % 3); }
3896+
};
3897+
static_assert(__is_trivially_equality_comparable(MaybeTriviallyEqualityComparable<true>));
3898+
static_assert(!__is_trivially_equality_comparable(MaybeTriviallyEqualityComparable<false>));
3899+
3900+
struct NotTriviallyEqualityComparableMoreConstrainedExternalOp {
3901+
int i;
3902+
bool operator==(const NotTriviallyEqualityComparableMoreConstrainedExternalOp&) const = default;
3903+
};
3904+
3905+
bool operator==(const NotTriviallyEqualityComparableMoreConstrainedExternalOp&,
3906+
const NotTriviallyEqualityComparableMoreConstrainedExternalOp&) __attribute__((enable_if(true, ""))) {}
3907+
3908+
static_assert(!__is_trivially_equality_comparable(NotTriviallyEqualityComparableMoreConstrainedExternalOp));
3909+
3910+
struct TriviallyEqualityComparableExternalDefaultedOp {
3911+
int i;
3912+
friend bool operator==(TriviallyEqualityComparableExternalDefaultedOp, TriviallyEqualityComparableExternalDefaultedOp);
3913+
};
3914+
bool operator==(TriviallyEqualityComparableExternalDefaultedOp, TriviallyEqualityComparableExternalDefaultedOp) = default;
3915+
3916+
static_assert(__is_trivially_equality_comparable(TriviallyEqualityComparableExternalDefaultedOp));
3917+
38903918
namespace hidden_friend {
38913919

38923920
struct TriviallyEqualityComparable {

0 commit comments

Comments
 (0)