[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