r295279 - [cxx1z-constexpr-lambda] Implement captures - thus completing implementation of constexpr lambdas.
Richard Smith via cfe-commits
cfe-commits at lists.llvm.org
Tue Feb 21 16:10:22 PST 2017
Timeout -- committed r295791 on your behalf.
On 16 February 2017 at 05:04, Faisal Vali <faisalv at 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 <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
> >
> >
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20170221/fa6f8643/attachment-0001.html>
More information about the cfe-commits
mailing list