https://github.com/awson updated 
https://github.com/llvm/llvm-project/pull/172995

>From 63835fcf92e997506fdfccaac38bb56dac9e147f Mon Sep 17 00:00:00 2001
From: awson <[email protected]>
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  |  3 +-
 clang/test/SemaCXX/GH112189.cpp | 41 ++++++++++++++++++
 4 files changed, 123 insertions(+), 1 deletion(-)
 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..b405dc05ca257 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -4619,7 +4619,8 @@ bool TreeTransform<Derived>::TransformExprs(Expr *const 
*Inputs,
 
     ExprResult Result =
       IsCall ? getDerived().TransformInitializer(Inputs[I], 
/*DirectInit*/false)
-             : getDerived().TransformExpr(Inputs[I]);
+             : 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>();
+}

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to