From 1942a2538b86fe55b9723800db950391cc05402b Mon Sep 17 00:00:00 2001 From: =?utf8?q?Timm=20B=C3=A4der?= Date: Fri, 16 Sep 2022 19:11:58 +0200 Subject: [PATCH] [clang][Interp] Start implementing record types Implement simple constructors as well as member access expressions. Differential Revision: https://reviews.llvm.org/D134057 --- clang/lib/AST/Interp/ByteCodeExprGen.cpp | 105 +++++++++++++++++++++++++++++++ clang/lib/AST/Interp/ByteCodeExprGen.h | 4 ++ clang/lib/AST/Interp/ByteCodeStmtGen.cpp | 4 -- clang/lib/AST/Interp/Interp.h | 3 + clang/lib/AST/Interp/Record.h | 1 + clang/test/AST/Interp/records.cpp | 105 +++++++++++++++++++++++++++++++ clang/test/AST/Interp/references.cpp | 5 +- 7 files changed, 220 insertions(+), 7 deletions(-) create mode 100644 clang/test/AST/Interp/records.cpp diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp index e23727a..2e83435 100644 --- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp +++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp @@ -304,6 +304,28 @@ bool ByteCodeExprGen::VisitUnaryExprOrTypeTraitExpr( return false; } +template +bool ByteCodeExprGen::VisitMemberExpr(const MemberExpr *E) { + // 'Base.Member' + const Expr *Base = E->getBase(); + const ValueDecl *Member = E->getMemberDecl(); + + if (!this->visit(Base)) + return false; + + // Base above gives us a pointer on the stack. + // TODO: Implement non-FieldDecl members. + if (const auto *FD = dyn_cast(Member)) { + const RecordDecl *RD = FD->getParent(); + const Record *R = getRecord(RD); + const Record::Field *F = R->getField(FD); + // Leave a pointer to the field on the stack. + return this->emitGetPtrField(F->Offset, E); + } + + return false; +} + template bool ByteCodeExprGen::discard(const Expr *E) { OptionScope Scope(this, /*NewDiscardResult=*/true); return this->Visit(E); @@ -610,12 +632,87 @@ bool ByteCodeExprGen::visitArrayInitializer(const Expr *Initializer) { } template +bool ByteCodeExprGen::visitRecordInitializer(const Expr *Initializer) { + Initializer = Initializer->IgnoreParenImpCasts(); + assert(Initializer->getType()->isRecordType()); + + if (const auto CtorExpr = dyn_cast(Initializer)) { + const CXXConstructorDecl *Ctor = CtorExpr->getConstructor(); + const RecordDecl *RD = Ctor->getParent(); + const Record *R = getRecord(RD); + + for (const auto *Init : Ctor->inits()) { + const FieldDecl *Member = Init->getMember(); + const Expr *InitExpr = Init->getInit(); + + if (Optional T = classify(InitExpr->getType())) { + const Record::Field *F = R->getField(Member); + + if (!this->emitDupPtr(Initializer)) + return false; + + if (!this->visit(InitExpr)) + return false; + + if (!this->emitInitField(*T, F->Offset, Initializer)) + return false; + } else { + assert(false && "Handle initializer for non-primitive values"); + } + } + + // FIXME: Actually visit() the constructor Body + const Stmt *Body = Ctor->getBody(); + (void)Body; + return true; + } else if (const auto *InitList = dyn_cast(Initializer)) { + const Record *R = getRecord(InitList->getType()); + + unsigned InitIndex = 0; + for (const Expr *Init : InitList->inits()) { + const Record::Field *FieldToInit = R->getField(InitIndex); + + if (Optional T = classify(Init->getType())) { + if (!this->emitDupPtr(Initializer)) + return false; + + if (!this->visit(Init)) + return false; + + if (!this->emitInitField(*T, FieldToInit->Offset, Initializer)) + return false; + } + ++InitIndex; + } + + return true; + } else if (const CallExpr *CE = dyn_cast(Initializer)) { + const Decl *Callee = CE->getCalleeDecl(); + const Function *Func = P.getFunction(dyn_cast(Callee)); + + if (Func->hasRVO()) { + // RVO functions expect a pointer to initialize on the stack. + // Dup our existing pointer so it has its own copy to use. + if (!this->emitDupPtr(Initializer)) + return false; + + return this->visit(CE); + } + } + + return false; +} + +template bool ByteCodeExprGen::visitInitializer(const Expr *Initializer) { QualType InitializerType = Initializer->getType(); if (InitializerType->isArrayType()) return visitArrayInitializer(Initializer); + if (InitializerType->isRecordType()) + return visitRecordInitializer(Initializer); + // Otherwise, visit the expression like normal. return this->Visit(Initializer); } @@ -755,6 +852,8 @@ bool ByteCodeExprGen::VisitCallExpr(const CallExpr *E) { return this->emitCall(*T, Func, E); return this->emitCallVoid(Func, E); } else { + if (Func->hasRVO()) + return this->emitCallVoid(Func, E); assert(false && "Can't classify function return type"); } @@ -766,6 +865,12 @@ bool ByteCodeExprGen::VisitCallExpr(const CallExpr *E) { } template +bool ByteCodeExprGen::VisitCXXDefaultInitExpr( + const CXXDefaultInitExpr *E) { + return this->visit(E->getExpr()); +} + +template bool ByteCodeExprGen::VisitCXXDefaultArgExpr( const CXXDefaultArgExpr *E) { return this->visit(E->getExpr()); diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.h b/clang/lib/AST/Interp/ByteCodeExprGen.h index d73a8df..669c805 100644 --- a/clang/lib/AST/Interp/ByteCodeExprGen.h +++ b/clang/lib/AST/Interp/ByteCodeExprGen.h @@ -71,6 +71,7 @@ public: bool VisitBinaryOperator(const BinaryOperator *E); bool VisitCXXDefaultArgExpr(const CXXDefaultArgExpr *E); bool VisitCallExpr(const CallExpr *E); + bool VisitCXXDefaultInitExpr(const CXXDefaultInitExpr *E); bool VisitCXXBoolLiteralExpr(const CXXBoolLiteralExpr *E); bool VisitCXXNullPtrLiteralExpr(const CXXNullPtrLiteralExpr *E); bool VisitUnaryOperator(const UnaryOperator *E); @@ -81,6 +82,7 @@ public: bool VisitInitListExpr(const InitListExpr *E); bool VisitConstantExpr(const ConstantExpr *E); bool VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *E); + bool VisitMemberExpr(const MemberExpr *E); protected: bool visitExpr(const Expr *E) override; @@ -138,6 +140,8 @@ protected: bool visitInitializer(const Expr *E); /// Compiles an array initializer. bool visitArrayInitializer(const Expr *Initializer); + /// Compiles a record initializer. + bool visitRecordInitializer(const Expr *Initializer); /// Visits an expression and converts it to a boolean. bool visitBool(const Expr *E); diff --git a/clang/lib/AST/Interp/ByteCodeStmtGen.cpp b/clang/lib/AST/Interp/ByteCodeStmtGen.cpp index c4a1efc..47c5527 100644 --- a/clang/lib/AST/Interp/ByteCodeStmtGen.cpp +++ b/clang/lib/AST/Interp/ByteCodeStmtGen.cpp @@ -94,10 +94,6 @@ bool ByteCodeStmtGen::visitFunc(const FunctionDecl *F) { // Classify the return type. ReturnType = this->classify(F->getReturnType()); - // Set up fields and context if a constructor. - if (auto *MD = dyn_cast(F)) - return this->bail(MD); - if (auto *Body = F->getBody()) if (!visitStmt(Body)) return false; diff --git a/clang/lib/AST/Interp/Interp.h b/clang/lib/AST/Interp/Interp.h index 0df7ead..9356c57 100644 --- a/clang/lib/AST/Interp/Interp.h +++ b/clang/lib/AST/Interp/Interp.h @@ -492,6 +492,9 @@ bool InitThisFieldActive(InterpState &S, CodePtr OpPC, uint32_t I) { return true; } +/// 1) Pops the value from the stack +/// 2) Pops a pointer from the stack +/// 3) Writes the value to field I of the pointer template ::T> bool InitField(InterpState &S, CodePtr OpPC, uint32_t I) { const T &Value = S.Stk.pop(); diff --git a/clang/lib/AST/Interp/Record.h b/clang/lib/AST/Interp/Record.h index c59adef..7743ad8 100644 --- a/clang/lib/AST/Interp/Record.h +++ b/clang/lib/AST/Interp/Record.h @@ -67,6 +67,7 @@ public: } unsigned getNumFields() const { return Fields.size(); } + const Field *getField(unsigned I) const { return &Fields[I]; } Field *getField(unsigned I) { return &Fields[I]; } using const_base_iter = BaseList::const_iterator; diff --git a/clang/test/AST/Interp/records.cpp b/clang/test/AST/Interp/records.cpp new file mode 100644 index 0000000..0237640 --- /dev/null +++ b/clang/test/AST/Interp/records.cpp @@ -0,0 +1,105 @@ +// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -verify %s +// RUN: %clang_cc1 -verify=ref %s + +// ref-no-diagnostics +// expected-no-diagnostics + +struct BoolPair { + bool first; + bool second; +}; + +struct Ints { + int a = 20; + int b = 30; + bool c = true; + // BoolPair bp = {true, false}; FIXME + + static const int five = 5; + static constexpr int getFive() { + return five; + } + + constexpr int getTen() const { + return 10; + } +}; + +static_assert(Ints::getFive() == 5, ""); + +constexpr Ints ints; +static_assert(ints.a == 20, ""); +static_assert(ints.b == 30, ""); +static_assert(ints.c, ""); +static_assert(ints.getTen() == 10, ""); + +constexpr Ints ints2{-20, -30, false}; +static_assert(ints2.a == -20, ""); +static_assert(ints2.b == -30, ""); +static_assert(!ints2.c, ""); + +#if 0 +constexpr Ints getInts() { + return {64, 128, true}; +} +constexpr Ints ints3 = getInts(); +static_assert(ints3.a == 64, ""); +static_assert(ints3.b == 128, ""); +static_assert(ints3.c, ""); +#endif + +constexpr Ints ints4 = { + .a = 40 * 50, + .b = 0, + .c = (ints.a > 0), + +}; +static_assert(ints4.a == (40 * 50), ""); +static_assert(ints4.b == 0, ""); +static_assert(ints4.c, ""); + + +// FIXME: Implement initialization by DeclRefExpr. +//constexpr Ints ints4 = ints3; TODO + + + +struct Ints2 { + int a = 10; + int b; +}; +// FIXME: Broken in the new constant interpreter. +// Should be rejected, but without asan errors. +//constexpr Ints2 ints2; + +class C { + public: + int a; + int b; + + constexpr C() : a(100), b(200) {} +}; + +constexpr C c; +static_assert(c.a == 100, ""); +static_assert(c.b == 200, ""); + +constexpr int getB() { + C c; + int &j = c.b; + + j = j * 2; + + return c.b; +} +static_assert(getB() == 400, ""); + +constexpr int getA(const C &c) { + return c.a; +} +static_assert(getA(c) == 100, ""); + +constexpr const C* getPointer() { + return &c; +} +static_assert(getPointer()->a == 100, ""); diff --git a/clang/test/AST/Interp/references.cpp b/clang/test/AST/Interp/references.cpp index 9a23c0a..7d2ed60 100644 --- a/clang/test/AST/Interp/references.cpp +++ b/clang/test/AST/Interp/references.cpp @@ -83,9 +83,8 @@ constexpr int RefToMemberExpr() { S s{1, 2}; int &j = s.i; - j += 10; + j = j + 10; return j; } -// FIXME: Should be accepted. -static_assert(RefToMemberExpr() == 11, ""); // expected-error {{not an integral constant expression}} +static_assert(RefToMemberExpr() == 11, ""); -- 2.7.4