[clang] [Clang][ExprConst] Accept prvalue for nothrow new (PR #226753)

via cfe-commits cfe-commits at lists.llvm.org
Sun Sep 27 00:02:39 PDT 2026


https://github.com/aokblast updated https://github.com/llvm/llvm-project/pull/226753

>From ff5453f705924f0409df46cc523fcaceedcf459b Mon Sep 17 00:00:00 2001
From: ShengYi Hung <aokblast at FreeBSD.org>
Date: Sat, 26 Sep 2026 23:52:05 -0500
Subject: [PATCH 1/4] [Clang][ExprConst] Accept prvalue for nothrow new

A user defined operator new can accept prvalue for nothrow. There is
no guarantee in spec that it should be lvalue. Relax the check with
side effect only.
---
 clang/lib/AST/ExprConstant.cpp              |  7 +++++--
 clang/test/SemaCXX/new-nothrow-by-value.cpp | 18 ++++++++++++++++++
 2 files changed, 23 insertions(+), 2 deletions(-)
 create mode 100644 clang/test/SemaCXX/new-nothrow-by-value.cpp

diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 2df754dc9007f..578ce94e93e3e 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -11030,8 +11030,11 @@ bool PointerExprEvaluator::VisitCXXNewExpr(const CXXNewExpr *E) {
     // (which should presumably be valid only if N is a multiple of
     // alignof(int), and in any case can't be deallocated unless N is
     // alignof(X) and X has new-extended alignment).
-    LValue Nothrow;
-    if (!EvaluateLValue(E->getPlacementArg(0), Nothrow, Info))
+    //
+    // The argument is usually an lvalue (the standard signature takes a
+    // 'const std::nothrow_t &'), but a user-declared allocation function may
+    // take 'std::nothrow_t' by value, in which case it is a prvalue.
+    if (!EvaluateIgnoredValue(Info, E->getPlacementArg(0)))
       return false;
     IsNothrow = true;
   } else if (OperatorNew->isReservedGlobalPlacementOperator()) {
diff --git a/clang/test/SemaCXX/new-nothrow-by-value.cpp b/clang/test/SemaCXX/new-nothrow-by-value.cpp
new file mode 100644
index 0000000000000..acb70803b020c
--- /dev/null
+++ b/clang/test/SemaCXX/new-nothrow-by-value.cpp
@@ -0,0 +1,18 @@
+// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %s
+
+// The constant evaluator used to assume the (std::nothrow) placement argument
+// is always an lvalue and crashed when a user-declared allocation function
+// takes std::nothrow_t by value, making the argument a prvalue.
+
+namespace std {
+inline constexpr struct nothrow_t {
+} nothrow;
+} // namespace std
+
+void *operator new[](unsigned long, std::nothrow_t) noexcept;
+void *operator new(unsigned long, std::nothrow_t) noexcept;
+
+void set(int *p) {
+  p = (1 ? new (std::nothrow) int[1] : nullptr);
+  p = (1 ? new (std::nothrow) int : nullptr);
+}

>From ef60af81d777f6a8cbd8a428b7bc7bccad6d1deb Mon Sep 17 00:00:00 2001
From: ShengYi Hung <aokblast at FreeBSD.org>
Date: Sun, 27 Sep 2026 00:29:09 -0500
Subject: [PATCH 2/4] fixup! [Clang][ExprConst] Accept prvalue for nothrow new

---
 clang/test/SemaCXX/new-nothrow-by-value.cpp | 1 +
 1 file changed, 1 insertion(+)

diff --git a/clang/test/SemaCXX/new-nothrow-by-value.cpp b/clang/test/SemaCXX/new-nothrow-by-value.cpp
index acb70803b020c..569ccec204530 100644
--- a/clang/test/SemaCXX/new-nothrow-by-value.cpp
+++ b/clang/test/SemaCXX/new-nothrow-by-value.cpp
@@ -1,4 +1,5 @@
 // RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %s -fexperimental-new-constant-interpreter
 
 // The constant evaluator used to assume the (std::nothrow) placement argument
 // is always an lvalue and crashed when a user-declared allocation function

>From c514037b10d0bf7a75f6a3f4e39cadf47440b622 Mon Sep 17 00:00:00 2001
From: ShengYi Hung <aokblast at FreeBSD.org>
Date: Sun, 27 Sep 2026 01:23:48 -0500
Subject: [PATCH 3/4] fixup! [Clang][ExprConst] Accept prvalue for nothrow new

---
 clang/test/SemaCXX/new-nothrow-by-value.cpp | 1 +
 1 file changed, 1 insertion(+)

diff --git a/clang/test/SemaCXX/new-nothrow-by-value.cpp b/clang/test/SemaCXX/new-nothrow-by-value.cpp
index 569ccec204530..5bd784b157d5e 100644
--- a/clang/test/SemaCXX/new-nothrow-by-value.cpp
+++ b/clang/test/SemaCXX/new-nothrow-by-value.cpp
@@ -1,5 +1,6 @@
 // RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %s
 // RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %s -fexperimental-new-constant-interpreter
+// expected-no-diagnostics
 
 // The constant evaluator used to assume the (std::nothrow) placement argument
 // is always an lvalue and crashed when a user-declared allocation function

>From 220b5f54e2e2cca92e36d7e0033f0000e8381073 Mon Sep 17 00:00:00 2001
From: ShengYi Hung <aokblast at FreeBSD.org>
Date: Sun, 27 Sep 2026 02:02:22 -0500
Subject: [PATCH 4/4] fixup! [Clang][ExprConst] Accept prvalue for nothrow new

---
 clang/test/SemaCXX/new-nothrow-by-value.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/clang/test/SemaCXX/new-nothrow-by-value.cpp b/clang/test/SemaCXX/new-nothrow-by-value.cpp
index 5bd784b157d5e..2f7af00b9316b 100644
--- a/clang/test/SemaCXX/new-nothrow-by-value.cpp
+++ b/clang/test/SemaCXX/new-nothrow-by-value.cpp
@@ -11,8 +11,8 @@ inline constexpr struct nothrow_t {
 } nothrow;
 } // namespace std
 
-void *operator new[](unsigned long, std::nothrow_t) noexcept;
-void *operator new(unsigned long, std::nothrow_t) noexcept;
+void *operator new[](__SIZE_TYPE__, std::nothrow_t) noexcept;
+void *operator new(__SIZE_TYPE__, std::nothrow_t) noexcept;
 
 void set(int *p) {
   p = (1 ? new (std::nothrow) int[1] : nullptr);



More information about the cfe-commits mailing list