]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
c: GNU extension allowing compound literals of variable size
authorMartin Uecker <uecker@tugraz.at>
Sun, 5 Oct 2025 18:53:43 +0000 (20:53 +0200)
committerMartin Uecker <uecker@gcc.gnu.org>
Sat, 1 Nov 2025 07:00:45 +0000 (08:00 +0100)
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/ChangeLog:
* doc/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
gcc/c/c-parser.cc
gcc/c/c-typeck.cc
gcc/doc/extend.texi
gcc/testsuite/gcc.dg/gnu-compoundlit-1.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/gnu-compoundlit-2.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/pr68090.c
gcc/testsuite/gcc.dg/vla-init-4.c
gcc/testsuite/gcc.dg/vla-init-5.c

index 2b31a4328f87787dd846dd78d63db63bdf19ef0b..1e1da2d2a35f4593d6322874f2e456f10c151dd1 100644 (file)
@@ -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;
index a559a4e6a060bdd0fd97789ed49599d061701a34..9b3a7861dfae8413119f3a6d56f1fdaf20bc8054 100644 (file)
@@ -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 ();
index eba57afac095385d96000268da49c118614fde36..2cef4636bd71ea03c05e4d2603050938817ca76a 100644 (file)
@@ -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");
index 8aaedaeb3b30077d79d2a5503d76c312969e485d..30eae4bdacce4c08f561cb224d613b67a72d6dee 100644 (file)
@@ -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 (file)
index 0000000..a7f3496
--- /dev/null
@@ -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 (file)
index 0000000..dcc5775
--- /dev/null
@@ -0,0 +1,20 @@
+/* { dg-do run } */
+/* { 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;
+}
+
index 87b3b93ed0352623de617b9d161e3805b1ed69bc..84e0ca40bbf3496fbc3773f888e99e60918ba093 100644 (file)
@@ -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 } */
 }
index 06351d04e0341fdb14904e0fe13e3003386259cc..7d1aa5bff6a921d2b56984a9ecf8da749bb5e978 100644 (file)
@@ -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" } */
index aa9f4910ac9630a2cc238b59c33d6e4434c04a20..2c249ecda8d12780cae80bb6e8ff4ce51884c20f 100644 (file)
@@ -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" } */