From a64d9c96d8ebb0ba2a52daec85779b1a99c2f7fd Mon Sep 17 00:00:00 2001
From: Jakub Jelinek <jakub@redhat.com>
Date: Tue, 4 Feb 2025 21:16:57 +0100
Subject: [PATCH] c++: Fix ICE with #embed/RAW_DATA_CST after list conversion
 [PR118671]

The following testcases ICE with RAW_DATA_CSTs (so the first one since
introduction of #embed C++ optimizations and the latter since optimization
of large sequences of comma separated literals).
I've missed the fact that implicit_conversion can embed the exact expression
passed to it into stuff pointed out by conversion * (e.g. for user
conversions in sub->cand->args).
So, it isn't enough in convert_like_internal to pass the right INTEGER_CST
for each element of the RAW_DATA_CST because the whole RAW_DATA_CST might be
in sub->cand->args etc.
Either I'd need to chase for wherever the RAW_DATA_CST is found and update
those for each element processed, or, as implemented in the following patch,
build_list_conv detects the easy optimizable case where
convert_like_internal can be kept as the whole RAW_DATA_CST with changed
type and possibly narrowing diagnostics, and otherwise instead of having
a single subconversion it has RAW_DATA_CST separate subconversions.
Instead of trying to reallocate the subconvs array when we detect that case,
the patch instead uses an artificial ck_list inside of the u.list array
to hold the individual subconversions.
Seems the only places where u.list is used are build_list_conv and
convert_like_internal.

2025-02-04  Jakub Jelinek  <jakub@redhat.com>

	PR c++/118671
	* call.cc (build_list_conv): For RAW_DATA_CST, call
	implicit_conversion with INTEGER_CST representing first byte instead
	of the whole RAW_DATA_CST.  If it is an optimizable trivial
	conversion, just save that to subconvs, otherwise allocate an
	artificial ck_list for all the RAW_DATA_CST bytes and create
	subsubconv for each of them.
	(convert_like_internal): For ck_list with RAW_DATA_CST, instead of
	doing all the checks for optimizable conversion just check kind and
	assert everything else, otherwise use subsubconversions instead of
	the subconversion for each element.

	* g++.dg/cpp/embed-25.C: New test.
	* g++.dg/cpp0x/pr118671.C: New test.
---
 gcc/cp/call.cc                        | 89 +++++++++++++++++++++++----
 gcc/testsuite/g++.dg/cpp/embed-25.C   | 56 +++++++++++++++++
 gcc/testsuite/g++.dg/cpp0x/pr118671.C | 61 ++++++++++++++++++
 3 files changed, 193 insertions(+), 13 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp/embed-25.C
 create mode 100644 gcc/testsuite/g++.dg/cpp0x/pr118671.C

diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc
index 3a56a82632d9..c08bd0c8634a 100644
--- a/gcc/cp/call.cc
+++ b/gcc/cp/call.cc
@@ -868,6 +868,67 @@ build_list_conv (tree type, tree ctor, int flags, tsubst_flags_t complain)
 
   FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (ctor), i, val)
     {
+      if (TREE_CODE (val) == RAW_DATA_CST)
+	{
+	  tree elt
+	    = build_int_cst (TREE_TYPE (val), RAW_DATA_UCHAR_ELT (val, 0));
+	  conversion *sub
+	    = implicit_conversion (elttype, TREE_TYPE (val), elt,
+				   false, flags, complain);
+	  conversion *next;
+	  if (sub == NULL)
+	    return NULL;
+	  /* For conversion to initializer_list<unsigned char> or
+	     initializer_list<char> or initializer_list<signed char>
+	     we can optimize and keep RAW_DATA_CST with adjusted
+	     type if we report narrowing errors if needed.
+	     Use just one subconversion for that case.  */
+	  if (sub->kind == ck_std
+	      && sub->type
+	      && (TREE_CODE (sub->type) == INTEGER_TYPE
+		  || is_byte_access_type (sub->type))
+	      && TYPE_PRECISION (sub->type) == CHAR_BIT
+	      && (next = next_conversion (sub))
+	      && next->kind == ck_identity)
+	    {
+	      subconvs[i] = sub;
+	      continue;
+	    }
+	  /* Otherwise. build separate subconv for each RAW_DATA_CST
+	     byte.  Wrap those into an artificial ck_list which convert_like
+	     will then handle.  */
+	  conversion **subsubconvs = alloc_conversions (RAW_DATA_LENGTH (val));
+	  unsigned int j;
+	  subsubconvs[0] = sub;
+	  for (j = 1; j < (unsigned) RAW_DATA_LENGTH (val); ++j)
+	    {
+	      elt = build_int_cst (TREE_TYPE (val),
+				   RAW_DATA_UCHAR_ELT (val, j));
+	      sub = implicit_conversion (elttype, TREE_TYPE (val), elt,
+					 false, flags, complain);
+	      if (sub == NULL)
+		return NULL;
+	      subsubconvs[j] = sub;
+	    }
+
+	  t = alloc_conversion (ck_list);
+	  t->type = type;
+	  t->u.list = subsubconvs;
+	  t->rank = cr_exact;
+	  for (j = 0; j < (unsigned) RAW_DATA_LENGTH (val); ++j)
+	    {
+	      sub = subsubconvs[i];
+	      if (sub->rank > t->rank)
+		t->rank = sub->rank;
+	      if (sub->user_conv_p)
+		t->user_conv_p = true;
+	      if (sub->bad_p)
+		t->bad_p = true;
+	    }
+	  subconvs[i] = t;
+	  continue;
+	}
+
       conversion *sub
 	= implicit_conversion (elttype, TREE_TYPE (val), val,
 			       false, flags, complain);
@@ -8850,22 +8911,22 @@ convert_like_internal (conversion *convs, tree expr, tree fn, int argnum,
 	      {
 		if (TREE_CODE (val) == RAW_DATA_CST)
 		  {
-		    tree elt_type;
-		    conversion *next;
 		    /* For conversion to initializer_list<unsigned char> or
 		       initializer_list<char> or initializer_list<signed char>
 		       we can optimize and keep RAW_DATA_CST with adjusted
 		       type if we report narrowing errors if needed, for
 		       others this converts each element separately.  */
-		    if (convs->u.list[ix]->kind == ck_std
-			&& (elt_type = convs->u.list[ix]->type)
-			&& (TREE_CODE (elt_type) == INTEGER_TYPE
-			    || is_byte_access_type (elt_type))
-			&& TYPE_PRECISION (elt_type) == CHAR_BIT
-			&& (next = next_conversion (convs->u.list[ix]))
-			&& next->kind == ck_identity)
+		    if (convs->u.list[ix]->kind == ck_std)
 		      {
-			if (!TYPE_UNSIGNED (elt_type)
+			tree et = convs->u.list[ix]->type;
+			conversion *next = next_conversion (convs->u.list[ix]);
+			gcc_assert (et
+				    && (TREE_CODE (et) == INTEGER_TYPE
+					|| is_byte_access_type (et))
+				    && TYPE_PRECISION (et) == CHAR_BIT
+				    && next
+				    && next->kind == ck_identity);
+			if (!TYPE_UNSIGNED (et)
 			    /* For RAW_DATA_CST, TREE_TYPE (val) can be
 			       either integer_type_node (when it has been
 			       created by the lexer from CPP_EMBED) or
@@ -8891,7 +8952,7 @@ convert_like_internal (conversion *convs, tree expr, tree fn, int argnum,
 						 "narrowing conversion of "
 						 "%qd from %qH to %qI",
 						 RAW_DATA_UCHAR_ELT (val, i),
-						 TREE_TYPE (val), elt_type);
+						 TREE_TYPE (val), et);
 				  if (errorcount != savederrorcount)
 				    return error_mark_node;
 				}
@@ -8899,19 +8960,21 @@ convert_like_internal (conversion *convs, tree expr, tree fn, int argnum,
 				return error_mark_node;
 			    }
 			tree sub = copy_node (val);
-			TREE_TYPE (sub) = elt_type;
+			TREE_TYPE (sub) = et;
 			CONSTRUCTOR_APPEND_ELT (CONSTRUCTOR_ELTS (new_ctor),
 						NULL_TREE, sub);
 		      }
 		    else
 		      {
+			conversion *conv = convs->u.list[ix];
+			gcc_assert (conv->kind == ck_list);
 			for (int i = 0; i < RAW_DATA_LENGTH (val); ++i)
 			  {
 			    tree elt
 			      = build_int_cst (TREE_TYPE (val),
 					       RAW_DATA_UCHAR_ELT (val, i));
 			    tree sub
-			      = convert_like (convs->u.list[ix], elt,
+			      = convert_like (conv->u.list[i], elt,
 					      fn, argnum, false, false,
 					      /*nested_p=*/true, complain);
 			    if (sub == error_mark_node)
diff --git a/gcc/testsuite/g++.dg/cpp/embed-25.C b/gcc/testsuite/g++.dg/cpp/embed-25.C
new file mode 100644
index 000000000000..3acffeb9caf8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp/embed-25.C
@@ -0,0 +1,56 @@
+// PR c++/118671
+// { dg-do run { target c++11 } }
+// { dg-options "-O2" }
+
+namespace std {
+template <typename T>
+struct initializer_list {
+private:
+  T *_M_array;
+  decltype (sizeof 0) _M_len;
+public:
+  constexpr decltype (sizeof 0)
+  size () const noexcept { return _M_len; }
+  constexpr const T *
+  begin () const noexcept { return _M_array; }
+  constexpr const T *
+  end () const noexcept { return begin () + size (); }
+};
+}
+
+struct A {} a;
+
+struct B {
+  constexpr B (int x) : B (a, x) {}
+  template <typename... T>
+  constexpr B (A, T... x) : b(x...) {}
+  int b;
+};
+
+struct C {
+  C (std::initializer_list<B> x)
+  {
+    unsigned char buf[] = {
+#embed __FILE__
+    };
+    if (x.size () != sizeof (buf))
+      __builtin_abort ();
+    unsigned int i = 0;
+    for (auto a = x.begin (); a < x.end (); ++a, ++i)
+      if (a->b != buf[i])
+	__builtin_abort ();
+    c = true;
+  }
+  bool c;
+};
+
+C c {
+#embed __FILE__
+};
+
+int
+main ()
+{
+  if (!c.c)
+    __builtin_abort ();
+}
diff --git a/gcc/testsuite/g++.dg/cpp0x/pr118671.C b/gcc/testsuite/g++.dg/cpp0x/pr118671.C
new file mode 100644
index 000000000000..d531274ab3e0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/pr118671.C
@@ -0,0 +1,61 @@
+// PR c++/118671
+// { dg-do run { target c++11 } }
+// { dg-options "-O2" }
+
+namespace std {
+template <typename T>
+struct initializer_list {
+private:
+  T *_M_array;
+  decltype (sizeof 0) _M_len;
+public:
+  constexpr decltype (sizeof 0)
+  size () const noexcept { return _M_len; }
+  constexpr const T *
+  begin () const noexcept { return _M_array; }
+  constexpr const T *
+  end () const noexcept { return begin () + size (); }
+};
+}
+
+struct A {} a;
+
+struct B {
+  constexpr B (int x) : B (a, x) {}
+  template <typename... T>
+  constexpr B (A, T... x) : b(x...) {}
+  int b;
+};
+
+struct C {
+  C (std::initializer_list<B> x)
+  {
+    if (x.size () != 130)
+      __builtin_abort ();
+    unsigned int i = 1;
+    for (auto a = x.begin (); a < x.end (); ++a)
+      if (a->b != i)
+	__builtin_abort ();
+      else
+	i = (i & 15) + 1;
+    c = true;
+  }
+  bool c;
+};
+
+C c { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+      1, 2 };
+
+int
+main ()
+{
+  if (!c.c)
+    __builtin_abort ();
+}
-- 
GitLab