diff options
author | Jonathan Wakely <jwakely@redhat.com> | 2022-04-01 12:25:02 +0100 |
---|---|---|
committer | Jonathan Wakely <jwakely@redhat.com> | 2022-04-01 13:17:18 +0100 |
commit | babaabbcc8346758be0051b32272da18d54f5eea (patch) | |
tree | 1a63a98ad862f3fa05fd8e12882a37795f4db7ce | |
parent | 944da70a5d1cdc5bd4327b2d32420f57b6883985 (diff) | |
download | gcc-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/utility | 26 | ||||
-rw-r--r-- | libstdc++-v3/include/std/version | 1 | ||||
-rw-r--r-- | libstdc++-v3/src/c++11/debug.cc | 7 | ||||
-rw-r--r-- | libstdc++-v3/testsuite/20_util/unreachable/1.cc | 17 | ||||
-rw-r--r-- | libstdc++-v3/testsuite/20_util/unreachable/version.cc | 10 |
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 |