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>;

Reply via email to