Commit 7eb5be6a by Marek Polacek

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.
parent 4512b7d8
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> 2020-03-10 Marek Polacek <polacek@redhat.com>
PR c++/94124 - wrong conversion error with non-viable overload. PR c++/94124 - wrong conversion error with non-viable overload.
......
...@@ -4384,6 +4384,22 @@ maybe_simplify_trivial_copy (tree &target, tree &init) ...@@ -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 /* Return true if we are modifying something that is const during constant
expression evaluation. CODE is the code of the statement, OBJ is the 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 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) ...@@ -4401,7 +4417,23 @@ modifying_const_object_p (tree_code code, tree obj, bool mutable_p)
if (mutable_p) if (mutable_p)
return false; 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. */ /* Evaluate an INIT_EXPR or MODIFY_EXPR. */
...@@ -4759,6 +4791,14 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t, ...@@ -4759,6 +4791,14 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t,
else else
*valp = init; *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 /* Update TREE_CONSTANT and TREE_SIDE_EFFECTS on enclosing
CONSTRUCTORs, if any. */ CONSTRUCTORs, if any. */
tree elt; tree elt;
......
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> 2020-03-11 Jakub Jelinek <jakub@redhat.com>
PR target/94134 PR target/94134
......
// 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 };
// 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" }
// 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" }
// 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 };
// 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" }
// 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" }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment