Skip to content

Commit 40f4bd1

Browse files
committed
[clang][Interp] Allow reading mutable members if they were created...
... in this evaluation.
1 parent 37698d9 commit 40f4bd1

File tree

10 files changed

+80
-22
lines changed

10 files changed

+80
-22
lines changed

clang/lib/AST/Interp/Context.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ bool Context::isPotentialConstantExpr(State &Parent, const FunctionDecl *FD) {
3939
}
4040

4141
bool Context::evaluateAsRValue(State &Parent, const Expr *E, APValue &Result) {
42+
++EvalID;
4243
bool Recursing = !Stk.empty();
4344
Compiler<EvalEmitter> C(*this, *P, Parent, Stk);
4445

@@ -65,6 +66,7 @@ bool Context::evaluateAsRValue(State &Parent, const Expr *E, APValue &Result) {
6566
}
6667

6768
bool Context::evaluate(State &Parent, const Expr *E, APValue &Result) {
69+
++EvalID;
6870
bool Recursing = !Stk.empty();
6971
Compiler<EvalEmitter> C(*this, *P, Parent, Stk);
7072

@@ -90,6 +92,7 @@ bool Context::evaluate(State &Parent, const Expr *E, APValue &Result) {
9092

9193
bool Context::evaluateAsInitializer(State &Parent, const VarDecl *VD,
9294
APValue &Result) {
95+
++EvalID;
9396
bool Recursing = !Stk.empty();
9497
Compiler<EvalEmitter> C(*this, *P, Parent, Stk);
9598

clang/lib/AST/Interp/Context.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,8 @@ class Context final {
109109

110110
const Record *getRecord(const RecordDecl *D) const;
111111

112+
unsigned getEvalID() const { return EvalID; }
113+
112114
private:
113115
/// Runs a function.
114116
bool Run(State &Parent, const Function *Func, APValue &Result);
@@ -119,6 +121,8 @@ class Context final {
119121
InterpStack Stk;
120122
/// Constexpr program.
121123
std::unique_ptr<Program> P;
124+
/// ID identifying an evaluation.
125+
unsigned EvalID = 0;
122126
};
123127

124128
} // namespace interp

clang/lib/AST/Interp/EvalEmitter.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ EvalEmitter::LabelTy EvalEmitter::getLabel() { return NextLabel++; }
8484
Scope::Local EvalEmitter::createLocal(Descriptor *D) {
8585
// Allocate memory for a local.
8686
auto Memory = std::make_unique<char[]>(sizeof(Block) + D->getAllocSize());
87-
auto *B = new (Memory.get()) Block(D, /*isStatic=*/false);
87+
auto *B = new (Memory.get()) Block(Ctx.getEvalID(), D, /*isStatic=*/false);
8888
B->invokeCtor();
8989

9090
// Initialize local variable inline descriptor.

clang/lib/AST/Interp/Interp.cpp

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -400,7 +400,7 @@ bool CheckDowncast(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
400400

401401
bool CheckConst(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
402402
assert(Ptr.isLive() && "Pointer is not live");
403-
if (!Ptr.isConst())
403+
if (!Ptr.isConst() || Ptr.isMutable())
404404
return true;
405405

406406
// The This pointer is writable in constructors and destructors,
@@ -422,9 +422,14 @@ bool CheckConst(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
422422

423423
bool CheckMutable(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
424424
assert(Ptr.isLive() && "Pointer is not live");
425-
if (!Ptr.isMutable()) {
425+
if (!Ptr.isMutable())
426+
return true;
427+
428+
// In C++14 onwards, it is permitted to read a mutable member whose
429+
// lifetime began within the evaluation.
430+
if (S.getLangOpts().CPlusPlus14 &&
431+
Ptr.block()->getEvalID() == S.Ctx.getEvalID())
426432
return true;
427-
}
428433

429434
const SourceInfo &Loc = S.Current->getSource(OpPC);
430435
const FieldDecl *Field = Ptr.getField();

clang/lib/AST/Interp/InterpBlock.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,8 @@ bool Block::hasPointer(const Pointer *P) const {
9292
#endif
9393

9494
DeadBlock::DeadBlock(DeadBlock *&Root, Block *Blk)
95-
: Root(Root), B(Blk->Desc, Blk->IsStatic, Blk->IsExtern, /*isDead=*/true) {
95+
: Root(Root),
96+
B(~0u, Blk->Desc, Blk->IsStatic, Blk->IsExtern, /*isDead=*/true) {
9697
// Add the block to the chain of dead blocks.
9798
if (Root)
9899
Root->Prev = this;

clang/lib/AST/Interp/InterpBlock.h

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -49,17 +49,19 @@ enum PrimType : unsigned;
4949
class Block final {
5050
public:
5151
/// Creates a new block.
52-
Block(const std::optional<unsigned> &DeclID, const Descriptor *Desc,
53-
bool IsStatic = false, bool IsExtern = false)
54-
: DeclID(DeclID), IsStatic(IsStatic), IsExtern(IsExtern), Desc(Desc) {
55-
assert(Desc);
56-
}
57-
58-
Block(const Descriptor *Desc, bool IsStatic = false, bool IsExtern = false)
59-
: DeclID((unsigned)-1), IsStatic(IsStatic), IsExtern(IsExtern),
52+
Block(unsigned EvalID, const std::optional<unsigned> &DeclID,
53+
const Descriptor *Desc, bool IsStatic = false, bool IsExtern = false)
54+
: EvalID(EvalID), DeclID(DeclID), IsStatic(IsStatic), IsExtern(IsExtern),
6055
Desc(Desc) {
61-
assert(Desc);
62-
}
56+
assert(Desc);
57+
}
58+
59+
Block(unsigned EvalID, const Descriptor *Desc, bool IsStatic = false,
60+
bool IsExtern = false)
61+
: EvalID(EvalID), DeclID((unsigned)-1), IsStatic(IsStatic),
62+
IsExtern(IsExtern), Desc(Desc) {
63+
assert(Desc);
64+
}
6365

6466
/// Returns the block's descriptor.
6567
const Descriptor *getDescriptor() const { return Desc; }
@@ -75,7 +77,11 @@ class Block final {
7577
unsigned getSize() const { return Desc->getAllocSize(); }
7678
/// Returns the declaration ID.
7779
std::optional<unsigned> getDeclID() const { return DeclID; }
80+
/// Returns whether the data of this block has been initialized via
81+
/// invoking the Ctor func.
7882
bool isInitialized() const { return IsInitialized; }
83+
/// The Evaluation ID this block was created in.
84+
unsigned getEvalID() const { return EvalID; }
7985

8086
/// Returns a pointer to the stored data.
8187
/// You are allowed to read Desc->getSize() bytes from this address.
@@ -130,8 +136,10 @@ class Block final {
130136
friend class DeadBlock;
131137
friend class InterpState;
132138

133-
Block(const Descriptor *Desc, bool IsExtern, bool IsStatic, bool IsDead)
134-
: IsStatic(IsStatic), IsExtern(IsExtern), IsDead(true), Desc(Desc) {
139+
Block(unsigned EvalID, const Descriptor *Desc, bool IsExtern, bool IsStatic,
140+
bool IsDead)
141+
: EvalID(EvalID), IsStatic(IsStatic), IsExtern(IsExtern), IsDead(true),
142+
Desc(Desc) {
135143
assert(Desc);
136144
}
137145

@@ -146,6 +154,7 @@ class Block final {
146154
bool hasPointer(const Pointer *P) const;
147155
#endif
148156

157+
const unsigned EvalID = ~0u;
149158
/// Start of the chain of pointers.
150159
Pointer *Pointers = nullptr;
151160
/// Unique identifier of the declaration.

clang/lib/AST/Interp/InterpFrame.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ InterpFrame::InterpFrame(InterpState &S, const Function *Func,
3737
Locals = std::make_unique<char[]>(FrameSize);
3838
for (auto &Scope : Func->scopes()) {
3939
for (auto &Local : Scope.locals()) {
40-
Block *B = new (localBlock(Local.Offset)) Block(Local.Desc);
40+
Block *B =
41+
new (localBlock(Local.Offset)) Block(S.Ctx.getEvalID(), Local.Desc);
4142
B->invokeCtor();
4243
new (localInlineDesc(Local.Offset)) InlineDescriptor(Local.Desc);
4344
}
@@ -220,7 +221,7 @@ Pointer InterpFrame::getParamPointer(unsigned Off) {
220221
const auto &Desc = Func->getParamDescriptor(Off);
221222
size_t BlockSize = sizeof(Block) + Desc.second->getAllocSize();
222223
auto Memory = std::make_unique<char[]>(BlockSize);
223-
auto *B = new (Memory.get()) Block(Desc.second);
224+
auto *B = new (Memory.get()) Block(S.Ctx.getEvalID(), Desc.second);
224225

225226
// Copy the initial value.
226227
TYPE_SWITCH(Desc.first, new (B->data()) T(stackRef<T>(Off)));

clang/lib/AST/Interp/Program.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ unsigned Program::createGlobalString(const StringLiteral *S) {
6363
// The byte length does not include the null terminator.
6464
unsigned I = Globals.size();
6565
unsigned Sz = Desc->getAllocSize();
66-
auto *G = new (Allocator, Sz) Global(Desc, /*isStatic=*/true,
66+
auto *G = new (Allocator, Sz) Global(Ctx.getEvalID(), Desc, /*isStatic=*/true,
6767
/*isExtern=*/false);
6868
G->block()->invokeCtor();
6969

@@ -170,7 +170,8 @@ std::optional<unsigned> Program::getOrCreateDummy(const ValueDecl *VD) {
170170
unsigned I = Globals.size();
171171

172172
auto *G = new (Allocator, Desc->getAllocSize())
173-
Global(getCurrentDecl(), Desc, /*IsStatic=*/true, /*IsExtern=*/false);
173+
Global(Ctx.getEvalID(), getCurrentDecl(), Desc, /*IsStatic=*/true,
174+
/*IsExtern=*/false);
174175
G->block()->invokeCtor();
175176

176177
Globals.push_back(G);
@@ -231,7 +232,7 @@ std::optional<unsigned> Program::createGlobal(const DeclTy &D, QualType Ty,
231232
unsigned I = Globals.size();
232233

233234
auto *G = new (Allocator, Desc->getAllocSize())
234-
Global(getCurrentDecl(), Desc, IsStatic, IsExtern);
235+
Global(Ctx.getEvalID(), getCurrentDecl(), Desc, IsStatic, IsExtern);
235236
G->block()->invokeCtor();
236237

237238
// Initialize InlineDescriptor fields.

clang/test/AST/Interp/const-temporaries.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,3 +84,9 @@ typedef int v[2];
8484
struct Z { int &&x, y; };
8585
Z z = { v{1,2}[0], z.x = 10 };
8686

87+
// CHECK: @_ZGR2z2_ ={{.*}} global %struct.R { i64 10 }
88+
// @z = {{.}} global %struct.Z { ptr @_ZGR1z_, %struct.R { i64 10 } }
89+
struct R { mutable long x; };
90+
struct Z2 { const R &x, y; };
91+
Z2 z2 = { R{1}, z2.x.x = 10 };
92+

clang/test/AST/Interp/mutable.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -std=c++11 -verify=expected,expected11,both,both11 %s
2+
// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -std=c++14 -verify=expected,expected14,both %s
3+
// RUN: %clang_cc1 -std=c++11 -verify=ref,ref11,both,both11 %s
4+
// RUN: %clang_cc1 -std=c++14 -verify=ref,ref14,both %s
5+
6+
7+
8+
9+
10+
namespace Simple {
11+
struct S {
12+
mutable int a; // both-note {{declared here}} \
13+
// both11-note {{declared here}}
14+
int a2;
15+
};
16+
17+
constexpr S s{12, 24};
18+
static_assert(s.a == 12, ""); // both-error {{not an integral constant expression}} \
19+
// both-note {{read of mutable member 'a'}}
20+
static_assert(s.a2 == 24, "");
21+
22+
23+
constexpr S s2{12, s2.a}; // both11-error {{must be initialized by a constant expression}} \
24+
// both11-note {{read of mutable member 'a'}} \
25+
// both11-note {{declared here}}
26+
static_assert(s2.a2 == 12, ""); // both11-error {{not an integral constant expression}} \
27+
// both11-note {{initializer of 's2' is not a constant expression}}
28+
}

0 commit comments

Comments
 (0)