[libc++] Improve the implementation of std::unreachable
authorLouis Dionne <ldionne.2@gmail.com>
Wed, 10 Aug 2022 21:27:00 +0000 (17:27 -0400)
committerLouis Dionne <ldionne.2@gmail.com>
Wed, 11 Jan 2023 15:15:25 +0000 (10:15 -0500)
First, use __builtin_unreachable unconditionally. It is implemented by
all the compilers that we support. Clang started supporting it around
Clang 4, and GCC around GCC 4.10.

Also add _LIBCPP_ASSERT so that we will actually get a guaranteed crash
if we reached `std::unreachable()` and assertions have been enabled,
since that's UB that's extremely easy to catch.

Differential Revision: https://reviews.llvm.org/D131620

libcxx/include/__utility/unreachable.h
libcxx/include/utility
libcxx/test/libcxx/transitive_includes/cxx2b.csv
libcxx/test/std/utilities/utility/utility.unreachable/assert.unreachable.pass.cpp [new file with mode: 0644]
libcxx/test/std/utilities/utility/utility.unreachable/unreachable.compile.pass.cpp [deleted file]
libcxx/test/std/utilities/utility/utility.unreachable/unreachable.pass.cpp [new file with mode: 0644]

index 485edb227c92ce28648b9adabee923fbf9ae8595..d93e60b10b8694b9d3cadc3e82ca28ff3aa235aa 100644 (file)
@@ -9,8 +9,8 @@
 #ifndef _LIBCPP___UTILITY_UNREACHABLE_H
 #define _LIBCPP___UTILITY_UNREACHABLE_H
 
+#include <__assert>
 #include <__config>
-#include <cstdlib>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-_LIBCPP_NORETURN _LIBCPP_HIDE_FROM_ABI inline void __libcpp_unreachable()
-{
-#if __has_builtin(__builtin_unreachable)
-  __builtin_unreachable();
-#else
-  std::abort();
-#endif
+_LIBCPP_NORETURN _LIBCPP_HIDE_FROM_ABI inline void __libcpp_unreachable() {
+    _LIBCPP_ASSERT(false, "std::unreachable() was reached");
+    __builtin_unreachable();
 }
 
 #if _LIBCPP_STD_VER > 20
 
 [[noreturn]] _LIBCPP_HIDE_FROM_ABI inline void unreachable() { __libcpp_unreachable(); }
 
-#endif // _LIBCPP_STD_VER > 20
+#endif
 
 _LIBCPP_END_NAMESPACE_STD
 
-#endif
+#endif // _LIBCPP___UTILITY_UNREACHABLE_H
index 4de69f7bdea85f3f6b5f4f6a1005e6eae9ba9e0a..1d56759310f92783f7dbdd658dedb28b40373433 100644 (file)
@@ -274,6 +274,7 @@ template <class T>
 #endif
 
 #if !defined(_LIBCPP_REMOVE_TRANSITIVE_INCLUDES) && _LIBCPP_STD_VER <= 20
+#  include <cstdlib>
 #  include <iosfwd>
 #  include <type_traits>
 #endif
index 3f98abd77120cc19ad1689682b4aa196f8d56dbb..0e6f3780b8675b54430767a49b33a60bc586968e 100644 (file)
@@ -693,7 +693,6 @@ unordered_set type_traits
 unordered_set version
 utility compare
 utility cstddef
-utility cstdlib
 utility initializer_list
 utility limits
 utility version
diff --git a/libcxx/test/std/utilities/utility/utility.unreachable/assert.unreachable.pass.cpp b/libcxx/test/std/utilities/utility/utility.unreachable/assert.unreachable.pass.cpp
new file mode 100644 (file)
index 0000000..cfb3606
--- /dev/null
@@ -0,0 +1,25 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// REQUIRES: has-unix-headers
+// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx{{10.9|10.10|10.11|10.12|10.13|10.14|10.15|11.0|12.0}}
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_ASSERTIONS=1
+
+// Make sure that reaching std::unreachable() with assertions enabled triggers an assertion.
+
+#include <utility>
+
+#include "check_assertion.h"
+
+int main(int, char**) {
+    TEST_LIBCPP_ASSERT_FAILURE(std::unreachable(), "std::unreachable() was reached");
+
+    return 0;
+}
diff --git a/libcxx/test/std/utilities/utility/utility.unreachable/unreachable.compile.pass.cpp b/libcxx/test/std/utilities/utility/utility.unreachable/unreachable.compile.pass.cpp
deleted file mode 100644 (file)
index 017fa30..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
-
-#include <utility>
-#include <type_traits>
-
-static_assert(std::is_same_v<decltype(std::unreachable()), void>);
diff --git a/libcxx/test/std/utilities/utility/utility.unreachable/unreachable.pass.cpp b/libcxx/test/std/utilities/utility/utility.unreachable/unreachable.pass.cpp
new file mode 100644 (file)
index 0000000..290c61f
--- /dev/null
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// Make sure we can use `std::unreachable()`. We can't actually execute it cause that would be
+// UB, but we can make sure that it doesn't cause a linker error or something like that.
+
+#include <cassert>
+#include <type_traits>
+#include <utility>
+
+int main(int argc, char**) {
+    assert(argc == 1);
+    if (argc != 1) {
+        std::unreachable();
+    }
+
+    static_assert(std::is_same_v<decltype(std::unreachable()), void>);
+
+    return 0;
+}