https://github.com/awson updated https://github.com/llvm/llvm-project/pull/172995
>From f32910e6349b89b81f501f0d4dda1d8e7cb5a280 Mon Sep 17 00:00:00 2001 From: awson <[email protected]> Date: Fri, 19 Dec 2025 07:43:21 +0300 Subject: [PATCH 1/2] 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>(); +} >From 9e703320a4a78447057c16d1823eceb93161eec2 Mon Sep 17 00:00:00 2001 From: awson <[email protected]> Date: Sun, 21 Dec 2025 15:55:26 +0300 Subject: [PATCH 2/2] Implement @ojhunt suggestion to use SmallVector since the short-lived allocation is perfectly fine. --- clang/lib/AST/Expr.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index 723b39fa45fa9..0ac7f683a06e7 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -167,15 +167,14 @@ Expr *Expr::CloneIfIAmAStringLiteral(ASTContext &C) { 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); + SmallVector<Expr *> NewGSEAEs(GSEAEs); 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(), + ArrayRef<Expr *>(NewGSEAEs), GSE->getDefaultLoc(), GSE->getRParenLoc(), GSE->containsUnexpandedParameterPack(), GSE->getResultIndex()); }; _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
