Author: Timm Bäder Date: 2023-06-16T10:05:33+02:00 New Revision: 461f91b1e4ef8bd8a5acc95d2e4badc3b2da5f30
URL: https://github.com/llvm/llvm-project/commit/461f91b1e4ef8bd8a5acc95d2e4badc3b2da5f30 DIFF: https://github.com/llvm/llvm-project/commit/461f91b1e4ef8bd8a5acc95d2e4badc3b2da5f30.diff LOG: [clang][Interp] Handle LambdaExprs Differential Revision: https://reviews.llvm.org/D146030 Added: clang/test/AST/Interp/lambda.cpp Modified: 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 Removed: ################################################################################ diff --git a/clang/lib/AST/Interp/ByteCodeEmitter.cpp b/clang/lib/AST/Interp/ByteCodeEmitter.cpp index 34ad9bf2b326b..3533559ac3ca4 100644 --- a/clang/lib/AST/Interp/ByteCodeEmitter.cpp +++ b/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 @@ ByteCodeEmitter::compileFunc(const FunctionDecl *FuncDecl) { // 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. diff --git a/clang/lib/AST/Interp/ByteCodeEmitter.h b/clang/lib/AST/Interp/ByteCodeEmitter.h index 30da06b20250f..a184ee071e9df 100644 --- a/clang/lib/AST/Interp/ByteCodeEmitter.h +++ b/clang/lib/AST/Interp/ByteCodeEmitter.h @@ -70,6 +70,10 @@ class ByteCodeEmitter { /// 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; diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp index 94db8b868758e..b2a27725307f8 100644 --- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp +++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp @@ -895,6 +895,43 @@ bool ByteCodeExprGen<Emitter>::VisitTypeTraitExpr(const TypeTraitExpr *E) { 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; @@ -1483,6 +1520,8 @@ bool ByteCodeExprGen<Emitter>::visitRecordInitializer(const Expr *Initializer) { 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; @@ -1980,6 +2019,16 @@ bool ByteCodeExprGen<Emitter>::VisitDeclRefExpr(const DeclRefExpr *E) { } } + // 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; } diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.h b/clang/lib/AST/Interp/ByteCodeExprGen.h index 52a8eca593c2b..70961bea98c31 100644 --- a/clang/lib/AST/Interp/ByteCodeExprGen.h +++ b/clang/lib/AST/Interp/ByteCodeExprGen.h @@ -93,6 +93,7 @@ class ByteCodeExprGen : public ConstStmtVisitor<ByteCodeExprGen<Emitter>, bool>, 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; diff --git a/clang/lib/AST/Interp/EvalEmitter.h b/clang/lib/AST/Interp/EvalEmitter.h index 99933900f2921..d1901359f2c2e 100644 --- a/clang/lib/AST/Interp/EvalEmitter.h +++ b/clang/lib/AST/Interp/EvalEmitter.h @@ -76,6 +76,10 @@ class EvalEmitter : public SourceMapper { /// 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; diff --git a/clang/lib/AST/Interp/Interp.h b/clang/lib/AST/Interp/Interp.h index b54ef9542010b..d2f4af82089bf 100644 --- a/clang/lib/AST/Interp/Interp.h +++ b/clang/lib/AST/Interp/Interp.h @@ -840,6 +840,7 @@ bool SetField(InterpState &S, CodePtr OpPC, uint32_t I) { const Pointer &Field = Obj.atField(I); if (!CheckStore(S, OpPC, Field)) return false; + Field.initialize(); Field.deref<T>() = Value; return true; } diff --git a/clang/test/AST/Interp/lambda.cpp b/clang/test/AST/Interp/lambda.cpp new file mode 100644 index 0000000000000..49e2290743285 --- /dev/null +++ b/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}} +} + _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits