Skip to content

Commit 33676ba

Browse files
committed
[clang][Interp] Fix variable initialization in inactive regions
When the EvalEmitter is inactive, it will simply not evaluate any of the operations we emit via emit*. However, it will still allocate variables. So the variables will be allocated, but we won't evaluate their initializer, so later when we see the variable again, it is uninitialized. Stop creating variables in that case.
1 parent 6eaf204 commit 33676ba

File tree

5 files changed

+39
-9
lines changed

5 files changed

+39
-9
lines changed

clang/lib/AST/Interp/ByteCodeEmitter.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ class ByteCodeEmitter {
5454
bool jump(const LabelTy &Label);
5555
bool fallthrough(const LabelTy &Label);
5656

57+
/// We're always emitting bytecode.
58+
bool isActive() const { return true; }
59+
5760
/// Callback for local registration.
5861
Local createLocal(Descriptor *D);
5962

clang/lib/AST/Interp/ByteCodeExprGen.cpp

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3404,11 +3404,16 @@ bool ByteCodeExprGen<Emitter>::visitDecl(const VarDecl *VD,
34043404
}
34053405

34063406
template <class Emitter>
3407-
bool ByteCodeExprGen<Emitter>::visitVarDecl(const VarDecl *VD) {
3407+
VarCreationState ByteCodeExprGen<Emitter>::visitVarDecl(const VarDecl *VD) {
34083408
// We don't know what to do with these, so just return false.
34093409
if (VD->getType().isNull())
34103410
return false;
34113411

3412+
// This case is EvalEmitter-only. If we won't create any instructions for the
3413+
// initializer anyway, don't bother creating the variable in the first place.
3414+
if (!this->isActive())
3415+
return VarCreationState::NotCreated();
3416+
34123417
const Expr *Init = VD->getInit();
34133418
std::optional<PrimType> VarT = classify(VD->getType());
34143419

@@ -4237,7 +4242,10 @@ bool ByteCodeExprGen<Emitter>::visitDeclRef(const ValueDecl *D, const Expr *E) {
42374242
if ((VD->hasGlobalStorage() || VD->isLocalVarDecl() ||
42384243
VD->isStaticDataMember()) &&
42394244
typeShouldBeVisited(VD->getType())) {
4240-
if (!this->visitVarDecl(VD))
4245+
auto VarState = this->visitVarDecl(VD);
4246+
if (VarState.notCreated())
4247+
return true;
4248+
if (!VarState)
42414249
return false;
42424250
// Retry.
42434251
return this->visitDeclRef(VD, E);
@@ -4247,7 +4255,10 @@ bool ByteCodeExprGen<Emitter>::visitDeclRef(const ValueDecl *D, const Expr *E) {
42474255
if (const auto *VD = dyn_cast<VarDecl>(D);
42484256
VD && VD->getAnyInitializer() &&
42494257
VD->getType().isConstant(Ctx.getASTContext()) && !VD->isWeak()) {
4250-
if (!this->visitVarDecl(VD))
4258+
auto VarState = this->visitVarDecl(VD);
4259+
if (VarState.notCreated())
4260+
return true;
4261+
if (!VarState)
42514262
return false;
42524263
// Retry.
42534264
return this->visitDeclRef(VD, E);

clang/lib/AST/Interp/ByteCodeExprGen.h

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,18 @@ struct InitLink {
7070
};
7171
};
7272

73+
/// State encapsulating if a the variable creation has been successful,
74+
/// unsuccessful, or no variable has been created at all.
75+
struct VarCreationState {
76+
std::optional<bool> S = std::nullopt;
77+
VarCreationState() = default;
78+
VarCreationState(bool b) : S(b) {}
79+
static VarCreationState NotCreated() { return VarCreationState(); }
80+
81+
operator bool() const { return S && *S; }
82+
bool notCreated() const { return !S; }
83+
};
84+
7385
/// Compilation context for expressions.
7486
template <class Emitter>
7587
class ByteCodeExprGen : public ConstStmtVisitor<ByteCodeExprGen<Emitter>, bool>,
@@ -220,9 +232,8 @@ class ByteCodeExprGen : public ConstStmtVisitor<ByteCodeExprGen<Emitter>, bool>,
220232
/// Just pass evaluation on to \p E. This leaves all the parsing flags
221233
/// intact.
222234
bool delegate(const Expr *E);
223-
224235
/// Creates and initializes a variable from the given decl.
225-
bool visitVarDecl(const VarDecl *VD);
236+
VarCreationState visitVarDecl(const VarDecl *VD);
226237
/// Visit an APValue.
227238
bool visitAPValue(const APValue &Val, PrimType ValType, const Expr *E);
228239
bool visitAPValueInitializer(const APValue &Val, const Expr *E);

clang/lib/AST/Interp/EvalEmitter.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,10 @@ class EvalEmitter : public SourceMapper {
6363
bool jump(const LabelTy &Label);
6464
bool fallthrough(const LabelTy &Label);
6565

66+
/// Since expressions can only jump forward, predicated execution is
67+
/// used to deal with if-else statements.
68+
bool isActive() const { return CurrentLabel == ActiveLabel; }
69+
6670
/// Callback for registering a local.
6771
Local createLocal(Descriptor *D);
6872

@@ -117,10 +121,6 @@ class EvalEmitter : public SourceMapper {
117121
/// Active block which should be executed.
118122
LabelTy ActiveLabel = 0;
119123

120-
/// Since expressions can only jump forward, predicated execution is
121-
/// used to deal with if-else statements.
122-
bool isActive() const { return CurrentLabel == ActiveLabel; }
123-
124124
protected:
125125
#define GET_EVAL_PROTO
126126
#include "Opcodes.inc"

clang/test/AST/Interp/c.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,3 +288,8 @@ char test10_global[test10_bound]; // all-error {{variable length array declarati
288288
void test10(void) {
289289
char test10_local[test10_bound] = "help"; // all-error {{variable-sized object may not be initialized}}
290290
}
291+
292+
void SuperSpecialFunc(void) {
293+
const int SuperSpecialCase = 10;
294+
_Static_assert((sizeof(SuperSpecialCase) == 12 && SuperSpecialCase == 3) || SuperSpecialCase == 10, ""); // pedantic-warning {{GNU extension}}
295+
}

0 commit comments

Comments
 (0)