// coroutine standard header (core)

// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#ifndef _COROUTINE_
#define _COROUTINE_
#include <yvals_core.h>
#if _STL_COMPILER_PREPROCESSOR

#ifdef _RESUMABLE_FUNCTIONS_SUPPORTED
_EMIT_STL_WARNING(STL4039, "The contents of <coroutine> are not available with /await. "
                           "Remove /await or use /await:strict for standard coroutines. "
                           "Use <experimental/coroutine> for legacy /await support.");
#else // ^^^ /await / no /await vvv
#ifndef __cpp_lib_coroutine
_EMIT_STL_WARNING(STL4038, "The contents of <coroutine> are available only with C++20 or later or /await:strict.");
#else // ^^^ <coroutine> is not available / <coroutine> is available vvv
#ifndef _ALLOW_COROUTINE_ABI_MISMATCH
#pragma detect_mismatch("_COROUTINE_ABI", "2")
#endif // !defined(_ALLOW_COROUTINE_ABI_MISMATCH)

#if _HAS_CXX20
#include <compare>
#endif // _HAS_CXX20
#include <type_traits>

#pragma pack(push, _CRT_PACKING)
#pragma warning(push, _STL_WARNING_LEVEL)
#pragma warning(disable : _STL_DISABLED_WARNINGS)
_STL_DISABLE_CLANG_WARNINGS
#pragma push_macro("new")
#undef new

_STD_BEGIN

template <class _Ret, class = void>
struct _Coroutine_traits {};

template <class _Ret>
struct _Coroutine_traits<_Ret, void_t<typename _Ret::promise_type>> {
    using promise_type = typename _Ret::promise_type;
};

_EXPORT_STD template <class _Ret, class...>
struct coroutine_traits : _Coroutine_traits<_Ret> {};

_EXPORT_STD template <class = void>
struct coroutine_handle;

template <>
struct coroutine_handle<void> {
    constexpr coroutine_handle() noexcept = default;
    constexpr coroutine_handle(nullptr_t) noexcept {}

    coroutine_handle& operator=(nullptr_t) noexcept {
        _Ptr = nullptr;
        return *this;
    }

    _NODISCARD constexpr void* address() const noexcept {
        return _Ptr;
    }

    _NODISCARD static constexpr coroutine_handle from_address(void* const _Addr) noexcept { // strengthened
        coroutine_handle _Result;
        _Result._Ptr = _Addr;
        return _Result;
    }

    constexpr explicit operator bool() const noexcept {
        return _Ptr != nullptr;
    }

    _NODISCARD bool done() const noexcept { // strengthened
        return __builtin_coro_done(_Ptr);
    }

    void operator()() const {
        __builtin_coro_resume(_Ptr);
    }

    void resume() const {
        __builtin_coro_resume(_Ptr);
    }

    void destroy() const noexcept { // strengthened
        __builtin_coro_destroy(_Ptr);
    }

private:
    void* _Ptr = nullptr;
};

_EXPORT_STD template <class _CoroPromise>
struct coroutine_handle {
    constexpr coroutine_handle() noexcept = default;
    constexpr coroutine_handle(nullptr_t) noexcept {}

    _NODISCARD static coroutine_handle from_promise(_CoroPromise& _Prom) noexcept { // strengthened
        const auto _Prom_ptr  = const_cast<void*>(static_cast<const volatile void*>(_STD addressof(_Prom)));
        const auto _Frame_ptr = __builtin_coro_promise(_Prom_ptr, 0, true);
        coroutine_handle _Result;
        _Result._Ptr = _Frame_ptr;
        return _Result;
    }

    coroutine_handle& operator=(nullptr_t) noexcept {
        _Ptr = nullptr;
        return *this;
    }

    _NODISCARD constexpr void* address() const noexcept {
        return _Ptr;
    }

    _NODISCARD static constexpr coroutine_handle from_address(void* const _Addr) noexcept { // strengthened
        coroutine_handle _Result;
        _Result._Ptr = _Addr;
        return _Result;
    }

    constexpr operator coroutine_handle<>() const noexcept {
        return coroutine_handle<>::from_address(_Ptr);
    }

    constexpr explicit operator bool() const noexcept {
        return _Ptr != nullptr;
    }

    _NODISCARD bool done() const noexcept { // strengthened
        return __builtin_coro_done(_Ptr);
    }

    void operator()() const {
        __builtin_coro_resume(_Ptr);
    }

    void resume() const {
        __builtin_coro_resume(_Ptr);
    }

    void destroy() const noexcept { // strengthened
        __builtin_coro_destroy(_Ptr);
    }

    _NODISCARD _CoroPromise& promise() const noexcept { // strengthened
        return *reinterpret_cast<_CoroPromise*>(__builtin_coro_promise(_Ptr, 0, false));
    }

private:
    void* _Ptr = nullptr;
};

_EXPORT_STD _NODISCARD constexpr bool operator==(
    const coroutine_handle<> _Left, const coroutine_handle<> _Right) noexcept {
    return _Left.address() == _Right.address();
}

#if _HAS_CXX20
_EXPORT_STD _NODISCARD constexpr strong_ordering operator<=>(
    const coroutine_handle<> _Left, const coroutine_handle<> _Right) noexcept {
    return compare_three_way{}(_Left.address(), _Right.address());
}
#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv
_NODISCARD constexpr bool operator!=(const coroutine_handle<> _Left, const coroutine_handle<> _Right) noexcept {
    return !(_Left == _Right);
}

_NODISCARD constexpr bool operator<(const coroutine_handle<> _Left, const coroutine_handle<> _Right) noexcept {
    return less<void*>{}(_Left.address(), _Right.address());
}

_NODISCARD constexpr bool operator>(const coroutine_handle<> _Left, const coroutine_handle<> _Right) noexcept {
    return _Right < _Left;
}

_NODISCARD constexpr bool operator<=(const coroutine_handle<> _Left, const coroutine_handle<> _Right) noexcept {
    return !(_Left > _Right);
}

_NODISCARD constexpr bool operator>=(const coroutine_handle<> _Left, const coroutine_handle<> _Right) noexcept {
    return !(_Left < _Right);
}
#endif // ^^^ !_HAS_CXX20 ^^^

template <class _CoroPromise>
struct hash<coroutine_handle<_CoroPromise>> {
    _NODISCARD _STATIC_CALL_OPERATOR size_t operator()(
        const coroutine_handle<_CoroPromise>& _Coro) _CONST_CALL_OPERATOR noexcept {
        return _Hash_representation(_Coro.address());
    }
};

_EXPORT_STD struct noop_coroutine_promise {};

template <>
struct coroutine_handle<noop_coroutine_promise> {
    friend coroutine_handle noop_coroutine() noexcept;

    constexpr operator coroutine_handle<>() const noexcept {
        return coroutine_handle<>::from_address(_Ptr);
    }

    constexpr explicit operator bool() const noexcept {
        return true;
    }
    _NODISCARD constexpr bool done() const noexcept {
        return false;
    }

    constexpr void operator()() const noexcept {}
    constexpr void resume() const noexcept {}
    constexpr void destroy() const noexcept {}

    _NODISCARD noop_coroutine_promise& promise() const noexcept {
        // Returns a reference to the associated promise
        return *static_cast<noop_coroutine_promise*>(__builtin_coro_promise(_Ptr, 0, false));
    }

    _NODISCARD constexpr void* address() const noexcept {
        return _Ptr;
    }

private:
    coroutine_handle() noexcept = default;

    void* _Ptr = __builtin_coro_noop();
};

_EXPORT_STD using noop_coroutine_handle = coroutine_handle<noop_coroutine_promise>;

_EXPORT_STD _NODISCARD inline noop_coroutine_handle noop_coroutine() noexcept {
    // Returns a handle to a coroutine that has no observable effects when resumed or destroyed.
    return noop_coroutine_handle{};
}

_EXPORT_STD struct suspend_never {
    _NODISCARD constexpr bool await_ready() const noexcept {
        return true;
    }

    constexpr void await_suspend(coroutine_handle<>) const noexcept {}
    constexpr void await_resume() const noexcept {}
};

_EXPORT_STD struct suspend_always {
    _NODISCARD constexpr bool await_ready() const noexcept {
        return false;
    }

    constexpr void await_suspend(coroutine_handle<>) const noexcept {}
    constexpr void await_resume() const noexcept {}
};

_STD_END

#pragma pop_macro("new")
_STL_RESTORE_CLANG_WARNINGS
#pragma warning(pop)
#pragma pack(pop)

#endif // ^^^ defined(__cpp_lib_coroutine) ^^^
#endif // ^^^ !defined(_RESUMABLE_FUNCTIONS_SUPPORTED) ^^^
#endif // _STL_COMPILER_PREPROCESSOR
#endif // _COROUTINE_
