From d19b4342c19e5a7fd84888aa06ebc106438d0c46 Mon Sep 17 00:00:00 2001 From: Jason Merrill Date: Wed, 1 Jun 2022 17:30:25 -0400 Subject: [PATCH] c++: more constexpr empty base [PR105795] Following on from the previous patch, for trunk let's consistently set ctx->ctor to NULL_TREE for empty subobjects. PR c++/105795 gcc/cp/ChangeLog: * constexpr.cc (init_subob_ctx): Clear ctx->ctor for empty subob. (cxx_eval_store_expression): Likewise. (cxx_eval_bare_aggregate): Handle null ctx->ctor. --- gcc/cp/constexpr.cc | 63 +++++++++++++++++++++++++++++---------------- 1 file changed, 41 insertions(+), 22 deletions(-) diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc index 021eaa34920..1346a1d4c10 100644 --- a/gcc/cp/constexpr.cc +++ b/gcc/cp/constexpr.cc @@ -4695,9 +4695,17 @@ init_subob_ctx (const constexpr_ctx *ctx, constexpr_ctx &new_ctx, else new_ctx.object = build_ctor_subob_ref (index, type, ctx->object); } - tree elt = build_constructor (type, NULL); - CONSTRUCTOR_NO_CLEARING (elt) = true; - new_ctx.ctor = elt; + + if (is_empty_class (type)) + /* Leave ctor null for an empty subobject, they aren't represented in the + result of evaluation. */ + new_ctx.ctor = NULL_TREE; + else + { + tree elt = build_constructor (type, NULL); + CONSTRUCTOR_NO_CLEARING (elt) = true; + new_ctx.ctor = elt; + } if (TREE_CODE (value) == TARGET_EXPR) /* Avoid creating another CONSTRUCTOR when we expand the TARGET_EXPR. */ @@ -4762,11 +4770,14 @@ cxx_eval_bare_aggregate (const constexpr_ctx *ctx, tree t, ctx = &new_ctx; }; verify_ctor_sanity (ctx, type); - vec **p = &CONSTRUCTOR_ELTS (ctx->ctor); - vec_alloc (*p, vec_safe_length (v)); - - if (CONSTRUCTOR_PLACEHOLDER_BOUNDARY (t)) - CONSTRUCTOR_PLACEHOLDER_BOUNDARY (ctx->ctor) = 1; + vec **p = nullptr; + if (ctx->ctor) + { + p = &CONSTRUCTOR_ELTS (ctx->ctor); + vec_alloc (*p, vec_safe_length (v)); + if (CONSTRUCTOR_PLACEHOLDER_BOUNDARY (t)) + CONSTRUCTOR_PLACEHOLDER_BOUNDARY (ctx->ctor) = 1; + } unsigned i; tree index, value; @@ -4814,17 +4825,19 @@ cxx_eval_bare_aggregate (const constexpr_ctx *ctx, tree t, inner->value = elt; changed = true; } + else if (no_slot) + /* This is an initializer for an empty field; now that we've + checked that it's constant, we can ignore it. */ + changed = true; else if (index && (TREE_CODE (index) == NOP_EXPR || TREE_CODE (index) == POINTER_PLUS_EXPR)) { - /* This is an initializer for an empty base; now that we've - checked that it's constant, we can ignore it. */ + /* Old representation of empty bases. FIXME remove. */ + gcc_checking_assert (false); gcc_assert (is_empty_class (TREE_TYPE (TREE_TYPE (index)))); changed = true; } - else if (no_slot) - changed = true; else { if (TREE_CODE (type) == UNION_TYPE @@ -4849,6 +4862,8 @@ cxx_eval_bare_aggregate (const constexpr_ctx *ctx, tree t, if (*non_constant_p || !changed) return t; t = ctx->ctor; + if (!t) + t = build_constructor (type, NULL); /* We're done building this CONSTRUCTOR, so now we can interpret an element without an explicit initializer as value-initialized. */ CONSTRUCTOR_NO_CLEARING (t) = false; @@ -5833,6 +5848,16 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t, valp = &cep->value; } + /* For initialization of an empty base, the original target will be + *(base*)this, evaluation of which resolves to the object + argument, which has the derived type rather than the base type. */ + if (!empty_base && !(same_type_ignoring_top_level_qualifiers_p + (initialized_type (init), type))) + { + gcc_assert (is_empty_class (TREE_TYPE (target))); + empty_base = true; + } + /* Detect modifying a constant object in constexpr evaluation. We have found a const object that is being modified. Figure out if we need to issue an error. Consider @@ -5901,7 +5926,7 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t, *valp = build_constructor (type, NULL); CONSTRUCTOR_NO_CLEARING (*valp) = no_zero_init; } - new_ctx.ctor = *valp; + new_ctx.ctor = empty_base ? NULL_TREE : *valp; new_ctx.object = target; /* Avoid temporary materialization when initializing from a TARGET_EXPR. We don't need to mess with AGGR_EXPR_SLOT/VEC_INIT_EXPR_SLOT because @@ -5931,16 +5956,10 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t, gcc_checking_assert (!*valp || (same_type_ignoring_top_level_qualifiers_p (TREE_TYPE (*valp), type))); - if (empty_base || !(same_type_ignoring_top_level_qualifiers_p - (initialized_type (init), type))) + if (empty_base) { - /* For initialization of an empty base, the original target will be - *(base*)this, evaluation of which resolves to the object - argument, which has the derived type rather than the base type. In - this situation, just evaluate the initializer and return, since - there's no actual data to store, and we didn't build a CONSTRUCTOR. */ - gcc_assert (is_empty_class (TREE_TYPE (target))); - empty_base = true; + /* Just evaluate the initializer and return, since there's no actual data + to store, and we didn't build a CONSTRUCTOR. */ if (!*valp) { /* But do make sure we have something in *valp. */