Skip to content

Commit

Permalink
[clang] __is_trivially_equality_comparable for types containing lambd…
Browse files Browse the repository at this point in the history
…as (#68506)

Lambdas (closure types) are trivially equality-comparable iff they are
non-capturing, because non-capturing lambdas are convertible to function
pointers: if (lam1 == lam2) compiles, then lam1 and lam2 must have
the same type, and be always-equal, and be empty.
  • Loading branch information
AMP999 committed Oct 11, 2023
1 parent 3bfc5eb commit 4313351
Show file tree
Hide file tree
Showing 6 changed files with 33 additions and 9 deletions.
6 changes: 6 additions & 0 deletions clang/include/clang/AST/DeclCXX.h
Original file line number Diff line number Diff line change
Expand Up @@ -1052,6 +1052,12 @@ class CXXRecordDecl : public RecordDecl {
return static_cast<LambdaCaptureDefault>(getLambdaData().CaptureDefault);
}

bool isCapturelessLambda() const {
if (!isLambda())
return false;
return getLambdaCaptureDefault() == LCD_None && capture_size() == 0;
}

/// Set the captures for this lambda closure type.
void setCaptures(ASTContext &Context, ArrayRef<LambdaCapture> Captures);

Expand Down
2 changes: 1 addition & 1 deletion clang/lib/AST/DeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -686,7 +686,7 @@ bool CXXRecordDecl::lambdaIsDefaultConstructibleAndAssignable() const {
// C++17 [expr.prim.lambda]p21:
// The closure type associated with a lambda-expression has no default
// constructor and a deleted copy assignment operator.
if (getLambdaCaptureDefault() != LCD_None || capture_size() != 0)
if (!isCapturelessLambda())
return false;
return getASTContext().getLangOpts().CPlusPlus20;
}
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/AST/Type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2663,6 +2663,8 @@ 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() ==
Expand Down
5 changes: 2 additions & 3 deletions clang/lib/CodeGen/CodeGenFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1216,11 +1216,10 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy,
SkippedChecks.set(SanitizerKind::ObjectSize, true);
QualType ThisTy = MD->getThisType();

// If this is the call operator of a lambda with no capture-default, it
// If this is the call operator of a lambda with no captures, it
// may have a static invoker function, which may call this operator with
// a null 'this' pointer.
if (isLambdaCallOperator(MD) &&
MD->getParent()->getLambdaCaptureDefault() == LCD_None)
if (isLambdaCallOperator(MD) && MD->getParent()->isCapturelessLambda())
SkippedChecks.set(SanitizerKind::Null, true);

EmitTypeCheck(
Expand Down
3 changes: 1 addition & 2 deletions clang/lib/Sema/SemaLambda.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -393,8 +393,7 @@ void Sema::DiagnoseInvalidExplicitObjectParameterInLambda(
CXXRecordDecl *RD = Method->getParent();
if (Method->getType()->isDependentType())
return;
if (RD->getLambdaCaptureDefault() == LambdaCaptureDefault::LCD_None &&
RD->capture_size() == 0)
if (RD->isCapturelessLambda())
return;
QualType ExplicitObjectParameterType = Method->getParamDecl(0)
->getType()
Expand Down
24 changes: 21 additions & 3 deletions clang/test/SemaCXX/type-traits.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3160,11 +3160,18 @@ static_assert(!__is_trivially_equality_comparable(float), "");
static_assert(!__is_trivially_equality_comparable(double), "");
static_assert(!__is_trivially_equality_comparable(long double), "");

struct TriviallyEqualityComparableNoDefaultedComparator {
struct NonTriviallyEqualityComparableNoComparator {
int i;
int j;
};
static_assert(!__is_trivially_equality_comparable(TriviallyEqualityComparableNoDefaultedComparator), "");
static_assert(!__is_trivially_equality_comparable(NonTriviallyEqualityComparableNoComparator), "");

struct NonTriviallyEqualityComparableNonDefaultedComparator {
int i;
int j;
bool operator==(const NonTriviallyEqualityComparableNonDefaultedComparator&);
};
static_assert(!__is_trivially_equality_comparable(NonTriviallyEqualityComparableNonDefaultedComparator), "");

#if __cplusplus >= 202002L

Expand All @@ -3177,7 +3184,7 @@ struct TriviallyEqualityComparable {

bool operator==(const TriviallyEqualityComparable&) const = default;
};
static_assert(__is_trivially_equality_comparable(TriviallyEqualityComparable), "");
static_assert(__is_trivially_equality_comparable(TriviallyEqualityComparable));

struct TriviallyEqualityComparableContainsArray {
int a[4];
Expand All @@ -3193,6 +3200,17 @@ struct TriviallyEqualityComparableContainsMultiDimensionArray {
};
static_assert(__is_trivially_equality_comparable(TriviallyEqualityComparableContainsMultiDimensionArray));

auto GetNonCapturingLambda() { return [](){ return 42; }; }

struct TriviallyEqualityComparableContainsLambda {
[[no_unique_address]] decltype(GetNonCapturingLambda()) l;
int i;

bool operator==(const TriviallyEqualityComparableContainsLambda&) const = default;
};
static_assert(!__is_trivially_equality_comparable(decltype(GetNonCapturingLambda()))); // padding
static_assert(__is_trivially_equality_comparable(TriviallyEqualityComparableContainsLambda));

struct TriviallyEqualityComparableNonTriviallyCopyable {
TriviallyEqualityComparableNonTriviallyCopyable(const TriviallyEqualityComparableNonTriviallyCopyable&);
~TriviallyEqualityComparableNonTriviallyCopyable();
Expand Down

0 comments on commit 4313351

Please sign in to comment.