From 4f14c8e86aa4bb98627f988b434085e971f29179 Mon Sep 17 00:00:00 2001 From: Mikhail Borisov Date: Mon, 19 Jan 2026 15:51:00 +0300 Subject: [PATCH 1/6] Explicitly ignore getchar() result Some toolchains (e.g. recent clang/libcxx) issue "unused result" warnings for getchar. --- include/boost/test/impl/unit_test_main.ipp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/include/boost/test/impl/unit_test_main.ipp b/include/boost/test/impl/unit_test_main.ipp index 2fedecea44..b52f3dda77 100644 --- a/include/boost/test/impl/unit_test_main.ipp +++ b/include/boost/test/impl/unit_test_main.ipp @@ -212,7 +212,9 @@ unit_test_main( init_unit_test_func init_func, int argc, char* argv[] ) // getchar is defined as a macro in uClibc. Use parenthesis to fix // gcc bug 58952 for gcc <= 4.8.2. - (std::getchar)(); + int ch = (std::getchar)(); + boost::ignore_unused(ch); + results_reporter::get_stream() << "Continuing..." << std::endl; } From fd4d83033fe635aed5ea1bbf10d8efa7f79bbd4a Mon Sep 17 00:00:00 2001 From: Andrey Semashev Date: Thu, 22 Jan 2026 19:58:16 +0300 Subject: [PATCH 2/6] Remove dependencies on Boost.StaticAssert. Boost.StaticAssert has been merged into Boost.Config, so remove the dependency. --- CMakeLists.txt | 1 - build.jam | 1 - test/cmake_test/CMakeLists.txt | 1 - 3 files changed, 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e551bc6a2c..af3374b4be 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,7 +25,6 @@ set(_boost_test_dependencies Boost::optional Boost::preprocessor Boost::smart_ptr - Boost::static_assert Boost::type_traits Boost::utility ) diff --git a/build.jam b/build.jam index dd6db1f895..80d7d135af 100644 --- a/build.jam +++ b/build.jam @@ -21,7 +21,6 @@ constant boost_dependencies : /boost/optional//boost_optional /boost/preprocessor//boost_preprocessor /boost/smart_ptr//boost_smart_ptr - /boost/static_assert//boost_static_assert /boost/type_traits//boost_type_traits /boost/utility//boost_utility ; diff --git a/test/cmake_test/CMakeLists.txt b/test/cmake_test/CMakeLists.txt index 7134060718..a4dfa7e329 100644 --- a/test/cmake_test/CMakeLists.txt +++ b/test/cmake_test/CMakeLists.txt @@ -32,7 +32,6 @@ else() optional preprocessor smart_ptr - static_assert type_traits utility From b095c0c161c8e47cad50d36256c538a13164f717 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 29 Jan 2026 08:42:17 -0500 Subject: [PATCH 3/6] Add reproducer test set --- test/Jamfile.v2 | 1 + test/writing-test-ts/github_issue_475.cpp | 15 +++++++++++++++ 2 files changed, 16 insertions(+) create mode 100644 test/writing-test-ts/github_issue_475.cpp diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 33c2bcdfb3..9e8ca58a7c 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -188,6 +188,7 @@ test-suite "writing-test-ts" # [ boost.test-self-test run-fail : writing-test-ts : test-timeout-fail : : : : : : [ requires cxx11_hdr_thread cxx11_hdr_chrono ] ] # [ boost.test-self-test run : writing-test-ts : test-timeout-suite : : : : : : $(requirements_datasets) [ requires cxx11_hdr_thread cxx11_hdr_chrono ] ] # [ boost.test-self-test run-fail : writing-test-ts : test-timeout-suite-fail : : : : : : $(requirements_datasets) [ requires cxx11_hdr_thread cxx11_hdr_chrono ] ] + [ boost.test-self-test run : writing-test-ts : github_issue_475 : : : : : : [ requires cxx17_hdr_optional ] ] ; #_________________________________________________________________________________________________# diff --git a/test/writing-test-ts/github_issue_475.cpp b/test/writing-test-ts/github_issue_475.cpp new file mode 100644 index 0000000000..5959db92bd --- /dev/null +++ b/test/writing-test-ts/github_issue_475.cpp @@ -0,0 +1,15 @@ +// Copyright 2025 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#define BOOST_TEST_MODULE github_issue_475 +#include +#include + +BOOST_TEST_DONT_PRINT_LOG_VALUE(std::optional) + +BOOST_AUTO_TEST_CASE(test1) +{ + std::optional a,b; + BOOST_TEST(a==b); +} From 42db56200992946d336a298ce0b81826f6094b12 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 29 Jan 2026 09:38:55 -0500 Subject: [PATCH 4/6] Add base class to allow for friend functions with std::optional --- include/boost/test/tools/assertion.hpp | 153 +++++++++++++++++++++++-- 1 file changed, 144 insertions(+), 9 deletions(-) diff --git a/include/boost/test/tools/assertion.hpp b/include/boost/test/tools/assertion.hpp index 39eab3b03b..d350707563 100644 --- a/include/boost/test/tools/assertion.hpp +++ b/include/boost/test/tools/assertion.hpp @@ -30,6 +30,12 @@ #include #endif +// GCC < 10 workaround for std::optional comparison ambiguity (see below) +#if defined(__GNUC__) && !defined(__clang__) && (__GNUC__ < 10) && \ + (__cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)) +#include +#endif + #include //____________________________________________________________________________// @@ -38,6 +44,26 @@ namespace boost { namespace test_tools { namespace assertion { +// ************************************************************************** // +// ************** assertion::is_std_optional ************** // +// ************************************************************************** // +// Trait to detect std::optional. Used for GCC < 10 workaround. + +template +struct is_std_optional : std::false_type {}; + +#if defined(__GNUC__) && !defined(__clang__) && (__GNUC__ < 10) && \ + (__cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)) +template +struct is_std_optional> : std::true_type {}; +template +struct is_std_optional&> : std::true_type {}; +template +struct is_std_optional const&> : std::true_type {}; +template +struct is_std_optional&&> : std::true_type {}; +#endif + // ************************************************************************** // // ************** assertion::operators ************** // // ************************************************************************** // @@ -77,7 +103,8 @@ namespace op { #ifndef BOOST_NO_CXX11_DECLTYPE -#define BOOST_TEST_FOR_EACH_CONST_OP(action)\ +// Non-comparison operators (never need SFINAE workaround) +#define BOOST_TEST_FOR_EACH_NONCOMP_OP(action)\ action(->*, MEMP, ->*, MEMP ) \ \ action( * , MUL , * , MUL ) \ @@ -90,15 +117,20 @@ namespace op { action( <<, LSH , << , LSH ) \ action( >>, RSH , >> , RSH ) \ \ - BOOST_TEST_FOR_EACH_COMP_OP(action) \ - \ action( & , BAND, & , BAND ) \ action( ^ , XOR , ^ , XOR ) \ action( | , BOR , | , BOR ) \ /**/ +#define BOOST_TEST_FOR_EACH_CONST_OP(action)\ + BOOST_TEST_FOR_EACH_NONCOMP_OP(action) \ + BOOST_TEST_FOR_EACH_COMP_OP(action) \ +/**/ + #else +#define BOOST_TEST_FOR_EACH_NONCOMP_OP(action) + #define BOOST_TEST_FOR_EACH_CONST_OP(action)\ BOOST_TEST_FOR_EACH_COMP_OP(action) \ /**/ @@ -181,20 +213,93 @@ BOOST_TEST_FOR_EACH_CONST_OP( DEFINE_CONST_OPER ) } // namespace op // ************************************************************************** // -// ************** assertion::expression_base ************** // +// ************** assertion::optional_friends_base ************** // // ************************************************************************** // -// Defines expression operators +// GCC < 10 workaround: Base class that conditionally provides hidden friend +// comparison operators only when ValType is std::optional. Hidden friends are +// found via ADL and as non-templates beat std::optional's template operators. +// See: https://github.com/boostorg/test/issues/475 template class binary_expr; +#if defined(__GNUC__) && !defined(__clang__) && (__GNUC__ < 10) && \ + (__cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)) +#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES + +// Primary template - empty (for non-optional types) +template::value> +struct optional_friends_base {}; + +// Specialization for std::optional types - provides hidden friend operators +template +struct optional_friends_base { + friend binary_expr> + operator==( ExprType lhs, ValType rhs ) + { + return binary_expr>( + std::move(lhs), std::move(rhs)); + } + + friend binary_expr> + operator!=( ExprType lhs, ValType rhs ) + { + return binary_expr>( + std::move(lhs), std::move(rhs)); + } + + friend binary_expr> + operator<( ExprType lhs, ValType rhs ) + { + return binary_expr>( + std::move(lhs), std::move(rhs)); + } + + friend binary_expr> + operator<=( ExprType lhs, ValType rhs ) + { + return binary_expr>( + std::move(lhs), std::move(rhs)); + } + + friend binary_expr> + operator>( ExprType lhs, ValType rhs ) + { + return binary_expr>( + std::move(lhs), std::move(rhs)); + } + + friend binary_expr> + operator>=( ExprType lhs, ValType rhs ) + { + return binary_expr>( + std::move(lhs), std::move(rhs)); + } +}; + +#else // BOOST_NO_CXX11_RVALUE_REFERENCES +template +struct optional_friends_base {}; +#endif + +#else // Not GCC < 10 +template +struct optional_friends_base {}; +#endif // GCC < 10 && C++17 + +// ************************************************************************** // +// ************** assertion::expression_base ************** // +// ************************************************************************** // +// Defines expression operators + template -class expression_base { +class expression_base : public optional_friends_base { public: #ifndef BOOST_NO_CXX11_RVALUE_REFERENCES template struct RhsT : remove_const::type> {}; - + +// Regular operator support (non-comparison operators) #define ADD_OP_SUPPORT( oper, name, _, _i ) \ template \ binary_expr(rhs) ); \ } \ /**/ + +// GCC < 10 workaround: comparison operators with SFINAE to exclude std::optional +// when ValType is also std::optional. The hidden friend operators in the base class +// optional_friends_base will handle the optional==optional case instead. +#if defined(__GNUC__) && !defined(__clang__) && (__GNUC__ < 10) && \ + (__cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)) +#define ADD_OP_SUPPORT_COMP( oper, name, _, _i ) \ + template::value || \ + !is_std_optional::type>::value, int>::type = 0> \ + binary_expr::type> > \ + operator oper( T&& rhs ) \ + { \ + return binary_expr::type> > \ + ( std::forward( \ + *static_cast(this) ), \ + std::forward(rhs) ); \ + } \ +/**/ #else +#define ADD_OP_SUPPORT_COMP ADD_OP_SUPPORT +#endif + +#else // BOOST_NO_CXX11_RVALUE_REFERENCES #define ADD_OP_SUPPORT( oper, name, _, _i ) \ template \ @@ -222,10 +353,14 @@ class expression_base { rhs ); \ } \ /**/ -#endif +#define ADD_OP_SUPPORT_COMP ADD_OP_SUPPORT + +#endif // BOOST_NO_CXX11_RVALUE_REFERENCES - BOOST_TEST_FOR_EACH_CONST_OP( ADD_OP_SUPPORT ) + BOOST_TEST_FOR_EACH_NONCOMP_OP( ADD_OP_SUPPORT ) + BOOST_TEST_FOR_EACH_COMP_OP( ADD_OP_SUPPORT_COMP ) #undef ADD_OP_SUPPORT + #undef ADD_OP_SUPPORT_COMP #ifndef BOOST_NO_CXX11_AUTO_DECLARATIONS // Disabled operators From be5918db08947b5239382915640e679ee4743380 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 29 Jan 2026 09:43:41 -0500 Subject: [PATCH 5/6] Remove unnecessary macros --- include/boost/test/tools/assertion.hpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/include/boost/test/tools/assertion.hpp b/include/boost/test/tools/assertion.hpp index d350707563..ad424cead5 100644 --- a/include/boost/test/tools/assertion.hpp +++ b/include/boost/test/tools/assertion.hpp @@ -224,7 +224,6 @@ template class binary_expr; #if defined(__GNUC__) && !defined(__clang__) && (__GNUC__ < 10) && \ (__cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)) -#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES // Primary template - empty (for non-optional types) template::value> @@ -276,11 +275,6 @@ struct optional_friends_base { } }; -#else // BOOST_NO_CXX11_RVALUE_REFERENCES -template -struct optional_friends_base {}; -#endif - #else // Not GCC < 10 template struct optional_friends_base {}; From 3efd8970899218b96cc37a1ff7fce13e206284da Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 29 Jan 2026 14:51:29 -0500 Subject: [PATCH 6/6] Apply same workaround for clangs < 10 --- include/boost/test/tools/assertion.hpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/include/boost/test/tools/assertion.hpp b/include/boost/test/tools/assertion.hpp index ad424cead5..472b60e221 100644 --- a/include/boost/test/tools/assertion.hpp +++ b/include/boost/test/tools/assertion.hpp @@ -31,7 +31,7 @@ #endif // GCC < 10 workaround for std::optional comparison ambiguity (see below) -#if defined(__GNUC__) && !defined(__clang__) && (__GNUC__ < 10) && \ +#if ((defined(__GNUC__) && !defined(__clang__) && (__GNUC__ < 10)) || (defined(__clang__) && __clang_major__ < 11)) && \ (__cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)) #include #endif @@ -52,8 +52,9 @@ namespace assertion { template struct is_std_optional : std::false_type {}; -#if defined(__GNUC__) && !defined(__clang__) && (__GNUC__ < 10) && \ +#if ((defined(__GNUC__) && !defined(__clang__) && (__GNUC__ < 10)) || (defined(__clang__) && __clang_major__ < 11)) && \ (__cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)) + template struct is_std_optional> : std::true_type {}; template @@ -62,6 +63,7 @@ template struct is_std_optional const&> : std::true_type {}; template struct is_std_optional&&> : std::true_type {}; + #endif // ************************************************************************** // @@ -222,7 +224,7 @@ BOOST_TEST_FOR_EACH_CONST_OP( DEFINE_CONST_OPER ) template class binary_expr; -#if defined(__GNUC__) && !defined(__clang__) && (__GNUC__ < 10) && \ +#if ((defined(__GNUC__) && !defined(__clang__) && (__GNUC__ < 10)) || (defined(__clang__) && __clang_major__ < 11)) && \ (__cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)) // Primary template - empty (for non-optional types) @@ -311,7 +313,7 @@ class expression_base : public optional_friends_base { // GCC < 10 workaround: comparison operators with SFINAE to exclude std::optional // when ValType is also std::optional. The hidden friend operators in the base class // optional_friends_base will handle the optional==optional case instead. -#if defined(__GNUC__) && !defined(__clang__) && (__GNUC__ < 10) && \ +#if ((defined(__GNUC__) && !defined(__clang__) && (__GNUC__ < 10)) || (defined(__clang__) && __clang_major__ < 11)) && \ (__cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)) #define ADD_OP_SUPPORT_COMP( oper, name, _, _i ) \ template