[clang] 33676ba - [clang][Interp] Fix variable initialization in inactive regions
Timm Bäder via cfe-commits
cfe-commits at lists.llvm.org
Mon Jun 24 01:18:20 PDT 2024
Author: Timm Bäder
Date: 2024-06-24T10:18:05+02:00
New Revision: 33676ba543737f8e286e28a9cae81a848bdd3f09
URL: https://github.com/llvm/llvm-project/commit/33676ba543737f8e286e28a9cae81a848bdd3f09
DIFF: https://github.com/llvm/llvm-project/commit/33676ba543737f8e286e28a9cae81a848bdd3f09.diff
LOG: [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.
Added:
Modified:
clang/lib/AST/Interp/ByteCodeEmitter.h
clang/lib/AST/Interp/ByteCodeExprGen.cpp
clang/lib/AST/Interp/ByteCodeExprGen.h
clang/lib/AST/Interp/EvalEmitter.h
clang/test/AST/Interp/c.c
Removed:
################################################################################
diff --git a/clang/lib/AST/Interp/ByteCodeEmitter.h b/clang/lib/AST/Interp/ByteCodeEmitter.h
index d797a0ab4a1c9..9a329e969f339 100644
--- a/clang/lib/AST/Interp/ByteCodeEmitter.h
+++ b/clang/lib/AST/Interp/ByteCodeEmitter.h
@@ -54,6 +54,9 @@ class ByteCodeEmitter {
bool jump(const LabelTy &Label);
bool fallthrough(const LabelTy &Label);
+ /// We're always emitting bytecode.
+ bool isActive() const { return true; }
+
/// Callback for local registration.
Local createLocal(Descriptor *D);
diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
index 72c569f56a788..cb5962466f35f 100644
--- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp
+++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
@@ -3404,11 +3404,16 @@ bool ByteCodeExprGen<Emitter>::visitDecl(const VarDecl *VD,
}
template <class Emitter>
-bool ByteCodeExprGen<Emitter>::visitVarDecl(const VarDecl *VD) {
+VarCreationState ByteCodeExprGen<Emitter>::visitVarDecl(const VarDecl *VD) {
// We don't know what to do with these, so just return false.
if (VD->getType().isNull())
return false;
+ // This case is EvalEmitter-only. If we won't create any instructions for the
+ // initializer anyway, don't bother creating the variable in the first place.
+ if (!this->isActive())
+ return VarCreationState::NotCreated();
+
const Expr *Init = VD->getInit();
std::optional<PrimType> VarT = classify(VD->getType());
@@ -4237,7 +4242,10 @@ bool ByteCodeExprGen<Emitter>::visitDeclRef(const ValueDecl *D, const Expr *E) {
if ((VD->hasGlobalStorage() || VD->isLocalVarDecl() ||
VD->isStaticDataMember()) &&
typeShouldBeVisited(VD->getType())) {
- if (!this->visitVarDecl(VD))
+ auto VarState = this->visitVarDecl(VD);
+ if (VarState.notCreated())
+ return true;
+ if (!VarState)
return false;
// Retry.
return this->visitDeclRef(VD, E);
@@ -4247,7 +4255,10 @@ bool ByteCodeExprGen<Emitter>::visitDeclRef(const ValueDecl *D, const Expr *E) {
if (const auto *VD = dyn_cast<VarDecl>(D);
VD && VD->getAnyInitializer() &&
VD->getType().isConstant(Ctx.getASTContext()) && !VD->isWeak()) {
- if (!this->visitVarDecl(VD))
+ auto VarState = this->visitVarDecl(VD);
+ if (VarState.notCreated())
+ return true;
+ if (!VarState)
return false;
// Retry.
return this->visitDeclRef(VD, E);
diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.h b/clang/lib/AST/Interp/ByteCodeExprGen.h
index eef8cae6e38cd..2921ffe49c45f 100644
--- a/clang/lib/AST/Interp/ByteCodeExprGen.h
+++ b/clang/lib/AST/Interp/ByteCodeExprGen.h
@@ -70,6 +70,18 @@ struct InitLink {
};
};
+/// State encapsulating if a the variable creation has been successful,
+/// unsuccessful, or no variable has been created at all.
+struct VarCreationState {
+ std::optional<bool> S = std::nullopt;
+ VarCreationState() = default;
+ VarCreationState(bool b) : S(b) {}
+ static VarCreationState NotCreated() { return VarCreationState(); }
+
+ operator bool() const { return S && *S; }
+ bool notCreated() const { return !S; }
+};
+
/// Compilation context for expressions.
template <class Emitter>
class ByteCodeExprGen : public ConstStmtVisitor<ByteCodeExprGen<Emitter>, bool>,
@@ -220,9 +232,8 @@ class ByteCodeExprGen : public ConstStmtVisitor<ByteCodeExprGen<Emitter>, bool>,
/// Just pass evaluation on to \p E. This leaves all the parsing flags
/// intact.
bool delegate(const Expr *E);
-
/// Creates and initializes a variable from the given decl.
- bool visitVarDecl(const VarDecl *VD);
+ VarCreationState visitVarDecl(const VarDecl *VD);
/// Visit an APValue.
bool visitAPValue(const APValue &Val, PrimType ValType, const Expr *E);
bool visitAPValueInitializer(const APValue &Val, const Expr *E);
diff --git a/clang/lib/AST/Interp/EvalEmitter.h b/clang/lib/AST/Interp/EvalEmitter.h
index 68accbc5214c2..e85f37d757b69 100644
--- a/clang/lib/AST/Interp/EvalEmitter.h
+++ b/clang/lib/AST/Interp/EvalEmitter.h
@@ -63,6 +63,10 @@ class EvalEmitter : public SourceMapper {
bool jump(const LabelTy &Label);
bool fallthrough(const LabelTy &Label);
+ /// Since expressions can only jump forward, predicated execution is
+ /// used to deal with if-else statements.
+ bool isActive() const { return CurrentLabel == ActiveLabel; }
+
/// Callback for registering a local.
Local createLocal(Descriptor *D);
@@ -117,10 +121,6 @@ class EvalEmitter : public SourceMapper {
/// Active block which should be executed.
LabelTy ActiveLabel = 0;
- /// Since expressions can only jump forward, predicated execution is
- /// used to deal with if-else statements.
- bool isActive() const { return CurrentLabel == ActiveLabel; }
-
protected:
#define GET_EVAL_PROTO
#include "Opcodes.inc"
diff --git a/clang/test/AST/Interp/c.c b/clang/test/AST/Interp/c.c
index 1cc450e48def0..684658da26a0e 100644
--- a/clang/test/AST/Interp/c.c
+++ b/clang/test/AST/Interp/c.c
@@ -288,3 +288,8 @@ char test10_global[test10_bound]; // all-error {{variable length array declarati
void test10(void) {
char test10_local[test10_bound] = "help"; // all-error {{variable-sized object may not be initialized}}
}
+
+void SuperSpecialFunc(void) {
+const int SuperSpecialCase = 10;
+_Static_assert((sizeof(SuperSpecialCase) == 12 && SuperSpecialCase == 3) || SuperSpecialCase == 10, ""); // pedantic-warning {{GNU extension}}
+}
More information about the cfe-commits
mailing list