This program: #include <string_view> namespace { constexpr uint64_t CalcHash (std::string_view name) { return 0; } template<typename T> struct Command { const uint64_t Hash_; const T Cmd_; consteval Command (std::string_view name, T cmd) : Hash_ { CalcHash (name) } , Cmd_ { cmd } { } }; template<typename T> consteval auto MakeHash (auto&&... commands) { const std::initializer_list<Command<T>> commandsList { commands... }; for (auto i = commandsList.begin (); i != std::prev (commandsList.end ()); ++i) for (auto j = std::next (i); j != commandsList.end (); ++j) if (i->Hash_ == j->Hash_) throw "duplicate hashes"; return [=] (std::string_view str) { const auto strHash = CalcHash (str); T result {}; ((commands.Hash_ == strHash && (result = commands.Cmd_)) || ...); return result; }; } } struct Foo { int foo1(int x) { return x * 1; } }; int bar(std::string_view arg) { constexpr auto hash = MakeHash<int (Foo::*) (int)> (Command { "foo1", &Foo::foo1 }); Foo f {}; auto res = hash(arg); return (f.*res)(42); } compiled with -std=c++20 (as in g++ -std=c++20 foo.cpp -o foo.o) results in foo.cpp: In function 'consteval auto {anonymous}::MakeHash(auto:11&& ...) [with T = int (Foo::*)(int); auto:11 = {{anonymous}::Command<int (Foo::*)(int)>}]': foo.cpp:27:17: internal compiler error: in cp_gimplify_expr, at cp/cp-gimplify.c:557 27 | for (auto i = commandsList.begin (); i != std::prev (commandsList.end ()); ++i) | ^~~ 0x164bc1d internal_error(char const*, ...) ???:0 0x604ee1 fancy_abort(char const*, int, char const*) ???:0 0xa7662b gimplify_expr(tree_node**, gimple**, gimple**, bool (*)(tree_node*), int) ???:0 0xa77265 gimplify_expr(tree_node**, gimple**, gimple**, bool (*)(tree_node*), int) ???:0 0xa7734c gimplify_expr(tree_node**, gimple**, gimple**, bool (*)(tree_node*), int) ???:0 0xa77265 gimplify_expr(tree_node**, gimple**, gimple**, bool (*)(tree_node*), int) ???:0 0xa7734c gimplify_expr(tree_node**, gimple**, gimple**, bool (*)(tree_node*), int) ???:0 0xa85bd4 gimplify_body(tree_node*, bool) ???:0 0xa8611e gimplify_function_tree(tree_node*) ???:0 0x8fd5ab cgraph_node::analyze() ???:0 0x903af3 symbol_table::finalize_compilation_unit() ???:0 Please submit a full bug report, with preprocessed source if appropriate. Please include the complete backtrace with any bug report. See <https://bugs.gentoo.org/> for instructions. This reproduces both with gcc 11.1.0 and 11.2.1: Using built-in specs. COLLECT_GCC=gcc COLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-pc-linux-gnu/11.2.1/lto-wrapper Target: x86_64-pc-linux-gnu Configured with: /var/tmp/portage/sys-devel/gcc-11.2.1_p20211127/work/gcc-11-20211127/configure --host=x86_64-pc-linux-gnu --build=x86_64-pc-linux-gnu --prefix=/usr --bindir=/usr/x86_64-pc-linux-gnu/gcc-bin/11.2.1 --includedir=/usr/lib/gcc/x86_64-pc-linux-gnu/11.2.1/include --datadir=/usr/share/gcc-data/x86_64-pc-linux-gnu/11.2.1 --mandir=/usr/share/gcc-data/x86_64-pc-linux-gnu/11.2.1/man --infodir=/usr/share/gcc-data/x86_64-pc-linux-gnu/11.2.1/info --with-gxx-include-dir=/usr/lib/gcc/x86_64-pc-linux-gnu/11.2.1/include/g++-v11 --with-python-dir=/share/gcc-data/x86_64-pc-linux-gnu/11.2.1/python --enable-languages=c,c++,fortran --enable-obsolete --enable-secureplt --disable-werror --with-system-zlib --enable-nls --without-included-gettext --disable-libunwind-exceptions --enable-checking=release --with-bugurl=https://bugs.gentoo.org/ --with-pkgversion='Gentoo 11.2.1_p20211127 p3' --disable-esp --enable-libstdcxx-time --with-build-config=bootstrap-lto --enable-shared --enable-threads=posix --enable-__cxa_atexit --enable-clocale=gnu --enable-multilib --with-multilib-list=m32,m64 --disable-fixed-point --enable-targets=all --enable-libgomp --disable-libssp --disable-libada --disable-cet --disable-systemtap --disable-valgrind-annotations --disable-vtable-verify --disable-libvtv --without-zstd --enable-lto --with-isl --disable-isl-version-check --disable-default-pie --enable-default-ssp Thread model: posix Supported LTO compression algorithms: zlib gcc version 11.2.1 20211127 (Gentoo 11.2.1_p20211127 p3)
Confirmed reduced testcase: struct f{f();}; consteval auto MakeHash () { if (1) ; return [] (f str) { }; } void bar(f arg) { auto hash = MakeHash (); hash(arg); }
Started with r10-4303-gf968ef9b8df2bc22, was rejected before the revision.
This happens because of: /* Preserve a functions function context node. It will later be needed to output debug info. */ if (tree fn = decl_function_context (decl)) { cgraph_node *origin_node = cgraph_node::get_create (fn); enqueue_node (origin_node); } in cgraphunit.c, operator() of the lambda is needed, so it is enqueued and the above forces gimplification of the consteval function too, which is a big no no, such functions shouldn't exist. The big question is what do we want to do for debug info in these cases. When using instead: struct A {A (); }; constexpr auto foo () { if (1) ; return [] (A x) { }; } void bar (A x) { constinit static auto h = foo (); h (x); } ipa.c discovers that foo isn't reachable even when it is needed (as function context of the lambda) and throws away the body and in the debug info just emits DW_TAG_subprogram etc. for foo, but only emits non-code related stuff for it.
Created attachment 52130 [details] gcc12-pr103912.patch Untested fix.
The master branch has been updated by Jakub Jelinek <jakub@gcc.gnu.org>: https://gcc.gnu.org/g:54fa7daefe35cacf4a933947d1802318da193c01 commit r12-6423-g54fa7daefe35cacf4a933947d1802318da193c01 Author: Jakub Jelinek <jakub@redhat.com> Date: Mon Jan 10 20:49:11 2022 +0100 c++: Ensure some more that immediate functions aren't gimplified [PR103912] Immediate functions should never be emitted into assembly, the FE doesn't genericize them and does various things to ensure they aren't gimplified. But the following testcase ICEs anyway due to that, because the consteval function returns a lambda, and operator() of the lambda has decl_function_context of the consteval function. cgraphunit.c then does: /* Preserve a functions function context node. It will later be needed to output debug info. */ if (tree fn = decl_function_context (decl)) { cgraph_node *origin_node = cgraph_node::get_create (fn); enqueue_node (origin_node); } which enqueues the immediate function and then tries to gimplify it, which results in ICE because it hasn't been genericized. When I try similar testcase with constexpr instead of consteval and static constinit auto instead of auto in main, what happens is that the functions are gimplified, later ipa.c discovers they aren't reachable and sets body_removed to true for them (and clears other flags) and we end up with a debug info which has the foo and bar functions without DW_AT_low_pc and other code specific attributes, just stuff from its BLOCK structure and in there the lambda with DW_AT_low_pc etc. The following patch attempts to emulate that behavior early, so that cgraph doesn't try to gimplify those and pretends they were already gimplified and found unused and optimized away. 2022-01-10 Jakub Jelinek <jakub@redhat.com> PR c++/103912 * semantics.c (expand_or_defer_fn): For immediate functions, set node->body_removed to true and clear analyzed, definition and force_output. * decl2.c (c_parse_final_cleanups): Ignore immediate functions for expand_or_defer_fn. * g++.dg/cpp2a/consteval26.C: New test.
The releases/gcc-11 branch has been updated by Jakub Jelinek <jakub@gcc.gnu.org>: https://gcc.gnu.org/g:18abe529d092ca00903fe6a9ec5210c91d45030f commit r11-9499-g18abe529d092ca00903fe6a9ec5210c91d45030f Author: Jakub Jelinek <jakub@redhat.com> Date: Mon Jan 10 20:49:11 2022 +0100 c++: Ensure some more that immediate functions aren't gimplified [PR103912] Immediate functions should never be emitted into assembly, the FE doesn't genericize them and does various things to ensure they aren't gimplified. But the following testcase ICEs anyway due to that, because the consteval function returns a lambda, and operator() of the lambda has decl_function_context of the consteval function. cgraphunit.c then does: /* Preserve a functions function context node. It will later be needed to output debug info. */ if (tree fn = decl_function_context (decl)) { cgraph_node *origin_node = cgraph_node::get_create (fn); enqueue_node (origin_node); } which enqueues the immediate function and then tries to gimplify it, which results in ICE because it hasn't been genericized. When I try similar testcase with constexpr instead of consteval and static constinit auto instead of auto in main, what happens is that the functions are gimplified, later ipa.c discovers they aren't reachable and sets body_removed to true for them (and clears other flags) and we end up with a debug info which has the foo and bar functions without DW_AT_low_pc and other code specific attributes, just stuff from its BLOCK structure and in there the lambda with DW_AT_low_pc etc. The following patch attempts to emulate that behavior early, so that cgraph doesn't try to gimplify those and pretends they were already gimplified and found unused and optimized away. 2022-01-10 Jakub Jelinek <jakub@redhat.com> PR c++/103912 * semantics.c (expand_or_defer_fn): For immediate functions, set node->body_removed to true and clear analyzed, definition and force_output. * decl2.c (c_parse_final_cleanups): Ignore immediate functions for expand_or_defer_fn. * g++.dg/cpp2a/consteval26.C: New test. (cherry picked from commit 54fa7daefe35cacf4a933947d1802318da193c01)
Fixed for 11.3 too.
*** Bug 105060 has been marked as a duplicate of this bug. ***
The releases/gcc-10 branch has been updated by Jakub Jelinek <jakub@gcc.gnu.org>: https://gcc.gnu.org/g:9b80a9b0466f02e6e30790396c2b1545a9f8ff94 commit r10-10665-g9b80a9b0466f02e6e30790396c2b1545a9f8ff94 Author: Jakub Jelinek <jakub@redhat.com> Date: Mon Jan 10 20:49:11 2022 +0100 c++: Ensure some more that immediate functions aren't gimplified [PR103912] Immediate functions should never be emitted into assembly, the FE doesn't genericize them and does various things to ensure they aren't gimplified. But the following testcase ICEs anyway due to that, because the consteval function returns a lambda, and operator() of the lambda has decl_function_context of the consteval function. cgraphunit.c then does: /* Preserve a functions function context node. It will later be needed to output debug info. */ if (tree fn = decl_function_context (decl)) { cgraph_node *origin_node = cgraph_node::get_create (fn); enqueue_node (origin_node); } which enqueues the immediate function and then tries to gimplify it, which results in ICE because it hasn't been genericized. When I try similar testcase with constexpr instead of consteval and static constinit auto instead of auto in main, what happens is that the functions are gimplified, later ipa.c discovers they aren't reachable and sets body_removed to true for them (and clears other flags) and we end up with a debug info which has the foo and bar functions without DW_AT_low_pc and other code specific attributes, just stuff from its BLOCK structure and in there the lambda with DW_AT_low_pc etc. The following patch attempts to emulate that behavior early, so that cgraph doesn't try to gimplify those and pretends they were already gimplified and found unused and optimized away. 2022-01-10 Jakub Jelinek <jakub@redhat.com> PR c++/103912 * semantics.c (expand_or_defer_fn): For immediate functions, set node->body_removed to true and clear analyzed, definition and force_output. * decl2.c (c_parse_final_cleanups): Ignore immediate functions for expand_or_defer_fn. * g++.dg/cpp2a/consteval26.C: New test. (cherry picked from commit 54fa7daefe35cacf4a933947d1802318da193c01)
Fixed for 10.4 too.