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>
PR c++/94124 - wrong conversion error with non-viable overload.
......
......@@ -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;
......
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
......
// 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