From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTPS id 6F571385609A for ; Thu, 9 Jun 2022 08:38:02 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 6F571385609A Received: from mimecast-mx02.redhat.com (mx3-rdu2.redhat.com [66.187.233.73]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-558-XuOcAUmDMCmWNfowWbNmPg-1; Thu, 09 Jun 2022 04:38:00 -0400 X-MC-Unique: XuOcAUmDMCmWNfowWbNmPg-1 Received: from smtp.corp.redhat.com (int-mx09.intmail.prod.int.rdu2.redhat.com [10.11.54.9]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id A83B91C1394F for ; Thu, 9 Jun 2022 08:38:00 +0000 (UTC) Received: from tucnak.zalov.cz (unknown [10.39.192.11]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 65995492CA3; Thu, 9 Jun 2022 08:38:00 +0000 (UTC) Received: from tucnak.zalov.cz (localhost [127.0.0.1]) by tucnak.zalov.cz (8.17.1/8.17.1) with ESMTPS id 2598bvkm1626677 (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 bits=256 verify=NOT); Thu, 9 Jun 2022 10:37:58 +0200 Received: (from jakub@localhost) by tucnak.zalov.cz (8.17.1/8.17.1/Submit) id 2598bvdu1626676; Thu, 9 Jun 2022 10:37:57 +0200 Date: Thu, 9 Jun 2022 10:37:57 +0200 From: Jakub Jelinek To: Jason Merrill Cc: gcc-patches@gcc.gnu.org Subject: [PATCH] c++: Add support for __real__/__imag__ modifications in constant expressions [PR88174] Message-ID: Reply-To: Jakub Jelinek MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.85 on 10.11.54.9 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Type: text/plain; charset=utf-8 Content-Disposition: inline Content-Transfer-Encoding: 8bit X-Spam-Status: No, score=-4.8 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_NONE, TXREP, T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 09 Jun 2022 08:38:04 -0000 Hi! We claim we support P0415R1 (constexpr complex), but e.g. #include constexpr bool foo () { std::complex a (1.0, 2.0); a += 3.0; a.real (6.0); return a.real () == 6.0 && a.imag () == 2.0; } static_assert (foo ()); fails with test.C:12:20: error: non-constant condition for static assertion 12 | static_assert (foo ()); | ~~~~^~ test.C:12:20: in ‘constexpr’ expansion of ‘foo()’ test.C:8:10: in ‘constexpr’ expansion of ‘a.std::complex::real(6.0e+0)’ test.C:12:20: error: modification of ‘__real__ a.std::complex::_M_value’ is not a constant expression The problem is we don't handle REALPART_EXPR and IMAGPART_EXPR in cxx_eval_store_expression. The following patch attempts to support it (with a requirement that those are the outermost expressions, ARRAY_REF/COMPONENT_REF etc. are just not possible on the result of these, BIT_FIELD_REF would be theoretically possible if trying to extract some bits from one part of a complex int, but I don't see how it could appear in the FE trees. For these references, the code handles value being COMPLEX_CST, COMPLEX_EXPR or CONSTRUCTOR_NO_CLEARING empty CONSTRUCTOR (what we use to represent uninitialized values for C++20 and later) and the code starts by rewriting it to COMPLEX_EXPR, so that we can freely adjust the individual parts and later on possibly optimize it back to COMPLEX_CST if both halves are constant. Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk? 2022-06-09 Jakub Jelinek PR c++/88174 * constexpr.cc (cxx_eval_store_expression): Handle REALPART_EXPR and IMAGPART_EXPR. * g++.dg/cpp1y/constexpr-complex1.C: New test. --- gcc/cp/constexpr.cc.jj 2022-06-08 08:21:02.973448193 +0200 +++ gcc/cp/constexpr.cc 2022-06-08 17:13:04.986040449 +0200 @@ -5707,6 +5707,20 @@ cxx_eval_store_expression (const constex } break; + case REALPART_EXPR: + gcc_assert (probe == target); + vec_safe_push (refs, integer_zero_node); + vec_safe_push (refs, TREE_TYPE (probe)); + probe = TREE_OPERAND (probe, 0); + break; + + case IMAGPART_EXPR: + gcc_assert (probe == target); + vec_safe_push (refs, integer_one_node); + vec_safe_push (refs, TREE_TYPE (probe)); + probe = TREE_OPERAND (probe, 0); + break; + default: if (evaluated) object = probe; @@ -5749,6 +5763,8 @@ cxx_eval_store_expression (const constex auto_vec index_pos_hints; bool activated_union_member_p = false; bool empty_base = false; + int complex_part = -1; + tree *complex_expr = NULL; while (!refs->is_empty ()) { if (*valp == NULL_TREE) @@ -5785,14 +5801,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); + complex_expr = valp; + valp = &TREE_OPERAND (*valp, index != integer_zero_node); + gcc_checking_assert (refs->is_empty ()); + type = reftype; + complex_part = index != integer_zero_node; + 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 @@ -5946,6 +5984,24 @@ cxx_eval_store_expression (const constex = get_or_insert_ctor_field (*valp, indexes[i], index_pos_hints[i]); valp = &cep->value; } + if (complex_part != -1) + { + 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); + complex_expr = valp; + valp = &TREE_OPERAND (*valp, complex_part); + } } if (*non_constant_p) @@ -6016,6 +6072,22 @@ cxx_eval_store_expression (const constex if (TREE_CODE (TREE_TYPE (elt)) == UNION_TYPE) CONSTRUCTOR_NO_CLEARING (elt) = false; } + if (complex_expr) + { + if (tree c = const_binop (COMPLEX_EXPR, TREE_TYPE (*complex_expr), + TREE_OPERAND (*complex_expr, 0), + TREE_OPERAND (*complex_expr, 1))) + *complex_expr = c; + else + { + TREE_CONSTANT (*complex_expr) + = (TREE_CONSTANT (TREE_OPERAND (*complex_expr, 0)) + & TREE_CONSTANT (TREE_OPERAND (*complex_expr, 1))); + TREE_SIDE_EFFECTS (*complex_expr) + = (TREE_SIDE_EFFECTS (TREE_OPERAND (*complex_expr, 0)) + | TREE_SIDE_EFFECTS (TREE_OPERAND (*complex_expr, 1))); + } + } if (lval) return target; --- gcc/testsuite/g++.dg/cpp1y/constexpr-complex1.C.jj 2022-06-08 17:32:39.190148964 +0200 +++ gcc/testsuite/g++.dg/cpp1y/constexpr-complex1.C 2022-06-08 17:29:04.413321741 +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