[gcc(refs/vendors/ibm/heads/perf)] c++: Fix wrong modifying const object error for COMPONENT_REF [PR94074]

Jiu Fu Guo guojiufu@gcc.gnu.org
Thu Mar 19 06:16:07 GMT 2020


https://gcc.gnu.org/g:7eb5be6ab91ec03f93038ac2bcf3028cf2e7c82b

commit 7eb5be6ab91ec03f93038ac2bcf3028cf2e7c82b
Author: Marek Polacek <polacek@redhat.com>
Date:   Fri Mar 6 17:30:11 2020 -0500

    c++: Fix wrong modifying const object error for COMPONENT_REF [PR94074]
    
    I got a report that building Chromium fails with the "modifying a const
    object" error.  After some poking I realized it's a bug in GCC, not in
    their codebase.
    
    Much like with ARRAY_REFs, which can be const even though the array
    itself isn't, COMPONENT_REFs can be const although neither the object
    nor the field were declared const.  So let's dial down the checking.
    Here the COMPONENT_REF was const because of the "const_cast<const U &>(m)"
    thing -- cxx_eval_component_reference then builds a COMPONENT_REF with
    TREE_TYPE (t).
    
    While looking into this I noticed that we don't detect modifying a const
    object in certain cases like in
    <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94074#c2>.  That's because
    we never evaluate an X::X() CALL_EXPR -- there's none.  Fixed as per
    Jason's suggestion by setting TREE_READONLY on a CONSTRUCTOR after
    initialization in cxx_eval_store_expression.
    
    2020-03-11  Marek Polacek  <polacek@redhat.com>
                Jason Merrill  <jason@redhat.com>
    
            PR c++/94074 - wrong modifying const object error for COMPONENT_REF.
            * constexpr.c (cref_has_const_field): New function.
            (modifying_const_object_p): Consider a COMPONENT_REF
            const only if any of its fields are const.
            (cxx_eval_store_expression): Mark a CONSTRUCTOR of a const type
            as readonly after its initialization has been done.
    
            * g++.dg/cpp1y/constexpr-tracking-const17.C: New test.
            * g++.dg/cpp1y/constexpr-tracking-const18.C: New test.
            * g++.dg/cpp1y/constexpr-tracking-const19.C: New test.
            * g++.dg/cpp1y/constexpr-tracking-const20.C: New test.
            * g++.dg/cpp1y/constexpr-tracking-const21.C: New test.
            * g++.dg/cpp1y/constexpr-tracking-const22.C: New test.

Diff:
---
 gcc/cp/ChangeLog                                   | 10 ++++++
 gcc/cp/constexpr.c                                 | 42 +++++++++++++++++++++-
 gcc/testsuite/ChangeLog                            | 10 ++++++
 .../g++.dg/cpp1y/constexpr-tracking-const17.C      | 23 ++++++++++++
 .../g++.dg/cpp1y/constexpr-tracking-const18.C      | 23 ++++++++++++
 .../g++.dg/cpp1y/constexpr-tracking-const19.C      | 23 ++++++++++++
 .../g++.dg/cpp1y/constexpr-tracking-const20.C      | 28 +++++++++++++++
 .../g++.dg/cpp1y/constexpr-tracking-const21.C      | 28 +++++++++++++++
 .../g++.dg/cpp1y/constexpr-tracking-const22.C      | 17 +++++++++
 9 files changed, 203 insertions(+), 1 deletion(-)

diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index ebccb511ee0..da768cd2bbe 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -1,3 +1,13 @@
+2020-03-11  Marek Polacek  <polacek@redhat.com>
+	    Jason Merrill  <jason@redhat.com>
+
+	PR c++/94074 - wrong modifying const object error for COMPONENT_REF.
+	* constexpr.c (cref_has_const_field): New function.
+	(modifying_const_object_p): Consider a COMPONENT_REF
+	const only if any of its fields are const.
+	(cxx_eval_store_expression): Mark a CONSTRUCTOR of a const type
+	as readonly after its initialization has been done.
+
 2020-03-10  Marek Polacek  <polacek@redhat.com>
 
 	PR c++/94124 - wrong conversion error with non-viable overload.
diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index 76af0d710c4..192face9a3a 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -4384,6 +4384,22 @@ maybe_simplify_trivial_copy (tree &target, tree &init)
     }
 }
 
+/* Returns true if REF, which is a COMPONENT_REF, has any fields
+   of constant type.  This does not check for 'mutable', so the
+   caller is expected to be mindful of that.  */
+
+static bool
+cref_has_const_field (tree ref)
+{
+  while (TREE_CODE (ref) == COMPONENT_REF)
+    {
+      if (CP_TYPE_CONST_P (TREE_TYPE (TREE_OPERAND (ref, 1))))
+       return true;
+      ref = TREE_OPERAND (ref, 0);
+    }
+  return false;
+}
+
 /* Return true if we are modifying something that is const during constant
    expression evaluation.  CODE is the code of the statement, OBJ is the
    object in question, MUTABLE_P is true if one of the subobjects were
@@ -4401,7 +4417,23 @@ modifying_const_object_p (tree_code code, tree obj, bool mutable_p)
   if (mutable_p)
     return false;
 
-  return (TREE_READONLY (obj) || CP_TYPE_CONST_P (TREE_TYPE (obj)));
+  if (TREE_READONLY (obj))
+    return true;
+
+  if (CP_TYPE_CONST_P (TREE_TYPE (obj)))
+    {
+      /* Although a COMPONENT_REF may have a const type, we should
+	 only consider it modifying a const object when any of the
+	 field components is const.  This can happen when using
+	 constructs such as const_cast<const T &>(m), making something
+	 const even though it wasn't declared const.  */
+      if (TREE_CODE (obj) == COMPONENT_REF)
+	return cref_has_const_field (obj);
+      else
+	return true;
+    }
+
+  return false;
 }
 
 /* Evaluate an INIT_EXPR or MODIFY_EXPR.  */
@@ -4759,6 +4791,14 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t,
   else
     *valp = init;
 
+  /* After initialization, 'const' semantics apply to the value of the
+     object.  Make a note of this fact by marking the CONSTRUCTOR
+     TREE_READONLY.  */
+  if (TREE_CODE (t) == INIT_EXPR
+      && TREE_CODE (*valp) == CONSTRUCTOR
+      && TYPE_READONLY (type))
+    TREE_READONLY (*valp) = true;
+
   /* Update TREE_CONSTANT and TREE_SIDE_EFFECTS on enclosing
      CONSTRUCTORs, if any.  */
   tree elt;
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index ba4cd147d8c..1ed0071da2b 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,13 @@
+2020-03-06  Marek Polacek  <polacek@redhat.com>
+
+	PR c++/94074 - wrong modifying const object error for COMPONENT_REF.
+	* g++.dg/cpp1y/constexpr-tracking-const17.C: New test.
+	* g++.dg/cpp1y/constexpr-tracking-const18.C: New test.
+	* g++.dg/cpp1y/constexpr-tracking-const19.C: New test.
+	* g++.dg/cpp1y/constexpr-tracking-const20.C: New test.
+	* g++.dg/cpp1y/constexpr-tracking-const21.C: New test.
+	* g++.dg/cpp1y/constexpr-tracking-const22.C: New test.
+
 2020-03-11  Jakub Jelinek  <jakub@redhat.com>
 
 	PR target/94134
diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const17.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const17.C
new file mode 100644
index 00000000000..3f215d28175
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const17.C
@@ -0,0 +1,23 @@
+// PR c++/94074 - wrong modifying const object error for COMPONENT_REF.
+// { dg-do compile { target c++14 } }
+
+typedef decltype (sizeof (0)) size_t;
+
+template <typename E, size_t N>
+struct array
+{
+  constexpr const E &operator[](size_t n) const noexcept { return elems[n]; }
+  E elems[N];
+};
+
+template <typename T>
+struct S {
+  using U = array<T, 4>;
+  U m;
+  constexpr S(int) : m{}
+  {
+    const_cast<int &>(const_cast<const U &>(m)[0]) = 42;
+  }
+};
+
+constexpr S<int> p = { 10 };
diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const18.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const18.C
new file mode 100644
index 00000000000..11a680468c2
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const18.C
@@ -0,0 +1,23 @@
+// PR c++/94074 - wrong modifying const object error for COMPONENT_REF.
+// { dg-do compile { target c++14 } }
+
+typedef decltype (sizeof (0)) size_t;
+
+template <typename E, size_t N>
+struct array
+{
+  constexpr const E &operator[](size_t n) const noexcept { return elems[n]; }
+  E elems[N];
+};
+
+template <typename T>
+struct S {
+  using U = array<T, 4>;
+  const U m;
+  constexpr S(int) : m{}
+  {
+    const_cast<int &>(const_cast<const U &>(m)[0]) = 42; // { dg-error "modifying a const object" }
+  }
+};
+
+constexpr S<int> p = { 10 }; // { dg-message "originally declared" }
diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const19.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const19.C
new file mode 100644
index 00000000000..c31222ffcdd
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const19.C
@@ -0,0 +1,23 @@
+// PR c++/94074 - wrong modifying const object error for COMPONENT_REF.
+// { dg-do compile { target c++14 } }
+
+typedef decltype (sizeof (0)) size_t;
+
+template <typename E, size_t N>
+struct array
+{
+  constexpr const E &operator[](size_t n) const noexcept { return elems[n]; }
+  const E elems[N];
+};
+
+template <typename T>
+struct S {
+  using U = array<T, 4>;
+  U m;
+  constexpr S(int) : m{}
+  {
+    const_cast<int &>(const_cast<const U &>(m)[0]) = 42; // { dg-error "modifying a const object" }
+  }
+};
+
+constexpr S<int> p = { 10 }; // { dg-message "originally declared" }
diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const20.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const20.C
new file mode 100644
index 00000000000..2d5034945bd
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const20.C
@@ -0,0 +1,28 @@
+// PR c++/94074 - wrong modifying const object error for COMPONENT_REF.
+// { dg-do compile { target c++14 } }
+
+typedef decltype (sizeof (0)) size_t;
+
+template <typename E, size_t N>
+struct array
+{
+  constexpr const E &operator[](size_t n) const noexcept { return elems[n]; }
+  E elems[N];
+};
+
+template <typename E, size_t N>
+struct array2 {
+  array<E, N> a;
+};
+
+template <typename T>
+struct S {
+  using U = array2<T, 4>;
+  U m;
+  constexpr S(int) : m{}
+  {
+    const_cast<int &>(const_cast<const U &>(m).a[0]) = 42;
+  }
+};
+
+constexpr S<int> p = { 10 };
diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const21.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const21.C
new file mode 100644
index 00000000000..0b16193398e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const21.C
@@ -0,0 +1,28 @@
+// PR c++/94074 - wrong modifying const object error for COMPONENT_REF.
+// { dg-do compile { target c++14 } }
+
+typedef decltype (sizeof (0)) size_t;
+
+template <typename E, size_t N>
+struct array
+{
+  constexpr const E &operator[](size_t n) const noexcept { return elems[n]; }
+  E elems[N];
+};
+
+template <typename E, size_t N>
+struct array2 {
+  array<E, N> a;
+};
+
+template <typename T>
+struct S {
+  using U = array2<T, 4>;
+  const U m;
+  constexpr S(int) : m{}
+  {
+    const_cast<int &>(m.a[0]) = 42; // { dg-error "modifying a const object" }
+  }
+};
+
+constexpr S<int> p = { 10 }; // { dg-message "originally declared" }
diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const22.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const22.C
new file mode 100644
index 00000000000..216cf1607a4
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const22.C
@@ -0,0 +1,17 @@
+// PR c++/94074 - wrong modifying const object error for COMPONENT_REF.
+// { dg-do compile { target c++14 } }
+
+struct X {
+  int i;
+};
+
+template <typename T>
+struct S {
+  const X x;
+  constexpr S(int) : x{}
+  {
+    const_cast<X&>(x).i = 19; // { dg-error "modifying a const object" }
+  }
+};
+
+constexpr S<int> p = { 10 }; // { dg-message "originally declared" }


More information about the Gcc-cvs mailing list