[clang] Fix incorrect array initialization with string literal in dependent settings (fixes #112189) (PR #172995)
via cfe-commits
cfe-commits at lists.llvm.org
Fri Dec 19 23:08:37 PST 2025
https://github.com/awson updated https://github.com/llvm/llvm-project/pull/172995
>From f32910e6349b89b81f501f0d4dda1d8e7cb5a280 Mon Sep 17 00:00:00 2001
From: awson <kyrab at mail.ru>
Date: Fri, 19 Dec 2025 07:43:21 +0300
Subject: [PATCH] If we ever want to transform (possibly parenthesized)
`StringLiteral` expression when transforming list initializers, then clone it
beforehand.
(add string literal cloner to `Expr` class for this)
---
clang/include/clang/AST/Expr.h | 4 ++
clang/lib/AST/Expr.cpp | 76 +++++++++++++++++++++++++++++++++
clang/lib/Sema/TreeTransform.h | 6 ++-
clang/test/SemaCXX/GH112189.cpp | 41 ++++++++++++++++++
4 files changed, 125 insertions(+), 2 deletions(-)
create mode 100644 clang/test/SemaCXX/GH112189.cpp
diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h
index 3e30a8b420f19..4e219d50111fe 100644
--- a/clang/include/clang/AST/Expr.h
+++ b/clang/include/clang/AST/Expr.h
@@ -1023,6 +1023,10 @@ class Expr : public ValueStmt {
return skipRValueSubobjectAdjustments(CommaLHSs, Adjustments);
}
+ /// If the expression is a (possibly parenthesized) string literal
+ /// then make a copy of it.
+ Expr *CloneIfIAmAStringLiteral(ASTContext &Ctx);
+
/// Checks that the two Expr's will refer to the same value as a comparison
/// operand. The caller must ensure that the values referenced by the Expr's
/// are not modified between E1 and E2 or the result my be invalid.
diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp
index 616db5df23c5f..723b39fa45fa9 100644
--- a/clang/lib/AST/Expr.cpp
+++ b/clang/lib/AST/Expr.cpp
@@ -133,6 +133,82 @@ const Expr *Expr::skipRValueSubobjectAdjustments(
return E;
}
+static StringLiteral *CloneStringLiteral(const StringLiteral *SL,
+ ASTContext &C) {
+ SourceLocation *SLocs = new (C) SourceLocation[SL->getNumConcatenated()];
+ std::copy(SL->tokloc_begin(), SL->tokloc_end(), SLocs);
+ return StringLiteral::Create(
+ C, SL->getBytes(), SL->getKind(), SL->isPascal(), SL->getType(),
+ ArrayRef<SourceLocation>(SLocs, SL->getNumConcatenated()));
+}
+
+// Exactly follow `IgnoreParensSingleStep` (`AST/IgnoreExpr.h`)
+// We only recursively visit those subexpressions which `IgnoreParensSingleStep`
+// drills down to.
+Expr *Expr::CloneIfIAmAStringLiteral(ASTContext &C) {
+ if (auto *SL = dyn_cast<StringLiteral>(this)) {
+ return CloneStringLiteral(SL, C);
+ }
+
+ if (auto *PE = dyn_cast<ParenExpr>(this)) {
+ return new (C) ParenExpr(PE->getBeginLoc(), PE->getEndLoc(),
+ PE->getSubExpr()->CloneIfIAmAStringLiteral(C));
+ }
+
+ if (auto *UO = dyn_cast<UnaryOperator>(this)) {
+ if (UO->getOpcode() == UO_Extension) {
+ return UnaryOperator::Create(
+ C, UO->getSubExpr()->CloneIfIAmAStringLiteral(C), UO_Extension,
+ UO->getType(), UO->getValueKind(), UO->getObjectKind(),
+ UO->getBeginLoc(), UO->canOverflow(), UO->getFPOptionsOverride());
+ }
+ }
+
+ else if (auto *GSE = dyn_cast<GenericSelectionExpr>(this)) {
+ if (!GSE->isResultDependent()) {
+ ArrayRef<Expr *> GSEAEs = GSE->getAssocExprs();
+ Expr **NewGSEAEs = new (C) Expr *[GSEAEs.size()];
+ std::copy(GSEAEs.begin(), GSEAEs.end(), NewGSEAEs);
+ NewGSEAEs[GSE->getResultIndex()] =
+ GSE->getResultExpr()->CloneIfIAmAStringLiteral(C);
+
+ auto GSECreate = [&](auto *ExprOrTSI) -> Expr * {
+ return GenericSelectionExpr::Create(
+ C, GSE->getGenericLoc(), ExprOrTSI, GSE->getAssocTypeSourceInfos(),
+ ArrayRef<Expr *>(NewGSEAEs, GSEAEs.size()), GSE->getDefaultLoc(),
+ GSE->getRParenLoc(), GSE->containsUnexpandedParameterPack(),
+ GSE->getResultIndex());
+ };
+
+ return GSE->isExprPredicate() ? GSECreate(GSE->getControllingExpr())
+ : GSECreate(GSE->getControllingType());
+ }
+ }
+
+ else if (auto *CE = dyn_cast<ChooseExpr>(this)) {
+ if (!CE->isConditionDependent()) {
+ // Drills to `CE->getChosenSubExpr()`
+ const bool isCondTrue = CE->isConditionTrue();
+ return new (C) ChooseExpr(
+ CE->getBeginLoc(), CE->getCond(),
+ isCondTrue ? CE->getLHS()->CloneIfIAmAStringLiteral(C) : CE->getLHS(),
+ isCondTrue ? CE->getRHS() : CE->getRHS()->CloneIfIAmAStringLiteral(C),
+ CE->getType(), CE->getValueKind(), CE->getObjectKind(),
+ CE->getRParenLoc(), CE->isConditionTrue());
+ }
+ }
+
+ else if (auto *PE = dyn_cast<PredefinedExpr>(this)) {
+ if (PE->isTransparent() && PE->getFunctionName()) {
+ return PredefinedExpr::Create(
+ C, PE->getLocation(), PE->getType(), PE->getIdentKind(),
+ PE->isTransparent(), CloneStringLiteral(PE->getFunctionName(), C));
+ }
+ }
+
+ return this;
+}
+
bool Expr::isKnownToHaveBooleanValue(bool Semantic) const {
const Expr *E = IgnoreParens();
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index bc923c80b7132..64ce720cc31a3 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -4618,8 +4618,10 @@ bool TreeTransform<Derived>::TransformExprs(Expr *const *Inputs,
}
ExprResult Result =
- IsCall ? getDerived().TransformInitializer(Inputs[I], /*DirectInit*/false)
- : getDerived().TransformExpr(Inputs[I]);
+ IsCall
+ ? getDerived().TransformInitializer(Inputs[I], /*DirectInit*/ false)
+ : getDerived().TransformExpr(Inputs[I]->CloneIfIAmAStringLiteral(
+ getSema().getASTContext()));
if (Result.isInvalid())
return true;
diff --git a/clang/test/SemaCXX/GH112189.cpp b/clang/test/SemaCXX/GH112189.cpp
new file mode 100644
index 0000000000000..036fd8ea83c0e
--- /dev/null
+++ b/clang/test/SemaCXX/GH112189.cpp
@@ -0,0 +1,41 @@
+// RUN: %clang_cc1 -verify %s
+// expected-no-diagnostics
+
+template<unsigned int SPACE>
+char foo_choose() {
+ char buffer[SPACE] {__builtin_choose_expr(6, "foo", "boo")};
+ return buffer[0];
+}
+
+int boro_choose()
+{
+ int r = foo_choose<10>();
+ r += foo_choose<100>();
+ return r + foo_choose<4>();
+}
+
+template<unsigned int SPACE>
+char foo_gen_ext() {
+ char buffer[SPACE] {__extension__ (_Generic(0, int: (__extension__ "foo" )))};
+ return buffer[0];
+}
+
+int boro_gen_ext()
+{
+ int r = foo_gen_ext<10>();
+ r += foo_gen_ext<100>();
+ return r + foo_gen_ext<4>();
+}
+
+template<unsigned int SPACE>
+char foo_paren_predef() {
+ char buffer[SPACE] {(((__FILE__)))};
+ return buffer[0];
+}
+
+int boro_paren_predef()
+{
+ int r = foo_paren_predef<200000>();
+ r += foo_paren_predef<300000>();
+ return r + foo_paren_predef<100000>();
+}
More information about the cfe-commits
mailing list