ned14 / outcome

Provides very lightweight outcome<T> and result<T> (non-Boost edition)
https://ned14.github.io/outcome
Other
675 stars 62 forks source link

clang fail to compile, implicit conversion from one custom domain to another #278

Closed frkami123 closed 2 weeks ago

frkami123 commented 1 year ago

I have two custom domains SystemStatusDomain and EngineStatusDomain. i want to implicitly convert the failure_type<SystemStatus> to basic_result<void, EngineStatus, outcome_v2::experimental::policy::all_narrow>

OUTCOME_V2_NAMESPACE::basic_result<void, Eao::EngineStatus, OUTCOME_V2_NAMESPACE::experimental::policy::all_narrow>
test() 
{
    SystemCode returnCode = SystemCode::Unknown;
    using Eao::make_error_code;
    using EC = std::decay_t<decltype(make_error_code(returnCode))>;
    return OUTCOME_V2_NAMESPACE::failure_type<EC>{make_error_code(returnCode), 0};
}

I have added the make_status_code implict conversion function to enable ADL

constexpr inline auto make_status_code(SystemStatus systemStatus) -> EngineStatus
{
  const EngineCode code = static_cast<EngineCode>(systemStatus.value().statusCode);
  return make_error_code(code);
}

This setup works fine on Linux Mint with GCC12, and MSVC (cl version 19.36.32535) on Windows 10. But doesn't compile with clang-cl 16.0.6 on Windows. I tried clang 16.0.0 on compiler explorer and run into the same compile error.

Here the setup in compiler explorer with CMake. https://compiler-explorer.com/z/WvcPsG7ha

I tried to figure out what happened but it seem like detai::safe_get_make_status_code_result doesn't behave nicely on clang.

Here a summary of clang output:

[ 50%] Building CXX object CMakeFiles/the_executable.dir/main.cpp.o
/app/main.cpp:14:12: error: no viable conversion from returned value of type 'outcome_v2::failure_type<EC>' (aka 'failure_type<system_error2::status_code<Eao::SystemStatusDomain>>') to function return type 'outcome_v2::basic_result<void, Eao::EngineStatus, outcome_v2::experimental::policy::all_narrow>' (aka 'basic_result<void, status_code<EngineStatusDomain>, outcome_v2::policy::all_narrow>')
    return OUTCOME_V2_NAMESPACE::failure_type<EC>{make_error_code(returnCode),
           ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/libs/outcome/single-header/outcome-experimental.hpp:1039:57: note: expanded from macro 'OUTCOME_V2_NAMESPACE'
#define OUTCOME_V2_NAMESPACE QUICKCPPLIB_BIND_NAMESPACE(OUTCOME_V2)
                                                        ^
/opt/compiler-explorer/libs/outcome/single-header/outcome-experimental.hpp:1027:56: note: expanded from macro 'OUTCOME_V2'
#define OUTCOME_V2 (QUICKCPPLIB_BIND_NAMESPACE_VERSION(outcome_v2))
                                                       ^
/opt/compiler-explorer/libs/outcome/single-header/outcome-experimental.hpp:939:111: note: expanded from macro 'QUICKCPPLIB_BIND_NAMESPACE_VERSION'
#define QUICKCPPLIB_BIND_NAMESPACE_VERSION(...) QUICKCPPLIB_CALL_OVERLOAD(QUICKCPPLIB_BIND_NAMESPACE_VERSION, __VA_ARGS__)
                                                                                                              ^
note: (skipping 8 expansions in backtrace; use -fmacro-backtrace-limit=0 to see all)
/opt/compiler-explorer/libs/outcome/single-header/outcome-experimental.hpp:825:144: note: expanded from macro 'QUICKCPPLIB_CALL_OVERLOAD_'
#define QUICKCPPLIB_CALL_OVERLOAD_(name, ...) QUICKCPPLIB_GLUE_(QUICKCPPLIB_OVERLOAD_MACRO_(name, QUICKCPPLIB_COUNT_ARGS_MAX8_(__VA_ARGS__)), (__VA_ARGS__))
                                                                                                                                               ^
/opt/compiler-explorer/libs/outcome/single-header/outcome-experimental.hpp:818:35: note: expanded from macro 'QUICKCPPLIB_GLUE_'
#define QUICKCPPLIB_GLUE_(x, y) x y
                                  ^
/opt/compiler-explorer/libs/outcome/single-header/outcome-experimental.hpp:942:51: note: expanded from macro 'QUICKCPPLIB_BIND_NAMESPACE_SELECT_1'
#define QUICKCPPLIB_BIND_NAMESPACE_SELECT_1(name) name
                                                  ^
/opt/compiler-explorer/libs/outcome/single-header/outcome-experimental.hpp:4488:3: note: candidate constructor not viable: no known conversion from 'outcome_v2::failure_type<EC>' (aka 'failure_type<system_error2::status_code<Eao::SystemStatusDomain>>') to 'basic_result<void, status_code<EngineStatusDomain>, all_narrow> &&' for 1st argument
  basic_result(basic_result && /*unused*/) = default; // NOLINT
  ^
/opt/compiler-explorer/libs/outcome/single-header/outcome-experimental.hpp:4492:3: note: candidate constructor not viable: no known conversion from 'outcome_v2::failure_type<EC>' (aka 'failure_type<system_error2::status_code<Eao::SystemStatusDomain>>') to 'const basic_result<void, status_code<EngineStatusDomain>, all_narrow> &' for 1st argument
  basic_result(const basic_result & /*unused*/) = default;
  ^
/opt/compiler-explorer/libs/outcome/single-header/outcome-experimental.hpp:4695:13: note: candidate constructor not viable: no known conversion from 'outcome_v2::failure_type<EC>' (aka 'failure_type<system_error2::status_code<Eao::SystemStatusDomain>>') to 'const success_type<void> &' for 1st argument
  constexpr basic_result(const success_type<void> &o) noexcept(std::is_nothrow_default_constructible<value_type>::value) // NOLINT
            ^
/opt/compiler-explorer/libs/outcome/single-header/outcome-experimental.hpp:4507:3: note: candidate template ignored: constraints not satisfied [with Arg = outcome_v2::failure_type<EC>, Args = <>]
  basic_result(Arg && /*unused*/, Args &&... /*unused*/) = delete; // NOLINT basic_result<T, T> is NOT SUPPORTED, see docs!
  ^
/opt/compiler-explorer/libs/outcome/single-header/outcome-experimental.hpp:4506:35: note: because '!predicate::constructors_enabled' evaluated to false
  OUTCOME_TREQUIRES(OUTCOME_TPRED(!predicate::constructors_enabled && (sizeof...(Args) >= 0)))
                                  ^
/opt/compiler-explorer/libs/outcome/single-header/outcome-experimental.hpp:4514:3: note: candidate template ignored: constraints not satisfied [with T = outcome_v2::failure_type<EC>]
  basic_result(T && /*unused*/, implicit_constructors_disabled_tag /*unused*/ = implicit_constructors_disabled_tag()) =
  ^
/opt/compiler-explorer/libs/outcome/single-header/outcome-experimental.hpp:4512:71: note: because '!predicate::implicit_constructors_enabled' evaluated to false
  OUTCOME_TREQUIRES(OUTCOME_TPRED((predicate::constructors_enabled && !predicate::implicit_constructors_enabled //
                                                                      ^
/opt/compiler-explorer/libs/outcome/single-header/outcome-experimental.hpp:4521:13: note: candidate template ignored: constraints not satisfied [with T = outcome_v2::failure_type<EC>]
  constexpr basic_result(T &&t, value_converting_constructor_tag /*unused*/ = value_converting_constructor_tag()) noexcept(
            ^
/opt/compiler-explorer/libs/outcome/single-header/outcome-experimental.hpp:4520:35: note: because 'predicate::enable_value_converting_constructor<outcome_v2::failure_type<system_error2::status_code<Eao::SystemStatusDomain> > >' evaluated to false
  OUTCOME_TREQUIRES(OUTCOME_TPRED(predicate::template enable_value_converting_constructor<T>))
                                  ^
/opt/compiler-explorer/libs/outcome/single-header/outcome-experimental.hpp:4532:13: note: candidate template ignored: constraints not satisfied [with T = outcome_v2::failure_type<EC>]
  constexpr basic_result(T &&t, error_converting_constructor_tag /*unused*/ = error_converting_constructor_tag()) noexcept(
            ^
/opt/compiler-explorer/libs/outcome/single-header/outcome-experimental.hpp:4531:35: note: because 'predicate::enable_error_converting_constructor<outcome_v2::failure_type<system_error2::status_code<Eao::SystemStatusDomain> > >' evaluated to false
  OUTCOME_TREQUIRES(OUTCOME_TPRED(predicate::template enable_error_converting_constructor<T>))
                                  ^
/opt/compiler-explorer/libs/outcome/single-header/outcome-experimental.hpp:4544:13: note: candidate template ignored: constraints not satisfied [with ErrorCondEnum = outcome_v2::failure_type<EC>]
  constexpr basic_result(ErrorCondEnum &&t, error_condition_converting_constructor_tag /*unused*/ = error_condition_converting_constructor_tag()) noexcept(
            ^
/opt/compiler-explorer/libs/outcome/single-header/outcome-experimental.hpp:4542:46: note: because '(error_type(make_error_code(ErrorCondEnum())))' would be invalid: no matching function for call to 'make_error_code'
  OUTCOME_TREQUIRES(OUTCOME_TEXPR(error_type(make_error_code(ErrorCondEnum()))), //
                                             ^
/opt/compiler-explorer/libs/outcome/single-header/outcome-experimental.hpp:4706:13: note: candidate template ignored: could not match 'success_type' against 'failure_type'
  constexpr basic_result(const success_type<T> &o) noexcept(detail::is_nothrow_constructible<value_type, T>) // NOLINT
            ^
/opt/compiler-explorer/libs/outcome/single-header/outcome-experimental.hpp:4717:13: note: candidate template ignored: could not match 'success_type' against 'failure_type'
  constexpr basic_result(success_type<T> &&o) noexcept(detail::is_nothrow_constructible<value_type, T>) // NOLINT
            ^
/opt/compiler-explorer/libs/outcome/single-header/outcome-experimental.hpp:4728:13: note: candidate template ignored: constraints not satisfied [with T = system_error2::status_code<Eao::SystemStatusDomain>]
  constexpr basic_result(const failure_type<T> &o, explicit_compatible_copy_conversion_tag /*unused*/ = explicit_compatible_copy_conversion_tag()) noexcept(
            ^
/opt/compiler-explorer/libs/outcome/single-header/outcome-experimental.hpp:4727:35: note: because 'predicate::enable_compatible_conversion<void, system_error2::status_code<Eao::SystemStatusDomain>, void>' evaluated to false
  OUTCOME_TREQUIRES(OUTCOME_TPRED(predicate::template enable_compatible_conversion<void, T, void>))
                                  ^
/opt/compiler-explorer/libs/outcome/single-header/outcome-experimental.hpp:4740:13: note: candidate template ignored: constraints not satisfied [with T = system_error2::status_code<Eao::SystemStatusDomain>]
  constexpr basic_result(failure_type<T> &&o, explicit_compatible_move_conversion_tag /*unused*/ = explicit_compatible_move_conversion_tag()) noexcept(
            ^
/opt/compiler-explorer/libs/outcome/single-header/outcome-experimental.hpp:4739:35: note: because 'predicate::enable_compatible_conversion<void, system_error2::status_code<Eao::SystemStatusDomain>, void>' evaluated to false
  OUTCOME_TREQUIRES(OUTCOME_TPRED(predicate::template enable_compatible_conversion<void, T, void>))
                                  ^
/opt/compiler-explorer/libs/outcome/single-header/outcome-experimental.hpp:4752:13: note: candidate template ignored: constraints not satisfied [with T = system_error2::status_code<Eao::SystemStatusDomain>]
  constexpr basic_result(const failure_type<T> &o,
            ^
/opt/compiler-explorer/libs/outcome/single-header/outcome-experimental.hpp:4751:35: note: because 'predicate::enable_make_error_code_compatible_conversion<void, system_error2::status_code<Eao::SystemStatusDomain>, void>' evaluated to false
  OUTCOME_TREQUIRES(OUTCOME_TPRED(predicate::template enable_make_error_code_compatible_conversion<void, T, void>))
                                  ^
/opt/compiler-explorer/libs/outcome/single-header/outcome-experimental.hpp:4765:13: note: candidate template ignored: constraints not satisfied [with T = system_error2::status_code<Eao::SystemStatusDomain>]
  constexpr basic_result(failure_type<T> &&o,
            ^
/opt/compiler-explorer/libs/outcome/single-header/outcome-experimental.hpp:4764:35: note: because 'predicate::enable_make_error_code_compatible_conversion<void, system_error2::status_code<Eao::SystemStatusDomain>, void>' evaluated to false
  OUTCOME_TREQUIRES(OUTCOME_TPRED(predicate::template enable_make_error_code_compatible_conversion<void, T, void>))
                                  ^
/opt/compiler-explorer/libs/outcome/single-header/outcome-experimental.hpp:4778:13: note: candidate template ignored: constraints not satisfied [with T = system_error2::status_code<Eao::SystemStatusDomain>]
  constexpr basic_result(const failure_type<T> &o,
            ^
/opt/compiler-explorer/libs/outcome/single-header/outcome-experimental.hpp:4777:35: note: because 'predicate::enable_make_exception_ptr_compatible_conversion<void, system_error2::status_code<Eao::SystemStatusDomain>, void>' evaluated to false
  OUTCOME_TREQUIRES(OUTCOME_TPRED(predicate::template enable_make_exception_ptr_compatible_conversion<void, T, void>))
                                  ^
/opt/compiler-explorer/libs/outcome/single-header/outcome-experimental.hpp:4791:13: note: candidate template ignored: constraints not satisfied [with T = system_error2::status_code<Eao::SystemStatusDomain>]
  constexpr basic_result(failure_type<T> &&o,
            ^
/opt/compiler-explorer/libs/outcome/single-header/outcome-experimental.hpp:4790:35: note: because 'predicate::enable_make_exception_ptr_compatible_conversion<void, system_error2::status_code<Eao::SystemStatusDomain>, void>' evaluated to false
  OUTCOME_TREQUIRES(OUTCOME_TPRED(predicate::template enable_make_exception_ptr_compatible_conversion<void, T, void>))
                                  ^
/opt/compiler-explorer/libs/outcome/single-header/outcome-experimental.hpp:4556:22: note: explicit constructor is not a candidate
  constexpr explicit basic_result(T &&o,
                     ^
/opt/compiler-explorer/libs/outcome/single-header/outcome-experimental.hpp:4566:22: note: explicit constructor is not a candidate
  constexpr explicit basic_result(
                     ^
/opt/compiler-explorer/libs/outcome/single-header/outcome-experimental.hpp:4579:22: note: explicit constructor is not a candidate
  constexpr explicit basic_result(
                     ^
/opt/compiler-explorer/libs/outcome/single-header/outcome-experimental.hpp:4592:22: note: explicit constructor is not a candidate
  constexpr explicit basic_result(const basic_result<T, U, V> &o,
                     ^
/opt/compiler-explorer/libs/outcome/single-header/outcome-experimental.hpp:4605:22: note: explicit constructor is not a candidate
  constexpr explicit basic_result(basic_result<T, U, V> &&o,
                     ^
/opt/compiler-explorer/libs/outcome/single-header/outcome-experimental.hpp:4618:22: note: explicit constructor is not a candidate
  constexpr explicit basic_result(const basic_result<T, U, V> &o,
                     ^
/opt/compiler-explorer/libs/outcome/single-header/outcome-experimental.hpp:4631:22: note: explicit constructor is not a candidate
  constexpr explicit basic_result(basic_result<T, U, V> &&o,
                     ^
/opt/compiler-explorer/libs/outcome/single-header/outcome-experimental.hpp:4644:22: note: explicit constructor is not a candidate
  constexpr explicit basic_result(in_place_type_t<value_type_if_enabled> _, Args &&... args) noexcept(detail::is_nothrow_constructible<value_type, Args...>)
                     ^
/opt/compiler-explorer/libs/outcome/single-header/outcome-experimental.hpp:4665:22: note: explicit constructor is not a candidate
  constexpr explicit basic_result(in_place_type_t<error_type_if_enabled> _, Args &&... args) noexcept(detail::is_nothrow_constructible<error_type, Args...>)
                     ^
In file included from /app/main.cpp:3:
In file included from /app/EngineCode.hpp:2:
In file included from /opt/compiler-explorer/libs/outcome/single-header/outcome-experimental.hpp:6508:
In file included from /opt/compiler-explorer/gcc-12.2.0/lib/gcc/x86_64-linux-gnu/12.2.0/../../../../include/c++/12.2.0/atomic:41:
In file included from /opt/compiler-explorer/gcc-12.2.0/lib/gcc/x86_64-linux-gnu/12.2.0/../../../../include/c++/12.2.0/bits/atomic_base.h:41:
In file included from /opt/compiler-explorer/gcc-12.2.0/lib/gcc/x86_64-linux-gnu/12.2.0/../../../../include/c++/12.2.0/bits/atomic_wait.h:49:
In file included from /opt/compiler-explorer/gcc-12.2.0/lib/gcc/x86_64-linux-gnu/12.2.0/../../../../include/c++/12.2.0/bits/std_mutex.h:39:
In file included from /opt/compiler-explorer/gcc-12.2.0/lib/gcc/x86_64-linux-gnu/12.2.0/../../../../include/c++/12.2.0/system_error:41:
In file included from /opt/compiler-explorer/gcc-12.2.0/lib/gcc/x86_64-linux-gnu/12.2.0/../../../../include/c++/12.2.0/stdexcept:39:
In file included from /opt/compiler-explorer/gcc-12.2.0/lib/gcc/x86_64-linux-gnu/12.2.0/../../../../include/c++/12.2.0/string:41:
In file included from /opt/compiler-explorer/gcc-12.2.0/lib/gcc/x86_64-linux-gnu/12.2.0/../../../../include/c++/12.2.0/bits/allocator.h:46:
...
frkami123 commented 12 months ago

One other thing i noticed If you try to replace

template<class DomainType>
friend class SYSTEM_ERROR2_NAMESPACE::status_code;

with

template<class DomainType>
friend class OUTCOME_V2_NAMESPACE::experimental::status_code;

clang would error out:

/app/SystemCode.hpp:27:52: error: friend declaration of 'status_code' does not match any declaration in namespace 'outcome_v2::experimental'
  friend class OUTCOME_V2_NAMESPACE::experimental::status_code;

While GCC, and MSVC seem fine with this. I don't know if it related or a different bug, or an existing clang bug.

ned14 commented 11 months ago

Alas it does not surprise me clang 16 has issues :(

ned14 commented 2 weeks ago

clang 18 has considerably improved QoI since 16, so if your issue repros in clang 18 please do reopen this issue.