Am 22.07.20 um 16:38 schrieb Niall Douglas via Boost:
On 21/07/2020 15:51, Alexander Grund via Boost wrote:
Just some observations that might help:
- Failing test is BOOST_CHECK(a == b); with "outcome<int> a(1); result<int> b(1);" - Hence the operation is "operator==(outcome, result) - Failing stacktrace shows bottom-most: "operator==(const basic_result
&a, const basic_outcome &b)" so the other way round - you define the availability and noexceptness basically as "b == a" reversing the results All correct. Is it possible that outcome<T> and result<T> are convertible? If so, that would explain the recursion: to check a==b you need to check b==a so you need to check a==b, ... An outcome<T> can be explicitly constructed from a result<T> yes.
But the recursion isn't from that. result == outcome is available if outcome == result is available. outcome == result
is available if the expressions A == C and B == D are valid. As A and C are both ints, and B and D are both error_code, this should "just work", and indeed it does on GCC and MSVC, and indeed clang if in a C++ standard before C++ 20. For some reason I don't understand, clang in C++ 20 mode seems to loop the result == outcome step i.e. in the constraints that result == outcome is available if and only if outcome == result is available, it considers the availability of the result == outcome overload itself. As you suggest, if an outcome were *implicitly* constructible from a result, that might be a valid consideration. But it is not available due to the explicit constructibility, so the compiler should never consider it.
Another option is that this is simply a corner case bug in clang. Xcode's clang is actually pretty bug ridden, Outcome's unit tests fail badly on Xcode clang, I had to go disable a whole bunch of them to achieve a pass. But LLVM clang is pretty good, generally. It's the best of all the major compilers for this sort of stuff, in my opinion.
Now, there is the argument that in C++, just because A == B is available doesn't mean than B == A is available, and Outcome ought to reflect that properly. This is one of those many areas where I've deliberately been lazy, and not spent code and thus maintenance time on what are in my opinion pathological corner case support. To date, no users have complained :)
What about the fact that Clang considers "operator==(const basic_result, const basic_outcome)" for a test "operator==(outcome, result)"? Have you checked that? This way you might find where it converts one into another. Maybe even check that in isolation by assigning "b = a" first and see what it does. Could you check that locally in a file having only this? Maybe remove the constraints for this test to make it compile and debug it through to see why it tries that? For why "clang in C++20": You switch your macros to use concepts instead of template sfinae in C++20, don't you? If so, temporarily disable that? Maybe this switch enables a ctor that is otherwise explicit or disabled?