From 967cdbe66296535fa496b88406a1125c8acaf6e2 Mon Sep 17 00:00:00 2001 From: Jason Merrill Date: Tue, 12 Apr 2022 17:46:59 -0400 Subject: [PATCH] c++: empty base constexpr adjustment [PR105245] While looking at PR105245 in stage 4, I wanted to reorganize the code a bit, but it seemed prudent to defer that to stage 1. PR c++/105245 PR c++/100111 gcc/cp/ChangeLog: * constexpr.cc (cxx_eval_store_expression): Reorganize empty base handling. --- gcc/cp/constexpr.cc | 69 +++++++++++++++++++++++---------------------- 1 file changed, 35 insertions(+), 34 deletions(-) diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc index 9b1e71857fc..6c204ab2265 100644 --- a/gcc/cp/constexpr.cc +++ b/gcc/cp/constexpr.cc @@ -5718,6 +5718,7 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t, releasing_vec ctors, indexes; auto_vec index_pos_hints; bool activated_union_member_p = false; + bool empty_base = false; while (!refs->is_empty ()) { if (*valp == NULL_TREE) @@ -5759,7 +5760,7 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t, no_zero_init = CONSTRUCTOR_NO_CLEARING (*valp); enum tree_code code = TREE_CODE (type); - type = refs->pop(); + tree reftype = refs->pop(); tree index = refs->pop(); if (code == RECORD_TYPE && is_empty_field (index)) @@ -5768,7 +5769,12 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t, fields, which confuses the middle-end. The code below will notice that we don't have a CONSTRUCTOR for our inner target and just return init. */ - break; + { + empty_base = true; + break; + } + + type = reftype; if (code == UNION_TYPE && CONSTRUCTOR_NELTS (*valp) && CONSTRUCTOR_ELT (*valp, 0)->index != index) @@ -5902,44 +5908,41 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t, } } + if (*non_constant_p) + return t; + /* Don't share a CONSTRUCTOR that might be changed later. */ init = unshare_constructor (init); - if (*valp && TREE_CODE (*valp) == CONSTRUCTOR - && TREE_CODE (init) == CONSTRUCTOR) + 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 + (TREE_TYPE (init), type))) { - /* An outer ctx->ctor might be pointing to *valp, so replace - its contents. */ - if (!same_type_ignoring_top_level_qualifiers_p (TREE_TYPE (init), - TREE_TYPE (*valp))) - { - /* 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. */ - gcc_assert (is_empty_class (TREE_TYPE (init))); - return lval ? target : init; - } - CONSTRUCTOR_ELTS (*valp) = CONSTRUCTOR_ELTS (init); - TREE_CONSTANT (*valp) = TREE_CONSTANT (init); - TREE_SIDE_EFFECTS (*valp) = TREE_SIDE_EFFECTS (init); - CONSTRUCTOR_NO_CLEARING (*valp) - = CONSTRUCTOR_NO_CLEARING (init); - } - else if (TREE_CODE (init) == CONSTRUCTOR - && !same_type_ignoring_top_level_qualifiers_p (TREE_TYPE (init), - type)) - { - /* See above on initialization of empty bases. */ - gcc_assert (is_empty_class (TREE_TYPE (init)) && !lval); + /* 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. */ + empty_base = true; + gcc_assert (is_empty_class (TREE_TYPE (init))); if (!*valp) { /* But do make sure we have something in *valp. */ *valp = build_constructor (type, nullptr); CONSTRUCTOR_NO_CLEARING (*valp) = no_zero_init; } - return init; + } + else if (*valp && TREE_CODE (*valp) == CONSTRUCTOR + && TREE_CODE (init) == CONSTRUCTOR) + { + /* An outer ctx->ctor might be pointing to *valp, so replace + its contents. */ + CONSTRUCTOR_ELTS (*valp) = CONSTRUCTOR_ELTS (init); + TREE_CONSTANT (*valp) = TREE_CONSTANT (init); + TREE_SIDE_EFFECTS (*valp) = TREE_SIDE_EFFECTS (init); + CONSTRUCTOR_NO_CLEARING (*valp) + = CONSTRUCTOR_NO_CLEARING (init); } else *valp = init; @@ -5958,7 +5961,7 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t, constructor of a delegating constructor). Leave it up to the caller that set 'this' to set TREE_READONLY appropriately. */ gcc_checking_assert (same_type_ignoring_top_level_qualifiers_p - (TREE_TYPE (target), type)); + (TREE_TYPE (target), type) || empty_base); else TREE_READONLY (*valp) = true; } @@ -5980,9 +5983,7 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t, CONSTRUCTOR_NO_CLEARING (elt) = false; } - if (*non_constant_p) - return t; - else if (lval) + if (lval) return target; else return init;