r295279 - [cxx1z-constexpr-lambda] Implement captures - thus completing implementation of constexpr lambdas.
Faisal Vali via cfe-commits
cfe-commits at lists.llvm.org
Thu Feb 16 05:04:28 PST 2017
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 <richard at metafoo.co.uk> wrote:
> On 15 February 2017 at 20:12, Faisal Vali via cfe-commits
> <cfe-commits at 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 at lists.llvm.org
>> http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
>
>
More information about the cfe-commits
mailing list