From patchwork Fri May 13 23:41:40 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Marek Polacek X-Patchwork-Id: 53984 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 7372439730D9 for ; Fri, 13 May 2022 23:42:18 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 7372439730D9 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1652485338; bh=PrdOKnUphW52mR6VUt2HlgaDDLi/CnRMGsO32ZHRC5E=; h=Date:To:Subject:References:In-Reply-To:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc: From; b=mx0pRKqfgg2UM/G2W652/qgAUInudIvMIPLLxr8Bnmoa83FOYoFNOAhPpp5rwwlyi nkwrTjxSLLr6Is0PGQzt9mDyCJ+jzXuYR0llkBBu9aZBupyIS8s/vKwJ0xJ10gpqDi 7Am7B2jNdO167UQoSMNP/WPypRUGizbWdzcyF1Iw= X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org 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 49D70396E020 for ; Fri, 13 May 2022 23:41:46 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 49D70396E020 Received: from mail-qv1-f70.google.com (mail-qv1-f70.google.com [209.85.219.70]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-154-MA_c9YeJMZiR_Kd8Vv5f8g-1; Fri, 13 May 2022 19:41:44 -0400 X-MC-Unique: MA_c9YeJMZiR_Kd8Vv5f8g-1 Received: by mail-qv1-f70.google.com with SMTP id n5-20020a0cbe85000000b0045aff56564bso7904734qvi.4 for ; Fri, 13 May 2022 16:41:44 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:date:from:to:cc:subject:message-id:references :mime-version:content-disposition:in-reply-to:user-agent; bh=PrdOKnUphW52mR6VUt2HlgaDDLi/CnRMGsO32ZHRC5E=; b=uU68aTQkFbGRd0FJJjjc4MB7TfHmmSPkq6YqXK1OwnUGk1gJ0KMYnfIKGPSveW/0IS a2xjEyprZjWFEY9Otn0cCKgv5UvBQaoCvez20wrfZDR2Fd10JY8yMmKZBS5kj5MgbRkR lpYUAE4pjMbH7guZxitabcSrMC6w5/c4PEWrElGxcQHRQxeucGQ3+iTtaf6OvonJfPfK MbxEdI3gOxwNMjZWTYMP3NXC3Pyc9lqlkkRzDgHEf2xBCX3yi8RTQQyAJAs9/4ZLQR+d r9wwjsik53tiwmheaiz0H5srmY48yrTxFISakZnGUgL5RREu2oD1e3JwxvY+Agwi2Sbk /myA== X-Gm-Message-State: AOAM532xzkMfvy9k383+EVDF23fuXTMigUhQaSdnbkEkYCAZSmAxbuqs Yk0KYnM5FTqocxixe4sDG7GcpOsuSo2guBAEHxjGMWxzorqk0MI05O99tK/xs/unu8feVTlsbcN UtlYAuzlTKwQKdyD8bw== X-Received: by 2002:a05:622a:251:b0:2f3:b99e:3dba with SMTP id c17-20020a05622a025100b002f3b99e3dbamr6764501qtx.395.1652485303944; Fri, 13 May 2022 16:41:43 -0700 (PDT) X-Google-Smtp-Source: ABdhPJySpMNXIvxiWuZLdL2pPEKuJuwJCbpCmhGNN1p/deQ5YfmxPPa4/RHvIUk7EUEy2jYhhssMPQ== X-Received: by 2002:a05:622a:251:b0:2f3:b99e:3dba with SMTP id c17-20020a05622a025100b002f3b99e3dbamr6764477qtx.395.1652485303382; Fri, 13 May 2022 16:41:43 -0700 (PDT) Received: from redhat.com ([2601:184:4780:4310::e811]) by smtp.gmail.com with ESMTPSA id g1-20020a379d01000000b006a05a6243d5sm2089108qke.13.2022.05.13.16.41.42 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 13 May 2022 16:41:42 -0700 (PDT) Date: Fri, 13 May 2022 19:41:40 -0400 To: Jason Merrill Subject: [PATCH v4] c++: ICE with temporary of class type in DMI [PR100252] Message-ID: References: <20220426230226.677300-1-polacek@redhat.com> <8e0bc483-7fba-225c-807b-dbe89efceb5e@idea> <41c4c020-d058-cdeb-efa9-9b7956ca3f05@redhat.com> <1211247e-15ac-5851-029b-6c632208eb36@redhat.com> MIME-Version: 1.0 In-Reply-To: <1211247e-15ac-5851-029b-6c632208eb36@redhat.com> User-Agent: Mutt/2.2.1 (2022-02-19) X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Disposition: inline X-Spam-Status: No, score=-23.5 required=5.0 tests=BAYES_00, DKIM_INVALID, DKIM_SIGNED, GIT_PATCH_0, KAM_DMARC_NONE, KAM_DMARC_STATUS, RCVD_IN_DNSWL_LOW, 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-Patchwork-Original-From: Marek Polacek via Gcc-patches From: Marek Polacek Reply-To: Marek Polacek Cc: Jakub Jelinek , GCC Patches Errors-To: gcc-patches-bounces+patchwork=sourceware.org@gcc.gnu.org Sender: "Gcc-patches" On Sat, May 07, 2022 at 06:02:13PM -0400, Jason Merrill wrote: > On 5/7/22 15:11, Marek Polacek wrote: > > On Tue, May 03, 2022 at 04:59:38PM -0400, Jason Merrill wrote: > > > Does this testcase still work with this patch? > > > > > > struct A { > > > const A* p = this; > > > }; > > > > > > struct B { > > > A a = A{}; > > > }; > > > > > > constexpr B b; > > > static_assert (b.a.p == &b.a); > > > > Ouch, no. Thanks for catching this, it would have been very unpleasant > > to hit this problem later... > > > > The reason your testcase was rejected is that replacing a PLACEHOLDER > > in the outermost TARGET_EXPR broke guaranteed copy elision. Now that > > I've spent another day on this, I still think we have to replace the > > placeholders created when temporary materialization takes place in an > > NSDMI, but it must happen in digest_nsdmi_init (as Patrick suggested), > > where we can see the full initializer. That way I can avoid breaking > > copy elision. > > > > I've added a bunch of new testcases and updated the > > (already long) description. The yuge comment hopefully also explains > > how this problem is avoided. Thanks again, > > > > Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk? > > > > -- >8 -- > > Consider > > > > struct A { > > int x; > > int y = x; > > }; > > > > struct B { > > int x = 0; > > int y = A{x}.y; // #1 > > }; > > > > where for #1 we end up with > > > > {.x=(&)->x, .y=(&)->x} > > > > that is, two PLACEHOLDER_EXPRs for different types on the same level in > > a {}. This crashes because our CONSTRUCTOR_PLACEHOLDER_BOUNDARY mechanism to > > avoid replacing unrelated PLACEHOLDER_EXPRs cannot deal with it. > > > > Here's why we wound up with those PLACEHOLDER_EXPRs: When we're performing > > cp_parser_late_parsing_nsdmi for "int y = A{x}.y;" we use finish_compound_literal > > on type=A, compound_literal={((struct B *) this)->x}. When digesting this > > initializer, we call get_nsdmi which creates a PLACEHOLDER_EXPR for A -- we don't > > have any object to refer to yet. After digesting, we have > > > > {.x=((struct B *) this)->x, .y=(&)->x} > > > > and since we've created a PLACEHOLDER_EXPR inside it, we marked the whole ctor > > CONSTRUCTOR_PLACEHOLDER_BOUNDARY. f_c_l creates a TARGET_EXPR and returns > > > > TARGET_EXPR x, .y=(&)->x}> > > > > Then we get to > > > > B b = {}; > > > > and call store_init_value, which digests the {}, which produces > > > > {.x=NON_LVALUE_EXPR <0>, .y=(TARGET_EXPR )->x, .y=(&)->x}>).y} > > > > lookup_placeholder in constexpr won't find an object to replace the > > PLACEHOLDER_EXPR for B, because ctx->object will be D.2395 of type A, and we > > cannot search outward from D.2395 to find 'b'. > > > > The call to replace_placeholders in store_init_value will not do anything: > > we've marked the inner { } CONSTRUCTOR_PLACEHOLDER_BOUNDARY, and it's only > > a sub-expression, so replace_placeholders does nothing, so the > > stays even though now is the perfect time to replace it because we have an > > object for it: 'b'. > > > > Later, in cp_gimplify_init_expr the *expr_p is > > > > D.2395 = {.x=(&)->x, .y=(&)->x} > > > > where D.2395 is of type A, but we crash because we hit , which > > has a different type. > > > > My idea was to replace with D.2384 after creating the > > TARGET_EXPR because that means we have an object we can refer to. > > Then clear CONSTRUCTOR_PLACEHOLDER_BOUNDARY because we no longer have > > a PLACEHOLDER_EXPR in the {}. Then store_init_value will be able to > > replace with 'b', and we should be good to go. We must > > be careful not to break guaranteed copy elision, so this replacement > > happens in digest_nsdmi_init where we can see the whole initializer, > > and avoid replacing any placeholders in the outermost TARGET_EXPR. > > > > PR c++/100252 > > > > gcc/cp/ChangeLog: > > > > * typeck2.cc (replace_placeholders_for_class_temp_r): New. > > (digest_nsdmi_init): Call it. > > > > gcc/testsuite/ChangeLog: > > > > * g++.dg/cpp1y/nsdmi-aggr14.C: New test. > > * g++.dg/cpp1y/nsdmi-aggr15.C: New test. > > * g++.dg/cpp1y/nsdmi-aggr16.C: New test. > > * g++.dg/cpp1y/nsdmi-aggr17.C: New test. > > * g++.dg/cpp1y/nsdmi-aggr18.C: New test. > > --- > > gcc/cp/typeck2.cc | 54 +++++++++++++++++++++++ > > gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr14.C | 46 +++++++++++++++++++ > > gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr15.C | 29 ++++++++++++ > > gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr16.C | 19 ++++++++ > > gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr17.C | 47 ++++++++++++++++++++ > > gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr18.C | 25 +++++++++++ > > 6 files changed, 220 insertions(+) > > create mode 100644 gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr14.C > > create mode 100644 gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr15.C > > create mode 100644 gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr16.C > > create mode 100644 gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr17.C > > create mode 100644 gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr18.C > > > > diff --git a/gcc/cp/typeck2.cc b/gcc/cp/typeck2.cc > > index 63d95c1529a..72d7cfaf1d3 100644 > > --- a/gcc/cp/typeck2.cc > > +++ b/gcc/cp/typeck2.cc > > @@ -1371,6 +1371,34 @@ digest_init_flags (tree type, tree init, int flags, tsubst_flags_t complain) > > return digest_init_r (type, init, 0, flags, complain); > > } > > +/* Callback to replace PLACEHOLDER_EXPRs in a TARGET_EXPR (which isn't used > > + in the context of guaranteed copy elision). */ > > + > > +static tree > > +replace_placeholders_for_class_temp_r (tree *tp, int *walk_subtrees, void *data) > > +{ > > + tree t = *tp; > > + tree full_expr = *static_cast(data); > > + > > + /* We're looking for a TARGET_EXPR nested in the whole expression. */ > > + if (TREE_CODE (t) == TARGET_EXPR && t != full_expr) > > Just comparing to full_expr doesn't seem enough to catch all the cases where > a prvalue is used as an initializer rather than for temporary > materialization; you will also need to handle comma and conditional > expressions. And probably PAREN_EXPR. > > I'm failing to find a term for this in the standard; "potential result" also > includes objects that the result is an xvalue subobject of (as in #1 below). > Maybe "potential prvalue result", to exclude the xvalue cases? So, > > && !potential_prvalue_result_of (t, full_expr) > > ? Thanks, the previous version indeed did have problems when ?: or , was used. I've greatly enhanced the tests to detect more cases, and added potential_prvalue_result_of. While looking into this, I found PR105550, which turned out to be a big can of worms. So, XFAIL for now. Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk? -- >8 -- Consider struct A { int x; int y = x; }; struct B { int x = 0; int y = A{x}.y; // #1 }; where for #1 we end up with {.x=(&)->x, .y=(&)->x} that is, two PLACEHOLDER_EXPRs for different types on the same level in a {}. This crashes because our CONSTRUCTOR_PLACEHOLDER_BOUNDARY mechanism to avoid replacing unrelated PLACEHOLDER_EXPRs cannot deal with it. Here's why we wound up with those PLACEHOLDER_EXPRs: When we're performing cp_parser_late_parsing_nsdmi for "int y = A{x}.y;" we use finish_compound_literal on type=A, compound_literal={((struct B *) this)->x}. When digesting this initializer, we call get_nsdmi which creates a PLACEHOLDER_EXPR for A -- we don't have any object to refer to yet. After digesting, we have {.x=((struct B *) this)->x, .y=(&)->x} and since we've created a PLACEHOLDER_EXPR inside it, we marked the whole ctor CONSTRUCTOR_PLACEHOLDER_BOUNDARY. f_c_l creates a TARGET_EXPR and returns TARGET_EXPR x, .y=(&)->x}> Then we get to B b = {}; and call store_init_value, which digests the {}, which produces {.x=NON_LVALUE_EXPR <0>, .y=(TARGET_EXPR )->x, .y=(&)->x}>).y} lookup_placeholder in constexpr won't find an object to replace the PLACEHOLDER_EXPR for B, because ctx->object will be D.2395 of type A, and we cannot search outward from D.2395 to find 'b'. The call to replace_placeholders in store_init_value will not do anything: we've marked the inner { } CONSTRUCTOR_PLACEHOLDER_BOUNDARY, and it's only a sub-expression, so replace_placeholders does nothing, so the stays even though now is the perfect time to replace it because we have an object for it: 'b'. Later, in cp_gimplify_init_expr the *expr_p is D.2395 = {.x=(&)->x, .y=(&)->x} where D.2395 is of type A, but we crash because we hit , which has a different type. My idea was to replace with D.2384 after creating the TARGET_EXPR because that means we have an object we can refer to. Then clear CONSTRUCTOR_PLACEHOLDER_BOUNDARY because we no longer have a PLACEHOLDER_EXPR in the {}. Then store_init_value will be able to replace with 'b', and we should be good to go. We must be careful not to break guaranteed copy elision, so this replacement happens in digest_nsdmi_init where we can see the whole initializer, and avoid replacing any placeholders in TARGET_EXPRs used in the context of initialization/copy elision. This is achieved via the new function called potential_prvalue_result_of. While fixing this problem, I found PR105550, thus the FIXMEs in the tests. PR c++/100252 gcc/cp/ChangeLog: * typeck2.cc (potential_prvalue_result_of): New. (replace_placeholders_for_class_temp_r): New. (digest_nsdmi_init): Call it. gcc/testsuite/ChangeLog: * g++.dg/cpp1y/nsdmi-aggr14.C: New test. * g++.dg/cpp1y/nsdmi-aggr15.C: New test. * g++.dg/cpp1y/nsdmi-aggr16.C: New test. * g++.dg/cpp1y/nsdmi-aggr17.C: New test. * g++.dg/cpp1y/nsdmi-aggr18.C: New test. * g++.dg/cpp1y/nsdmi-aggr19.C: New test. --- gcc/cp/typeck2.cc | 90 ++++++++++++++ gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr14.C | 131 ++++++++++++++++++++ gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr15.C | 80 +++++++++++++ gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr16.C | 58 +++++++++ gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr17.C | 138 ++++++++++++++++++++++ gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr18.C | 56 +++++++++ gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr19.C | 28 +++++ 7 files changed, 581 insertions(+) create mode 100644 gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr14.C create mode 100644 gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr15.C create mode 100644 gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr16.C create mode 100644 gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr17.C create mode 100644 gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr18.C create mode 100644 gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr19.C base-commit: dd7813f05df50d2ad8e0dc34503f2dff0b521d89 diff --git a/gcc/cp/typeck2.cc b/gcc/cp/typeck2.cc index 1d92310edd0..f60e1df4c27 100644 --- a/gcc/cp/typeck2.cc +++ b/gcc/cp/typeck2.cc @@ -1371,6 +1371,70 @@ digest_init_flags (tree type, tree init, int flags, tsubst_flags_t complain) return digest_init_r (type, init, 0, flags, complain); } +/* Return true if a prvalue is used as an initializer rather than for + temporary materialization. For instance: + + A a = A{}; // initializer + A a = (A{}); // initializer + A a = (1, A{}); // initializer + A a = true ? A{} : A{}; // initializer + auto x = A{}.x; // temporary materialization + auto x = foo(A{}); // temporary materialization + + FULL_EXPR is the whole expression, SUBOB is its TARGET_EXPR subobject. */ + +static bool +potential_prvalue_result_of (tree subob, tree full_expr) +{ + if (subob == full_expr) + return true; + else if (TREE_CODE (full_expr) == TARGET_EXPR) + { + tree init = TARGET_EXPR_INITIAL (full_expr); + if (TREE_CODE (init) == COND_EXPR) + return (potential_prvalue_result_of (subob, TREE_OPERAND (init, 1)) + || potential_prvalue_result_of (subob, TREE_OPERAND (init, 2))); + else if (TREE_CODE (init) == COMPOUND_EXPR) + return (potential_prvalue_result_of (subob, TREE_OPERAND (init, 0)) + || potential_prvalue_result_of (subob, TREE_OPERAND (init, 1))); + /* ??? I don't know if this can be hit. If so, look inside the ( ) + instead of the assert. */ + else if (TREE_CODE (init) == PAREN_EXPR) + gcc_checking_assert (false); + } + return false; +} + +/* Callback to replace PLACEHOLDER_EXPRs in a TARGET_EXPR (which isn't used + in the context of guaranteed copy elision). */ + +static tree +replace_placeholders_for_class_temp_r (tree *tp, int *, void *data) +{ + tree t = *tp; + tree full_expr = *static_cast(data); + + /* We're looking for a TARGET_EXPR nested in the whole expression. */ + if (TREE_CODE (t) == TARGET_EXPR + && !potential_prvalue_result_of (t, full_expr)) + { + tree init = TARGET_EXPR_INITIAL (t); + while (TREE_CODE (init) == COMPOUND_EXPR) + init = TREE_OPERAND (init, 1); + if (TREE_CODE (init) == CONSTRUCTOR + && CONSTRUCTOR_PLACEHOLDER_BOUNDARY (init)) + { + tree obj = TARGET_EXPR_SLOT (t); + replace_placeholders (init, obj); + /* We should have dealt with all PLACEHOLDER_EXPRs. */ + CONSTRUCTOR_PLACEHOLDER_BOUNDARY (init) = false; + gcc_checking_assert (!find_placeholders (init)); + } + } + + return NULL_TREE; +} + /* Process the initializer INIT for an NSDMI DECL (a FIELD_DECL). */ tree digest_nsdmi_init (tree decl, tree init, tsubst_flags_t complain) @@ -1390,6 +1454,32 @@ digest_nsdmi_init (tree decl, tree init, tsubst_flags_t complain) && CP_AGGREGATE_TYPE_P (type)) init = reshape_init (type, init, complain); init = digest_init_flags (type, init, flags, complain); + + /* We may have temporary materialization in a NSDMI, if the initializer + has something like A{} in it. Digesting the {} could have introduced + a PLACEHOLDER_EXPR referring to A. Now that we've got a TARGET_EXPR, + we have an object we can refer to. The reason we bother doing this + here is for code like + + struct A { + int x; + int y = x; + }; + + struct B { + int x = 0; + int y = A{x}.y; // #1 + }; + + where in #1 we don't want to end up with two PLACEHOLDER_EXPRs for + different types on the same level in a {} when lookup_placeholder + wouldn't find a named object for the PLACEHOLDER_EXPR for A. Note, + temporary materialization does not occur when initializing an object + from a prvalue of the same type, therefore we must not replace the + placeholder with a temporary object so that it can be elided. */ + cp_walk_tree (&init, replace_placeholders_for_class_temp_r, &init, + nullptr); + return init; } diff --git a/gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr14.C b/gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr14.C new file mode 100644 index 00000000000..28b908a0a1a --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr14.C @@ -0,0 +1,131 @@ +// PR c++/100252 +// { dg-do run { target c++14 } } + +#define SA(X) static_assert ((X),#X) + +struct A { + int x; + int y = x; +}; + +struct B { + int x = 0; + int y = A{x}.y; +}; + +constexpr B csb1 = { }; +SA(csb1.x == 0 && csb1.y == csb1.x); +constexpr B csb2 = { 1 }; +SA(csb2.x == 1 && csb2.y == csb2.x); +constexpr B csb3 = { 1, 2 }; +SA(csb3.x == 1 && csb3.y == 2); + +B sb1 = { }; +B sb2 = { 1 }; +B sb3 = { 1, 2}; + +struct C { + int x = 0; + int y = (true, A{x}.y) + (A{x}.y, 0); +}; + +constexpr C csc1 = { }; +SA(csc1.x == 0 && csc1.y == csc1.x); +constexpr C csc2 = { 1 }; +SA(csc2.x == 1 && csc2.y == csc2.x); +constexpr C csc3 = { 1, 2 }; +SA(csc3.x == 1 && csc3.y == 2); + +C sc1 = { }; +C sc2 = { 1 }; +C sc3 = { 1, 2}; + +struct D { + int x = 0; + int y = (A{x}.y); +}; + +constexpr D csd1 = { }; +SA(csd1.x == 0 && csd1.y == csd1.x); +constexpr D csd2 = { 1 }; +SA(csd2.x == 1 && csd2.y == csd2.x); +constexpr D csd3 = { 1, 2 }; +SA(csd3.x == 1 && csd3.y == 2); + +D sd1 = { }; +D sd2 = { 1 }; +D sd3 = { 1, 2}; + +struct E { + int x = 0; + int y = x ? A{x}.y : A{x}.y; +}; + +constexpr E cse1 = { }; +SA(cse1.x == 0 && cse1.y == cse1.x); +constexpr E cse2 = { 1 }; +SA(cse2.x == 1 && cse2.y == cse2.x); +constexpr E cse3 = { 1, 2 }; +SA(cse3.x == 1 && cse3.y == 2); + +E se1 = { }; +E se2 = { 1 }; +E se3 = { 1, 2}; + +int +main () +{ + if (sb1.x != 0 || sb1.x != sb1.y) + __builtin_abort(); + if (sb2.x != 1 || sb2.x != sb2.y) + __builtin_abort(); + if (sb3.x != 1 || sb3.y != 2) + __builtin_abort(); + + if (sc1.x != 0 || sc1.x != sc1.y) + __builtin_abort(); + if (sc2.x != 1 || sc2.x != sc2.y) + __builtin_abort(); + if (sc3.x != 1 || sc3.y != 2) + __builtin_abort(); + + B b1 = { }; + B b2 = { 1 }; + B b3 = { 1, 2}; + if (b1.x != 0 || b1.x != b1.y) + __builtin_abort(); + if (b2.x != 1 || b2.x != b2.y) + __builtin_abort(); + if (b3.x != 1 || b3.y != 2) + __builtin_abort(); + + C c1 = { }; + C c2 = { 1 }; + C c3 = { 1, 2}; + if (c1.x != 0 || c1.x != c1.y) + __builtin_abort(); + if (c2.x != 1 || c2.x != c2.y) + __builtin_abort(); + if (c3.x != 1 || c3.y != 2) + __builtin_abort(); + + D d1 = { }; + D d2 = { 1 }; + D d3 = { 1, 2}; + if (d1.x != 0 || d1.x != d1.y) + __builtin_abort(); + if (d2.x != 1 || d2.x != d2.y) + __builtin_abort(); + if (d3.x != 1 || d3.y != 2) + __builtin_abort(); + + E e1 = { }; + E e2 = { 1 }; + E e3 = { 1, 2}; + if (e1.x != 0 || e1.x != e1.y) + __builtin_abort(); + if (e2.x != 1 || e2.x != e2.y) + __builtin_abort(); + if (e3.x != 1 || e3.y != 2) + __builtin_abort(); +} diff --git a/gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr15.C b/gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr15.C new file mode 100644 index 00000000000..d091d693042 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr15.C @@ -0,0 +1,80 @@ +// PR c++/100252 +// { dg-do run { target c++14 } } + +struct A { + int x; + int y = x; +}; + +struct B { + int x = 0; + int y = A{x}.y; +}; + +static void +test_b (B b1 = B{}, B b2 = B{1}, B b3 = B{1, 2}) +{ + if (b1.x != 0 || b1.y != b1.x) + __builtin_abort(); + if (b2.x != 1 || b2.y != b2.x) + __builtin_abort(); + if (b3.x != 1 || b3.y != 2) + __builtin_abort(); +} + +struct C { + int x = 0; + int y = (true, A{x}.y) + (A{x}.y, 0); +}; + +static void +test_c (C c1 = C{}, C c2 = C{1}, C c3 = C{1, 2}) +{ + if (c1.x != 0 || c1.y != c1.x) + __builtin_abort(); + if (c2.x != 1 || c2.y != c2.x) + __builtin_abort(); + if (c3.x != 1 || c3.y != 2) + __builtin_abort(); +} + +struct D { + int x = 0; + int y = (A{x}.y); +}; + +static void +test_d (D d1 = D{}, D d2 = D{1}, D d3 = D{1, 2}) +{ + if (d1.x != 0 || d1.y != d1.x) + __builtin_abort(); + if (d2.x != 1 || d2.y != d2.x) + __builtin_abort(); + if (d3.x != 1 || d3.y != 2) + __builtin_abort(); +} + +struct E { + int x = 0; + int y = x ? A{x}.y : A{x}.y; +}; + +static void +test_e (E e1 = E{}, E e2 = E{1}, E e3 = E{1, 2}) +{ + if (e1.x != 0 || e1.y != e1.x) + __builtin_abort(); + if (e2.x != 1 || e2.y != e2.x) + __builtin_abort(); + if (e3.x != 1 || e3.y != 2) + __builtin_abort(); +} + +int +main () +{ + test_b (); + test_c (); + test_d (); + test_e (); +} diff --git a/gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr16.C b/gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr16.C new file mode 100644 index 00000000000..dc6492c1b0b --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr16.C @@ -0,0 +1,58 @@ +// PR c++/100252 +// { dg-do compile { target c++14 } } + +#define SA(X) static_assert ((X),#X) + +struct A { + const A* p = this; +}; + +struct B { + A a = A{}; +}; + +constexpr B b; +SA(b.a.p == &b.a); +B b1 = { }; + +struct C { + A a = (true, A{}); +}; + +constexpr C c; +SA(c.a.p == &c.a); +C c1 = { }; + +struct D { + A a = (A{}); +}; + +constexpr D d; +SA(d.a.p == &d.a); +D d1 = { }; + +static constexpr A global_a; + +struct E { + A a = true ? A{} : A{}; + A b = true ? global_a : (false ? A{} : A{}); + A c = true ? (false ? A{} : A{}) : global_a; + A d = true ? (false ? A{} : A{}) : (false ? A{} : A{}); +}; + +// FIXME: When fixing this, also fix nsdmi-aggr17.C. +constexpr E e; // { dg-bogus "" "PR105550" { xfail *-*-* } } +SA (e.a.p == &e.a); // { dg-bogus "" "PR105550" { xfail *-*-* } } + +E e1 = { }; + +struct F { + bool b = (A{}, true); +}; + +constexpr F f; + +void +g (B b2 = B{}, C c2 = C{}, D d2 = D{}, E e2 = E{}) +{ +} diff --git a/gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr17.C b/gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr17.C new file mode 100644 index 00000000000..fc27a2cdac7 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr17.C @@ -0,0 +1,138 @@ +// PR c++/100252 +// { dg-do run { target c++14 } } + +#define SA(X) static_assert ((X),#X) + +struct A { + int x; + int y = x; + const A* p = this; +}; + +struct B { + int x = 42; + A a = A{x}; +}; + +constexpr B b; +SA(b.a.p == &b.a); +SA(b.x == 42); +B b2 = { }; +B b3 = { 42 }; + +struct C { + int x = 42; + B b = B{x}; +}; + +constexpr C c; +C c2; +C c3; + +struct D { + int x = 42; + A a = (true, A{x}); +}; + +constexpr D d; +SA(d.a.p == &d.a); +SA(d.x == 42); +D d2 = { }; +D d3 = { 42 }; + +struct E { + int x = 42; + A a = (A{x}); +}; + +constexpr E e; +SA(e.a.p == &e.a); +SA(e.x == 42); +E e2 = { }; +E e3 = { 42 }; + +struct F { + int x = 42; + A a = true ? A{x} : A{x}; +}; + +// FIXME: Doesn't work due to PR105550. +//constexpr F f; +//SA (f.a.p == &f.a); +SA (e.x == 42); +F f2 = { }; +F f3 = { 42 }; + +static void +test_b (B b4 = B{}, B b5 = B{ 42 }) +{ + if (b2.x != 42 || b2.a.x != 42 || b2.a.y != b2.a.x) + __builtin_abort (); + if (b3.x != 42 || b3.a.x != 42 || b3.a.y != b3.a.x) + __builtin_abort (); + if (b4.x != 42 || b4.a.x != 42 || b4.a.y != b4.a.x) + __builtin_abort (); + if (b5.x != 42 || b5.a.x != 42 || b5.a.y != b5.a.x) + __builtin_abort (); +} + +static void +test_c (C c4 = C{}, C c5 = C{ 42 }) +{ + if (c2.b.x != 42 || c2.b.a.x != 42 || c2.b.a.y != c2.b.a.x) + __builtin_abort (); + if (c3.b.x != 42 || c3.b.a.x != 42 || c3.b.a.y != c3.b.a.x) + __builtin_abort (); + if (c4.b.x != 42 || c4.b.a.x != 42 || c4.b.a.y != c4.b.a.x) + __builtin_abort (); + if (c5.b.x != 42 || c5.b.a.x != 42 || c5.b.a.y != c5.b.a.x) + __builtin_abort (); +} + +static void +test_d (D d4 = D{}, D d5 = D{ 42 }) +{ + if (d2.x != 42 || d2.a.x != 42 || d2.a.y != d2.a.x) + __builtin_abort (); + if (d3.x != 42 || d3.a.x != 42 || d3.a.y != d3.a.x) + __builtin_abort (); + if (d4.x != 42 || d4.a.x != 42 || d4.a.y != d4.a.x) + __builtin_abort (); + if (d5.x != 42 || d5.a.x != 42 || d5.a.y != d5.a.x) + __builtin_abort (); +} + +static void +test_e (E e4 = E{}, E e5 = E{ 42 }) +{ + if (e2.x != 42 || e2.a.x != 42 || e2.a.y != e2.a.x) + __builtin_abort (); + if (e3.x != 42 || e3.a.x != 42 || e3.a.y != e3.a.x) + __builtin_abort (); + if (e4.x != 42 || e4.a.x != 42 || e4.a.y != e4.a.x) + __builtin_abort (); + if (e5.x != 42 || e5.a.x != 42 || e5.a.y != e5.a.x) + __builtin_abort (); +} + +static void +test_f (F f4 = F{}, F f5 = F{ 42 }) +{ + if (f2.x != 42 || f2.a.x != 42 || f2.a.y != f2.a.x) + __builtin_abort (); + if (f3.x != 42 || f3.a.x != 42 || f3.a.y != f3.a.x) + __builtin_abort (); + if (f4.x != 42 || f4.a.x != 42 || f4.a.y != f4.a.x) + __builtin_abort (); + if (f5.x != 42 || f5.a.x != 42 || f5.a.y != f5.a.x) + __builtin_abort (); +} +int +main () +{ + test_b (); + test_c (); + test_d (); + test_e (); + test_f (); +} diff --git a/gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr18.C b/gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr18.C new file mode 100644 index 00000000000..567b8ee96d9 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr18.C @@ -0,0 +1,56 @@ +// PR c++/100252 +// { dg-do compile { target c++14 } } + +struct B { }; + +struct A { + int x; + int y = x; + constexpr operator B() { return B{}; } +}; + +struct C { + int x = 42; + B b = A{x}; +}; + +C c1 = {}; +C c2 = { 42 }; +constexpr C c3 = {}; +constexpr C c4 = { 42 }; + +struct D { + int x = 42; + B b = (true, A{x}); +}; + +D d1 = {}; +D d2 = { 42 }; +constexpr D d3 = {}; +constexpr D d4 = { 42 }; + +struct E { + int x = 42; + B b = (A{x}); +}; + +E e1 = {}; +E e2 = { 42 }; +constexpr E e3 = {}; +constexpr E e4 = { 42 }; + +struct F { + int x = 42; + B b = (A{x}); +}; + +F f1 = {}; +F f2 = { 42 }; +constexpr F f3 = {}; +constexpr F f4 = { 42 }; + +void +g (C c5 = C{}, C c6 = C{ 42 }, D d5 = D{}, D d6 = D{ 42 }, + E e5 = E{}, E e6 = E{ 42 }, F f5 = F{}, F f6 = F{ 42 }) +{ +} diff --git a/gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr19.C b/gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr19.C new file mode 100644 index 00000000000..f4892e3379b --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr19.C @@ -0,0 +1,28 @@ +// PR c++/100252 +// { dg-do compile { target c++14 } } + +#define SA(X) static_assert ((X),#X) + +struct A { + const A* p = this; +}; + +struct B { + A a = (A{}, A{}); +}; + +constexpr B b; +SA(b.a.p == &b.a); + +struct C { + int x; + int y = x; +}; + +struct D { + int x = 0; + int y = (C{x}.y, C{x}.y); +}; + +constexpr D d = { }; +D d2 = {};