diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.h b/clang/lib/AST/Interp/ByteCodeExprGen.h --- a/clang/lib/AST/Interp/ByteCodeExprGen.h +++ b/clang/lib/AST/Interp/ByteCodeExprGen.h @@ -90,6 +90,7 @@ bool VisitPointerCompoundAssignOperator(const CompoundAssignOperator *E); bool VisitExprWithCleanups(const ExprWithCleanups *E); bool VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *E); + bool VisitCompoundLiteralExpr(const CompoundLiteralExpr *E); protected: bool visitExpr(const Expr *E) override; @@ -346,7 +347,10 @@ BlockScope(ByteCodeExprGen *Ctx) : LocalScope(Ctx) {} void addExtended(const Scope::Local &Local) override { - llvm_unreachable("Cannot create temporaries in full scopes"); + // If we to this point, just add the variable as a normal local + // variable. It will be destroyed at the end of the block just + // like all others. + this->addLocal(Local); } }; @@ -357,8 +361,8 @@ ExprScope(ByteCodeExprGen *Ctx) : LocalScope(Ctx) {} void addExtended(const Scope::Local &Local) override { - assert(this->Parent); - this->Parent->addLocal(Local); + if (this->Parent) + this->Parent->addLocal(Local); } }; diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp --- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp +++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp @@ -835,19 +835,10 @@ template bool ByteCodeExprGen::VisitMaterializeTemporaryExpr( const MaterializeTemporaryExpr *E) { - StorageDuration SD = E->getStorageDuration(); - - // We conservatively only support these for now. - if (SD != SD_Static && SD != SD_Automatic) - return false; - const Expr *SubExpr = E->getSubExpr(); std::optional SubExprT = classify(SubExpr); - // FIXME: Implement this for records and arrays as well. - if (!SubExprT) - return false; - if (SD == SD_Static) { + if (E->getStorageDuration() == SD_Static) { if (std::optional GlobalIndex = P.createGlobal(E)) { const LifetimeExtendedTemporaryDecl *TempDecl = E->getLifetimeExtendedTemporaryDecl(); @@ -859,15 +850,54 @@ return false; return this->emitGetPtrGlobal(*GlobalIndex, E); } - } else if (SD == SD_Automatic) { - if (std::optional LocalIndex = - allocateLocalPrimitive(SubExpr, *SubExprT, true, true)) { + + return false; + } + + // For everyhing else, use local variables. + if (SubExprT) { + if (std::optional LocalIndex = allocateLocalPrimitive( + SubExpr, *SubExprT, /*IsMutable=*/true, /*IsExtended=*/true)) { if (!this->visitInitializer(SubExpr)) return false; + this->emitSetLocal(*SubExprT, *LocalIndex, E); + return this->emitGetPtrLocal(*LocalIndex, E); + } + } else { + if (std::optional LocalIndex = + allocateLocal(SubExpr, /*IsExtended=*/true)) { + if (!this->emitGetPtrLocal(*LocalIndex, E)) + return false; + return this->visitInitializer(SubExpr); + } + } + return false; +} - if (!this->emitSetLocal(*SubExprT, *LocalIndex, E)) +template +bool ByteCodeExprGen::VisitCompoundLiteralExpr( + const CompoundLiteralExpr *E) { + std::optional T = classify(E->getType()); + const Expr *Init = E->getInitializer(); + if (E->isFileScope()) { + if (std::optional GlobalIndex = P.createGlobal(E)) { + if (classify(E->getType())) + return this->visit(Init); + if (!this->emitGetPtrGlobal(*GlobalIndex, E)) return false; - return this->emitGetPtrLocal(*LocalIndex, E); + return this->visitInitializer(Init); + } + } + + // Otherwise, use a local variable. + if (T) { + // For primitive types, we just visit the initializer. + return this->visit(Init); + } else { + if (std::optional LocalIndex = allocateLocal(Init)) { + if (!this->emitGetPtrLocal(*LocalIndex, E)) + return false; + return this->visitInitializer(Init); } } @@ -1326,6 +1356,8 @@ } } return true; + } else if (const auto *CLE = dyn_cast(Initializer)) { + return visitInitializer(CLE->getInitializer()); } assert(false && "Unknown expression for array initialization"); @@ -1511,6 +1543,10 @@ template bool ByteCodeExprGen::visitVarDecl(const VarDecl *VD) { + // We don't know what to do with these, so just return false. + if (VD->getType().isNull()) + return false; + const Expr *Init = VD->getInit(); std::optional VarT = classify(VD->getType()); diff --git a/clang/test/AST/Interp/cxx17.cpp b/clang/test/AST/Interp/cxx17.cpp --- a/clang/test/AST/Interp/cxx17.cpp +++ b/clang/test/AST/Interp/cxx17.cpp @@ -2,6 +2,7 @@ // RUN: %clang_cc1 -std=c++17 -verify=ref %s // ref-no-diagnostics +// expected-no-diagnostics struct F { int a; int b;}; constexpr F getF() { @@ -21,8 +22,7 @@ return a + b; } -// FIXME: This should work, but the MaterializeTemporaryExpr handling is not ready for it. -static_assert(structRefs() == 15); // expected-error {{not an integral constant expression}} +static_assert(structRefs() == 15); constexpr int structRefs2() { F f = getF(); diff --git a/clang/test/AST/Interp/literals.cpp b/clang/test/AST/Interp/literals.cpp --- a/clang/test/AST/Interp/literals.cpp +++ b/clang/test/AST/Interp/literals.cpp @@ -708,3 +708,38 @@ } }; #endif + +namespace CompoundLiterals { + constexpr int get5() { + return (int[]){1,2,3,4,5}[4]; + } + static_assert(get5() == 5, ""); + + constexpr int get6(int f = (int[]){1,2,6}[2]) { // ref-note {{subexpression not valid in a constant expression}} \ + // ref-note {{declared here}} + return f; + } + static_assert(get6(6) == 6, ""); + // FIXME: Who's right here? + static_assert(get6() == 6, ""); // ref-error {{not an integral constant expression}} + + constexpr int x = (int){3}; + static_assert(x == 3, ""); +#if __cplusplus >= 201402L + constexpr int getX() { + int x = (int){3}; + x = (int){5}; + return x; + } + static_assert(getX() == 5, ""); +#endif + +#if __cplusplus >= 202002L + constexpr int get3() { + int m; + m = (int){3}; + return m; + } + static_assert(get3() == 3, ""); +#endif +}; diff --git a/clang/test/AST/Interp/records.cpp b/clang/test/AST/Interp/records.cpp --- a/clang/test/AST/Interp/records.cpp +++ b/clang/test/AST/Interp/records.cpp @@ -50,9 +50,6 @@ static_assert(ints2.b == -30, ""); static_assert(!ints2.c, ""); -#if __cplusplus >= 201703L -// FIXME: In c++14, this uses a MaterializeTemporaryExpr, -// which the new interpreter doesn't support yet. constexpr Ints getInts() { return {64, 128, true}; } @@ -60,7 +57,6 @@ static_assert(ints3.a == 64, ""); static_assert(ints3.b == 128, ""); static_assert(ints3.c, ""); -#endif constexpr Ints ints4 = { .a = 40 * 50, @@ -88,9 +84,9 @@ int a = 10; int b; }; -// FIXME: Broken in the new constant interpreter. -// Should be rejected, but without asan errors. -//constexpr Ints2 ints2; +constexpr Ints2 ints22; // expected-error {{without a user-provided default constructor}} \ + // expected-error {{must be initialized by a constant expression}} \ + // ref-error {{without a user-provided default constructor}} class C { public: @@ -124,9 +120,6 @@ } static_assert(getPointer()->a == 100, ""); -#if __cplusplus >= 201703L -// FIXME: In c++14, this uses a MaterializeTemporaryExpr, -// which the new interpreter doesn't support yet. constexpr C RVOAndParams(const C *c) { return C(); } @@ -137,7 +130,6 @@ return C(); } constexpr C RVOAndParamsResult2 = RVOAndParams(12); -#endif class Bar { // expected-note {{definition of 'Bar' is not complete}} \ // ref-note {{definition of 'Bar' is not complete}} @@ -158,16 +150,16 @@ c.a = 10; // Assignment, not an initializer. - // c = C(); FIXME + c = C(); c.a = 10; // Assignment, not an initializer. - //c = RVOAndParams(&c); FIXME + c = RVOAndParams(&c); return c.a; } -static_assert(locals() == 10, ""); +static_assert(locals() == 100, ""); namespace thisPointer { struct S { @@ -235,10 +227,7 @@ this->a; // expected-warning {{expression result unused}} \ // ref-warning {{expression result unused}} get5(); -#if __cplusplus >= 201703L - // FIXME: Enable once we support MaterializeConstantExpr properly. getInts(); -#endif } constexpr int m() const {