[clang] [clang][bytecode] Use bytecode interpreter in isPotentialConstantExprU… (PR #149462)
Timm Baeder via cfe-commits
cfe-commits at lists.llvm.org
Fri Jul 18 07:17:24 PDT 2025
https://github.com/tbaederr updated https://github.com/llvm/llvm-project/pull/149462
>From 13e2505f6c51c00e3fcfe7174ea4dc75865e0c9b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Fri, 18 Jul 2025 08:24:49 +0200
Subject: [PATCH] [clang][bytecode] Use bytecode interprete in
isPotentialConstantExprUnevaluated
Fake a function call to the given function and evaluate the given
expression as if it was part of that function call.
---
clang/lib/AST/ByteCode/Compiler.cpp | 5 +++++
clang/lib/AST/ByteCode/Context.cpp | 13 +++++++++++++
clang/lib/AST/ByteCode/Context.h | 4 +++-
clang/lib/AST/ByteCode/EvalEmitter.cpp | 13 +++++++++++++
clang/lib/AST/ByteCode/EvalEmitter.h | 3 +++
clang/lib/AST/ByteCode/Interp.cpp | 9 ++++++++-
clang/lib/AST/ByteCode/Interp.h | 2 +-
clang/lib/AST/ExprConstant.cpp | 5 +++++
clang/test/AST/ByteCode/builtin-constant-p.cpp | 8 ++++++++
clang/test/Sema/diagnose_if.c | 1 +
clang/test/SemaCXX/diagnose_if-ext.cpp | 1 +
clang/test/SemaCXX/diagnose_if.cpp | 1 +
12 files changed, 62 insertions(+), 3 deletions(-)
diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp
index ea473730350b6..65ad7caf8913b 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -6670,6 +6670,11 @@ bool Compiler<Emitter>::visitDeclRef(const ValueDecl *D, const Expr *E) {
}
// Function parameters.
if (const auto *PVD = dyn_cast<ParmVarDecl>(D)) {
+ if (Ctx.getLangOpts().CPlusPlus && !Ctx.getLangOpts().CPlusPlus11 &&
+ !D->getType()->isIntegralOrEnumerationType()) {
+ return this->emitInvalidDeclRef(cast<DeclRefExpr>(E),
+ /*InitializerFailed=*/false, E);
+ }
if (auto It = this->Params.find(PVD); It != this->Params.end()) {
if (IsReference || !It->second.IsPtr)
return this->emitGetParam(classifyPrim(E), It->second.Offset, E);
diff --git a/clang/lib/AST/ByteCode/Context.cpp b/clang/lib/AST/ByteCode/Context.cpp
index a629ff9569428..ead6e4af5d403 100644
--- a/clang/lib/AST/ByteCode/Context.cpp
+++ b/clang/lib/AST/ByteCode/Context.cpp
@@ -52,6 +52,19 @@ bool Context::isPotentialConstantExpr(State &Parent, const FunctionDecl *FD) {
return Func->isValid();
}
+void Context::isPotentialConstantExprUnevaluated(State &Parent, const Expr *E,
+ const FunctionDecl *FD) {
+ assert(Stk.empty());
+ ++EvalID;
+ size_t StackSizeBefore = Stk.size();
+ Compiler<EvalEmitter> C(*this, *P, Parent, Stk);
+
+ if (!C.interpretCall(FD, E)) {
+ C.cleanup();
+ Stk.clearTo(StackSizeBefore);
+ }
+}
+
bool Context::evaluateAsRValue(State &Parent, const Expr *E, APValue &Result) {
++EvalID;
bool Recursing = !Stk.empty();
diff --git a/clang/lib/AST/ByteCode/Context.h b/clang/lib/AST/ByteCode/Context.h
index 5898ab5e54599..acf750421f8af 100644
--- a/clang/lib/AST/ByteCode/Context.h
+++ b/clang/lib/AST/ByteCode/Context.h
@@ -47,7 +47,9 @@ class Context final {
~Context();
/// Checks if a function is a potential constant expression.
- bool isPotentialConstantExpr(State &Parent, const FunctionDecl *FnDecl);
+ bool isPotentialConstantExpr(State &Parent, const FunctionDecl *FD);
+ void isPotentialConstantExprUnevaluated(State &Parent, const Expr *E,
+ const FunctionDecl *FD);
/// Evaluates a toplevel expression as an rvalue.
bool evaluateAsRValue(State &Parent, const Expr *E, APValue &Result);
diff --git a/clang/lib/AST/ByteCode/EvalEmitter.cpp b/clang/lib/AST/ByteCode/EvalEmitter.cpp
index 5498065657e0a..6e511bc7d2fab 100644
--- a/clang/lib/AST/ByteCode/EvalEmitter.cpp
+++ b/clang/lib/AST/ByteCode/EvalEmitter.cpp
@@ -90,6 +90,19 @@ EvaluationResult EvalEmitter::interpretAsPointer(const Expr *E,
return std::move(this->EvalResult);
}
+bool EvalEmitter::interpretCall(const FunctionDecl *FD, const Expr *E) {
+ // Add parameters to the parameter map. The values in the ParamOffset don't
+ // matter in this case as reading from them can't ever work.
+ for (const ParmVarDecl *PD : FD->parameters()) {
+ this->Params.insert({PD, {0, false}});
+ }
+
+ if (!this->visit(E))
+ return false;
+ PrimType T = Ctx.classify(E).value_or(PT_Ptr);
+ return this->emitPop(T, E);
+}
+
void EvalEmitter::emitLabel(LabelTy Label) { CurrentLabel = Label; }
EvalEmitter::LabelTy EvalEmitter::getLabel() { return NextLabel++; }
diff --git a/clang/lib/AST/ByteCode/EvalEmitter.h b/clang/lib/AST/ByteCode/EvalEmitter.h
index 7303adba22af7..2fe7da608c739 100644
--- a/clang/lib/AST/ByteCode/EvalEmitter.h
+++ b/clang/lib/AST/ByteCode/EvalEmitter.h
@@ -40,6 +40,9 @@ class EvalEmitter : public SourceMapper {
EvaluationResult interpretDecl(const VarDecl *VD, bool CheckFullyInitialized);
/// Interpret the given Expr to a Pointer.
EvaluationResult interpretAsPointer(const Expr *E, PtrCallback PtrCB);
+ /// Interpret the given expression as if it was in the body of the given
+ /// function, i.e. the parameters of the function are available for use.
+ bool interpretCall(const FunctionDecl *FD, const Expr *E);
/// Clean up all resources.
void cleanup();
diff --git a/clang/lib/AST/ByteCode/Interp.cpp b/clang/lib/AST/ByteCode/Interp.cpp
index e8b519478c026..215ccd3cf38d4 100644
--- a/clang/lib/AST/ByteCode/Interp.cpp
+++ b/clang/lib/AST/ByteCode/Interp.cpp
@@ -142,8 +142,12 @@ static bool diagnoseUnknownDecl(InterpState &S, CodePtr OpPC,
return false;
if (isa<ParmVarDecl>(D)) {
- if (D->getType()->isReferenceType())
+ if (D->getType()->isReferenceType()) {
+ if (S.inConstantContext() && S.getLangOpts().CPlusPlus &&
+ !S.getLangOpts().CPlusPlus11)
+ diagnoseNonConstVariable(S, OpPC, D);
return false;
+ }
const SourceInfo &Loc = S.Current->getSource(OpPC);
if (S.getLangOpts().CPlusPlus11) {
@@ -658,6 +662,9 @@ bool CheckInitialized(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
if (Ptr.isInitialized())
return true;
+ if (Ptr.isExtern() && S.checkingPotentialConstantExpression())
+ return false;
+
if (const auto *VD = Ptr.getDeclDesc()->asVarDecl();
VD && (VD->isConstexpr() || VD->hasGlobalStorage())) {
diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h
index ce0ebdd8321b7..aac519d7c74fd 100644
--- a/clang/lib/AST/ByteCode/Interp.h
+++ b/clang/lib/AST/ByteCode/Interp.h
@@ -1308,7 +1308,7 @@ bool Dup(InterpState &S, CodePtr OpPC) {
template <PrimType Name, class T = typename PrimConv<Name>::T>
bool Pop(InterpState &S, CodePtr OpPC) {
- S.Stk.pop<T>();
+ S.Stk.discard<T>();
return true;
}
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 8797eaddd0e18..cfc4729be4184 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -18018,6 +18018,11 @@ bool Expr::isPotentialConstantExprUnevaluated(Expr *E,
Info.InConstantContext = true;
Info.CheckingPotentialConstantExpression = true;
+ if (Info.EnableNewConstInterp) {
+ Info.Ctx.getInterpContext().isPotentialConstantExprUnevaluated(Info, E, FD);
+ return Diags.empty();
+ }
+
// Fabricate a call stack frame to give the arguments a plausible cover story.
CallStackFrame Frame(Info, SourceLocation(), FD, /*This=*/nullptr,
/*CallExpr=*/nullptr, CallRef());
diff --git a/clang/test/AST/ByteCode/builtin-constant-p.cpp b/clang/test/AST/ByteCode/builtin-constant-p.cpp
index 9f5521590833d..315a907949c34 100644
--- a/clang/test/AST/ByteCode/builtin-constant-p.cpp
+++ b/clang/test/AST/ByteCode/builtin-constant-p.cpp
@@ -140,3 +140,11 @@ void test17(void) {
F("string literal" + 1); // both-warning {{adding}} \
// both-note {{use array indexing}}
}
+
+/// FIXME
+static void foo(int i) __attribute__((__diagnose_if__(!__builtin_constant_p(i), "not constant", "error"))) // expected-note {{from}}
+{
+}
+static void bar(int i) {
+ foo(15); // expected-error {{not constant}}
+}
diff --git a/clang/test/Sema/diagnose_if.c b/clang/test/Sema/diagnose_if.c
index e9b8497d5ca4e..a4cf43e9c869f 100644
--- a/clang/test/Sema/diagnose_if.c
+++ b/clang/test/Sema/diagnose_if.c
@@ -1,4 +1,5 @@
// RUN: %clang_cc1 %s -verify -fno-builtin
+// RUN: %clang_cc1 %s -verify -fno-builtin -fexperimental-new-constant-interpreter
#define _diagnose_if(...) __attribute__((diagnose_if(__VA_ARGS__)))
diff --git a/clang/test/SemaCXX/diagnose_if-ext.cpp b/clang/test/SemaCXX/diagnose_if-ext.cpp
index d5625b501322e..e0f73976eea3a 100644
--- a/clang/test/SemaCXX/diagnose_if-ext.cpp
+++ b/clang/test/SemaCXX/diagnose_if-ext.cpp
@@ -1,4 +1,5 @@
// RUN: %clang_cc1 -Wpedantic -fsyntax-only %s -verify
+// RUN: %clang_cc1 -Wpedantic -fsyntax-only %s -verify -fexperimental-new-constant-interpreter
void foo() __attribute__((diagnose_if(1, "", "error"))); // expected-warning{{'diagnose_if' is a clang extension}}
void foo(int a) __attribute__((diagnose_if(a, "", "error"))); // expected-warning{{'diagnose_if' is a clang extension}}
diff --git a/clang/test/SemaCXX/diagnose_if.cpp b/clang/test/SemaCXX/diagnose_if.cpp
index 21897c5184b73..1b9e660c4e224 100644
--- a/clang/test/SemaCXX/diagnose_if.cpp
+++ b/clang/test/SemaCXX/diagnose_if.cpp
@@ -1,4 +1,5 @@
// RUN: %clang_cc1 %s -verify -fno-builtin -std=c++14
+// RUN: %clang_cc1 %s -verify -fno-builtin -std=c++14 -fexperimental-new-constant-interpreter
#define _diagnose_if(...) __attribute__((diagnose_if(__VA_ARGS__)))
More information about the cfe-commits
mailing list