[clang] [clang][bytecode] Handle negative array sizes in constexpr `new` instead of asserting (PR #155737)
Samarth Narang via cfe-commits
cfe-commits at lists.llvm.org
Thu Aug 28 06:19:47 PDT 2025
https://github.com/snarang181 updated https://github.com/llvm/llvm-project/pull/155737
>From 0b13b0e77e184666d46450b264e1237e6c41a1de Mon Sep 17 00:00:00 2001
From: Samarth Narang <snarang at umass.edu>
Date: Wed, 27 Aug 2025 22:45:25 -0400
Subject: [PATCH 1/4] Enable nullptr handle with negative elemsize in a dynamic
allocation
---
clang/lib/AST/ByteCode/Interp.h | 7 ++++++-
clang/test/SemaCXX/new-neg-size.cpp | 15 +++++++++++++++
2 files changed, 21 insertions(+), 1 deletion(-)
create mode 100644 clang/test/SemaCXX/new-neg-size.cpp
diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h
index 92e60b6b88e6a..e505712b60dd3 100644
--- a/clang/lib/AST/ByteCode/Interp.h
+++ b/clang/lib/AST/ByteCode/Interp.h
@@ -3490,7 +3490,12 @@ inline bool AllocN(InterpState &S, CodePtr OpPC, PrimType T, const Expr *Source,
S.Stk.push<Pointer>(0, nullptr);
return true;
}
- assert(NumElements.isPositive());
+ if (!NumElements.isPositive()) {
+ if (!IsNoThrow)
+ return false;
+ S.Stk.push<Pointer>(0, nullptr);
+ return true;
+ }
if (!CheckArraySize(S, OpPC, static_cast<uint64_t>(NumElements)))
return false;
diff --git a/clang/test/SemaCXX/new-neg-size.cpp b/clang/test/SemaCXX/new-neg-size.cpp
new file mode 100644
index 0000000000000..4b5a0d4bfe228
--- /dev/null
+++ b/clang/test/SemaCXX/new-neg-size.cpp
@@ -0,0 +1,15 @@
+// RUN: not %clang_cc1 -std=c++20 -fsyntax-only %s 2>&1 \
+// RUN: | FileCheck %s --implicit-check-not='Assertion `NumElements.isPositive()` failed'
+
+// In C++20, constexpr dynamic allocation is permitted *only* if valid.
+// A negative element count must be diagnosed (and must not crash).
+
+constexpr void f_bad_neg() {
+ int a = -1;
+ (void) new int[a]; // triggers negative-size path in the interpreter
+}
+
+// Force evaluation so we definitely run the constexpr interpreter.
+constexpr bool force_eval = (f_bad_neg(), true);
+
+// CHECK: error: constexpr function never produces a constant expression
>From 6d28107ebb963803b3bf6c4ae09734ec1ec6b526 Mon Sep 17 00:00:00 2001
From: Samarth Narang <snarang at umass.edu>
Date: Wed, 27 Aug 2025 23:10:05 -0400
Subject: [PATCH 2/4] Add test case
---
clang/test/SemaCXX/new-neg-size.cpp | 18 ++++++++++++++----
1 file changed, 14 insertions(+), 4 deletions(-)
diff --git a/clang/test/SemaCXX/new-neg-size.cpp b/clang/test/SemaCXX/new-neg-size.cpp
index 4b5a0d4bfe228..e03f34c183809 100644
--- a/clang/test/SemaCXX/new-neg-size.cpp
+++ b/clang/test/SemaCXX/new-neg-size.cpp
@@ -1,7 +1,7 @@
// RUN: not %clang_cc1 -std=c++20 -fsyntax-only %s 2>&1 \
// RUN: | FileCheck %s --implicit-check-not='Assertion `NumElements.isPositive()` failed'
-// In C++20, constexpr dynamic allocation is permitted *only* if valid.
+// In C++20, constexpr dynamic allocation is permitted only if valid.
// A negative element count must be diagnosed (and must not crash).
constexpr void f_bad_neg() {
@@ -9,7 +9,17 @@ constexpr void f_bad_neg() {
(void) new int[a]; // triggers negative-size path in the interpreter
}
-// Force evaluation so we definitely run the constexpr interpreter.
-constexpr bool force_eval = (f_bad_neg(), true);
+struct __nothrow_t { };
+extern const __nothrow_t __nothrow_dummy;
+void* operator new[](unsigned long, const __nothrow_t&) noexcept;
-// CHECK: error: constexpr function never produces a constant expression
+// Ensure we take the nothrow overload.
+constexpr void f_bad_neg_nothrow() {
+ (void) new (__nothrow_dummy) int[-7]; // should evaluate to nullptr (no crash)
+}
+
+// Force evaluation so the constexpr interpreter actually runs both cases.
+constexpr bool force_eval1 = (f_bad_neg(), true);
+constexpr bool force_eval2 = (f_bad_neg_nothrow(), true);
+
+// CHECK: error: constexpr function {{(never produces|is not a)}} constant expression
\ No newline at end of file
>From 204c5995105abf8aa8c7949754ddecda2dd3e95e Mon Sep 17 00:00:00 2001
From: Samarth Narang <snarang at umass.edu>
Date: Wed, 27 Aug 2025 23:42:39 -0400
Subject: [PATCH 3/4] Revert "Add test case"
This reverts commit 6d28107ebb963803b3bf6c4ae09734ec1ec6b526.
---
clang/test/SemaCXX/new-neg-size.cpp | 18 ++++--------------
1 file changed, 4 insertions(+), 14 deletions(-)
diff --git a/clang/test/SemaCXX/new-neg-size.cpp b/clang/test/SemaCXX/new-neg-size.cpp
index e03f34c183809..4b5a0d4bfe228 100644
--- a/clang/test/SemaCXX/new-neg-size.cpp
+++ b/clang/test/SemaCXX/new-neg-size.cpp
@@ -1,7 +1,7 @@
// RUN: not %clang_cc1 -std=c++20 -fsyntax-only %s 2>&1 \
// RUN: | FileCheck %s --implicit-check-not='Assertion `NumElements.isPositive()` failed'
-// In C++20, constexpr dynamic allocation is permitted only if valid.
+// In C++20, constexpr dynamic allocation is permitted *only* if valid.
// A negative element count must be diagnosed (and must not crash).
constexpr void f_bad_neg() {
@@ -9,17 +9,7 @@ constexpr void f_bad_neg() {
(void) new int[a]; // triggers negative-size path in the interpreter
}
-struct __nothrow_t { };
-extern const __nothrow_t __nothrow_dummy;
-void* operator new[](unsigned long, const __nothrow_t&) noexcept;
+// Force evaluation so we definitely run the constexpr interpreter.
+constexpr bool force_eval = (f_bad_neg(), true);
-// Ensure we take the nothrow overload.
-constexpr void f_bad_neg_nothrow() {
- (void) new (__nothrow_dummy) int[-7]; // should evaluate to nullptr (no crash)
-}
-
-// Force evaluation so the constexpr interpreter actually runs both cases.
-constexpr bool force_eval1 = (f_bad_neg(), true);
-constexpr bool force_eval2 = (f_bad_neg_nothrow(), true);
-
-// CHECK: error: constexpr function {{(never produces|is not a)}} constant expression
\ No newline at end of file
+// CHECK: error: constexpr function never produces a constant expression
>From 21e1f9646cbfad9eece1fefa03a24519b5ffba3a Mon Sep 17 00:00:00 2001
From: Samarth Narang <snarang at umass.edu>
Date: Thu, 28 Aug 2025 09:18:05 -0400
Subject: [PATCH 4/4] Add nothrow test for negative size allocation in constant
expressions Move test directory to AST/ Add Diagnostic Note for negative size
allocation in constant expressions
---
.../include/clang/Basic/DiagnosticASTKinds.td | 2 ++
clang/lib/AST/ByteCode/Interp.h | 4 +++-
.../test/AST/ByteCode/new-neg-size-nothrow.cpp | 18 ++++++++++++++++++
clang/test/AST/ByteCode/new-neg-size.cpp | 7 +++++++
clang/test/SemaCXX/new-neg-size.cpp | 15 ---------------
5 files changed, 30 insertions(+), 16 deletions(-)
create mode 100644 clang/test/AST/ByteCode/new-neg-size-nothrow.cpp
create mode 100644 clang/test/AST/ByteCode/new-neg-size.cpp
delete mode 100644 clang/test/SemaCXX/new-neg-size.cpp
diff --git a/clang/include/clang/Basic/DiagnosticASTKinds.td b/clang/include/clang/Basic/DiagnosticASTKinds.td
index a63bd80b89657..0ce6a4f7d0113 100644
--- a/clang/include/clang/Basic/DiagnosticASTKinds.td
+++ b/clang/include/clang/Basic/DiagnosticASTKinds.td
@@ -37,6 +37,8 @@ def note_constexpr_invalid_inhctor : Note<
"constant expression; derived class cannot be implicitly initialized">;
def note_constexpr_no_return : Note<
"control reached end of constexpr function">;
+def note_constexpr_negative_allocation_size : Note<
+"cannot allocate array with negative size in a constant expression">;
def note_constexpr_virtual_call : Note<
"cannot evaluate call to virtual function in a constant expression "
"in C++ standards before C++20">;
diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h
index e505712b60dd3..0f937e6beb137 100644
--- a/clang/lib/AST/ByteCode/Interp.h
+++ b/clang/lib/AST/ByteCode/Interp.h
@@ -3491,8 +3491,10 @@ inline bool AllocN(InterpState &S, CodePtr OpPC, PrimType T, const Expr *Source,
return true;
}
if (!NumElements.isPositive()) {
- if (!IsNoThrow)
+ if (!IsNoThrow) {
+ S.FFDiag(Source, diag::note_constexpr_negative_allocation_size);
return false;
+ }
S.Stk.push<Pointer>(0, nullptr);
return true;
}
diff --git a/clang/test/AST/ByteCode/new-neg-size-nothrow.cpp b/clang/test/AST/ByteCode/new-neg-size-nothrow.cpp
new file mode 100644
index 0000000000000..79cee693131fc
--- /dev/null
+++ b/clang/test/AST/ByteCode/new-neg-size-nothrow.cpp
@@ -0,0 +1,18 @@
+// RUN: %clang_cc1 -std=c++20 -fsyntax-only -fexperimental-new-constant-interpreter -verify %s
+// expected-no-diagnostics
+
+struct __nothrow_t { };
+extern const __nothrow_t __nothrow_dummy;
+void* operator new[](unsigned long, const __nothrow_t&) noexcept;
+
+// This test ensures that new (nothrow) int[-1] does not crash in constexpr interpreter.
+// It should evaluate to a nullptr, not assert.
+constexpr int get_neg_size() {
+ return -1;
+}
+
+void test_nothrow_negative_size() {
+ int x = get_neg_size();
+ int *p = new (__nothrow_dummy) int[x];
+ (void)p;
+}
diff --git a/clang/test/AST/ByteCode/new-neg-size.cpp b/clang/test/AST/ByteCode/new-neg-size.cpp
new file mode 100644
index 0000000000000..d3755e67f04d8
--- /dev/null
+++ b/clang/test/AST/ByteCode/new-neg-size.cpp
@@ -0,0 +1,7 @@
+// RUN: %clang_cc1 -std=c++20 -fsyntax-only -fexperimental-new-constant-interpreter -verify %s
+
+constexpr void f() {
+ int a = -1;
+ int *b = new int[a]; // expected-note {{cannot allocate array with negative size in a constant expression}}
+}
+// expected-error at -4 {{constexpr function never produces a constant expression}}
diff --git a/clang/test/SemaCXX/new-neg-size.cpp b/clang/test/SemaCXX/new-neg-size.cpp
deleted file mode 100644
index 4b5a0d4bfe228..0000000000000
--- a/clang/test/SemaCXX/new-neg-size.cpp
+++ /dev/null
@@ -1,15 +0,0 @@
-// RUN: not %clang_cc1 -std=c++20 -fsyntax-only %s 2>&1 \
-// RUN: | FileCheck %s --implicit-check-not='Assertion `NumElements.isPositive()` failed'
-
-// In C++20, constexpr dynamic allocation is permitted *only* if valid.
-// A negative element count must be diagnosed (and must not crash).
-
-constexpr void f_bad_neg() {
- int a = -1;
- (void) new int[a]; // triggers negative-size path in the interpreter
-}
-
-// Force evaluation so we definitely run the constexpr interpreter.
-constexpr bool force_eval = (f_bad_neg(), true);
-
-// CHECK: error: constexpr function never produces a constant expression
More information about the cfe-commits
mailing list