r295279 - [cxx1z-constexpr-lambda] Implement captures - thus completing implementation of constexpr lambdas.
Faisal Vali via cfe-commits
cfe-commits at lists.llvm.org
Wed Feb 15 20:12:21 PST 2017
Author: faisalv
Date: Wed Feb 15 22:12:21 2017
New Revision: 295279
URL: http://llvm.org/viewvc/llvm-project?rev=295279&view=rev
Log:
[cxx1z-constexpr-lambda] Implement captures - thus completing implementation of constexpr lambdas.
Enable evaluation of captures within constexpr lambdas by using a strategy similar to that used in CodeGen:
- when starting evaluation of a lambda's call operator, create a map from VarDecl's to a closure's FieldDecls
- every time a VarDecl (or '*this) that represents a capture is encountered while evaluating the expression via the expression evaluator (specifically the LValueEvaluator) in ExprConstant.cpp - it is replaced by the corresponding FieldDecl LValue (an Lvalue-to-Rvalue conversion on this LValue representation then determines the right rvalue when needed).
Thanks to Richard Smith and Hubert Tong for their review and feedback!
https://reviews.llvm.org/D29748
Modified:
cfe/trunk/lib/AST/ExprConstant.cpp
cfe/trunk/test/SemaCXX/cxx1z-constexpr-lambdas.cpp
Modified: cfe/trunk/lib/AST/ExprConstant.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ExprConstant.cpp?rev=295279&r1=295278&r2=295279&view=diff
==============================================================================
--- cfe/trunk/lib/AST/ExprConstant.cpp (original)
+++ cfe/trunk/lib/AST/ExprConstant.cpp Wed Feb 15 22:12:21 2017
@@ -425,6 +425,17 @@ namespace {
/// Index - The call index of this call.
unsigned Index;
+ // FIXME: Adding this to every 'CallStackFrame' may have a nontrivial impact
+ // on the overall stack usage of deeply-recursing constexpr evaluataions.
+ // (We should cache this map rather than recomputing it repeatedly.)
+ // But let's try this and see how it goes; we can look into caching the map
+ // as a later change.
+
+ /// LambdaCaptureFields - Mapping from captured variables/this to
+ /// corresponding data members in the closure class.
+ llvm::DenseMap<const VarDecl *, FieldDecl *> LambdaCaptureFields;
+ FieldDecl *LambdaThisCaptureField;
+
CallStackFrame(EvalInfo &Info, SourceLocation CallLoc,
const FunctionDecl *Callee, const LValue *This,
APValue *Arguments);
@@ -2279,6 +2290,10 @@ static bool HandleLValueComplexElement(E
return true;
}
+static bool handleLValueToRValueConversion(EvalInfo &Info, const Expr *Conv,
+ QualType Type, const LValue &LVal,
+ APValue &RVal);
+
/// Try to evaluate the initializer for a variable declaration.
///
/// \param Info Information about the ongoing evaluation.
@@ -2290,6 +2305,7 @@ static bool HandleLValueComplexElement(E
static bool evaluateVarDeclInit(EvalInfo &Info, const Expr *E,
const VarDecl *VD, CallStackFrame *Frame,
APValue *&Result) {
+
// If this is a parameter to an active constexpr function call, perform
// argument substitution.
if (const ParmVarDecl *PVD = dyn_cast<ParmVarDecl>(VD)) {
@@ -4180,6 +4196,10 @@ static bool HandleFunctionCall(SourceLoc
return false;
This->moveInto(Result);
return true;
+ } else if (MD && isLambdaCallOperator(MD)) {
+ // We're in a lambda; determine the lambda capture field maps.
+ MD->getParent()->getCaptureFields(Frame.LambdaCaptureFields,
+ Frame.LambdaThisCaptureField);
}
StmtResult Ret = {Result, ResultSlot};
@@ -5041,6 +5061,33 @@ bool LValueExprEvaluator::VisitDeclRefEx
bool LValueExprEvaluator::VisitVarDecl(const Expr *E, const VarDecl *VD) {
+
+ // If we are within a lambda's call operator, check whether the 'VD' referred
+ // to within 'E' actually represents a lambda-capture that maps to a
+ // data-member/field within the closure object, and if so, evaluate to the
+ // field or what the field refers to.
+ if (Info.CurrentCall && isLambdaCallOperator(Info.CurrentCall->Callee)) {
+ if (auto *FD = Info.CurrentCall->LambdaCaptureFields.lookup(VD)) {
+ if (Info.checkingPotentialConstantExpression())
+ return false;
+ // Start with 'Result' referring to the complete closure object...
+ Result = *Info.CurrentCall->This;
+ // ... then update it to refer to the field of the closure object
+ // that represents the capture.
+ if (!HandleLValueMember(Info, E, Result, FD))
+ return false;
+ // And if the field is of reference type, update 'Result' to refer to what
+ // the field refers to.
+ if (FD->getType()->isReferenceType()) {
+ APValue RVal;
+ if (!handleLValueToRValueConversion(Info, E, FD->getType(), Result,
+ RVal))
+ return false;
+ Result.setFrom(Info.Ctx, RVal);
+ }
+ return true;
+ }
+ }
CallStackFrame *Frame = nullptr;
if (VD->hasLocalStorage() && Info.CurrentCall->Index > 1) {
// Only if a local variable was declared in the function currently being
@@ -5440,6 +5487,27 @@ public:
return false;
}
Result = *Info.CurrentCall->This;
+ // If we are inside a lambda's call operator, the 'this' expression refers
+ // to the enclosing '*this' object (either by value or reference) which is
+ // either copied into the closure object's field that represents the '*this'
+ // or refers to '*this'.
+ if (isLambdaCallOperator(Info.CurrentCall->Callee)) {
+ // Update 'Result' to refer to the data member/field of the closure object
+ // that represents the '*this' capture.
+ if (!HandleLValueMember(Info, E, Result,
+ Info.CurrentCall->LambdaThisCaptureField))
+ return false;
+ // If we captured '*this' by reference, replace the field with its referent.
+ if (Info.CurrentCall->LambdaThisCaptureField->getType()
+ ->isPointerType()) {
+ APValue RVal;
+ if (!handleLValueToRValueConversion(Info, E, E->getType(), Result,
+ RVal))
+ return false;
+
+ Result.setFrom(Info.Ctx, RVal);
+ }
+ }
return true;
}
@@ -6269,14 +6337,40 @@ bool RecordExprEvaluator::VisitLambdaExp
if (ClosureClass->isInvalidDecl()) return false;
if (Info.checkingPotentialConstantExpression()) return true;
- if (E->capture_size()) {
- Info.FFDiag(E, diag::note_unimplemented_constexpr_lambda_feature_ast)
- << "can not evaluate lambda expressions with captures";
- return false;
+
+ const size_t NumFields =
+ std::distance(ClosureClass->field_begin(), ClosureClass->field_end());
+
+ assert(NumFields ==
+ std::distance(E->capture_init_begin(), E->capture_init_end()) &&
+ "The number of lambda capture initializers should equal the number of "
+ "fields within the closure type");
+
+ Result = APValue(APValue::UninitStruct(), /*NumBases*/0, NumFields);
+ // Iterate through all the lambda's closure object's fields and initialize
+ // them.
+ auto *CaptureInitIt = E->capture_init_begin();
+ const LambdaCapture *CaptureIt = ClosureClass->captures_begin();
+ bool Success = true;
+ for (const auto *Field : ClosureClass->fields()) {
+ assert(CaptureInitIt != E->capture_init_end());
+ // Get the initializer for this field
+ Expr *const CurFieldInit = *CaptureInitIt++;
+
+ // If there is no initializer, either this is a VLA or an error has
+ // occurred.
+ if (!CurFieldInit)
+ return Error(E);
+
+ APValue &FieldVal = Result.getStructField(Field->getFieldIndex());
+ if (!EvaluateInPlace(FieldVal, Info, This, CurFieldInit)) {
+ if (!Info.keepEvaluatingAfterFailure())
+ return false;
+ Success = false;
+ }
+ ++CaptureIt;
}
- // FIXME: Implement captures.
- Result = APValue(APValue::UninitStruct(), /*NumBases*/0, /*NumFields*/0);
- return true;
+ return Success;
}
static bool EvaluateRecord(const Expr *E, const LValue &This,
Modified: cfe/trunk/test/SemaCXX/cxx1z-constexpr-lambdas.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/cxx1z-constexpr-lambdas.cpp?rev=295279&r1=295278&r2=295279&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/cxx1z-constexpr-lambdas.cpp (original)
+++ cfe/trunk/test/SemaCXX/cxx1z-constexpr-lambdas.cpp Wed Feb 15 22:12:21 2017
@@ -1,6 +1,6 @@
-// RUN: %clang_cc1 -std=c++1z -verify -fsyntax-only -fblocks %s
-// RUN: %clang_cc1 -std=c++1z -verify -fsyntax-only -fblocks -fdelayed-template-parsing %s
-// RUN: %clang_cc1 -std=c++14 -verify -fsyntax-only -fblocks %s -DCPP14_AND_EARLIER
+// RUN: %clang_cc1 -std=c++1z -verify -fsyntax-only -fblocks %s -fcxx-exceptions
+// RUN: %clang_cc1 -std=c++1z -verify -fsyntax-only -fblocks -fdelayed-template-parsing %s -fcxx-exceptions
+// RUN: %clang_cc1 -std=c++14 -verify -fsyntax-only -fblocks %s -DCPP14_AND_EARLIER -fcxx-exceptions
namespace test_lambda_is_literal {
@@ -157,18 +157,115 @@ constexpr auto M = // expected-error{{m
} // end ns1_simple_lambda
-namespace ns1_unimplemented {
-namespace ns1_captures {
+namespace test_captures_1 {
+namespace ns1 {
constexpr auto f(int i) {
- double d = 3.14;
- auto L = [=](auto a) { //expected-note{{coming soon}}
- int Isz = i + d;
- return sizeof(i) + sizeof(a) + sizeof(d);
+ struct S { int x; } s = { i * 2 };
+ auto L = [=](auto a) {
+ return i + s.x + a;
+ };
+ return L;
+}
+constexpr auto M = f(3);
+
+static_assert(M(10) == 19);
+
+} // end test_captures_1::ns1
+
+namespace ns2 {
+
+constexpr auto foo(int n) {
+ auto L = [i = n] (auto N) mutable {
+ if (!N(i)) throw "error";
+ return [&i] {
+ return ++i;
+ };
};
+ auto M = L([n](int p) { return p == n; });
+ M(); M();
+ L([n](int p) { return p == n + 2; });
+
return L;
}
-constexpr auto M = f(3); //expected-error{{constant expression}} expected-note{{in call to}}
-} // end ns1_captures
+
+constexpr auto L = foo(3);
+
+} // end test_captures_1::ns2
+namespace ns3 {
+
+constexpr auto foo(int n) {
+ auto L = [i = n] (auto N) mutable {
+ if (!N(i)) throw "error";
+ return [&i] {
+ return [i]() mutable {
+ return ++i;
+ };
+ };
+ };
+ auto M = L([n](int p) { return p == n; });
+ M()(); M()();
+ L([n](int p) { return p == n; });
+
+ return L;
+}
+
+constexpr auto L = foo(3);
+} // end test_captures_1::ns3
+
+namespace ns2_capture_this_byval {
+struct S {
+ int s;
+ constexpr S(int s) : s{s} { }
+ constexpr auto f(S o) {
+ return [*this,o] (auto a) { return s + o.s + a.s; };
+ }
+};
+
+constexpr auto L = S{5}.f(S{10});
+static_assert(L(S{100}) == 115);
+} // end test_captures_1::ns2_capture_this_byval
+
+namespace ns2_capture_this_byref {
+
+struct S {
+ int s;
+ constexpr S(int s) : s{s} { }
+ constexpr auto f() const {
+ return [this] { return s; };
+ }
+};
+
+constexpr S SObj{5};
+constexpr auto L = SObj.f();
+constexpr int I = L();
+static_assert(I == 5);
+
+} // end ns2_capture_this_byref
+
+} // end test_captures_1
+
+namespace test_capture_array {
+namespace ns1 {
+constexpr auto f(int I) {
+ int arr[] = { I, I *2, I * 3 };
+ auto L1 = [&] (auto a) { return arr[a]; };
+ int r = L1(2);
+ struct X { int x, y; };
+ return [=](auto a) { return X{arr[a],r}; };
+}
+constexpr auto L = f(3);
+static_assert(L(0).x == 3);
+static_assert(L(0).y == 9);
+static_assert(L(1).x == 6);
+static_assert(L(1).y == 9);
+} // end ns1
+
+} // end test_capture_array
+namespace ns1_test_lvalue_type {
+ void f() {
+ volatile int n;
+ constexpr bool B = [&]{ return &n; }() == &n; // should be accepted
+ }
} // end ns1_unimplemented
} // end ns test_lambda_is_cce
More information about the cfe-commits
mailing list