aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonathan Wakely <jwakely@redhat.com>2022-04-01 12:25:02 +0100
committerJonathan Wakely <jwakely@redhat.com>2022-04-01 13:17:18 +0100
commitbabaabbcc8346758be0051b32272da18d54f5eea (patch)
tree1a63a98ad862f3fa05fd8e12882a37795f4db7ce
parent944da70a5d1cdc5bd4327b2d32420f57b6883985 (diff)
downloadgcc-babaabbcc8346758be0051b32272da18d54f5eea.zip
gcc-babaabbcc8346758be0051b32272da18d54f5eea.tar.gz
gcc-babaabbcc8346758be0051b32272da18d54f5eea.tar.bz2
libstdc++: Implement std::unreachable() for C++23 (P0627R6)
This defines std::unreachable as an assertion for debug mode, a trap when _GLIBCXX_ASSERTIONS is defined, and __builtin_unreachable() otherwise. The reason for only using __builtin_trap() in the second case is to avoid the overhead of setting up a call to __glibcxx_assert_fail that should never happen. UBsan can detect if __builtin_unreachable() is executed, so if a feature test macro for that sanitizer is added, we could change just use __builtin_unreachable() when the sanitizer is enabled. While thinking about what the debug assertion failure should print, I noticed that the __glibcxx_assert_fail function doesn't check for null pointers. This adds a check so we don't try to print them if null. libstdc++-v3/ChangeLog: * include/std/utility (unreachable): Define for C++23. * include/std/version (__cpp_lib_unreachable): Define. * src/c++11/debug.cc (__glibcxx_assert_fail): Check for valid arguments. Handle only the function being given. * testsuite/20_util/unreachable/1.cc: New test. * testsuite/20_util/unreachable/version.cc: New test.
-rw-r--r--libstdc++-v3/include/std/utility26
-rw-r--r--libstdc++-v3/include/std/version1
-rw-r--r--libstdc++-v3/src/c++11/debug.cc7
-rw-r--r--libstdc++-v3/testsuite/20_util/unreachable/1.cc17
-rw-r--r--libstdc++-v3/testsuite/20_util/unreachable/version.cc10
5 files changed, 59 insertions, 2 deletions
diff --git a/libstdc++-v3/include/std/utility b/libstdc++-v3/include/std/utility
index 0d7f895..ad5faa5 100644
--- a/libstdc++-v3/include/std/utility
+++ b/libstdc++-v3/include/std/utility
@@ -186,6 +186,32 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
constexpr underlying_type_t<_Tp>
to_underlying(_Tp __value) noexcept
{ return static_cast<underlying_type_t<_Tp>>(__value); }
+
+#define __cpp_lib_unreachable 202202L
+ /// Informs the compiler that program control flow never reaches this point.
+ /**
+ * Evaluating a call to this function results in undefined behaviour.
+ * This can be used as an assertion informing the compiler that certain
+ * conditions are impossible, for when the compiler is unable to determine
+ * that by itself.
+ *
+ * For example, it can be used to prevent warnings about reaching the
+ * end of a non-void function without returning.
+ *
+ * @since C++23
+ */
+ [[noreturn,__gnu__::__always_inline__]]
+ inline void
+ unreachable()
+ {
+#ifdef _GLIBCXX_DEBUG
+ std::__glibcxx_assert_fail(nullptr, 0, "std::unreachable()", nullptr);
+#elif defined _GLIBCXX_ASSERTIONS
+ __builtin_trap();
+#else
+ __builtin_unreachable();
+#endif
+ }
#endif // C++23
#endif // C++20
#endif // C++17
diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version
index 44b8a9f..51f2110 100644
--- a/libstdc++-v3/include/std/version
+++ b/libstdc++-v3/include/std/version
@@ -326,6 +326,7 @@
# define __cpp_lib_string_resize_and_overwrite 202110L
#endif
#define __cpp_lib_to_underlying 202102L
+#define __cpp_lib_unreachable 202202L
#endif
#endif // C++2b
#endif // C++20
diff --git a/libstdc++-v3/src/c++11/debug.cc b/libstdc++-v3/src/c++11/debug.cc
index 98fe2dc..4706def 100644
--- a/libstdc++-v3/src/c++11/debug.cc
+++ b/libstdc++-v3/src/c++11/debug.cc
@@ -52,8 +52,11 @@ namespace std
__glibcxx_assert_fail(const char* file, int line,
const char* function, const char* condition) noexcept
{
- fprintf(stderr, "%s:%d: %s: Assertion '%s' failed.\n",
- file, line, function, condition);
+ if (file && function && condition)
+ fprintf(stderr, "%s:%d: %s: Assertion '%s' failed.\n",
+ file, line, function, condition);
+ else if (function)
+ fprintf(stderr, "%s: Undefined behavior detected.\n", function);
abort();
}
}
diff --git a/libstdc++-v3/testsuite/20_util/unreachable/1.cc b/libstdc++-v3/testsuite/20_util/unreachable/1.cc
new file mode 100644
index 0000000..0c463d5
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/unreachable/1.cc
@@ -0,0 +1,17 @@
+// { dg-options "-std=gnu++23" }
+// { dg-do compile { target c++23 } }
+
+#include <utility>
+
+#ifndef __cpp_lib_unreachable
+# error "Feature-test macro for unreachable missing in <utility>"
+#elif __cpp_lib_unreachable != 202202L
+# error "Feature-test macro for unreachable has wrong value in <utility>"
+#endif
+
+bool test01(int i)
+{
+ if (i == 4)
+ return true;
+ std::unreachable();
+} // { dg-bogus "control reaches end of non-void function" }
diff --git a/libstdc++-v3/testsuite/20_util/unreachable/version.cc b/libstdc++-v3/testsuite/20_util/unreachable/version.cc
new file mode 100644
index 0000000..c779590
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/unreachable/version.cc
@@ -0,0 +1,10 @@
+// { dg-options "-std=gnu++23" }
+// { dg-do preprocess { target c++23 } }
+
+#include <version>
+
+#ifndef __cpp_lib_unreachable
+# error "Feature-test macro for unreachable missing in <version>"
+#elif __cpp_lib_unreachable != 202202L
+# error "Feature-test macro for unreachable has wrong value in <version>"
+#endif