On 10/3/19 2:25 PM, Jakub Jelinek wrote:
Hi!

On Tue, Oct 01, 2019 at 05:56:00PM -0400, Jason Merrill wrote:
I think we want to factor this function more, so we don't have the same code
in multiple places for handling an array, and an array member, and a pointer
to array.  Do you want to take a look at bug 71504 while you're touching
this code?

The following patch does that.
I haven't touched fold-const.c (perhaps we could handle multidimensional
arrays there, but I wouldn't handle more, because that can be compile time
expensive and there it is just an optimization), nor the last else if in
cxx_fold_indirect_ref you've tweaked in your patch (nothing in the testcases
needed it nor it is needed for the constexpr new stuff).

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

Or, alternatively we could perhaps drop the max_val related stuff, not check
the upper bound at all, then constexpr-array13.C and pr63956.C tests
wouldn't need to be adjusted.  The difference is that with the max_val stuff
in the patch we don't create ARRAY_REF in that case and fail constexpr
evaluation because we didn't simplify it, while if max_val isn't there, we
create ARRAY_REF, but when evaluating it we determine it is out of bounds
access and complain.  The initial reason for the max_val was to deal with
upper bounds for the aggregate members, but that is already handled in the
record/union case.

That sounds better.

And, second comment, not 100% sure about the unions, e.g. whether we
shouldn't somehow try to figure what is the active union member and only use
the active one rather than trying all.

Hmm, good question. Can we get away with not recursing for unions? Trying all of them might end up choosing an inactive member that shares a common initial sequence with the active member. Which is OK for reads, but not for writes.

2019-10-03  Jakub Jelinek  <ja...@redhat.com>

        PR c++/71504
        * constexpr.c (cxx_fold_indirect_ref_1): New function.
        (cxx_fold_indirect_ref): Use it.

        * g++.dg/cpp0x/constexpr-array13.C: Adjust expected diagnostics.
        * g++.dg/cpp0x/constexpr-array21.C: New test.
        * g++.dg/cpp1y/constexpr-array7.C: New test.
        * g++.dg/cpp1z/constexpr-array1.C: New test.
        * g++.dg/ubsan/pr63956.C: Adjust expected diagnostics.

2019-10-03  Jason Merrill  <ja...@redhat.com>

        PR c++/71504
        * g++.dg/cpp0x/constexpr-array20.C: New test.

--- gcc/cp/constexpr.c.jj       2019-10-03 00:32:15.603526965 +0200
+++ gcc/cp/constexpr.c  2019-10-03 14:53:59.268559514 +0200
@@ -3346,6 +3346,111 @@ same_type_ignoring_tlq_and_bounds_p (tre
    return same_type_ignoring_top_level_qualifiers_p (type1, type2);
  }
+/* Helper function for cxx_fold_indirect_ref_1, called recursively. */
+
+static tree
+cxx_fold_indirect_ref_1 (location_t loc, tree type, tree op,
+                        unsigned HOST_WIDE_INT off, bool *empty_base)
+{
+  tree optype = TREE_TYPE (op);
+  unsigned HOST_WIDE_INT const_nunits;
+  if (off == 0)
+    {
+      if (similar_type_p (optype, type))
+       return op;
+      /* Also handle conversion to an empty base class, which
+        is represented with a NOP_EXPR.  */
+      /* *(foo *)&complexfoo => __real__ complexfoo */
+      else if (TREE_CODE (optype) == COMPLEX_TYPE
+              && similar_type_p (type, TREE_TYPE (optype)))
+       return build1_loc (loc, REALPART_EXPR, type, op);
+    }
+  /* ((foo*)&complexfoo)[1] => __imag__ complexfoo */
+  else if (TREE_CODE (optype) == COMPLEX_TYPE
+          && similar_type_p (type, TREE_TYPE (optype))
+          && tree_to_uhwi (TYPE_SIZE_UNIT (type)) == off)
+    return build1_loc (loc, IMAGPART_EXPR, type, op);
+  if (is_empty_class (type)
+      && CLASS_TYPE_P (optype)
+      && DERIVED_FROM_P (type, optype))
+    {
+      *empty_base = true;
+      return op;
+    }
+  /* ((foo*)&vectorfoo)[x] => BIT_FIELD_REF<vectorfoo,...> */
+  else if (VECTOR_TYPE_P (optype)
+          && similar_type_p (type, TREE_TYPE (optype))
+          && TYPE_VECTOR_SUBPARTS (optype).is_constant (&const_nunits))
+    {
+      unsigned HOST_WIDE_INT part_width = tree_to_uhwi (TYPE_SIZE_UNIT (type));
+      unsigned HOST_WIDE_INT max_offset = part_width * const_nunits;
+      if (off < max_offset && off % part_width == 0)
+       {
+         tree index = bitsize_int (off * BITS_PER_UNIT);
+         return build3_loc (loc, BIT_FIELD_REF, type, op,
+                            TYPE_SIZE (type), index);
+       }
+    }
+  /* ((foo *)&fooarray)[x] => fooarray[x] */
+  else if (TREE_CODE (optype) == ARRAY_TYPE
+          && tree_fits_uhwi_p (TYPE_SIZE_UNIT (TREE_TYPE (optype)))
+          && !integer_zerop (TYPE_SIZE_UNIT (TREE_TYPE (optype))))
+    {
+      tree type_domain = TYPE_DOMAIN (optype);
+      tree min_val = size_zero_node;
+      tree max_val = NULL_TREE;
+      if (type_domain && TYPE_MIN_VALUE (type_domain))
+       min_val = TYPE_MIN_VALUE (type_domain);
+      if (type_domain && TYPE_MAX_VALUE (type_domain))
+       max_val = TYPE_MAX_VALUE (type_domain);
+      unsigned HOST_WIDE_INT el_sz
+       = tree_to_uhwi (TYPE_SIZE_UNIT (TREE_TYPE (optype)));
+      unsigned HOST_WIDE_INT idx = off / el_sz;
+      unsigned HOST_WIDE_INT rem = off % el_sz;
+      if (tree_fits_uhwi_p (min_val)
+         && (max_val == NULL_TREE
+             /* Punt on checking VLA bounds here.  */
+             || TREE_CODE (max_val) != INTEGER_CST
+             || (tree_fits_uhwi_p (max_val)
+                 && idx <= tree_to_uhwi (max_val))))
+       {
+         tree index = size_int (idx + tree_to_uhwi (min_val));
+         op = build4_loc (loc, ARRAY_REF, TREE_TYPE (optype), op, index,
+                          NULL_TREE, NULL_TREE);
+         return cxx_fold_indirect_ref_1 (loc, type, op, rem,
+                                         empty_base);
+       }
+    }
+  /* ((foo *)&struct_with_foo_field)[x] => COMPONENT_REF */
+  else if (RECORD_OR_UNION_TYPE_P (optype))
+    {
+      for (tree field = TYPE_FIELDS (optype);
+          field; field = DECL_CHAIN (field))
+       if (TREE_CODE (field) == FIELD_DECL
+           && TREE_TYPE (field) != error_mark_node
+           && tree_fits_uhwi_p (TYPE_SIZE_UNIT (TREE_TYPE (field))))
+         {
+           tree pos = byte_position (field);
+           if (!tree_fits_uhwi_p (pos))
+             continue;
+           unsigned HOST_WIDE_INT upos = tree_to_uhwi (pos);
+           unsigned el_sz
+             = tree_to_uhwi (TYPE_SIZE_UNIT (TREE_TYPE (field)));
+           if (upos <= off && off < upos + el_sz)
+             {
+               tree cop = build3 (COMPONENT_REF, TREE_TYPE (field),
+                                  op, field, NULL_TREE);
+               if (tree ret = cxx_fold_indirect_ref_1 (loc, type, cop,
+                                                       off - upos,
+                                                       empty_base))
+                 return ret;
+             }
+         }
+    }
+
+  return NULL_TREE;
+}
+
  /* A less strict version of fold_indirect_ref_1, which requires cv-quals to
     match.  We want to be less strict for simple *& folding; if we have a
     non-const temporary that we access through a const pointer, that should
@@ -3353,9 +3458,7 @@ same_type_ignoring_tlq_and_bounds_p (tre
     because we're dealing with things like ADDR_EXPR of INTEGER_CST which
     don't really make sense outside of constant expression evaluation.  Also
     we want to allow folding to COMPONENT_REF, which could cause trouble
-   with TBAA in fold_indirect_ref_1.
-
-   Try to keep this function synced with fold_indirect_ref_1.  */
+   with TBAA in fold_indirect_ref_1.  */
static tree
  cxx_fold_indirect_ref (location_t loc, tree type, tree op0, bool *empty_base)
@@ -3386,139 +3489,19 @@ cxx_fold_indirect_ref (location_t loc, t
          else
            return op;
        }
-      /* *(foo *)&fooarray => fooarray[0] */
-      else if (TREE_CODE (optype) == ARRAY_TYPE
-              && similar_type_p (type, TREE_TYPE (optype)))
-       {
-         tree type_domain = TYPE_DOMAIN (optype);
-         tree min_val = size_zero_node;
-         if (type_domain && TYPE_MIN_VALUE (type_domain))
-           min_val = TYPE_MIN_VALUE (type_domain);
-         return build4_loc (loc, ARRAY_REF, type, op, min_val,
-                            NULL_TREE, NULL_TREE);
-       }
-      /* *(foo *)&complexfoo => __real__ complexfoo */
-      else if (TREE_CODE (optype) == COMPLEX_TYPE
-              && similar_type_p (type, TREE_TYPE (optype)))
-       return fold_build1_loc (loc, REALPART_EXPR, type, op);
-      /* *(foo *)&vectorfoo => BIT_FIELD_REF<vectorfoo,...> */
-      else if (VECTOR_TYPE_P (optype)
-              && similar_type_p (type, TREE_TYPE (optype)))
-       {
-         tree part_width = TYPE_SIZE (type);
-         tree index = bitsize_int (0);
-         return fold_build3_loc (loc, BIT_FIELD_REF, type, op, part_width,
-                                 index);
-       }
-      /* Also handle conversion to an empty base class, which
-        is represented with a NOP_EXPR.  */
-      else if (is_empty_class (type)
-              && CLASS_TYPE_P (optype)
-              && DERIVED_FROM_P (type, optype))
-       {
-         *empty_base = true;
-         return op;
-       }
-      /* *(foo *)&struct_with_foo_field => COMPONENT_REF */
-      else if (RECORD_OR_UNION_TYPE_P (optype))
-       {
-         tree field = TYPE_FIELDS (optype);
-         for (; field; field = DECL_CHAIN (field))
-           if (TREE_CODE (field) == FIELD_DECL
-               && TREE_TYPE (field) != error_mark_node
-               && integer_zerop (byte_position (field))
-               && similar_type_p (TREE_TYPE (field), type))
-             return fold_build3 (COMPONENT_REF, type, op, field, NULL_TREE);
-       }
+      else
+       return cxx_fold_indirect_ref_1 (loc, type, op, 0, empty_base);
      }
    else if (TREE_CODE (sub) == POINTER_PLUS_EXPR
-          && poly_int_tree_p (TREE_OPERAND (sub, 1), &const_op01))
+          && tree_fits_uhwi_p (TREE_OPERAND (sub, 1)))
      {
        tree op00 = TREE_OPERAND (sub, 0);
        tree op01 = TREE_OPERAND (sub, 1);
STRIP_NOPS (op00);
        if (TREE_CODE (op00) == ADDR_EXPR)
-       {
-         tree op00type;
-         op00 = TREE_OPERAND (op00, 0);
-         op00type = TREE_TYPE (op00);
-
-         /* ((foo*)&vectorfoo)[1] => BIT_FIELD_REF<vectorfoo,...> */
-         if (VECTOR_TYPE_P (op00type)
-             && similar_type_p (type, TREE_TYPE (op00type))
-             /* POINTER_PLUS_EXPR second operand is sizetype, unsigned,
-                but we want to treat offsets with MSB set as negative.
-                For the code below negative offsets are invalid and
-                TYPE_SIZE of the element is something unsigned, so
-                check whether op01 fits into poly_int64, which implies
-                it is from 0 to INTTYPE_MAXIMUM (HOST_WIDE_INT), and
-                then just use poly_uint64 because we want to treat the
-                value as unsigned.  */
-             && tree_fits_poly_int64_p (op01))
-           {
-             tree part_width = TYPE_SIZE (type);
-             poly_uint64 max_offset
-               = (tree_to_uhwi (part_width) / BITS_PER_UNIT
-                  * TYPE_VECTOR_SUBPARTS (op00type));
-             if (known_lt (const_op01, max_offset))
-               {
-                 tree index = bitsize_int (const_op01 * BITS_PER_UNIT);
-                 return fold_build3_loc (loc,
-                                         BIT_FIELD_REF, type, op00,
-                                         part_width, index);
-               }
-           }
-         /* ((foo*)&complexfoo)[1] => __imag__ complexfoo */
-         else if (TREE_CODE (op00type) == COMPLEX_TYPE
-                  && similar_type_p (type, TREE_TYPE (op00type)))
-           {
-             if (known_eq (wi::to_poly_offset (TYPE_SIZE_UNIT (type)),
-                           const_op01))
-               return fold_build1_loc (loc, IMAGPART_EXPR, type, op00);
-           }
-         /* ((foo *)&fooarray)[1] => fooarray[1] */
-         else if (TREE_CODE (op00type) == ARRAY_TYPE
-                  && similar_type_p (type, TREE_TYPE (op00type)))
-           {
-             tree type_domain = TYPE_DOMAIN (op00type);
-             tree min_val = size_zero_node;
-             if (type_domain && TYPE_MIN_VALUE (type_domain))
-               min_val = TYPE_MIN_VALUE (type_domain);
-             offset_int off = wi::to_offset (op01);
-             offset_int el_sz = wi::to_offset (TYPE_SIZE_UNIT (type));
-             offset_int remainder;
-             off = wi::divmod_trunc (off, el_sz, SIGNED, &remainder);
-             if (remainder == 0 && TREE_CODE (min_val) == INTEGER_CST)
-               {
-                 off = off + wi::to_offset (min_val);
-                 op01 = wide_int_to_tree (sizetype, off);
-                 return build4_loc (loc, ARRAY_REF, type, op00, op01,
-                                    NULL_TREE, NULL_TREE);
-               }
-           }
-         /* Also handle conversion to an empty base class, which
-            is represented with a NOP_EXPR.  */
-         else if (is_empty_class (type)
-                  && CLASS_TYPE_P (op00type)
-                  && DERIVED_FROM_P (type, op00type))
-           {
-             *empty_base = true;
-             return op00;
-           }
-         /* ((foo *)&struct_with_foo_field)[1] => COMPONENT_REF */
-         else if (RECORD_OR_UNION_TYPE_P (op00type))
-           {
-             tree field = TYPE_FIELDS (op00type);
-             for (; field; field = DECL_CHAIN (field))
-               if (TREE_CODE (field) == FIELD_DECL
-                   && TREE_TYPE (field) != error_mark_node
-                   && tree_int_cst_equal (byte_position (field), op01)
-                   && similar_type_p (TREE_TYPE (field), type))
-                 return fold_build3 (COMPONENT_REF, type, op00,
-                                     field, NULL_TREE);
-           }
-       }
+       return cxx_fold_indirect_ref_1 (loc, type, TREE_OPERAND (op00, 0),
+                                       tree_to_uhwi (op01), empty_base);
      }
    /* *(foo *)fooarrptr => (*fooarrptr)[0] */
    else if (TREE_CODE (TREE_TYPE (subtype)) == ARRAY_TYPE
--- gcc/testsuite/g++.dg/cpp0x/constexpr-array13.C.jj   2015-11-26 
10:40:59.546389024 +0100
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-array13.C      2019-10-03 
16:13:58.636977962 +0200
@@ -3,4 +3,4 @@
constexpr char c[] = "hello";
  constexpr const char *p = c;
-constexpr char ch = *(p-1);  // { dg-error "array subscript" }
+constexpr char ch = *(p-1);  // { dg-error "is not a constant expression" }
--- gcc/testsuite/g++.dg/cpp0x/constexpr-array20.C.jj   2019-10-03 
15:59:29.108161779 +0200
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-array20.C      2019-10-03 
14:29:34.443660433 +0200
@@ -0,0 +1,15 @@
+// PR c++/71504
+// { dg-do compile { target c++11 } }
+
+enum E { e };
+
+constexpr bool arr[1][1] = {{true}};
+
+template<E x, E y>
+void check() {
+    static_assert(arr[x][y], "");
+}
+
+int main() {
+    check<e, e>();
+}
--- gcc/testsuite/g++.dg/cpp0x/constexpr-array21.C.jj   2019-10-03 
15:59:32.840105187 +0200
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-array21.C      2019-10-03 
15:15:42.260881097 +0200
@@ -0,0 +1,27 @@
+// PR c++/71504
+// { dg-do compile { target c++11 } }
+
+typedef const char A4 [10];
+
+constexpr A4 a [] = { "123", "123456", "123456789" };
+
+constexpr int len (const char *s)
+{
+  return *s ? 1 + len (s + 1) : 0;
+}
+
+constexpr const char *s = a[0];
+constexpr const char *t = (a + 2)[-2];
+
+constexpr int n0 = len (s);
+constexpr int n1 = len (t);
+
+constexpr int n2 = len (a[0]);
+constexpr int n3 = len ((a + 2)[-2]);
+
+#define A(e) static_assert ((e), #e)
+
+A (n0 == 3);
+A (n0 == n1);
+A (n0 == n2);
+A (n0 == n3);
--- gcc/testsuite/g++.dg/cpp1y/constexpr-array7.C.jj    2019-10-03 
16:04:04.280988955 +0200
+++ gcc/testsuite/g++.dg/cpp1y/constexpr-array7.C       2019-10-03 
16:01:46.674075678 +0200
@@ -0,0 +1,16 @@
+// PR c++/71504
+// { dg-do compile { target c++14 } }
+
+template <typename A>
+constexpr auto
+sum (A const &a)
+{
+  int tot = 0;
+  for (auto &row : a)
+    for (auto elem : row)
+      tot += elem;
+  return tot;
+}
+
+constexpr int const a22[2][2] = {{1,2},{3,4}};
+static_assert (sum(a22) == 10, "badsum");
--- gcc/testsuite/g++.dg/cpp1z/constexpr-array1.C.jj    2019-10-03 
16:00:05.962602903 +0200
+++ gcc/testsuite/g++.dg/cpp1z/constexpr-array1.C       2019-10-03 
15:40:09.290739197 +0200
@@ -0,0 +1,46 @@
+// PR c++/71504
+// { dg-do compile { target c++17 } }
+
+typedef __SIZE_TYPE__ size_t;
+template <typename T, T v>
+struct integral_constant
+{
+  static constexpr T value = v;
+  typedef T value_type;
+  typedef integral_constant<T, v> type;
+  constexpr operator value_type () const noexcept { return value; }
+  constexpr value_type operator() () const noexcept { return value; }
+};
+template <typename T, T v>
+constexpr T integral_constant<T, v>::value;
+typedef integral_constant<bool, true> true_type;
+typedef integral_constant<bool, false> false_type;
+template <typename>
+struct is_array : public false_type { };
+template <typename T, size_t s>
+struct is_array<T[s]> : public true_type { };
+template <typename T>
+struct is_array<T[]> : public true_type { };
+template <bool, typename, typename>
+struct conditional;
+template <bool C, typename T, typename F>
+struct conditional { typedef T type; };
+template <typename T, typename F>
+struct conditional<false, T, F> { typedef F type; };
+template <typename T>
+struct array_ref;
+template <typename T>
+using ref_t = typename conditional<is_array<T>::value, array_ref<T>, T&>::type;
+template <typename T, unsigned N>
+struct array_ref<T[N]>
+{
+  T *a;
+  using const_reference = const ref_t<T>;
+  constexpr const_reference operator[] (unsigned I) const { return {a[I]}; }
+};
+template <typename A>
+array_ref (A&) -> array_ref<A>;
+constexpr int a2[2] = {1,2};
+static_assert (array_ref{a2}[0] == 1);
+constexpr int a22[2][2] = {{1,2},{3,4}};
+static_assert (array_ref{a22}[0][0] == 1);
--- gcc/testsuite/g++.dg/ubsan/pr63956.C.jj     2019-06-19 10:20:12.390666122 
+0200
+++ gcc/testsuite/g++.dg/ubsan/pr63956.C        2019-10-03 18:57:25.541783658 
+0200
@@ -80,7 +80,7 @@ constexpr int
  fn5 (const int *a, int b)
  {
    if (b != 2)
-    b = a[b]; // { dg-error "array subscript" }
+    b = a[b]; // { dg-error "is not a constant expression" }
    return b;
  }

        Jakub


Reply via email to