Index: clang/lib/AST/Interp/ByteCodeExprGen.h =================================================================== --- clang/lib/AST/Interp/ByteCodeExprGen.h +++ clang/lib/AST/Interp/ByteCodeExprGen.h @@ -71,6 +71,7 @@ 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 @@ 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 @@ 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); Index: clang/lib/AST/Interp/ByteCodeExprGen.cpp =================================================================== --- clang/lib/AST/Interp/ByteCodeExprGen.cpp +++ clang/lib/AST/Interp/ByteCodeExprGen.cpp @@ -303,6 +303,26 @@ 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. + const auto *FD = dyn_cast(Member); + assert(FD); + const RecordDecl *RD = FD->getParent(); + assert(RD); + const Record *R = P.getOrCreateRecord(RD); + const Record::Field *F = R->getField(FD); + // Leave a pointer to the field on the stack. + return this->emitGetPtrField(F->Offset, E); +} + template bool ByteCodeExprGen::discard(const Expr *E) { OptionScope Scope(this, /*NewDiscardResult=*/true); return this->Visit(E); @@ -607,6 +627,80 @@ return true; } +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. + // TODO: Instead of doing this, we could just skip removing + // the RVO pointer from the stack. + if (!this->emitDupPtr(Initializer)) + return false; + + return this->visit(CE); + } + } + + return false; +} + template bool ByteCodeExprGen::visitInitializer(const Expr *Initializer) { QualType InitializerType = Initializer->getType(); @@ -614,6 +708,9 @@ if (InitializerType->isArrayType()) return visitArrayInitializer(Initializer); + if (InitializerType->isRecordType()) + return visitRecordInitializer(Initializer); + // Otherwise, visit the expression like normal. return this->Visit(Initializer); } @@ -753,6 +850,8 @@ 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"); } @@ -763,6 +862,12 @@ return false; } +template +bool ByteCodeExprGen::VisitCXXDefaultInitExpr( + const CXXDefaultInitExpr *E) { + return this->visit(E->getExpr()); +} + template bool ByteCodeExprGen::VisitCXXDefaultArgExpr( const CXXDefaultArgExpr *E) { Index: clang/lib/AST/Interp/ByteCodeStmtGen.cpp =================================================================== --- clang/lib/AST/Interp/ByteCodeStmtGen.cpp +++ clang/lib/AST/Interp/ByteCodeStmtGen.cpp @@ -94,10 +94,6 @@ // 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; Index: clang/lib/AST/Interp/Interp.h =================================================================== --- clang/lib/AST/Interp/Interp.h +++ clang/lib/AST/Interp/Interp.h @@ -492,6 +492,9 @@ 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(); Index: clang/lib/AST/Interp/Record.h =================================================================== --- clang/lib/AST/Interp/Record.h +++ clang/lib/AST/Interp/Record.h @@ -67,6 +67,7 @@ } 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; Index: clang/test/AST/Interp/records.cpp =================================================================== --- /dev/null +++ clang/test/AST/Interp/records.cpp @@ -0,0 +1,88 @@ +// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -verify %s +// RUN: %clang_cc1 -verify=ref %s + +// ref-no-diagnostics +// expected-no-diagnostics + +struct Ints { + int a = 20; + int b = 30; + bool c = true; + + static const int five = 5; + static constexpr int getFive() { + return five; + } + + constexpr int getTen() const { + return 10; + } +}; + +constexpr Ints getInts() { + return {64, 128, true}; +} + +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, ""); + +constexpr Ints ints3 = getInts(); +static_assert(ints3.a == 64, ""); +static_assert(ints3.b == 128, ""); +static_assert(ints3.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, ""); Index: clang/test/AST/Interp/references.cpp =================================================================== --- clang/test/AST/Interp/references.cpp +++ clang/test/AST/Interp/references.cpp @@ -83,9 +83,9 @@ 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, "");