On 6/10/26 3:48 PM, Marek Polacek wrote:
On Tue, Jun 09, 2026 at 04:22:38PM -0400, Jason Merrill wrote:
On 6/4/26 6:26 PM, Marek Polacek wrote:
On Thu, Jun 04, 2026 at 05:20:30PM -0400, Jason Merrill wrote:
On 6/4/26 3:39 PM, Marek Polacek wrote:
On Wed, Jun 03, 2026 at 03:41:56PM -0400, Jason Merrill wrote:
On 6/2/26 5:59 PM, Marek Polacek wrote:
Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk/branches?

-- >8 --
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.  But avoiding the call to
build_cplus_new seems to work.

        PR c++/125539

gcc/cp/ChangeLog:

        * cvt.cc (ocp_convert): Don't call build_cplus_new in a
        template.

I notice that build_aggr_init_expr already shortcuts
processing_template_decl.  What if we do that in build_cplus_new as well, if
init has the right type?

I think that looks better, and is a more general fix.

dg.exp passed; ok for trunk/branches if the rest of the testing passes too?

-- >8 --
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 returning early in build_cplus_new in a template,
like in build_aggr_init_expr.

        PR c++/125539

gcc/cp/ChangeLog:

        * tree.cc (build_cplus_new): In a template, return before creating
        a TARGET_EXPR.

gcc/testsuite/ChangeLog:

        * g++.dg/cpp1y/var-templ89.C: New test.
---
    gcc/cp/tree.cc                           |  5 ++++
    gcc/testsuite/g++.dg/cpp1y/var-templ89.C | 32 ++++++++++++++++++++++++
    2 files changed, 37 insertions(+)
    create mode 100644 gcc/testsuite/g++.dg/cpp1y/var-templ89.C

diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc
index 360eae87698..df9dd562314 100644
--- a/gcc/cp/tree.cc
+++ b/gcc/cp/tree.cc
@@ -838,6 +838,11 @@ build_cplus_new (tree type, tree init, tsubst_flags_t 
complain)
      if (abstract_virtuals_error (NULL_TREE, type, complain))
        return error_mark_node;
+  /* As in build_aggr_init_expr.  We don't want to create a TARGET_EXPR
+     in a template.  */
+  if (processing_template_decl)
+    return rval;

I mentioned "if init has the right type"; we need to check that so we don't
just return a void constructor call.

Oop, now I see what you meant, sorry.

If I check

    if (processing_template_decl
        && same_type_ignoring_top_level_qualifiers_p (type, TREE_TYPE (init)))
      return rval;

then we crash on var-templ89.C with -std=c++14 because we don't
elide the copy, so have a ctor call, so the types don't match, so
we create a TARGET_EXPR.

Ah, well.  So I guess shortcutting the template case in ocp_convert, but
again returning the constructor call is wrong; perhaps we should take the
earlier perform_implicit_conversion route in a template?

Thanks.  Maybe something like this, then?

Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk/branches?

-- >8 --
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.

gcc/testsuite/ChangeLog:

        * g++.dg/cpp1y/var-templ89.C: New test.
---
  gcc/cp/cvt.cc                            |  5 +++-
  gcc/testsuite/g++.dg/cpp1y/var-templ89.C | 32 ++++++++++++++++++++++++
  2 files changed, 36 insertions(+), 1 deletion(-)
  create mode 100644 gcc/testsuite/g++.dg/cpp1y/var-templ89.C

diff --git a/gcc/cp/cvt.cc b/gcc/cp/cvt.cc
index b1176317d99..d5a64e098d2 100644
--- a/gcc/cp/cvt.cc
+++ b/gcc/cp/cvt.cc
@@ -968,7 +968,10 @@ 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))
+      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 (type, ctor, complain);

I think this should be perform_implicit_conversion_flags now.

With this patch do we still need this in check_initializer?

=>            if (processing_template_decl && TREE_CODE (rhs) == TARGET_EXPR)
                /* Avoid leaking TARGET_EXPR into template trees.  */
                rhs = build_implicit_conv_flags (type, init, flags);

Jason

Reply via email to