On 7/27/26 4:39 PM, Jakub Jelinek wrote:
On Mon, Jul 27, 2026 at 09:26:55AM +0200, Jakub Jelinek wrote:
On Mon, Jul 27, 2026 at 09:01:32AM +0200, Jakub Jelinek wrote:
Added to testsuite (also the other testcases from the thread).
I think if we really wanted this lookup caching, either we'd need to create
a vector, store the cached lookup results in there and pass it around
to recursive calls, or we could create a vector, store there the original
values, change the *->index to the lookup results and ensure (through RAII,
we have way too many returns) to revert this back afterwards.

Actually, for the RAII cleanup, we could just remember the start (i.e.
d->cur from some point) and count how many need to be restored, the
old ones should be always IDENTIFIER_NODE and new ones FIELD_DECLs from
their lookup, so the cleanup could be just store there DECL_NAME of
the FIELD_DECLs.

Here is an updated version which does it that way.

Nice.

But it seems to me that once we cache the lookups this way, we don't need to determine the set of designators that belong to the current base; we should be able to look them all up and let the normal handling work from there.

I felt awkward about continuing to ask you for changes, so I poked at it some myself. The first patch changes reshape_init_class to avoid changing d->end, and I think makes sense to combine with your patch.

It would also be good to check -Wmissing-braces in at least one of the new testcases.

The second patch changes _class to go through reshape_init_r for this case instead of directly recursing into reshape_init_class, which avoids the duplication of -Wmissing-braces handling. This required more adjustment of reshape_init_r than I expected, so I'm inclined to make it a followup, but I think it's a useful clarification.

Thoughts?

Jason
commit a8af09c2d0fb6abba8e99c4c1c7a7d8402e499fc
Author: Jason Merrill <[email protected]>
Date:   Tue Aug 4 05:15:01 2026 -0400

    avoid changing d->end

diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index 62951dda3b0..b5d9ed05874 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -7854,9 +7854,9 @@ reshape_init_class (tree type, reshape_iter *d, bool first_initializer_p,
     {
       tree field_init;
       constructor_elt *old_cur = d->cur;
-      constructor_elt *old_end = d->end, *new_end = NULL;
       unsigned old_raw_idx = d->raw_idx;
       bool direct_desig = false;
+      bool subclass = false;
 
       /* Handle C++20 designated initializers.  */
       if (d->cur->index)
@@ -7946,11 +7946,18 @@ reshape_init_class (tree type, reshape_iter *d, bool first_initializer_p,
 		  ictx = cctx;
 		}
 
+	      /* In C++29 a designator can name a member of a base; in that
+		 case, go through the designators and replace ids with _DECLs
+		 to record the lookup for the most-derived class.  */
 	      if (cxx_dialect >= cxx29)
 		{
 		  tree ibinfo = lookup_base (type, ictx, ba_unique, NULL,
 					     complain);
-		  if (ibinfo && ibinfo != error_mark_node)
+		  if (!ibinfo)
+		    /* The designator names a field outside this base class,
+		       so we're done.  */
+		    break;
+		  else if (ibinfo != error_mark_node)
 		    {
 		      while (BINFO_INHERITANCE_CHAIN (ibinfo) != binfo)
 			ibinfo = BINFO_INHERITANCE_CHAIN (ibinfo);
@@ -7958,28 +7965,22 @@ reshape_init_class (tree type, reshape_iter *d, bool first_initializer_p,
 
 		      desig_undo.undo ();
 
-		      /* Find out which following elements also correspond
-			 to the same ibinfo, and temporarily change d->end
-			 to the first element after that.  */
-		      new_end = d->cur + 1;
 		      if (d->cur->index != field)
 			{
 			  d->cur->index = field;
 			  desig_undo.start = d->cur;
 			}
-		      while (new_end != d->end)
+		      constructor_elt *e = d->cur + 1;
+		      for (; e != d->end; ++e)
 			{
-			  if (new_end->index == NULL_TREE)
+			  if (e->index == NULL_TREE
+			      || e->index == error_mark_node)
 			    break;
 			  if (desig_undo.start)
 			    {
-			      if (TREE_CODE (new_end->index)
-				  != IDENTIFIER_NODE)
-				break;
-			      field = get_class_binding (type, new_end->index);
-			      if (field != NULL_TREE)
-				break;
-			      field = lookup_member (type, new_end->index,
+			      gcc_assert (TREE_CODE (e->index)
+					  == IDENTIFIER_NODE);
+			      field = lookup_member (type, e->index,
 						     /*protect=*/2,
 						     /*want_type=*/false,
 						     tf_none);
@@ -7988,32 +7989,15 @@ reshape_init_class (tree type, reshape_iter *d, bool first_initializer_p,
 			    }
 			  else
 			    {
-			      if (TREE_CODE (new_end->index) != FIELD_DECL)
-				break;
-			      field = new_end->index;
+			      gcc_assert (TREE_CODE (e->index) == FIELD_DECL);
+			      field = e->index;
 			    }
 
-			  tree nictx = DECL_CONTEXT (field);
-			  if (same_type_ignoring_top_level_qualifiers_p (nictx,
-									 type))
-			    break;
-			  while (ANON_AGGR_TYPE_P (nictx))
-			    nictx = TYPE_CONTEXT (nictx);
-
-			  ibinfo = lookup_base (type, nictx, ba_unique, NULL,
-						tf_none);
-			  if (!ibinfo || ibinfo == error_mark_node)
-			    break;
-			  while (BINFO_INHERITANCE_CHAIN (ibinfo) != binfo)
-			    ibinfo = BINFO_INHERITANCE_CHAIN (ibinfo);
-			  if (TREE_TYPE (ibinfo) != ictx)
-			    break;
 			  if (desig_undo.start)
-			    new_end->index = field;
-			  new_end++;
+			    e->index = field;
 			}
 		      if (desig_undo.start)
-			desig_undo.end = new_end;
+			desig_undo.end = e;
 		      goto found;
 		    }
 		}
@@ -8032,6 +8016,7 @@ reshape_init_class (tree type, reshape_iter *d, bool first_initializer_p,
 	      gcc_assert (aafield);
 	      field = aafield;
 	      direct_desig = false;
+	      subclass = true;
 	    }
 	}
 
@@ -8063,13 +8048,17 @@ reshape_init_class (tree type, reshape_iter *d, bool first_initializer_p,
 					    d->cur->value, complain);
 	  d->cur++;
 	}
-      else if (new_end)
+      else if (subclass)
 	{
-	  d->end = new_end;
+	  if (complain & tf_warning)
+	    warning (OPT_Wmissing_braces,
+		     "missing braces around initializer for %qT",
+		     TREE_TYPE (field));
 	  field_init = reshape_init_class (TREE_TYPE (field), d,
 					   /*first_initializer_p=*/NULL_TREE,
 					   complain);
-	  d->end = old_end;
+	  if (TREE_CODE (field_init) == CONSTRUCTOR)
+	    CONSTRUCTOR_BRACES_ELIDED_P (field_init) = true;
 	}
       else
 	field_init = reshape_init_r (TREE_TYPE (field), d,
commit ea48c3d29132384a32b8c0fd1e036818abbb8996
Author: Jason Merrill <[email protected]>
Date:   Tue Aug 4 19:45:17 2026 -0400

    avoid reshape_init_class recursion

diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index b5d9ed05874..ac820d53d77 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -7473,6 +7473,7 @@ struct reshape_iter
 };
 
 static tree reshape_init_r (tree, reshape_iter *, tree, tsubst_flags_t);
+static tree reshape_single_init (tree, tree, tsubst_flags_t);
 
 /* FIELD is an element of TYPE_FIELDS or NULL.  In the former case, the value
    returned is the next FIELD_DECL (possibly FIELD itself) that can be
@@ -7659,6 +7660,11 @@ reshape_init_array_1 (tree elt_type, tree max_index, reshape_iter *d,
 	    }
 	  TREE_TYPE (elt_init) = elt_type;
 	}
+      else if (d->cur->index)
+	{
+	  elt_init = reshape_single_init (elt_type, d->cur->value, complain);
+	  d->cur++;
+	}
       else
 	elt_init = reshape_init_r (elt_type, d,
 				   /*first_initializer_p=*/NULL_TREE,
@@ -7856,7 +7862,6 @@ reshape_init_class (tree type, reshape_iter *d, bool first_initializer_p,
       constructor_elt *old_cur = d->cur;
       unsigned old_raw_idx = d->raw_idx;
       bool direct_desig = false;
-      bool subclass = false;
 
       /* Handle C++20 designated initializers.  */
       if (d->cur->index)
@@ -8016,7 +8021,6 @@ reshape_init_class (tree type, reshape_iter *d, bool first_initializer_p,
 	      gcc_assert (aafield);
 	      field = aafield;
 	      direct_desig = false;
-	      subclass = true;
 	    }
 	}
 
@@ -8048,18 +8052,6 @@ reshape_init_class (tree type, reshape_iter *d, bool first_initializer_p,
 					    d->cur->value, complain);
 	  d->cur++;
 	}
-      else if (subclass)
-	{
-	  if (complain & tf_warning)
-	    warning (OPT_Wmissing_braces,
-		     "missing braces around initializer for %qT",
-		     TREE_TYPE (field));
-	  field_init = reshape_init_class (TREE_TYPE (field), d,
-					   /*first_initializer_p=*/NULL_TREE,
-					   complain);
-	  if (TREE_CODE (field_init) == CONSTRUCTOR)
-	    CONSTRUCTOR_BRACES_ELIDED_P (field_init) = true;
-	}
       else
 	field_init = reshape_init_r (TREE_TYPE (field), d,
 				     /*first_initializer_p=*/NULL_TREE,
@@ -8126,9 +8118,13 @@ has_designator_problem (reshape_iter *d, tsubst_flags_t complain)
   if (d->cur->index)
     {
       if (complain & tf_error)
-	error_at (cp_expr_loc_or_input_loc (d->cur->index),
-		  "C99 designator %qE outside aggregate initializer",
-		  d->cur->index);
+	{
+	  error_at (cp_expr_loc_or_input_loc (d->cur->index),
+		    "C99 designator %qE outside aggregate initializer",
+		    d->cur->index);
+	  /* Avoid confusion in reshape_init_r.  */
+	  d->cur->index = NULL_TREE;
+	}
       else
 	return true;
     }
@@ -8187,12 +8183,14 @@ reshape_init_r (tree type, reshape_iter *d, tree first_initializer_p,
 
   /* A non-aggregate type is always initialized with a single
      initializer.  */
-  if (!CP_AGGREGATE_TYPE_P (type)
-      /* As is an array with dependent bound, which we can see
-	 during C++20 aggregate CTAD.  */
-      || (cxx_dialect >= cxx20
-	  && TREE_CODE (type) == ARRAY_TYPE
-	  && uses_template_parms (TYPE_DOMAIN (type))))
+  if ((!CP_AGGREGATE_TYPE_P (type)
+       /* As is an array with dependent bound, which we can see
+	  during C++20 aggregate CTAD.  */
+       || (cxx_dialect >= cxx20
+	   && TREE_CODE (type) == ARRAY_TYPE
+	   && uses_template_parms (TYPE_DOMAIN (type))))
+      /* But if there's a designator, defer so we diagnose that.  */
+      && !d->cur->index)
     {
       /* It is invalid to initialize a non-aggregate type with a
 	 brace-enclosed initializer before C++0x.
@@ -8259,6 +8257,8 @@ reshape_init_r (tree type, reshape_iter *d, tree first_initializer_p,
 	 looking through the outermost braces; A a2 = { a1 }; is not a
 	 valid aggregate initialization.  */
       && !first_initializer_p
+      /* But a designator can indicate brace elision.  */
+      && !d->cur->index
       && (same_type_ignoring_top_level_qualifiers_p (type, TREE_TYPE (init))
 	  || can_convert_arg (type, TREE_TYPE (init),
 			      TREE_CODE (init) == RAW_DATA_CST
@@ -8313,7 +8313,9 @@ reshape_init_r (tree type, reshape_iter *d, tree first_initializer_p,
   bool braces_elided_p = false;
   if (!first_initializer_p)
     {
-      if (TREE_CODE (stripped_init) == CONSTRUCTOR)
+      if (TREE_CODE (stripped_init) == CONSTRUCTOR
+	  /* If a designator gets here, we're missing braces.  */
+	  && !d->cur->index)
 	{
 	  tree init_type = TREE_TYPE (init);
 	  if (init_type && TYPE_PTRMEMFUNC_P (init_type))
@@ -8326,15 +8328,6 @@ reshape_init_r (tree type, reshape_iter *d, tree first_initializer_p,
 	     to handle initialization of arrays and similar.  */
 	  else if (COMPOUND_LITERAL_P (stripped_init))
 	    gcc_assert (!BRACE_ENCLOSED_INITIALIZER_P (stripped_init));
-	  /* If we have an unresolved designator, we need to find the member it
-	     designates within TYPE, so proceed to the routines below.  For
-	     FIELD_DECL or INTEGER_CST designators, we're already initializing
-	     the designated element.  */
-	  else if (d->cur->index
-		   && TREE_CODE (d->cur->index) == IDENTIFIER_NODE)
-	    /* Brace elision with designators is only permitted for anonymous
-	       aggregates.  */
-	    gcc_checking_assert (ANON_AGGR_TYPE_P (type));
 	  /* A CONSTRUCTOR of the target's type is a previously
 	     digested initializer.  */
 	  else if (same_type_ignoring_top_level_qualifiers_p (type, init_type))

Reply via email to