]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
c: Give permerror for excess braces in scalar initializers [PR88642]
authorJoseph Myers <josmyers@redhat.com>
Fri, 26 Sep 2025 11:12:12 +0000 (11:12 +0000)
committerJoseph Myers <josmyers@redhat.com>
Fri, 26 Sep 2025 11:12:12 +0000 (11:12 +0000)
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.

gcc/c/c-typeck.cc
gcc/testsuite/gcc.dg/c2y-init-1.c [new file with mode: 0644]

index 6c807a2a7927aa9c923699a9b4e7955299406e90..b96215adc768604faa909f31e6df6a4b4fee4645 100644 (file)
@@ -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;
     }
 }
 \f
@@ -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 (file)
index 0000000..2fa200d
--- /dev/null
@@ -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" } */