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 <snar...@umass.edu> Date: Wed, 27 Aug 2025 22:45:25 -0400 Subject: [PATCH 1/6] 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 <snar...@umass.edu> Date: Wed, 27 Aug 2025 23:10:05 -0400 Subject: [PATCH 2/6] 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 <snar...@umass.edu> Date: Wed, 27 Aug 2025 23:42:39 -0400 Subject: [PATCH 3/6] 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 <snar...@umass.edu> Date: Thu, 28 Aug 2025 09:18:05 -0400 Subject: [PATCH 4/6] 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@-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 >From 5394a043db1005eb82305a31099396ae99fafdc9 Mon Sep 17 00:00:00 2001 From: Samarth Narang <snar...@umass.edu> Date: Thu, 28 Aug 2025 10:39:20 -0400 Subject: [PATCH 5/6] Move test cases into new-delete --- clang/test/AST/ByteCode/new-delete.cpp | 26 +++++++++++++++++++ .../AST/ByteCode/new-neg-size-nothrow.cpp | 18 ------------- clang/test/AST/ByteCode/new-neg-size.cpp | 7 ----- 3 files changed, 26 insertions(+), 25 deletions(-) delete mode 100644 clang/test/AST/ByteCode/new-neg-size-nothrow.cpp delete mode 100644 clang/test/AST/ByteCode/new-neg-size.cpp diff --git a/clang/test/AST/ByteCode/new-delete.cpp b/clang/test/AST/ByteCode/new-delete.cpp index 3f0e928c7664e..6db58859d5bbc 100644 --- a/clang/test/AST/ByteCode/new-delete.cpp +++ b/clang/test/AST/ByteCode/new-delete.cpp @@ -1069,6 +1069,32 @@ namespace BaseCompare { static_assert(foo()); } + +namespace NegativeArraySize { + +constexpr void f() { + int x = -1; + int *p = new int[x]; // expected-note {{cannot allocate array with negative size in a constant expression}} +} +// both-error@-4 {{constexpr function never produces a constant expression}} +// ref-note@-3 {{cannot allocate array; evaluated array bound -1 is negative}} + +} // namespace NegativeArraySize + +namespace NewNegSizeNothrow { + constexpr int get_neg_size() { + return -1; + } + + constexpr bool test_nothrow_neg_size() { + int x = get_neg_size(); + int* p = new (std::nothrow) int[x]; + return p == nullptr; + } + + static_assert(test_nothrow_neg_size(), "expected nullptr"); +} // namespace NewNegSizeNothrow + #else /// Make sure we reject this prior to C++20 constexpr int a() { // both-error {{never produces a constant expression}} diff --git a/clang/test/AST/ByteCode/new-neg-size-nothrow.cpp b/clang/test/AST/ByteCode/new-neg-size-nothrow.cpp deleted file mode 100644 index 79cee693131fc..0000000000000 --- a/clang/test/AST/ByteCode/new-neg-size-nothrow.cpp +++ /dev/null @@ -1,18 +0,0 @@ -// 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 deleted file mode 100644 index d3755e67f04d8..0000000000000 --- a/clang/test/AST/ByteCode/new-neg-size.cpp +++ /dev/null @@ -1,7 +0,0 @@ -// 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@-4 {{constexpr function never produces a constant expression}} >From 46325899b68dac06f27551037b434668c5acdb42 Mon Sep 17 00:00:00 2001 From: Samarth Narang <snar...@umass.edu> Date: Thu, 28 Aug 2025 11:25:06 -0400 Subject: [PATCH 6/6] Shift expected output to line it is emitted on --- clang/test/AST/ByteCode/new-delete.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/clang/test/AST/ByteCode/new-delete.cpp b/clang/test/AST/ByteCode/new-delete.cpp index 6db58859d5bbc..665cb4cc2e48e 100644 --- a/clang/test/AST/ByteCode/new-delete.cpp +++ b/clang/test/AST/ByteCode/new-delete.cpp @@ -1070,14 +1070,13 @@ namespace BaseCompare { } -namespace NegativeArraySize { +namespace NegativeArraySize { -constexpr void f() { +constexpr void f() { // both-error {{constexpr function never produces a constant expression}} int x = -1; - int *p = new int[x]; // expected-note {{cannot allocate array with negative size in a constant expression}} + int *p = new int[x]; // expected-note {{cannot allocate array with negative size in a constant expression}} \ + // ref-note {{cannot allocate array; evaluated array bound -1 is negative}} } -// both-error@-4 {{constexpr function never produces a constant expression}} -// ref-note@-3 {{cannot allocate array; evaluated array bound -1 is negative}} } // namespace NegativeArraySize _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits