On 3/6/20 6:54 PM, Marek Polacek wrote:
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).
What is folding the const into the COMPONENT_REF? Should that build a
VIEW_CONVERT_EXPR instead?
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. So there's no
CONSTRUCTOR to set TREE_READONLY on. No idea how to fix this, but it's
likely something for GCC 11 anyway.
PR c++/94074 - wrong modifying const object error for COMPONENT_REF.
* constexpr.c (modifying_const_object_p): Consider a COMPONENT_REF
const only if its object or field are const.
* 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.
---
gcc/cp/constexpr.c | 30 ++++++++++++++++++-
.../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 +++++++++++++++++
5 files changed, 126 insertions(+), 1 deletion(-)
create mode 100644 gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const17.C
create mode 100644 gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const18.C
create mode 100644 gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const19.C
create mode 100644 gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const20.C
diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index 521c87f6210..936d171b9e4 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -4401,7 +4401,35 @@ 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 the field
+ or object 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)
+ {
+ tree op1 = TREE_OPERAND (obj, 1);
+ if (CP_TYPE_CONST_P (TREE_TYPE (op1)))
+ return true;
+ else
+ {
+ tree op0 = TREE_OPERAND (obj, 0);
+ /* The LHS of . or -> might itself be a COMPONENT_REF. */
+ if (TREE_CODE (op0) == COMPONENT_REF)
+ op0 = TREE_OPERAND (op0, 1);
+ return CP_TYPE_CONST_P (TREE_TYPE (op0));
+ }
+ }
+ else
+ return true;
+ }
+
+ return false;
}
/* Evaluate an INIT_EXPR or MODIFY_EXPR. */
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 };
base-commit: 191bcd0f30dd37dec773efb0125afdcae9bd90ef