Skip to content

Commit ee7eb18

Browse files
GNU and Clang C++ mode fix for members skipped by a designated initializer [GH edgcpp#260]
1 parent d6f8317 commit ee7eb18

16 files changed

Lines changed: 238 additions & 143 deletions

File tree

‎src/Changes‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
11
Version 7.1
22

3+
10/9/26 [GH #260]
4+
GNU and Clang C++ modes: members skipped by a designated initializer
5+
6+
In GNU C++ modes before C++20 and in all Clang C++ modes, a member that was
7+
not designated, but that precedes a designated member, was zero-initialized
8+
instead of being initialized from its default member initializer. That is
9+
now fixed. For example, with --gnu_version=170100:
10+
11+
struct P { int x = 1, y = 2, z = 3; };
12+
P p = { .x = 10, .z = 30 }; // p.y was previously 0. It is now 2.
13+
14+
315
10/9/26 [GH #27]
416
Clearer diagnostic when auto is deduced from an incomplete type
517

‎src/cp_gen_be.c‎

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8305,6 +8305,25 @@ one step instead of class-by-class, return TRUE.
83058305
} /* pm_cast_is_unambiguous */
83068306

83078307

8308+
static a_constant_ptr first_explicit_aggr_constant(a_constant_ptr con)
8309+
/*
8310+
con is NULL or a constant in the list of an aggregate constant. Return the
8311+
first constant in that list, starting with con, that is not an implicit
8312+
initializer for a base class or member (such initializers do not appear in
8313+
the generated source), or NULL if there is none.
8314+
*/
8315+
{
8316+
while (con != NULL &&
8317+
(con->constant_for_base_class_from_constexpr_folding ||
8318+
con->implicit_aggr_element ||
8319+
(constant_is(con, ck_dynamic_init) &&
8320+
is_default_dynamic_init(con->variant.dynamic_init.ptr)))) {
8321+
con = con->next;
8322+
} /* while */
8323+
return con;
8324+
} /* first_explicit_aggr_constant */
8325+
8326+
83088327
static void gen_designator(a_constant_ptr con,
83098328
a_field_ptr *field,
83108329
a_constant_ptr *p_eff_con,
@@ -8388,12 +8407,13 @@ brace/parenthesis delimiters around it should be suppressed.
83888407
if (!eff_con->explicit_braces_on_aggregate &&
83898408
!eff_con->explicit_parentheses_on_aggregate &&
83908409
!eff_con->explicit_cast_applied) {
8410+
a_constant_ptr first_con = eff_con->variant.aggregate.first_constant;
8411+
first_con = first_explicit_aggr_constant(first_con);
83918412
*suppress_delims = TRUE;
8392-
if (eff_con->variant.aggregate.first_constant != NULL &&
8393-
constant_is(eff_con->variant.aggregate.first_constant,
8394-
ck_designator)) {
8413+
if (first_con != NULL && constant_is(first_con, ck_designator)) {
83958414
/* Do not close the designator if another one will follow
8396-
immediately. */
8415+
immediately (i.e., a chained designator, possibly preceded by
8416+
implicit initializers for the members it skips). */
83978417
close = FALSE;
83988418
} /* if */
83998419
} /* if */
@@ -8603,13 +8623,7 @@ which constant is the value.
86038623
/* Loop through the list of initializer constants. */
86048624
/* First, skip over any implicit initializers for base classes and
86058625
initial members; they shouldn't appear in the generated source. */
8606-
for (sub_con = first_con;
8607-
sub_con != NULL &&
8608-
(sub_con->constant_for_base_class_from_constexpr_folding ||
8609-
sub_con->implicit_aggr_element ||
8610-
(sub_con->kind == (a_constant_repr_kind)ck_dynamic_init &&
8611-
is_default_dynamic_init(sub_con->variant.dynamic_init.ptr)));
8612-
sub_con = sub_con->next) {}
8626+
sub_con = first_explicit_aggr_constant(first_con);
86138627
if (sub_con != NULL &&
86148628
sub_con->kind == (a_constant_repr_kind)ck_init_repeat) {
86158629
/* A ck_init_repeat constant is used to do default initialization

‎src/decl_inits.c‎

Lines changed: 44 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3319,6 +3319,33 @@ Return TRUE if field second follows field first in the declaration order.
33193319
} /* fields_are_ordered */
33203320

33213321

3322+
static a_boolean field_range_is_designated(a_constant_ptr aggr_con,
3323+
a_field_ptr first,
3324+
a_field_ptr end)
3325+
/*
3326+
Return TRUE if a ck_designator constant in the aggregate constant aggr_con
3327+
designates field first or a field declared between first and end (excluding
3328+
end itself). That is possible only when out-of-order designators are accepted:
3329+
E.g., in "{ .c = 3, .a = 1, .d = 4 }", c lies between b (the field following
3330+
a) and d.
3331+
*/
3332+
{
3333+
a_boolean result = FALSE;
3334+
a_constant_ptr con;
3335+
3336+
for (con = aggr_con->variant.aggregate.first_constant;
3337+
con != NULL && !result;
3338+
con = con->next) {
3339+
if (constant_is(con, ck_designator) &&
3340+
con->variant.designator.is_field_designator) {
3341+
a_field_ptr fp = con->variant.designator.variant.field;
3342+
result = fields_are_ordered(first, fp) && !fields_are_ordered(end, fp);
3343+
} /* if */
3344+
} /* for */
3345+
return result;
3346+
} /* field_range_is_designated */
3347+
3348+
33223349
static void aggr_init_field_designator(an_init_component_ptr *p_icp,
33233350
a_type_ptr class_type,
33243351
an_init_state *is,
@@ -3561,18 +3588,23 @@ initialization. */
35613588
} /* if */
35623589
if (okay) {
35633590
if ((orig_field != *field || *p_bcp != NULL ) &&
3564-
cpp20_designators_restriction && !is->init_error &&
3565-
!is->check_validity_only && orig_field != NULL &&
3566-
!type_is(class_type, tk_union)) {
3567-
/* C++20 designators can cause base classes and certain members to
3568-
be skipped. Initialize those members before initializing the
3569-
designated member. If we found an error, we shouldn't proceed with
3570-
the initialization of remaining members, as the designators
3571-
may not be in order. If orig_field is NULL, we have already
3572-
initialized all the members and this designator is invalid. It is
3573-
possible that it has not been diagnosed as invalid yet, so we
3574-
check orig_field here just in case. If we're checking validity only,
3575-
there's no need to initialize the remainder. */
3591+
!is->init_error && !is->check_validity_only && orig_field != NULL &&
3592+
!type_is(class_type, tk_union) &&
3593+
(cpp20_designators_restriction || gpp_version_is(>= 80000) ||
3594+
(clang_mode && !C_mode() && fields_are_ordered(orig_field, *field) &&
3595+
!field_range_is_designated(aggr_con, orig_field, *field)))) {
3596+
/* C++ designators can cause base classes and certain members to
3597+
be skipped. Initialize those members (e.g., using their default member
3598+
initializers) before initializing the designated member. If we found
3599+
an error, we shouldn't proceed with the initialization of remaining
3600+
members, as the designators may not be in order. If orig_field is
3601+
NULL, we have already initialized all the members and this designator
3602+
is invalid. It is possible that it has not been diagnosed as invalid
3603+
yet, so we check orig_field here just in case. If we're checking
3604+
validity only, there's no need to initialize the remainder. Clang
3605+
accepts out-of-order designators (with a warning), so in that mode the
3606+
skipped members are initialized only if the designator does not go
3607+
backward and none of them was designated earlier. */
35763608
aggr_init_class_remainder_if_needed(aggr_con, class_type, orig_field,
35773609
*p_bcp, is, diag_pos, *field);
35783610
*p_bcp = NULL;

‎tests/expectations/edg_x86_64/imported.log‎

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1424,9 +1424,6 @@ clang/cpp/SemaCXX/unavailable_aligned_allocation.sft.cpp:fn:-DTEST_NUMBER=7 --c+
14241424
clang/cpp/SemaCXX/unavailable_aligned_allocation.sft.cpp:fn:-DTEST_NUMBER=8 --c++17 -DWATCHOS -w --exceptions:FAIL:
14251425
clang/cpp/SemaCXX/unavailable_aligned_allocation.sft.cpp:fn:-DTEST_NUMBER=9 --c++17 -DNO_ERRORS --exceptions:FAIL:
14261426
clang/cpp/SemaCXX/undefined-inline.sft.cpp:fp:-DTEST_NUMBER=1 -w --c++11:FAIL:
1427-
clang/cpp/SemaCXX/uninitialized.sft.cpp:fp:-DTEST_NUMBER=1 --c++17:ABORT:
1428-
clang/cpp/SemaCXX/uninitialized.sft.cpp:fp:-DTEST_NUMBER=2 --c++17:ABORT:
1429-
clang/cpp/SemaCXX/uninitialized.sft.cpp:fp:-DTEST_NUMBER=3 --c++20:ABORT:
14301427
clang/cpp/SemaCXX/unqualified-std-call-fixits.sft.cpp:fp:-DTEST_NUMBER=1 --c++20:FAIL:
14311428
clang/cpp/SemaCXX/unqualified-std-call-fixits.sft.cpp:fp:-DTEST_NUMBER=2 --c++20:FAIL:
14321429
clang/cpp/SemaCXX/unqualified-std-call-fixits.sft.cpp:fp:-DTEST_NUMBER=3 --c++20:FAIL:
@@ -7195,7 +7192,6 @@ gnu/cpp/diagnostic/alignof3.sft.cpp:fn:-DTEST_NUMBER=1:FAIL:
71957192
gnu/cpp/diagnostic/auto1.sft.cpp:fp:-DTEST_NUMBER=1 --c++17:FAIL:
71967193
gnu/cpp/diagnostic/bad-binary-ops-highlight-colors.sft.cpp:fp:-DTEST_NUMBER=1:FAIL:
71977194
gnu/cpp/diagnostic/bad-binary-ops-no-highlight-colors.sft.cpp:fp:-DTEST_NUMBER=1:FAIL:
7198-
gnu/cpp/diagnostic/base.sft.cpp:fp:-DTEST_NUMBER=1 --c++17:ABORT:
71997195
gnu/cpp/diagnostic/bindings1.sft.cpp:rp:-DTEST_NUMBER=1:FAIL:
72007196
gnu/cpp/diagnostic/constexpr3.sft.cpp:fn:-DTEST_NUMBER=1 --c++14:ABORT:
72017197
gnu/cpp/diagnostic/constexpr4.sft.cpp:fn:-DTEST_NUMBER=1 --c++11:FAIL:

‎tests/tests/imported/clang/cpp/CXX/dcl/dcl.init/.aggr.rto/default.1.1.txt‎

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,6 @@ fe_only -DTEST_NUMBER=1 --clang -tused -e 200 --no_wrap_diagnostics --clang_vers
33
static constexpr C c2 = { .a = 1.0, .x = 3 };
44
^
55

6-
"Test_name.c", line 86: error: static assertion failed
7-
static_assert(a.a.d == string{}.d);
8-
^
9-
"Test_name.c", line 86: note: the final comparison was 0 == 43
10-
static_assert(a.a.d == string{}.d);
11-
^
12-
13-
"Test_name.c", line 87: error: static assertion failed
14-
static_assert(a.b == 42);
15-
^
16-
"Test_name.c", line 87: note: the final comparison was 0 == 42
17-
static_assert(a.b == 42);
18-
^
19-
206
"Test_name.c", line 112: error: an initializer cannot be specified for a flexible array member
217
int y[] = { 0 };
228
^
@@ -53,4 +39,4 @@ fe_only -DTEST_NUMBER=1 --clang -tused -e 200 --no_wrap_diagnostics --clang_vers
5339
constexpr S2 y[2] = {
5440
^
5541

56-
9 errors detected in the compilation of "Test_name.c".
42+
7 errors detected in the compilation of "Test_name.c".

‎tests/tests/imported/clang/cpp/SemaCXX/.designated-initializers.rto/default.1.1.txt‎

Lines changed: 78 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,17 @@
11
fe_only -DTEST_NUMBER=1 --clang -tused -e 200 --no_wrap_diagnostics --clang_version 999999 --c++11 Test_name.c
2+
"Test_name.c", line 48: error: no instance of constructor "Reorder::X::X" matches the argument list
3+
foo n = {.y = 4, .x = 5};
4+
^
5+
"Test_name.c", line 37: note: number of parameters of function "Reorder::X::X(const Reorder::X &)" does not match the call
6+
struct X {
7+
^
8+
"Test_name.c", line 37: note: number of parameters of function "Reorder::X::X(Reorder::X &&)" does not match the call
9+
struct X {
10+
^
11+
"Test_name.c", line 38: note: number of parameters of function "Reorder::X::X(int)" does not match the call
12+
X(int n);
13+
^
14+
215
"Test_name.c", line 48: warning: out-of-order initializers are nonstandard in C++
316
foo n = {.y = 4, .x = 5};
417
^
@@ -35,6 +48,10 @@ fe_only -DTEST_NUMBER=1 --clang -tused -e 200 --no_wrap_diagnostics --clang_vers
3548
S s1 = {
3649
^
3750

51+
"Test_name.c", line 90: error: variable "Reorder3::s2" has an uninitialized const or reference member
52+
S s2 = {
53+
^
54+
3855
"Test_name.c", line 94: warning: out-of-order initializers are nonstandard in C++
3956
.b = s3.a, .a = 1,
4057
^
@@ -43,6 +60,20 @@ fe_only -DTEST_NUMBER=1 --clang -tused -e 200 --no_wrap_diagnostics --clang_vers
4360
S s3 = {
4461
^
4562

63+
"Test_name.c", line 101: error: a class or namespace qualified name is required
64+
template<typename T> struct Error { using type = typename T::type; }; // expected-error 3{{'::'}}
65+
^
66+
detected during:
67+
instantiation of class "NoCheckingFilledHoles::Error<T> [with T=int [1]]" at line 105
68+
instantiation of "NoCheckingFilledHoles::DefaultInitIsError<N>::DefaultInitIsError(NoCheckingFilledHoles::Error<int [N]>) [with N=1]" at line 119
69+
70+
"Test_name.c", line 101: error: expected a ";"
71+
template<typename T> struct Error { using type = typename T::type; }; // expected-error 3{{'::'}}
72+
^
73+
detected during:
74+
instantiation of class "NoCheckingFilledHoles::Error<T> [with T=int [1]]" at line 105
75+
instantiation of "NoCheckingFilledHoles::DefaultInitIsError<N>::DefaultInitIsError(NoCheckingFilledHoles::Error<int [N]>) [with N=1]" at line 119
76+
4677
"Test_name.c", line 117: warning: out-of-order initializers are nonstandard in C++
4778
.a = 1,
4879
^
@@ -65,10 +96,6 @@ fe_only -DTEST_NUMBER=1 --clang -tused -e 200 --no_wrap_diagnostics --clang_vers
6596
instantiation of class "NoCheckingFilledHoles::Error<T> [with T=int [2]]" at line 105
6697
instantiation of "NoCheckingFilledHoles::DefaultInitIsError<N>::DefaultInitIsError(NoCheckingFilledHoles::Error<int [N]>) [with N=2]" at line 124
6798

68-
"Test_name.c", line 127: warning: out-of-order initializers are nonstandard in C++
69-
.a = 1
70-
^
71-
7299
"Test_name.c", line 101: error: a class or namespace qualified name is required
73100
template<typename T> struct Error { using type = typename T::type; }; // expected-error 3{{'::'}}
74101
^
@@ -83,14 +110,60 @@ fe_only -DTEST_NUMBER=1 --clang -tused -e 200 --no_wrap_diagnostics --clang_vers
83110
instantiation of class "NoCheckingFilledHoles::Error<T> [with T=int [3]]" at line 105
84111
instantiation of "NoCheckingFilledHoles::DefaultInitIsError<N>::DefaultInitIsError(NoCheckingFilledHoles::Error<int [N]>) [with N=3]" at line 128
85112

113+
"Test_name.c", line 127: warning: out-of-order initializers are nonstandard in C++
114+
.a = 1
115+
^
116+
117+
"Test_name.c", line 101: error: a class or namespace qualified name is required
118+
template<typename T> struct Error { using type = typename T::type; }; // expected-error 3{{'::'}}
119+
^
120+
detected during:
121+
instantiation of class "NoCheckingFilledHoles::Error<T> [with T=int [4]]" at line 105
122+
instantiation of "NoCheckingFilledHoles::DefaultInitIsError<N>::DefaultInitIsError(NoCheckingFilledHoles::Error<int [N]>) [with N=4]" at line 132
123+
124+
"Test_name.c", line 101: error: expected a ";"
125+
template<typename T> struct Error { using type = typename T::type; }; // expected-error 3{{'::'}}
126+
^
127+
detected during:
128+
instantiation of class "NoCheckingFilledHoles::Error<T> [with T=int [4]]" at line 105
129+
instantiation of "NoCheckingFilledHoles::DefaultInitIsError<N>::DefaultInitIsError(NoCheckingFilledHoles::Error<int [N]>) [with N=4]" at line 132
130+
86131
"Test_name.c", line 135: warning: out-of-order initializers are nonstandard in C++
87132
.a = 1,
88133
^
89134

135+
"Test_name.c", line 101: error: a class or namespace qualified name is required
136+
template<typename T> struct Error { using type = typename T::type; }; // expected-error 3{{'::'}}
137+
^
138+
detected during:
139+
instantiation of class "NoCheckingFilledHoles::Error<T> [with T=int [6]]" at line 105
140+
instantiation of "NoCheckingFilledHoles::DefaultInitIsError<N>::DefaultInitIsError(NoCheckingFilledHoles::Error<int [N]>) [with N=6]" at line 142
141+
142+
"Test_name.c", line 101: error: expected a ";"
143+
template<typename T> struct Error { using type = typename T::type; }; // expected-error 3{{'::'}}
144+
^
145+
detected during:
146+
instantiation of class "NoCheckingFilledHoles::Error<T> [with T=int [6]]" at line 105
147+
instantiation of "NoCheckingFilledHoles::DefaultInitIsError<N>::DefaultInitIsError(NoCheckingFilledHoles::Error<int [N]>) [with N=6]" at line 142
148+
90149
"Test_name.c", line 141: warning: out-of-order initializers are nonstandard in C++
91150
.e = {4, 4}
92151
^
93152

153+
"Test_name.c", line 101: error: a class or namespace qualified name is required
154+
template<typename T> struct Error { using type = typename T::type; }; // expected-error 3{{'::'}}
155+
^
156+
detected during:
157+
instantiation of class "NoCheckingFilledHoles::Error<T> [with T=int [7]]" at line 105
158+
instantiation of "NoCheckingFilledHoles::DefaultInitIsError<N>::DefaultInitIsError(NoCheckingFilledHoles::Error<int [N]>) [with N=7]" at line 148
159+
160+
"Test_name.c", line 101: error: expected a ";"
161+
template<typename T> struct Error { using type = typename T::type; }; // expected-error 3{{'::'}}
162+
^
163+
detected during:
164+
instantiation of class "NoCheckingFilledHoles::Error<T> [with T=int [7]]" at line 105
165+
instantiation of "NoCheckingFilledHoles::DefaultInitIsError<N>::DefaultInitIsError(NoCheckingFilledHoles::Error<int [N]>) [with N=7]" at line 148
166+
94167
"Test_name.c", line 101: error: a class or namespace qualified name is required
95168
template<typename T> struct Error { using type = typename T::type; }; // expected-error 3{{'::'}}
96169
^
@@ -119,4 +192,4 @@ fe_only -DTEST_NUMBER=1 --clang -tused -e 200 --no_wrap_diagnostics --clang_vers
119192
^
120193
detected during instantiation of "void Bar<T>::Test() [with T=bool]" at line 33
121194

122-
11 errors detected in the compilation of "Test_name.c".
195+
21 errors detected in the compilation of "Test_name.c".

0 commit comments

Comments
 (0)