https://gcc.gnu.org/g:bb63537088d755ad62b64887bd537d1ed84e823d
commit r16-4920-gbb63537088d755ad62b64887bd537d1ed84e823d Author: Martin Uecker <[email protected]> Date: Sun Oct 5 20:53:43 2025 +0200 c: GNU extension allowing compound literals of variable size 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. Diff: --- 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(-) diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc index 2b31a4328f87..1e1da2d2a35f 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 a559a4e6a060..9b3a7861dfae 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 eba57afac095..2cef4636bd71 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 8aaedaeb3b30..30eae4bdacce 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 000000000000..a7f349671633 --- /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 000000000000..dcc57753077f --- /dev/null +++ b/gcc/testsuite/gcc.dg/gnu-compoundlit-2.c @@ -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; +} + diff --git a/gcc/testsuite/gcc.dg/pr68090.c b/gcc/testsuite/gcc.dg/pr68090.c index 87b3b93ed035..84e0ca40bbf3 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 06351d04e034..7d1aa5bff6a9 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 aa9f4910ac96..2c249ecda8d1 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" } */
