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

            Bug ID: 125680
           Summary: [meta] ICE in nested_anon_class_index, at
                    cp/mangle.cc:1814 with C++26 reflection and anonymous
                    struct array inside lambda within template for loop
           Product: gcc
           Version: 16.1.1
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c++
          Assignee: unassigned at gcc dot gnu.org
          Reporter: berningchen at gmail dot com
  Target Milestone: ---

When using C++26 experimental reflection (-freflection), the compiler triggers
an Internal Compiler Error (ICE) during the mangling phase
(nested_anon_class_index, at cp/mangle.cc). This happens when we query the type
of an element of an array of an anonymous struct (or nested unnamed
union/struct) via decltype inside a lambda closure expression evaluated within
a template metaprogramming context (such as template for), and then attempt to
pass that escaped type reflection info (std::meta::info) to instantiate a
downstream class template.

Compilation options:

-std=c++26 -freflection

Example 1:

#include <concepts>
#include <memory>
#include <meta>
#include <ranges>

namespace detail
{
template <typename Tp> struct is_shared_ptr : std::false_type
{
};
template <typename Tp> struct is_shared_ptr<std::shared_ptr<Tp>> :
std::true_type
{
};

template <typename T> struct ReflectionUtils
{
    static constexpr auto get_members()
    {
        if constexpr (std::is_array_v<T>)
            return std::define_static_array(std::views::iota(0u,
std::meta::extent(^^T)));
        else if constexpr (std::is_class_v<T>)
            return
std::define_static_array(std::meta::nonstatic_data_members_of(^^T,
std::meta::access_context::unchecked()));
        else
            return std::define_static_array(std::vector<std::meta::info>{});
    }
    static constexpr auto ranges = get_members();
};
} // namespace detail

template <typename Tp> struct Reflection
{
    static constexpr bool enabled = false;
};
template <typename Tp>
    requires(std::is_arithmetic_v<std::remove_const_t<Tp>>)
struct Reflection<Tp>
{
    static constexpr bool enabled = true;
};

template <typename T> class ReflectionPanel
{
public:
    void updateGUI() { _panel->updateGUI(); }
    ReflectionPanel(std::shared_ptr<T> target) : _target(target) { buildUI(); }

private:
    template <std::meta::info currField, typename ParentPanelType> class
ReflectionSubPanel
    {
        auto getSelf()
        {
            auto p = [&]()
            {
                if constexpr (std::meta::is_type(currField))
                    return _parent->getField(0);
                else
                    return _parent->template getField<currField>();
            }();

            if constexpr
(detail::is_shared_ptr<std::remove_pointer_t<decltype(p)>>::value)
                return p->get();
            else
                return p;
        }

    public:
        ReflectionSubPanel(ParentPanelType *parent)
            requires(!std::meta::is_type(currField))
            : _parent(parent)
        {
            buildUI();
        }
        ReflectionSubPanel(ParentPanelType *parent, int index)
            requires(std::meta::is_type(currField))
            : _parent(parent)
        {
            buildUI();
        }

    private:
        ParentPanelType *_parent;

        void buildUI()
        {
            using SelfType =
std::remove_pointer_t<decltype(std::declval<ReflectionSubPanel>().getSelf())>;

            constexpr bool isArray = std::is_array_v<SelfType>;
            template for (constexpr auto member :
detail::ReflectionUtils<SelfType>::ranges)
            {
                constexpr auto fieldTypeInfo = [member]()
                {
                    if constexpr (isArray)
                    {
                        using MemberType =
std::remove_pointer_t<decltype(getField(0))>;
                        return ^^MemberType;
                    }
                    else
                    {
                        using MemberType =
std::remove_pointer_t<decltype(getField<member>())>;
                        return ^^MemberType;
                    }
                }();

                if constexpr (!Reflection<typename[:fieldTypeInfo:]>::enabled)
                    if constexpr (isArray)
                        ReflectionSubPanel<fieldTypeInfo,
ReflectionSubPanel>(this, member);
                    else
                        ReflectionSubPanel<member, ReflectionSubPanel>(this);
            }
        }

    public:
        template <std::meta::info internalField> auto getField() //
{dependency, pointer}
        {
            return std::addressof(getSelf()->[:internalField:]);
        }
        auto getField(std::size_t index) // {dependency, pointer}
        {
            return std::addressof((*getSelf())[index]);
        }
    };

    std::shared_ptr<T> _target;
    std::shared_ptr<ReflectionSubPanel<^^_target, ReflectionPanel>> _panel;

    template <std::meta::info field> std::shared_ptr<T> *getField() //
{dependency, pointer}
    {
        return std::addressof(_target);
    }

    void buildUI() { ReflectionSubPanel<^^_target, ReflectionPanel>(this); }
};
;

struct TestStruct
{
    struct
    {
        int b;
    } a[4];
};

int main()
{
   
std::make_shared<ReflectionPanel<TestStruct>>(std::make_shared<TestStruct>());

    return 0;
}

Output:

<source>: In instantiation of
'ReflectionPanel<T>::ReflectionSubPanel<currField,
ParentPanelType>::ReflectionSubPanel(ParentPanelType*, int) requires 
is_type(std::meta::info'meta_type' not supported by
direct_abstract_declarator)(currField) [with std::meta::info currField =
^^MemberType; ParentPanelType =
ReflectionPanel<TestStruct>::ReflectionSubPanel<^^TestStruct::a,
ReflectionPanel<TestStruct>::ReflectionSubPanel<^^ReflectionPanel<TestStruct>::_target,
ReflectionPanel<TestStruct> > >; T = TestStruct]':
<source>:106:25:   required from 'void
ReflectionPanel<T>::ReflectionSubPanel<currField, ParentPanelType>::buildUI()
[with std::meta::info currField = ^^TestStruct::a; ParentPanelType =
ReflectionPanel<TestStruct>::ReflectionSubPanel<^^ReflectionPanel<TestStruct>::_target,
ReflectionPanel<TestStruct> >; T = TestStruct]'
  106 |                         ReflectionSubPanel<fieldTypeInfo,
ReflectionSubPanel>(this, member);
      |                        
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<source>:71:13:   required from
'ReflectionPanel<T>::ReflectionSubPanel<currField,
ParentPanelType>::ReflectionSubPanel(ParentPanelType*) requires !
is_type(std::meta::info'meta_type' not supported by
direct_abstract_declarator)(currField) [with std::meta::info currField =
^^TestStruct::a; ParentPanelType =
ReflectionPanel<TestStruct>::ReflectionSubPanel<^^ReflectionPanel<TestStruct>::_target,
ReflectionPanel<TestStruct> >; T = TestStruct]'
   71 |             buildUI();
      |             ^~~~~~~
<source>:108:25:   required from 'void
ReflectionPanel<T>::ReflectionSubPanel<currField, ParentPanelType>::buildUI()
[with std::meta::info currField = ^^ReflectionPanel<TestStruct>::_target;
ParentPanelType = ReflectionPanel<TestStruct>; T = TestStruct]'
  108 |                         ReflectionSubPanel<member,
ReflectionSubPanel>(this);
      |                        
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<source>:71:13:   required from
'ReflectionPanel<T>::ReflectionSubPanel<currField,
ParentPanelType>::ReflectionSubPanel(ParentPanelType*) requires !
is_type(std::meta::info'meta_type' not supported by
direct_abstract_declarator)(currField) [with std::meta::info currField =
^^ReflectionPanel<TestStruct>::_target; ParentPanelType =
ReflectionPanel<TestStruct>; T = TestStruct]'
   71 |             buildUI();
      |             ^~~~~~~
<source>:131:22:   required from 'void ReflectionPanel<T>::buildUI() [with T =
TestStruct]'
  131 |     void buildUI() { ReflectionSubPanel<^^_target,
ReflectionPanel>(this); }
      |                     
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<source>:45:68:   [ skipping 3 instantiation contexts, use
-ftemplate-backtrace-limit=0 to disable ]
   45 |     ReflectionPanel(std::shared_ptr<T> target) : _target(target) {
buildUI(); }
      |                                                                   
^~~~~~~
/cefs/97/97a4e8c0f997d8d8242b8779_gcc-trunk-20260608/include/c++/17.0.0/bits/shared_ptr_base.h:638:39:
  required from 'std::_Sp_counted_ptr_inplace<_Tp, _Alloc,
_Lp>::_Sp_counted_ptr_inplace(_Alloc, _Args&& ...) [with _Args =
{std::shared_ptr<TestStruct>}; _Tp = ReflectionPanel<TestStruct>; _Alloc =
std::allocator<void>; __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic]'
  638 |           allocator_traits<_Alloc>::construct(__a, _M_ptr(),
      |           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~
  639 |               std::forward<_Args>(__args)...); // might throw
      |               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
/cefs/97/97a4e8c0f997d8d8242b8779_gcc-trunk-20260608/include/c++/17.0.0/bits/shared_ptr_base.h:1000:16:
  required from 'std::__shared_count<_Lp>::__shared_count(_Tp*&,
std::_Sp_alloc_shared_tag<_Alloc>, _Args&& ...) [with _Tp =
ReflectionPanel<TestStruct>; _Alloc = std::allocator<void>; _Args =
{std::shared_ptr<TestStruct>}; __gnu_cxx::_Lock_policy _Lp =
__gnu_cxx::_S_atomic]'
 1000 |           auto __pi = ::new (__mem)
      |                       ^~~~~~~~~~~~~
 1001 |             _Sp_cp_type(__a._M_a, std::forward<_Args>(__args)...);
      |             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/cefs/97/97a4e8c0f997d8d8242b8779_gcc-trunk-20260608/include/c++/17.0.0/bits/shared_ptr_base.h:1776:14:
  required from 'std::__shared_ptr<_Tp,
_Lp>::__shared_ptr(std::_Sp_alloc_shared_tag<_Tp>, _Args&& ...) [with _Alloc =
std::allocator<void>; _Args = {std::shared_ptr<TestStruct>}; _Tp =
ReflectionPanel<TestStruct>; __gnu_cxx::_Lock_policy _Lp =
__gnu_cxx::_S_atomic]'
 1776 |         : _M_ptr(), _M_refcount(_M_ptr, __tag,
std::forward<_Args>(__args)...)
      |                    
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/cefs/97/97a4e8c0f997d8d8242b8779_gcc-trunk-20260608/include/c++/17.0.0/bits/shared_ptr.h:445:59:
  required from
'std::shared_ptr<_Tp>::shared_ptr(std::_Sp_alloc_shared_tag<_Tp>, _Args&& ...)
[with _Alloc = std::allocator<void>; _Args = {std::shared_ptr<TestStruct>}; _Tp
= ReflectionPanel<TestStruct>]'
  445 |         : __shared_ptr<_Tp>(__tag, std::forward<_Args>(__args)...)
      |                                                                  ^
/cefs/97/97a4e8c0f997d8d8242b8779_gcc-trunk-20260608/include/c++/17.0.0/bits/shared_ptr.h:1047:14:
  required from 'std::shared_ptr<std::_NonArray<_Tp> > std::make_shared(_Args&&
...) [with _Tp = ReflectionPanel<TestStruct>; _Args = {shared_ptr<TestStruct>};
_NonArray<_Tp> = ReflectionPanel<TestStruct>]'
 1047 |       return shared_ptr<_Tp>(_Sp_alloc_shared_tag<_Alloc>{__a},
      |              ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 1048 |                              std::forward<_Args>(__args)...);
      |                              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<source>:145:50:   required from here
  145 |    
std::make_shared<ReflectionPanel<TestStruct>>(std::make_shared<TestStruct>());
      |    
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<source>:73:9: internal compiler error: in nested_anon_class_index, at
cp/mangle.cc:1814
   73 |         ReflectionSubPanel(ParentPanelType *parent, int index)
      |         ^~~~~~~~~~~~~~~~~~
0x29f5248 diagnostics::context::diagnostic_impl(rich_location*,
diagnostics::metadata const*, diagnostics::option_id, char const*,
__va_list_tag (*) [1], diagnostics::kind)
        ???:0
0x29e9e8b internal_error(char const*, ...)
        ???:0
0xb33f5c fancy_abort(char const*, int, char const*)
        ???:0
0xca0999 mangle_decl(tree_node*)
        ???:0
0x18fbac5 decl_assembler_name(tree_node*)
        ???:0
0xd15065 maybe_clone_body(tree_node*)
        ???:0
0xe2bf0e expand_or_defer_fn(tree_node*)
        ???:0
0xdec1bd instantiate_decl(tree_node*, bool, bool)
        ???:0
0xdf5efa instantiate_pending_templates(int)
        ???:0
0xc59be8 c_parse_final_cleanups()
        ???:0
0xf17588 c_common_parse_file()
        ???:0

Reply via email to