]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
libstdc++: Implement std::unreachable() for C++23 (P0627R6)
authorJonathan Wakely <jwakely@redhat.com>
Fri, 1 Apr 2022 11:25:02 +0000 (12:25 +0100)
committerJonathan Wakely <jwakely@redhat.com>
Fri, 1 Apr 2022 12:17:18 +0000 (13:17 +0100)
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.

libstdc++-v3/include/std/utility
libstdc++-v3/include/std/version
libstdc++-v3/src/c++11/debug.cc
libstdc++-v3/testsuite/20_util/unreachable/1.cc [new file with mode: 0644]
libstdc++-v3/testsuite/20_util/unreachable/version.cc [new file with mode: 0644]

index 0d7f8954c5ae7219c3cf1d091ad9a058fb1699a1..ad5faa50f57306af581e1466d35cd03928704579 100644 (file)
@@ -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
index 44b8a9f88b538c31d7d121999e1d0a1a8f40f51a..51f2110b68ef1f8cfa9462115649be7ef150aa6c 100644 (file)
 # 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
index 98fe2dcc153a8d662ac7ade6652f6cceb6d8752d..4706defedf153962e36074eba9d425299b45c595 100644 (file)
@@ -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 (file)
index 0000000..0c463d5
--- /dev/null
@@ -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 (file)
index 0000000..c779590
--- /dev/null
@@ -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