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 <[email protected]>
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));

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

Reply via email to