diff --git a/src/Changes b/src/Changes index ef7a906913..abd217eed2 100644 --- a/src/Changes +++ b/src/Changes @@ -54,6 +54,20 @@ This is now fixed: When generating code targeting GCC, the C-generating back end now preserves the __weak__ attribute and suppresses the __always_inline__ attribute instead. +10/7/26 [GH #238] +Clang C compatibility: Empty aggregate initializers + +The changes for EDGcpfe/29042 (version 7.0) restricted empty initializers +in pre-C23 Clang C modes to Clang 17 and later. However, earlier versions +of Clang accept empty initializers for aggregate types as an extension. +This is now fixed. For example, the following was incorrectly rejected +when emulating Clang 16 and earlier: + + struct A { + int i; + }; + + struct A a = {}; // Previously an error. Now okay. 10/5/26 [GH #195] C++-generating back end: dependent multi-dimensional subscripts diff --git a/src/decl_inits.c b/src/decl_inits.c index 53eca54a4b..922e007f26 100644 --- a/src/decl_inits.c +++ b/src/decl_inits.c @@ -2365,7 +2365,7 @@ for this array initialization). *is describes the initialization as a whole. diag_pos = &icp->variant.braced.end_pos; /* Unwrap the braced list for the processing that follows. */ icp = icp->variant.braced.list; - if (icp == NULL && C_mode() && !empty_c_initializer_allowed) { + if (icp == NULL && C_mode() && !empty_c_aggregate_initializer_allowed) { /* Empty initializer lists are permitted only in the C modes that offer them. */ pos_error(ec_exp_primary_expr, diag_pos); @@ -3778,7 +3778,7 @@ issued if no more specific position is available. /* Unwrap the braced list for the processing that follows. */ icp = icp->variant.braced.list; top_icp = icp; - if (icp == NULL && C_mode() && !empty_c_initializer_allowed) { + if (icp == NULL && C_mode() && !empty_c_aggregate_initializer_allowed) { /* Empty initializer lists are permitted only in the C modes that offer them. */ pos_error(ec_exp_primary_expr, diag_pos); diff --git a/src/lang_feat.h b/src/lang_feat.h index cde404bf29..7b1eada8cb 100644 --- a/src/lang_feat.h +++ b/src/lang_feat.h @@ -1959,6 +1959,15 @@ by the C23 standard or later C standards. */ #define c23_mode (C_mode() && std_version >= 202311) +/* +Macro that is TRUE when an empty aggregate initializer is accepted in C +mode. Such initializers are standard in C23. They are also an extension in +the GNU C modes, and in the Clang C modes. +*/ +#define empty_c_aggregate_initializer_allowed (c23_mode || \ + gcc_version_is(any_version) || \ + clangc_version_is(any_version)) + /* Macro that is TRUE when an empty initializer ("int i = {};") is accepted in C mode. Such initializers are standard in C23. They are also an extension in diff --git a/tests/tests/regressions/.clang-empty-aggregate-initialization.rto/default.1.1.txt b/tests/tests/regressions/.clang-empty-aggregate-initialization.rto/default.1.1.txt new file mode 100644 index 0000000000..b99525ee22 --- /dev/null +++ b/tests/tests/regressions/.clang-empty-aggregate-initialization.rto/default.1.1.txt @@ -0,0 +1 @@ +fe_only -DTEST_NUMBER=1 --c11 --clang_version 30800 Test_name.c diff --git a/tests/tests/regressions/.clang-empty-scalar-initialization.rto/default.1.1.txt b/tests/tests/regressions/.clang-empty-scalar-initialization.rto/default.1.1.txt new file mode 100644 index 0000000000..ab7c1fe4ae --- /dev/null +++ b/tests/tests/regressions/.clang-empty-scalar-initialization.rto/default.1.1.txt @@ -0,0 +1,6 @@ +fe_only -DTEST_NUMBER=1 --c11 --clang_version 160000 Test_name.c +"Test_name.c", line 6: error: expected an expression + int scalar = {}; + ^ + +1 error detected in the compilation of "Test_name.c". diff --git a/tests/tests/regressions/.clang-empty-universal-initialization.rto/default.1.1.txt b/tests/tests/regressions/.clang-empty-universal-initialization.rto/default.1.1.txt new file mode 100644 index 0000000000..6f585a510b --- /dev/null +++ b/tests/tests/regressions/.clang-empty-universal-initialization.rto/default.1.1.txt @@ -0,0 +1,5 @@ +fe_only -DTEST_NUMBER=1 --c11 --clang_version 170000 Test_name.c +"Test_name.c", line 18: warning: variable "vla" was declared but never referenced + int vla[n] = {}; + ^ + diff --git a/tests/tests/regressions/.clang-empty-vla-initialization.rto/default.1.1.txt b/tests/tests/regressions/.clang-empty-vla-initialization.rto/default.1.1.txt new file mode 100644 index 0000000000..471dc6254c --- /dev/null +++ b/tests/tests/regressions/.clang-empty-vla-initialization.rto/default.1.1.txt @@ -0,0 +1,10 @@ +fe_only -DTEST_NUMBER=1 --c11 --clang_version 160000 Test_name.c +"Test_name.c", line 8: error: variable "vla" may not be initialized + int vla[n] = {}; + ^ + +"Test_name.c", line 8: warning: variable "vla" was declared but never referenced + int vla[n] = {}; + ^ + +1 error detected in the compilation of "Test_name.c". diff --git a/tests/tests/regressions/clang-empty-aggregate-initialization.sft.cpp b/tests/tests/regressions/clang-empty-aggregate-initialization.sft.cpp new file mode 100644 index 0000000000..cd3e2bead5 --- /dev/null +++ b/tests/tests/regressions/clang-empty-aggregate-initialization.sft.cpp @@ -0,0 +1,25 @@ +//type:fp +//options_all:--c11 --clang_version 30800 +// +// Clang accepts empty initializers for aggregate types as an extension in +// pre-C23 C modes. + +struct A { + int i; +}; + +union U { + int i; + double d; +}; + +struct B { + int i; + struct A a; +}; + +struct A a = {}; +union U u = {}; +int fixed_array[4] = {}; +int unknown_bound_array[] = {}; +struct B nested = { 1, {} }; diff --git a/tests/tests/regressions/clang-empty-scalar-initialization.sft.cpp b/tests/tests/regressions/clang-empty-scalar-initialization.sft.cpp new file mode 100644 index 0000000000..dbc3dfca39 --- /dev/null +++ b/tests/tests/regressions/clang-empty-scalar-initialization.sft.cpp @@ -0,0 +1,7 @@ +//type:fn +//options_all:--c11 --clang_version 160000 +// +// Clang versions before 17 do not accept accept empty scalar initializers. + +int scalar = {}; + diff --git a/tests/tests/regressions/clang-empty-universal-initialization.sft.cpp b/tests/tests/regressions/clang-empty-universal-initialization.sft.cpp new file mode 100644 index 0000000000..b45329b1c6 --- /dev/null +++ b/tests/tests/regressions/clang-empty-universal-initialization.sft.cpp @@ -0,0 +1,19 @@ +//type:fp +//options_all:--c11 --clang_version 170000 +// +// Beginning with Clang 17, empty initializers are accepted as an extension for +// scalar types and variable-length arrays as well as aggregates. + +struct A { + int i; +}; + +struct A aggregate = {}; +int scalar = {}; +double floating = {}; +void *pointer = {}; +int compound_literal = (int){}; + +void f(int n) { + int vla[n] = {}; +} diff --git a/tests/tests/regressions/clang-empty-vla-initialization.sft.cpp b/tests/tests/regressions/clang-empty-vla-initialization.sft.cpp new file mode 100644 index 0000000000..bfe3cb97f0 --- /dev/null +++ b/tests/tests/regressions/clang-empty-vla-initialization.sft.cpp @@ -0,0 +1,9 @@ +//type:fn +//options_all:--c11 --clang_version 160000 +// +// Clang versions before 17 do not accept empty initializers for variable-length +// arrays. + +void f(int n) { + int vla[n] = {}; +}