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 @@ -87,6 +87,8 @@ bool VisitCharacterLiteral(const CharacterLiteral *E); bool VisitCompoundAssignOperator(const CompoundAssignOperator *E); bool VisitFloatCompoundAssignOperator(const CompoundAssignOperator *E); + bool VisitExprWithCleanups(const ExprWithCleanups *E); + bool VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *E); protected: bool visitExpr(const Expr *E) override; 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 @@ -782,6 +782,57 @@ return this->emitStore(*ResultT, E); } +template +bool ByteCodeExprGen::VisitExprWithCleanups( + const ExprWithCleanups *E) { + const Expr *SubExpr = E->getSubExpr(); + + assert(E->getNumObjects() == 0 && "TODO: Implement cleanups"); + return this->visit(SubExpr); +} + +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 (std::optional GlobalIndex = P.createGlobal(E)) { + const LifetimeExtendedTemporaryDecl *TempDecl = + E->getLifetimeExtendedTemporaryDecl(); + + if (!this->visitInitializer(SubExpr)) + return false; + + if (!this->emitInitGlobalTemp(*SubExprT, *GlobalIndex, TempDecl, E)) + return false; + return this->emitGetPtrGlobal(*GlobalIndex, E); + } + } else if (SD == SD_Automatic) { + if (std::optional LocalIndex = + allocateLocalPrimitive(SubExpr, *SubExprT, true, true)) { + if (!this->visitInitializer(SubExpr)) + return false; + + if (!this->emitSetLocal(*SubExprT, *LocalIndex, E)) + return false; + return this->emitGetPtrLocal(*LocalIndex, E); + } + } + + return false; +} + template bool ByteCodeExprGen::discard(const Expr *E) { if (E->containsErrors()) return false; diff --git a/clang/lib/AST/Interp/Interp.h b/clang/lib/AST/Interp/Interp.h --- a/clang/lib/AST/Interp/Interp.h +++ b/clang/lib/AST/Interp/Interp.h @@ -818,6 +818,22 @@ return true; } +/// 1) Converts the value on top of the stack to an APValue +/// 2) Sets that APValue on \Temp +/// 3) Initialized global with index \I with that +template ::T> +bool InitGlobalTemp(InterpState &S, CodePtr OpPC, uint32_t I, + const LifetimeExtendedTemporaryDecl *Temp) { + assert(Temp); + const T Value = S.Stk.peek(); + APValue APV = Value.toAPValue(); + APValue *Cached = Temp->getOrCreateValue(true); + *Cached = APV; + + S.P.getGlobal(I)->deref() = S.Stk.pop(); + return true; +} + template ::T> bool InitThisField(InterpState &S, CodePtr OpPC, uint32_t I) { if (S.checkingPotentialConstantExpression()) diff --git a/clang/lib/AST/Interp/Opcodes.td b/clang/lib/AST/Interp/Opcodes.td --- a/clang/lib/AST/Interp/Opcodes.td +++ b/clang/lib/AST/Interp/Opcodes.td @@ -49,6 +49,7 @@ def ArgRecordField : ArgType { let Name = "const Record::Field *"; } def ArgFltSemantics : ArgType { let Name = "const llvm::fltSemantics *"; } def ArgRoundingMode : ArgType { let Name = "llvm::RoundingMode"; } +def ArgLETD: ArgType { let Name = "const LifetimeExtendedTemporaryDecl *"; } //===----------------------------------------------------------------------===// // Classes of types instructions operate on. @@ -332,6 +333,10 @@ // [Value] -> [] def InitGlobal : AccessOpcode; // [Value] -> [] +def InitGlobalTemp : AccessOpcode { + let Args = [ArgUint32, ArgLETD]; +} +// [Value] -> [] def SetGlobal : AccessOpcode; // [] -> [Value] diff --git a/clang/test/AST/Interp/references.cpp b/clang/test/AST/Interp/references.cpp --- a/clang/test/AST/Interp/references.cpp +++ b/clang/test/AST/Interp/references.cpp @@ -2,8 +2,6 @@ // RUN: %clang_cc1 -verify=ref %s -// ref-no-diagnostics - constexpr int a = 10; constexpr const int &b = a; static_assert(a == b, ""); @@ -71,9 +69,22 @@ } static_assert(testGetValue() == 30, ""); -// FIXME: ExprWithCleanups + MaterializeTemporaryExpr not implemented -constexpr const int &MCE = 1; // expected-error{{must be initialized by a constant expression}} +constexpr const int &MCE = 20; +static_assert(MCE == 20, ""); +static_assert(MCE == 30, ""); // expected-error {{static assertion failed}} \ + // expected-note {{evaluates to '20 == 30'}} \ + // ref-error {{static assertion failed}} \ + // ref-note {{evaluates to '20 == 30'}} +constexpr int LocalMCE() { + const int &m = 100; + return m; +} +static_assert(LocalMCE() == 100, ""); +static_assert(LocalMCE() == 200, ""); // expected-error {{static assertion failed}} \ + // expected-note {{evaluates to '100 == 200'}} \ + // ref-error {{static assertion failed}} \ + // ref-note {{evaluates to '100 == 200'}} struct S { int i, j;