c++: Unshare constructors in convert_reflect_constant_arg [PR124792]

Message ID adXxyce71FKKw7EU@tucnak
State New
Headers
Series c++: Unshare constructors in convert_reflect_constant_arg [PR124792] |

Commit Message

Jakub Jelinek April 8, 2026, 6:12 a.m. UTC
  Hi!

The following testcase is miscompiled due to incorrect CONSTRUCTOR sharing.
For reflect_constant_array elts which aren't integral we call
convert_reflect_constant_arg (but reflect_constant etc. do the same) and
that eventually indirectly calls get_template_parm_object which creates
the TPO VAR_DECL with DECL_INITIAL.  Unfortunately, some CONSTRUCTORs
in that DECL_INITIAL can be shared with whatever we use during constant
expression evaluation later on, so when we evaluate ~vector, we modify
those and with that modify also the CONSTRUCTORs inside of TPO
DECL_INITIAL CONSTRUCTOR.
So we need to unshare it somewhere.  One possibility is in
get_template_parm_object but I don't think we have CONSTRUCTOR sharing
issues when building normal templates, so we'd unshare in 99.9% of cases
uselessly, another one is where the patch does it and yet another
one would be in the reflect.cc convert_reflect_constant_arg and
get_template_parm_object callers (but there are several).  Furthermore,
I think for the 2 get_template_parm_object callers in reflect.cc we
don't need it, for the eval_reflect_constant_string case the argument
is always a STRING_CST and for eval_reflect_constant_array everything
is already unshared when needed.

Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

2026-04-08  Jakub Jelinek  <jakub@redhat.com>

	PR c++/124792
	* pt.cc (convert_reflect_constant_arg): Call unshare_constructor
	on expr.

	* g++.dg/reflect/pr124792.C: New test.


	Jakub
  

Patch

--- gcc/cp/pt.cc.jj	2026-04-07 20:40:51.244059110 +0200
+++ gcc/cp/pt.cc	2026-04-07 21:26:16.569565617 +0200
@@ -33674,7 +33674,7 @@  convert_reflect_constant_arg (tree type,
   if (invalid_nontype_parm_type_p (type, tf_none))
     return error_mark_node;
 
-  expr = convert_nontype_argument (type, expr, tf_none);
+  expr = convert_nontype_argument (type, unshare_constructor (expr), tf_none);
   if (!expr)
     return error_mark_node;
   return expr;
--- gcc/testsuite/g++.dg/reflect/pr124792.C.jj	2026-04-07 21:27:11.166653698 +0200
+++ gcc/testsuite/g++.dg/reflect/pr124792.C	2026-04-07 21:27:53.611944745 +0200
@@ -0,0 +1,11 @@ 
+// PR c++/124792
+// { dg-do compile { target c++26 } }
+// { dg-additional-options "-freflection" }
+
+#include <meta>
+
+struct S { const char *name = nullptr; };
+
+constexpr auto a = std::define_static_array (std::vector { S { std::define_static_string ("foo") } });
+constexpr auto b = std::define_static_array (std::vector { S { std::define_static_string ("foo") },
+							   S { std::define_static_string ("bar") } });