Skip to content

Commit d1507bf

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

File tree

5 files changed

+123
-64
lines changed

5 files changed

+123
-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: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5197,6 +5197,82 @@ 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+
5226+
const auto *CallExpr = dyn_cast<CXXOperatorCallExpr>(Result.get());
5227+
if (!CallExpr)
5228+
return false;
5229+
const auto *Callee = CallExpr->getDirectCallee();
5230+
auto ParamT = Callee->getParamDecl(0)->getType();
5231+
if (!Callee->isDefaulted())
5232+
return false;
5233+
if (!ParamT->isReferenceType() && !Decl->isTriviallyCopyable())
5234+
return false;
5235+
if (ParamT.getNonReferenceType()->getUnqualifiedDesugaredType() !=
5236+
Decl->getTypeForDecl())
5237+
return false;
5238+
}
5239+
5240+
return llvm::all_of(Decl->bases(),
5241+
[&](const CXXBaseSpecifier &BS) {
5242+
if (const auto *RD = BS.getType()->getAsCXXRecordDecl())
5243+
return HasNonDeletedDefaultedEqualityComparison(S,
5244+
RD);
5245+
return true;
5246+
}) &&
5247+
llvm::all_of(Decl->fields(), [&](const FieldDecl *FD) {
5248+
auto Type = FD->getType();
5249+
if (Type->isArrayType())
5250+
Type = Type->getBaseElementTypeUnsafe()
5251+
->getCanonicalTypeUnqualified();
5252+
5253+
if (Type->isReferenceType() || Type->isEnumeralType())
5254+
return false;
5255+
if (const auto *RD = Type->getAsCXXRecordDecl())
5256+
return HasNonDeletedDefaultedEqualityComparison(S, RD);
5257+
return true;
5258+
});
5259+
}
5260+
5261+
static bool isTriviallyEqualityComparableType(Sema &S, QualType Type) {
5262+
QualType CanonicalType = Type.getCanonicalType();
5263+
if (CanonicalType->isIncompleteType() || CanonicalType->isDependentType() ||
5264+
CanonicalType->isEnumeralType() || CanonicalType->isArrayType())
5265+
return false;
5266+
5267+
if (const auto *RD = CanonicalType->getAsCXXRecordDecl()) {
5268+
if (!HasNonDeletedDefaultedEqualityComparison(S, RD))
5269+
return false;
5270+
}
5271+
5272+
return S.getASTContext().hasUniqueObjectRepresentations(
5273+
CanonicalType, /*CheckIfTriviallyCopyable=*/false);
5274+
}
5275+
52005276
static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT,
52015277
SourceLocation KeyLoc,
52025278
TypeSourceInfo *TInfo) {
@@ -5627,7 +5703,7 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT,
56275703
Self.Diag(KeyLoc, diag::err_builtin_pass_in_regs_non_class) << T;
56285704
return false;
56295705
case UTT_IsTriviallyEqualityComparable:
5630-
return T.isTriviallyEqualityComparableType(C);
5706+
return isTriviallyEqualityComparableType(Self, T);
56315707
}
56325708
}
56335709

clang/test/SemaCXX/type-traits.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3677,6 +3677,12 @@ struct NonTriviallyEqualityComparableNoComparator {
36773677
};
36783678
static_assert(!__is_trivially_equality_comparable(NonTriviallyEqualityComparableNoComparator));
36793679

3680+
struct NonTriviallyEqualityComparableConvertibleToBuiltin {
3681+
int i;
3682+
operator unsigned() const;
3683+
};
3684+
static_assert(!__is_trivially_equality_comparable(NonTriviallyEqualityComparableConvertibleToBuiltin));
3685+
36803686
struct NonTriviallyEqualityComparableNonDefaultedComparator {
36813687
int i;
36823688
int j;
@@ -3885,8 +3891,45 @@ struct NotTriviallyEqualityComparableNonTriviallyEqualityComparableArrs2 {
38853891

38863892
bool operator==(const NotTriviallyEqualityComparableNonTriviallyEqualityComparableArrs2&) const = default;
38873893
};
3894+
38883895
static_assert(!__is_trivially_equality_comparable(NotTriviallyEqualityComparableNonTriviallyEqualityComparableArrs2));
38893896

3897+
template<bool B>
3898+
struct MaybeTriviallyEqualityComparable {
3899+
int i;
3900+
bool operator==(const MaybeTriviallyEqualityComparable&) const requires B = default;
3901+
bool operator==(const MaybeTriviallyEqualityComparable& rhs) const { return (i % 3) == (rhs.i % 3); }
3902+
};
3903+
static_assert(__is_trivially_equality_comparable(MaybeTriviallyEqualityComparable<true>));
3904+
static_assert(!__is_trivially_equality_comparable(MaybeTriviallyEqualityComparable<false>));
3905+
3906+
struct NotTriviallyEqualityComparableMoreConstrainedExternalOp {
3907+
int i;
3908+
bool operator==(const NotTriviallyEqualityComparableMoreConstrainedExternalOp&) const = default;
3909+
};
3910+
3911+
bool operator==(const NotTriviallyEqualityComparableMoreConstrainedExternalOp&,
3912+
const NotTriviallyEqualityComparableMoreConstrainedExternalOp&) __attribute__((enable_if(true, ""))) {}
3913+
3914+
static_assert(!__is_trivially_equality_comparable(NotTriviallyEqualityComparableMoreConstrainedExternalOp));
3915+
3916+
struct TriviallyEqualityComparableExternalDefaultedOp {
3917+
int i;
3918+
friend bool operator==(TriviallyEqualityComparableExternalDefaultedOp, TriviallyEqualityComparableExternalDefaultedOp);
3919+
};
3920+
bool operator==(TriviallyEqualityComparableExternalDefaultedOp, TriviallyEqualityComparableExternalDefaultedOp) = default;
3921+
3922+
static_assert(__is_trivially_equality_comparable(TriviallyEqualityComparableExternalDefaultedOp));
3923+
3924+
struct EqualityComparableBase {
3925+
bool operator==(const EqualityComparableBase&) const = default;
3926+
};
3927+
3928+
struct ComparingBaseOnly : EqualityComparableBase {
3929+
int j_ = 0;
3930+
};
3931+
static_assert(!__is_trivially_equality_comparable(ComparingBaseOnly));
3932+
38903933
namespace hidden_friend {
38913934

38923935
struct TriviallyEqualityComparable {

0 commit comments

Comments
 (0)