On 6/17/22 13:06, Jakub Jelinek wrote:
On Fri, Jun 10, 2022 at 09:57:06PM +0200, Jakub Jelinek via Gcc-patches wrote:
On Fri, Jun 10, 2022 at 01:27:28PM -0400, Jason Merrill wrote:
Doesn't this assert mean that complex_expr will always be == valp?
No, even when handling the pushed *PART_EXPR, it will set
valp = &TREE_OPERAND (*valp, index != integer_zero_node);
So, valp will be either &TREE_OPERAND (*complex_expr, 0)
or &TREE_OPERAND (*complex_expr, 1).
As *valp = init; is what is usually then stored and we want to store there
the scalar.
I don't understand this block; shouldn't valp point to the real or imag part
of the complex number at this point? How could complex_part be set without
us handling the complex case in the loop already?
Because for most references, the code will do:
vec_safe_push (ctors, *valp);
vec_safe_push (indexes, index);
I chose not to do this for *PART_EXPR, because the COMPLEX_EXPR isn't a
CONSTRUCTOR and code later on e.g. walks all the ctors and accesses
CONSTRUCTOR_NO_CLEARING on them etc. As the *PART_EXPR is asserted to
be outermost only, complex_expr is a variant of that ctors push and
complex_part of the indexes.
The reason for the above if is just in case the evaluation of the rhs
of the store would store to the complex and could e.g. make it a COMPLEX_CST
again.
I might have added the COMPLEX_EXPR to ctors instead of a separate variable,
but this is fine too.
See above.
The COMPLEX_EXPR needs special handling (conversion into COMPLEX_CST if it
is constant) anyway.
Here is a variant patch which pushes even the *PART_EXPR related entries
into ctors and indexes vectors, so it doesn't need to use extra variables
for the complex stuff.
Thanks.
2022-06-17 Jakub Jelinek <ja...@redhat.com>
PR c++/88174
* constexpr.cc (cxx_eval_store_expression): Handle REALPART_EXPR
and IMAGPART_EXPR. Change ctors from releasing_vec to
auto_vec<tree *>, adjust all uses.
* g++.dg/cpp1y/constexpr-complex1.C: New test.
--- gcc/cp/constexpr.cc.jj 2022-06-09 17:42:23.606243920 +0200
+++ gcc/cp/constexpr.cc 2022-06-17 18:59:54.809208997 +0200
@@ -5714,6 +5714,20 @@ cxx_eval_store_expression (const constex
}
break;
+ case REALPART_EXPR:
+ gcc_assert (probe == target);
+ vec_safe_push (refs, probe);
+ vec_safe_push (refs, TREE_TYPE (probe));
+ probe = TREE_OPERAND (probe, 0);
+ break;
+
+ case IMAGPART_EXPR:
+ gcc_assert (probe == target);
+ vec_safe_push (refs, probe);
+ vec_safe_push (refs, TREE_TYPE (probe));
+ probe = TREE_OPERAND (probe, 0);
+ break;
+
default:
if (evaluated)
object = probe;
@@ -5752,7 +5766,8 @@ cxx_eval_store_expression (const constex
type = TREE_TYPE (object);
bool no_zero_init = true;
- releasing_vec ctors, indexes;
+ auto_vec<tree *> ctors;
+ releasing_vec indexes;
auto_vec<int> index_pos_hints;
bool activated_union_member_p = false;
bool empty_base = false;
@@ -5792,14 +5807,36 @@ cxx_eval_store_expression (const constex
*valp = ary_ctor;
}
- /* If the value of object is already zero-initialized, any new ctors for
- subobjects will also be zero-initialized. */
- no_zero_init = CONSTRUCTOR_NO_CLEARING (*valp);
-
enum tree_code code = TREE_CODE (type);
tree reftype = refs->pop();
tree index = refs->pop();
+ if (code == COMPLEX_TYPE)
+ {
+ if (TREE_CODE (*valp) == COMPLEX_CST)
+ *valp = build2 (COMPLEX_EXPR, type, TREE_REALPART (*valp),
+ TREE_IMAGPART (*valp));
+ else if (TREE_CODE (*valp) == CONSTRUCTOR
+ && CONSTRUCTOR_NELTS (*valp) == 0
+ && CONSTRUCTOR_NO_CLEARING (*valp))
+ {
+ tree r = build_constructor (reftype, NULL);
+ CONSTRUCTOR_NO_CLEARING (r) = 1;
+ *valp = build2 (COMPLEX_EXPR, type, r, r);
+ }
+ gcc_assert (TREE_CODE (*valp) == COMPLEX_EXPR);
+ ctors.safe_push (valp);
+ vec_safe_push (indexes, index);
+ valp = &TREE_OPERAND (*valp, TREE_CODE (index) == IMAGPART_EXPR);
+ gcc_checking_assert (refs->is_empty ());
+ type = reftype;
+ break;
+ }
+
+ /* If the value of object is already zero-initialized, any new ctors for
+ subobjects will also be zero-initialized. */
+ no_zero_init = CONSTRUCTOR_NO_CLEARING (*valp);
+
if (code == RECORD_TYPE && is_empty_field (index))
/* Don't build a sub-CONSTRUCTOR for an empty base or field, as they
have no data and might have an offset lower than previously declared
@@ -5842,7 +5879,7 @@ cxx_eval_store_expression (const constex
no_zero_init = true;
}
- vec_safe_push (ctors, *valp);
+ ctors.safe_push (valp);
vec_safe_push (indexes, index);
constructor_elt *cep
@@ -5904,11 +5941,11 @@ cxx_eval_store_expression (const constex
semantics are not applied on an object under construction.
They come into effect when the constructor for the most
derived object ends." */
- for (tree elt : *ctors)
+ for (tree *elt : ctors)
if (same_type_ignoring_top_level_qualifiers_p
- (TREE_TYPE (const_object_being_modified), TREE_TYPE (elt)))
+ (TREE_TYPE (const_object_being_modified), TREE_TYPE (*elt)))
{
- fail = TREE_READONLY (elt);
+ fail = TREE_READONLY (*elt);
break;
}
}
@@ -5949,6 +5986,28 @@ cxx_eval_store_expression (const constex
valp = ctx->global->values.get (object);
for (unsigned i = 0; i < vec_safe_length (indexes); i++)
{
+ ctors[i] = valp;
+ if (TREE_CODE (indexes[i]) == REALPART_EXPR
+ || TREE_CODE (indexes[i]) == IMAGPART_EXPR)
+ {
+ if (TREE_CODE (*valp) == COMPLEX_CST)
+ *valp = build2 (COMPLEX_EXPR, TREE_TYPE (*valp),
+ TREE_REALPART (*valp),
+ TREE_IMAGPART (*valp));
+ else if (TREE_CODE (*valp) == CONSTRUCTOR
+ && CONSTRUCTOR_NELTS (*valp) == 0
+ && CONSTRUCTOR_NO_CLEARING (*valp))
+ {
+ tree r = build_constructor (TREE_TYPE (TREE_TYPE (*valp)),
+ NULL);
+ CONSTRUCTOR_NO_CLEARING (r) = 1;
+ *valp = build2 (COMPLEX_EXPR, TREE_TYPE (*valp), r, r);
+ }
+ gcc_assert (TREE_CODE (*valp) == COMPLEX_EXPR);
+ valp = &TREE_OPERAND (*valp,
+ TREE_CODE (indexes[i]) == IMAGPART_EXPR);
+ break;
+ }
Hmm, why do we need to handle complex in the !preeval case? I'd think
we want to preevaluate all complex values or components thereof.
constructor_elt *cep
= get_or_insert_ctor_field (*valp, indexes[i], index_pos_hints[i]);
valp = &cep->value;
@@ -6012,17 +6071,41 @@ cxx_eval_store_expression (const constex
bool c = TREE_CONSTANT (init);
bool s = TREE_SIDE_EFFECTS (init);
if (!c || s || activated_union_member_p)
- for (tree elt : *ctors)
+ for (tree *elt : ctors)
{
+ if (TREE_CODE (*elt) != CONSTRUCTOR)
+ continue;
if (!c)
- TREE_CONSTANT (elt) = false;
+ TREE_CONSTANT (*elt) = false;
if (s)
- TREE_SIDE_EFFECTS (elt) = true;
+ TREE_SIDE_EFFECTS (*elt) = true;
/* Clear CONSTRUCTOR_NO_CLEARING since we've activated a member of
this union. */
- if (TREE_CODE (TREE_TYPE (elt)) == UNION_TYPE)
- CONSTRUCTOR_NO_CLEARING (elt) = false;
+ if (TREE_CODE (TREE_TYPE (*elt)) == UNION_TYPE)
+ CONSTRUCTOR_NO_CLEARING (*elt) = false;
}
+ if (!indexes->is_empty ())
+ {
+ tree last = indexes->last ();
+ if (TREE_CODE (last) == REALPART_EXPR
+ || TREE_CODE (last) == IMAGPART_EXPR)
+ {
+ tree *cexpr = ctors.last ();
+ if (tree c = const_binop (COMPLEX_EXPR, TREE_TYPE (*cexpr),
+ TREE_OPERAND (*cexpr, 0),
+ TREE_OPERAND (*cexpr, 1)))
+ *cexpr = c;
+ else
+ {
+ TREE_CONSTANT (*cexpr)
+ = (TREE_CONSTANT (TREE_OPERAND (*cexpr, 0))
+ & TREE_CONSTANT (TREE_OPERAND (*cexpr, 1)));
+ TREE_SIDE_EFFECTS (*cexpr)
+ = (TREE_SIDE_EFFECTS (TREE_OPERAND (*cexpr, 0))
+ | TREE_SIDE_EFFECTS (TREE_OPERAND (*cexpr, 1)));
This seems like it needs to come before the ctors loop, so that these
flags can be propagated out to enclosing constructors.
+ }
+ }
+ }
if (lval)
return target;
--- gcc/testsuite/g++.dg/cpp1y/constexpr-complex1.C.jj 2022-06-17
17:41:45.885780190 +0200
+++ gcc/testsuite/g++.dg/cpp1y/constexpr-complex1.C 2022-06-17
17:41:45.885780190 +0200
@@ -0,0 +1,24 @@
+// PR c++/88174
+// { dg-do compile { target c++14 } }
+
+constexpr bool
+foo (double x, double y, double z, double w)
+{
+ __complex__ double a = 0;
+ __real__ a = x;
+ __imag__ a = y;
+#if __cpp_constexpr >= 201907L
+ __complex__ double b;
+ __real__ b = z;
+#else
+ __complex__ double b = z;
+#endif
+ __imag__ b = w;
+ a += b;
+ a -= b;
+ a *= b;
+ a /= b;
+ return __real__ a == x && __imag__ a == y;
+}
+
+static_assert (foo (1.0, 2.0, 3.0, 4.0), "");
Jakub