Skip to content

Commit cabff59

Browse files
committed
[Clang] Fix __is_trivially_equality_comparable returning true with ineligebile defaulted overloads
1 parent daaaf4e commit cabff59

File tree

5 files changed

+113
-64
lines changed

5 files changed

+113
-64
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -640,6 +640,9 @@ Bug Fixes in This Version
640640
- Correctly reject declarations where a statement is required in C.
641641
Fixes #GH92775
642642

643+
- ``__is_trivially_equality_comparable`` no longer returns true for types which
644+
have a constrained defaulted comparison operator (#GH89293).
645+
643646
Bug Fixes to Compiler Builtins
644647
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
645648

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
@@ -2778,66 +2778,6 @@ bool QualType::isTriviallyRelocatableType(const ASTContext &Context) const {
27782778
}
27792779
}
27802780

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

clang/lib/Sema/SemaExprCXX.cpp

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5197,6 +5197,78 @@ static bool HasNoThrowOperator(const RecordType *RT, OverloadedOperatorKind Op,
51975197
return false;
51985198
}
51995199

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

clang/test/SemaCXX/type-traits.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3885,8 +3885,45 @@ 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+
3918+
struct EqualityComparableBase {
3919+
bool operator==(const EqualityComparableBase&) const = default;
3920+
};
3921+
3922+
struct ComparingBaseOnly : EqualityComparableBase {
3923+
int j_ = 0;
3924+
};
3925+
static_assert(!__is_trivially_equality_comparable(ComparingBaseOnly));
3926+
38903927
namespace hidden_friend {
38913928

38923929
struct TriviallyEqualityComparable {

0 commit comments

Comments
 (0)