[clang] [clang][constexpr] Evaluate parenthesized array new with the allocated bound (PR #228675)

Macro Terra via cfe-commits cfe-commits at lists.llvm.org
Sat Oct 3 00:40:40 PDT 2026


https://github.com/hongtaihu created https://github.com/llvm/llvm-project/pull/228675

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


>From 7a3f90b3e00a2151b0402fd9433dfbe11066cca6 Mon Sep 17 00:00:00 2001
From: hongtaihu <13541229370 at qq.com>
Date: Sat, 3 Oct 2026 15:40:16 +0800
Subject: [PATCH] [clang][constexpr] Evaluate parenthesized array new with the
 allocated bound

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
---
 clang/lib/AST/ExprConstant.cpp                | 38 ++++++++++--------
 .../constexpr-array-new-paren-init.cpp        | 39 +++++++++++++++++++
 2 files changed, 60 insertions(+), 17 deletions(-)
 create mode 100644 clang/test/SemaCXX/constexpr-array-new-paren-init.cpp

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));



More information about the cfe-commits mailing list