#include <string> struct X { X(std::initializer_list<std::string>) { } }; void test01() { X x { "E", "E", "T", "T" }; return; } int main() { test01(); return 0; } With GCC 11 debugging this works as expected, but since GCC 12.1 stepping into test01 jumps around while constructing the initializer_list: $ gdb -q -ex start -ex step -ex n -ex n -ex n -ex cont -ex q ./a.out Reading symbols from ./a.out... Temporary breakpoint 1 at 0x4023b2: file 83709.cc, line 13. Starting program: /tmp/a.out [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib64/libthread_db.so.1". Temporary breakpoint 1, main () at 83709.cc:13 13 test01(); test01 () at 83709.cc:10 10 } 8 X x { "E", "E", "T", "T" }; 10 } 9 return; Continuing. [Inferior 1 (process 830655) exited normally] We start on line 13 but after stepping into test01 we're on line 10 (the closing brace of test01), then "next" takes us to line 8 where the initializer_list is used, then "next" takes us back to line 10, then to line 9. With GCC 11 it's great: Temporary breakpoint 1, main () at 83709.cc:13 13 test01(); test01 () at 83709.cc:8 8 X x { "E", "E", "T", "T" }; 9 return; 10 } main () at 83709.cc:14 14 return 0; Continuing. [Inferior 1 (process 830849) exited normally]
I think this usually happens when some subexpression doesn't have accurate location info, so it's just "somewhere in test01" which gdb treats as the closing brace.
I thought I saw this before ...
for me this is what I get: In GCC 13: <bb 2> : [/app/example.cpp:10:1] _15 = &D.35543; ... But in GCC 14 and the trunk: <bb 2> : [/app/example.cpp:8:28] _15 = &D.36094; So for me it looks like it was fixed in GCC 14
Hmm, you're right, for the reduced case it is fixed in GCC 14.1 But for the original case I reduced, it's not: #include <unordered_map> #include <string> void test01() { std::unordered_map<std::string, std::string> m { {"E", "E" }, { "T", "T" } }; m.insert({ {"E", "E" }, { "T", "T" } }); } int main() { test01(); return 0; } Temporary breakpoint 1, main () at 83709.cc:12 12 test01(); test01 () at 83709.cc:9 9 } 6 { {"E", "E" }, { "T", "T" } }; 9 } 8 m.insert({ {"E", "E" }, { "T", "T" } }); Continuing. We step into line 9, then line 6, then line 9, then line 8. That's exactly the same behaviour as for the reduced one, but still present on trunk.
(sorry for not double-checking the reduced version on trunk before reporting it)
namespace std { /// initializer_list template<class _E> class initializer_list { public: using size_type = decltype(sizeof(1)); private: const _E* _M_array; size_type _M_len; // The compiler can call a private constructor. constexpr initializer_list(const _E* __a, size_type __l) : _M_array(__a), _M_len(__l) { } public: constexpr initializer_list() noexcept : _M_array(0), _M_len(0) { } // Number of elements. constexpr size_type size() const noexcept { return _M_len; } // First element. constexpr const _E* begin() const noexcept { return _M_array; } // One past the last element. constexpr const _E* end() const noexcept { return begin() + size(); } }; } struct V { V(const char*) { } ~V() { } }; struct X { X(std::initializer_list<V>) { } }; void test01() { X x { {"E"}, {"E"}, {"T"}, {"T"} }; return; } int main() { test01(); return 0; } This only jumps to the closing brace once: Temporary breakpoint 1, main () at 83709.cc:51 51 test01(); test01 () at 83709.cc:48 48 } 46 X x { {"E"}, {"E"}, {"T"}, {"T"} }; 47 return; 48 } Continuing.
Seems to have started with r12-6329 c++: EH and partially constructed aggr temp [PR66139] Now that PR94041 is fixed, I can return to addressing PR66139, missing cleanups for partially constructed aggregate temporaries. My previous approach of calling split_nonconstant_init in cp_gimplify_init_expr broke because gimplification is too late to be introducing destructor calls. So instead I now call it under cp_fold_function, just before cp_genericize; doing it from cp_genericize itself ran into trouble with the rewriting of invisible references. So now the prediction in cp_gimplify_expr that cp_gimplify_init_expr might need to replace references to TARGET_EXPR_SLOT within TARGET_EXPR_INITIAL has come to pass. constexpr.c already had the simple search-and-replace tree walk I needed, but it needed to be fixed to actually replace all occurrences instead of just the first one. Handling of VEC_INIT_EXPR at gimplify time had similar issues that we worked around with build_vec_init_elt, so I'm moving that to cp_fold_function as well. But it seems that build_vec_init_elt is still useful for setting the VEC_INIT_EXPR_IS_CONSTEXPR flag, so I'm leaving it alone. This also fixes 52320, because build_aggr_init of each X from build_vec_init builds an INIT_EXPR rather than call split_nonconstant_init at that point, and now that INIT_EXPR gets split later.
So it looks like r12-9430 for PR107154 was only a partial fix.
Confirmed. [/app/example.cpp:6:31] try { [/app/example.cpp:9:1] D.53100 = &D.52758; [/app/example.cpp:9:1] D.53101 = D.53100; [/app/example.cpp:9:1] D.53102 = 1; try { [/app/example.cpp:9:1] std::pair<const std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >::pair<const char (&)[2], const char (&)[2]> (D.53101, [/app/example.cpp:9:1] "E", [/app/example.cpp:6:11] "E"); [/app/example.cpp:9:1] D.53101 = D.53101 + 64; [/app/example.cpp:9:1] D.53102 = D.53102 + -1; [/app/example.cpp:9:1] std::pair<const std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >::pair<const char (&)[2], const char (&)[2]> (D.53101, [/app/example.cpp:9:1] "T", [/app/example.cpp:6:25] "T"); [/app/example.cpp:9:1] D.53101 = D.53101 + 64; [/app/example.cpp:9:1] D.53102 = D.53102 + -1; [/app/example.cpp:9:1] retval.0 = D.53100; [/app/example.cpp:9:1] D.53102 = 1; try { [/app/example.cpp:6:31] D.52760._M_array = &D.52758; [/app/example.cpp:6:31] D.52760._M_len = 2;
GCC 12 branch is being closed.