r295279 - [cxx1z-constexpr-lambda] Implement captures - thus completing implementation of constexpr lambdas.

Richard Smith via cfe-commits cfe-commits at lists.llvm.org
Wed Feb 15 20:30:39 PST 2017


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/ExprCo
> nstant.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/20170215/1e0e5bb8/attachment-0001.html>


More information about the cfe-commits mailing list