Timeout -- committed r295791 on your behalf. On 16 February 2017 at 05:04, Faisal Vali <fais...@gmail.com> wrote:
> Of course Richard - I'll be happy to bump that value for C++1z > hopefully later today. > Thanks! > Faisal Vali > > > > On Wed, Feb 15, 2017 at 10:30 PM, Richard Smith <rich...@metafoo.co.uk> > wrote: > > 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/ > 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 > >> > >> > >> _______________________________________________ > >> 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