tbaeder created this revision.
tbaeder added reviewers: aaron.ballman, erichkeane, tahonermann, shafik.
Herald added a project: All.
tbaeder requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

Unfortunately I couldn't find an existing test file in `test/SemaCXX` that 
focused on lambdas in constant expressions.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D146030

Files:
  clang/lib/AST/Interp/ByteCodeEmitter.cpp
  clang/lib/AST/Interp/ByteCodeEmitter.h
  clang/lib/AST/Interp/ByteCodeExprGen.cpp
  clang/lib/AST/Interp/ByteCodeExprGen.h
  clang/lib/AST/Interp/EvalEmitter.h
  clang/lib/AST/Interp/Interp.h
  clang/test/AST/Interp/lambda.cpp

Index: clang/test/AST/Interp/lambda.cpp
===================================================================
--- /dev/null
+++ clang/test/AST/Interp/lambda.cpp
@@ -0,0 +1,93 @@
+// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -verify -std=c++20 %s
+// RUN: %clang_cc1 -verify=ref -std=c++20 %s
+
+constexpr int a = 12;
+constexpr int f = [c = a]() { return c; }();
+static_assert(f == a);
+
+
+constexpr int inc() {
+  int a = 10;
+  auto f = [&a]() {
+    ++a;
+  };
+
+  f();f();
+
+  return a;
+}
+static_assert(inc() == 12);
+
+constexpr int add(int a, int b) {
+  auto doIt = [a, b](int c) {
+    return a + b + c;
+  };
+
+  return doIt(2);
+}
+static_assert(add(4, 5) == 11);
+
+
+constexpr int add2(int a, int b) {
+  auto doIt = [a, b](int c) {
+    auto bar = [a]() { return a; };
+    auto bar2 = [b]() { return b; };
+
+    return bar() + bar2() + c;
+  };
+
+  return doIt(2);
+}
+static_assert(add2(4, 5) == 11);
+
+
+constexpr int div(int a, int b) {
+  auto f = [=]() {
+    return a / b; // expected-note {{division by zero}} \
+                  // ref-note {{division by zero}}
+  };
+
+  return f(); // expected-note {{in call to '&f->operator()()'}} \
+              // ref-note {{in call to '&f->operator()()'}}
+}
+static_assert(div(8, 2) == 4);
+static_assert(div(8, 0) == 4); // expected-error {{not an integral constant expression}} \
+                               // expected-note {{in call to 'div(8, 0)'}} \
+                               // ref-error {{not an integral constant expression}} \
+                               // ref-note {{in call to 'div(8, 0)'}}
+
+
+struct F {
+  float f;
+};
+
+constexpr float captureStruct() {
+  F someF = {1.0};
+
+  auto p = [someF]() {
+    return someF.f;
+  };
+
+  return p();
+}
+
+static_assert(captureStruct() == 1.0);
+
+namespace LambdaParams {
+  template<typename T>
+  constexpr void callThis(T t) {
+    return t();
+  }
+
+  constexpr int foo() {
+    int a = 0;
+    auto f = [&a]() { ++a; };
+
+    callThis(f);
+
+    return a;
+  }
+  /// FIXME: This should work in the new interpreter.
+  static_assert(foo() == 1); // expected-error {{not an integral constant expression}}
+}
+
Index: clang/lib/AST/Interp/Interp.h
===================================================================
--- clang/lib/AST/Interp/Interp.h
+++ clang/lib/AST/Interp/Interp.h
@@ -760,6 +760,7 @@
   const Pointer &Field = Obj.atField(I);
   if (!CheckStore(S, OpPC, Field))
     return false;
+  Field.initialize();
   Field.deref<T>() = Value;
   return true;
 }
Index: clang/lib/AST/Interp/EvalEmitter.h
===================================================================
--- clang/lib/AST/Interp/EvalEmitter.h
+++ clang/lib/AST/Interp/EvalEmitter.h
@@ -76,6 +76,9 @@
 
   /// Parameter indices.
   llvm::DenseMap<const ParmVarDecl *, unsigned> Params;
+  /// Lambda captures.
+  llvm::DenseMap<const ValueDecl *, std::pair<unsigned, bool>> LambdaCaptures;
+  unsigned LambdaThisCapture;
   /// Local descriptors.
   llvm::SmallVector<SmallVector<Local, 8>, 2> Descriptors;
 
Index: clang/lib/AST/Interp/ByteCodeExprGen.h
===================================================================
--- clang/lib/AST/Interp/ByteCodeExprGen.h
+++ clang/lib/AST/Interp/ByteCodeExprGen.h
@@ -92,6 +92,7 @@
   bool VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *E);
   bool VisitCompoundLiteralExpr(const CompoundLiteralExpr *E);
   bool VisitTypeTraitExpr(const TypeTraitExpr *E);
+  bool VisitLambdaExpr(const LambdaExpr *E);
 
 protected:
   bool visitExpr(const Expr *E) override;
Index: clang/lib/AST/Interp/ByteCodeExprGen.cpp
===================================================================
--- clang/lib/AST/Interp/ByteCodeExprGen.cpp
+++ clang/lib/AST/Interp/ByteCodeExprGen.cpp
@@ -953,6 +953,43 @@
   return this->emitConstBool(E->getValue(), E);
 }
 
+template <class Emitter>
+bool ByteCodeExprGen<Emitter>::VisitLambdaExpr(const LambdaExpr *E) {
+  // XXX We assume here that a pointer-to-initialize is on the stack.
+
+  const Record *R = P.getOrCreateRecord(E->getLambdaClass());
+
+  auto *CaptureInitIt = E->capture_init_begin();
+  // Initialize all fields (which represent lambda captures) of the
+  // record with their initializers.
+  for (const Record::Field &F : R->fields()) {
+    const Expr *Init = *CaptureInitIt;
+    ++CaptureInitIt;
+
+    if (std::optional<PrimType> T = classify(Init)) {
+      if (!this->visit(Init))
+        return false;
+
+      if (!this->emitSetField(*T, F.Offset, E))
+        return false;
+    } else {
+      if (!this->emitDupPtr(E))
+        return false;
+
+      if (!this->emitGetPtrField(F.Offset, E))
+        return false;
+
+      if (!this->visitInitializer(Init))
+        return false;
+
+      if (!this->emitPopPtr(E))
+        return false;
+    }
+  }
+
+  return true;
+}
+
 template <class Emitter> bool ByteCodeExprGen<Emitter>::discard(const Expr *E) {
   if (E->containsErrors())
     return false;
@@ -1529,6 +1566,8 @@
                  dyn_cast<AbstractConditionalOperator>(Initializer)) {
     return this->visitConditional(
         ACO, [this](const Expr *E) { return this->visitRecordInitializer(E); });
+  } else if (const auto *LE = dyn_cast<LambdaExpr>(Initializer)) {
+    return this->VisitLambdaExpr(LE);
   }
 
   return false;
@@ -1993,6 +2032,16 @@
     }
   }
 
+  // Handle lambda captures.
+  if (auto It = this->LambdaCaptures.find(D);
+      It != this->LambdaCaptures.end()) {
+    auto [Offset, IsReference] = It->second;
+
+    if (IsReference)
+      return this->emitGetThisField(PT_Ptr, Offset, E);
+    return this->emitGetPtrThisField(Offset, E);
+  }
+
   return false;
 }
 
Index: clang/lib/AST/Interp/ByteCodeEmitter.h
===================================================================
--- clang/lib/AST/Interp/ByteCodeEmitter.h
+++ clang/lib/AST/Interp/ByteCodeEmitter.h
@@ -70,6 +70,9 @@
 
   /// Parameter indices.
   llvm::DenseMap<const ParmVarDecl *, unsigned> Params;
+  /// Lambda captures.
+  llvm::DenseMap<const ValueDecl *, std::pair<unsigned, bool>> LambdaCaptures;
+  unsigned *LambdaThisCapture;
   /// Local descriptors.
   llvm::SmallVector<SmallVector<Local, 8>, 2> Descriptors;
 
Index: clang/lib/AST/Interp/ByteCodeEmitter.cpp
===================================================================
--- clang/lib/AST/Interp/ByteCodeEmitter.cpp
+++ clang/lib/AST/Interp/ByteCodeEmitter.cpp
@@ -11,6 +11,7 @@
 #include "Floating.h"
 #include "Opcode.h"
 #include "Program.h"
+#include "clang/AST/ASTLambda.h"
 #include "clang/AST/DeclCXX.h"
 #include <type_traits>
 
@@ -42,11 +43,28 @@
   // the 'this' pointer. This parameter is pop()ed from the
   // InterpStack when calling the function.
   bool HasThisPointer = false;
-  if (const auto *MD = dyn_cast<CXXMethodDecl>(FuncDecl);
-      MD && MD->isInstance()) {
-    HasThisPointer = true;
-    ParamTypes.push_back(PT_Ptr);
-    ParamOffset += align(primSize(PT_Ptr));
+  if (const auto *MD = dyn_cast<CXXMethodDecl>(FuncDecl)) {
+    if (MD->isInstance()) {
+      HasThisPointer = true;
+      ParamTypes.push_back(PT_Ptr);
+      ParamOffset += align(primSize(PT_Ptr));
+    }
+
+    // Set up lambda capture to closure record field mapping.
+    if (isLambdaCallOperator(MD)) {
+      const Record *R = P.getOrCreateRecord(MD->getParent());
+      llvm::DenseMap<const ValueDecl *, FieldDecl *> _LambdaCaptures;
+      FieldDecl *_LambdaThisCapture;
+
+      MD->getParent()->getCaptureFields(_LambdaCaptures, _LambdaThisCapture);
+
+      for (auto Cap : _LambdaCaptures) {
+        unsigned Offset = R->getField(Cap.second)->Offset;
+        this->LambdaCaptures[Cap.first] = {
+            Offset, Cap.second->getType()->isReferenceType()};
+      }
+      // FIXME: LambdaThisCapture
+    }
   }
 
   // Assign descriptors to all parameters.
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to