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/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 <snar...@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 <snar...@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 a749937e27b5352856dfd67fac3ada545321021b 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/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..aeeed8776987d --- /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; +} \ No newline at end of file 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 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits