On Thu, Jan 09, 2025 at 09:30:04PM -0500, Jason Merrill wrote:
> > --- gcc/cp/call.cc.jj       2024-12-11 17:27:52.481221310 +0100
> > +++ gcc/cp/call.cc  2024-12-19 18:50:52.478315892 +0100
> > @@ -8766,8 +8766,8 @@ convert_like_internal (conversion *convs
> >     if (tree init = maybe_init_list_as_array (elttype, expr))
> >       {
> > -       elttype = cp_build_qualified_type
> > -         (elttype, cp_type_quals (elttype) | TYPE_QUAL_CONST);
> > +       elttype = cp_build_qualified_type (elttype, cp_type_quals (elttype)
> > +                                                   | TYPE_QUAL_CONST);
> 
> Emacs won't preserve this formatting, it will move the | left to line up
> with the (.

So
            elttype = cp_build_qualified_type (elttype,
                                               (cp_type_quals (elttype)
                                                | TYPE_QUAL_CONST));
then or
            elttype
              = cp_build_qualified_type (elttype, (cp_type_quals (elttype)
                                                   | TYPE_QUAL_CONST));
?  The function-name on different line from the ( and first parameter is
just weird and last resort when nothing else works.

> > +           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 (!TYPE_UNSIGNED (elt_type)
> > +                       && (TYPE_UNSIGNED (TREE_TYPE (val))
> > +                           || (TYPE_PRECISION (TREE_TYPE (val))
> > +                               > CHAR_BIT)))
> 
> Is it possible to have a RAW_DATA_CST with elements larger than char?

Initially, RAW_DATA_CST has int type, i.e. the type
each element would have if it was just
  1, 2, 3, 4, 5, 6, 7, 8, 9, ..., 115, 116, 117, 118
and then the reshaping turns it either into char/signed char/unsigned char
or perhaps std::byte (i.e. something CHAR_BIT), or splits it appart.

As I saw some bool variable somewhere in the code whether the CONSTRUCTOR
has been reshaped or not (unfortunately don't remember where and can't find
it now), I've tried to be safe for both cases, i.e. handle both the int
case where we know the values are 0 .. CHAR_BIT-1 with some extra bits on
top, so even when TREE_TYPE (val) is signed, the values need to be taken as
unsigned, and then the reshaped ones where !TYPE_UNSIGNED implies elts with
most significant bit set are negative.
If TREE_TYPE (val) is signed char (or char with -fsigned-char), then
-Wnarrowing is supposed to have been already diagnosed when it was converted
from int to signed char (either in digest_init_r or in this spot before).

On the testcases from this patch, TREE_TYPE (val) is still
integer_type_node.

Even that could have precision CHAR_BIT, e.g. on avr
with -mint8, I think we just shouldn't create RAW_DATA_CST at all in that
case if any of the bytes is above SCHAR_MAX, because then in
  1, 2, 3, 128, 4, 5
the 128 has long or long long type rather than int, while the rest have int.
Maybe even preprocessor shouldn't create CPP_EMBED then, but dunno if libcpp
knows the precision of int on the target.  I'll deal with avr -mint8 later on
during stage4 (guess it affects C as well).

Here is an updated patch with changed formatting of the
cp_build_qualified_type calls and comment added.
If you really don't want the formatting changes, I can surely take that out,
and if you think being there extra careful about TREE_TYPE (val) isn't
needed either because it must be always integer_type_node and that libcpp
shouldn't create CPP_EMBED for avr -mint8 with 128+ values, that conditional
could be just if (!TYPE_UNSIGNED (elt_type)) alone as well.

2025-01-10  Jakub Jelinek  <ja...@redhat.com>

        PR c++/118124
        * call.cc (convert_like_internal): Handle RAW_DATA_CST in
        ck_list handling.  Formatting fixes.

        * g++.dg/cpp/embed-15.C: New test.
        * g++.dg/cpp/embed-16.C: New test.
        * g++.dg/cpp0x/initlist-opt3.C: New test.
        * g++.dg/cpp0x/initlist-opt4.C: New test.

--- gcc/cp/call.cc.jj   2024-12-11 17:27:52.481221310 +0100
+++ gcc/cp/call.cc      2025-01-10 10:13:25.268639098 +0100
@@ -8806,8 +8806,9 @@ convert_like_internal (conversion *convs
 
        if (tree init = maybe_init_list_as_array (elttype, expr))
          {
-           elttype = cp_build_qualified_type
-             (elttype, cp_type_quals (elttype) | TYPE_QUAL_CONST);
+           elttype
+             = cp_build_qualified_type (elttype, (cp_type_quals (elttype)
+                                                  | TYPE_QUAL_CONST));
            array = build_array_of_n_type (elttype, len);
            array = build_vec_init_expr (array, init, complain);
            array = get_target_expr (array);
@@ -8815,13 +8816,94 @@ convert_like_internal (conversion *convs
          }
        else if (len)
          {
-           tree val; unsigned ix;
-
+           tree val;
+           unsigned ix;
            tree new_ctor = build_constructor (init_list_type_node, NULL);
 
            /* Convert all the elements.  */
            FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (expr), ix, val)
              {
+               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 (!TYPE_UNSIGNED (elt_type)
+                           /* 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
+                              after digestion/conversion some integral
+                              type with CHAR_BIT precision.  For int with
+                              precision higher than CHAR_BIT or unsigned char
+                              diagnose narrowing conversions from
+                              that int/unsigned char to signed char if any
+                              byte has most significant bit set.  */
+                           && (TYPE_UNSIGNED (TREE_TYPE (val))
+                               || (TYPE_PRECISION (TREE_TYPE (val))
+                                   > CHAR_BIT)))
+                         for (int i = 0; i < RAW_DATA_LENGTH (val); ++i)
+                           {
+                             if (RAW_DATA_SCHAR_ELT (val, i) >= 0)
+                               continue;
+                             else if (complain & tf_error)
+                               {
+                                 location_t loc
+                                   = cp_expr_loc_or_input_loc (val);
+                                 int savederrorcount = errorcount;
+                                 permerror_opt (loc, OPT_Wnarrowing,
+                                                "narrowing conversion of "
+                                                "%qd from %qH to %qI",
+                                                RAW_DATA_UCHAR_ELT (val, i),
+                                                TREE_TYPE (val), elt_type);
+                                 if (errorcount != savederrorcount)
+                                   return error_mark_node;
+                               }
+                             else
+                               return error_mark_node;
+                           }
+                       tree sub = copy_node (val);
+                       TREE_TYPE (sub) = elt_type;
+                       CONSTRUCTOR_APPEND_ELT (CONSTRUCTOR_ELTS (new_ctor),
+                                               NULL_TREE, sub);
+                     }
+                   else
+                     {
+                       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,
+                                             fn, argnum, false, false,
+                                             /*nested_p=*/true, complain);
+                           if (sub == error_mark_node)
+                             return sub;
+                           if (!check_narrowing (TREE_TYPE (sub), elt,
+                                                 complain))
+                             return error_mark_node;
+                           tree nc = new_ctor;
+                           CONSTRUCTOR_APPEND_ELT (CONSTRUCTOR_ELTS (nc),
+                                                   NULL_TREE, sub);
+                           if (!TREE_CONSTANT (sub))
+                             TREE_CONSTANT (new_ctor) = false;
+                         }
+                     }
+                   len += RAW_DATA_LENGTH (val) - 1;
+                   continue;
+                 }
                tree sub = convert_like (convs->u.list[ix], val, fn,
                                         argnum, false, false,
                                         /*nested_p=*/true, complain);
@@ -8836,8 +8918,9 @@ convert_like_internal (conversion *convs
                  TREE_CONSTANT (new_ctor) = false;
              }
            /* Build up the array.  */
-           elttype = cp_build_qualified_type
-             (elttype, cp_type_quals (elttype) | TYPE_QUAL_CONST);
+           elttype
+             = cp_build_qualified_type (elttype, (cp_type_quals (elttype)
+                                                  | TYPE_QUAL_CONST));
            array = build_array_of_n_type (elttype, len);
            array = finish_compound_literal (array, new_ctor, complain);
            /* This is dubious now, should be blessed by P2752.  */
--- gcc/testsuite/g++.dg/cpp/embed-15.C.jj      2024-12-19 15:07:28.564605983 
+0100
+++ gcc/testsuite/g++.dg/cpp/embed-15.C 2024-12-19 15:08:14.194998077 +0100
@@ -0,0 +1,35 @@
+// PR c++/118124
+// { dg-do compile { target c++11 } }
+// { dg-options "-O2" }
+
+namespace std {
+template <class T> struct initializer_list {
+private:
+  const T *_M_array;
+  __SIZE_TYPE__ _M_len;
+};
+}
+struct A {
+  A (std::initializer_list<char>);
+};
+A a {
+#embed __FILE__
+};
+struct B {
+  B (std::initializer_list<unsigned char>);
+};
+B b {
+#embed __FILE__
+};
+struct C {
+  C (std::initializer_list<int>);
+};
+C c {
+#embed __FILE__
+};
+struct D {
+  D (std::initializer_list<float>);
+};
+D d {
+#embed __FILE__
+};
--- gcc/testsuite/g++.dg/cpp/embed-16.C.jj      2024-12-19 15:09:20.929109016 
+0100
+++ gcc/testsuite/g++.dg/cpp/embed-16.C 2024-12-19 15:11:12.811618467 +0100
@@ -0,0 +1,18 @@
+// PR c++/118124
+// { dg-do compile { target c++11 } }
+// { dg-options "-O2" }
+// non-ASCII chars here: áéí
+
+namespace std {
+template <class T> struct initializer_list {
+private:
+  const T *_M_array;
+  __SIZE_TYPE__ _M_len;
+};
+}
+struct A {
+  A (std::initializer_list<signed char>);
+};
+A a {
+#embed __FILE__
+};     // { dg-error "narrowing conversion of '\[0-9]*' from 'int' to 'signed 
char'" }
--- gcc/testsuite/g++.dg/cpp0x/initlist-opt3.C.jj       2024-12-19 
14:52:17.389013179 +0100
+++ gcc/testsuite/g++.dg/cpp0x/initlist-opt3.C  2024-12-19 15:06:28.652404163 
+0100
@@ -0,0 +1,47 @@
+// PR c++/118124
+// { dg-do compile { target c++11 } }
+// { dg-options "-O2" }
+
+namespace std {
+template <class T> struct initializer_list {
+private:
+  const T *_M_array;
+  __SIZE_TYPE__ _M_len;
+};
+}
+struct A {
+  A (std::initializer_list<char>);
+};
+A a { 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7,
+      8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+      0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1,
+      2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3,
+      4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5,
+      6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5 };
+struct B {
+  B (std::initializer_list<unsigned char>);
+};
+B b { 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7,
+      8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+      0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1,
+      2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3,
+      4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5,
+      6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5 };
+struct C {
+  C (std::initializer_list<int>);
+};
+C c { 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7,
+      8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+      0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1,
+      2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3,
+      4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5,
+      6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5 };
+struct D {
+  D (std::initializer_list<float>);
+};
+D d { 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7,
+      8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+      0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1,
+      2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3,
+      4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5,
+      6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5 };
--- gcc/testsuite/g++.dg/cpp0x/initlist-opt4.C.jj       2024-12-19 
14:52:46.517612306 +0100
+++ gcc/testsuite/g++.dg/cpp0x/initlist-opt4.C  2024-12-19 15:06:52.513086275 
+0100
@@ -0,0 +1,20 @@
+// PR c++/118124
+// { dg-do compile { target c++11 } }
+// { dg-options "-O2" }
+
+namespace std {
+template <class T> struct initializer_list {
+private:
+  const T *_M_array;
+  __SIZE_TYPE__ _M_len;
+};
+}
+struct A {
+  A (std::initializer_list<signed char>);
+};
+A a { 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7,
+      8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+      0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1,
+      2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 209, 0, 1, 2, 3,
+      4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5,
+      6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5 };    // { 
dg-error "narrowing conversion of '209' from 'int' to 'signed char'" }


        Jakub

Reply via email to