[libc-commits] [libc] [llvm] [libc] Move nullptr_t definition to cstddef/nullptr_t.h (PR #236712)

Pavel Labath via libc-commits libc-commits at lists.llvm.org
Thu Oct 29 23:54:34 PDT 2026


https://github.com/labath updated https://github.com/llvm/llvm-project/pull/236712

>From c1f963e986768caa15f0c3e368ee6490868104f2 Mon Sep 17 00:00:00 2001
From: Pavel Labath <pavel at labath.sk>
Date: Thu, 29 Oct 2026 09:13:14 +0000
Subject: [PATCH 1/2] [libc] Move nullptr_t definition to cstddef/nullptr_t.h

cpp::nullptr_t was previously defined in type_traits/is_null_pointer.h,
which is an odd place for it. In standard C++, nullptr_t is defined in
<cstddef>. However, putting it directly into the umbrella cstddef.h
pulls in byte, which depends on type_traits.h for enable_if_t and
is_integral_v, creating an include cycle if type_traits wants to use
nullptr_t.

To solve this, I've placed nullptr_t into a granular
src/__support/CPP/cstddef/nullptr_t.h header (following the precedent of
headers in utility/ and libc++'s __cstddef/nullptr_t.h).

With this change:
- cstddef.h re-exports nullptr_t from cstddef/nullptr_t.h.
- is_null_pointer.h includes cstddef/nullptr_t.h and uses cpp::nullptr_t
  without circular dependencies.
- unique_ptr.h, LibcTest.h, and tests include cstddef/nullptr_t.h
  directly and use nullptr_t in place of decltype(nullptr).
- added CMake target libc.src.__support.CPP.nullptr_t.
- added nullptr_t construction tests in unique_ptr_test.

Assisted-by: Gemini
---
 libc/src/__support/CPP/CMakeLists.txt         | 11 ++++++++++
 libc/src/__support/CPP/cstddef.h              |  1 +
 libc/src/__support/CPP/cstddef/nullptr_t.h    | 22 +++++++++++++++++++
 .../CPP/type_traits/is_null_pointer.h         |  2 +-
 libc/src/__support/CPP/unique_ptr.h           |  5 +++--
 libc/test/UnitTest/CMakeLists.txt             |  1 +
 libc/test/UnitTest/LibcTest.h                 |  1 +
 libc/test/src/__support/CPP/CMakeLists.txt    |  2 ++
 .../src/__support/CPP/functional_test.cpp     |  6 +++--
 .../src/__support/CPP/unique_ptr_test.cpp     | 15 +++++++++++++
 .../llvm-project-overlay/libc/BUILD.bazel     | 10 +++++++++
 .../libc/test/UnitTest/BUILD.bazel            |  1 +
 .../libc/test/src/__support/CPP/BUILD.bazel   |  1 +
 13 files changed, 73 insertions(+), 5 deletions(-)
 create mode 100644 libc/src/__support/CPP/cstddef/nullptr_t.h

diff --git a/libc/src/__support/CPP/CMakeLists.txt b/libc/src/__support/CPP/CMakeLists.txt
index 97898e2744f7b0..6c911333f01079 100644
--- a/libc/src/__support/CPP/CMakeLists.txt
+++ b/libc/src/__support/CPP/CMakeLists.txt
@@ -29,11 +29,20 @@ add_header_library(
     bitset.h
 )
 
+add_header_library(
+  nullptr_t
+  HDRS
+    cstddef/nullptr_t.h
+  DEPENDS
+    libc.src.__support.macros.config
+)
+
 add_header_library(
   cstddef
   HDRS
     cstddef.h
   DEPENDS
+    .nullptr_t
     .type_traits
 )
 
@@ -186,6 +195,7 @@ add_header_library(
     type_traits/type_identity.h
     type_traits/void_t.h
   DEPENDS
+    .nullptr_t
     libc.include.llvm-libc-macros.stdfix_macros
     libc.src.__support.macros.attributes
     libc.src.__support.macros.properties.types
@@ -229,6 +239,7 @@ add_header_library(
     unique_ptr.h
   DEPENDS
     .new
+    .nullptr_t
     .type_traits
     .utility
     libc.hdr.types.size_t
diff --git a/libc/src/__support/CPP/cstddef.h b/libc/src/__support/CPP/cstddef.h
index 1507789cb1b10c..4b0b4a25790d08 100644
--- a/libc/src/__support/CPP/cstddef.h
+++ b/libc/src/__support/CPP/cstddef.h
@@ -9,6 +9,7 @@
 #ifndef LLVM_LIBC_SRC___SUPPORT_CPP_CSTDDEF_H
 #define LLVM_LIBC_SRC___SUPPORT_CPP_CSTDDEF_H
 
+#include "src/__support/CPP/cstddef/nullptr_t.h"
 #include "src/__support/macros/attributes.h"
 #include "src/__support/macros/config.h"
 #include "type_traits.h" // For enable_if_t, is_integral_v.
diff --git a/libc/src/__support/CPP/cstddef/nullptr_t.h b/libc/src/__support/CPP/cstddef/nullptr_t.h
new file mode 100644
index 00000000000000..6cf39070d00356
--- /dev/null
+++ b/libc/src/__support/CPP/cstddef/nullptr_t.h
@@ -0,0 +1,22 @@
+//===-- nullptr_t type ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC___SUPPORT_CPP_CSTDDEF_NULLPTR_T_H
+#define LLVM_LIBC_SRC___SUPPORT_CPP_CSTDDEF_NULLPTR_T_H
+
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+namespace cpp {
+
+using nullptr_t = decltype(nullptr);
+
+} // namespace cpp
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC___SUPPORT_CPP_CSTDDEF_NULLPTR_T_H
diff --git a/libc/src/__support/CPP/type_traits/is_null_pointer.h b/libc/src/__support/CPP/type_traits/is_null_pointer.h
index acf341311be06e..87a94192f01b22 100644
--- a/libc/src/__support/CPP/type_traits/is_null_pointer.h
+++ b/libc/src/__support/CPP/type_traits/is_null_pointer.h
@@ -8,6 +8,7 @@
 #ifndef LLVM_LIBC_SRC___SUPPORT_CPP_TYPE_TRAITS_IS_NULL_POINTER_H
 #define LLVM_LIBC_SRC___SUPPORT_CPP_TYPE_TRAITS_IS_NULL_POINTER_H
 
+#include "src/__support/CPP/cstddef/nullptr_t.h"
 #include "src/__support/CPP/type_traits/is_same.h"
 #include "src/__support/CPP/type_traits/remove_cv.h"
 #include "src/__support/macros/attributes.h"
@@ -17,7 +18,6 @@ namespace LIBC_NAMESPACE_DECL {
 namespace cpp {
 
 // is_null_pointer
-using nullptr_t = decltype(nullptr);
 template <class T>
 struct is_null_pointer : cpp::is_same<cpp::nullptr_t, cpp::remove_cv_t<T>> {};
 template <class T>
diff --git a/libc/src/__support/CPP/unique_ptr.h b/libc/src/__support/CPP/unique_ptr.h
index a8cf11d2ae8a72..6a98d4223b368e 100644
--- a/libc/src/__support/CPP/unique_ptr.h
+++ b/libc/src/__support/CPP/unique_ptr.h
@@ -15,6 +15,7 @@
 #define LLVM_LIBC_SRC___SUPPORT_CPP_UNIQUE_PTR_H
 
 #include "hdr/types/size_t.h"
+#include "src/__support/CPP/cstddef/nullptr_t.h"
 #include "src/__support/CPP/new.h"
 #include "src/__support/CPP/type_traits.h"
 #include "src/__support/CPP/utility.h"
@@ -66,7 +67,7 @@ class LIBC_TRIVIAL_ABI unique_ptr {
   using pointer = T *;
 
   LIBC_INLINE constexpr unique_ptr() = default;
-  LIBC_INLINE constexpr unique_ptr(decltype(nullptr)) : ptr(nullptr) {}
+  LIBC_INLINE constexpr unique_ptr(nullptr_t) : ptr(nullptr) {}
   LIBC_INLINE constexpr explicit unique_ptr(pointer p) : ptr(p) {}
 
   LIBC_INLINE constexpr unique_ptr(pointer p, const deleter_type &d)
@@ -156,7 +157,7 @@ class LIBC_TRIVIAL_ABI unique_ptr<T[], Deleter> {
   using pointer = T *;
 
   LIBC_INLINE constexpr unique_ptr() = default;
-  LIBC_INLINE constexpr unique_ptr(decltype(nullptr)) : ptr(nullptr) {}
+  LIBC_INLINE constexpr unique_ptr(nullptr_t) : ptr(nullptr) {}
   LIBC_INLINE constexpr explicit unique_ptr(pointer p) : ptr(p) {}
 
   LIBC_INLINE constexpr unique_ptr(pointer p, const deleter_type &d)
diff --git a/libc/test/UnitTest/CMakeLists.txt b/libc/test/UnitTest/CMakeLists.txt
index de82c2f1362d65..2622e74a8ec78f 100644
--- a/libc/test/UnitTest/CMakeLists.txt
+++ b/libc/test/UnitTest/CMakeLists.txt
@@ -103,6 +103,7 @@ add_unittest_framework_library(
     libc.src.__support.big_int
     libc.src.__support.c_string
     libc.src.__support.CPP.functional
+    libc.src.__support.CPP.nullptr_t
     libc.src.__support.CPP.string
     libc.src.__support.CPP.string_view
     libc.src.__support.CPP.type_traits
diff --git a/libc/test/UnitTest/LibcTest.h b/libc/test/UnitTest/LibcTest.h
index 56ed7f3383c053..3feecb262f4988 100644
--- a/libc/test/UnitTest/LibcTest.h
+++ b/libc/test/UnitTest/LibcTest.h
@@ -24,6 +24,7 @@
 // This file can only include headers from src/__support/ or test/UnitTest. No
 // other headers should be included.
 
+#include "src/__support/CPP/cstddef/nullptr_t.h"
 #include "src/__support/CPP/functional.h"
 #include "src/__support/CPP/string.h"
 #include "src/__support/CPP/string_view.h"
diff --git a/libc/test/src/__support/CPP/CMakeLists.txt b/libc/test/src/__support/CPP/CMakeLists.txt
index ffb1ab6f9d61df..5116f559e71c0a 100644
--- a/libc/test/src/__support/CPP/CMakeLists.txt
+++ b/libc/test/src/__support/CPP/CMakeLists.txt
@@ -72,6 +72,7 @@ add_libc_test(
     functional_test.cpp
   DEPENDS
     libc.src.__support.CPP.functional
+    libc.src.__support.CPP.nullptr_t
     libc.src.__support.CPP.type_traits
 )
 
@@ -174,6 +175,7 @@ add_libc_test(
     unique_ptr_test.cpp
   DEPENDS
     libc.hdr.signal_macros
+    libc.src.__support.CPP.nullptr_t
     libc.src.__support.CPP.unique_ptr
     libc.src.__support.CPP.utility
 )
diff --git a/libc/test/src/__support/CPP/functional_test.cpp b/libc/test/src/__support/CPP/functional_test.cpp
index db427f7e6901d2..5c803d2bf07b9c 100644
--- a/libc/test/src/__support/CPP/functional_test.cpp
+++ b/libc/test/src/__support/CPP/functional_test.cpp
@@ -11,6 +11,7 @@
 ///
 //===----------------------------------------------------------------------===//
 
+#include "src/__support/CPP/cstddef/nullptr_t.h"
 #include "src/__support/CPP/functional.h"
 #include "src/__support/CPP/type_traits.h"
 #include "test/UnitTest/Test.h"
@@ -18,6 +19,7 @@
 using LIBC_NAMESPACE::cpp::function_ref;
 using LIBC_NAMESPACE::cpp::is_assignable_v;
 using LIBC_NAMESPACE::cpp::is_constructible_v;
+using LIBC_NAMESPACE::cpp::nullptr_t;
 
 static int forty_two() { return 42; }
 static int seven() { return 7; }
@@ -43,7 +45,7 @@ using IntIntFn = function_ref<int(int)>;
 
 // Like std::function_ref, there is no empty state.
 static_assert(!is_constructible_v<IntFn>);
-static_assert(!is_constructible_v<IntFn, decltype(nullptr)>);
+static_assert(!is_constructible_v<IntFn, nullptr_t>);
 static_assert(!is_constructible_v<bool, IntFn>);
 
 // Callables with incompatible signatures are rejected.
@@ -61,7 +63,7 @@ static_assert(is_assignable_v<IntFn &, int (&)()>);
 static_assert(is_constructible_v<IntFn, Functor>);
 static_assert(!is_assignable_v<IntFn &, Functor>);
 static_assert(!is_assignable_v<IntFn &, Functor &>);
-static_assert(!is_assignable_v<IntFn &, decltype(nullptr)>);
+static_assert(!is_assignable_v<IntFn &, nullptr_t>);
 
 TEST(LlvmLibcFunctionRefTest, Lambda) {
   int offset = 10;
diff --git a/libc/test/src/__support/CPP/unique_ptr_test.cpp b/libc/test/src/__support/CPP/unique_ptr_test.cpp
index 56bdd77eb1e7ac..7e26a8e3449b3a 100644
--- a/libc/test/src/__support/CPP/unique_ptr_test.cpp
+++ b/libc/test/src/__support/CPP/unique_ptr_test.cpp
@@ -12,12 +12,14 @@
 //===----------------------------------------------------------------------===//
 
 #include "hdr/signal_macros.h"
+#include "src/__support/CPP/cstddef/nullptr_t.h"
 #include "src/__support/CPP/unique_ptr.h"
 #include "src/__support/CPP/utility.h"
 #include "src/__support/alloc-checker.h"
 #include "test/UnitTest/Test.h"
 
 using LIBC_NAMESPACE::AllocChecker;
+using LIBC_NAMESPACE::cpp::nullptr_t;
 using LIBC_NAMESPACE::cpp::unique_ptr;
 
 struct DestructTracker {
@@ -48,6 +50,19 @@ TEST(LlvmLibcUniquePtrTest, Nullptr) {
   unique_ptr<int> ptr(nullptr);
   ASSERT_FALSE(static_cast<bool>(ptr));
   ASSERT_EQ(ptr.get(), nullptr);
+
+  constexpr nullptr_t null{};
+  unique_ptr<int> ptr2(null);
+  ASSERT_FALSE(static_cast<bool>(ptr2));
+  ASSERT_EQ(ptr2.get(), nullptr);
+
+  unique_ptr<int[]> arr(nullptr);
+  ASSERT_FALSE(static_cast<bool>(arr));
+  ASSERT_EQ(arr.get(), nullptr);
+
+  unique_ptr<int[]> arr2(null);
+  ASSERT_FALSE(static_cast<bool>(arr2));
+  ASSERT_EQ(arr2.get(), nullptr);
 }
 
 // Test move construction transferring ownership.
diff --git a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
index f468f19e65edba..c97ba47c41c273 100644
--- a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
+++ b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
@@ -1784,10 +1784,19 @@ libc_support_library(
     ],
 )
 
+libc_support_library(
+    name = "__support_cpp_nullptr_t",
+    hdrs = ["src/__support/CPP/cstddef/nullptr_t.h"],
+    deps = [
+        ":__support_macros_config",
+    ],
+)
+
 libc_support_library(
     name = "__support_cpp_cstddef",
     hdrs = ["src/__support/CPP/cstddef.h"],
     deps = [
+        ":__support_cpp_nullptr_t",
         ":__support_cpp_type_traits",
         ":__support_macros_attributes",
         ":__support_macros_config",
@@ -1965,6 +1974,7 @@ libc_support_library(
         "src/__support/CPP/utility/forward.h",
     ],
     deps = [
+        ":__support_cpp_nullptr_t",
         ":__support_macros_attributes",
         ":__support_macros_config",
         ":__support_macros_properties_compiler",
diff --git a/utils/bazel/llvm-project-overlay/libc/test/UnitTest/BUILD.bazel b/utils/bazel/llvm-project-overlay/libc/test/UnitTest/BUILD.bazel
index 70c48473105a6b..bc926453af3fc6 100644
--- a/utils/bazel/llvm-project-overlay/libc/test/UnitTest/BUILD.bazel
+++ b/utils/bazel/llvm-project-overlay/libc/test/UnitTest/BUILD.bazel
@@ -55,6 +55,7 @@ libc_test_library(
         "//libc:__support_cpp_bitset",
         "//libc:__support_cpp_functional",
         "//libc:__support_cpp_limits",
+        "//libc:__support_cpp_nullptr_t",
         "//libc:__support_cpp_scope",
         "//libc:__support_cpp_span",
         "//libc:__support_cpp_string",
diff --git a/utils/bazel/llvm-project-overlay/libc/test/src/__support/CPP/BUILD.bazel b/utils/bazel/llvm-project-overlay/libc/test/src/__support/CPP/BUILD.bazel
index 56a64bf0c3c918..139e9412218bec 100644
--- a/utils/bazel/llvm-project-overlay/libc/test/src/__support/CPP/BUILD.bazel
+++ b/utils/bazel/llvm-project-overlay/libc/test/src/__support/CPP/BUILD.bazel
@@ -43,6 +43,7 @@ libc_test(
     srcs = ["functional_test.cpp"],
     deps = [
         "//libc:__support_cpp_functional",
+        "//libc:__support_cpp_nullptr_t",
         "//libc:__support_cpp_type_traits",
     ],
 )

>From 5c1cb9b5b51e61392f95224c3e82937151e77ccf Mon Sep 17 00:00:00 2001
From: Pavel Labath <pavel at labath.sk>
Date: Fri, 30 Oct 2026 06:54:11 +0000
Subject: [PATCH 2/2] fix header

---
 libc/src/__support/CPP/cstddef/nullptr_t.h | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/libc/src/__support/CPP/cstddef/nullptr_t.h b/libc/src/__support/CPP/cstddef/nullptr_t.h
index 6cf39070d00356..334143c47d8d3a 100644
--- a/libc/src/__support/CPP/cstddef/nullptr_t.h
+++ b/libc/src/__support/CPP/cstddef/nullptr_t.h
@@ -1,10 +1,15 @@
-//===-- nullptr_t type ------------------------------------------*- C++ -*-===//
+//===----------------------------------------------------------------------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
+///
+/// \file
+/// Implementation of cpp::nullptr_t.
+///
+//===----------------------------------------------------------------------===//
 
 #ifndef LLVM_LIBC_SRC___SUPPORT_CPP_CSTDDEF_NULLPTR_T_H
 #define LLVM_LIBC_SRC___SUPPORT_CPP_CSTDDEF_NULLPTR_T_H



More information about the libc-commits mailing list