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