https://gcc.gnu.org/g:9af58600fd08d986894051be4d0f37454ad38abe
commit r17-1592-g9af58600fd08d986894051be4d0f37454ad38abe Author: Marek Polacek <[email protected]> Date: Mon Jun 1 17:32:38 2026 -0400 c++: ICE in tsubst_expr with comma expr [PR125539] Here we crash because a TARGET_EXPR gets into tsubst_expr with something like: template<typename> constexpr static A a = (A{}, A{}); Pre r14-4796, when we had tsubst_copy, we didn't crash because we had an early exit when substituting with args=NULL_TREE. tsubst_expr deliberately doesn't have that early exit. Note that template<typename> constexpr static A a = A{}; works because expand_aggr_init_1 sees a COMPOUND_LITERAL_P and does the early exit without calling expand_default_init. But with a COMPOUND_EXPR we don't take that path. The TARGET_EXPR is created via check_initializer -> build_aggr_init_full_exprs -> build_aggr_init -> expand_aggr_init_1 -> expand_default_init -> ocp_convert -> build_cplus_new -> build_target_expr. I tried adjusting the big and ugly check in check_initializer before build_aggr_init_full_exprs but that didn't work out. I also tried avoiding the call to ocp_convert but that broke some reflection tests. We can fix this by calling perform_implicit_conversion in a template to create an IMPLICIT_CONV_EXPR, then build_cplus_new won't create a TARGET_EXPR. PR c++/125539 gcc/cp/ChangeLog: * cvt.cc (ocp_convert): In a template, always call perform_implicit_conversion. Pass flags to perform_implicit_conversion_flags. * decl.cc (check_initializer): Remove a call to build_implicit_conv_flags. gcc/testsuite/ChangeLog: * g++.dg/cpp1y/var-templ89.C: New test. Reviewed-by: Jason Merrill <[email protected]> Diff: --- gcc/cp/cvt.cc | 7 +++++-- gcc/cp/decl.cc | 7 +------ gcc/testsuite/g++.dg/cpp1y/var-templ89.C | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 38 insertions(+), 8 deletions(-) diff --git a/gcc/cp/cvt.cc b/gcc/cp/cvt.cc index b1176317d996..b18f66e582f9 100644 --- a/gcc/cp/cvt.cc +++ b/gcc/cp/cvt.cc @@ -968,8 +968,11 @@ ocp_convert (tree type, tree expr, int convtype, int flags, if (abstract_virtuals_error (NULL_TREE, type, complain)) return error_mark_node; - if (BRACE_ENCLOSED_INITIALIZER_P (ctor)) - ctor = perform_implicit_conversion (type, ctor, complain); + if (BRACE_ENCLOSED_INITIALIZER_P (ctor) + /* We don't want to create a TARGET_EXPR in a template by the + build_cplus_new below. */ + || processing_template_decl) + ctor = perform_implicit_conversion_flags (type, ctor, complain, flags); else if ((flags & LOOKUP_ONLYCONVERTING) && ! (CLASS_TYPE_P (dtype) && DERIVED_FROM_P (type, dtype))) /* For copy-initialization, first we create a temp of the proper type diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc index cba85c959ff7..23f23b39544d 100644 --- a/gcc/cp/decl.cc +++ b/gcc/cp/decl.cc @@ -8625,12 +8625,7 @@ check_initializer (tree decl, tree init, int flags, vec<tree, va_gc> **cleanups) /* In C++20, the call to build_aggr_init could have created an INIT_EXPR with a CONSTRUCTOR as the RHS to handle A(1, 2). */ - tree rhs = TREE_OPERAND (init_code, 1); - if (processing_template_decl && TREE_CODE (rhs) == TARGET_EXPR) - /* Avoid leaking TARGET_EXPR into template trees. */ - rhs = build_implicit_conv_flags (type, init, flags); - init = rhs; - + init = TREE_OPERAND (init_code, 1); init_code = NULL_TREE; /* Don't call digest_init; it's unnecessary and will complain about aggregate initialization of non-aggregate classes. */ diff --git a/gcc/testsuite/g++.dg/cpp1y/var-templ89.C b/gcc/testsuite/g++.dg/cpp1y/var-templ89.C new file mode 100644 index 000000000000..5f98a95580cf --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1y/var-templ89.C @@ -0,0 +1,32 @@ +// PR c++/125539 +// { dg-do compile { target c++14 } } + +struct A {}; +struct B { int i; }; +constexpr void foo () {} + +template<typename T> +constexpr static A a1 = (foo (), A {}); +template<typename T> +constexpr static A a2 = (A{}, A{}); +template<typename T> +constexpr static A a3 = A{}; +template<typename T> +constexpr static A a4 = {}; +template<typename T> +constexpr static A a5{}; +template<typename T> +constexpr static A a6 = (A{}, A{}, A{}, A{}, A{}, A{}, A{}); +template<int N> +constexpr static B b1 = B{N}; +template<int N> +constexpr static B b2 = (B{N}, B{N}); + +template const A a1<int>; +template const A a2<int>; +template const A a3<int>; +template const A a4<int>; +template const A a5<int>; +template const A a6<int>; +template const B b1<42>; +template const B b2<42>;
