Skip to content

Conversation

@tbaederr
Copy link
Contributor

Fixes #157492

@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" clang:bytecode Issues for the clang bytecode constexpr interpreter labels Sep 22, 2025
@llvmbot
Copy link
Member

llvmbot commented Sep 22, 2025

@llvm/pr-subscribers-clang

Author: Timm Baeder (tbaederr)

Changes

Fixes #157492


Full diff: https://github.com/llvm/llvm-project/pull/160118.diff

3 Files Affected:

  • (modified) clang/lib/AST/ByteCode/Context.cpp (+42)
  • (modified) clang/lib/AST/ByteCode/Context.h (+4)
  • (modified) clang/lib/AST/ExprConstant.cpp (+7-1)
diff --git a/clang/lib/AST/ByteCode/Context.cpp b/clang/lib/AST/ByteCode/Context.cpp index cfda6e8ded760..fffdafe4b2e2f 100644 --- a/clang/lib/AST/ByteCode/Context.cpp +++ b/clang/lib/AST/ByteCode/Context.cpp @@ -236,6 +236,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 3b9ca82910033..e569263c9ab3c 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -18770,9 +18770,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> 
@tbaederr
Copy link
Contributor Author

@cor3ntin Are the additional test changes ok?

Copy link
Contributor

@cor3ntin cor3ntin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@@ -45,3 +48,10 @@ void f2() {
void test() {
f<constCat3, constMsg3>(nullptr);
}

/// Arguments must be null terminated.
constexpr char K[] = {'a', 'b'};
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we try a case where the array is null terminated. How about a case where the array has multiple null terminators etc

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a null-terminated array test case, but I can't check that we really have the right string here.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that is ok, we are just making we test both the happy and unhappy path.

@tbaederr tbaederr merged commit f9305c9 into llvm:main Sep 25, 2025
9 checks passed
mahesh-attarde pushed a commit to mahesh-attarde/llvm-project that referenced this pull request Oct 3, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

clang:bytecode Issues for the clang bytecode constexpr interpreter clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category

4 participants