From: Patrick Palka <ppalka@redhat.com>
To: Jason Merrill <jason@redhat.com>
Cc: Patrick Palka <ppalka@redhat.com>, GCC Patches <gcc-patches@gcc.gnu.org>
Subject: Re: [PATCH] c++: constexpr init of union sub-aggr w/ base [PR105491]
Date: Tue, 31 May 2022 12:41:55 -0400 (EDT) [thread overview]
Message-ID: <1c566819-8c54-ad63-83e0-4e4cbbd0bd17@idea> (raw)
In-Reply-To: <89968899-201b-9dc6-5bcf-eca32b9d47ae@redhat.com>
On Wed, 18 May 2022, Jason Merrill wrote:
> On 5/17/22 12:34, Patrick Palka wrote:
> > On Sat, May 7, 2022 at 5:18 PM Jason Merrill <jason@redhat.com> 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
> > > >
> > > > <source>: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?
Ah makes sense, like so?
-- >8 --
Subject: [PATCH] c++: constexpr init of union sub-aggr w/ base [PR105491]
...
NB: This minimal backport of r13-211-g0c7bce0ac184c0 for 12.2 just adds
next_subobject_field and makes reduced_constant_expression_p use it.
PR c++/105491
gcc/cp/ChangeLog:
* constexpr.cc (reduced_constant_expression_p): Use
next_subobject_field instead.
* cp-tree.h (next_subobject_field): Declare.
* decl.cc (next_subobject_field): Define.
gcc/testsuite/ChangeLog:
* g++.dg/cpp0x/constexpr-union7.C: New test.
* g++.dg/cpp0x/constexpr-union7a.C: New test.
* g++.dg/cpp2a/constinit17.C: New test.
(cherry picked from commit 0c7bce0ac184c057bacad9c8e615ce82923835fd)
---
gcc/cp/constexpr.cc | 8 +++----
gcc/cp/cp-tree.h | 1 +
gcc/cp/decl.cc | 19 +++++++++++++++
gcc/testsuite/g++.dg/cpp0x/constexpr-union7.C | 17 +++++++++++++
.../g++.dg/cpp0x/constexpr-union7a.C | 15 ++++++++++++
gcc/testsuite/g++.dg/cpp2a/constinit17.C | 24 +++++++++++++++++++
6 files changed, 80 insertions(+), 4 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/constexpr.cc b/gcc/cp/constexpr.cc
index 47d5113ace2..1e3342adbb9 100644
--- a/gcc/cp/constexpr.cc
+++ b/gcc/cp/constexpr.cc
@@ -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-tree.h b/gcc/cp/cp-tree.h
index e9a3d09ac4c..63437415296 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -6884,6 +6884,7 @@ extern void initialize_artificial_var (tree, vec<constructor_elt, va_gc> *);
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_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 2852093d624..af5eca71ba1 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -6409,6 +6409,25 @@ next_initializable_field (tree 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 corresponds
+ to a subobject. If there are no more such fields, the return value will be
+ NULL. */
+
+tree
+next_subobject_field (tree field)
+{
+ while (field
+ && (TREE_CODE (field) != FIELD_DECL
+ || DECL_UNNAMED_BIT_FIELD (field)
+ || (DECL_ARTIFICIAL (field)
+ && !DECL_FIELD_IS_BASE (field)
+ && !DECL_VIRTUAL_P (field))))
+ field = DECL_CHAIN (field);
+
+ return field;
+}
+
/* Return true for [dcl.init.list] direct-list-initialization from
single element of enumeration with a fixed underlying type. */
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_;
--
2.36.1.203.g1bcf4f6271
next prev parent reply other threads:[~2022-05-31 16:41 UTC|newest]
Thread overview: 15+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-05-06 15:22 Patrick Palka
2022-05-06 15:56 ` Jason Merrill
2022-05-06 16:04 ` Marek Polacek
2022-05-06 16:40 ` Patrick Palka
2022-05-06 18:00 ` Patrick Palka
2022-05-06 18:22 ` Jason Merrill
2022-05-06 19:24 ` Patrick Palka
2022-05-06 20:10 ` Patrick Palka
2022-05-06 20:27 ` Jason Merrill
2022-05-06 20:46 ` Patrick Palka
2022-05-07 21:18 ` Jason Merrill
2022-05-17 16:34 ` Patrick Palka
2022-05-18 14:22 ` Jason Merrill
2022-05-31 16:41 ` Patrick Palka [this message]
2022-05-31 19:06 ` Jason Merrill
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1c566819-8c54-ad63-83e0-4e4cbbd0bd17@idea \
--to=ppalka@redhat.com \
--cc=gcc-patches@gcc.gnu.org \
--cc=jason@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).