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