Hi!

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.

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

2025-01-28  Jakub Jelinek  <ja...@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.jj   2025-01-22 09:22:53.000000000 +0100
+++ gcc/cp/call.cc      2025-01-27 21:36:31.159889633 +0100
@@ -868,6 +868,67 @@ build_list_conv (tree type, tree ctor, i
 
   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);
@@ -8841,22 +8902,22 @@ convert_like_internal (conversion *convs
              {
                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
@@ -8882,7 +8943,7 @@ convert_like_internal (conversion *convs
                                                 "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;
                                }
@@ -8890,19 +8951,21 @@ convert_like_internal (conversion *convs
                                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)
--- gcc/testsuite/g++.dg/cpp/embed-25.C.jj      2025-01-27 21:51:00.190990295 
+0100
+++ gcc/testsuite/g++.dg/cpp/embed-25.C 2025-01-27 21:50:45.954183524 +0100
@@ -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 ();
+}
--- gcc/testsuite/g++.dg/cpp0x/pr118671.C.jj    2025-01-27 21:47:01.154242793 
+0100
+++ gcc/testsuite/g++.dg/cpp0x/pr118671.C       2025-01-27 21:46:43.379486760 
+0100
@@ -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 ();
+}

        Jakub

Reply via email to