/* A very simple result type
(C) 2017-2020 Niall Douglas <http://www.nedproductions.biz/> (14 commits)
File Created: June 2017


Boost Software License - Version 1.0 - August 17th, 2003

Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:

The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/

#ifndef BOOST_OUTCOME_BASIC_RESULT_HPP
#define BOOST_OUTCOME_BASIC_RESULT_HPP

#include "config.hpp"
#include "convert.hpp"
#include "detail/basic_result_final.hpp"

#include "policy/all_narrow.hpp"
#include "policy/terminate.hpp"

#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdocumentation"  // Standardese markup confuses clang
#endif

BOOST_OUTCOME_V2_NAMESPACE_EXPORT_BEGIN

template <class R, class S, class NoValuePolicy>  //
class basic_result;

namespace detail
{
  // These are reused by basic_outcome to save load on the compiler
  template <class value_type, class error_type> struct result_predicates
  {
    // Predicate for the implicit constructors to be available. Weakened to allow result<int, C enum>.
    static constexpr bool implicit_constructors_enabled =  //
    !(trait::is_error_type<std::decay_t<value_type>>::value &&
      trait::is_error_type<std::decay_t<error_type>>::value)  // both value and error types are not whitelisted error types
    && ((!detail::is_implicitly_constructible<value_type, error_type> &&
         !detail::is_implicitly_constructible<error_type, value_type>)       // if value and error types cannot be constructed into one another
        || (trait::is_error_type<std::decay_t<error_type>>::value            // if error type is a whitelisted error type
            && !detail::is_implicitly_constructible<error_type, value_type>  // AND which cannot be constructed from the value type
            && std::is_integral<value_type>::value));                        // AND the value type is some integral type

    // Predicate for the value converting constructor to be available. Weakened to allow result<int, C enum>.
    template <class T>
    static constexpr bool enable_value_converting_constructor =                                                      //
    implicit_constructors_enabled                                                                                    //
    && !is_in_place_type_t<std::decay_t<T>>::value                                                                   // not in place construction
    && !trait::is_error_type_enum<error_type, std::decay_t<T>>::value                                                // not an enum valid for my error type
    && ((detail::is_implicitly_constructible<value_type, T> && !detail::is_implicitly_constructible<error_type, T>)  // is unambiguously for value type
        || (std::is_same<value_type, std::decay_t<T>>::value                                                         // OR is my value type exactly
            && detail::is_implicitly_constructible<value_type, T>) );  // and my value type is constructible from this ref form of T


    // Predicate for the error converting constructor to be available. Weakened to allow result<int, C enum>.
    template <class T>
    static constexpr bool enable_error_converting_constructor =                                                      //
    implicit_constructors_enabled                                                                                    //
    && !is_in_place_type_t<std::decay_t<T>>::value                                                                   // not in place construction
    && !trait::is_error_type_enum<error_type, std::decay_t<T>>::value                                                // not an enum valid for my error type
    && ((!detail::is_implicitly_constructible<value_type, T> && detail::is_implicitly_constructible<error_type, T>)  // is unambiguously for error type
        || (std::is_same<error_type, std::decay_t<T>>::value                                                         // OR is my error type exactly
            && detail::is_implicitly_constructible<error_type, T>) );  // and my error type is constructible from this ref form of T

    // Predicate for the error condition converting constructor to be available.
    template <class ErrorCondEnum>
    static constexpr bool enable_error_condition_converting_constructor =         //
    !is_in_place_type_t<std::decay_t<ErrorCondEnum>>::value                       // not in place construction
    && trait::is_error_type_enum<error_type, std::decay_t<ErrorCondEnum>>::value  // is an error condition enum
    /*&& !detail::is_implicitly_constructible<value_type, ErrorCondEnum> && !detail::is_implicitly_constructible<error_type, ErrorCondEnum>*/;  // not
                                                                                                                                                // constructible
                                                                                                                                                // via any other
                                                                                                                                                // means

    // Predicate for the converting constructor from a compatible input to be available.
    template <class T, class U, class V>
    static constexpr bool enable_compatible_conversion =  //
    (std::is_void<T>::value ||
     detail::is_explicitly_constructible<value_type, typename basic_result<T, U, V>::value_type>)  // if our value types are constructible
    &&(std::is_void<U>::value ||
       detail::is_explicitly_constructible<error_type, typename basic_result<T, U, V>::error_type>)  // if our error types are constructible
    ;

    // Predicate for the converting constructor from a make_error_code() of the input to be available.
    template <class T, class U, class V>
    static constexpr bool enable_make_error_code_compatible_conversion =  //
    trait::is_error_code_available<std::decay_t<error_type>>::value       // if error type has an error code
    && !enable_compatible_conversion<T, U, V>                             // and the normal compatible conversion is not available
    && (std::is_void<T>::value ||
        detail::is_explicitly_constructible<value_type, typename basic_result<T, U, V>::value_type>)  // and if our value types are constructible
    &&detail::is_explicitly_constructible<error_type,
                                          typename trait::is_error_code_available<U>::type>;  // and our error type is constructible from a make_error_code()

    // Predicate for the converting constructor from a make_exception_ptr() of the input to be available.
    template <class T, class U, class V>
    static constexpr bool enable_make_exception_ptr_compatible_conversion =  //
    trait::is_exception_ptr_available<std::decay_t<error_type>>::value       // if error type has an exception ptr
    && !enable_compatible_conversion<T, U, V>                                // and the normal compatible conversion is not available
    && (std::is_void<T>::value ||
        detail::is_explicitly_constructible<value_type, typename basic_result<T, U, V>::value_type>)         // and if our value types are constructible
    &&detail::is_explicitly_constructible<error_type, typename trait::is_exception_ptr_available<U>::type>;  // and our error type is constructible from a
                                                                                                             // make_exception_ptr()

    // Predicate for the implicit converting inplace constructor from a compatible input to be available.
    struct disable_inplace_value_error_constructor;
    template <class... Args>
    using choose_inplace_value_error_constructor = std::conditional_t<                               //
    detail::is_constructible<value_type, Args...> && detail::is_constructible<error_type, Args...>,  //
    disable_inplace_value_error_constructor,                                                         //
    std::conditional_t<                                                                              //
    detail::is_constructible<value_type, Args...>,                                                   //
    value_type,                                                                                      //
    std::conditional_t<                                                                              //
    detail::is_constructible<error_type, Args...>,                                                   //
    error_type,                                                                                      //
    disable_inplace_value_error_constructor>>>;
    template <class... Args>
    static constexpr bool enable_inplace_value_error_constructor =
    implicit_constructors_enabled  //
    && !std::is_same<choose_inplace_value_error_constructor<Args...>, disable_inplace_value_error_constructor>::value;
  };

  template <class T, class U> constexpr inline const U &extract_value_from_success(const success_type<U> &v) { return v.value(); }
  template <class T, class U> constexpr inline U &&extract_value_from_success(success_type<U> &&v) { return static_cast<success_type<U> &&>(v).value(); }
  template <class T> constexpr inline T extract_value_from_success(const success_type<void> & /*unused*/) { return T{}; }

  template <class T, class U, class V> constexpr inline const U &extract_error_from_failure(const failure_type<U, V> &v) { return v.error(); }
  template <class T, class U, class V> constexpr inline U &&extract_error_from_failure(failure_type<U, V> &&v)
  {
    return static_cast<failure_type<U, V> &&>(v).error();
  }
  template <class T, class V> constexpr inline T extract_error_from_failure(const failure_type<void, V> & /*unused*/) { return T{}; }

  template <class T> struct is_basic_result
  {
    static constexpr bool value = false;
  };
  template <class R, class S, class T> struct is_basic_result<basic_result<R, S, T>>
  {
    static constexpr bool value = true;
  };
}  // namespace detail

/*! AWAITING HUGO JSON CONVERSION TOOL
type alias template <class T> is_basic_result. Potential doc page: `is_basic_result<T>`
*/
template <class T> using is_basic_result = detail::is_basic_result<std::decay_t<T>>;
/*! AWAITING HUGO JSON CONVERSION TOOL
SIGNATURE NOT RECOGNISED
*/
template <class T> static constexpr bool is_basic_result_v = detail::is_basic_result<std::decay_t<T>>::value;

namespace concepts
{
#if defined(__cpp_concepts)
  /* The `basic_result` concept.
  \requires That `U` matches a `basic_result`.
  */
  template <class U>
  concept BOOST_OUTCOME_GCC6_CONCEPT_BOOL basic_result =
  BOOST_OUTCOME_V2_NAMESPACE::is_basic_result<U>::value ||
  (requires(U v) { BOOST_OUTCOME_V2_NAMESPACE::basic_result<typename U::value_type, typename U::error_type, typename U::no_value_policy_type>(v); } &&    //
   detail::convertible<U, BOOST_OUTCOME_V2_NAMESPACE::basic_result<typename U::value_type, typename U::error_type, typename U::no_value_policy_type>> &&  //
   detail::base_of<BOOST_OUTCOME_V2_NAMESPACE::basic_result<typename U::value_type, typename U::error_type, typename U::no_value_policy_type>, U>);
#else
  namespace detail
  {
    inline no_match match_basic_result(...);
    template <class R, class S, class NVP, class T,                                                                      //
              typename = typename T::value_type,                                                                         //
              typename = typename T::error_type,                                                                         //
              typename = typename T::no_value_policy_type,                                                               //
              typename std::enable_if_t<std::is_convertible<T, BOOST_OUTCOME_V2_NAMESPACE::basic_result<R, S, NVP>>::value &&  //
                                        std::is_base_of<BOOST_OUTCOME_V2_NAMESPACE::basic_result<R, S, NVP>, T>::value,
                                        bool> = true>
    inline BOOST_OUTCOME_V2_NAMESPACE::basic_result<R, S, NVP> match_basic_result(BOOST_OUTCOME_V2_NAMESPACE::basic_result<R, S, NVP> &&, T &&);

    template <class U>
    static constexpr bool basic_result = BOOST_OUTCOME_V2_NAMESPACE::is_basic_result<U>::value ||
                                         !std::is_same<no_match, decltype(match_basic_result(std::declval<BOOST_OUTCOME_V2_NAMESPACE::detail::devoid<U>>(),
                                                                                             std::declval<BOOST_OUTCOME_V2_NAMESPACE::detail::devoid<U>>()))>::value;
  }  // namespace detail
  /* The `basic_result` concept.
  \requires That `U` matches a `basic_result`.
  */
  template <class U> static constexpr bool basic_result = detail::basic_result<U>;
#endif
}  // namespace concepts

/*! AWAITING HUGO JSON CONVERSION TOOL
SIGNATURE NOT RECOGNISED
*/
namespace hooks
{
  /*! AWAITING HUGO JSON CONVERSION TOOL
SIGNATURE NOT RECOGNISED
*/
  template <class T, class U> constexpr inline void hook_result_construction(T * /*unused*/, U && /*unused*/) noexcept {}
  /*! AWAITING HUGO JSON CONVERSION TOOL
SIGNATURE NOT RECOGNISED
*/
  template <class T, class U> constexpr inline void hook_result_copy_construction(T * /*unused*/, U && /*unused*/) noexcept {}
  /*! AWAITING HUGO JSON CONVERSION TOOL
SIGNATURE NOT RECOGNISED
*/
  template <class T, class U> constexpr inline void hook_result_move_construction(T * /*unused*/, U && /*unused*/) noexcept {}
  /*! AWAITING HUGO JSON CONVERSION TOOL
SIGNATURE NOT RECOGNISED
*/
  template <class T, class U, class... Args>
  constexpr inline void hook_result_in_place_construction(T * /*unused*/, in_place_type_t<U> /*unused*/, Args &&... /*unused*/) noexcept
  {
  }

  /*! AWAITING HUGO JSON CONVERSION TOOL
SIGNATURE NOT RECOGNISED
*/
  template <class R, class S, class NoValuePolicy> constexpr inline uint16_t spare_storage(const detail::basic_result_storage<R, S, NoValuePolicy> *r) noexcept
  {
    return r->_state._status.spare_storage_value;
  }
  /*! AWAITING HUGO JSON CONVERSION TOOL
SIGNATURE NOT RECOGNISED
*/
  template <class R, class S, class NoValuePolicy>
  constexpr inline void set_spare_storage(detail::basic_result_storage<R, S, NoValuePolicy> *r, uint16_t v) noexcept
  {
    r->_state._status.spare_storage_value = v;
  }
}  // namespace hooks

/*! AWAITING HUGO JSON CONVERSION TOOL
type definition template <class R, class S, class NoValuePolicy> basic_result. Potential doc page: `basic_result<T, E, NoValuePolicy>`
*/
template <class R, class S, class NoValuePolicy>  //
class BOOST_OUTCOME_NODISCARD basic_result : public detail::basic_result_final<R, S, NoValuePolicy>
{
  static_assert(trait::type_can_be_used_in_basic_result<R>, "The type R cannot be used in a basic_result");
  static_assert(trait::type_can_be_used_in_basic_result<S>, "The type S cannot be used in a basic_result");
  static_assert(std::is_void<S>::value || std::is_default_constructible<S>::value, "The type S must be void or default constructible");

  using base = detail::basic_result_final<R, S, NoValuePolicy>;

  struct implicit_constructors_disabled_tag
  {
  };
  struct value_converting_constructor_tag
  {
  };
  struct error_converting_constructor_tag
  {
  };
  struct error_condition_converting_constructor_tag
  {
  };
  struct explicit_valueornone_converting_constructor_tag
  {
  };
  struct explicit_valueorerror_converting_constructor_tag
  {
  };
  struct explicit_compatible_copy_conversion_tag
  {
  };
  struct explicit_compatible_move_conversion_tag
  {
  };
  struct explicit_make_error_code_compatible_copy_conversion_tag
  {
  };
  struct explicit_make_error_code_compatible_move_conversion_tag
  {
  };
  struct explicit_make_exception_ptr_compatible_copy_conversion_tag
  {
  };
  struct explicit_make_exception_ptr_compatible_move_conversion_tag
  {
  };

public:
  using value_type = R;
  using error_type = S;
  using no_value_policy_type = NoValuePolicy;

  using value_type_if_enabled = typename base::_value_type;
  using error_type_if_enabled = typename base::_error_type;

  template <class T, class U = S, class V = NoValuePolicy> using rebind = basic_result<T, U, V>;

protected:
  // Requirement predicates for result.
  struct predicate
  {
    using base = detail::result_predicates<value_type, error_type>;

    // Predicate for any constructors to be available at all
    static constexpr bool constructors_enabled = !std::is_same<std::decay_t<value_type>, std::decay_t<error_type>>::value;

    // Predicate for implicit constructors to be available at all
    static constexpr bool implicit_constructors_enabled = constructors_enabled && base::implicit_constructors_enabled;

    // Predicate for the value converting constructor to be available.
    template <class T>
    static constexpr bool enable_value_converting_constructor =  //
    constructors_enabled                                         //
    && !std::is_same<std::decay_t<T>, basic_result>::value       // not my type
    && base::template enable_value_converting_constructor<T>;

    // Predicate for the error converting constructor to be available.
    template <class T>
    static constexpr bool enable_error_converting_constructor =  //
    constructors_enabled                                         //
    && !std::is_same<std::decay_t<T>, basic_result>::value       // not my type
    && base::template enable_error_converting_constructor<T>;

    // Predicate for the error condition converting constructor to be available.
    template <class ErrorCondEnum>
    static constexpr bool enable_error_condition_converting_constructor =  //
    constructors_enabled                                                   //
    && !std::is_same<std::decay_t<ErrorCondEnum>, basic_result>::value     // not my type
    && base::template enable_error_condition_converting_constructor<ErrorCondEnum>;

    // Predicate for the converting constructor from a compatible input to be available.
    template <class T, class U, class V>
    static constexpr bool enable_compatible_conversion =          //
    constructors_enabled                                          //
    && !std::is_same<basic_result<T, U, V>, basic_result>::value  // not my type
    && base::template enable_compatible_conversion<T, U, V>;

    // Predicate for the converting constructor from a make_error_code() of the input to be available.
    template <class T, class U, class V>
    static constexpr bool enable_make_error_code_compatible_conversion =  //
    constructors_enabled                                                  //
    && !std::is_same<basic_result<T, U, V>, basic_result>::value          // not my type
    && base::template enable_make_error_code_compatible_conversion<T, U, V>;

    // Predicate for the converting constructor from a make_exception_ptr() of the input to be available.
    template <class T, class U, class V>
    static constexpr bool enable_make_exception_ptr_compatible_conversion =  //
    constructors_enabled                                                     //
    && !std::is_same<basic_result<T, U, V>, basic_result>::value             // not my type
    && base::template enable_make_exception_ptr_compatible_conversion<T, U, V>;

    // Predicate for the inplace construction of value to be available.
    template <class... Args>
    static constexpr bool enable_inplace_value_constructor =  //
    constructors_enabled                                      //
    && (std::is_void<value_type>::value                       //
        || detail::is_constructible<value_type, Args...>);

    // Predicate for the inplace construction of error to be available.
    template <class... Args>
    static constexpr bool enable_inplace_error_constructor =  //
    constructors_enabled                                      //
    && (std::is_void<error_type>::value                       //
        || detail::is_constructible<error_type, Args...>);

    // Predicate for the implicit converting inplace constructor to be available.
    template <class... Args>
    static constexpr bool enable_inplace_value_error_constructor =  //
    constructors_enabled                                            //
    &&base::template enable_inplace_value_error_constructor<Args...>;
    template <class... Args> using choose_inplace_value_error_constructor = typename base::template choose_inplace_value_error_constructor<Args...>;
  };

public:
  /*! AWAITING HUGO JSON CONVERSION TOOL
SIGNATURE NOT RECOGNISED
*/
  basic_result() = delete;
  /*! AWAITING HUGO JSON CONVERSION TOOL
SIGNATURE NOT RECOGNISED
*/
  basic_result(basic_result && /*unused*/) = default;  // NOLINT
  /*! AWAITING HUGO JSON CONVERSION TOOL
SIGNATURE NOT RECOGNISED
*/
  basic_result(const basic_result & /*unused*/) = default;
  /*! AWAITING HUGO JSON CONVERSION TOOL
SIGNATURE NOT RECOGNISED
*/
  basic_result &operator=(basic_result && /*unused*/) = default;  // NOLINT
  /*! AWAITING HUGO JSON CONVERSION TOOL
SIGNATURE NOT RECOGNISED
*/
  basic_result &operator=(const basic_result & /*unused*/) = default;
  ~basic_result() = default;

  /*! AWAITING HUGO JSON CONVERSION TOOL
SIGNATURE NOT RECOGNISED
*/
  BOOST_OUTCOME_TEMPLATE(class Arg, class... Args)
  BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(!predicate::constructors_enabled && (sizeof...(Args) >= 0)))
  basic_result(Arg && /*unused*/, Args &&... /*unused*/) = delete;  // NOLINT basic_result<T, T> is NOT SUPPORTED, see docs!

  /*! AWAITING HUGO JSON CONVERSION TOOL
SIGNATURE NOT RECOGNISED
*/
  BOOST_OUTCOME_TEMPLATE(class T)
  BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED((predicate::constructors_enabled && !predicate::implicit_constructors_enabled  //
                                   && (detail::is_implicitly_constructible<value_type, T> || detail::is_implicitly_constructible<error_type, T>) )))
  basic_result(T && /*unused*/, implicit_constructors_disabled_tag /*unused*/ = implicit_constructors_disabled_tag()) =
  delete;  // NOLINT Implicit constructors disabled, use explicit in_place_type<T>, success() or failure(). see docs!

  /*! AWAITING HUGO JSON CONVERSION TOOL
SIGNATURE NOT RECOGNISED
*/
  BOOST_OUTCOME_TEMPLATE(class T)
  BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(predicate::template enable_value_converting_constructor<T>))
  constexpr basic_result(T &&t, value_converting_constructor_tag /*unused*/ = value_converting_constructor_tag()) noexcept(
  detail::is_nothrow_constructible<value_type, T>)  // NOLINT
      : base{in_place_type<typename base::value_type>, static_cast<T &&>(t)}
  {
    using namespace hooks;
    hook_result_construction(this, static_cast<T &&>(t));
  }
  /*! AWAITING HUGO JSON CONVERSION TOOL
SIGNATURE NOT RECOGNISED
*/
  BOOST_OUTCOME_TEMPLATE(class T)
  BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(predicate::template enable_error_converting_constructor<T>))
  constexpr basic_result(T &&t, error_converting_constructor_tag /*unused*/ = error_converting_constructor_tag()) noexcept(
  detail::is_nothrow_constructible<error_type, T>)  // NOLINT
      : base{in_place_type<typename base::error_type>, static_cast<T &&>(t)}
  {
    using namespace hooks;
    hook_result_construction(this, static_cast<T &&>(t));
  }
  /*! AWAITING HUGO JSON CONVERSION TOOL
SIGNATURE NOT RECOGNISED
*/
  BOOST_OUTCOME_TEMPLATE(class ErrorCondEnum)
  BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TEXPR(error_type(make_error_code(ErrorCondEnum()))),  //
                    BOOST_OUTCOME_TPRED(predicate::template enable_error_condition_converting_constructor<ErrorCondEnum>))
  constexpr basic_result(ErrorCondEnum &&t, error_condition_converting_constructor_tag /*unused*/ = error_condition_converting_constructor_tag()) noexcept(
  noexcept(error_type(make_error_code(static_cast<ErrorCondEnum &&>(t)))))  // NOLINT
      : base{in_place_type<typename base::error_type>, make_error_code(t)}
  {
    using namespace hooks;
    hook_result_construction(this, static_cast<ErrorCondEnum &&>(t));
  }

  /*! AWAITING HUGO JSON CONVERSION TOOL
SIGNATURE NOT RECOGNISED
*/
  BOOST_OUTCOME_TEMPLATE(class T)
  BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(convert::value_or_error<basic_result, std::decay_t<T>>::enable_result_inputs || !concepts::basic_result<T>),  //
                    BOOST_OUTCOME_TEXPR(convert::value_or_error<basic_result, std::decay_t<T>>{}(std::declval<T>())))
  constexpr explicit basic_result(T &&o,
                                  explicit_valueorerror_converting_constructor_tag /*unused*/ = explicit_valueorerror_converting_constructor_tag())  // NOLINT
      : basic_result{convert::value_or_error<basic_result, std::decay_t<T>>{}(static_cast<T &&>(o))}
  {
  }
  /*! AWAITING HUGO JSON CONVERSION TOOL
SIGNATURE NOT RECOGNISED
*/
  BOOST_OUTCOME_TEMPLATE(class T, class U, class V)
  BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(predicate::template enable_compatible_conversion<T, U, V>))
  constexpr explicit basic_result(
  const basic_result<T, U, V> &o,
  explicit_compatible_copy_conversion_tag /*unused*/ =
  explicit_compatible_copy_conversion_tag()) noexcept(detail::is_nothrow_constructible<value_type, T> &&detail::is_nothrow_constructible<error_type, U>)
      : base{typename base::compatible_conversion_tag(), o}
  {
    using namespace hooks;
    hook_result_copy_construction(this, o);
  }
  /*! AWAITING HUGO JSON CONVERSION TOOL
SIGNATURE NOT RECOGNISED
*/
  BOOST_OUTCOME_TEMPLATE(class T, class U, class V)
  BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(predicate::template enable_compatible_conversion<T, U, V>))
  constexpr explicit basic_result(
  basic_result<T, U, V> &&o,
  explicit_compatible_move_conversion_tag /*unused*/ =
  explicit_compatible_move_conversion_tag()) noexcept(detail::is_nothrow_constructible<value_type, T> &&detail::is_nothrow_constructible<error_type, U>)
      : base{typename base::compatible_conversion_tag(), static_cast<basic_result<T, U, V> &&>(o)}
  {
    using namespace hooks;
    hook_result_move_construction(this, static_cast<basic_result<T, U, V> &&>(o));
  }
  /*! AWAITING HUGO JSON CONVERSION TOOL
SIGNATURE NOT RECOGNISED
*/
  BOOST_OUTCOME_TEMPLATE(class T, class U, class V)
  BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(predicate::template enable_make_error_code_compatible_conversion<T, U, V>))
  constexpr explicit basic_result(const basic_result<T, U, V> &o,
                                  explicit_make_error_code_compatible_copy_conversion_tag /*unused*/ =
                                  explicit_make_error_code_compatible_copy_conversion_tag()) noexcept(detail::is_nothrow_constructible<value_type, T>
                                                                                                      &&noexcept(make_error_code(std::declval<U>())))
      : base{typename base::make_error_code_compatible_conversion_tag(), o}
  {
    using namespace hooks;
    hook_result_copy_construction(this, o);
  }
  /*! AWAITING HUGO JSON CONVERSION TOOL
SIGNATURE NOT RECOGNISED
*/
  BOOST_OUTCOME_TEMPLATE(class T, class U, class V)
  BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(predicate::template enable_make_error_code_compatible_conversion<T, U, V>))
  constexpr explicit basic_result(basic_result<T, U, V> &&o,
                                  explicit_make_error_code_compatible_move_conversion_tag /*unused*/ =
                                  explicit_make_error_code_compatible_move_conversion_tag()) noexcept(detail::is_nothrow_constructible<value_type, T>
                                                                                                      &&noexcept(make_error_code(std::declval<U>())))
      : base{typename base::make_error_code_compatible_conversion_tag(), static_cast<basic_result<T, U, V> &&>(o)}
  {
    using namespace hooks;
    hook_result_move_construction(this, static_cast<basic_result<T, U, V> &&>(o));
  }
  /*! AWAITING HUGO JSON CONVERSION TOOL
SIGNATURE NOT RECOGNISED
*/
  BOOST_OUTCOME_TEMPLATE(class T, class U, class V)
  BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(predicate::template enable_make_exception_ptr_compatible_conversion<T, U, V>))
  constexpr explicit basic_result(const basic_result<T, U, V> &o,
                                  explicit_make_exception_ptr_compatible_copy_conversion_tag /*unused*/ =
                                  explicit_make_exception_ptr_compatible_copy_conversion_tag()) noexcept(detail::is_nothrow_constructible<value_type, T>
                                                                                                         &&noexcept(make_exception_ptr(std::declval<U>())))
      : base{typename base::make_exception_ptr_compatible_conversion_tag(), o}
  {
    using namespace hooks;
    hook_result_copy_construction(this, o);
  }
  /*! AWAITING HUGO JSON CONVERSION TOOL
SIGNATURE NOT RECOGNISED
*/
  BOOST_OUTCOME_TEMPLATE(class T, class U, class V)
  BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(predicate::template enable_make_exception_ptr_compatible_conversion<T, U, V>))
  constexpr explicit basic_result(basic_result<T, U, V> &&o,
                                  explicit_make_exception_ptr_compatible_move_conversion_tag /*unused*/ =
                                  explicit_make_exception_ptr_compatible_move_conversion_tag()) noexcept(detail::is_nothrow_constructible<value_type, T>
                                                                                                         &&noexcept(make_exception_ptr(std::declval<U>())))
      : base{typename base::make_exception_ptr_compatible_conversion_tag(), static_cast<basic_result<T, U, V> &&>(o)}
  {
    using namespace hooks;
    hook_result_move_construction(this, static_cast<basic_result<T, U, V> &&>(o));
  }

  /*! AWAITING HUGO JSON CONVERSION TOOL
SIGNATURE NOT RECOGNISED
*/
  BOOST_OUTCOME_TEMPLATE(class... Args)
  BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(predicate::template enable_inplace_value_constructor<Args...>))
  constexpr explicit basic_result(in_place_type_t<value_type_if_enabled> _, Args &&... args) noexcept(detail::is_nothrow_constructible<value_type, Args...>)
      : base{_, static_cast<Args &&>(args)...}
  {
    using namespace hooks;
    hook_result_in_place_construction(this, in_place_type<value_type>, static_cast<Args &&>(args)...);
  }
  /*! AWAITING HUGO JSON CONVERSION TOOL
SIGNATURE NOT RECOGNISED
*/
  BOOST_OUTCOME_TEMPLATE(class U, class... Args)
  BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(predicate::template enable_inplace_value_constructor<std::initializer_list<U>, Args...>))
  constexpr explicit basic_result(in_place_type_t<value_type_if_enabled> _, std::initializer_list<U> il,
                                  Args &&... args) noexcept(detail::is_nothrow_constructible<value_type, std::initializer_list<U>, Args...>)
      : base{_, il, static_cast<Args &&>(args)...}
  {
    using namespace hooks;
    hook_result_in_place_construction(this, in_place_type<value_type>, il, static_cast<Args &&>(args)...);
  }
  /*! AWAITING HUGO JSON CONVERSION TOOL
SIGNATURE NOT RECOGNISED
*/
  BOOST_OUTCOME_TEMPLATE(class... Args)
  BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(predicate::template enable_inplace_error_constructor<Args...>))
  constexpr explicit basic_result(in_place_type_t<error_type_if_enabled> _, Args &&... args) noexcept(detail::is_nothrow_constructible<error_type, Args...>)
      : base{_, static_cast<Args &&>(args)...}
  {
    using namespace hooks;
    hook_result_in_place_construction(this, in_place_type<error_type>, static_cast<Args &&>(args)...);
  }
  /*! AWAITING HUGO JSON CONVERSION TOOL
SIGNATURE NOT RECOGNISED
*/
  BOOST_OUTCOME_TEMPLATE(class U, class... Args)
  BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(predicate::template enable_inplace_error_constructor<std::initializer_list<U>, Args...>))
  constexpr explicit basic_result(in_place_type_t<error_type_if_enabled> _, std::initializer_list<U> il,
                                  Args &&... args) noexcept(detail::is_nothrow_constructible<error_type, std::initializer_list<U>, Args...>)
      : base{_, il, static_cast<Args &&>(args)...}
  {
    using namespace hooks;
    hook_result_in_place_construction(this, in_place_type<error_type>, il, static_cast<Args &&>(args)...);
  }
  /*! AWAITING HUGO JSON CONVERSION TOOL
SIGNATURE NOT RECOGNISED
*/
  BOOST_OUTCOME_TEMPLATE(class A1, class A2, class... Args)
  BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(predicate::template enable_inplace_value_error_constructor<A1, A2, Args...>))
  constexpr basic_result(A1 &&a1, A2 &&a2, Args &&... args) noexcept(noexcept(
  typename predicate::template choose_inplace_value_error_constructor<A1, A2, Args...>(std::declval<A1>(), std::declval<A2>(), std::declval<Args>()...)))
      : basic_result(in_place_type<typename predicate::template choose_inplace_value_error_constructor<A1, A2, Args...>>, static_cast<A1 &&>(a1),
                     static_cast<A2 &&>(a2), static_cast<Args &&>(args)...)
  {
    /* I was a little surprised that the below is needed given that we forward to another constructor.
    But it turns out that ADL only fires on the first constructor for some reason.
    */
    using namespace hooks;
    // hook_result_in_place_construction(in_place_type<typename predicate::template choose_inplace_value_error_constructor<A1, A2, Args...>>, this);
  }

  /*! AWAITING HUGO JSON CONVERSION TOOL
SIGNATURE NOT RECOGNISED
*/
  constexpr basic_result(const success_type<void> &o) noexcept(std::is_nothrow_default_constructible<value_type>::value)  // NOLINT
      : base{in_place_type<value_type_if_enabled>}
  {
    using namespace hooks;
    hook_result_copy_construction(this, o);
  }
  /*! AWAITING HUGO JSON CONVERSION TOOL
SIGNATURE NOT RECOGNISED
*/
  BOOST_OUTCOME_TEMPLATE(class T)
  BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(predicate::template enable_compatible_conversion<T, void, void>))
  constexpr basic_result(const success_type<T> &o) noexcept(detail::is_nothrow_constructible<value_type, T>)  // NOLINT
      : base{in_place_type<value_type_if_enabled>, detail::extract_value_from_success<value_type>(o)}
  {
    using namespace hooks;
    hook_result_copy_construction(this, o);
  }
  /*! AWAITING HUGO JSON CONVERSION TOOL
SIGNATURE NOT RECOGNISED
*/
  BOOST_OUTCOME_TEMPLATE(class T)
  BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(!std::is_void<T>::value && predicate::template enable_compatible_conversion<T, void, void>))
  constexpr basic_result(success_type<T> &&o) noexcept(detail::is_nothrow_constructible<value_type, T>)  // NOLINT
      : base{in_place_type<value_type_if_enabled>, detail::extract_value_from_success<value_type>(static_cast<success_type<T> &&>(o))}
  {
    using namespace hooks;
    hook_result_move_construction(this, static_cast<success_type<T> &&>(o));
  }
  /*! AWAITING HUGO JSON CONVERSION TOOL
SIGNATURE NOT RECOGNISED
*/
  BOOST_OUTCOME_TEMPLATE(class T)
  BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(predicate::template enable_compatible_conversion<void, T, void>))
  constexpr basic_result(const failure_type<T> &o, explicit_compatible_copy_conversion_tag /*unused*/ = explicit_compatible_copy_conversion_tag()) noexcept(
  detail::is_nothrow_constructible<error_type, T>)  // NOLINT
      : base{in_place_type<error_type_if_enabled>, detail::extract_error_from_failure<error_type>(o)}
  {
    using namespace hooks;
    hook_result_copy_construction(this, o);
  }
  /*! AWAITING HUGO JSON CONVERSION TOOL
SIGNATURE NOT RECOGNISED
*/
  BOOST_OUTCOME_TEMPLATE(class T)
  BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(predicate::template enable_compatible_conversion<void, T, void>))
  constexpr basic_result(failure_type<T> &&o, explicit_compatible_move_conversion_tag /*unused*/ = explicit_compatible_move_conversion_tag()) noexcept(
  detail::is_nothrow_constructible<error_type, T>)  // NOLINT
      : base{in_place_type<error_type_if_enabled>, detail::extract_error_from_failure<error_type>(static_cast<failure_type<T> &&>(o))}
  {
    using namespace hooks;
    hook_result_move_construction(this, static_cast<failure_type<T> &&>(o));
  }
  /*! AWAITING HUGO JSON CONVERSION TOOL
SIGNATURE NOT RECOGNISED
*/
  BOOST_OUTCOME_TEMPLATE(class T)
  BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(predicate::template enable_make_error_code_compatible_conversion<void, T, void>))
  constexpr basic_result(const failure_type<T> &o,
                         explicit_make_error_code_compatible_copy_conversion_tag /*unused*/ =
                         explicit_make_error_code_compatible_copy_conversion_tag()) noexcept(noexcept(make_error_code(std::declval<T>())))  // NOLINT
      : base{in_place_type<error_type_if_enabled>, make_error_code(detail::extract_error_from_failure<error_type>(o))}
  {
    using namespace hooks;
    hook_result_copy_construction(this, o);
  }
  /*! AWAITING HUGO JSON CONVERSION TOOL
SIGNATURE NOT RECOGNISED
*/
  BOOST_OUTCOME_TEMPLATE(class T)
  BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(predicate::template enable_make_error_code_compatible_conversion<void, T, void>))
  constexpr basic_result(failure_type<T> &&o,
                         explicit_make_error_code_compatible_move_conversion_tag /*unused*/ =
                         explicit_make_error_code_compatible_move_conversion_tag()) noexcept(noexcept(make_error_code(std::declval<T>())))  // NOLINT
      : base{in_place_type<error_type_if_enabled>, make_error_code(detail::extract_error_from_failure<error_type>(static_cast<failure_type<T> &&>(o)))}
  {
    using namespace hooks;
    hook_result_move_construction(this, static_cast<failure_type<T> &&>(o));
  }
  /*! AWAITING HUGO JSON CONVERSION TOOL
SIGNATURE NOT RECOGNISED
*/
  BOOST_OUTCOME_TEMPLATE(class T)
  BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(predicate::template enable_make_exception_ptr_compatible_conversion<void, T, void>))
  constexpr basic_result(const failure_type<T> &o,
                         explicit_make_exception_ptr_compatible_copy_conversion_tag /*unused*/ =
                         explicit_make_exception_ptr_compatible_copy_conversion_tag()) noexcept(noexcept(make_exception_ptr(std::declval<T>())))  // NOLINT
      : base{in_place_type<error_type_if_enabled>, make_exception_ptr(detail::extract_error_from_failure<error_type>(o))}
  {
    using namespace hooks;
    hook_result_copy_construction(this, o);
  }
  /*! AWAITING HUGO JSON CONVERSION TOOL
SIGNATURE NOT RECOGNISED
*/
  BOOST_OUTCOME_TEMPLATE(class T)
  BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(predicate::template enable_make_exception_ptr_compatible_conversion<void, T, void>))
  constexpr basic_result(failure_type<T> &&o,
                         explicit_make_exception_ptr_compatible_move_conversion_tag /*unused*/ =
                         explicit_make_exception_ptr_compatible_move_conversion_tag()) noexcept(noexcept(make_exception_ptr(std::declval<T>())))  // NOLINT
      : base{in_place_type<error_type_if_enabled>, make_exception_ptr(detail::extract_error_from_failure<error_type>(static_cast<failure_type<T> &&>(o)))}
  {
    using namespace hooks;
    hook_result_move_construction(this, static_cast<failure_type<T> &&>(o));
  }

  /*! AWAITING HUGO JSON CONVERSION TOOL
SIGNATURE NOT RECOGNISED
*/
  constexpr void swap(basic_result &o) noexcept((std::is_void<value_type>::value || detail::is_nothrow_swappable<value_type>::value)  //
                                                && (std::is_void<error_type>::value || detail::is_nothrow_swappable<error_type>::value))
  {
    constexpr bool value_throws = !std::is_void<value_type>::value && !detail::is_nothrow_swappable<value_type>::value;
    constexpr bool error_throws = !std::is_void<error_type>::value && !detail::is_nothrow_swappable<error_type>::value;
    detail::basic_result_storage_swap<value_throws, error_throws>(*this, o);
  }

  /*! AWAITING HUGO JSON CONVERSION TOOL
SIGNATURE NOT RECOGNISED
*/
  auto as_failure() const & { return failure(this->assume_error()); }
  /*! AWAITING HUGO JSON CONVERSION TOOL
SIGNATURE NOT RECOGNISED
*/
  auto as_failure() && { return failure(static_cast<basic_result &&>(*this).assume_error()); }

#ifdef __APPLE__
  failure_type<error_type> _xcode_workaround_as_failure() &&;
#endif
};

/*! AWAITING HUGO JSON CONVERSION TOOL
SIGNATURE NOT RECOGNISED
*/
template <class R, class S, class P> inline void swap(basic_result<R, S, P> &a, basic_result<R, S, P> &b) noexcept(noexcept(a.swap(b)))
{
  a.swap(b);
}

#if !defined(NDEBUG)
// Check is trivial in all ways except default constructibility
// static_assert(std::is_trivial<basic_result<int, long, policy::all_narrow>>::value, "result<int> is not trivial!");
// static_assert(std::is_trivially_default_constructible<basic_result<int, long, policy::all_narrow>>::value, "result<int> is not trivially default
// constructible!");
static_assert(std::is_trivially_copyable<basic_result<int, long, policy::all_narrow>>::value, "result<int> is not trivially copyable!");
static_assert(std::is_trivially_assignable<basic_result<int, long, policy::all_narrow>, basic_result<int, long, policy::all_narrow>>::value,
              "result<int> is not trivially assignable!");
static_assert(std::is_trivially_destructible<basic_result<int, long, policy::all_narrow>>::value, "result<int> is not trivially destructible!");
static_assert(std::is_trivially_copy_constructible<basic_result<int, long, policy::all_narrow>>::value, "result<int> is not trivially copy constructible!");
static_assert(std::is_trivially_move_constructible<basic_result<int, long, policy::all_narrow>>::value, "result<int> is not trivially move constructible!");
static_assert(std::is_trivially_copy_assignable<basic_result<int, long, policy::all_narrow>>::value, "result<int> is not trivially copy assignable!");
static_assert(std::is_trivially_move_assignable<basic_result<int, long, policy::all_narrow>>::value, "result<int> is not trivially move assignable!");
// Also check is standard layout
static_assert(std::is_standard_layout<basic_result<int, long, policy::all_narrow>>::value, "result<int> is not a standard layout type!");
#endif

BOOST_OUTCOME_V2_NAMESPACE_END

#ifdef __clang__
#pragma clang diagnostic pop
#endif

#endif
