[clang] 40f4bd1 - [clang][Interp] Allow reading mutable members if they were created...

Timm Bäder via cfe-commits cfe-commits at lists.llvm.org
Sat Jun 29 12:39:47 PDT 2024


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}}
+}


        


More information about the cfe-commits mailing list