Index: lib/AST/ExprConstant.cpp =================================================================== --- lib/AST/ExprConstant.cpp +++ lib/AST/ExprConstant.cpp @@ -425,6 +425,9 @@ /// Index - The call index of this call. unsigned Index; + llvm::DenseMap LambdaCaptureFields; + FieldDecl *LambdaThisCaptureField; + CallStackFrame(EvalInfo &Info, SourceLocation CallLoc, const FunctionDecl *Callee, const LValue *This, APValue *Arguments); @@ -2279,6 +2282,10 @@ 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 +2297,7 @@ 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(VD)) { @@ -4180,6 +4188,10 @@ 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}; @@ -5034,6 +5046,35 @@ bool LValueExprEvaluator::VisitVarDecl(const Expr *E, const VarDecl *VD) { + + if (Info.CurrentCall && isLambdaCallOperator(Info.CurrentCall->Callee)) { + if (auto *FD = Info.CurrentCall->LambdaCaptureFields.lookup(VD)) { + if (Info.checkingPotentialConstantExpression()) + return false; + Result = *Info.CurrentCall->This; + // Set Result to the subobject of the lambda + if (HandleLValueMember(Info, E, Result, FD)) { + // If the field is of reference type, replace it with the variable or + // enclosing field it refers to, otherwise use the value of the field. + if (FD->getType()->isReferenceType()) { + APValue RVal; + // FIXME: We need to make sure we're passing the right type that + // maintains cv-qualifiers. + if (!handleLValueToRValueConversion(Info, E, E->getType(), Result, + RVal)) + return false; + assert(RVal.isLValue() && "Reference captures through their " + "corresponding field members must refer to " + "lvalues (VarDecls or FieldDecls)"); + Result.setFrom(Info.Ctx, RVal); + } + return true; + } + + Info.FFDiag(E, diag::note_invalid_subexpr_in_const_expr); + return false; + } + } CallStackFrame *Frame = nullptr; if (VD->hasLocalStorage() && Info.CurrentCall->Index > 1) { // Only if a local variable was declared in the function currently being @@ -5423,6 +5464,28 @@ 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)) { + + if (HandleLValueMember(Info, E, Result, + Info.CurrentCall->LambdaThisCaptureField)) { + 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; + } + //Info.FFDiag(E); + return false; + } return true; } @@ -6250,14 +6313,40 @@ 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 false; + + 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, Index: test/SemaCXX/cxx1z-constexpr-lambdas.cpp =================================================================== --- test/SemaCXX/cxx1z-constexpr-lambdas.cpp +++ test/SemaCXX/cxx1z-constexpr-lambdas.cpp @@ -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,111 @@ } // 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); //expected-error{{constant expression}} expected-note{{in call to}} -} // end ns1_captures +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 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_unimplemented { } // end ns1_unimplemented } // end ns test_lambda_is_cce