Skip to content

Commit bd77a26

Browse files
Sirraidecor3ntinAaronBallman
authored
[Clang][Sema] Properly get captured 'this' pointer in lambdas with an explicit object parameter in constant evaluator (#81102)
There were some bugs wrt explicit object parameters in lambdas in the constant evaluator: - The code evaluating a `CXXThisExpr` wasn’t checking for explicit object parameters at all and thus assumed that there was no `this` in the current context because the lambda didn’t have one, even though we were in a member function and had captured its `this`. - The code retrieving captures as lvalues *did* account for explicit object parameters, but it did not handle the case of the explicit object parameter being passed by value rather than by reference. This fixes #80997. --------- Co-authored-by: cor3ntin <[email protected]> Co-authored-by: Aaron Ballman <[email protected]>
1 parent 175b533 commit bd77a26

File tree

3 files changed

+111
-55
lines changed

3 files changed

+111
-55
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,9 @@ Bug Fixes to C++ Support
373373
and (`#74494 <https://github.com/llvm/llvm-project/issues/74494>`_)
374374
- Allow access to a public template alias declaration that refers to friend's
375375
private nested type. (#GH25708).
376+
- Fixed a crash in constant evaluation when trying to access a
377+
captured ``this`` pointer in a lambda with an explicit object parameter.
378+
Fixes (#GH80997)
376379

377380
Bug Fixes to AST Handling
378381
^^^^^^^^^^^^^^^^^^^^^^^^^

clang/lib/AST/ExprConstant.cpp

Lines changed: 74 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -8517,6 +8517,53 @@ class LValueExprEvaluator
85178517
};
85188518
} // end anonymous namespace
85198519

8520+
/// Get an lvalue to a field of a lambda's closure type.
8521+
static bool HandleLambdaCapture(EvalInfo &Info, const Expr *E, LValue &Result,
8522+
const CXXMethodDecl *MD, const FieldDecl *FD,
8523+
bool LValueToRValueConversion) {
8524+
// Static lambda function call operators can't have captures. We already
8525+
// diagnosed this, so bail out here.
8526+
if (MD->isStatic()) {
8527+
assert(Info.CurrentCall->This == nullptr &&
8528+
"This should not be set for a static call operator");
8529+
return false;
8530+
}
8531+
8532+
// Start with 'Result' referring to the complete closure object...
8533+
if (MD->isExplicitObjectMemberFunction()) {
8534+
// Self may be passed by reference or by value.
8535+
const ParmVarDecl *Self = MD->getParamDecl(0);
8536+
if (Self->getType()->isReferenceType()) {
8537+
APValue *RefValue = Info.getParamSlot(Info.CurrentCall->Arguments, Self);
8538+
Result.setFrom(Info.Ctx, *RefValue);
8539+
} else {
8540+
const ParmVarDecl *VD = Info.CurrentCall->Arguments.getOrigParam(Self);
8541+
CallStackFrame *Frame =
8542+
Info.getCallFrameAndDepth(Info.CurrentCall->Arguments.CallIndex)
8543+
.first;
8544+
unsigned Version = Info.CurrentCall->Arguments.Version;
8545+
Result.set({VD, Frame->Index, Version});
8546+
}
8547+
} else
8548+
Result = *Info.CurrentCall->This;
8549+
8550+
// ... then update it to refer to the field of the closure object
8551+
// that represents the capture.
8552+
if (!HandleLValueMember(Info, E, Result, FD))
8553+
return false;
8554+
8555+
// And if the field is of reference type (or if we captured '*this' by
8556+
// reference), update 'Result' to refer to what
8557+
// the field refers to.
8558+
if (LValueToRValueConversion) {
8559+
APValue RVal;
8560+
if (!handleLValueToRValueConversion(Info, E, FD->getType(), Result, RVal))
8561+
return false;
8562+
Result.setFrom(Info.Ctx, RVal);
8563+
}
8564+
return true;
8565+
}
8566+
85208567
/// Evaluate an expression as an lvalue. This can be legitimately called on
85218568
/// expressions which are not glvalues, in three cases:
85228569
/// * function designators in C, and
@@ -8561,37 +8608,8 @@ bool LValueExprEvaluator::VisitVarDecl(const Expr *E, const VarDecl *VD) {
85618608

85628609
if (auto *FD = Info.CurrentCall->LambdaCaptureFields.lookup(VD)) {
85638610
const auto *MD = cast<CXXMethodDecl>(Info.CurrentCall->Callee);
8564-
8565-
// Static lambda function call operators can't have captures. We already
8566-
// diagnosed this, so bail out here.
8567-
if (MD->isStatic()) {
8568-
assert(Info.CurrentCall->This == nullptr &&
8569-
"This should not be set for a static call operator");
8570-
return false;
8571-
}
8572-
8573-
// Start with 'Result' referring to the complete closure object...
8574-
if (MD->isExplicitObjectMemberFunction()) {
8575-
APValue *RefValue =
8576-
Info.getParamSlot(Info.CurrentCall->Arguments, MD->getParamDecl(0));
8577-
Result.setFrom(Info.Ctx, *RefValue);
8578-
} else
8579-
Result = *Info.CurrentCall->This;
8580-
8581-
// ... then update it to refer to the field of the closure object
8582-
// that represents the capture.
8583-
if (!HandleLValueMember(Info, E, Result, FD))
8584-
return false;
8585-
// And if the field is of reference type, update 'Result' to refer to what
8586-
// the field refers to.
8587-
if (FD->getType()->isReferenceType()) {
8588-
APValue RVal;
8589-
if (!handleLValueToRValueConversion(Info, E, FD->getType(), Result,
8590-
RVal))
8591-
return false;
8592-
Result.setFrom(Info.Ctx, RVal);
8593-
}
8594-
return true;
8611+
return HandleLambdaCapture(Info, E, Result, MD, FD,
8612+
FD->getType()->isReferenceType());
85958613
}
85968614
}
85978615

@@ -9069,45 +9087,46 @@ class PointerExprEvaluator
90699087
return Error(E);
90709088
}
90719089
bool VisitCXXThisExpr(const CXXThisExpr *E) {
9072-
// Can't look at 'this' when checking a potential constant expression.
9073-
if (Info.checkingPotentialConstantExpression())
9074-
return false;
9075-
if (!Info.CurrentCall->This) {
9090+
auto DiagnoseInvalidUseOfThis = [&] {
90769091
if (Info.getLangOpts().CPlusPlus11)
90779092
Info.FFDiag(E, diag::note_constexpr_this) << E->isImplicit();
90789093
else
90799094
Info.FFDiag(E);
9095+
};
9096+
9097+
// Can't look at 'this' when checking a potential constant expression.
9098+
if (Info.checkingPotentialConstantExpression())
90809099
return false;
9100+
9101+
bool IsExplicitLambda =
9102+
isLambdaCallWithExplicitObjectParameter(Info.CurrentCall->Callee);
9103+
if (!IsExplicitLambda) {
9104+
if (!Info.CurrentCall->This) {
9105+
DiagnoseInvalidUseOfThis();
9106+
return false;
9107+
}
9108+
9109+
Result = *Info.CurrentCall->This;
90819110
}
9082-
Result = *Info.CurrentCall->This;
90839111

90849112
if (isLambdaCallOperator(Info.CurrentCall->Callee)) {
90859113
// Ensure we actually have captured 'this'. If something was wrong with
90869114
// 'this' capture, the error would have been previously reported.
90879115
// Otherwise we can be inside of a default initialization of an object
90889116
// declared by lambda's body, so no need to return false.
9089-
if (!Info.CurrentCall->LambdaThisCaptureField)
9090-
return true;
9091-
9092-
// If we have captured 'this', the 'this' expression refers
9093-
// to the enclosing '*this' object (either by value or reference) which is
9094-
// either copied into the closure object's field that represents the
9095-
// '*this' or refers to '*this'.
9096-
// Update 'Result' to refer to the data member/field of the closure object
9097-
// that represents the '*this' capture.
9098-
if (!HandleLValueMember(Info, E, Result,
9099-
Info.CurrentCall->LambdaThisCaptureField))
9100-
return false;
9101-
// If we captured '*this' by reference, replace the field with its referent.
9102-
if (Info.CurrentCall->LambdaThisCaptureField->getType()
9103-
->isPointerType()) {
9104-
APValue RVal;
9105-
if (!handleLValueToRValueConversion(Info, E, E->getType(), Result,
9106-
RVal))
9117+
if (!Info.CurrentCall->LambdaThisCaptureField) {
9118+
if (IsExplicitLambda && !Info.CurrentCall->This) {
9119+
DiagnoseInvalidUseOfThis();
91079120
return false;
9121+
}
91089122

9109-
Result.setFrom(Info.Ctx, RVal);
9123+
return true;
91109124
}
9125+
9126+
const auto *MD = cast<CXXMethodDecl>(Info.CurrentCall->Callee);
9127+
return HandleLambdaCapture(
9128+
Info, E, Result, MD, Info.CurrentCall->LambdaThisCaptureField,
9129+
Info.CurrentCall->LambdaThisCaptureField->getType()->isPointerType());
91119130
}
91129131
return true;
91139132
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// RUN: %clang_cc1 -std=c++23 -verify %s
2+
// expected-no-diagnostics
3+
4+
struct S {
5+
int i = 42;
6+
constexpr auto f1() {
7+
return [this](this auto) {
8+
return this->i;
9+
}();
10+
};
11+
12+
constexpr auto f2() {
13+
return [this](this auto&&) {
14+
return this->i;
15+
}();
16+
};
17+
18+
constexpr auto f3() {
19+
return [i = this->i](this auto) {
20+
return i;
21+
}();
22+
};
23+
24+
constexpr auto f4() {
25+
return [i = this->i](this auto&&) {
26+
return i;
27+
}();
28+
};
29+
};
30+
31+
static_assert(S().f1() == 42);
32+
static_assert(S().f2() == 42);
33+
static_assert(S().f3() == 42);
34+
static_assert(S().f4() == 42);

0 commit comments

Comments
 (0)