Author: Timm Bäder Date: 2024-06-20T16:34:34+02:00 New Revision: 67f5312c41a072aaa725b5943cce2aa0f1643781
URL: https://github.com/llvm/llvm-project/commit/67f5312c41a072aaa725b5943cce2aa0f1643781 DIFF: https://github.com/llvm/llvm-project/commit/67f5312c41a072aaa725b5943cce2aa0f1643781.diff LOG: [clang][Interp] Nested ThisExprs that don't refer to the frame this ptr Use a series of ops in that case, getting us to the right declaration field. Added: Modified: clang/lib/AST/Interp/ByteCodeExprGen.cpp clang/lib/AST/Interp/ByteCodeExprGen.h clang/lib/AST/Interp/ByteCodeStmtGen.cpp clang/lib/AST/Interp/Interp.h clang/test/AST/Interp/records.cpp clang/test/SemaCXX/uninitialized.cpp Removed: ################################################################################ diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp index d26b2ab3f8243..b18873287ffcc 100644 --- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp +++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp @@ -34,11 +34,13 @@ template <class Emitter> class DeclScope final : public VariableScope<Emitter> { OldInitializingDecl(Ctx->InitializingDecl) { Ctx->GlobalDecl = Context::shouldBeGloballyIndexed(VD); Ctx->InitializingDecl = VD; + Ctx->InitStack.push_back(InitLink::Decl(VD)); } ~DeclScope() { this->Ctx->GlobalDecl = OldGlobalDecl; this->Ctx->InitializingDecl = OldInitializingDecl; + this->Ctx->InitStack.pop_back(); } private: @@ -72,6 +74,20 @@ template <class Emitter> class OptionScope final { bool OldInitializing; }; +template <class Emitter> +bool InitLink::emit(ByteCodeExprGen<Emitter> *Ctx, const Expr *E) const { + switch (Kind) { + case K_This: + return Ctx->emitThis(E); + case K_Field: + // We're assuming there's a base pointer on the stack already. + return Ctx->emitGetPtrFieldPop(Offset, E); + case K_Decl: + return Ctx->visitDeclRef(D, E); + } + return true; +} + } // namespace interp } // namespace clang @@ -3732,7 +3748,12 @@ template <class Emitter> bool ByteCodeExprGen<Emitter>::VisitCXXDefaultInitExpr( const CXXDefaultInitExpr *E) { SourceLocScope<Emitter> SLS(this, E); - return this->delegate(E->getExpr()); + + bool Old = InitStackActive; + InitStackActive = !isa<FunctionDecl>(E->getUsedContext()); + bool Result = this->delegate(E->getExpr()); + InitStackActive = Old; + return Result; } template <class Emitter> @@ -3788,6 +3809,17 @@ bool ByteCodeExprGen<Emitter>::VisitCXXThisExpr(const CXXThisExpr *E) { return this->emitGetPtrThisField(this->LambdaThisCapture.Offset, E); } + // In some circumstances, the 'this' pointer does not actually refer to the + // instance pointer of the current function frame, but e.g. to the declaration + // currently being initialized. Here we emit the necessary instruction(s) for + // this scenario. + if (InitStackActive && !InitStack.empty()) { + for (const InitLink &IL : InitStack) { + if (!IL.emit<Emitter>(this, E)) + return false; + } + return true; + } return this->emitThis(E); } diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.h b/clang/lib/AST/Interp/ByteCodeExprGen.h index 1a62d236b74b9..eef8cae6e38cd 100644 --- a/clang/lib/AST/Interp/ByteCodeExprGen.h +++ b/clang/lib/AST/Interp/ByteCodeExprGen.h @@ -32,10 +32,44 @@ template <class Emitter> class LocalScope; template <class Emitter> class DestructorScope; template <class Emitter> class VariableScope; template <class Emitter> class DeclScope; +template <class Emitter> class InitLinkScope; template <class Emitter> class OptionScope; template <class Emitter> class ArrayIndexScope; template <class Emitter> class SourceLocScope; +template <class Emitter> class ByteCodeExprGen; +struct InitLink { +public: + enum { + K_This = 0, + K_Field = 1, + K_Decl = 2, + }; + + static InitLink This() { return InitLink{K_This}; } + static InitLink Field(unsigned Offset) { + InitLink IL{K_Field}; + IL.Offset = Offset; + return IL; + } + static InitLink Decl(const ValueDecl *D) { + InitLink IL{K_Decl}; + IL.D = D; + return IL; + } + + InitLink(uint8_t Kind) : Kind(Kind) {} + template <class Emitter> + bool emit(ByteCodeExprGen<Emitter> *Ctx, const Expr *E) const; + +private: + uint32_t Kind; + union { + unsigned Offset; + const ValueDecl *D; + }; +}; + /// Compilation context for expressions. template <class Emitter> class ByteCodeExprGen : public ConstStmtVisitor<ByteCodeExprGen<Emitter>, bool>, @@ -254,9 +288,11 @@ class ByteCodeExprGen : public ConstStmtVisitor<ByteCodeExprGen<Emitter>, bool>, friend class LocalScope<Emitter>; friend class DestructorScope<Emitter>; friend class DeclScope<Emitter>; + friend class InitLinkScope<Emitter>; friend class OptionScope<Emitter>; friend class ArrayIndexScope<Emitter>; friend class SourceLocScope<Emitter>; + friend struct InitLink; /// Emits a zero initializer. bool visitZeroInitializer(PrimType T, QualType QT, const Expr *E); @@ -325,6 +361,9 @@ class ByteCodeExprGen : public ConstStmtVisitor<ByteCodeExprGen<Emitter>, bool>, bool Initializing = false; const ValueDecl *InitializingDecl = nullptr; + llvm::SmallVector<InitLink> InitStack; + bool InitStackActive = false; + /// Flag indicating if we're initializing a global variable. bool GlobalDecl = false; }; @@ -548,6 +587,18 @@ template <class Emitter> class SourceLocScope final { bool Enabled = false; }; +template <class Emitter> class InitLinkScope final { +public: + InitLinkScope(ByteCodeExprGen<Emitter> *Ctx, InitLink &&Link) : Ctx(Ctx) { + Ctx->InitStack.push_back(std::move(Link)); + } + + ~InitLinkScope() { this->Ctx->InitStack.pop_back(); } + +private: + ByteCodeExprGen<Emitter> *Ctx; +}; + } // namespace interp } // namespace clang diff --git a/clang/lib/AST/Interp/ByteCodeStmtGen.cpp b/clang/lib/AST/Interp/ByteCodeStmtGen.cpp index 6ee7898f228de..0618ec1aa8f58 100644 --- a/clang/lib/AST/Interp/ByteCodeStmtGen.cpp +++ b/clang/lib/AST/Interp/ByteCodeStmtGen.cpp @@ -155,8 +155,10 @@ bool ByteCodeStmtGen<Emitter>::visitFunc(const FunctionDecl *F) { return this->emitInitThisBitField(*T, F, FieldOffset, InitExpr); return this->emitInitThisField(*T, FieldOffset, InitExpr); } + // Non-primitive case. Get a pointer to the field-to-initialize // on the stack and call visitInitialzer() for it. + InitLinkScope<Emitter> FieldScope(this, InitLink::Field(F->Offset)); if (!this->emitGetPtrThisField(FieldOffset, InitExpr)) return false; @@ -178,6 +180,7 @@ bool ByteCodeStmtGen<Emitter>::visitFunc(const FunctionDecl *F) { if (!R) return false; + InitLinkScope<Emitter> InitScope(this, InitLink::This()); for (const auto *Init : Ctor->inits()) { // Scope needed for the initializers. BlockScope<Emitter> Scope(this); diff --git a/clang/lib/AST/Interp/Interp.h b/clang/lib/AST/Interp/Interp.h index fea83de829261..916d268aa4f09 100644 --- a/clang/lib/AST/Interp/Interp.h +++ b/clang/lib/AST/Interp/Interp.h @@ -1432,7 +1432,6 @@ inline bool GetPtrField(InterpState &S, CodePtr OpPC, uint32_t Off) { if (Ptr.isBlockPointer() && Off > Ptr.block()->getSize()) return false; - S.Stk.push<Pointer>(Ptr.atField(Off)); return true; } diff --git a/clang/test/AST/Interp/records.cpp b/clang/test/AST/Interp/records.cpp index 8a18f7a2a4890..9f341f5bc6d1d 100644 --- a/clang/test/AST/Interp/records.cpp +++ b/clang/test/AST/Interp/records.cpp @@ -468,19 +468,12 @@ namespace ConditionalInit { static_assert(getS(true).a == 12, ""); static_assert(getS(false).a == 13, ""); }; -/// FIXME: The following tests are broken. -/// They are using CXXDefaultInitExprs which contain a CXXThisExpr. The This pointer -/// in those refers to the declaration we are currently initializing, *not* the -/// This pointer of the current stack frame. This is something we haven't -/// implemented in the new interpreter yet. namespace DeclRefs { - struct A{ int m; const int &f = m; }; // expected-note {{implicit use of 'this'}} + struct A{ int m; const int &f = m; }; - constexpr A a{10}; // expected-error {{must be initialized by a constant expression}} \ - // expected-note {{declared here}} + constexpr A a{10}; static_assert(a.m == 10, ""); - static_assert(a.f == 10, ""); // expected-error {{not an integral constant expression}} \ - // expected-note {{initializer of 'a' is not a constant expression}} + static_assert(a.f == 10, ""); class Foo { public: @@ -499,12 +492,8 @@ namespace DeclRefs { A a = A{100}; }; constexpr B b; - /// FIXME: The following two lines don't work because we don't get the - /// pointers on the LHS correct. They make us run into an assertion - /// in CheckEvaluationResult. However, this may just be caused by the - /// problems in the previous examples. - //static_assert(b.a.m == 100, ""); - //static_assert(b.a.f == 100, ""); + static_assert(b.a.m == 100, ""); + static_assert(b.a.f == 100, ""); } namespace PointerArith { diff --git a/clang/test/SemaCXX/uninitialized.cpp b/clang/test/SemaCXX/uninitialized.cpp index c83c2e795824a..8a640c9691b32 100644 --- a/clang/test/SemaCXX/uninitialized.cpp +++ b/clang/test/SemaCXX/uninitialized.cpp @@ -1,4 +1,5 @@ // RUN: %clang_cc1 -fsyntax-only -Wall -Wuninitialized -Wno-unused-value -Wno-unused-lambda-capture -Wno-uninitialized-const-reference -std=c++1z -verify %s +// RUN: %clang_cc1 -fsyntax-only -Wall -Wuninitialized -Wno-unused-value -Wno-unused-lambda-capture -Wno-uninitialized-const-reference -std=c++1z -verify %s -fexperimental-new-constant-interpreter // definitions for std::move namespace std { _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits