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. 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 -- Marek Polacek • Red Hat, Inc. • 300 A St, Boston, MA