[test] BOOST_TEST macro and std::max
Hi, I'd expect the following cheks to pass: BOOST_TEST(3u == (std::max)(0u, 3u)); BOOST_TEST(3u == ((std::max)(0u, 3u))); But both fails with: check 3u == ((std::max)(0u, 3u)) has failed [3 != 7095312] Did I miss something regarding BOOST_TEST usage? Platform: Linux, GCC 5.3, Boost 1.60 Thanks, Benedek
Le 05/01/16 00:35, Benedek Thaler a écrit :
Hi,
I'd expect the following cheks to pass:
BOOST_TEST(3u == (std::max)(0u, 3u)); BOOST_TEST(3u == ((std::max)(0u, 3u)));
But both fails with: check 3u == ((std::max)(0u, 3u)) has failed [3 != 7095312]
Did I miss something regarding BOOST_TEST usage?
Platform: Linux, GCC 5.3, Boost 1.60
Thanks, Benedek
Thanks for the report, would you please open a ticket on trac? Best, Raffi
Thanks for the report, would you please open a ticket on trac?
Best, Raffi
Ticket filed: https://svn.boost.org/trac/boost/ticket/11887
Benedek Thaler
Hi,
I'd expect the following cheks to pass:
BOOST_TEST(3u == (std::max)(0u, 3u)); BOOST_TEST(3u == ((std::max)(0u, 3u)));
But both fails with: check 3u == ((std::max)(0u, 3u)) has failed [3 != 7095312]
I can't reproduce this neither on Linux nor Windows. What configuration do you use?
Benedek Thaler
Platform: Linux, GCC 5.3 and GCC 4.8, Boost 1.60, variant=debug Can't reproduce it with variant=release.
I on ubuntu, boost trunk (same as 1.60 I believe), gcc 4.8. Both release and debug are fine. Can you dig a bit further? Gennadiy
Can you dig a bit further?
I drilled down the code with gdb, here is the detailed listing: http://pastebin.com/pUjxEsiW As far as I understand, the binary_expr gets constructed, holding the right value (3) in m_rhs. The address of this member is watched, which gets modified twice, by a lazy_ostream object, which happens to be at the same address. The latest value is the same as the one Boost.Test prints when reports the failed test. Thanks, Benedek
Le 06/01/16 21:07, Benedek Thaler a écrit :
Can you dig a bit further?
I drilled down the code with gdb, here is the detailed listing: http://pastebin.com/pUjxEsiW
As far as I understand, the binary_expr gets constructed, holding the right value (3) in m_rhs. The address of this member is watched, which gets modified twice, by a lazy_ostream object, which happens to be at the same address. The latest value is the same as the one Boost.Test prints when reports the failed test.
Thanks, let's continue in the ticket :) Raffi
Benedek Thaler
As far as I understand, the binary_expr gets constructed, holding the right value (3) in m_rhs. The address of this member is watched, which gets modified twice, by a lazy_ostream object, which happens to be at the same address.
This some voodoo crap is going on here. We should have some choice words with your compiler about placing two separate live objects on the same stack address. Seriously though. There are two relevant statements involved in BOOST_TEST: BOOST_TEST_BUILD_ASSERTION( P ); and report_assertion( E.evaluate(), BOOST_TEST_LAZY_MSG( "description" ), __FILE__, __LINE__, ::boost::test_tools::tt_detail::CHECK, ::boost::test_tools::tt_detail::CHECK_BUILT_ASSERTION, 0 ); You can unwrap remaining macros as well. Can you try to create trivial example illustrating the issue? you can replace report_assertion with your own function and get rid of the most of Boost.Test references (including BOOST_AUTO_TEST_CASE). Gennadiy
On Thu, Jan 7, 2016 at 12:33 AM, Gennadiy Rozental
report_assertion( E.evaluate(), BOOST_TEST_LAZY_MSG( "description" ), __FILE__, __LINE__, ::boost::test_tools::tt_detail::CHECK, ::boost::test_tools::tt_detail::CHECK_BUILT_ASSERTION, 0 );
You can unwrap remaining macros as well. Can you try to create trivial example illustrating the issue? you can replace report_assertion with your own function and get rid of the most of Boost.Test references (including BOOST_AUTO_TEST_CASE).
I reduced the original example to this: (plus includes) bool my_report_assertion( boost::test_tools::assertion_result const& ar, boost::test_tools::tt_detail::lazy_ostream const& assertion_descr ) { if (!ar) { std::cout << "Failed: " << assertion_descr << " -- " << ar << std::endl; } return true; } int main() { auto const& E = ::boost::test_tools::assertion::seed()->*3u == ((std::max)(0u, 3u)); my_report_assertion( E.evaluate(), (::boost::unit_test::lazy_ostream::instance() << ::boost::unit_test::const_string( " ", sizeof( " ") - 1)) ); } It prints "Failed: -- 0", with and without -O2. Removing the lazy_ostream or the << call, or the std::max expression fixes the issue (prints nothing). My gcc version is: g++-5 (Ubuntu 5.3.0-3ubuntu1~14.04) 5.3.0 20151204 The preprocessed file is 138k lines long, I'm not sure how to proceed. Benedek
Benedek Thaler
auto const& E = ::boost::test_tools::assertion::seed()->*3u == ((std::max)(0u, 3u));
max returns unsigned int&&, which for some unclear reason is bound to unsigned int const& in expression template E. This obviously becomes dangling reference as soon as we leave this line.
my_report_assertion( E.evaluate(), (::boost::unit_test::lazy_ostream::instance() << ::boost::unit_test::const_string( " ", sizeof( " ") - 1)) );
Instance of temporary lazy_ostream is created at the same address as unsigned int used to be and it changes the memory. What is still unclear is WHY this is happening. Can you tell me what is the value of BOOST_NO_CXX11_RVALUE_REFERENCES in our build? Gennadiy
2016-01-11 4:11 GMT+01:00 Gennadiy Rozental
Benedek Thaler
writes: I believe at this point we understand WHAT is going on.
auto const& E = ::boost::test_tools::assertion::seed()->*3u == ((std::max)(0u, 3u));
max returns unsigned int&&, which for some unclear reason is bound to unsigned int const& in expression template E. This obviously becomes dangling reference as soon as we leave this line.
This is an expected behavior in C++: an xvalue (rvalue reference returned from a function) can be bound to a const lvalue reference. See example: http://melpon.org/wandbox/permlink/yon7EWpj2B5KQNcT
Le 11/01/16 10:51, Andrzej Krzemienski a écrit :
2016-01-11 4:11 GMT+01:00 Gennadiy Rozental
: Benedek Thaler
writes: I believe at this point we understand WHAT is going on.
auto const& E = ::boost::test_tools::assertion::seed()->*3u == ((std::max)(0u, 3u));
max returns unsigned int&&, which for some unclear reason is bound to unsigned int const& in expression template E. This obviously becomes dangling reference as soon as we leave this line.
This is an expected behavior in C++: an xvalue (rvalue reference returned from a function) can be bound to a const lvalue reference. See example: http://melpon.org/wandbox/permlink/yon7EWpj2B5KQNcT
This is a nice web site :) (I should update myself)
The example here is more what is happening:
http://melpon.org/wandbox/permlink/R2obgUazntZM9LVT
the "printit" function with an universal ref. The return of std::max
with two identical types is a const lvalue ref according to the standard.
That ref lifetime is expanded until what point?
The "binary_expr" that is returned with boost.test holds that ref. The
code is
template<typename T>
binary_expr
2016-01-11 13:46 GMT+01:00 Raffi Enficiaud
Le 11/01/16 10:51, Andrzej Krzemienski a écrit :
2016-01-11 4:11 GMT+01:00 Gennadiy Rozental
: Benedek Thaler
writes: I believe at this point we understand WHAT is going on.
auto const& E = ::boost::test_tools::assertion::seed()->*3u
== ((std::max)(0u, 3u));
max returns unsigned int&&, which for some unclear reason is bound to unsigned int const& in expression template E. This obviously becomes dangling reference as soon as we leave this line.
This is an expected behavior in C++: an xvalue (rvalue reference returned from a function) can be bound to a const lvalue reference. See example: http://melpon.org/wandbox/permlink/yon7EWpj2B5KQNcT
This is a nice web site :) (I should update myself)
The example here is more what is happening: http://melpon.org/wandbox/permlink/R2obgUazntZM9LVT
the "printit" function with an universal ref. The return of std::max with two identical types is a const lvalue ref according to the standard.
That ref lifetime is expanded until what point?
The "binary_expr" that is returned with boost.test holds that ref. The code is
template<typename T> binary_expr
> operator==( T&& rhs ) { // ... Is this ref valid at the return of operator== ?
In the case of
auto const& E = ::boost::test_tools::assertion::seed()->*3u == ((std::max)(0u, 3u)) ;
that ref would exists until " ; ", but if "E" contains an object having this reference, it is dandling after the " ; ".
Yes, a member reference inside E is dangling after " ; ". Member references do not prolong the life-time of temporary objects.
Le 09/01/16 11:32, Benedek Thaler a écrit :
On Thu, Jan 7, 2016 at 12:33 AM, Gennadiy Rozental
wrote:
[snip]
It prints "Failed: -- 0", with and without -O2. Removing the lazy_ostream or the << call, or the std::max expression fixes the issue (prints nothing).
My gcc version is: g++-5 (Ubuntu 5.3.0-3ubuntu1~14.04) 5.3.0 20151204
The preprocessed file is 138k lines long, I'm not sure how to proceed.
Benedek
We have a fix, would you please have a look to the trac ticket? Thanks, Raffi
participants (4)
-
Andrzej Krzemienski
-
Benedek Thaler
-
Gennadiy Rozental
-
Raffi Enficiaud