diff --git a/CMakeLists.txt b/CMakeLists.txt
index 1676ee79..4b4e4f58 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -34,7 +34,7 @@ if(CMAKE_BINARY_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
endif()
project(Catch2
- VERSION 3.9.0 # CML version placeholder, don't delete
+ VERSION 3.9.1 # CML version placeholder, don't delete
LANGUAGES CXX
HOMEPAGE_URL "https://github.com/catchorg/Catch2"
DESCRIPTION "A modern, C++-native, unit test framework."
diff --git a/docs/release-notes.md b/docs/release-notes.md
index b8b973c9..6f9ee6a1 100644
--- a/docs/release-notes.md
+++ b/docs/release-notes.md
@@ -2,6 +2,7 @@
# Release notes
**Contents**
+[3.9.1](#391)
[3.9.0](#390)
[3.8.1](#381)
[3.8.0](#380)
@@ -68,6 +69,19 @@
[Even Older versions](#even-older-versions)
+## 3.9.1
+
+### Fixes
+* Fixed bad error reporting for multiple nested assertions (#1292)
+* Fixed W4702 (unreachable code) in the polyfill for std::unreachable (#3007)
+* Fixed decomposition of assertions comparing enum-backed bitfields (#3001)
+* Fixed StringMaker specialization for `time_point` with non-default duration type (#2685)
+
+### Improvements
+* Exceptions thrown during stringification of decomposed expression no longer fail the assertion (#2980)
+* The selection logic for `CATCH_TRAP` prefers `__builtin_debugtrap` on all platforms when Catch2 is compiled with Clang
+
+
## 3.9.0
### Improvements
diff --git a/extras/catch_amalgamated.cpp b/extras/catch_amalgamated.cpp
index 3a9cd757..79ec8d1a 100644
--- a/extras/catch_amalgamated.cpp
+++ b/extras/catch_amalgamated.cpp
@@ -6,8 +6,8 @@
// SPDX-License-Identifier: BSL-1.0
-// Catch v3.9.0
-// Generated: 2025-07-24 22:00:25.173359
+// Catch v3.9.1
+// Generated: 2025-08-09 00:29:21.552225
// ----------------------------------------------------------
// This file is an amalgamation of multiple different files.
// You probably shouldn't edit it directly.
@@ -2026,6 +2026,13 @@ namespace Detail {
rss << std::setw(2) << static_cast(bytes[i]);
return rss.str();
}
+
+ std::string makeExceptionHappenedString() {
+ return "{ stringification failed with an exception: \"" +
+ translateActiveException() + "\" }";
+
+ }
+
} // end Detail namespace
@@ -2271,7 +2278,7 @@ namespace Catch {
}
Version const& libraryVersion() {
- static Version version( 3, 9, 0, "", 0 );
+ static Version version( 3, 9, 1, "", 0 );
return version;
}
@@ -3981,10 +3988,10 @@ namespace Catch {
// To avoid having to handle TFE explicitly everywhere, we just
// rethrow it so that it goes back up the caller.
catch( TestFailureException& ) {
- std::rethrow_exception(std::current_exception());
+ return "{ nested assertion failed }";
}
catch( TestSkipException& ) {
- std::rethrow_exception(std::current_exception());
+ return "{ nested SKIP() called }";
}
catch( std::exception const& ex ) {
return ex.what();
diff --git a/extras/catch_amalgamated.hpp b/extras/catch_amalgamated.hpp
index 608a184b..7e331cc8 100644
--- a/extras/catch_amalgamated.hpp
+++ b/extras/catch_amalgamated.hpp
@@ -6,8 +6,8 @@
// SPDX-License-Identifier: BSL-1.0
-// Catch v3.9.0
-// Generated: 2025-07-24 22:00:24.654688
+// Catch v3.9.1
+// Generated: 2025-08-09 00:29:20.303175
// ----------------------------------------------------------
// This file is an amalgamation of multiple different files.
// You probably shouldn't edit it directly.
@@ -958,7 +958,7 @@ namespace Detail {
}
explicit operator bool() const {
- return m_ptr;
+ return m_ptr != nullptr;
}
friend void swap(unique_ptr& lhs, unique_ptr& rhs) {
@@ -2151,9 +2151,7 @@ namespace Catch {
auto analysis = Detail::analyse(*cfg, samples.data(), samples.data() + samples.size());
BenchmarkStats<> stats{ CATCH_MOVE(info), CATCH_MOVE(analysis.samples), analysis.mean, analysis.standard_deviation, analysis.outliers, analysis.outlier_variance };
getResultCapture().benchmarkEnded(stats);
- } CATCH_CATCH_ANON (TestFailureException const&) {
- getResultCapture().benchmarkFailed("Benchmark failed due to failed assertion"_sr);
- } CATCH_CATCH_ALL{
+ } CATCH_CATCH_ALL {
getResultCapture().benchmarkFailed(translateActiveException());
// We let the exception go further up so that the
// test case is marked as failed.
@@ -2566,11 +2564,17 @@ namespace Catch {
namespace Detail {
+ std::string makeExceptionHappenedString();
+
// This function dispatches all stringification requests inside of Catch.
// Should be preferably called fully qualified, like ::Catch::Detail::stringify
template
- std::string stringify(const T& e) {
- return ::Catch::StringMaker>>::convert(e);
+ std::string stringify( const T& e ) {
+ CATCH_TRY {
+ return ::Catch::StringMaker<
+ std::remove_cv_t>>::convert( e );
+ }
+ CATCH_CATCH_ALL { return makeExceptionHappenedString(); }
}
template
@@ -3053,17 +3057,19 @@ struct ratio_string {
template
struct StringMaker> {
static std::string convert(std::chrono::time_point const& time_point) {
- auto converted = std::chrono::system_clock::to_time_t(time_point);
+ const auto systemish = std::chrono::time_point_cast<
+ std::chrono::system_clock::duration>( time_point );
+ const auto as_time_t = std::chrono::system_clock::to_time_t( systemish );
#ifdef _MSC_VER
std::tm timeInfo = {};
- const auto err = gmtime_s(&timeInfo, &converted);
+ const auto err = gmtime_s( &timeInfo, &as_time_t );
if ( err ) {
return "gmtime from provided timepoint has failed. This "
"happens e.g. with pre-1970 dates using Microsoft libc";
}
#else
- std::tm* timeInfo = std::gmtime(&converted);
+ std::tm* timeInfo = std::gmtime( &as_time_t );
#endif
auto const timeStampSize = sizeof("2017-01-16T17:06:45Z");
@@ -5238,10 +5244,11 @@ namespace Detail {
*
* 3) If a type has no linkage, we also cannot capture it by reference.
* The solution is once again to capture them by value. We handle
- * the common cases by using `std::is_arithmetic` as the default
- * for `Catch::capture_by_value`, but that is only a some-effort
- * heuristic. But as with 2), users can specialize `capture_by_value`
- * for their own types as needed.
+ * the common cases by using `std::is_arithmetic` and `std::is_enum`
+ * as the default for `Catch::capture_by_value`, but that is only a
+ * some-effort heuristic. These combine to capture all possible bitfield
+ * bases, and also some trait-like types. As with 2), users can
+ * specialize `capture_by_value` for their own types as needed.
*
* 4) To support C++20 and make the SFINAE on our decomposing operators
* work, the SFINAE has to happen in return type, rather than in
@@ -5293,13 +5300,22 @@ namespace Catch {
using RemoveCVRef_t = std::remove_cv_t>;
}
- // Note: There is nothing that stops us from extending this,
- // e.g. to `std::is_scalar`, but the more encompassing
- // traits are usually also more expensive. For now we
- // keep this as it used to be and it can be changed later.
+ // Note: This is about as much as we can currently reasonably support.
+ // In an ideal world, we could capture by value small trivially
+ // copyable types, but the actual `std::is_trivially_copyable`
+ // trait is a huge mess with standard-violating results on
+ // GCC and Clang, which are unlikely to be fixed soon due to ABI
+ // concerns.
+ // `std::is_scalar` also causes issues due to the `is_pointer`
+ // component, which causes ambiguity issues with (references-to)
+ // function pointer. If those are resolved, we still need to
+ // disambiguate the overload set for arrays, through explicit
+ // overload for references to sized arrays.
template
struct capture_by_value
- : std::integral_constant{}> {};
+ : std::integral_constant::value ||
+ std::is_enum::value> {};
#if defined( CATCH_CONFIG_CPP20_COMPARE_OVERLOADS )
template <>
@@ -6241,9 +6257,13 @@ namespace Catch {
__assume( false );
# elif defined( __GNUC__ )
__builtin_unreachable();
-# endif
-# endif // ^^ NDEBUG
+# else // vv platform without known optimization hint
std::terminate();
+# endif
+# else // ^^ NDEBUG
+ // For non-release builds, we prefer termination on bug over UB
+ std::terminate();
+# endif //
}
} // namespace Detail
@@ -7447,7 +7467,7 @@ namespace Catch {
#define CATCH_VERSION_MAJOR 3
#define CATCH_VERSION_MINOR 9
-#define CATCH_VERSION_PATCH 0
+#define CATCH_VERSION_PATCH 1
#endif // CATCH_VERSION_MACROS_HPP_INCLUDED
@@ -9500,6 +9520,17 @@ namespace Catch {
bool isDebuggerActive();
}
+#if !defined( CATCH_TRAP ) && defined( __clang__ ) && defined( __has_builtin )
+# if __has_builtin( __builtin_debugtrap )
+# define CATCH_TRAP() __builtin_debugtrap()
+# endif
+#endif
+
+#if !defined( CATCH_TRAP ) && defined( _MSC_VER )
+# define CATCH_TRAP() __debugbreak()
+#endif
+
+#if !defined(CATCH_TRAP) // If we couldn't use compiler-specific impl from above, we get into platform-specific options
#ifdef CATCH_PLATFORM_MAC
#if defined(__i386__) || defined(__x86_64__)
@@ -9535,15 +9566,15 @@ namespace Catch {
#define CATCH_TRAP() raise(SIGTRAP)
#endif
-#elif defined(_MSC_VER)
- #define CATCH_TRAP() __debugbreak()
#elif defined(__MINGW32__)
extern "C" __declspec(dllimport) void __stdcall DebugBreak();
#define CATCH_TRAP() DebugBreak()
#endif
+#endif // ^^ CATCH_TRAP is not defined yet, so we define it
-#ifndef CATCH_BREAK_INTO_DEBUGGER
- #ifdef CATCH_TRAP
+
+#if !defined(CATCH_BREAK_INTO_DEBUGGER)
+ #if defined(CATCH_TRAP)
#define CATCH_BREAK_INTO_DEBUGGER() []{ if( Catch::isDebuggerActive() ) { CATCH_TRAP(); } }()
#else
#define CATCH_BREAK_INTO_DEBUGGER() []{}()
diff --git a/meson.build b/meson.build
index 021266d8..3e8abc5a 100644
--- a/meson.build
+++ b/meson.build
@@ -8,7 +8,7 @@
project(
'catch2',
'cpp',
- version: '3.9.0', # CML version placeholder, don't delete
+ version: '3.9.1', # CML version placeholder, don't delete
license: 'BSL-1.0',
meson_version: '>=0.54.1',
)
diff --git a/src/catch2/catch_version.cpp b/src/catch2/catch_version.cpp
index a012101e..8c9d1c7f 100644
--- a/src/catch2/catch_version.cpp
+++ b/src/catch2/catch_version.cpp
@@ -36,7 +36,7 @@ namespace Catch {
}
Version const& libraryVersion() {
- static Version version( 3, 9, 0, "", 0 );
+ static Version version( 3, 9, 1, "", 0 );
return version;
}
diff --git a/src/catch2/catch_version_macros.hpp b/src/catch2/catch_version_macros.hpp
index a795e4ba..eac8d0e5 100644
--- a/src/catch2/catch_version_macros.hpp
+++ b/src/catch2/catch_version_macros.hpp
@@ -10,6 +10,6 @@
#define CATCH_VERSION_MAJOR 3
#define CATCH_VERSION_MINOR 9
-#define CATCH_VERSION_PATCH 0
+#define CATCH_VERSION_PATCH 1
#endif // CATCH_VERSION_MACROS_HPP_INCLUDED