//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// SPDX-FileCopyrightText: Copyright (c) 2023 NVIDIA CORPORATION & AFFILIATES.
//
//===----------------------------------------------------------------------===//

#ifndef _LIBCUDACXX___TUPLE_STRUCTURED_BINDINGS_H
#define _LIBCUDACXX___TUPLE_STRUCTURED_BINDINGS_H

#ifdef __cuda_std__

#  if defined(_LIBCUDACXX_COMPILER_CLANG)
#    pragma clang diagnostic push
#    pragma clang diagnostic ignored "-Wmismatched-tags"
#  endif // _LIBCUDACXX_COMPILER_CLANG

#  if !defined(__CUDACC_RTC__)
// Fetch utility to get primary template for ::std::tuple_size necessary for the specialization of
// ::std::tuple_size<cuda::std::tuple> to enable structured bindings.
// See https://github.com/NVIDIA/libcudacxx/issues/316
#    include <utility>
#  endif

#  include "../__fwd/array.h"
#  include "../__fwd/pair.h"
#  include "../__fwd/tuple.h"
#  include "../__tuple_dir/tuple_element.h"
#  include "../__tuple_dir/tuple_size.h"
#  include "../__type_traits/integral_constant.h"

// This is a workaround for the fact that structured bindings require that the specializations of
// `tuple_size` and `tuple_element` reside in namespace std (https://eel.is/c++draft/dcl.struct.bind#4).
// See https://github.com/NVIDIA/libcudacxx/issues/316 for a short discussion
#  if _LIBCUDACXX_STD_VER > 14
namespace std {
#    if defined(__CUDACC_RTC__)
template <class... _Tp>
struct tuple_size;

template <size_t _Ip, class... _Tp>
struct tuple_element;
#    endif

template <class _Tp, size_t _Size>
struct tuple_size<_CUDA_VSTD::array<_Tp, _Size>> : _CUDA_VSTD::tuple_size<_CUDA_VSTD::array<_Tp, _Size>>
{};

template <class _Tp, size_t _Size>
struct tuple_size<const _CUDA_VSTD::array<_Tp, _Size>> : _CUDA_VSTD::tuple_size<_CUDA_VSTD::array<_Tp, _Size>>
{};

template <class _Tp, size_t _Size>
struct tuple_size<volatile _CUDA_VSTD::array<_Tp, _Size>> : _CUDA_VSTD::tuple_size<_CUDA_VSTD::array<_Tp, _Size>>
{};

template <class _Tp, size_t _Size>
struct tuple_size<const volatile _CUDA_VSTD::array<_Tp, _Size>> : _CUDA_VSTD::tuple_size<_CUDA_VSTD::array<_Tp, _Size>>
{};

template <size_t _Ip, class _Tp, size_t _Size>
struct tuple_element<_Ip, _CUDA_VSTD::array<_Tp, _Size>> : _CUDA_VSTD::tuple_element<_Ip, _CUDA_VSTD::array<_Tp, _Size>>
{};

template <size_t _Ip, class _Tp, size_t _Size>
struct tuple_element<_Ip, const _CUDA_VSTD::array<_Tp, _Size>>
    : _CUDA_VSTD::tuple_element<_Ip, const _CUDA_VSTD::array<_Tp, _Size>>
{};

template <size_t _Ip, class _Tp, size_t _Size>
struct tuple_element<_Ip, volatile _CUDA_VSTD::array<_Tp, _Size>>
    : _CUDA_VSTD::tuple_element<_Ip, volatile _CUDA_VSTD::array<_Tp, _Size>>
{};

template <size_t _Ip, class _Tp, size_t _Size>
struct tuple_element<_Ip, const volatile _CUDA_VSTD::array<_Tp, _Size>>
    : _CUDA_VSTD::tuple_element<_Ip, const volatile _CUDA_VSTD::array<_Tp, _Size>>
{};

template <class _Tp, class _Up>
struct tuple_size<_CUDA_VSTD::pair<_Tp, _Up>> : _CUDA_VSTD::tuple_size<_CUDA_VSTD::pair<_Tp, _Up>>
{};

template <class _Tp, class _Up>
struct tuple_size<const _CUDA_VSTD::pair<_Tp, _Up>> : _CUDA_VSTD::tuple_size<_CUDA_VSTD::pair<_Tp, _Up>>
{};

template <class _Tp, class _Up>
struct tuple_size<volatile _CUDA_VSTD::pair<_Tp, _Up>> : _CUDA_VSTD::tuple_size<_CUDA_VSTD::pair<_Tp, _Up>>
{};

template <class _Tp, class _Up>
struct tuple_size<const volatile _CUDA_VSTD::pair<_Tp, _Up>> : _CUDA_VSTD::tuple_size<_CUDA_VSTD::pair<_Tp, _Up>>
{};

template <size_t _Ip, class _Tp, class _Up>
struct tuple_element<_Ip, _CUDA_VSTD::pair<_Tp, _Up>> : _CUDA_VSTD::tuple_element<_Ip, _CUDA_VSTD::pair<_Tp, _Up>>
{};

template <size_t _Ip, class _Tp, class _Up>
struct tuple_element<_Ip, const _CUDA_VSTD::pair<_Tp, _Up>>
    : _CUDA_VSTD::tuple_element<_Ip, const _CUDA_VSTD::pair<_Tp, _Up>>
{};

template <size_t _Ip, class _Tp, class _Up>
struct tuple_element<_Ip, volatile _CUDA_VSTD::pair<_Tp, _Up>>
    : _CUDA_VSTD::tuple_element<_Ip, volatile _CUDA_VSTD::pair<_Tp, _Up>>
{};

template <size_t _Ip, class _Tp, class _Up>
struct tuple_element<_Ip, const volatile _CUDA_VSTD::pair<_Tp, _Up>>
    : _CUDA_VSTD::tuple_element<_Ip, const volatile _CUDA_VSTD::pair<_Tp, _Up>>
{};

template <class... _Tp>
struct tuple_size<_CUDA_VSTD::tuple<_Tp...>> : _CUDA_VSTD::tuple_size<_CUDA_VSTD::tuple<_Tp...>>
{};

template <class... _Tp>
struct tuple_size<const _CUDA_VSTD::tuple<_Tp...>> : _CUDA_VSTD::tuple_size<_CUDA_VSTD::tuple<_Tp...>>
{};

template <class... _Tp>
struct tuple_size<volatile _CUDA_VSTD::tuple<_Tp...>> : _CUDA_VSTD::tuple_size<_CUDA_VSTD::tuple<_Tp...>>
{};

template <class... _Tp>
struct tuple_size<const volatile _CUDA_VSTD::tuple<_Tp...>> : _CUDA_VSTD::tuple_size<_CUDA_VSTD::tuple<_Tp...>>
{};

template <size_t _Ip, class... _Tp>
struct tuple_element<_Ip, _CUDA_VSTD::tuple<_Tp...>> : _CUDA_VSTD::tuple_element<_Ip, _CUDA_VSTD::tuple<_Tp...>>
{};

template <size_t _Ip, class... _Tp>
struct tuple_element<_Ip, const _CUDA_VSTD::tuple<_Tp...>>
    : _CUDA_VSTD::tuple_element<_Ip, const _CUDA_VSTD::tuple<_Tp...>>
{};

template <size_t _Ip, class... _Tp>
struct tuple_element<_Ip, volatile _CUDA_VSTD::tuple<_Tp...>>
    : _CUDA_VSTD::tuple_element<_Ip, volatile _CUDA_VSTD::tuple<_Tp...>>
{};

template <size_t _Ip, class... _Tp>
struct tuple_element<_Ip, const volatile _CUDA_VSTD::tuple<_Tp...>>
    : _CUDA_VSTD::tuple_element<_Ip, const volatile _CUDA_VSTD::tuple<_Tp...>>
{};
} // namespace std
#  endif // _LIBCUDACXX_STD_VER > 14

#  if defined(_LIBCUDACXX_COMPILER_CLANG)
#    pragma clang diagnostic pop
#  endif // _LIBCUDACXX_COMPILER_CLANG

#endif // __cuda_std__

#endif // _LIBCUDACXX___TUPLE_STRUCTURED_BINDINGS_H
