From ad2908ed4ec5eff3cad3fd142cde5c1fac4788e9 Mon Sep 17 00:00:00 2001
From: Jakub Jelinek <jakub@redhat.com>
Date: Wed, 26 Feb 2025 19:31:08 +0100
Subject: [PATCH] c: Assorted fixes for flexible array members in unions
 [PR119001]

r15-209 allowed flexible array members inside of unions, but as the
following testcase shows, not everything has been adjusted for that.
Unlike structures, in unions flexible array member (as an extension)
can be any of the members, not just the last one, as in union all
members are effectively last.
The first hunk is about an ICE on the initialization of the FAM
in union which is not the last FIELD_DECL with a string literal,
the second hunk just formatting fix, third hunk fixes a bug in which
we were just throwing away the initializers (except for with string literal)
of FAMs in unions which aren't the last FIELD_DECL, and the last hunk
is to diagnose FAM errors in unions the same as for structures, in
particular trying to initialize a FAM with non-constant or initialization
in nested context.

2025-02-26  Jakub Jelinek  <jakub@redhat.com>

	PR c/119001
gcc/
	* varasm.cc (output_constructor_regular_field): Don't fail
	assertion if next is non-NULL and FIELD_DECL if
	TREE_CODE (local->type) is UNION_TYPE.
gcc/c/
	* c-typeck.cc (pop_init_level): Don't clear constructor_type
	if DECL_CHAIN of constructor_fields is NULL but p->type is UNION_TYPE.
	Formatting fix.
	(process_init_element): Diagnose non-static initialization of flexible
	array member in union or FAM in union initialization in nested context.
gcc/testsuite/
	* gcc.dg/pr119001-1.c: New test.
	* gcc.dg/pr119001-2.c: New test.
---
 gcc/c/c-typeck.cc                 | 42 +++++++++++++++++++++++++++++--
 gcc/testsuite/gcc.dg/pr119001-1.c | 35 ++++++++++++++++++++++++++
 gcc/testsuite/gcc.dg/pr119001-2.c | 20 +++++++++++++++
 gcc/varasm.cc                     |  9 ++++---
 4 files changed, 101 insertions(+), 5 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/pr119001-1.c
 create mode 100644 gcc/testsuite/gcc.dg/pr119001-2.c

diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index 9ac1fea31791..691b583db3f8 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -10270,7 +10270,8 @@ pop_init_level (location_t loc, int implicit,
 	  gcc_assert (!TYPE_SIZE (constructor_type));
 
 	  if (constructor_depth > 2)
-	    error_init (loc, "initialization of flexible array member in a nested context");
+	    error_init (loc, "initialization of flexible array member "
+			     "in a nested context");
 	  else
 	    pedwarn_init (loc, OPT_Wpedantic,
 			  "initialization of a flexible array member");
@@ -10278,7 +10279,8 @@ pop_init_level (location_t loc, int implicit,
 	  /* We have already issued an error message for the existence
 	     of a flexible array member not at the end of the structure.
 	     Discard the initializer so that we do not die later.  */
-	  if (DECL_CHAIN (constructor_fields) != NULL_TREE)
+	  if (DECL_CHAIN (constructor_fields) != NULL_TREE
+	      && (!p->type || TREE_CODE (p->type) != UNION_TYPE))
 	    constructor_type = NULL_TREE;
 	}
     }
@@ -12124,6 +12126,42 @@ retry:
 	    warning (OPT_Wtraditional, "traditional C rejects initialization "
 		     "of unions");
 
+	  /* Error for non-static initialization of a flexible array member.  */
+	  if (fieldcode == ARRAY_TYPE
+	      && !require_constant_value
+	      && TYPE_SIZE (fieldtype) == NULL_TREE)
+	    {
+	      error_init (loc, "non-static initialization of a flexible "
+			  "array member");
+	      break;
+	    }
+
+	  /* Error for initialization of a flexible array member with
+	     a string constant if the structure is in an array.  E.g.:
+	     union U { int x; char y[]; };
+	     union U s[] = { { 1, "foo" } };
+	     is invalid.  */
+	  if (string_flag
+	      && fieldcode == ARRAY_TYPE
+	      && constructor_depth > 1
+	      && TYPE_SIZE (fieldtype) == NULL_TREE)
+	    {
+	      bool in_array_p = false;
+	      for (struct constructor_stack *p = constructor_stack;
+		   p && p->type; p = p->next)
+		if (TREE_CODE (p->type) == ARRAY_TYPE)
+		  {
+		    in_array_p = true;
+		    break;
+		  }
+	      if (in_array_p)
+		{
+		  error_init (loc, "initialization of flexible array "
+			      "member in a nested context");
+		  break;
+		}
+	    }
+
 	  /* Accept a string constant to initialize a subarray.  */
 	  if (value.value != NULL_TREE
 	      && fieldcode == ARRAY_TYPE
diff --git a/gcc/testsuite/gcc.dg/pr119001-1.c b/gcc/testsuite/gcc.dg/pr119001-1.c
new file mode 100644
index 000000000000..22ceca9da77d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pr119001-1.c
@@ -0,0 +1,35 @@
+/* PR c/119001 */
+/* { dg-do run } */
+/* { dg-options "" } */
+
+union U { char a[]; int i; };
+union U u = { "12345" };
+union U v = { .a = "6789" };
+union U w = { { 1, 2, 3, 4, 5, 6 } };
+union U x = { .a = { 7, 8, 9 } };
+union V { int i; char a[]; };
+union V y = { .a = "abcdefghijk" };
+union V z = { .a = { 10, 11, 12, 13, 14, 15, 16, 17 } };
+
+int
+main ()
+{
+  for (int i = 0; i < 6; ++i)
+    if (u.a[i] != "12345"[i])
+      __builtin_abort ();
+  for (int i = 0; i < 5; ++i)
+    if (v.a[i] != "6789"[i])
+      __builtin_abort ();
+  for (int i = 0; i < 6; ++i)
+    if (w.a[i] != i + 1)
+      __builtin_abort ();
+  for (int i = 0; i < 3; ++i)
+    if (x.a[i] != i + 7)
+      __builtin_abort ();
+  for (int i = 0; i < 12; ++i)
+    if (y.a[i] != "abcdefghijk"[i])
+      __builtin_abort ();
+  for (int i = 0; i < 8; ++i)
+    if (z.a[i] != i + 10)
+      __builtin_abort ();
+}
diff --git a/gcc/testsuite/gcc.dg/pr119001-2.c b/gcc/testsuite/gcc.dg/pr119001-2.c
new file mode 100644
index 000000000000..b95fd0d1198e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pr119001-2.c
@@ -0,0 +1,20 @@
+/* PR c/119001 */
+/* { dg-do compile } */
+/* { dg-options "" } */
+
+union U { char a[]; int i; };
+union U u[1] = { { "12345" } };			/* { dg-error "initialization of flexible array member in a nested context" } */
+union U v[1] = { { .a = "6789" } };		/* { dg-error "initialization of flexible array member in a nested context" } */
+union U w[1] = { { { 1, 2, 3, 4, 5, 6 } } };	/* { dg-error "initialization of flexible array member in a nested context" } */
+union U x[1] = { { .a = { 7, 8, 9 } } };	/* { dg-error "initialization of flexible array member in a nested context" } */
+union V { int i; char a[]; };
+union V y[1] = { { .a = "6789" } };		/* { dg-error "initialization of flexible array member in a nested context" } */
+union V z[1] = { { .a = { 7, 8, 9 } } };	/* { dg-error "initialization of flexible array member in a nested context" } */
+
+void
+foo (int x)
+{
+  union U a = { { x, x + 1 } };			/* { dg-error "non-static initialization of a flexible array member" } */
+  union U b = { .a = { x + 2, x + 3 } };	/* { dg-error "non-static initialization of a flexible array member" } */
+  union V c = { .a = { x + 4, x + 5 } };	/* { dg-error "non-static initialization of a flexible array member" } */
+}
diff --git a/gcc/varasm.cc b/gcc/varasm.cc
index 6d93fe97d7bf..eddfb6a3524d 100644
--- a/gcc/varasm.cc
+++ b/gcc/varasm.cc
@@ -5827,10 +5827,13 @@ output_constructor_regular_field (oc_local_state *local)
 	     and the FE splits them into dynamic initialization.  */
 	  gcc_checking_assert (fieldsize >= fldsize);
 	  /* Given a non-empty initialization, this field had better
-	     be last.  Given a flexible array member, the next field
-	     on the chain is a TYPE_DECL of the enclosing struct.  */
+	     be last except in unions.  Given a flexible array member, the next
+	     field on the chain is a TYPE_DECL of the enclosing struct.  */
 	  const_tree next = DECL_CHAIN (local->field);
-	  gcc_assert (!fieldsize || !next || TREE_CODE (next) != FIELD_DECL);
+	  gcc_assert (!fieldsize
+		      || !next
+		      || TREE_CODE (next) != FIELD_DECL
+		      || TREE_CODE (local->type) == UNION_TYPE);
 	}
       else
 	fieldsize = tree_to_uhwi (DECL_SIZE_UNIT (local->field));
-- 
GitLab