[clang] [clang][constexpr] Evaluate parenthesized array new with the allocated bound (PR #228675)
via cfe-commits
cfe-commits at lists.llvm.org
Sat Oct 3 00:41:14 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Macro Terra (hongtaihu)
<details>
<summary>Changes</summary>
When evaluating an array new-expression, the actual allocation bound can differ from the bound recorded in the initializer's type. This occurs when the array bound is a function parameter whose value becomes known during constant evaluation.
The branch handling this difference assumes that the initializer is an `InitListExpr`. However, parenthesized array initialization produces a `CXXParenListInitExpr`, causing the cast to assert.
For example, this valid C++20 program triggers the assertion:
```cpp
constexpr bool f(unsigned n) {
int *p = new int[n](5);
delete[] p;
return true;
}
static_assert(f(3));
```
Preserve the initializer as an `Expr` and dispatch according to its actual node type. For parenthesized initialization, reuse the existing array evaluation logic with the actual allocation type. This also ensures that the remaining elements are initialized using the correct array bound.
Add regression coverage for:
- Parenthesized integer array initialization with different bounds.
- Class array initialization with default construction of remaining elements.
- Existing braced string initialization.
Validation on the existing local assertions build, before rebasing onto current main: the regression passes in C++20 and C++23 modes. The original malformed input produces normal diagnostics without crashing in both syntax-only and assembly compilation modes.
Fixes #<!-- -->204757
Assisted by Codex
---
Full diff: https://github.com/llvm/llvm-project/pull/228675.diff
2 Files Affected:
- (modified) clang/lib/AST/ExprConstant.cpp (+21-17)
- (added) clang/test/SemaCXX/constexpr-array-new-paren-init.cpp (+39)
``````````diff
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 3f295f35d1361..30116acfe5716 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -10963,9 +10963,9 @@ bool PointerExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
}
}
-static bool EvaluateArrayNewInitList(EvalInfo &Info, LValue &This,
- APValue &Result, const InitListExpr *ILE,
- QualType AllocType);
+static bool EvaluateArrayNewInit(EvalInfo &Info, LValue &This,
+ APValue &Result, const Expr *Init,
+ QualType AllocType);
static bool EvaluateArrayNewConstructExpr(EvalInfo &Info, LValue &This,
APValue &Result,
const CXXConstructExpr *CCE,
@@ -11035,7 +11035,7 @@ bool PointerExprEvaluator::VisitCXXNewExpr(const CXXNewExpr *E) {
}
const Expr *Init = E->getInitializer();
- const InitListExpr *ResizedArrayILE = nullptr;
+ const Expr *ResizedArrayInit = nullptr;
const CXXConstructExpr *ResizedArrayCCE = nullptr;
bool ValueInit = false;
@@ -11104,10 +11104,11 @@ bool PointerExprEvaluator::VisitCXXNewExpr(const CXXNewExpr *E) {
return false;
}
- // If the sizes differ, we must have an initializer list, and we need
- // special handling for this case when we initialize.
+ // Array initialization can use either braces or parentheses. If the
+ // sizes differ, evaluate it with the allocated bound rather than the
+ // bound used when checking the initializer.
if (InitBound != AllocBound)
- ResizedArrayILE = cast<InitListExpr>(Init);
+ ResizedArrayInit = Init;
}
AllocType = Info.Ctx.getConstantArrayType(AllocType, ArrayBound, nullptr,
@@ -11205,9 +11206,8 @@ bool PointerExprEvaluator::VisitCXXNewExpr(const CXXNewExpr *E) {
ImplicitValueInitExpr VIE(AllocType);
if (!EvaluateInPlace(*Val, Info, Result, &VIE))
return false;
- } else if (ResizedArrayILE) {
- if (!EvaluateArrayNewInitList(Info, Result, *Val, ResizedArrayILE,
- AllocType))
+ } else if (ResizedArrayInit) {
+ if (!EvaluateArrayNewInit(Info, Result, *Val, ResizedArrayInit, AllocType))
return false;
} else if (ResizedArrayCCE) {
if (!EvaluateArrayNewConstructExpr(Info, Result, *Val, ResizedArrayCCE,
@@ -15636,14 +15636,18 @@ static bool EvaluateArray(const Expr *E, const LValue &This,
return ArrayExprEvaluator(Info, This, Result).Visit(E);
}
-static bool EvaluateArrayNewInitList(EvalInfo &Info, LValue &This,
- APValue &Result, const InitListExpr *ILE,
- QualType AllocType) {
- assert(!ILE->isValueDependent());
- assert(ILE->isPRValue() && ILE->getType()->isArrayType() &&
+static bool EvaluateArrayNewInit(EvalInfo &Info, LValue &This,
+ APValue &Result, const Expr *Init,
+ QualType AllocType) {
+ assert(!Init->isValueDependent());
+ assert(Init->isPRValue() && Init->getType()->isArrayType() &&
"not an array prvalue");
- return ArrayExprEvaluator(Info, This, Result)
- .VisitInitListExpr(ILE, AllocType);
+ ArrayExprEvaluator Evaluator(Info, This, Result);
+ if (const auto *ILE = dyn_cast<InitListExpr>(Init))
+ return Evaluator.VisitInitListExpr(ILE, AllocType);
+ const auto *PLIE = cast<CXXParenListInitExpr>(Init);
+ return Evaluator.VisitCXXParenListOrInitListExpr(
+ PLIE, PLIE->getInitExprs(), PLIE->getArrayFiller(), AllocType);
}
static bool EvaluateArrayNewConstructExpr(EvalInfo &Info, LValue &This,
diff --git a/clang/test/SemaCXX/constexpr-array-new-paren-init.cpp b/clang/test/SemaCXX/constexpr-array-new-paren-init.cpp
new file mode 100644
index 0000000000000..4f0afa153ff8e
--- /dev/null
+++ b/clang/test/SemaCXX/constexpr-array-new-paren-init.cpp
@@ -0,0 +1,39 @@
+// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -std=c++23 -fsyntax-only -verify %s
+// expected-no-diagnostics
+
+constexpr bool integers(unsigned n) {
+ int *p = new int[n](5);
+ bool result = p[0] == 5;
+ for (unsigned i = 1; i != n; ++i)
+ result &= p[i] == 0;
+ delete[] p;
+ return result;
+}
+static_assert(integers(1));
+static_assert(integers(3));
+static_assert(integers(5));
+
+struct Element {
+ int value;
+ constexpr Element(int value = 7) : value(value) {}
+};
+constexpr bool objects(unsigned n) {
+ Element *p = new Element[n](Element(1), Element(2));
+ bool result = p[0].value == 1 && p[1].value == 2;
+ for (unsigned i = 2; i != n; ++i)
+ result &= p[i].value == 7;
+ delete[] p;
+ return result;
+}
+static_assert(objects(2));
+static_assert(objects(4));
+
+constexpr bool string(unsigned n) {
+ char *p = new char[n]{"abc"};
+ bool result = p[0] == 'a' && p[3] == 0 && p[n - 1] == 0;
+ delete[] p;
+ return result;
+}
+static_assert(string(4));
+static_assert(string(8));
``````````
</details>
https://github.com/llvm/llvm-project/pull/228675
More information about the cfe-commits
mailing list