faisalv created this revision.
faisalv added a project: clang-c.
Herald added a subscriber: EricWF.

This patch attempts to enable evaluation of all forms of captures (however 
deeply nested) within constexpr lambdas.

Appreciate the feedback.

Thanks!


Repository:
  rL LLVM

https://reviews.llvm.org/D29748

Files:
  lib/AST/ExprConstant.cpp
  test/SemaCXX/cxx1z-constexpr-lambdas.cpp

Index: test/SemaCXX/cxx1z-constexpr-lambdas.cpp
===================================================================
--- test/SemaCXX/cxx1z-constexpr-lambdas.cpp
+++ test/SemaCXX/cxx1z-constexpr-lambdas.cpp
@@ -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,111 @@
 
 } // 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);  //expected-error{{constant expression}} expected-note{{in call to}}
-} // end ns1_captures
+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 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_unimplemented {
 } // end ns1_unimplemented 
 
 } // end ns test_lambda_is_cce
Index: lib/AST/ExprConstant.cpp
===================================================================
--- lib/AST/ExprConstant.cpp
+++ lib/AST/ExprConstant.cpp
@@ -425,6 +425,9 @@
     /// Index - The call index of this call.
     unsigned Index;
 
+    llvm::DenseMap<const VarDecl *, FieldDecl *> LambdaCaptureFields;
+    FieldDecl *LambdaThisCaptureField;
+
     CallStackFrame(EvalInfo &Info, SourceLocation CallLoc,
                    const FunctionDecl *Callee, const LValue *This,
                    APValue *Arguments);
@@ -2279,6 +2282,10 @@
   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 +2297,7 @@
 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 +4188,10 @@
       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};
@@ -5034,6 +5046,35 @@
 
 
 bool LValueExprEvaluator::VisitVarDecl(const Expr *E, const VarDecl *VD) {
+
+  if (Info.CurrentCall && isLambdaCallOperator(Info.CurrentCall->Callee)) {
+    if (auto *FD = Info.CurrentCall->LambdaCaptureFields.lookup(VD)) {
+      if (Info.checkingPotentialConstantExpression())
+        return false;
+      Result = *Info.CurrentCall->This;
+      // Set Result to the subobject of the lambda
+      if (HandleLValueMember(Info, E, Result, FD)) {
+        // If the field is of reference type, replace it with the variable or
+        // enclosing field it refers to, otherwise use the value of the field.
+        if (FD->getType()->isReferenceType()) {
+          APValue RVal;
+          // FIXME: We need to make sure we're passing the right type that
+          // maintains cv-qualifiers.
+          if (!handleLValueToRValueConversion(Info, E, E->getType(), Result,
+                                              RVal))
+            return false;
+          assert(RVal.isLValue() && "Reference captures through their "
+                                    "corresponding field members must refer to "
+                                    "lvalues (VarDecls or FieldDecls)");
+          Result.setFrom(Info.Ctx, RVal);
+        }
+        return true;
+      }
+
+      Info.FFDiag(E, diag::note_invalid_subexpr_in_const_expr);
+      return false;
+    }
+  }
   CallStackFrame *Frame = nullptr;
   if (VD->hasLocalStorage() && Info.CurrentCall->Index > 1) {
     // Only if a local variable was declared in the function currently being
@@ -5423,6 +5464,28 @@
       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)) {
+      
+      if (HandleLValueMember(Info, E, Result,
+                             Info.CurrentCall->LambdaThisCaptureField)) {
+        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;
+      }
+      //Info.FFDiag(E);
+      return false;
+    }
     return true;
   }
 
@@ -6250,14 +6313,40 @@
   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 false;
+
+    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,
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to