The function that is causing the ICE: ``` constexpr int index_in_struct(const auto& s, auto... member) { int index = -1; int k = 0; avnd::for_each_field_ref(s, [&](auto& m) { if constexpr(requires { bool(&m == &(s.*....*member)); }) { if(&m == &(s.*....*member)) { index = k; } } ++k; }); assert(index >= 0); return index; } ``` The error: /home/jcelerier/score/3rdparty/avendish/include/avnd/common/for_nth.hpp:114:26: internal compiler error: trying to capture 'member#0' in instantiation of generic lambda 114 | if(&m == &(s.*....*member)) | ^~~~~~ 0x19eab38 internal_error(char const*, ...) ???:0 0x71c547 add_capture(tree_node*, tree_node*, tree_node*, bool, bool) ???:0 0x71c5b3 add_default_capture(tree_node*, tree_node*, tree_node*) ???:0 0x7e232a tsubst_copy_and_build(tree_node*, tree_node*, int, tree_node*, bool, bool) ???:0 0x7e0baa tsubst_copy_and_build(tree_node*, tree_node*, int, tree_node*, bool, bool) ???:0 0x7e8d8c tsubst_pack_expansion(tree_node*, tree_node*, int, tree_node*) ???:0 0x7dff89 tsubst_copy_and_build(tree_node*, tree_node*, int, tree_node*, bool, bool) ???:0 0x7e0d41 tsubst_copy_and_build(tree_node*, tree_node*, int, tree_node*, bool, bool) ???:0 0x7dfdeb tsubst_copy_and_build(tree_node*, tree_node*, int, tree_node*, bool, bool) ???:0 0x7db043 instantiate_decl(tree_node*, bool, bool) ???:0 0x6edb1b mark_used(tree_node*, int) ???:0 0x68b85f build_op_call(tree_node*, vec<tree_node*, va_gc, vl_embed>**, int) ???:0 0x810f2d finish_call_expr(tree_node*, vec<tree_node*, va_gc, vl_embed>**, bool, bool, int) ???:0 0x7e1f0c tsubst_copy_and_build(tree_node*, tree_node*, int, tree_node*, bool, bool) ???:0 0x7e8d8c tsubst_pack_expansion(tree_node*, tree_node*, int, tree_node*) ???:0 0x7dff89 tsubst_copy_and_build(tree_node*, tree_node*, int, tree_node*, bool, bool) ???:0 0x7db043 instantiate_decl(tree_node*, bool, bool) ???:0 0x6edb1b mark_used(tree_node*, int) ???:0 0x68b85f build_op_call(tree_node*, vec<tree_node*, va_gc, vl_embed>**, int) ???:0 0x810f2d finish_call_expr(tree_node*, vec<tree_node*, va_gc, vl_embed>**, bool, bool, int) ???:0
Created attachment 53851 [details] bug report provided by -fbug-report
Clang and MSVC compiles the same thing correctly
It worked in 11.2 and started failing in 11.3. Short repro: https://gcc.godbolt.org/z/GYYbrTdxh #include <boost/pfr.hpp> template <class T, class F> constexpr void for_each_field(T&& value, F&& func) { using namespace boost::pfr; using namespace boost::pfr::detail; constexpr std::size_t fields_count_val = fields_count<std::remove_reference_t<T>>(); auto t = boost::pfr::detail::tie_as_tuple(value); [&]<std::size_t... I>(std::index_sequence<I...>) { (func(get<I>(t)), ...); } (std::make_index_sequence<fields_count_val>{}); } constexpr int index_in_struct(const auto& s, auto... member) { int index = -1; int k = 0; for_each_field(s, [&](auto& m) { if constexpr(requires { bool(&m == &(s.*....*member)); }) { if(&m == &(s.*....*member)) { index = k; } } ++k; }); return index; } struct X { int z; }; int main() { constexpr int r = index_in_struct(X{}, &X::z); }
Adding an explicit "member..." to the lambda capture, e.g. [&, member...] makes it work
Reduced test-case: template <typename _Tp, _Tp> struct integral_constant {}; template <typename _Tp, _Tp> struct integer_sequence {}; template <typename _Tp, _Tp _Num> using make_integer_sequence = integer_sequence<_Tp, __integer_pack(_Num)...>; template <long... _Idx> using index_sequence = integer_sequence<unsigned long, _Idx...>; template <long N> using make_index_sequence = make_integer_sequence<unsigned long, N>; namespace sequence_tuple { struct tuple {}; template <int> auto get(tuple t) { return t; } } // namespace sequence_tuple template <long Index> using size_t_ = integral_constant<unsigned long, Index>; template <class T> auto tie_as_tuple(T, size_t_<1>) { sequence_tuple::tuple __trans_tmp_1; return __trans_tmp_1; } template <class, unsigned long N> constexpr auto detect_fields_count_dispatch(size_t_<N>) { return 1; } template <class> constexpr long fields_count() { long result = detect_fields_count_dispatch<int>(size_t_<0>{}); return result; } template <class T> auto tie_as_tuple(T val) { return tie_as_tuple(val, {}); } void get(); template <class T, class F> void for_each_field(T value, F func) { constexpr long fields_count_val = fields_count<T>(); auto t = tie_as_tuple(value); [&]<unsigned long... I>(index_sequence<I...>) { (func(get<I>(t)), ...); } (make_index_sequence<fields_count_val>{}); } void index_in_struct(auto s, auto... member) { for_each_field(s, [&](auto) { if constexpr (requires { (... * member); }) (... * member); }); } void z() { index_in_struct(int{}, z); }
Started with r12-2862-g9707d2e5dbb92d2b.
This is essentially a dup of PR100295, except here the unevaluated context is a requires-expr instead of sizeof, and r12-2862 exposed this bug by making us correctly recognize a requires-expr as an unevaluated context during cp_walk_subtrees
The master branch has been updated by Patrick Palka <ppalka@gcc.gnu.org>: https://gcc.gnu.org/g:18499b9f848707aee42d810e99ac0a4c9788433c commit r13-4730-g18499b9f848707aee42d810e99ac0a4c9788433c Author: Patrick Palka <ppalka@redhat.com> Date: Thu Dec 15 16:02:05 2022 -0500 c++: extract_local_specs and unevaluated contexts [PR100295] Here during partial instantiation of the constexpr if, extra_local_specs walks the statement looking for local specializations within to capture. However, we're thwarted by the fact that 'ts' first appears inside an unevaluated context, and so the calls to process_outer_var_ref for its local specializations are a no-op. And since we walk each tree exactly once, we end up not capturing the local specializations despite 'ts' later occurring in an evaluated context. This patch fixes this by making extract_local_specs walk evaluated contexts first before walking unevaluated contexts. We could probably get away with not walking unevaluated contexts at all, but this approach seems more clearly safe. PR c++/100295 PR c++/107579 gcc/cp/ChangeLog: * pt.cc (el_data::skip_unevaluated_operands): New data member. (extract_locals_r): If skip_unevaluated_operands is true, don't walk into unevaluated contexts. (extract_local_specs): Walk the pattern twice, first with skip_unevaluated_operands true followed by it set to false. gcc/testsuite/ChangeLog: * g++.dg/cpp1z/constexpr-if-lambda5.C: New test.
Fixed on trunk so far
The releases/gcc-12 branch has been updated by Patrick Palka <ppalka@gcc.gnu.org>: https://gcc.gnu.org/g:cdc1a14be1182874ccf1ceb27ee5b67c5ce8c62d commit r12-8998-gcdc1a14be1182874ccf1ceb27ee5b67c5ce8c62d Author: Patrick Palka <ppalka@redhat.com> Date: Thu Dec 15 16:02:05 2022 -0500 c++: extract_local_specs and unevaluated contexts [PR100295] Here during partial instantiation of the constexpr if, extra_local_specs walks the statement looking for local specializations within to capture. However, we're thwarted by the fact that 'ts' first appears inside an unevaluated context, and so the calls to process_outer_var_ref for its local specializations are a no-op. And since we walk each tree exactly once, we end up not capturing the local specializations despite 'ts' later occurring in an evaluated context. This patch fixes this by making extract_local_specs walk evaluated contexts first before walking unevaluated contexts. We could probably get away with not walking unevaluated contexts at all, but this approach seems more clearly safe. PR c++/100295 PR c++/107579 gcc/cp/ChangeLog: * pt.cc (el_data::skip_unevaluated_operands): New data member. (extract_locals_r): If skip_unevaluated_operands is true, don't walk into unevaluated contexts. (extract_local_specs): Walk the pattern twice, first with skip_unevaluated_operands true followed by it set to false. gcc/testsuite/ChangeLog: * g++.dg/cpp1z/constexpr-if-lambda5.C: New test. (cherry picked from commit 18499b9f848707aee42d810e99ac0a4c9788433c)
The releases/gcc-11 branch has been updated by Patrick Palka <ppalka@gcc.gnu.org>: https://gcc.gnu.org/g:58d2a4cdbbafa4431eb9cff3778099d185f9f391 commit r11-10804-g58d2a4cdbbafa4431eb9cff3778099d185f9f391 Author: Patrick Palka <ppalka@redhat.com> Date: Thu Dec 15 16:02:05 2022 -0500 c++: extract_local_specs and unevaluated contexts [PR100295] Here during partial instantiation of the constexpr if, extra_local_specs walks the statement looking for local specializations within to capture. However, we're thwarted by the fact that 'ts' first appears inside an unevaluated context, and so the calls to process_outer_var_ref for its local specializations are a no-op. And since we walk each tree exactly once, we end up not capturing the local specializations despite 'ts' later occurring in an evaluated context. This patch fixes this by making extract_local_specs walk evaluated contexts first before walking unevaluated contexts. We could probably get away with not walking unevaluated contexts at all, but this approach seems more clearly safe. PR c++/100295 PR c++/107579 gcc/cp/ChangeLog: * pt.c (el_data::skip_unevaluated_operands): New data member. (extract_locals_r): If skip_unevaluated_operands is true, don't walk into unevaluated contexts. (extract_local_specs): Walk the pattern twice, first with skip_unevaluated_operands true followed by it set to false. gcc/testsuite/ChangeLog: * g++.dg/cpp1z/constexpr-if-lambda5.C: New test. (cherry picked from commit 18499b9f848707aee42d810e99ac0a4c9788433c)
Fixed for GCC 11.4+