Bootstrapped and regression tested on x86_64.


This patch implements a GNU extension by allowing compound
literals to be VLAs which then can be initialized with
an empty initializer.  This addresses a use case where
one would now need to use alloca, but this also has limitations
(e.g. allocated memory accumulates in a loop).  The error for a
compound literal with variable size is changed to a pedwarn, and
a new error for static and constexpr is added.

gcc/c/ChangeLog:
        * c-decl.cc (build_compound_literal): Add error.
        * c-parser.cc (c_parser_braced_init): Take bool argument for
        variable size instead of DECL.
        (c_parser_initializer,c_parser_initval): Adapt.
        (c_parser_postfix_expression_after_paren_type): Change
        error to pedwarn.
        * c-typeck.cc (process_init_element): Add error for
        variable-size compound literal with static or constexpr.

gcc/doc/ChangeLog:
        * extend.texi: Document new extension.

gcc/testsuite/ChangeLog:
        * gcc.dg/gnu-compoundlit-1.c: New test.
        * gcc.dg/gnu-compoundlit-2.c: New test.
        * gcc.dg/pr68090.c: Adapt.
        * gcc.dg/vla-init-4.c: Adapt.
        * gcc.dg/vla-init-5.c: Adapt.
---
 gcc/c/c-decl.cc                          |  4 ++++
 gcc/c/c-parser.cc                        | 23 ++++++++++++---------
 gcc/c/c-typeck.cc                        |  4 +++-
 gcc/doc/extend.texi                      |  8 ++++++++
 gcc/testsuite/gcc.dg/gnu-compoundlit-1.c | 26 ++++++++++++++++++++++++
 gcc/testsuite/gcc.dg/gnu-compoundlit-2.c | 20 ++++++++++++++++++
 gcc/testsuite/gcc.dg/pr68090.c           |  7 ++++++-
 gcc/testsuite/gcc.dg/vla-init-4.c        |  2 +-
 gcc/testsuite/gcc.dg/vla-init-5.c        |  2 +-
 9 files changed, 82 insertions(+), 14 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/gnu-compoundlit-1.c
 create mode 100644 gcc/testsuite/gcc.dg/gnu-compoundlit-2.c

diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index 2b31a4328f8..1e1da2d2a35 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -6536,6 +6536,10 @@ build_compound_literal (location_t loc, tree type, tree 
init, bool non_const,
       return error_mark_node;
     }
 
+  if ((TREE_STATIC (decl) || C_DECL_DECLARED_CONSTEXPR (decl))
+      && C_TYPE_VARIABLE_SIZE (type))
+    error_at (loc, "storage size isn%'t constant");
+
   if (TREE_STATIC (decl)
       && !verify_type_context (loc, TCTX_STATIC_STORAGE, type))
     return error_mark_node;
diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc
index a559a4e6a06..9b3a7861dfa 100644
--- a/gcc/c/c-parser.cc
+++ b/gcc/c/c-parser.cc
@@ -1767,7 +1767,7 @@ static tree c_parser_simple_asm_expr (c_parser *);
 static tree c_parser_gnu_attributes (c_parser *);
 static struct c_expr c_parser_initializer (c_parser *, tree);
 static struct c_expr c_parser_braced_init (c_parser *, tree, bool,
-                                          struct obstack *, tree);
+                                          struct obstack *, bool);
 static void c_parser_initelt (c_parser *, struct obstack *);
 static void c_parser_initval (c_parser *, struct c_expr *,
                              struct obstack *);
@@ -6459,7 +6459,9 @@ static struct c_expr
 c_parser_initializer (c_parser *parser, tree decl)
 {
   if (c_parser_next_token_is (parser, CPP_OPEN_BRACE))
-    return c_parser_braced_init (parser, NULL_TREE, false, NULL, decl);
+    return c_parser_braced_init (parser, NULL_TREE, false, NULL,
+                                decl != error_mark_node
+                                && C_DECL_VARIABLE_SIZE (decl));
   else
     {
       struct c_expr ret;
@@ -6499,12 +6501,12 @@ location_t last_init_list_comma;
    compound literal, and NULL_TREE for other initializers and for
    nested braced lists.  NESTED_P is true for nested braced lists,
    false for the list of a compound literal or the list that is the
-   top-level initializer in a declaration.  DECL is the declaration for
-   the top-level initializer for a declaration, otherwise NULL_TREE.  */
+   top-level initializer in a declaration.  VARSIZE_P indicates
+   wether the object to be initialized has a variable size.  */
 
 static struct c_expr
 c_parser_braced_init (c_parser *parser, tree type, bool nested_p,
-                     struct obstack *outer_obstack, tree decl)
+                     struct obstack *outer_obstack, bool varsize_p)
 {
   struct c_expr ret;
   struct obstack braced_init_obstack;
@@ -6532,7 +6534,7 @@ c_parser_braced_init (c_parser *parser, tree type, bool 
nested_p,
     }
   else
     {
-      if (decl && decl != error_mark_node && C_DECL_VARIABLE_SIZE (decl))
+      if (varsize_p)
        error_at (brace_loc,
                  "variable-sized object may not be initialized except "
                  "with an empty initializer");
@@ -6826,7 +6828,7 @@ c_parser_initval (c_parser *parser, struct c_expr *after,
 
   if (c_parser_next_token_is (parser, CPP_OPEN_BRACE) && !after)
     init = c_parser_braced_init (parser, NULL_TREE, true,
-                                braced_init_obstack, NULL_TREE);
+                                braced_init_obstack, false);
   else
     {
       init = c_parser_expr_no_commas (parser, after);
@@ -13510,10 +13512,11 @@ c_parser_postfix_expression_after_paren_type 
(c_parser *parser,
               || (scspecs && scspecs->storage_class == csc_static)
               || constexpr_p), constexpr_p, &richloc);
   type = groktypename (type_name, &type_expr, &type_expr_const);
+  bool varsize_p = false;
   if (type != error_mark_node && C_TYPE_VARIABLE_SIZE (type))
     {
-      error_at (type_loc, "compound literal has variable size");
-      type = error_mark_node;
+      pedwarn (type_loc, OPT_Wpedantic, "compound literal has variable size");
+      varsize_p = true;
     }
   else if (TREE_CODE (type) == FUNCTION_TYPE)
     {
@@ -13538,7 +13541,7 @@ c_parser_postfix_expression_after_paren_type (c_parser 
*parser,
                                     (TYPE_QUALS (type_no_array)
                                      | TYPE_QUAL_CONST));
     }
-  init = c_parser_braced_init (parser, type, false, NULL, NULL_TREE);
+  init = c_parser_braced_init (parser, type, false, NULL, varsize_p);
   if (constexpr_p)
     finish_underspecified_init (NULL_TREE, underspec_state);
   finish_init ();
diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index b965f9ee416..3feba9aaee8 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -12734,7 +12734,9 @@ retry:
 
          if (constructor_max_index != NULL_TREE
              && (tree_int_cst_lt (constructor_max_index, constructor_index)
-                 || integer_all_onesp (constructor_max_index)))
+                 || integer_all_onesp (constructor_max_index))
+             /* For VLA we got an error already.  */
+             && !C_TYPE_VARIABLE_SIZE (constructor_type))
            {
              pedwarn_init (loc, 0,
                            "excess elements in array initializer");
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 8aaedaeb3b3..30eae4bdacc 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -1131,6 +1131,14 @@ such an initializer, as shown here:
 char **foo = (char *[]) @{ "x", "y", "z" @};
 @end smallexample
 
+As a GNU extension, GCC allows compound literals with a variable size.
+In this case, only empty initialization is allowed.
+
+@smallexample
+int n = 4;
+char (*p)[n] = &(char[n])@{ @};
+@end smallexample
+
 Compound literals for scalar types and union types are also allowed.  In
 the following example the variable @code{i} is initialized to the value
 @code{2}, the result of incrementing the unnamed object created by
diff --git a/gcc/testsuite/gcc.dg/gnu-compoundlit-1.c 
b/gcc/testsuite/gcc.dg/gnu-compoundlit-1.c
new file mode 100644
index 00000000000..a7f34967163
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/gnu-compoundlit-1.c
@@ -0,0 +1,26 @@
+/* { dg-do compile } */
+/* { dg-options "-std=gnu23" } */
+
+int g(int n, int (*p)[n]);
+int f(int n)
+{
+       return g(n, &(int[n]){ });
+}
+
+void h(int n)
+{
+       (int[n]){ 1 };          /* { dg-error "empty initializer" } */
+}
+
+void i(int n)
+{
+       (static int[3]){ };
+       (static int[n]){ };     /* { dg-error "storage size" } */
+       (constexpr int[3]){ };
+       (constexpr int[n]){ };  /* { dg-error "storage size" } */
+       (register int[3]){ };   /* { dg-error "register" } */
+       (register int[n]){ };   /* { dg-error "register" } */
+       (_Thread_local int[3]){ };      /* { dg-error "_Thread_local" } */
+       (_Thread_local int[n]){ };      /* { dg-error "_Thread_local" } */
+}
+
diff --git a/gcc/testsuite/gcc.dg/gnu-compoundlit-2.c 
b/gcc/testsuite/gcc.dg/gnu-compoundlit-2.c
new file mode 100644
index 00000000000..56f7c932e6e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/gnu-compoundlit-2.c
@@ -0,0 +1,20 @@
+/* { dg-do compile } */
+/* { dg-options "-std=gnu23 -Wall" } */
+
+[[gnu::noinline,gnu::noipa]]
+static bool f(int n)
+{
+       struct foo { char a[n]; };
+       struct foo x = { };
+
+       return 0 == __builtin_memcmp(&x, &(struct foo){ }, sizeof x);
+}
+
+int main()
+{
+       if (!f(7))
+               __builtin_abort();
+
+       return 0;
+}
+
diff --git a/gcc/testsuite/gcc.dg/pr68090.c b/gcc/testsuite/gcc.dg/pr68090.c
index 87b3b93ed03..84e0ca40bbf 100644
--- a/gcc/testsuite/gcc.dg/pr68090.c
+++ b/gcc/testsuite/gcc.dg/pr68090.c
@@ -1,13 +1,18 @@
 /* PR c/68090 */
 /* { dg-do compile } */
-/* { dg-options "" } */
+/* { dg-options "--pedantic-error" } */
 
 void
 fn (int i)
 {
   (int[(0, 1)]) { 0 }; /* { dg-error "compound literal has variable size" } */
+                       /* { dg-error "variable-size" "" { target *-*-* } .-1 } 
*/
   (int[i]) { 0 }; /* { dg-error "compound literal has variable size" } */
+                       /* { dg-error "variable-size" "" { target *-*-* } .-1 } 
*/
   (int[(0, i)]) { 0 }; /* { dg-error "compound literal has variable size" } */
+                       /* { dg-error "variable-size" "" { target *-*-* } .-1 } 
*/
   (int [][i]){ 0 }; /* { dg-error "compound literal has variable size" } */
+                       /* { dg-error "variable-size" "" { target *-*-* } .-1 } 
*/
   (int [][(1, 2)]){ 0 }; /* { dg-error "compound literal has variable size" } 
*/
+                       /* { dg-error "variable-size" "" { target *-*-* } .-1 } 
*/
 }
diff --git a/gcc/testsuite/gcc.dg/vla-init-4.c 
b/gcc/testsuite/gcc.dg/vla-init-4.c
index 06351d04e03..7d1aa5bff6a 100644
--- a/gcc/testsuite/gcc.dg/vla-init-4.c
+++ b/gcc/testsuite/gcc.dg/vla-init-4.c
@@ -4,4 +4,4 @@
 /* { dg-options "" } */
 
 const int i = 1;
-void foo() { char *p = (char [i]){ "" }; } /* { dg-error "compound literal has 
variable size" } */
+void foo() { char *p = (char [i]){ "" }; } /* { dg-error "variable-sized 
object" } */
diff --git a/gcc/testsuite/gcc.dg/vla-init-5.c 
b/gcc/testsuite/gcc.dg/vla-init-5.c
index aa9f4910ac9..2c249ecda8d 100644
--- a/gcc/testsuite/gcc.dg/vla-init-5.c
+++ b/gcc/testsuite/gcc.dg/vla-init-5.c
@@ -4,4 +4,4 @@
 /* { dg-options "" } */
 
 const int i = 1;
-void foo() { void *p = (char [][i]){ "" }; } /* { dg-error "compound literal 
has variable size" } */
+void foo() { void *p = (char [][i]){ "" }; } /* { dg-error "variable-sized 
object" } */
-- 
2.47.3

Reply via email to