[clang] [clang][bytecode] Use in `Expr::tryEvaluateString` (PR #160118)
Timm Baeder via cfe-commits
cfe-commits at lists.llvm.org
Wed Sep 24 00:19:22 PDT 2025
Timm =?utf-8?q?Bäder?= <tbaeder at redhat.com>
Message-ID:
In-Reply-To: <llvm.org/llvm/llvm-project/pull/160118 at github.com>
https://github.com/tbaederr updated https://github.com/llvm/llvm-project/pull/160118
>From 310ba0ec9d7c980d3023fcdc3e718c9155eadf13 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Tue, 16 Sep 2025 16:02:15 +0200
Subject: [PATCH 1/2] try evaluate string
---
clang/lib/AST/ByteCode/Context.cpp | 42 ++++++++++++++++++++++++++++++
clang/lib/AST/ByteCode/Context.h | 4 +++
clang/lib/AST/ExprConstant.cpp | 8 +++++-
3 files changed, 53 insertions(+), 1 deletion(-)
diff --git a/clang/lib/AST/ByteCode/Context.cpp b/clang/lib/AST/ByteCode/Context.cpp
index 71d0bcf61a5ff..43a4e5888506a 100644
--- a/clang/lib/AST/ByteCode/Context.cpp
+++ b/clang/lib/AST/ByteCode/Context.cpp
@@ -237,6 +237,48 @@ bool Context::evaluateCharRange(State &Parent, const Expr *SizeExpr,
return evaluateStringRepr(Parent, SizeExpr, PtrExpr, Result);
}
+bool Context::evaluateString(State &Parent, const Expr *E,
+ std::string &Result) {
+ assert(Stk.empty());
+ Compiler<EvalEmitter> C(*this, *P, Parent, Stk);
+
+ auto PtrRes = C.interpretAsPointer(E, [&](const Pointer &Ptr) {
+ const Descriptor *FieldDesc = Ptr.getFieldDesc();
+ if (!FieldDesc->isPrimitiveArray())
+ return false;
+
+ if (!Ptr.isConst())
+ return false;
+
+ unsigned N = Ptr.getNumElems();
+ if (Ptr.elemSize() == 1 /* bytes */) {
+ const char *Chars = reinterpret_cast<const char *>(Ptr.getRawAddress());
+ unsigned Length = strnlen(Chars, N);
+ Result.assign(Chars, Length);
+ return true;
+ }
+
+ PrimType ElemT = FieldDesc->getPrimType();
+ for (unsigned I = Ptr.getIndex(); I != N; ++I) {
+ INT_TYPE_SWITCH(ElemT, {
+ auto Elem = Ptr.elem<T>(I);
+ if (Elem.isZero())
+ return true;
+ Result.push_back(static_cast<char>(Elem));
+ });
+ }
+ // We didn't find a 0 byte.
+ return false;
+ });
+
+ if (PtrRes.isInvalid()) {
+ C.cleanup();
+ Stk.clear();
+ return false;
+ }
+ return true;
+}
+
bool Context::evaluateStrlen(State &Parent, const Expr *E, uint64_t &Result) {
assert(Stk.empty());
Compiler<EvalEmitter> C(*this, *P, Parent, Stk);
diff --git a/clang/lib/AST/ByteCode/Context.h b/clang/lib/AST/ByteCode/Context.h
index 280a31725555f..fadedf83e68ff 100644
--- a/clang/lib/AST/ByteCode/Context.h
+++ b/clang/lib/AST/ByteCode/Context.h
@@ -67,6 +67,10 @@ class Context final {
bool evaluateCharRange(State &Parent, const Expr *SizeExpr,
const Expr *PtrExpr, std::string &Result);
+ /// Evaluate \param E and if it can be evaluated to a sirint literal,
+ /// copy the result into \param Result.
+ bool evaluateString(State &Parent, const Expr *E, std::string &Result);
+
/// Evalute \param E and if it can be evaluated to a string literal,
/// run strlen() on it.
bool evaluateStrlen(State &Parent, const Expr *E, uint64_t &Result);
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index d10e2afeb2341..2f7f290e5715c 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -18843,9 +18843,15 @@ std::optional<std::string> Expr::tryEvaluateString(ASTContext &Ctx) const {
uint64_t Result;
std::string StringResult;
+ if (Info.EnableNewConstInterp) {
+ if (!Info.Ctx.getInterpContext().evaluateString(Info, this, StringResult))
+ return std::nullopt;
+ return StringResult;
+ }
+
if (EvaluateBuiltinStrLen(this, Result, Info, &StringResult))
return StringResult;
- return {};
+ return std::nullopt;
}
template <typename T>
>From 9766cad498666e53196abf60444d06603f4653d6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Tue, 23 Sep 2025 09:37:46 +0200
Subject: [PATCH 2/2] Reject and test non-null terminated strings
---
clang/lib/AST/ByteCode/Context.cpp | 4 ++++
clang/lib/AST/ByteCode/Context.h | 2 +-
clang/test/SemaCXX/verbose-trap.cpp | 14 ++++++++++++++
3 files changed, 19 insertions(+), 1 deletion(-)
diff --git a/clang/lib/AST/ByteCode/Context.cpp b/clang/lib/AST/ByteCode/Context.cpp
index 43a4e5888506a..306f95c479d0f 100644
--- a/clang/lib/AST/ByteCode/Context.cpp
+++ b/clang/lib/AST/ByteCode/Context.cpp
@@ -251,9 +251,13 @@ bool Context::evaluateString(State &Parent, const Expr *E,
return false;
unsigned N = Ptr.getNumElems();
+
if (Ptr.elemSize() == 1 /* bytes */) {
const char *Chars = reinterpret_cast<const char *>(Ptr.getRawAddress());
unsigned Length = strnlen(Chars, N);
+ // Wasn't null terminated.
+ if (N == Length)
+ return false;
Result.assign(Chars, Length);
return true;
}
diff --git a/clang/lib/AST/ByteCode/Context.h b/clang/lib/AST/ByteCode/Context.h
index fadedf83e68ff..f5fa977cbcad8 100644
--- a/clang/lib/AST/ByteCode/Context.h
+++ b/clang/lib/AST/ByteCode/Context.h
@@ -67,7 +67,7 @@ class Context final {
bool evaluateCharRange(State &Parent, const Expr *SizeExpr,
const Expr *PtrExpr, std::string &Result);
- /// Evaluate \param E and if it can be evaluated to a sirint literal,
+ /// Evaluate \param E and if it can be evaluated to a null-terminated string,
/// copy the result into \param Result.
bool evaluateString(State &Parent, const Expr *E, std::string &Result);
diff --git a/clang/test/SemaCXX/verbose-trap.cpp b/clang/test/SemaCXX/verbose-trap.cpp
index 2503f9860d9c3..0dd090e57d016 100644
--- a/clang/test/SemaCXX/verbose-trap.cpp
+++ b/clang/test/SemaCXX/verbose-trap.cpp
@@ -1,6 +1,9 @@
// RUN: %clang_cc1 -std=c++11 -fsyntax-only -fcxx-exceptions -verify %s
// RUN: %clang_cc1 -std=c++20 -fsyntax-only -fcxx-exceptions -verify %s
+// RUN: %clang_cc1 -std=c++11 -fsyntax-only -fcxx-exceptions -verify %s -fexperimental-new-constant-interpreter
+// RUN: %clang_cc1 -std=c++20 -fsyntax-only -fcxx-exceptions -verify %s -fexperimental-new-constant-interpreter
+
#if !__has_builtin(__builtin_verbose_trap)
#error
#endif
@@ -45,3 +48,14 @@ void f2() {
void test() {
f<constCat3, constMsg3>(nullptr);
}
+
+/// Arguments must be null terminated.
+int foo() {
+ constexpr char K[] = {'a', 'b'};
+ __builtin_verbose_trap("hehe", K); // expected-error {{argument to __builtin_verbose_trap must be a pointer to a constant string}}
+ __builtin_verbose_trap(K, "hehe"); //expected-error {{argument to __builtin_verbose_trap must be a pointer to a constant string}}
+
+ constexpr char K2[] = {'a', 'b', '\0'};
+ __builtin_verbose_trap("hehe", K2);
+ __builtin_verbose_trap(K2, "hehe");
+}
More information about the cfe-commits
mailing list