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