[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