]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
c++: empty base constexpr adjustment [PR105245]
authorJason Merrill <jason@redhat.com>
Tue, 12 Apr 2022 21:46:59 +0000 (17:46 -0400)
committerJason Merrill <jason@redhat.com>
Fri, 6 May 2022 20:29:49 +0000 (16:29 -0400)
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

index 9b1e71857fc7907645ac0ace3fce8e34046d1395..6c204ab226549993af7a63b37a26f07d9196594f 100644 (file)
@@ -5718,6 +5718,7 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t,
   releasing_vec ctors, indexes;
   auto_vec<int> 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,45 +5908,42 @@ 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)))
+    {
+      /* 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;
+       }
+    }
+  else if (*valp && TREE_CODE (*valp) == CONSTRUCTOR
+          && TREE_CODE (init) == CONSTRUCTOR)
     {
       /* 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);
-      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
     *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;