[pushed] c++: empty base constexpr adjustment [PR105245]

Message ID 20220506203025.2943558-1-jason@redhat.com
State Committed
Commit 967cdbe66296535fa496b88406a1125c8acaf6e2
Headers
Series [pushed] c++: empty base constexpr adjustment [PR105245] |

Commit Message

Jason Merrill May 6, 2022, 8:30 p.m. UTC
  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.

Tested x86_64-pc-linux-gnu, applying to trunk.

	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(-)


base-commit: 469c76f0d94d03e29467a9d1e77cd3613f46ac2f
  

Patch

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<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,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;