Skip to content

Commit

Permalink
Reapply "[Clang] Fix __is_trivially_equality_comparable returning tru…
Browse files Browse the repository at this point in the history
…e with ineligebile defaulted overloads" (llvm#97002) (llvm#97894)

This reverts commit 567b2c6.
  • Loading branch information
philnik777 authored Jul 12, 2024
1 parent 5ff3ff3 commit c6e996a
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 64 deletions.
3 changes: 3 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -827,6 +827,9 @@ Bug Fixes in This Version

- Fixed Clang crashing when failing to perform some C++ Initialization Sequences. (#GH98102)

- ``__is_trivially_equality_comparable`` no longer returns true for types which
have a constrained defaulted comparison operator (#GH89293).

Bug Fixes to Compiler Builtins
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Expand Down
3 changes: 0 additions & 3 deletions clang/include/clang/AST/Type.h
Original file line number Diff line number Diff line change
Expand Up @@ -1142,9 +1142,6 @@ class QualType {
/// Return true if this is a trivially relocatable type.
bool isTriviallyRelocatableType(const ASTContext &Context) const;

/// Return true if this is a trivially equality comparable type.
bool isTriviallyEqualityComparableType(const ASTContext &Context) const;

/// Returns true if it is a class and it might be dynamic.
bool mayBeDynamicClass() const;

Expand Down
60 changes: 0 additions & 60 deletions clang/lib/AST/Type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2815,66 +2815,6 @@ bool QualType::isTriviallyRelocatableType(const ASTContext &Context) const {
}
}

static bool
HasNonDeletedDefaultedEqualityComparison(const CXXRecordDecl *Decl) {
if (Decl->isUnion())
return false;
if (Decl->isLambda())
return Decl->isCapturelessLambda();

auto IsDefaultedOperatorEqualEqual = [&](const FunctionDecl *Function) {
return Function->getOverloadedOperator() ==
OverloadedOperatorKind::OO_EqualEqual &&
Function->isDefaulted() && Function->getNumParams() > 0 &&
(Function->getParamDecl(0)->getType()->isReferenceType() ||
Decl->isTriviallyCopyable());
};

if (llvm::none_of(Decl->methods(), IsDefaultedOperatorEqualEqual) &&
llvm::none_of(Decl->friends(), [&](const FriendDecl *Friend) {
if (NamedDecl *ND = Friend->getFriendDecl()) {
return ND->isFunctionOrFunctionTemplate() &&
IsDefaultedOperatorEqualEqual(ND->getAsFunction());
}
return false;
}))
return false;

return llvm::all_of(Decl->bases(),
[](const CXXBaseSpecifier &BS) {
if (const auto *RD = BS.getType()->getAsCXXRecordDecl())
return HasNonDeletedDefaultedEqualityComparison(RD);
return true;
}) &&
llvm::all_of(Decl->fields(), [](const FieldDecl *FD) {
auto Type = FD->getType();
if (Type->isArrayType())
Type = Type->getBaseElementTypeUnsafe()->getCanonicalTypeUnqualified();

if (Type->isReferenceType() || Type->isEnumeralType())
return false;
if (const auto *RD = Type->getAsCXXRecordDecl())
return HasNonDeletedDefaultedEqualityComparison(RD);
return true;
});
}

bool QualType::isTriviallyEqualityComparableType(
const ASTContext &Context) const {
QualType CanonicalType = getCanonicalType();
if (CanonicalType->isIncompleteType() || CanonicalType->isDependentType() ||
CanonicalType->isEnumeralType() || CanonicalType->isArrayType())
return false;

if (const auto *RD = CanonicalType->getAsCXXRecordDecl()) {
if (!HasNonDeletedDefaultedEqualityComparison(RD))
return false;
}

return Context.hasUniqueObjectRepresentations(
CanonicalType, /*CheckIfTriviallyCopyable=*/false);
}

bool QualType::isNonWeakInMRRWithObjCWeak(const ASTContext &Context) const {
return !Context.getLangOpts().ObjCAutoRefCount &&
Context.getLangOpts().ObjCWeak &&
Expand Down
79 changes: 78 additions & 1 deletion clang/lib/Sema/SemaExprCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5129,6 +5129,83 @@ static bool HasNoThrowOperator(const RecordType *RT, OverloadedOperatorKind Op,
return false;
}

static bool HasNonDeletedDefaultedEqualityComparison(Sema &S,
const CXXRecordDecl *Decl,
SourceLocation KeyLoc) {
if (Decl->isUnion())
return false;
if (Decl->isLambda())
return Decl->isCapturelessLambda();

{
EnterExpressionEvaluationContext UnevaluatedContext(
S, Sema::ExpressionEvaluationContext::Unevaluated);
Sema::SFINAETrap SFINAE(S, /*AccessCheckingSFINAE=*/true);
Sema::ContextRAII TUContext(S, S.Context.getTranslationUnitDecl());

// const ClassT& obj;
OpaqueValueExpr Operand(
{}, Decl->getTypeForDecl()->getCanonicalTypeUnqualified().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 (ParamT.getNonReferenceType()->getUnqualifiedDesugaredType() !=
Decl->getTypeForDecl())
return false;
}

return llvm::all_of(Decl->bases(),
[&](const CXXBaseSpecifier &BS) {
if (const auto *RD = BS.getType()->getAsCXXRecordDecl())
return HasNonDeletedDefaultedEqualityComparison(
S, RD, KeyLoc);
return true;
}) &&
llvm::all_of(Decl->fields(), [&](const FieldDecl *FD) {
auto Type = FD->getType();
if (Type->isArrayType())
Type = Type->getBaseElementTypeUnsafe()
->getCanonicalTypeUnqualified();

if (Type->isReferenceType() || Type->isEnumeralType())
return false;
if (const auto *RD = Type->getAsCXXRecordDecl())
return HasNonDeletedDefaultedEqualityComparison(S, RD, KeyLoc);
return true;
});
}

static bool isTriviallyEqualityComparableType(Sema &S, QualType Type, SourceLocation KeyLoc) {
QualType CanonicalType = Type.getCanonicalType();
if (CanonicalType->isIncompleteType() || CanonicalType->isDependentType() ||
CanonicalType->isEnumeralType() || CanonicalType->isArrayType())
return false;

if (const auto *RD = CanonicalType->getAsCXXRecordDecl()) {
if (!HasNonDeletedDefaultedEqualityComparison(S, RD, KeyLoc))
return false;
}

return S.getASTContext().hasUniqueObjectRepresentations(
CanonicalType, /*CheckIfTriviallyCopyable=*/false);
}

static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT,
SourceLocation KeyLoc,
TypeSourceInfo *TInfo) {
Expand Down Expand Up @@ -5561,7 +5638,7 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT,
Self.Diag(KeyLoc, diag::err_builtin_pass_in_regs_non_class) << T;
return false;
case UTT_IsTriviallyEqualityComparable:
return T.isTriviallyEqualityComparableType(C);
return isTriviallyEqualityComparableType(Self, T, KeyLoc);
}
}

Expand Down
49 changes: 49 additions & 0 deletions clang/test/SemaCXX/type-traits.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3677,6 +3677,12 @@ struct NonTriviallyEqualityComparableNoComparator {
};
static_assert(!__is_trivially_equality_comparable(NonTriviallyEqualityComparableNoComparator));

struct NonTriviallyEqualityComparableConvertibleToBuiltin {
int i;
operator unsigned() const;
};
static_assert(!__is_trivially_equality_comparable(NonTriviallyEqualityComparableConvertibleToBuiltin));

struct NonTriviallyEqualityComparableNonDefaultedComparator {
int i;
int j;
Expand Down Expand Up @@ -3885,8 +3891,51 @@ struct NotTriviallyEqualityComparableNonTriviallyEqualityComparableArrs2 {

bool operator==(const NotTriviallyEqualityComparableNonTriviallyEqualityComparableArrs2&) const = default;
};

static_assert(!__is_trivially_equality_comparable(NotTriviallyEqualityComparableNonTriviallyEqualityComparableArrs2));

template<bool B>
struct MaybeTriviallyEqualityComparable {
int i;
bool operator==(const MaybeTriviallyEqualityComparable&) const requires B = default;
bool operator==(const MaybeTriviallyEqualityComparable& rhs) const { return (i % 3) == (rhs.i % 3); }
};
static_assert(__is_trivially_equality_comparable(MaybeTriviallyEqualityComparable<true>));
static_assert(!__is_trivially_equality_comparable(MaybeTriviallyEqualityComparable<false>));

struct NotTriviallyEqualityComparableMoreConstrainedExternalOp {
int i;
bool operator==(const NotTriviallyEqualityComparableMoreConstrainedExternalOp&) const = default;
};

bool operator==(const NotTriviallyEqualityComparableMoreConstrainedExternalOp&,
const NotTriviallyEqualityComparableMoreConstrainedExternalOp&) __attribute__((enable_if(true, ""))) {}

static_assert(!__is_trivially_equality_comparable(NotTriviallyEqualityComparableMoreConstrainedExternalOp));

struct TriviallyEqualityComparableExternalDefaultedOp {
int i;
friend bool operator==(TriviallyEqualityComparableExternalDefaultedOp, TriviallyEqualityComparableExternalDefaultedOp);
};
bool operator==(TriviallyEqualityComparableExternalDefaultedOp, TriviallyEqualityComparableExternalDefaultedOp) = default;

static_assert(__is_trivially_equality_comparable(TriviallyEqualityComparableExternalDefaultedOp));

struct EqualityComparableBase {
bool operator==(const EqualityComparableBase&) const = default;
};

struct ComparingBaseOnly : EqualityComparableBase {
int j_ = 0;
};
static_assert(!__is_trivially_equality_comparable(ComparingBaseOnly));

template <class>
class Template {};

// Make sure we don't crash when instantiating a type
static_assert(!__is_trivially_equality_comparable(Template<Template<int>>));

namespace hidden_friend {

struct TriviallyEqualityComparable {
Expand Down

0 comments on commit c6e996a

Please sign in to comment.