re PR c++/71504 ([C++11] constexpr fails with multidimensional arrays)
authorJakub Jelinek <jakub@gcc.gnu.org>
Fri, 4 Oct 2019 06:56:02 +0000 (08:56 +0200)
committerJakub Jelinek <jakub@gcc.gnu.org>
Fri, 4 Oct 2019 06:56:02 +0000 (08:56 +0200)
PR c++/71504
* constexpr.c (cxx_fold_indirect_ref_1): New function.
(cxx_fold_indirect_ref): Use it.

* g++.dg/cpp0x/constexpr-array21.C: New test.
* g++.dg/cpp1y/constexpr-array7.C: New test.
* g++.dg/cpp1z/constexpr-array1.C: New test.

2019-10-04  Jason Merrill  <jason@redhat.com>

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

From-SVN: r276563

gcc/cp/ChangeLog
gcc/cp/constexpr.c
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/cpp0x/constexpr-array20.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp0x/constexpr-array21.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp1y/constexpr-array7.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp1z/constexpr-array1.C [new file with mode: 0644]

index a11bdaf693ab1dbe723110956d98866705c54921..e705bcce438dad5f93b1aac28e2645662a07d88d 100644 (file)
@@ -1,5 +1,9 @@
 2019-10-04  Jakub Jelinek  <jakub@redhat.com>
 
+       PR c++/71504
+       * constexpr.c (cxx_fold_indirect_ref_1): New function.
+       (cxx_fold_indirect_ref): Use it.
+
        PR c++/91974
        * cp-gimplify.c (cp_gimplify_expr) <case CALL_EXPR>: For
        -fstrong-eval-order ensure CALL_EXPR_FN side-effects are evaluated
index 200879378c532af47a51edb60c5d9b394ce8f22c..2dc57f145c3f6bd565e884fce1812610f10435ce 100644 (file)
@@ -3346,6 +3346,103 @@ same_type_ignoring_tlq_and_bounds_p (tree type1, tree type2)
   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;
+      if (type_domain && TYPE_MIN_VALUE (type_domain))
+       min_val = TYPE_MIN_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))
+       {
+         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 (TREE_CODE (optype) == RECORD_TYPE)
+    {
+      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 +3450,7 @@ same_type_ignoring_tlq_and_bounds_p (tree type1, tree type2)
    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 +3481,19 @@ cxx_fold_indirect_ref (location_t loc, tree type, tree op0, bool *empty_base)
          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
index 1dce341bd58f62b7b88dbf86fccbaa2f9854cbbf..60e6caa25205fd7d78a337afacbeec0d2ebce672 100644 (file)
@@ -1,3 +1,15 @@
+2019-10-04  Jakub Jelinek  <jakub@redhat.com>
+
+       PR c++/71504
+       * g++.dg/cpp0x/constexpr-array21.C: New test.
+       * g++.dg/cpp1y/constexpr-array7.C: New test.
+       * g++.dg/cpp1z/constexpr-array1.C: New test.
+
+2019-10-04  Jason Merrill  <jason@redhat.com>
+
+       PR c++/71504
+       * g++.dg/cpp0x/constexpr-array20.C: New test.
+
 2019-10-04  Jakub Jelinek  <jakub@redhat.com>
 
        PR c++/91974
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-array20.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-array20.C
new file mode 100644 (file)
index 0000000..217bbf4
--- /dev/null
@@ -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>(); 
+}
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-array21.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-array21.C
new file mode 100644 (file)
index 0000000..e085098
--- /dev/null
@@ -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);
diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-array7.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-array7.C
new file mode 100644 (file)
index 0000000..30bd8fd
--- /dev/null
@@ -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");
diff --git a/gcc/testsuite/g++.dg/cpp1z/constexpr-array1.C b/gcc/testsuite/g++.dg/cpp1z/constexpr-array1.C
new file mode 100644 (file)
index 0000000..02435d5
--- /dev/null
@@ -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);