Author: Timm Bäder Date: 2024-06-29T21:10:24+02:00 New Revision: 40f4bd18f2fb01731fa7891fb7349e05dc98aeec
URL: https://github.com/llvm/llvm-project/commit/40f4bd18f2fb01731fa7891fb7349e05dc98aeec DIFF: https://github.com/llvm/llvm-project/commit/40f4bd18f2fb01731fa7891fb7349e05dc98aeec.diff LOG: [clang][Interp] Allow reading mutable members if they were created... ... in this evaluation. Added: clang/test/AST/Interp/mutable.cpp Modified: clang/lib/AST/Interp/Context.cpp clang/lib/AST/Interp/Context.h clang/lib/AST/Interp/EvalEmitter.cpp clang/lib/AST/Interp/Interp.cpp clang/lib/AST/Interp/InterpBlock.cpp clang/lib/AST/Interp/InterpBlock.h clang/lib/AST/Interp/InterpFrame.cpp clang/lib/AST/Interp/Program.cpp clang/test/AST/Interp/const-temporaries.cpp Removed: ################################################################################ diff --git a/clang/lib/AST/Interp/Context.cpp b/clang/lib/AST/Interp/Context.cpp index 22ccae4fa30f8..913e8d514282a 100644 --- a/clang/lib/AST/Interp/Context.cpp +++ b/clang/lib/AST/Interp/Context.cpp @@ -39,6 +39,7 @@ bool Context::isPotentialConstantExpr(State &Parent, const FunctionDecl *FD) { } bool Context::evaluateAsRValue(State &Parent, const Expr *E, APValue &Result) { + ++EvalID; bool Recursing = !Stk.empty(); Compiler<EvalEmitter> C(*this, *P, Parent, Stk); @@ -65,6 +66,7 @@ bool Context::evaluateAsRValue(State &Parent, const Expr *E, APValue &Result) { } bool Context::evaluate(State &Parent, const Expr *E, APValue &Result) { + ++EvalID; bool Recursing = !Stk.empty(); Compiler<EvalEmitter> C(*this, *P, Parent, Stk); @@ -90,6 +92,7 @@ bool Context::evaluate(State &Parent, const Expr *E, APValue &Result) { bool Context::evaluateAsInitializer(State &Parent, const VarDecl *VD, APValue &Result) { + ++EvalID; bool Recursing = !Stk.empty(); Compiler<EvalEmitter> C(*this, *P, Parent, Stk); diff --git a/clang/lib/AST/Interp/Context.h b/clang/lib/AST/Interp/Context.h index c78dc9a2a471e..b8ea4ad6b3b44 100644 --- a/clang/lib/AST/Interp/Context.h +++ b/clang/lib/AST/Interp/Context.h @@ -109,6 +109,8 @@ class Context final { const Record *getRecord(const RecordDecl *D) const; + unsigned getEvalID() const { return EvalID; } + private: /// Runs a function. bool Run(State &Parent, const Function *Func, APValue &Result); @@ -119,6 +121,8 @@ class Context final { InterpStack Stk; /// Constexpr program. std::unique_ptr<Program> P; + /// ID identifying an evaluation. + unsigned EvalID = 0; }; } // namespace interp diff --git a/clang/lib/AST/Interp/EvalEmitter.cpp b/clang/lib/AST/Interp/EvalEmitter.cpp index 6748b305d5c8e..f4854adba9348 100644 --- a/clang/lib/AST/Interp/EvalEmitter.cpp +++ b/clang/lib/AST/Interp/EvalEmitter.cpp @@ -84,7 +84,7 @@ EvalEmitter::LabelTy EvalEmitter::getLabel() { return NextLabel++; } Scope::Local EvalEmitter::createLocal(Descriptor *D) { // Allocate memory for a local. auto Memory = std::make_unique<char[]>(sizeof(Block) + D->getAllocSize()); - auto *B = new (Memory.get()) Block(D, /*isStatic=*/false); + auto *B = new (Memory.get()) Block(Ctx.getEvalID(), D, /*isStatic=*/false); B->invokeCtor(); // Initialize local variable inline descriptor. diff --git a/clang/lib/AST/Interp/Interp.cpp b/clang/lib/AST/Interp/Interp.cpp index 2fe8ab7d0df4b..0411fcad88ad0 100644 --- a/clang/lib/AST/Interp/Interp.cpp +++ b/clang/lib/AST/Interp/Interp.cpp @@ -400,7 +400,7 @@ bool CheckDowncast(InterpState &S, CodePtr OpPC, const Pointer &Ptr, bool CheckConst(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { assert(Ptr.isLive() && "Pointer is not live"); - if (!Ptr.isConst()) + if (!Ptr.isConst() || Ptr.isMutable()) return true; // The This pointer is writable in constructors and destructors, @@ -422,9 +422,14 @@ bool CheckConst(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { bool CheckMutable(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { assert(Ptr.isLive() && "Pointer is not live"); - if (!Ptr.isMutable()) { + if (!Ptr.isMutable()) + return true; + + // In C++14 onwards, it is permitted to read a mutable member whose + // lifetime began within the evaluation. + if (S.getLangOpts().CPlusPlus14 && + Ptr.block()->getEvalID() == S.Ctx.getEvalID()) return true; - } const SourceInfo &Loc = S.Current->getSource(OpPC); const FieldDecl *Field = Ptr.getField(); diff --git a/clang/lib/AST/Interp/InterpBlock.cpp b/clang/lib/AST/Interp/InterpBlock.cpp index 9b33d1b778fb2..c34ea7634b4a9 100644 --- a/clang/lib/AST/Interp/InterpBlock.cpp +++ b/clang/lib/AST/Interp/InterpBlock.cpp @@ -92,7 +92,8 @@ bool Block::hasPointer(const Pointer *P) const { #endif DeadBlock::DeadBlock(DeadBlock *&Root, Block *Blk) - : Root(Root), B(Blk->Desc, Blk->IsStatic, Blk->IsExtern, /*isDead=*/true) { + : Root(Root), + B(~0u, Blk->Desc, Blk->IsStatic, Blk->IsExtern, /*isDead=*/true) { // Add the block to the chain of dead blocks. if (Root) Root->Prev = this; diff --git a/clang/lib/AST/Interp/InterpBlock.h b/clang/lib/AST/Interp/InterpBlock.h index 2bb195648a9a9..1f25de3589630 100644 --- a/clang/lib/AST/Interp/InterpBlock.h +++ b/clang/lib/AST/Interp/InterpBlock.h @@ -49,17 +49,19 @@ enum PrimType : unsigned; class Block final { public: /// Creates a new block. - Block(const std::optional<unsigned> &DeclID, const Descriptor *Desc, - bool IsStatic = false, bool IsExtern = false) - : DeclID(DeclID), IsStatic(IsStatic), IsExtern(IsExtern), Desc(Desc) { - assert(Desc); - } - - Block(const Descriptor *Desc, bool IsStatic = false, bool IsExtern = false) - : DeclID((unsigned)-1), IsStatic(IsStatic), IsExtern(IsExtern), + Block(unsigned EvalID, const std::optional<unsigned> &DeclID, + const Descriptor *Desc, bool IsStatic = false, bool IsExtern = false) + : EvalID(EvalID), DeclID(DeclID), IsStatic(IsStatic), IsExtern(IsExtern), Desc(Desc) { - assert(Desc); - } + assert(Desc); + } + + Block(unsigned EvalID, const Descriptor *Desc, bool IsStatic = false, + bool IsExtern = false) + : EvalID(EvalID), DeclID((unsigned)-1), IsStatic(IsStatic), + IsExtern(IsExtern), Desc(Desc) { + assert(Desc); + } /// Returns the block's descriptor. const Descriptor *getDescriptor() const { return Desc; } @@ -75,7 +77,11 @@ class Block final { unsigned getSize() const { return Desc->getAllocSize(); } /// Returns the declaration ID. std::optional<unsigned> getDeclID() const { return DeclID; } + /// Returns whether the data of this block has been initialized via + /// invoking the Ctor func. bool isInitialized() const { return IsInitialized; } + /// The Evaluation ID this block was created in. + unsigned getEvalID() const { return EvalID; } /// Returns a pointer to the stored data. /// You are allowed to read Desc->getSize() bytes from this address. @@ -130,8 +136,10 @@ class Block final { friend class DeadBlock; friend class InterpState; - Block(const Descriptor *Desc, bool IsExtern, bool IsStatic, bool IsDead) - : IsStatic(IsStatic), IsExtern(IsExtern), IsDead(true), Desc(Desc) { + Block(unsigned EvalID, const Descriptor *Desc, bool IsExtern, bool IsStatic, + bool IsDead) + : EvalID(EvalID), IsStatic(IsStatic), IsExtern(IsExtern), IsDead(true), + Desc(Desc) { assert(Desc); } @@ -146,6 +154,7 @@ class Block final { bool hasPointer(const Pointer *P) const; #endif + const unsigned EvalID = ~0u; /// Start of the chain of pointers. Pointer *Pointers = nullptr; /// Unique identifier of the declaration. diff --git a/clang/lib/AST/Interp/InterpFrame.cpp b/clang/lib/AST/Interp/InterpFrame.cpp index 54ccf9034c7a7..b33f74dfe99f1 100644 --- a/clang/lib/AST/Interp/InterpFrame.cpp +++ b/clang/lib/AST/Interp/InterpFrame.cpp @@ -37,7 +37,8 @@ InterpFrame::InterpFrame(InterpState &S, const Function *Func, Locals = std::make_unique<char[]>(FrameSize); for (auto &Scope : Func->scopes()) { for (auto &Local : Scope.locals()) { - Block *B = new (localBlock(Local.Offset)) Block(Local.Desc); + Block *B = + new (localBlock(Local.Offset)) Block(S.Ctx.getEvalID(), Local.Desc); B->invokeCtor(); new (localInlineDesc(Local.Offset)) InlineDescriptor(Local.Desc); } @@ -220,7 +221,7 @@ Pointer InterpFrame::getParamPointer(unsigned Off) { const auto &Desc = Func->getParamDescriptor(Off); size_t BlockSize = sizeof(Block) + Desc.second->getAllocSize(); auto Memory = std::make_unique<char[]>(BlockSize); - auto *B = new (Memory.get()) Block(Desc.second); + auto *B = new (Memory.get()) Block(S.Ctx.getEvalID(), Desc.second); // Copy the initial value. TYPE_SWITCH(Desc.first, new (B->data()) T(stackRef<T>(Off))); diff --git a/clang/lib/AST/Interp/Program.cpp b/clang/lib/AST/Interp/Program.cpp index 2a1ad4d4eb850..d3864d23925c0 100644 --- a/clang/lib/AST/Interp/Program.cpp +++ b/clang/lib/AST/Interp/Program.cpp @@ -63,7 +63,7 @@ unsigned Program::createGlobalString(const StringLiteral *S) { // The byte length does not include the null terminator. unsigned I = Globals.size(); unsigned Sz = Desc->getAllocSize(); - auto *G = new (Allocator, Sz) Global(Desc, /*isStatic=*/true, + auto *G = new (Allocator, Sz) Global(Ctx.getEvalID(), Desc, /*isStatic=*/true, /*isExtern=*/false); G->block()->invokeCtor(); @@ -170,7 +170,8 @@ std::optional<unsigned> Program::getOrCreateDummy(const ValueDecl *VD) { unsigned I = Globals.size(); auto *G = new (Allocator, Desc->getAllocSize()) - Global(getCurrentDecl(), Desc, /*IsStatic=*/true, /*IsExtern=*/false); + Global(Ctx.getEvalID(), getCurrentDecl(), Desc, /*IsStatic=*/true, + /*IsExtern=*/false); G->block()->invokeCtor(); Globals.push_back(G); @@ -231,7 +232,7 @@ std::optional<unsigned> Program::createGlobal(const DeclTy &D, QualType Ty, unsigned I = Globals.size(); auto *G = new (Allocator, Desc->getAllocSize()) - Global(getCurrentDecl(), Desc, IsStatic, IsExtern); + Global(Ctx.getEvalID(), getCurrentDecl(), Desc, IsStatic, IsExtern); G->block()->invokeCtor(); // Initialize InlineDescriptor fields. diff --git a/clang/test/AST/Interp/const-temporaries.cpp b/clang/test/AST/Interp/const-temporaries.cpp index 1f48786691c1d..bbb95b3c3dff7 100644 --- a/clang/test/AST/Interp/const-temporaries.cpp +++ b/clang/test/AST/Interp/const-temporaries.cpp @@ -84,3 +84,9 @@ typedef int v[2]; struct Z { int &&x, y; }; Z z = { v{1,2}[0], z.x = 10 }; +// CHECK: @_ZGR2z2_ ={{.*}} global %struct.R { i64 10 } +// @z = {{.}} global %struct.Z { ptr @_ZGR1z_, %struct.R { i64 10 } } +struct R { mutable long x; }; +struct Z2 { const R &x, y; }; +Z2 z2 = { R{1}, z2.x.x = 10 }; + diff --git a/clang/test/AST/Interp/mutable.cpp b/clang/test/AST/Interp/mutable.cpp new file mode 100644 index 0000000000000..aebbea920578c --- /dev/null +++ b/clang/test/AST/Interp/mutable.cpp @@ -0,0 +1,28 @@ +// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -std=c++11 -verify=expected,expected11,both,both11 %s +// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -std=c++14 -verify=expected,expected14,both %s +// RUN: %clang_cc1 -std=c++11 -verify=ref,ref11,both,both11 %s +// RUN: %clang_cc1 -std=c++14 -verify=ref,ref14,both %s + + + + + +namespace Simple { + struct S { + mutable int a; // both-note {{declared here}} \ + // both11-note {{declared here}} + int a2; + }; + + constexpr S s{12, 24}; + static_assert(s.a == 12, ""); // both-error {{not an integral constant expression}} \ + // both-note {{read of mutable member 'a'}} + static_assert(s.a2 == 24, ""); + + + constexpr S s2{12, s2.a}; // both11-error {{must be initialized by a constant expression}} \ + // both11-note {{read of mutable member 'a'}} \ + // both11-note {{declared here}} + static_assert(s2.a2 == 12, ""); // both11-error {{not an integral constant expression}} \ + // both11-note {{initializer of 's2' is not a constant expression}} +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits