Skip to content

Commit 9e428b7

Browse files
Keenutskuhar
andauthored
[LLVM][Support] add nonNull function helper (llvm#188718)
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. --------- Co-authored-by: Jakub Kuderski <kubakuderski@gmail.com>
1 parent c7c340b commit 9e428b7

File tree

3 files changed

+70
-0
lines changed

3 files changed

+70
-0
lines changed

llvm/docs/ProgrammersManual.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,15 @@ violations even in builds that do not enable assertions:
453453
reportFatalInternalError("Analysis 'foo' not preserved");
454454
}
455455

456+
Additionally, ``checkNotNull`` can be used to check/document that a pointer
457+
is never supposed to be null inline.
458+
459+
.. code-block:: c++
460+
461+
setMyPointer("key", Pointer);
462+
// [...]
463+
Type *P = checkNotNull(getMyPointer("key"));
464+
456465
Recoverable Errors
457466
^^^^^^^^^^^^^^^^^^
458467

llvm/include/llvm/Support/Error.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -811,6 +811,29 @@ T cantFail(Expected<T> ValOrErr, const char *Msg = nullptr) {
811811
}
812812
}
813813

814+
namespace detail {
815+
816+
template <typename T>
817+
using compare_nullptr_t = decltype(std::declval<T &>() == nullptr);
818+
819+
template <typename T>
820+
using is_nullptr_comparable = llvm::is_detected<compare_nullptr_t, T>;
821+
822+
} // namespace detail
823+
824+
/// Calls llvm_unreachable if Pointer is null, otherwise returns the
825+
/// pointer as is.
826+
template <typename T,
827+
typename = std::enable_if_t<detail::is_nullptr_comparable<T>::value>>
828+
[[nodiscard]] decltype(auto) checkNotNull(
829+
T &&Pointer,
830+
const char *Msg = "Expected a non-null pointer but got a null pointer") {
831+
assert(Msg);
832+
if (Pointer != nullptr)
833+
return std::forward<T>(Pointer);
834+
llvm_unreachable(Msg);
835+
}
836+
814837
/// Report a fatal error if ValOrErr is a failure value, otherwise unwraps and
815838
/// returns the contained reference.
816839
///

llvm/unittests/Support/ErrorTest.cpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1219,4 +1219,42 @@ TEST(Error, ForwardToExpected) {
12191219
EXPECT_THAT_ERROR(ExpectedReturningFct(false).moveInto(MaybeV), Succeeded());
12201220
EXPECT_EQ(*MaybeV, 42);
12211221
}
1222+
1223+
TEST(Error, NonNullSuccess) {
1224+
int tmp = 0;
1225+
int *ptr = &tmp;
1226+
const int *cptr = ptr;
1227+
const int *const ccptr = ptr;
1228+
auto uptr = std::make_unique<int>(0);
1229+
1230+
EXPECT_EQ(ptr, checkNotNull(&tmp));
1231+
EXPECT_EQ(ptr, checkNotNull(ptr));
1232+
EXPECT_EQ(ptr, checkNotNull(cptr));
1233+
EXPECT_EQ(ptr, checkNotNull(ccptr));
1234+
EXPECT_EQ(uptr, checkNotNull(std::move(uptr)));
1235+
}
1236+
1237+
#if !defined(NDEBUG) && GTEST_HAS_DEATH_TEST
1238+
TEST(Error, NonNullFails) {
1239+
int *NullPtr = nullptr;
1240+
EXPECT_DEATH(NullPtr = checkNotNull(NullPtr),
1241+
"Expected a non-null pointer but got a null pointer")
1242+
<< "checkNotNull(NullPtr) did not cause an abort for null pointer";
1243+
1244+
EXPECT_DEATH(NullPtr = checkNotNull(nullptr),
1245+
"Expected a non-null pointer but got a null pointer")
1246+
<< "checkNotNull(NullPtr) did not cause an abort for null pointer";
1247+
1248+
auto UniquePtr = std::make_unique<int>(0);
1249+
UniquePtr.release();
1250+
EXPECT_DEATH(auto TmpUniquePtr = checkNotNull(std::move(UniquePtr)),
1251+
"Expected a non-null pointer but got a null pointer")
1252+
<< "checkNotNull(NullPtr) did not cause an abort for null pointer";
1253+
1254+
EXPECT_DEATH(NullPtr = checkNotNull(NullPtr, "custom message"),
1255+
"custom message")
1256+
<< "checkNotNull(nullptr) did not cause an abort for null pointer";
1257+
}
1258+
#endif
1259+
12221260
} // namespace

0 commit comments

Comments
 (0)