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

Nathan Gauër via llvm-commits llvm-commits at lists.llvm.org
Thu Mar 26 05:02:50 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/4] [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/4] 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/4] 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/4] 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),



More information about the llvm-commits mailing list