[llvm] [LLVM][Support] add nonNull function helper (PR #188718)

Nathan Gauër via llvm-commits llvm-commits at lists.llvm.org
Mon Mar 30 06:16:34 PDT 2026


https://github.com/Keenuts updated https://github.com/llvm/llvm-project/pull/188718

>From 31aed38ac641cc0e7cd14a908094f8fe621f1203 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nathan=20Gau=C3=ABr?= <brioche at google.com>
Date: Thu, 26 Mar 2026 10:54:00 +0100
Subject: [PATCH 1/8] [LLVM][Support] add nonNull function helper

We often see a pattern like:
```
T *ptr = doSomething()
assert(ptr && "doSomething() shouldn't return nullptr");
```

We also have functions like `cantFail`, but those are working with
Expected types.
This commits adds a `nonNull` function, which can be used inline.
In practice, one could use:

```
T *ptr = cast<T>(functionReturningT());
```

But it conveys the meaning that `functionReturningT` might return
a subtype/supertype that we actually cast.

Function behaves like the other error kinds: calls llvm_unreachable,
which may or may not trap depending on the build options.
Calling this function with `nullptr` won't build, but it makes no sense
to do so.
---
 llvm/docs/ProgrammersManual.rst      |  8 ++++++++
 llvm/include/llvm/Support/Error.h    | 12 ++++++++++++
 llvm/unittests/Support/ErrorTest.cpp | 24 ++++++++++++++++++++++++
 3 files changed, 44 insertions(+)

diff --git a/llvm/docs/ProgrammersManual.rst b/llvm/docs/ProgrammersManual.rst
index 51a813eadee73..0b32dc62b0593 100644
--- a/llvm/docs/ProgrammersManual.rst
+++ b/llvm/docs/ProgrammersManual.rst
@@ -453,6 +453,14 @@ violations even in builds that do not enable assertions:
     reportFatalInternalError("Analysis 'foo' not preserved");
   }
 
+Additionally, ``nonNull`` can be used to check/document that a pointer
+is never supposed to be null inline.
+
+.. code-block:: c++
+    setMyPointer("key", Pointer);
+    // [...]
+    Type *P = nonNull(getMyPointer("key"));
+
 Recoverable Errors
 ^^^^^^^^^^^^^^^^^^
 
diff --git a/llvm/include/llvm/Support/Error.h b/llvm/include/llvm/Support/Error.h
index c9fd16fdb7c2b..442ab16734eb5 100644
--- a/llvm/include/llvm/Support/Error.h
+++ b/llvm/include/llvm/Support/Error.h
@@ -811,6 +811,18 @@ T cantFail(Expected<T> ValOrErr, const char *Msg = nullptr) {
   }
 }
 
+/// Calls llvm_unreachable if Pointer is null, otherwise returns the
+/// pointer as is.
+template<typename T>
+LLVM_ATTRIBUTE_RETURNS_NONNULL T* nonNull(T* Pointer, const char *Msg = nullptr) {
+  if (Pointer)
+    return Pointer;
+
+  if (!Msg)
+    Msg = "Expected a non-null pointer but got a null pointer";
+  llvm_unreachable(Msg);
+}
+
 /// Report a fatal error if ValOrErr is a failure value, otherwise unwraps and
 /// returns the contained reference.
 ///
diff --git a/llvm/unittests/Support/ErrorTest.cpp b/llvm/unittests/Support/ErrorTest.cpp
index 45c0a4f450b51..953160596e732 100644
--- a/llvm/unittests/Support/ErrorTest.cpp
+++ b/llvm/unittests/Support/ErrorTest.cpp
@@ -1219,4 +1219,28 @@ TEST(Error, ForwardToExpected) {
   EXPECT_THAT_ERROR(ExpectedReturningFct(false).moveInto(MaybeV), Succeeded());
   EXPECT_EQ(*MaybeV, 42);
 }
+
+TEST(Error, NonNullSuccess) {
+  int tmp = 0;
+  int *ptr = &tmp;
+  const int *cptr = ptr;
+  const int *const ccptr = ptr;
+
+  EXPECT_EQ(ptr, nonNull(&tmp));
+  EXPECT_EQ(ptr, nonNull(ptr));
+  EXPECT_EQ(ptr, nonNull(cptr));
+  EXPECT_EQ(ptr, nonNull(ccptr));
+}
+
+#if LLVM_ENABLE_ABI_BREAKING_CHECKS && !defined(NDEBUG)
+TEST(Error, NonNullFails) {
+  int *NullPtr = nullptr;
+  EXPECT_DEATH(nonNull(NullPtr), "Expected a non-null pointer but got a null pointer")
+      << "nonNull(NullPtr) did not cause an abort for null pointer";
+
+  EXPECT_DEATH(nonNull(NullPtr, "custom message"), "custom message")
+      << "nonNull(nullptr) did not cause an abort for null pointer";
+}
+#endif
+
 } // namespace

>From e46c19b9dd6c6593f4984b2cdd77bc37aa9b51e2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nathan=20Gau=C3=ABr?= <brioche at google.com>
Date: Thu, 26 Mar 2026 11:44:45 +0100
Subject: [PATCH 2/8] clang format

---
 llvm/include/llvm/Support/Error.h    | 5 +++--
 llvm/unittests/Support/ErrorTest.cpp | 3 ++-
 2 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/llvm/include/llvm/Support/Error.h b/llvm/include/llvm/Support/Error.h
index 442ab16734eb5..8474d54c42bed 100644
--- a/llvm/include/llvm/Support/Error.h
+++ b/llvm/include/llvm/Support/Error.h
@@ -813,8 +813,9 @@ T cantFail(Expected<T> ValOrErr, const char *Msg = nullptr) {
 
 /// Calls llvm_unreachable if Pointer is null, otherwise returns the
 /// pointer as is.
-template<typename T>
-LLVM_ATTRIBUTE_RETURNS_NONNULL T* nonNull(T* Pointer, const char *Msg = nullptr) {
+template <typename T>
+LLVM_ATTRIBUTE_RETURNS_NONNULL T *nonNull(T *Pointer,
+                                          const char *Msg = nullptr) {
   if (Pointer)
     return Pointer;
 
diff --git a/llvm/unittests/Support/ErrorTest.cpp b/llvm/unittests/Support/ErrorTest.cpp
index 953160596e732..572cac38ac094 100644
--- a/llvm/unittests/Support/ErrorTest.cpp
+++ b/llvm/unittests/Support/ErrorTest.cpp
@@ -1235,7 +1235,8 @@ TEST(Error, NonNullSuccess) {
 #if LLVM_ENABLE_ABI_BREAKING_CHECKS && !defined(NDEBUG)
 TEST(Error, NonNullFails) {
   int *NullPtr = nullptr;
-  EXPECT_DEATH(nonNull(NullPtr), "Expected a non-null pointer but got a null pointer")
+  EXPECT_DEATH(nonNull(NullPtr),
+               "Expected a non-null pointer but got a null pointer")
       << "nonNull(NullPtr) did not cause an abort for null pointer";
 
   EXPECT_DEATH(nonNull(NullPtr, "custom message"), "custom message")

>From 43422fe9bd92043ec92cd6094702368e535b949d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nathan=20Gau=C3=ABr?= <brioche at google.com>
Date: Thu, 26 Mar 2026 11:45:26 +0100
Subject: [PATCH 3/8] fix code-block

---
 llvm/docs/ProgrammersManual.rst | 1 +
 1 file changed, 1 insertion(+)

diff --git a/llvm/docs/ProgrammersManual.rst b/llvm/docs/ProgrammersManual.rst
index 0b32dc62b0593..3ee59b5ecf6df 100644
--- a/llvm/docs/ProgrammersManual.rst
+++ b/llvm/docs/ProgrammersManual.rst
@@ -457,6 +457,7 @@ Additionally, ``nonNull`` can be used to check/document that a pointer
 is never supposed to be null inline.
 
 .. code-block:: c++
+
     setMyPointer("key", Pointer);
     // [...]
     Type *P = nonNull(getMyPointer("key"));

>From 954b724c5bcc4dbc0b0d565c1010e1709f2f32ef Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nathan=20Gau=C3=ABr?= <brioche at google.com>
Date: Thu, 26 Mar 2026 13:02:08 +0100
Subject: [PATCH 4/8] pr-feedback: replace define

---
 llvm/unittests/Support/ErrorTest.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/unittests/Support/ErrorTest.cpp b/llvm/unittests/Support/ErrorTest.cpp
index 572cac38ac094..30e4efd0bc391 100644
--- a/llvm/unittests/Support/ErrorTest.cpp
+++ b/llvm/unittests/Support/ErrorTest.cpp
@@ -1232,7 +1232,7 @@ TEST(Error, NonNullSuccess) {
   EXPECT_EQ(ptr, nonNull(ccptr));
 }
 
-#if LLVM_ENABLE_ABI_BREAKING_CHECKS && !defined(NDEBUG)
+#if !defined(NDEBUG) && GTEST_HAS_DEATH_TEST
 TEST(Error, NonNullFails) {
   int *NullPtr = nullptr;
   EXPECT_DEATH(nonNull(NullPtr),

>From 2dceb4720019a0d8bd8fbcac49a1abc457655726 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nathan=20Gau=C3=ABr?= <brioche at google.com>
Date: Thu, 26 Mar 2026 14:30:44 +0100
Subject: [PATCH 5/8] pr-feedback: rename

---
 llvm/docs/ProgrammersManual.rst      |  4 ++--
 llvm/include/llvm/Support/Error.h    |  4 ++--
 llvm/unittests/Support/ErrorTest.cpp | 16 ++++++++--------
 3 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/llvm/docs/ProgrammersManual.rst b/llvm/docs/ProgrammersManual.rst
index 3ee59b5ecf6df..04e1983fb24e4 100644
--- a/llvm/docs/ProgrammersManual.rst
+++ b/llvm/docs/ProgrammersManual.rst
@@ -453,14 +453,14 @@ violations even in builds that do not enable assertions:
     reportFatalInternalError("Analysis 'foo' not preserved");
   }
 
-Additionally, ``nonNull`` can be used to check/document that a pointer
+Additionally, ``checkNotNull`` can be used to check/document that a pointer
 is never supposed to be null inline.
 
 .. code-block:: c++
 
     setMyPointer("key", Pointer);
     // [...]
-    Type *P = nonNull(getMyPointer("key"));
+    Type *P = checkNotNull(getMyPointer("key"));
 
 Recoverable Errors
 ^^^^^^^^^^^^^^^^^^
diff --git a/llvm/include/llvm/Support/Error.h b/llvm/include/llvm/Support/Error.h
index 8474d54c42bed..b549efa0f2622 100644
--- a/llvm/include/llvm/Support/Error.h
+++ b/llvm/include/llvm/Support/Error.h
@@ -814,8 +814,8 @@ T cantFail(Expected<T> ValOrErr, const char *Msg = nullptr) {
 /// Calls llvm_unreachable if Pointer is null, otherwise returns the
 /// pointer as is.
 template <typename T>
-LLVM_ATTRIBUTE_RETURNS_NONNULL T *nonNull(T *Pointer,
-                                          const char *Msg = nullptr) {
+LLVM_ATTRIBUTE_RETURNS_NONNULL T *checkNotNull(T *Pointer,
+                                               const char *Msg = nullptr) {
   if (Pointer)
     return Pointer;
 
diff --git a/llvm/unittests/Support/ErrorTest.cpp b/llvm/unittests/Support/ErrorTest.cpp
index 30e4efd0bc391..fc1ab053056c6 100644
--- a/llvm/unittests/Support/ErrorTest.cpp
+++ b/llvm/unittests/Support/ErrorTest.cpp
@@ -1226,21 +1226,21 @@ TEST(Error, NonNullSuccess) {
   const int *cptr = ptr;
   const int *const ccptr = ptr;
 
-  EXPECT_EQ(ptr, nonNull(&tmp));
-  EXPECT_EQ(ptr, nonNull(ptr));
-  EXPECT_EQ(ptr, nonNull(cptr));
-  EXPECT_EQ(ptr, nonNull(ccptr));
+  EXPECT_EQ(ptr, checkNotNull(&tmp));
+  EXPECT_EQ(ptr, checkNotNull(ptr));
+  EXPECT_EQ(ptr, checkNotNull(cptr));
+  EXPECT_EQ(ptr, checkNotNull(ccptr));
 }
 
 #if !defined(NDEBUG) && GTEST_HAS_DEATH_TEST
 TEST(Error, NonNullFails) {
   int *NullPtr = nullptr;
-  EXPECT_DEATH(nonNull(NullPtr),
+  EXPECT_DEATH(checkNotNull(NullPtr),
                "Expected a non-null pointer but got a null pointer")
-      << "nonNull(NullPtr) did not cause an abort for null pointer";
+      << "checkNotNull(NullPtr) did not cause an abort for null pointer";
 
-  EXPECT_DEATH(nonNull(NullPtr, "custom message"), "custom message")
-      << "nonNull(nullptr) did not cause an abort for null pointer";
+  EXPECT_DEATH(checkNotNull(NullPtr, "custom message"), "custom message")
+      << "checkNotNull(nullptr) did not cause an abort for null pointer";
 }
 #endif
 

>From 172ae096370d8937d44c0b05b7570ea78f13b077 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nathan=20Gau=C3=ABr?= <brioche at google.com>
Date: Thu, 26 Mar 2026 16:48:28 +0100
Subject: [PATCH 6/8] pr-feedback

---
 llvm/include/llvm/Support/Error.h    | 15 ++++++++-------
 llvm/unittests/Support/ErrorTest.cpp |  9 +++++++--
 2 files changed, 15 insertions(+), 9 deletions(-)

diff --git a/llvm/include/llvm/Support/Error.h b/llvm/include/llvm/Support/Error.h
index b549efa0f2622..7a1aa36d2e866 100644
--- a/llvm/include/llvm/Support/Error.h
+++ b/llvm/include/llvm/Support/Error.h
@@ -813,14 +813,15 @@ T cantFail(Expected<T> ValOrErr, const char *Msg = nullptr) {
 
 /// Calls llvm_unreachable if Pointer is null, otherwise returns the
 /// pointer as is.
-template <typename T>
-LLVM_ATTRIBUTE_RETURNS_NONNULL T *checkNotNull(T *Pointer,
-                                               const char *Msg = nullptr) {
-  if (Pointer)
+template <typename T, typename = std::enable_if_t<
+                          std::is_pointer_v<T> ||
+                          std::is_constructible_v<T, std::nullptr_t>>>
+[[nodiscard]] T checkNotNull(
+    T Pointer,
+    const char *Msg = "Expected a non-null pointer but got a null pointer") {
+  assert(Msg);
+  if (Pointer != nullptr)
     return Pointer;
-
-  if (!Msg)
-    Msg = "Expected a non-null pointer but got a null pointer";
   llvm_unreachable(Msg);
 }
 
diff --git a/llvm/unittests/Support/ErrorTest.cpp b/llvm/unittests/Support/ErrorTest.cpp
index fc1ab053056c6..9d43280550045 100644
--- a/llvm/unittests/Support/ErrorTest.cpp
+++ b/llvm/unittests/Support/ErrorTest.cpp
@@ -1235,11 +1235,16 @@ TEST(Error, NonNullSuccess) {
 #if !defined(NDEBUG) && GTEST_HAS_DEATH_TEST
 TEST(Error, NonNullFails) {
   int *NullPtr = nullptr;
-  EXPECT_DEATH(checkNotNull(NullPtr),
+  EXPECT_DEATH(NullPtr = checkNotNull(NullPtr),
                "Expected a non-null pointer but got a null pointer")
       << "checkNotNull(NullPtr) did not cause an abort for null pointer";
 
-  EXPECT_DEATH(checkNotNull(NullPtr, "custom message"), "custom message")
+  EXPECT_DEATH(NullPtr = checkNotNull(nullptr),
+               "Expected a non-null pointer but got a null pointer")
+      << "checkNotNull(NullPtr) did not cause an abort for null pointer";
+
+  EXPECT_DEATH(NullPtr = checkNotNull(NullPtr, "custom message"),
+               "custom message")
       << "checkNotNull(nullptr) did not cause an abort for null pointer";
 }
 #endif

>From 2c6e53476c2031489e0ed0a2371945d2f8eeee46 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nathan=20Gau=C3=ABr?= <brioche at google.com>
Date: Fri, 27 Mar 2026 14:31:55 +0100
Subject: [PATCH 7/8] pr-feedback: support unique+mlir value

---
 llvm/include/llvm/Support/Error.h    | 11 +++++------
 llvm/unittests/Support/ErrorTest.cpp |  8 ++++++++
 2 files changed, 13 insertions(+), 6 deletions(-)

diff --git a/llvm/include/llvm/Support/Error.h b/llvm/include/llvm/Support/Error.h
index 7a1aa36d2e866..8494fc1c97287 100644
--- a/llvm/include/llvm/Support/Error.h
+++ b/llvm/include/llvm/Support/Error.h
@@ -813,15 +813,14 @@ T cantFail(Expected<T> ValOrErr, const char *Msg = nullptr) {
 
 /// Calls llvm_unreachable if Pointer is null, otherwise returns the
 /// pointer as is.
-template <typename T, typename = std::enable_if_t<
-                          std::is_pointer_v<T> ||
-                          std::is_constructible_v<T, std::nullptr_t>>>
-[[nodiscard]] T checkNotNull(
-    T Pointer,
+template <typename T, typename = std::enable_if_t<std::is_convertible_v<
+                          decltype(std::declval<T>() != nullptr), bool>>>
+[[nodiscard]] T &&checkNotNull(
+    T &&Pointer,
     const char *Msg = "Expected a non-null pointer but got a null pointer") {
   assert(Msg);
   if (Pointer != nullptr)
-    return Pointer;
+    return std::forward<T>(Pointer);
   llvm_unreachable(Msg);
 }
 
diff --git a/llvm/unittests/Support/ErrorTest.cpp b/llvm/unittests/Support/ErrorTest.cpp
index 9d43280550045..b242ac7823977 100644
--- a/llvm/unittests/Support/ErrorTest.cpp
+++ b/llvm/unittests/Support/ErrorTest.cpp
@@ -1225,11 +1225,13 @@ TEST(Error, NonNullSuccess) {
   int *ptr = &tmp;
   const int *cptr = ptr;
   const int *const ccptr = ptr;
+  auto uptr = std::make_unique<int>(0);
 
   EXPECT_EQ(ptr, checkNotNull(&tmp));
   EXPECT_EQ(ptr, checkNotNull(ptr));
   EXPECT_EQ(ptr, checkNotNull(cptr));
   EXPECT_EQ(ptr, checkNotNull(ccptr));
+  EXPECT_EQ(uptr, checkNotNull(std::move(uptr)));
 }
 
 #if !defined(NDEBUG) && GTEST_HAS_DEATH_TEST
@@ -1243,6 +1245,12 @@ TEST(Error, NonNullFails) {
                "Expected a non-null pointer but got a null pointer")
       << "checkNotNull(NullPtr) did not cause an abort for null pointer";
 
+  auto UniquePtr = std::make_unique<int>(0);
+  UniquePtr.release();
+  EXPECT_DEATH(auto TmpUniquePtr = checkNotNull(std::move(UniquePtr)),
+               "Expected a non-null pointer but got a null pointer")
+      << "checkNotNull(NullPtr) did not cause an abort for null pointer";
+
   EXPECT_DEATH(NullPtr = checkNotNull(NullPtr, "custom message"),
                "custom message")
       << "checkNotNull(nullptr) did not cause an abort for null pointer";

>From 0b7bbbbdc94ac7e83e6993956e4ed8ccef2ef036 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nathan=20Gau=C3=ABr?= <github at keenuts.net>
Date: Mon, 30 Mar 2026 15:16:21 +0200
Subject: [PATCH 8/8] Update llvm/include/llvm/Support/Error.h

Co-authored-by: Jakub Kuderski <kubakuderski at gmail.com>
---
 llvm/include/llvm/Support/Error.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/include/llvm/Support/Error.h b/llvm/include/llvm/Support/Error.h
index 8494fc1c97287..5babee4fb3c36 100644
--- a/llvm/include/llvm/Support/Error.h
+++ b/llvm/include/llvm/Support/Error.h
@@ -815,7 +815,7 @@ T cantFail(Expected<T> ValOrErr, const char *Msg = nullptr) {
 /// pointer as is.
 template <typename T, typename = std::enable_if_t<std::is_convertible_v<
                           decltype(std::declval<T>() != nullptr), bool>>>
-[[nodiscard]] T &&checkNotNull(
+[[nodiscard]] decltype(auto) checkNotNull(
     T &&Pointer,
     const char *Msg = "Expected a non-null pointer but got a null pointer") {
   assert(Msg);



More information about the llvm-commits mailing list