From 7eb5be6ab91ec03f93038ac2bcf3028cf2e7c82b Mon Sep 17 00:00:00 2001 From: Marek Polacek Date: Fri, 6 Mar 2020 17:30:11 -0500 Subject: [PATCH] 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(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 . 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 Jason Merrill 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. --- 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(-) 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 create mode 100644 gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const21.C create mode 100644 gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const22.C 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 + Jason Merrill + + 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 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(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 + + 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 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 +struct array +{ + constexpr const E &operator[](size_t n) const noexcept { return elems[n]; } + E elems[N]; +}; + +template +struct S { + using U = array; + U m; + constexpr S(int) : m{} + { + const_cast(const_cast(m)[0]) = 42; + } +}; + +constexpr S 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 +struct array +{ + constexpr const E &operator[](size_t n) const noexcept { return elems[n]; } + E elems[N]; +}; + +template +struct S { + using U = array; + const U m; + constexpr S(int) : m{} + { + const_cast(const_cast(m)[0]) = 42; // { dg-error "modifying a const object" } + } +}; + +constexpr S 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 +struct array +{ + constexpr const E &operator[](size_t n) const noexcept { return elems[n]; } + const E elems[N]; +}; + +template +struct S { + using U = array; + U m; + constexpr S(int) : m{} + { + const_cast(const_cast(m)[0]) = 42; // { dg-error "modifying a const object" } + } +}; + +constexpr S 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 +struct array +{ + constexpr const E &operator[](size_t n) const noexcept { return elems[n]; } + E elems[N]; +}; + +template +struct array2 { + array a; +}; + +template +struct S { + using U = array2; + U m; + constexpr S(int) : m{} + { + const_cast(const_cast(m).a[0]) = 42; + } +}; + +constexpr S 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 +struct array +{ + constexpr const E &operator[](size_t n) const noexcept { return elems[n]; } + E elems[N]; +}; + +template +struct array2 { + array a; +}; + +template +struct S { + using U = array2; + const U m; + constexpr S(int) : m{} + { + const_cast(m.a[0]) = 42; // { dg-error "modifying a const object" } + } +}; + +constexpr S 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 +struct S { + const X x; + constexpr S(int) : x{} + { + const_cast(x).i = 19; // { dg-error "modifying a const object" } + } +}; + +constexpr S p = { 10 }; // { dg-message "originally declared" } -- 2.30.2