https://gcc.gnu.org/bugzilla/show_bug.cgi?id=97601

            Bug ID: 97601
           Summary: ICE when using type determined by
                    std::tuple_element_t<...>, on tuple generated from
                    type id stored in std::array
           Product: gcc
           Version: 10.2.1
               URL: https://gcc.godbolt.org/z/n13PKr
            Status: UNCONFIRMED
          Keywords: ice-on-valid-code
          Severity: normal
          Priority: P3
         Component: c++
          Assignee: unassigned at gcc dot gnu.org
          Reporter: milasudril at gmail dot com
  Target Milestone: ---
              Host: any
            Target: any

Code that triggers the bug(s). It uses enum values stored in an std::array, to
generate an std::tuple. Using std::tuple_element_t on the generated tuple
causes ICE.

Compiler options: -Wall -std=c++20 -O3

#include <memory>
#include <type_traits>
#include <array>
#include <complex>

namespace Enum
{
        template<class T>
        using Empty = std::type_identity<T>;

    template<class T>
        constexpr auto add(T base, std::underlying_type_t<T> offset = 1)
        {
                return static_cast<T>(std::underlying_type_t<T>(base) +
offset);
        }

        namespace detail
        {
                template<auto types,
                         template<auto>
                         class EnumItemTraits,
                         class T = std::make_integer_sequence<size_t,
types.size()>>
                struct make_tuple_from_array;

                template<auto types, template<auto> class EnumItemTraits,
size_t index>
                struct int_to_type_array: public EnumItemTraits<types[index]>
                {
                };

                template<auto types, template<auto> class EnumItemTraits,
size_t... indices>
                struct make_tuple_from_array<types,
                                             EnumItemTraits,
                                             std::integer_sequence<size_t,
indices...>>
                {
                        using type =
                            std::tuple<typename int_to_type_array<types,
EnumItemTraits, indices>::type...>;
                };
        }

        template<auto types, template<auto> class EnumItemTraits>
        using TupleFromTypeArray = typename
detail::make_tuple_from_array<types, EnumItemTraits>::type;
}

enum class PortType : size_t
{
    RgbaPixels,
    GrayscaleRealPixels,
    GrayscaleComplexPixels,
};

constexpr PortType begin(Enum::Empty<PortType>) { return PortType::RgbaPixels;
}
constexpr PortType end(Enum::Empty<PortType>)
{
    return Enum::add(PortType::GrayscaleComplexPixels);
}

using vec4_t __attribute__((vector_size(16))) = float;
using RgbaValue    = vec4_t;
using RealValue    = double;
using ComplexValue = std::complex<RealValue>;

template<PortType id>
struct PortTypeToType;

template<>
struct PortTypeToType<PortType::RgbaPixels>
{
    using type = std::unique_ptr<RgbaValue[]>;
};

template<>
struct PortTypeToType<PortType::GrayscaleRealPixels>
{
    using type = std::unique_ptr<RealValue[]>;
};

template<>
struct PortTypeToType<PortType::GrayscaleComplexPixels>
{
    using type = std::unique_ptr<ComplexValue[]>;
};

template<class T>
struct InputPortType
{
    using type = std::conditional_t<(sizeof(T) <= 16), T,
std::reference_wrapper<T const>>;
};

template<class T>
struct InputPortType<std::unique_ptr<T[]>>
{
    using type = T const*;
};

template<class T>
struct InputPortType<std::unique_ptr<T>>
{
    using type = std::reference_wrapper<T const>;
};

namespace detail
{
    template<PortType t>
    struct GenInputPortType
    {
        using type = typename InputPortType<typename
PortTypeToType<t>::type>::type;
    };
}

namespace detail
{
    template<class Object, class F, size_t... I>
    constexpr decltype(auto) create_impl(F&& f, std::index_sequence<I...>)
    {
        return Object{f(std::integral_constant<size_t, I>{})...};
    }
}

template<class Tuple, class F, size_t... I>
constexpr decltype(auto) createTuple(F&& f)
{
    return detail::create_impl<Tuple>(std::forward<F>(f),
                                       
std::make_index_sequence<std::tuple_size_v<Tuple>>{});
}

template<class T>
void doStuffWithTArg(T);

template<class T>
void doStuffWithT();


template<auto types>
class InArgTuple
{
    using storage_type = Enum::TupleFromTypeArray<types,
detail::GenInputPortType>;

public:
    constexpr explicit InArgTuple()
        : m_data{createTuple<storage_type>([]<class Tag>(Tag) {
            using T = typename
detail::GenInputPortType<types[Tag::value]>::type;

            // Trunk gets stuck here 
            // internal compiler error: in cxx_eval_constant_expression, at
cp/constexpr.c:6188
            using OtherT = std::tuple_element_t<Tag::value, storage_type>;

            static_assert(std::is_same_v<T, OtherT>);
            T foo{};
            // This triggers ICE: in finish_expr_stmt, at cp/semantics.c:681
            // doStuffWithT<OtherT>();

            doStuffWithT<T>();  // Not ICE

            OtherT bar{};
            // This also triggers ICE: in tsubst_copy, at cp/pt.c:16485
            //doStuffWithTArg(bar);

            doStuffWithTArg(foo);  // Not ICE
            //
            // Also in tsubst_copy, at cp/pt.c:16485
            //
            // return bar;
            //
            return foo;
        })}
    {
    }

    static constexpr auto size() { return types.size(); }

    template<size_t index>
    constexpr auto get() const
    {
        static_assert(index < types.size());
        return std::get<index>(m_data);
    }

private:
    storage_type m_data;
};

void test()
{
    constexpr std::array<PortType, 1> types{PortType::RgbaPixels};
    InArgTuple<types> test{};
}

Reply via email to