On Mon, Jun 27, 2022 at 06:31:18PM +0200, Jakub Jelinek via Gcc-patches wrote: > Because the late evaluation of the initializer could have touched > the destination, so we need to reevaluate it. > Same reason why we call get_or_insert_ctor_field again in the second > loop as we call it in the first loop. > If it would help, I could move that repeated part into:
> > This seems like it needs to come before the ctors loop, so that these flags > > can be propagated out to enclosing constructors. > > I could indeed move this in between > bool c = TREE_CONSTANT (init); > bool s = TREE_SIDE_EFFECTS (init); > and > if (!c || s || activated_union_member_p) > and update c and s from *cexpr flags. Here is it in patch form, so far lightly tested, ok for trunk if it passes full bootstrap/regtest? 2022-07-04 Jakub Jelinek <ja...@redhat.com> PR c++/88174 * constexpr.cc (canonicalize_complex_to_complex_expr): New function. (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-07-04 12:26:18.147053851 +0200 +++ gcc/cp/constexpr.cc 2022-07-04 17:35:53.100556949 +0200 @@ -5640,6 +5640,26 @@ modifying_const_object_p (tree_code code return false; } +/* Helper of cxx_eval_store_expression, turn a COMPLEX_CST or + empty no clearing CONSTRUCTOR into a COMPLEX_EXPR. */ + +static tree +canonicalize_complex_to_complex_expr (tree t) +{ + if (TREE_CODE (t) == COMPLEX_CST) + t = build2 (COMPLEX_EXPR, TREE_TYPE (t), + TREE_REALPART (t), TREE_IMAGPART (t)); + else if (TREE_CODE (t) == CONSTRUCTOR + && CONSTRUCTOR_NELTS (t) == 0 + && CONSTRUCTOR_NO_CLEARING (t)) + { + tree r = build_constructor (TREE_TYPE (TREE_TYPE (t)), NULL); + CONSTRUCTOR_NO_CLEARING (r) = 1; + t = build2 (COMPLEX_EXPR, TREE_TYPE (t), r, r); + } + return t; +} + /* Evaluate an INIT_EXPR or MODIFY_EXPR. */ static tree @@ -5726,6 +5746,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; @@ -5764,7 +5798,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; @@ -5804,14 +5839,26 @@ 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) + { + *valp = canonicalize_complex_to_complex_expr (*valp); + 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 @@ -5854,7 +5901,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 @@ -5916,11 +5963,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; } } @@ -5961,6 +6008,16 @@ 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) + { + *valp = canonicalize_complex_to_complex_expr (*valp); + gcc_assert (TREE_CODE (*valp) == COMPLEX_EXPR); + valp = &TREE_OPERAND (*valp, + TREE_CODE (indexes[i]) == IMAGPART_EXPR); + break; + } constructor_elt *cep = get_or_insert_ctor_field (*valp, indexes[i], index_pos_hints[i]); valp = &cep->value; @@ -6023,17 +6080,45 @@ cxx_eval_store_expression (const constex CONSTRUCTORs, if any. */ bool c = TREE_CONSTANT (init); bool s = TREE_SIDE_EFFECTS (init); + if (!indexes->is_empty ()) + { + tree last = indexes->last (); + if (TREE_CODE (last) == REALPART_EXPR + || TREE_CODE (last) == IMAGPART_EXPR) + { + /* And canonicalize COMPLEX_EXPR into COMPLEX_CST if + possible. */ + 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))); + } + c = TREE_CONSTANT (*cexpr); + s = TREE_SIDE_EFFECTS (*cexpr); + } + } 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 (lval) --- gcc/testsuite/g++.dg/cpp1y/constexpr-complex1.C.jj 2022-07-04 17:30:47.884580998 +0200 +++ gcc/testsuite/g++.dg/cpp1y/constexpr-complex1.C 2022-07-04 17:30:47.884580998 +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