/*
 * Copyright (C) 2016-2017 Apple Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 */

// Implementation of Library Fundamentals v3's std::expected, as described here: http://wg21.link/p0323r4

#pragma once

/*
    expected synopsis

namespace std {
namespace experimental {
inline namespace fundamentals_v3 {
    // ?.?.4, Expected for object types
    template <class T, class E>
        class expected;

    // ?.?.5, Expected specialization for void
    template <class E>
        class expected<void,E>;

    // ?.?.6, unexpect tag
    struct unexpect_t {
       unexpect_t() = default;
    };
    inline constexpr unexpect_t unexpect{};

    // ?.?.7, class bad_expected_access
    template <class E>
       class bad_expected_access;

    // ?.?.8, Specialization for void.
    template <>
       class bad_expected_access<void>;

    // ?.?.9, Expected relational operators
    template <class T, class E>
        constexpr bool operator==(const expected<T, E>&, const expected<T, E>&);
    template <class T, class E>
        constexpr bool operator!=(const expected<T, E>&, const expected<T, E>&);

    // ?.?.10, Comparison with T
    template <class T, class E>
      constexpr bool operator==(const expected<T, E>&, const T&);
    template <class T, class E>
      constexpr bool operator==(const T&, const expected<T, E>&);
    template <class T, class E>
      constexpr bool operator!=(const expected<T, E>&, const T&);
    template <class T, class E>
      constexpr bool operator!=(const T&, const expected<T, E>&);

    // ?.?.10, Comparison with unexpected<E>
    template <class T, class E>
      constexpr bool operator==(const expected<T, E>&, const unexpected<E>&);
    template <class T, class E>
      constexpr bool operator==(const unexpected<E>&, const expected<T, E>&);
    template <class T, class E>
      constexpr bool operator!=(const expected<T, E>&, const unexpected<E>&);
    template <class T, class E>
      constexpr bool operator!=(const unexpected<E>&, const expected<T, E>&);

    // ?.?.11, Specialized algorithms
    void swap(expected<T, E>&, expected<T, E>&) noexcept(see below);

    template <class T, class E>
    class expected
    {
    public:
        typedef T value_type;
        typedef E error_type;
        typedef unexpected<E> unexpected_type;
    
        template <class U>
            struct rebind {
            using type = expected<U, error_type>;
          };
    
        // ?.?.4.1, constructors
        constexpr expected();
        constexpr expected(const expected&);
        constexpr expected(expected&&) noexcept(see below);
        template <class U, class G>
            EXPLICIT constexpr expected(const expected<U, G>&);
        template <class U, class G>
            EXPLICIT constexpr expected(expected<U, G>&&);
    
        template <class U = T>
            EXPLICIT constexpr expected(U&& v);
    
        template <class... Args>
            constexpr explicit expected(in_place_t, Args&&...);
        template <class U, class... Args>
            constexpr explicit expected(in_place_t, initializer_list<U>, Args&&...);
        template <class G = E>
            constexpr expected(unexpected<G> const&);
        template <class G = E>
            constexpr expected(unexpected<G> &&);
        template <class... Args>
            constexpr explicit expected(unexpect_t, Args&&...);
        template <class U, class... Args>
            constexpr explicit expected(unexpect_t, initializer_list<U>, Args&&...);
    
        // ?.?.4.2, destructor
        ~expected();
    
        // ?.?.4.3, assignment
        expected& operator=(const expected&);
        expected& operator=(expected&&) noexcept(see below);
        template <class U = T> expected& operator=(U&&);
        template <class G = E>
            expected& operator=(const unexpected<G>&);
        template <class G = E>
            expected& operator=(unexpected<G>&&) noexcept(see below);
    
        template <class... Args>
            void emplace(Args&&...);
        template <class U, class... Args>
            void emplace(initializer_list<U>, Args&&...);
    
        // ?.?.4.4, swap
        void swap(expected&) noexcept(see below);
    
        // ?.?.4.5, observers
        constexpr const T* operator ->() const;
        constexpr T* operator ->();
        constexpr const T& operator *() const&;
        constexpr T& operator *() &;
        constexpr const T&& operator *() const &&;
        constexpr T&& operator *() &&;
        constexpr explicit operator bool() const noexcept;
        constexpr bool has_value() const noexcept;
        constexpr const T& value() const&;
        constexpr T& value() &;
        constexpr const T&& value() const &&;
        constexpr T&& value() &&;
        constexpr const E& error() const&;
        constexpr E& error() &;
        constexpr const E&& error() const &&;
        constexpr E&& error() &&;
        template <class U>
            constexpr T value_or(U&&) const&;
        template <class U>
            T value_or(U&&) &&;
    
    private:
        bool has_val; // exposition only
        union
        {
            value_type val; // exposition only
            unexpected_type unexpect; // exposition only
        };
    };

}}}

*/

#include <cstdlib>
#include <initializer_list>
#include <type_traits>
#include <utility>
#include <wtf/Assertions.h>
#include <wtf/Compiler.h>
#include <wtf/StdLibExtras.h>
#include <wtf/Unexpected.h>

namespace std {
namespace experimental {
inline namespace fundamentals_v3 {

struct unexpected_t {
    unexpected_t() = default;
};
#if __cplusplus < 201703L
#define __EXPECTED_INLINE_VARIABLE static const
#else
#define __EXPECTED_INLINE_VARIABLE inline
#endif

__EXPECTED_INLINE_VARIABLE constexpr unexpected_t unexpect { };

template<class E> class bad_expected_access;

template<>
class bad_expected_access<void> : public std::exception {
public:
    explicit bad_expected_access() { }
};

template<class E>
class bad_expected_access : public bad_expected_access<void> {
public:
    explicit bad_expected_access(E val) : val(val) { }
    virtual const char* what() const noexcept override { return std::exception::what(); }
    E& error() & { return val; }
    const E& error() const& { return val; }
    E&& error() && { return std::move(val); }
    const E&&  error() const&& { return std::move(val); }

private:
    E val;
};

namespace __expected_detail {

#if COMPILER_SUPPORTS(EXCEPTIONS)
#define __EXPECTED_THROW(__exception) (throw __exception)
#else
inline NO_RETURN_DUE_TO_CRASH void __expected_terminate() { RELEASE_ASSERT_NOT_REACHED(); }
#define __EXPECTED_THROW(...) __expected_detail::__expected_terminate()
#endif

__EXPECTED_INLINE_VARIABLE constexpr enum class value_tag_t { } value_tag { };
__EXPECTED_INLINE_VARIABLE constexpr enum class error_tag_t { } error_tag { };

template<class T, std::enable_if_t<std::is_trivially_destructible<T>::value>* = nullptr> void destroy(T&) { }
template<class T, std::enable_if_t<!std::is_trivially_destructible<T>::value && (std::is_class<T>::value || std::is_union<T>::value)>* = nullptr> void destroy(T& t) { t.~T(); }

template<class T, class E>
union constexpr_storage {
    typedef T value_type;
    typedef E error_type;
    typedef unexpected<E> unexpected_type;
    char dummy;
    value_type val;
    error_type err;
    constexpr constexpr_storage() : dummy() { }
    constexpr constexpr_storage(value_tag_t) : val() { }
    constexpr constexpr_storage(error_tag_t) : err() { }
    template<typename U = T>
    constexpr constexpr_storage(value_tag_t, U&& v) : val(std::forward<U>(v)) { }
    template<typename U = E>
    constexpr constexpr_storage(error_tag_t, U&& e) : err(std::forward<U>(e)) { }
    ~constexpr_storage() = default;
};

template<class T, class E>
union storage {
    typedef T value_type;
    typedef E error_type;
    typedef unexpected<E> unexpected_type;
    char dummy;
    value_type val;
    error_type err;
    constexpr storage() : dummy() { }
    constexpr storage(value_tag_t) : val() { }
    constexpr storage(error_tag_t) : err() { }
    constexpr storage(value_tag_t, const value_type& val) : val(val) { }
    constexpr storage(value_tag_t, value_type&& val) : val(std::forward<value_type>(val)) { }
    constexpr storage(error_tag_t, const error_type& err) : err(err) { }
    constexpr storage(error_tag_t, error_type&& err) : err(std::forward<error_type>(err)) { }
    ~storage() { }
};

template<class E>
union constexpr_storage<void, E> {
    typedef void value_type;
    typedef E error_type;
    typedef unexpected<E> unexpected_type;
    char dummy;
    error_type err;
    constexpr constexpr_storage() : dummy() { }
    constexpr constexpr_storage(value_tag_t) : dummy() { }
    constexpr constexpr_storage(error_tag_t) : err() { }
    constexpr constexpr_storage(error_tag_t, const error_type& e) : err(e) { }
    ~constexpr_storage() = default;
};

template<class E>
union storage<void, E> {
    typedef void value_type;
    typedef E error_type;
    typedef unexpected<E> unexpected_type;
    char dummy;
    error_type err;
    constexpr storage() : dummy() { }
    constexpr storage(value_tag_t) : dummy() { }
    constexpr storage(error_tag_t) : err() { }
    constexpr storage(error_tag_t, const error_type& err) : err(err) { }
    constexpr storage(error_tag_t, error_type&& err) : err(std::forward<error_type>(err)) { }
    ~storage() { }
};

template<class T, class E>
struct constexpr_base {
    typedef T value_type;
    typedef E error_type;
    typedef unexpected<E> unexpected_type;
    constexpr_storage<value_type, error_type> s;
    bool has;
    constexpr constexpr_base() : s(), has(true) { }
    constexpr constexpr_base(value_tag_t tag) : s(tag), has(true) { }
    constexpr constexpr_base(error_tag_t tag) : s(tag), has(false) { }
    template<typename U = T>
    constexpr constexpr_base(value_tag_t tag, U&& val) : s(tag, std::forward<U>(val)), has(true) { }
    template<typename U = E>
    constexpr constexpr_base(error_tag_t tag, U&& err) : s(tag, std::forward<U>(err)), has(false) { }
    ~constexpr_base() = default;
};

template<class T, class E>
struct base {
    typedef T value_type;
    typedef E error_type;
    typedef unexpected<E> unexpected_type;
    storage<value_type, error_type> s;
    bool has;
    constexpr base() : s(), has(true) { }
    constexpr base(value_tag_t tag) : s(tag), has(true) { }
    constexpr base(error_tag_t tag) : s(tag), has(false) { }
    constexpr base(value_tag_t tag, const value_type& val) : s(tag, val), has(true) { }
    constexpr base(value_tag_t tag, value_type&& val) : s(tag, std::forward<value_type>(val)), has(true) { }
    constexpr base(error_tag_t tag, const error_type& err) : s(tag, err), has(false) { }
    constexpr base(error_tag_t tag, error_type&& err) : s(tag, std::forward<error_type>(err)), has(false) { }
    base(const base& o)
        : has(o.has)
    {
        if (has)
            ::new (std::addressof(s.val)) value_type(o.s.val);
        else
            ::new (std::addressof(s.err)) error_type(o.s.err);
    }
    base(base&& o)
        : has(o.has)
    {
        if (has)
            ::new (std::addressof(s.val)) value_type(std::move(o.s.val));
        else
            ::new (std::addressof(s.err)) error_type(std::move(o.s.err));
    }
    ~base()
    {
        if (has)
            destroy(s.val);
        else
            destroy(s.err);
    }
};

template<class E>
struct constexpr_base<void, E> {
    typedef void value_type;
    typedef E error_type;
    typedef unexpected<E> unexpected_type;
    constexpr_storage<value_type, error_type> s;
    bool has;
    constexpr constexpr_base() : s(), has(true) { }
    constexpr constexpr_base(value_tag_t tag) : s(tag), has(true) { }
    constexpr constexpr_base(error_tag_t tag) : s(tag), has(false) { }
    constexpr constexpr_base(error_tag_t tag, const error_type& err) : s(tag, err), has(false) { }
    constexpr constexpr_base(error_tag_t tag, error_type&& err) : s(tag, std::forward<error_type>(err)), has(false) { }
    ~constexpr_base() = default;
};

template<class E>
struct base<void, E> {
    typedef void value_type;
    typedef E error_type;
    typedef unexpected<E> unexpected_type;
    storage<value_type, error_type> s;
    bool has;
    constexpr base() : s(), has(true) { }
    constexpr base(value_tag_t tag) : s(tag), has(true) { }
    constexpr base(error_tag_t tag) : s(tag), has(false) { }
    constexpr base(error_tag_t tag, const error_type& err) : s(tag, err), has(false) { }
    constexpr base(error_tag_t tag, error_type&& err) : s(tag, std::forward<error_type>(err)), has(false) { }
    base(const base& o)
        : has(o.has)
    {
        if (!has)
            ::new (std::addressof(s.err)) error_type(o.s.err);
    }
    base(base&& o)
        : has(o.has)
    {
        if (!has)
            ::new (std::addressof(s.err)) error_type(std::move(o.s.err));
    }
    ~base()
    {
        if (!has)
            destroy(s.err);
    }
};

template<class T, class E>
using base_select = typename std::conditional<
    ((std::is_void<T>::value || std::is_trivially_destructible<T>::value)
        && std::is_trivially_destructible<E>::value),
    constexpr_base<typename std::remove_const<T>::type, typename std::remove_const<E>::type>,
    base<typename std::remove_const<T>::type, typename std::remove_const<E>::type>
>::type;

} // namespace __expected_detail

template<class T, class E>
class expected : private __expected_detail::base_select<T, E> {
    typedef __expected_detail::base_select<T, E> base;

public:
    typedef typename base::value_type value_type;
    typedef typename base::error_type error_type;
    typedef typename base::unexpected_type unexpected_type;

private:
    typedef expected<value_type, error_type> type;

public:
    template<class U> struct rebind {
        using type = expected<U, error_type>;
    };

    constexpr expected() : base(__expected_detail::value_tag) { }
    expected(const expected&) = default;
    expected(expected&&) = default;

    constexpr expected(const value_type& e) : base(__expected_detail::value_tag, e) { }
    constexpr expected(value_type&& e) : base(__expected_detail::value_tag, std::forward<value_type>(e)) { }
    template<class... Args> constexpr explicit expected(std::in_place_t, Args&&... args) : base(__expected_detail::value_tag, value_type(std::forward<Args>(args)...)) { }
    // template<class U, class... Args> constexpr explicit expected(in_place_t, std::initializer_list<U>, Args&&...);
    constexpr expected(const unexpected_type& u) : base(__expected_detail::error_tag, u.value()) { }
    constexpr expected(unexpected_type&& u) : base(__expected_detail::error_tag, std::forward<unexpected_type>(u).value()) { }
    template<class Err> constexpr expected(const unexpected<Err>& u) : base(__expected_detail::error_tag, u.value()) { }
    template<class Err> constexpr expected(unexpected<Err>&& u) : base(__expected_detail::error_tag, std::forward<Err>(u.value())) { }
    template<class... Args> constexpr explicit expected(unexpected_t, Args&&... args) : base(__expected_detail::value_tag, unexpected_type(std::forward<Args>(args)...)) { }
    // template<class U, class... Args> constexpr explicit expected(unexpected_t, std::initializer_list<U>, Args&&...);

    ~expected() = default;

    expected& operator=(const expected& e) { type(e).swap(*this); return *this; }
    expected& operator=(expected&& e) { type(std::move(e)).swap(*this); return *this; }
    template<class U> expected& operator=(U&& u) { type(std::move(u)).swap(*this); return *this; }
    expected& operator=(const unexpected_type& u) { type(u).swap(*this); return *this; }
    expected& operator=(unexpected_type&& u) { type(std::move(u)).swap(*this); return *this; }
    // template<class... Args> void emplace(Args&&...);
    // template<class U, class... Args> void emplace(std::initializer_list<U>, Args&&...);

    void swap(expected& o)
    {
        using std::swap;
        if (base::has && o.has)
            swap(base::s.val, o.s.val);
        else if (base::has && !o.has) {
            error_type e(std::move(o.s.err));
            __expected_detail::destroy(o.s.err);
            ::new (std::addressof(o.s.val)) value_type(std::move(base::s.val));
            __expected_detail::destroy(base::s.val);
            ::new (std::addressof(base::s.err)) error_type(std::move(e));
            swap(base::has, o.has);
        } else if (!base::has && o.has) {
            value_type v(std::move(o.s.val));
            __expected_detail::destroy(o.s.val);
            ::new (std::addressof(o.s.err)) error_type(std::move(base::s.err));
            __expected_detail::destroy(base::s.err);
            ::new (std::addressof(base::s.val)) value_type(std::move(v));
            swap(base::has, o.has);
        } else
            swap(base::s.err, o.s.err);
    }

    constexpr const value_type* operator->() const { return &base::s.val; }
    value_type* operator->() { return &base::s.val; }
    constexpr const value_type& operator*() const & { return base::s.val; }
    value_type& operator*() & { return base::s.val; }
    constexpr const value_type&& operator*() const && { return std::move(base::s.val); }
    constexpr value_type&& operator*() && { return std::move(base::s.val); }
    constexpr explicit operator bool() const { return base::has; }
    constexpr bool has_value() const { return base::has; }
    constexpr const value_type& value() const & { return base::has ? base::s.val : (__EXPECTED_THROW(bad_expected_access<error_type>(base::s.err)), base::s.val); }
    constexpr value_type& value() & { return base::has ? base::s.val : (__EXPECTED_THROW(bad_expected_access<error_type>(base::s.err)), base::s.val); }
    constexpr const value_type&& value() const && { return std::move(base::has ? base::s.val : (__EXPECTED_THROW(bad_expected_access<error_type>(base::s.err)), base::s.val)); }
    constexpr value_type&& value() && { return std::move(base::has ? base::s.val : (__EXPECTED_THROW(bad_expected_access<error_type>(base::s.err)), base::s.val)); }
    constexpr const error_type& error() const & { return !base::has ? base::s.err : (__EXPECTED_THROW(bad_expected_access<void>()), base::s.err); }
    error_type& error() & { return !base::has ? base::s.err : (__EXPECTED_THROW(bad_expected_access<void>()), base::s.err); }
    constexpr error_type&& error() && { return std::move(!base::has ? base::s.err : (__EXPECTED_THROW(bad_expected_access<void>()), base::s.err)); }
    constexpr const error_type&& error() const && { return std::move(!base::has ? base::s.err : (__EXPECTED_THROW(bad_expected_access<void>()), base::s.err)); }
    template<class U> constexpr value_type value_or(U&& u) const & { return base::has ? **this : static_cast<value_type>(std::forward<U>(u)); }
    template<class U> value_type value_or(U&& u) && { return base::has ? std::move(**this) : static_cast<value_type>(std::forward<U>(u)); }
};

template<class E>
class expected<void, E> : private __expected_detail::base_select<void, E> {
    typedef __expected_detail::base_select<void, E> base;

public:
    typedef typename base::value_type value_type;
    typedef typename base::error_type error_type;
    typedef typename base::unexpected_type unexpected_type;

private:
    typedef expected<value_type, error_type> type;

public:
    template<class U> struct rebind {
        using type = expected<U, error_type>;
    };

    constexpr expected() : base(__expected_detail::value_tag) { }
    expected(const expected&) = default;
    expected(expected&&) = default;
    // constexpr explicit expected(in_place_t);
    constexpr expected(unexpected_type const& u) : base(__expected_detail::error_tag, u.value()) { }
    constexpr expected(unexpected_type&& u) : base(__expected_detail::error_tag, std::forward<unexpected_type>(u).value()) { }
    template<class Err> constexpr expected(unexpected<Err> const& u) : base(__expected_detail::error_tag, u.value()) { }

    ~expected() = default;

    expected& operator=(const expected& e) { type(e).swap(*this); return *this; }
    expected& operator=(expected&& e) { type(std::move(e)).swap(*this); return *this; }
    expected& operator=(const unexpected_type& u) { type(u).swap(*this); return *this; } // Not in the current paper.
    expected& operator=(unexpected_type&& u) { type(std::move(u)).swap(*this); return *this; } // Not in the current paper.
    // void emplace();

    void swap(expected& o)
    {
        using std::swap;
        if (base::has && o.has) {
            // Do nothing.
        } else if (base::has && !o.has) {
            error_type e(std::move(o.s.err));
            ::new (std::addressof(base::s.err)) error_type(e);
            swap(base::has, o.has);
        } else if (!base::has && o.has) {
            ::new (std::addressof(o.s.err)) error_type(std::move(base::s.err));
            swap(base::has, o.has);
        } else
            swap(base::s.err, o.s.err);
    }

    constexpr explicit operator bool() const { return base::has; }
    constexpr bool has_value() const { return base::has; }
    void value() const { !base::has ? __EXPECTED_THROW(bad_expected_access<void>()) : void(); }
    constexpr const E& error() const & { return !base::has ? base::s.err : (__EXPECTED_THROW(bad_expected_access<void>()), base::s.err); }
    E& error() & { return !base::has ? base::s.err : (__EXPECTED_THROW(bad_expected_access<void>()), base::s.err); }
    constexpr E&& error() && { return std::move(!base::has ? base::s.err : (__EXPECTED_THROW(bad_expected_access<void>()), base::s.err)); }
};

template<class T, class E> constexpr bool operator==(const expected<T, E>& x, const expected<T, E>& y) { return bool(x) == bool(y) && (x ? x.value() == y.value() : x.error() == y.error()); }
template<class T, class E> constexpr bool operator!=(const expected<T, E>& x, const expected<T, E>& y) { return !(x == y); }

template<class E> constexpr bool operator==(const expected<void, E>& x, const expected<void, E>& y) { return bool(x) == bool(y) && (x ? true : x.error() == y.error()); }

template<class T, class E> constexpr bool operator==(const expected<T, E>& x, const T& y) { return x ? *x == y : false; }
template<class T, class E> constexpr bool operator==(const T& x, const expected<T, E>& y) { return y ? x == *y : false; }
template<class T, class E> constexpr bool operator!=(const expected<T, E>& x, const T& y) { return x ? *x != y : true; }
template<class T, class E> constexpr bool operator!=(const T& x, const expected<T, E>& y) { return y ? x != *y : true; }

template<class T, class E> constexpr bool operator==(const expected<T, E>& x, const unexpected<E>& y) { return x ? false : x.error() == y.value(); }
template<class T, class E> constexpr bool operator==(const unexpected<E>& x, const expected<T, E>& y) { return y ? false : x.value() == y.error(); }
template<class T, class E> constexpr bool operator!=(const expected<T, E>& x, const unexpected<E>& y) { return x ? true : x.error() != y.value(); }
template<class T, class E> constexpr bool operator!=(const unexpected<E>& x, const expected<T, E>& y) { return y ? true : x.value() != y.error(); }

template<typename T, typename E> void swap(expected<T, E>& x, expected<T, E>& y) { x.swap(y); }

}}} // namespace std::experimental::fundamentals_v3

__EXPECTED_INLINE_VARIABLE constexpr auto& unexpect = std::experimental::unexpect;
template<class T, class E> using Expected = std::experimental::expected<T, E>;
