On 15 February 2017 at 20:12, Faisal Vali via cfe-commits < cfe-commits@lists.llvm.org> wrote:
> 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! > Awesome, thanks Faisal! Want to bump our value for __cpp_constexpr to 201603 in C++1z mode to advertise support for this? > 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/ExprCo > nstant.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 > > > _______________________________________________ > cfe-commits mailing list > cfe-commits@lists.llvm.org > http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits >
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits