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 9F9773857359 for ; Wed, 18 May 2022 14:22:54 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 9F9773857359 Received: from mail-qk1-f199.google.com (mail-qk1-f199.google.com [209.85.222.199]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-558-DvGx6FSWO0iC69O1sxE4Dg-1; Wed, 18 May 2022 10:22:52 -0400 X-MC-Unique: DvGx6FSWO0iC69O1sxE4Dg-1 Received: by mail-qk1-f199.google.com with SMTP id o18-20020a05620a2a1200b006a0cc3d8463so1691379qkp.4 for ; Wed, 18 May 2022 07:22:51 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:message-id:date:mime-version:user-agent:subject :content-language:to:cc:references:from:in-reply-to :content-transfer-encoding; bh=l3BlX5aPt1zvhpJNUz7jGESSKjDK08Y69qJKzmBpjLM=; b=F4W2fE8n3jO8NKzwmA23rnwqWjPt7XroN3sNfyFKXtLXE4jFDCA4YWrdZ5DCzUi3Ah 6Bk1oNYarKpFRVDiiHYwWpSzpiuacvVyfTpNUd01Dy4KachVfNhN4UZ0ytyOMtAraCj9 sv2xwK/UgNYKLuE0RX5jla+5epDRpgm+TOCEaZf7AK7ZfmXjYPLUZvj8HgPvQq0pSy7g 4NGsAImCUi3mm+ptdGPfGKOuJhXwXxekGVCutaJ2fWqGNDNQR+Yw4CDDXqCr1Cq68slD UtiX3A7kHbt/mem1HRu60EaIC3ChHx343GubTBZ8mpG9FEHWslcvcVpBaBTlUCrXqmNd isvw== X-Gm-Message-State: AOAM533XyGpI1vF77iRarcFdLIk9l0ilY15wWeXgptwV8NhKWrwI1GEN E0qd5fP3YrZ12DAcg7n2AdWn7Ikq9LZBJ+0TUchS+6XK0HQLY7g69OMHn9vY9H9h4jPsAm7kq0k MbByXappFCu1l1mFK7Q== X-Received: by 2002:a05:620a:d87:b0:67b:311c:ecbd with SMTP id q7-20020a05620a0d8700b0067b311cecbdmr20185608qkl.146.1652883770764; Wed, 18 May 2022 07:22:50 -0700 (PDT) X-Google-Smtp-Source: ABdhPJyK2yrAtjR8RfJlIIlg1zPEh1jm10CTTrw13yFsfMuF8GgPGjkmXLB6VjFZ96yeUloMLCt9Cw== X-Received: by 2002:a05:620a:d87:b0:67b:311c:ecbd with SMTP id q7-20020a05620a0d8700b0067b311cecbdmr20185555qkl.146.1652883770013; Wed, 18 May 2022 07:22:50 -0700 (PDT) Received: from [192.168.1.100] (130-44-159-43.s15913.c3-0.arl-cbr1.sbo-arl.ma.cable.rcncustomer.com. [130.44.159.43]) by smtp.gmail.com with ESMTPSA id 6-20020a370606000000b006a007f40477sm1583708qkg.118.2022.05.18.07.22.48 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Wed, 18 May 2022 07:22:49 -0700 (PDT) Message-ID: <89968899-201b-9dc6-5bcf-eca32b9d47ae@redhat.com> Date: Wed, 18 May 2022 10:22:48 -0400 MIME-Version: 1.0 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101 Thunderbird/91.9.0 Subject: Re: [PATCH] c++: constexpr init of union sub-aggr w/ base [PR105491] To: Patrick Palka Cc: GCC Patches References: <20220506152232.2843007-1-ppalka@redhat.com> <659fd0c4-5fa7-147d-9b97-bebf06ae7c23@redhat.com> <7ab4b646-bade-97b0-8083-c392cf37ea49@idea> <48baf04b-9cb9-8668-cbb6-047eba1033d7@idea> <660ef433-cb10-e137-e734-5fbf3223a39f@idea> From: Jason Merrill In-Reply-To: X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Language: en-US Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 8bit X-Spam-Status: No, score=-13.8 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, NICE_REPLY_A, 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-List-Received-Date: Wed, 18 May 2022 14:22:58 -0000 On 5/17/22 12:34, Patrick Palka wrote: > On Sat, May 7, 2022 at 5:18 PM Jason Merrill wrote: >> >> On 5/6/22 16:46, Patrick Palka wrote: >>> On Fri, 6 May 2022, Jason Merrill wrote: >>> >>>> On 5/6/22 16:10, Patrick Palka wrote: >>>>> On Fri, 6 May 2022, Patrick Palka wrote: >>>>> >>>>>> On Fri, 6 May 2022, Jason Merrill wrote: >>>>>> >>>>>>> On 5/6/22 14:00, Patrick Palka wrote: >>>>>>>> On Fri, 6 May 2022, Patrick Palka wrote: >>>>>>>> >>>>>>>>> On Fri, 6 May 2022, Jason Merrill wrote: >>>>>>>>> >>>>>>>>>> On 5/6/22 11:22, Patrick Palka wrote: >>>>>>>>>>> Here ever since r10-7313-gb599bf9d6d1e18, >>>>>>>>>>> reduced_constant_expression_p >>>>>>>>>>> in C++11/14 is rejecting the marked sub-aggregate initializer >>>>>>>>>>> (of type >>>>>>>>>>> S) >>>>>>>>>>> >>>>>>>>>>> W w = {.D.2445={.s={.D.2387={.m=0}, .b=0}}} >>>>>>>>>>> ^ >>>>>>>>>>> >>>>>>>>>>> ultimately because said initializer has CONSTRUCTOR_NO_CLEARING >>>>>>>>>>> set, >>>>>>>>>>> and >>>>>>>>>>> so the function proceeds to verify that all fields of S are >>>>>>>>>>> initialized. >>>>>>>>>>> And before C++17 we don't expect to see base class fields (since >>>>>>>>>>> next_initializable_field skips over the), so the base class >>>>>>>>>>> initializer >>>>>>>>>>> causes r_c_e_p to return false. >>>>>>>>>> >>>>>>>>>> That seems like the primary bug. I guess r_c_e_p shouldn't be >>>>>>>>>> using >>>>>>>>>> next_initializable_field. Really that function should only be >>>>>>>>>> used for >>>>>>>>>> aggregates. >>>>>>>>> >>>>>>>>> I see, I'll try replacing it in r_c_e_p. Would that be in addition >>>>>>>>> to >>>>>>>>> or instead of the clear_no_implicit_zero approach? >>>>>>>> >>>>>>>> I'm testing the following, which uses a custom predicate instead of >>>>>>>> next_initializable_field in r_c_e_p. >>>>>>> >>>>>>> Let's make it a public predicate, not internal to r_c_e_p. Maybe it >>>>>>> could be >>>>>>> next_subobject_field, and the current next_initializable_field change to >>>>>>> next_aggregate_field? >>>>>> >>>>>> Will do. >>>>>> >>>>>>> >>>>>>>> Looks like the inner initializer {.D.2387={.m=0}, .b=0} is formed >>>>>>>> during >>>>>>>> the subobject constructor call: >>>>>>>> >>>>>>>> V::V (&((struct S *) this)->D.2120); >>>>>>>> >>>>>>>> after the evaluation of which, 'result' in cxx_eval_call_expression is >>>>>>>> NULL >>>>>>>> (presumably because it's a CALL_EXPR, not AGGR_INIT_EXPR?): >>>>>>>> >>>>>>>> /* This can be null for a subobject constructor call, in >>>>>>>> which case what we care about is the initialization >>>>>>>> side-effects rather than the value. We could get at the >>>>>>>> value by evaluating *this, but we don't bother; there's >>>>>>>> no need to put such a call in the hash table. */ >>>>>>>> result = lval ? ctx->object : ctx->ctor; >>>>>>>> >>>>>>>> so we end up not calling clear_no_implicit_zero for the inner >>>>>>>> initializer >>>>>>>> directly. We only call clear_no_implicit_zero after evaluating the >>>>>>>> AGGR_INIT_EXPR for outermost initializer (of type W). >>>>>>> >>>>>>> Maybe for constructors we could call it on ctx->ctor instead of result, >>>>>>> or >>>>>>> call r_c_e_p in C++20+? >>>>>> >>>>>> But both ctx->ctor and ->object are NULL during a subobject constructor >>>>>> call (since we apparently clear these fields when entering a >>>>>> STATEMENT_LIST): >>>>>> >>>>>> So I tried instead obtaining the constructor by evaluating new_obj via >>>>>> >>>>>> --- a/gcc/cp/constexpr.cc >>>>>> +++ b/gcc/cp/constexpr.cc >>>>>> @@ -2993,6 +2988,9 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, >>>>>> tree t, >>>>>> in order to detect reading an unitialized object in constexpr >>>>>> instead >>>>>> of value-initializing it. (reduced_constant_expression_p is >>>>>> expected to >>>>>> take care of clearing the flag.) */ >>>>>> + if (new_obj && DECL_CONSTRUCTOR_P (fun)) >>>>>> + result = cxx_eval_constant_expression (ctx, new_obj, /*lval=*/false, >>>>>> + non_constant_p, overflow_p); >>>>>> if (TREE_CODE (result) == CONSTRUCTOR >>>>>> && (cxx_dialect < cxx20 >>>>>> || !DECL_CONSTRUCTOR_P (fun))) >>>>>> >>>>>> but that seems to break e.g. g++.dg/cpp2a/constexpr-init12.C because >>>>>> after the subobject constructor call >>>>>> >>>>>> S::S (&((struct W *) this)->s, NON_LVALUE_EXPR <8>); >>>>>> >>>>>> the constructor for the subobject a.s in new_obj is still completely >>>>>> missing (I suppose because S::S doesn't initialize any of its members) >>>>>> so trying to obtain it causes us to complain too soon from >>>>>> cxx_eval_component_reference: >>>>>> >>>>>> constexpr-init12.C:16:24: in ‘constexpr’ expansion of ‘W(42)’ >>>>>> constexpr-init12.C:10:22: in ‘constexpr’ expansion of >>>>>> ‘((W*)this)->W::s.S::S(8)’ >>>>>> constexpr-init12.C:16:24: error: accessing uninitialized member ‘W::s’ >>>>>> 16 | constexpr auto a = W(42); // { dg-error "not a constant >>>>>> expression" } >>>>>> | ^ >>>>>> >>>>>>> >>>>>>> It does seem dubious that we would clear the flag on an outer ctor when >>>>>>> it's >>>>>>> still set on an inner ctor, should probably add an assert somewhere. >>>>>> >>>>>> Makes sense, not sure where the best place would be.. >>>>> >>>>> On second thought, if I'm understanding your suggestion correctly, I >>>>> don't think we can generally enforce such a property for >>>>> CONSTRUCTOR_NO_CLEARING, given how cxx_eval_store_expression uses it for >>>>> unions: >>>>> >>>>> union U { >>>>> struct { int x, y; } a; >>>>> } u; >>>>> u.a.x = 0; >>>>> >>>>> Here after evaluating the assignment, the outer ctor for the union will >>>>> have CONSTRUCTOR_NO_CLEARING cleared to indicate we finished activating >>>>> the union member, but the inner ctor is certainly not fully initialized >>>>> so it'll have CONSTRUCTOR_NO_CLEARING set still. >>>> >>>> Why clear the flag on the union before the inner ctor is fully initialized, if >>>> the intent is to prevent changing the active member during initialization? >>>> >>>> In the loop over ctors in cxx_eval_store_expression, I'd think if we encounter >>>> a CONSTRUCTOR_NO_CLEARING ctor along the way, we shouldn't clear the flag on >>>> an outer ctor. >>> >>> Wouldn't that prevent us from later activating a different member, which is >>> allowed in C++20? It would cause us to reject e.g. >>> >>> constexpr auto f() { >>> union U { >>> struct { int x, y; } a; >>> int b; >>> } u; >>> u.a.x = 0; >>> u.b = 0; >>> return u; >>> } >>> >>> static_assert(f().b == 0); >>> >>> even in C++20 with >>> >>> :7:7: error: change of the active member of a union from ‘f()::U::a’ to ‘f()::U::b’ during initialization >>> 7 | u.b = 0; >>> | ~~~~^~~ >>> >>> because when evaluating the second assignment we'd see that the >>> CONSTRUCTOR_NO_CLEARING flag is still set on the union while it already >>> has an activated member. >> >> Ah, makes sense. >> >>> On a related note, here's the patch that mechanically renames >>> next_initializable_field to next_aggregate_field and makes it skip over vptr >>> fields, and defines next_subobject_field alongside it for use by r_c_e_p. >>> Bootstrap and regtesting in progress. >> >> OK if successful. > > Thanks, committed as r13-211-g0c7bce0ac184c0. Would this patch be > suitable for backporting to 12? It seems safe enough. Alternatively > I guess we could backport the original workaround that tweaks > clear_no_implicit_zero. Maybe for 12 just add next_subobject_field without touching any other users of next_initializable_field? >>> -- >8 -- >>> >>> PR c++/105491 >>> >>> gcc/cp/ChangeLog: >>> >>> * call.cc (field_in_pset): Adjust after next_initializable_field >>> renaming. >>> (build_aggr_conv): Likewise. >>> (convert_like_internal): Likewise. >>> (type_has_extended_temps): Likewise. >>> * class.cc (default_init_uninitialized_part): Likewise. >>> (finish_struct): Likewise. >>> * constexpr.cc (cx_check_missing_mem_inits): Likewise. >>> (reduced_constant_expression_p): Use next_subobject_field >>> instead. >>> * cp-gimplify.cc (get_source_location_impl_type): Adjust after >>> next_initializable_field renaming. >>> (fold_builtin_source_location): Likewise. >>> * cp-tree.h (next_initializable_field): Rename to ... >>> (next_aggregate_field): ... this. >>> (next_subobject_field): Declare. >>> * decl.cc (next_aggregate_field): Renamed from ... >>> (next_initializable_field): ... this. Skip over vptr fields. >>> Document that this is now intended for aggregate classes. >>> (next_subobject_field): Define. >>> (reshape_init_class): Adjust after next_initializable_field >>> renaming. >>> * init.cc (build_value_init_noctor): Likewise. >>> (emit_mem_initializers): Likewise. >>> * lambda.cc (build_capture_proxy): Likewise. >>> * method.cc (build_comparison_op): Likewise. >>> * pt.cc (maybe_aggr_guide): Likewise. >>> * tree.cc (structural_type_p): Likewise. >>> * typeck2.cc (split_nonconstant_init_1): Likewise. >>> (digest_init_r): Likewise. >>> >>> gcc/testsuite/ChangeLog: >>> >>> * gcc/testsuite/g++.dg/cpp0x/constexpr-union7.C: New test. >>> * gcc/testsuite/g++.dg/cpp0x/constexpr-union7a.C: New test. >>> * gcc/testsuite/g++.dg/cpp2a/constinit17.C: New test. >>> --- >>> gcc/cp/call.cc | 14 +++---- >>> gcc/cp/class.cc | 8 ++-- >>> gcc/cp/constexpr.cc | 10 ++--- >>> gcc/cp/cp-gimplify.cc | 4 +- >>> gcc/cp/cp-tree.h | 3 +- >>> gcc/cp/decl.cc | 38 +++++++++++++------ >>> gcc/cp/init.cc | 6 +-- >>> gcc/cp/lambda.cc | 4 +- >>> gcc/cp/method.cc | 8 ++-- >>> gcc/cp/pt.cc | 2 +- >>> gcc/cp/tree.cc | 4 +- >>> gcc/cp/typeck2.cc | 4 +- >>> gcc/testsuite/g++.dg/cpp0x/constexpr-union7.C | 17 +++++++++ >>> .../g++.dg/cpp0x/constexpr-union7a.C | 15 ++++++++ >>> gcc/testsuite/g++.dg/cpp2a/constinit17.C | 24 ++++++++++++ >>> 15 files changed, 117 insertions(+), 44 deletions(-) >>> create mode 100644 gcc/testsuite/g++.dg/cpp0x/constexpr-union7.C >>> create mode 100644 gcc/testsuite/g++.dg/cpp0x/constexpr-union7a.C >>> create mode 100644 gcc/testsuite/g++.dg/cpp2a/constinit17.C >>> >>> diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc >>> index 959279d6216..0240e364324 100644 >>> --- a/gcc/cp/call.cc >>> +++ b/gcc/cp/call.cc >>> @@ -948,7 +948,7 @@ field_in_pset (hash_set &pset, tree field) >>> for (field = TYPE_FIELDS (TREE_TYPE (field)); >>> field; field = DECL_CHAIN (field)) >>> { >>> - field = next_initializable_field (field); >>> + field = next_aggregate_field (field); >>> if (field == NULL_TREE) >>> break; >>> if (field_in_pset (pset, field)) >>> @@ -965,7 +965,7 @@ build_aggr_conv (tree type, tree ctor, int flags, tsubst_flags_t complain) >>> { >>> unsigned HOST_WIDE_INT i = 0; >>> conversion *c; >>> - tree field = next_initializable_field (TYPE_FIELDS (type)); >>> + tree field = next_aggregate_field (TYPE_FIELDS (type)); >>> tree empty_ctor = NULL_TREE; >>> hash_set pset; >>> >>> @@ -1011,7 +1011,7 @@ build_aggr_conv (tree type, tree ctor, int flags, tsubst_flags_t complain) >>> } >>> } >>> >>> - for (; field; field = next_initializable_field (DECL_CHAIN (field))) >>> + for (; field; field = next_aggregate_field (DECL_CHAIN (field))) >>> { >>> tree ftype = TREE_TYPE (field); >>> tree val; >>> @@ -8098,10 +8098,10 @@ convert_like_internal (conversion *convs, tree expr, tree fn, int argnum, >>> totype = complete_type_or_maybe_complain (totype, NULL_TREE, complain); >>> if (!totype) >>> return error_mark_node; >>> - tree field = next_initializable_field (TYPE_FIELDS (totype)); >>> + tree field = next_aggregate_field (TYPE_FIELDS (totype)); >>> vec *vec = NULL; >>> CONSTRUCTOR_APPEND_ELT (vec, field, array); >>> - field = next_initializable_field (DECL_CHAIN (field)); >>> + field = next_aggregate_field (DECL_CHAIN (field)); >>> CONSTRUCTOR_APPEND_ELT (vec, field, size_int (len)); >>> tree new_ctor = build_constructor (totype, vec); >>> return get_target_expr_sfinae (new_ctor, complain); >>> @@ -13267,8 +13267,8 @@ type_has_extended_temps (tree type) >>> { >>> if (is_std_init_list (type)) >>> return true; >>> - for (tree f = next_initializable_field (TYPE_FIELDS (type)); >>> - f; f = next_initializable_field (DECL_CHAIN (f))) >>> + for (tree f = next_aggregate_field (TYPE_FIELDS (type)); >>> + f; f = next_aggregate_field (DECL_CHAIN (f))) >>> if (type_has_extended_temps (TREE_TYPE (f))) >>> return true; >>> } >>> diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc >>> index bc94ed45e17..3c195b35396 100644 >>> --- a/gcc/cp/class.cc >>> +++ b/gcc/cp/class.cc >>> @@ -5494,8 +5494,8 @@ default_init_uninitialized_part (tree type) >>> if (r) >>> return r; >>> } >>> - for (t = next_initializable_field (TYPE_FIELDS (type)); t; >>> - t = next_initializable_field (DECL_CHAIN (t))) >>> + for (t = next_aggregate_field (TYPE_FIELDS (type)); t; >>> + t = next_aggregate_field (DECL_CHAIN (t))) >>> if (!DECL_INITIAL (t) && !DECL_ARTIFICIAL (t)) >>> { >>> r = default_init_uninitialized_part (TREE_TYPE (t)); >>> @@ -7781,10 +7781,10 @@ finish_struct (tree t, tree attributes) >>> bool ok = false; >>> if (processing_template_decl) >>> { >>> - tree f = next_initializable_field (TYPE_FIELDS (t)); >>> + tree f = next_aggregate_field (TYPE_FIELDS (t)); >>> if (f && TYPE_PTR_P (TREE_TYPE (f))) >>> { >>> - f = next_initializable_field (DECL_CHAIN (f)); >>> + f = next_aggregate_field (DECL_CHAIN (f)); >>> if (f && same_type_p (TREE_TYPE (f), size_type_node)) >>> ok = true; >>> } >>> diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc >>> index 9b1e71857fc..24717b4f8d5 100644 >>> --- a/gcc/cp/constexpr.cc >>> +++ b/gcc/cp/constexpr.cc >>> @@ -784,7 +784,7 @@ cx_check_missing_mem_inits (tree ctype, tree body, bool complain) >>> >>> if (TREE_CODE (ctype) == UNION_TYPE) >>> { >>> - if (nelts == 0 && next_initializable_field (field)) >>> + if (nelts == 0 && next_aggregate_field (field)) >>> { >>> if (complain) >>> error ("% constructor for union %qT must " >>> @@ -3053,7 +3053,7 @@ reduced_constant_expression_p (tree t) >>> field = NULL_TREE; >>> } >>> else >>> - field = next_initializable_field (TYPE_FIELDS (TREE_TYPE (t))); >>> + field = next_subobject_field (TYPE_FIELDS (TREE_TYPE (t))); >>> } >>> else >>> field = NULL_TREE; >>> @@ -3065,15 +3065,15 @@ reduced_constant_expression_p (tree t) >>> return false; >>> /* Empty class field may or may not have an initializer. */ >>> for (; field && e.index != field; >>> - field = next_initializable_field (DECL_CHAIN (field))) >>> + field = next_subobject_field (DECL_CHAIN (field))) >>> if (!is_really_empty_class (TREE_TYPE (field), >>> /*ignore_vptr*/false)) >>> return false; >>> if (field) >>> - field = next_initializable_field (DECL_CHAIN (field)); >>> + field = next_subobject_field (DECL_CHAIN (field)); >>> } >>> /* There could be a non-empty field at the end. */ >>> - for (; field; field = next_initializable_field (DECL_CHAIN (field))) >>> + for (; field; field = next_subobject_field (DECL_CHAIN (field))) >>> if (!is_really_empty_class (TREE_TYPE (field), /*ignore_vptr*/false)) >>> return false; >>> ok: >>> diff --git a/gcc/cp/cp-gimplify.cc b/gcc/cp/cp-gimplify.cc >>> index b52d9cb5754..ee47a4bfd37 100644 >>> --- a/gcc/cp/cp-gimplify.cc >>> +++ b/gcc/cp/cp-gimplify.cc >>> @@ -3106,7 +3106,7 @@ get_source_location_impl_type (location_t loc) >>> >>> int cnt = 0; >>> for (tree field = TYPE_FIELDS (type); >>> - (field = next_initializable_field (field)) != NULL_TREE; >>> + (field = next_aggregate_field (field)) != NULL_TREE; >>> field = DECL_CHAIN (field)) >>> { >>> if (DECL_NAME (field) != NULL_TREE) >>> @@ -3281,7 +3281,7 @@ fold_builtin_source_location (location_t loc) >>> vec *v = NULL; >>> vec_alloc (v, 4); >>> for (tree field = TYPE_FIELDS (source_location_impl); >>> - (field = next_initializable_field (field)) != NULL_TREE; >>> + (field = next_aggregate_field (field)) != NULL_TREE; >>> field = DECL_CHAIN (field)) >>> { >>> const char *n = IDENTIFIER_POINTER (DECL_NAME (field)); >>> diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h >>> index 663fe7a20fc..abcef7c20c4 100644 >>> --- a/gcc/cp/cp-tree.h >>> +++ b/gcc/cp/cp-tree.h >>> @@ -6870,7 +6870,8 @@ extern bool is_direct_enum_init (tree, tree); >>> extern void initialize_artificial_var (tree, vec *); >>> extern tree check_var_type (tree, tree, location_t); >>> extern tree reshape_init (tree, tree, tsubst_flags_t); >>> -extern tree next_initializable_field (tree); >>> +extern tree next_aggregate_field (tree); >>> +extern tree next_subobject_field (tree); >>> extern tree first_field (const_tree); >>> extern tree fndecl_declared_return_type (tree); >>> extern bool undeduced_auto_decl (tree); >>> diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc >>> index c9110db796a..d5ab2d0ad87 100644 >>> --- a/gcc/cp/decl.cc >>> +++ b/gcc/cp/decl.cc >>> @@ -6382,22 +6382,38 @@ struct reshape_iter >>> >>> static tree reshape_init_r (tree, reshape_iter *, tree, tsubst_flags_t); >>> >>> +/* FIELD is an element of TYPE_FIELDS of an aggregate class, or NULL. In the >>> + former case, the value returned is the next FIELD_DECL (possibly FIELD >>> + itself) that can be initialized. If there are no more such fields, the >>> + return value will be NULL. */ >>> + >>> +tree >>> +next_aggregate_field (tree field) >>> +{ >>> + while (field >>> + && (TREE_CODE (field) != FIELD_DECL >>> + || DECL_UNNAMED_BIT_FIELD (field) >>> + || (DECL_ARTIFICIAL (field) >>> + /* In C++17, aggregates can have base classes. */ >>> + && !(cxx_dialect >= cxx17 && DECL_FIELD_IS_BASE (field))))) >>> + field = DECL_CHAIN (field); >>> + >>> + return field; >>> +} >>> + >>> /* FIELD is an element of TYPE_FIELDS or NULL. In the former case, the value >>> - returned is the next FIELD_DECL (possibly FIELD itself) that can be >>> - initialized. If there are no more such fields, the return value >>> - will be NULL. */ >>> + returned is the next FIELD_DECL (possibly FIELD itself) that corresponds >>> + to a subobject. If there are no more such fields, the return value will be >>> + NULL. */ >>> >>> tree >>> -next_initializable_field (tree field) >>> +next_subobject_field (tree field) >>> { >>> while (field >>> && (TREE_CODE (field) != FIELD_DECL >>> || DECL_UNNAMED_BIT_FIELD (field) >>> || (DECL_ARTIFICIAL (field) >>> - /* In C++17, don't skip base class fields. */ >>> - && !(cxx_dialect >= cxx17 && DECL_FIELD_IS_BASE (field)) >>> - /* Don't skip vptr fields. We might see them when we're >>> - called from reduced_constant_expression_p. */ >>> + && !DECL_FIELD_IS_BASE (field) >>> && !DECL_VIRTUAL_P (field)))) >>> field = DECL_CHAIN (field); >>> >>> @@ -6595,7 +6611,7 @@ reshape_init_class (tree type, reshape_iter *d, bool first_initializer_p, >>> if (base_binfo) >>> field = base_binfo; >>> else >>> - field = next_initializable_field (TYPE_FIELDS (type)); >>> + field = next_aggregate_field (TYPE_FIELDS (type)); >>> >>> if (!field) >>> { >>> @@ -6760,10 +6776,10 @@ reshape_init_class (tree type, reshape_iter *d, bool first_initializer_p, >>> if (BINFO_BASE_ITERATE (binfo, ++binfo_idx, base_binfo)) >>> field = base_binfo; >>> else >>> - field = next_initializable_field (TYPE_FIELDS (type)); >>> + field = next_aggregate_field (TYPE_FIELDS (type)); >>> } >>> else >>> - field = next_initializable_field (DECL_CHAIN (field)); >>> + field = next_aggregate_field (DECL_CHAIN (field)); >>> } >>> >>> /* A trailing aggregate element that is a pack expansion is assumed to >>> diff --git a/gcc/cp/init.cc b/gcc/cp/init.cc >>> index 75ab965a218..f1ed9336dc9 100644 >>> --- a/gcc/cp/init.cc >>> +++ b/gcc/cp/init.cc >>> @@ -422,7 +422,7 @@ build_value_init_noctor (tree type, tsubst_flags_t complain) >>> && !COMPLETE_TYPE_P (ftype) >>> && !TYPE_DOMAIN (ftype) >>> && COMPLETE_TYPE_P (TREE_TYPE (ftype)) >>> - && (next_initializable_field (DECL_CHAIN (field)) >>> + && (next_aggregate_field (DECL_CHAIN (field)) >>> == NULL_TREE)) >>> continue; >>> >>> @@ -1477,9 +1477,9 @@ emit_mem_initializers (tree mem_inits) >>> >>> /* Initially that is all of them. */ >>> if (warn_uninitialized) >>> - for (tree f = next_initializable_field (TYPE_FIELDS (current_class_type)); >>> + for (tree f = next_aggregate_field (TYPE_FIELDS (current_class_type)); >>> f != NULL_TREE; >>> - f = next_initializable_field (DECL_CHAIN (f))) >>> + f = next_aggregate_field (DECL_CHAIN (f))) >>> if (!DECL_ARTIFICIAL (f) >>> && !is_really_empty_class (TREE_TYPE (f), /*ignore_vptr*/false)) >>> uninitialized.add (f); >>> diff --git a/gcc/cp/lambda.cc b/gcc/cp/lambda.cc >>> index afac53b6d7c..27704a578af 100644 >>> --- a/gcc/cp/lambda.cc >>> +++ b/gcc/cp/lambda.cc >>> @@ -425,9 +425,9 @@ build_capture_proxy (tree member, tree init) >>> if (DECL_VLA_CAPTURE_P (member)) >>> { >>> /* Rebuild the VLA type from the pointer and maxindex. */ >>> - tree field = next_initializable_field (TYPE_FIELDS (type)); >>> + tree field = next_aggregate_field (TYPE_FIELDS (type)); >>> tree ptr = build_simple_component_ref (object, field); >>> - field = next_initializable_field (DECL_CHAIN (field)); >>> + field = next_aggregate_field (DECL_CHAIN (field)); >>> tree max = build_simple_component_ref (object, field); >>> type = build_cplus_array_type (TREE_TYPE (TREE_TYPE (ptr)), >>> build_index_type (max)); >>> diff --git a/gcc/cp/method.cc b/gcc/cp/method.cc >>> index 903ee666ef3..0dffd648b0b 100644 >>> --- a/gcc/cp/method.cc >>> +++ b/gcc/cp/method.cc >>> @@ -1465,7 +1465,7 @@ build_comparison_op (tree fndecl, bool defining, tsubst_flags_t complain) >>> /* A defaulted comparison operator function for class C is defined as >>> deleted if ... C has variant members. */ >>> if (TREE_CODE (ctype) == UNION_TYPE >>> - && next_initializable_field (TYPE_FIELDS (ctype))) >>> + && next_aggregate_field (TYPE_FIELDS (ctype))) >>> { >>> if (complain & tf_error) >>> inform (info.loc, "cannot default compare union %qT", ctype); >>> @@ -1518,9 +1518,9 @@ build_comparison_op (tree fndecl, bool defining, tsubst_flags_t complain) >>> } >>> >>> /* Now compare the field subobjects. */ >>> - for (tree field = next_initializable_field (TYPE_FIELDS (ctype)); >>> + for (tree field = next_aggregate_field (TYPE_FIELDS (ctype)); >>> field; >>> - field = next_initializable_field (DECL_CHAIN (field))) >>> + field = next_aggregate_field (DECL_CHAIN (field))) >>> { >>> if (DECL_VIRTUAL_P (field) || DECL_FIELD_IS_BASE (field)) >>> /* We ignore the vptr, and we already handled bases. */ >>> @@ -1542,7 +1542,7 @@ build_comparison_op (tree fndecl, bool defining, tsubst_flags_t complain) >>> continue; >>> } >>> else if (ANON_UNION_TYPE_P (expr_type) >>> - && next_initializable_field (TYPE_FIELDS (expr_type))) >>> + && next_aggregate_field (TYPE_FIELDS (expr_type))) >>> { >>> if (complain & tf_error) >>> inform (field_loc, "cannot default compare " >>> diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc >>> index ac002907a41..86e7212daa5 100644 >>> --- a/gcc/cp/pt.cc >>> +++ b/gcc/cp/pt.cc >>> @@ -29578,7 +29578,7 @@ maybe_aggr_guide (tree tmpl, tree init, vec *args) >>> len; >>> --len, field = DECL_CHAIN (field)) >>> { >>> - field = next_initializable_field (field); >>> + field = next_aggregate_field (field); >>> if (!field) >>> return NULL_TREE; >>> tree ftype = finish_decltype_type (field, true, complain); >>> diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc >>> index ed0d0d22950..aba695116c5 100644 >>> --- a/gcc/cp/tree.cc >>> +++ b/gcc/cp/tree.cc >>> @@ -4852,8 +4852,8 @@ structural_type_p (tree t, bool explain) >>> explain_non_literal_class (t); >>> return false; >>> } >>> - for (tree m = next_initializable_field (TYPE_FIELDS (t)); m; >>> - m = next_initializable_field (DECL_CHAIN (m))) >>> + for (tree m = next_aggregate_field (TYPE_FIELDS (t)); m; >>> + m = next_aggregate_field (DECL_CHAIN (m))) >>> { >>> if (TREE_PRIVATE (m) || TREE_PROTECTED (m)) >>> { >>> diff --git a/gcc/cp/typeck2.cc b/gcc/cp/typeck2.cc >>> index 63d95c1529a..1d92310edd0 100644 >>> --- a/gcc/cp/typeck2.cc >>> +++ b/gcc/cp/typeck2.cc >>> @@ -606,7 +606,7 @@ split_nonconstant_init_1 (tree dest, tree init, bool last, >>> : TYPE_FIELDS (type)); >>> ; prev = DECL_CHAIN (prev)) >>> { >>> - prev = next_initializable_field (prev); >>> + prev = next_aggregate_field (prev); >>> if (prev == field_index) >>> break; >>> tree ptype = TREE_TYPE (prev); >>> @@ -1304,7 +1304,7 @@ digest_init_r (tree type, tree init, int nested, int flags, >>> the first element of d, which is the B base subobject. The base >>> of type B is copy-initialized from the D temporary, causing >>> object slicing. */ >>> - tree field = next_initializable_field (TYPE_FIELDS (type)); >>> + tree field = next_aggregate_field (TYPE_FIELDS (type)); >>> if (field && DECL_FIELD_IS_BASE (field)) >>> { >>> if (warning_at (loc, 0, "initializing a base class of type %qT " >>> diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-union7.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-union7.C >>> new file mode 100644 >>> index 00000000000..b3147d9db50 >>> --- /dev/null >>> +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-union7.C >>> @@ -0,0 +1,17 @@ >>> +// PR c++/105491 >>> +// { dg-do compile { target c++11 } } >>> + >>> +struct V { >>> + int m = 0; >>> +}; >>> +struct S : V { >>> + constexpr S(int) : b() { } >>> + bool b; >>> +}; >>> +struct W { >>> + constexpr W() : s(0) { } >>> + union { >>> + S s; >>> + }; >>> +}; >>> +constexpr W w; >>> diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-union7a.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-union7a.C >>> new file mode 100644 >>> index 00000000000..b676e7d1748 >>> --- /dev/null >>> +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-union7a.C >>> @@ -0,0 +1,15 @@ >>> +// PR c++/105491 >>> +// { dg-do compile { target c++11 } } >>> + >>> +struct V { >>> + int m = 0; >>> +}; >>> +struct S : V { >>> + constexpr S(int) : b() { } >>> + bool b; >>> +}; >>> +union W { >>> + constexpr W() : s(0) { } >>> + S s; >>> +}; >>> +constexpr W w; >>> diff --git a/gcc/testsuite/g++.dg/cpp2a/constinit17.C b/gcc/testsuite/g++.dg/cpp2a/constinit17.C >>> new file mode 100644 >>> index 00000000000..6431654ac85 >>> --- /dev/null >>> +++ b/gcc/testsuite/g++.dg/cpp2a/constinit17.C >>> @@ -0,0 +1,24 @@ >>> +// PR c++/105491 >>> +// { dg-do compile { target c++11 } } >>> + >>> +class Message { >>> + virtual int GetMetadata(); >>> +}; >>> +class ProtobufCFileOptions : Message { >>> +public: >>> + constexpr ProtobufCFileOptions(int); >>> + bool no_generate_; >>> + bool const_strings_; >>> + bool use_oneof_field_name_; >>> + bool gen_pack_helpers_; >>> + bool gen_init_helpers_; >>> +}; >>> +constexpr ProtobufCFileOptions::ProtobufCFileOptions(int) >>> + : no_generate_(), const_strings_(), use_oneof_field_name_(), >>> + gen_pack_helpers_(), gen_init_helpers_() {} >>> +struct ProtobufCFileOptionsDefaultTypeInternal { >>> + constexpr ProtobufCFileOptionsDefaultTypeInternal() : _instance({}) {} >>> + union { >>> + ProtobufCFileOptions _instance; >>> + }; >>> +} __constinit _ProtobufCFileOptions_default_instance_; >> >