From: Joseph Myers Date: Fri, 26 Sep 2025 11:12:12 +0000 (+0000) Subject: c: Give permerror for excess braces in scalar initializers [PR88642] X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=1b876bdffd875ce061b35c7bb73f76a9bf7ec9f2;p=thirdparty%2Fgcc.git c: Give permerror for excess braces in scalar initializers [PR88642] As noted in bug 88642, the C front end fails to give errors or pedwarns for scalar initializers with too many levels of surrounding braces. There is a warning for redundant braces around a scalar initializer within a larger braced initializer (valid for a single such level within a structure, union or array initializer; not valid for more than one such level, or where the outer layer of braces is itself for a scalar, either redundant braces themselves or part of a compound literal), but this never becomes an error even for invalid cases. Check for this case and turn the warning into a permerror when there are more levels of braces than permitted. The existing warning is unchanged for a single (permitted) level of redundant braces around a scalar initializer inside a structure, union or array initializer, and it's also unchanged that no such warning is given for a single (permitted) level of redundant braces around a top-level scalar initializer. Technically this is a C2y issue (these rules on valid initializers moved into Constraints as a result of N3346, accepted in Minneapolis; previously, as a "shall" outside constraints, violating these rules resulted in compile-time undefined behavior without requiring a diagnostic). Hopefully little code is actually relying on not getting an error here. In view of gcc.dg/tree-ssa/ssa-dse-10.c showing that at least some code may be using such over-braced initializers (initializer of pubKeys at line 1167 in that test; I'm not at all sure how that initializer ends up getting interpreted to translate it to something equivalent but properly structured), this is made a permerror rather than a hard error, so -fpermissive (as already used by that test) can be used to disable the error (the default -fpermissive for old standards modes is not a problem given that before C2y this is undefined behavior not a constraint violation). Bootstrapped with no regressions for x86_64-pc-linux-gnu. PR c/88642 gcc/c/ * c-typeck.cc (constructor_braced_scalar): New variable. (struct constructor_stack): Add braced_scalar field. (really_start_incremental_init): Handle constructor_braced_scalar and braced_scalar field. (push_init_level): Handle constructor_braced_scalar and braced_scalar field. Give permerror rather than warning for nested braces around scalar initializer. (pop_init_level): Handle constructor_braced_scalar and braced_scalar field. gcc/testsuite/ * gcc.dg/c2y-init-1.c: New test. --- diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc index 6c807a2a792..b96215adc76 100644 --- a/gcc/c/c-typeck.cc +++ b/gcc/c/c-typeck.cc @@ -10132,6 +10132,10 @@ static int constructor_zeroinit; /* 1 if this constructor should have padding bits zeroed (C23 {}. */ static bool constructor_zero_padding_bits; +/* 1 if this constructor is a braced scalar initializer (further nested levels + of braces are an error). */ +static bool constructor_braced_scalar; + /* Structure for managing pending initializer elements, organized as an AVL tree. */ @@ -10203,6 +10207,7 @@ struct constructor_stack char incremental; char designated; bool zero_padding_bits; + bool braced_scalar; int designator_depth; }; @@ -10379,6 +10384,7 @@ really_start_incremental_init (tree type) p->incremental = constructor_incremental; p->designated = constructor_designated; p->zero_padding_bits = constructor_zero_padding_bits; + p->braced_scalar = constructor_braced_scalar; p->designator_depth = designator_depth; p->next = 0; constructor_stack = p; @@ -10394,6 +10400,7 @@ really_start_incremental_init (tree type) constructor_designated = 0; constructor_zero_padding_bits = false; constructor_zeroinit = 1; + constructor_braced_scalar = false; designator_depth = 0; designator_erroneous = 0; @@ -10453,6 +10460,7 @@ really_start_incremental_init (tree type) /* Handle the case of int x = {5}; */ constructor_fields = constructor_type; constructor_unfilled_fields = constructor_type; + constructor_braced_scalar = true; } } @@ -10529,6 +10537,7 @@ push_init_level (location_t loc, int implicit, p->incremental = constructor_incremental; p->designated = constructor_designated; p->zero_padding_bits = constructor_zero_padding_bits; + p->braced_scalar = constructor_braced_scalar; p->designator_depth = designator_depth; p->next = constructor_stack; p->range_stack = 0; @@ -10546,6 +10555,7 @@ push_init_level (location_t loc, int implicit, /* If the upper initializer has padding bits zeroed, that includes all nested initializers as well. */ constructor_zero_padding_bits = p->zero_padding_bits; + constructor_braced_scalar = false; constructor_pending_elts = 0; if (!implicit) { @@ -10664,7 +10674,15 @@ push_init_level (location_t loc, int implicit, else { if (constructor_type != error_mark_node) - warning_init (input_location, 0, "braces around scalar initializer"); + { + if (p->braced_scalar) + permerror_init (input_location, 0, + "braces around scalar initializer"); + else + warning_init (input_location, 0, + "braces around scalar initializer"); + constructor_braced_scalar = true; + } constructor_fields = constructor_type; constructor_unfilled_fields = constructor_type; } @@ -10886,6 +10904,7 @@ pop_init_level (location_t loc, int implicit, constructor_incremental = p->incremental; constructor_designated = p->designated; constructor_zero_padding_bits = p->zero_padding_bits; + constructor_braced_scalar = p->braced_scalar; designator_depth = p->designator_depth; constructor_pending_elts = p->pending_elts; constructor_depth = p->depth; diff --git a/gcc/testsuite/gcc.dg/c2y-init-1.c b/gcc/testsuite/gcc.dg/c2y-init-1.c new file mode 100644 index 00000000000..2fa200d283f --- /dev/null +++ b/gcc/testsuite/gcc.dg/c2y-init-1.c @@ -0,0 +1,48 @@ +/* Test invalid initializers that are consistent with the syntax: undefined + behavior ("shall" in Semantics not Constraints) before C2y, constraint + violation in C2y. Scalar cases; see bug 88642. */ +/* { dg-do compile } */ +/* { dg-options "-std=c2y -pedantic-errors" } */ + +struct s { int a; }; +union u { int a; }; + +int i1 = { 1, 2 }; /* { dg-error "excess elements in scalar initializer" } */ +int i2 = { { 1 } }; /* { dg-error "braces around scalar initializer" } */ +int i3 = { { 1, } }; /* { dg-error "braces around scalar initializer" } */ +int i4 = { { 1 }, }; /* { dg-error "braces around scalar initializer" } */ +int i5 = { 1, { } }; /* { dg-error "excess elements in scalar initializer" } */ +/* { dg-error "braces around scalar initializer" "braces" { target *-*-* } .-1 } */ +int i6 = { { } }; /* { dg-error "braces around scalar initializer" } */ +int i7 = { { }, }; /* { dg-error "braces around scalar initializer" } */ +int i8 = { { { 1 } } }; /* { dg-error "braces around scalar initializer" } */ +struct s s1 = + { + { /* { dg-warning "braces around scalar initializer" } */ + { 1 } /* { dg-error "braces around scalar initializer" } */ + } + }; +union u u1 = + { + { /* { dg-warning "braces around scalar initializer" } */ + { 1 } /* { dg-error "braces around scalar initializer" } */ + } + }; +int a1[1] = + { + { /* { dg-warning "braces around scalar initializer" } */ + { 1 } /* { dg-error "braces around scalar initializer" } */ + } + }; +int *p1 = &(int) { { 1 } }; /* { dg-error "braces around scalar initializer" } */ +int *p2 = &(int) { { 1, } }; /* { dg-error "braces around scalar initializer" } */ + +int ok1 = { 1 }; +struct s ok2 = { { 1 } }; /* { dg-warning "braces around scalar initializer" } */ +struct s ok3 = { { 1, } }; /* { dg-warning "braces around scalar initializer" } */ +int *ok4 = &(int) { 1 }; +int *ok5 = &(int) { 1, }; +int ok6[1] = { { 1 } }; /* { dg-warning "braces around scalar initializer" } */ +int ok7[1] = { { 1, } }; /* { dg-warning "braces around scalar initializer" } */ +union u ok8 = { { 1 } }; /* { dg-warning "braces around scalar initializer" } */ +union u ok9 = { { 1, } }; /* { dg-warning "braces around scalar initializer" } */