tbaeder updated this revision to Diff 522204.
tbaeder marked an inline comment as done.
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D146030/new/
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,109 @@
+// 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);
+
+
+int constexpr FunCase() {
+ return [x = 10] {
+ decltype(x) y; // type int b/c not odr use
+ // refers to original init-capture
+ auto &z = x; // type const int & b/c odr use
+ // refers to lambdas copy of x
+ y = 10; // Ok
+ //z = 10; // Ill-formed
+ return y;
+ }();
+}
+
+constexpr int WC = FunCase();
+
+
+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
@@ -850,6 +850,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,10 @@
/// Parameter indices.
llvm::DenseMap<const ParmVarDecl *, unsigned> Params;
+ /// Lambda captures.
+ /// Map from Decl* to [Offset, IsReference] pair.
+ 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
@@ -93,6 +93,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
@@ -949,6 +949,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;
@@ -1532,6 +1569,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;
@@ -2029,6 +2068,16 @@
}
}
+ // Handle lambda captures.
+ if (auto It = this->LambdaCaptures.find(D);
+ It != this->LambdaCaptures.end()) {
+ auto [Offset, IsReference] = It->second;
+
+ if (IsReference)
+ return this->emitGetThisFieldPtr(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,10 @@
/// Parameter indices.
llvm::DenseMap<const ParmVarDecl *, unsigned> Params;
+ /// Lambda captures.
+ /// Map from Decl* to [Offset, IsReference] pair.
+ 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,29 @@
// 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 *> LC;
+ FieldDecl *LTC;
+
+ MD->getParent()->getCaptureFields(LC, LTC);
+
+ for (auto Cap : LC) {
+ unsigned Offset = R->getField(Cap.second)->Offset;
+ this->LambdaCaptures[Cap.first] = {
+ Offset, Cap.second->getType()->isReferenceType()};
+ }
+ // FIXME: LambdaThisCapture
+ (void)LTC;
+ }
}
// Assign descriptors to all parameters.
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits