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;
+
if (TREE_CODE (rval) == AGGR_INIT_EXPR)
slot = AGGR_INIT_EXPR_SLOT (rval);
else if (TREE_CODE (rval) == CALL_EXPR
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 00000000000..5f98a95580c
--- /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>;
base-commit: a0e00c323e50fb5f1401fd5f38e8296bc8cd5fe8
--
2.54.0