Index: clang/lib/AST/Interp/ByteCodeExprGen.h =================================================================== --- clang/lib/AST/Interp/ByteCodeExprGen.h +++ clang/lib/AST/Interp/ByteCodeExprGen.h @@ -97,6 +97,8 @@ bool visitDecl(const VarDecl *VD) override; bool visitVarDecl(const VarDecl *VD); + bool allocateVariable(const ValueDecl *VD, const Expr *Init); + /// Emits scope cleanup instructions. void emitCleanup(); @@ -287,8 +289,11 @@ /// Returns whether we should create a global variable for the /// given VarDecl. - bool isGlobalDecl(const VarDecl *VD) const { - return !VD->hasLocalStorage() || VD->isConstexpr(); + bool isGlobalDecl(const ValueDecl *VD) const { + if (const auto *V = dyn_cast(VD)) + return !V->hasLocalStorage() || V->isConstexpr(); + + return false; } }; Index: clang/lib/AST/Interp/ByteCodeExprGen.cpp =================================================================== --- clang/lib/AST/Interp/ByteCodeExprGen.cpp +++ clang/lib/AST/Interp/ByteCodeExprGen.cpp @@ -29,7 +29,7 @@ /// Scope used to handle temporaries in toplevel variable declarations. template class DeclScope final : public VariableScope { public: - DeclScope(ByteCodeExprGen *Ctx, const VarDecl *VD) + DeclScope(ByteCodeExprGen *Ctx, const ValueDecl *VD) : VariableScope(Ctx), Scope(Ctx->P, VD) {} virtual ~DeclScope() override { this->emitDestruction(); } @@ -796,14 +796,15 @@ bool ByteCodeExprGen::dereference( const Expr *LV, DerefKind AK, llvm::function_ref Direct, llvm::function_ref Indirect) { + if (Optional T = classify(LV->getType())) { if (!LV->refersToBitField()) { // Only primitive, non bit-field types can be dereferenced directly. - if (auto *DE = dyn_cast(LV)) { + if (const auto *DE = dyn_cast(LV)) { if (!DE->getDecl()->getType()->isReferenceType()) { - if (auto *PD = dyn_cast(DE->getDecl())) + if (const auto *PD = dyn_cast(DE->getDecl())) return dereferenceParam(LV, *T, PD, AK, Direct, Indirect); - if (auto *VD = dyn_cast(DE->getDecl())) + if (const auto *VD = dyn_cast(DE->getDecl())) return dereferenceVar(LV, *T, VD, AK, Direct, Indirect); } } @@ -1379,8 +1380,8 @@ } template -bool ByteCodeExprGen::visitVarDecl(const VarDecl *VD) { - const Expr *Init = VD->getInit(); +bool ByteCodeExprGen::allocateVariable(const ValueDecl *VD, + const Expr *Init) { Optional VarT = classify(VD->getType()); if (isGlobalDecl(VD)) { @@ -1411,6 +1412,14 @@ if (!this->visit(Init)) return false; + // Initializers are supposed to be rvalues. However, sometimes + // they aren't. This is the case for C++ decomposition decl + // bindings. + if (Init && Init->isLValue() && *VarT != PT_Ptr) { + if (!this->emitLoadPop(*VarT, VD)) + return false; + } + return this->emitSetLocal(*VarT, Offset, VD); } } else { @@ -1425,6 +1434,29 @@ return false; } +template +bool ByteCodeExprGen::visitVarDecl(const VarDecl *VD) { + // For decomposition decls, we just emit multiple variables. + if (const auto *DD = dyn_cast(VD)) { + // Expression to decompose. + const Expr *Init = DD->getInit(); + + if (!this->allocateVariable(DD, Init)) + return false; + + for (const auto *BD : DD->bindings()) { + assert(!BD->getHoldingVar()); // FIXME + if (!this->allocateVariable(BD, BD->getBinding())) + return false; + } + + return true; + } + + // Regular variables. + return this->allocateVariable(VD, VD->getInit()); +} + template bool ByteCodeExprGen::VisitCallExpr(const CallExpr *E) { const Decl *Callee = E->getCalleeDecl(); Index: clang/lib/AST/Interp/ByteCodeStmtGen.cpp =================================================================== --- clang/lib/AST/Interp/ByteCodeStmtGen.cpp +++ clang/lib/AST/Interp/ByteCodeStmtGen.cpp @@ -211,17 +211,11 @@ template bool ByteCodeStmtGen::visitDeclStmt(const DeclStmt *DS) { for (auto *D : DS->decls()) { - // Variable declarator. - if (auto *VD = dyn_cast(D)) { - if (!this->visitVarDecl(VD)) - return false; - continue; - } - - // Decomposition declarator. - if (auto *DD = dyn_cast(D)) { - return this->bail(DD); - } + const auto *VD = dyn_cast(D); + if (!VD) + return false; + if (!this->visitVarDecl(VD)) + return false; } return true; Index: clang/lib/AST/Interp/Program.h =================================================================== --- clang/lib/AST/Interp/Program.h +++ clang/lib/AST/Interp/Program.h @@ -131,7 +131,9 @@ /// Context to manage declaration lifetimes. class DeclScope { public: - DeclScope(Program &P, const VarDecl *VD) : P(P) { P.startDeclaration(VD); } + DeclScope(Program &P, const ValueDecl *VD) : P(P) { + P.startDeclaration(VD); + } ~DeclScope() { P.endDeclaration(); } private: @@ -222,7 +224,7 @@ unsigned CurrentDeclaration = NoDeclaration; /// Starts evaluating a declaration. - void startDeclaration(const VarDecl *Decl) { + void startDeclaration(const ValueDecl *Decl) { LastDeclaration += 1; CurrentDeclaration = LastDeclaration; } Index: clang/test/AST/Interp/cxx17.cpp =================================================================== --- /dev/null +++ clang/test/AST/Interp/cxx17.cpp @@ -0,0 +1,26 @@ +// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -std=c++17 -verify %s +// RUN: %clang_cc1 -std=c++17 -verify=ref %s + +// expected-no-diagnostics +// ref-no-diagnostics + +struct F { int a; int b;}; +constexpr F getF() { + return {12, 3}; +} +constexpr int f() { + auto [a1, b1] = getF(); + auto [a2, b2] = getF(); + + return a1 + a2 + b1 + b2; +} +static_assert(f() == 30); + + +constexpr int a() { + int a[2] = {5, 3}; + auto [x, y] = a; + return x + y; +} +static_assert(a() == 8); +