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.
Jason