Skip to content

Commit 5eca1b0

Browse files
committed
Rebased N3006 feature after LLVM 19 release branch creation
1 parent 893a303 commit 5eca1b0

File tree

6 files changed

+111
-2
lines changed

6 files changed

+111
-2
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,9 @@ C2y Feature Support
9393
C23 Feature Support
9494
^^^^^^^^^^^^^^^^^^^
9595

96+
- Clang now diagnoses `N3006 Underspecified object declarations`
97+
<https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3006.htm>`_.
98+
9699
New Compiler Flags
97100
------------------
98101

@@ -117,7 +120,7 @@ Improvements to Clang's diagnostics
117120
- Some template related diagnostics have been improved.
118121

119122
.. code-block:: c++
120-
123+
121124
void foo() { template <typename> int i; } // error: templates can only be declared in namespace or class scope
122125

123126
struct S {

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7793,6 +7793,8 @@ def err_attribute_arm_mve_polymorphism : Error<
77937793
"'__clang_arm_mve_strict_polymorphism' attribute can only be applied to an MVE/NEON vector type">;
77947794
def err_attribute_webassembly_funcref : Error<
77957795
"'__funcref' attribute can only be applied to a function pointer type">;
7796+
def err_c23_underspecified_object_declaration: Error<
7797+
"'%select{struct|<ERROR>|union|<ERROR>|enum}0 %1' is defined as an underspecified object initializer">;
77967798

77977799
def warn_setter_getter_impl_required : Warning<
77987800
"property %0 requires method %1 to be defined - "

clang/lib/Sema/SemaExpr.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7002,6 +7002,32 @@ Sema::BuildCompoundLiteralExpr(SourceLocation LParenLoc, TypeSourceInfo *TInfo,
70027002
diagID))
70037003
return ExprError();
70047004
}
7005+
} else if (LangOpts.C23 &&
7006+
(literalType->isRecordType() || literalType->isEnumeralType())) {
7007+
// C23 6.2.1p7: Structure, union, and enumeration tags have scope that
7008+
// begins just after the appearance of the tag in a type specifier that
7009+
// declares the tag.
7010+
// [...]
7011+
// An ordinary identifier that has an underspecified definition has scope
7012+
// that starts when the definition is completed; if the same ordinary
7013+
// identifier declares another entity with a scope that encloses the current
7014+
// block, that declaration is hidden as soon as the inner declarator is
7015+
// completed*.)
7016+
// [...]
7017+
// *) That means, that the outer declaration is not visible for the
7018+
// initializer.
7019+
auto Range = SourceRange(LParenLoc, RParenLoc);
7020+
const auto *Tag = literalType->castAs<TagType>();
7021+
const auto &TagRange = Tag->getDecl()->getSourceRange();
7022+
7023+
// We should diagnose underspecified declaration, unless the identifier has
7024+
// been diagnosed as being a redefinition, since the tag is made anonymous.
7025+
if (Range.fullyContains(TagRange) && Tag->getDecl()->getIdentifier()) {
7026+
Diag(TagRange.getBegin(), diag::err_c23_underspecified_object_declaration)
7027+
<< (unsigned)Tag->getDecl()->getTagKind() << Tag->getDecl()->getName()
7028+
<< TagRange;
7029+
return ExprError();
7030+
}
70057031
} else if (!literalType->isDependentType() &&
70067032
RequireCompleteType(LParenLoc, literalType,
70077033
diag::err_typecheck_decl_incomplete_type,

clang/test/C/C23/n3006.c

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// RUN: %clang_cc1 -std=c2x -verify %s
2+
3+
/* WG14 N3006: Full
4+
* Underspecified object declarations
5+
*/
6+
7+
struct S1 { int x, y; }; // expected-note {{previous definition is here}}
8+
union U1 { int a; double b; }; // expected-note {{previous definition is here}}
9+
enum E1 { FOO, BAR }; // expected-note {{previous definition is here}}
10+
11+
auto normal_struct = (struct S1){ 1, 2 };
12+
auto normal_struct2 = (struct S1) { .x = 1, .y = 2 };
13+
auto underspecified_struct = (struct S2 { int x, y; }){ 1, 2 }; // expected-error {{'struct S2' is defined as an underspecified object initializer}}
14+
auto underspecified_struct_redef = (struct S1 { char x, y; }){ 'A', 'B'}; // expected-error {{redefinition of 'S1'}}
15+
auto underspecified_empty_struct = (struct S3 { }){ }; // expected-error {{'struct S3' is defined as an underspecified object initializer}}
16+
17+
auto normal_union_int = (union U1){ .a = 12 };
18+
auto normal_union_double = (union U1){ .b = 2.4 };
19+
auto underspecified_union = (union U2 { int a; double b; }){ .a = 34 }; // expected-error {{'union U2' is defined as an underspecified object initializer}}
20+
auto underspecified_union_redef = (union U1 { char a; double b; }){ .a = 'A' }; // expected-error {{redefinition of 'U1'}}
21+
auto underspecified_empty_union = (union U3 { }){ }; // expected-error {{'union U3' is defined as an underspecified object initializer}}
22+
23+
auto normal_enum_foo = (enum E1){ FOO };
24+
auto normal_enum_bar = (enum E1){ BAR };
25+
auto underspecified_enum = (enum E2 { BAZ, QUX }){ BAZ }; // expected-error {{'enum E2' is defined as an underspecified object initializer}}
26+
auto underspecified_enum_redef = (enum E1 { ONE, TWO }){ ONE }; // expected-error {{redefinition of 'E1'}}
27+
auto underspecified_empty_enum = (enum E3 { }){ }; // expected-error {{'enum E3' is defined as an underspecified object initializer}} \
28+
expected-error {{use of empty enum}}
29+
void constexpr_test() {
30+
constexpr auto ce_struct = (struct S1){ 1, 2 };
31+
constexpr auto ce_union = (union U1){ .a = 12 };
32+
constexpr auto ce_enum = (enum E1){ FOO };
33+
}
34+
35+
void trivial_test() {
36+
constexpr int i = i; // expected-error {{constexpr variable 'i' must be initialized by a constant expression}} \
37+
expected-note {{read of object outside its lifetime is not allowed in a constant expression}}
38+
auto j = j; // expected-error {{variable 'j' declared with deduced type 'auto' cannot appear in its own initializer}}
39+
}
40+
41+
void double_definition_test() {
42+
const struct S { int x; } s; // expected-note {{previous definition is here}}
43+
constexpr struct S s = {0}; // expected-error {{redefinition of 's'}}
44+
}
45+
46+
void declaring_an_underspecified_defied_object_test() {
47+
struct S { int x, y; };
48+
constexpr int i = (struct T { int a, b; }){0, 1}.a; // expected-error {{'struct T' is defined as an underspecified object initializer}} \
49+
FIXME: `constexpr variable 'i' must be initialized by a constant expression` shoud appear
50+
51+
struct T t = { 1, 2 }; // TODO: Should this be diagnosed as an invalid declaration?
52+
}
53+
54+
void constexpr_complience_test() {
55+
int x = (struct Foo { int x; }){ 0 }.x; // expected-error {{'struct Foo' is defined as an underspecified object initializer}}
56+
constexpr int y = (struct Bar { int x; }){ 0 }.x; // expected-error {{'struct Bar' is defined as an underspecified object initializer}}
57+
}
58+
59+
void special_test() {
60+
constexpr typeof(struct s *) x = 0; // FIXME: declares `s` which is not an ordinary identifier
61+
constexpr struct S { int a, b; } y = { 0 }; // FIXME: declares `S`, `a`, and `b`, none of which are ordinary identifiers
62+
constexpr int a = 0, b = 0;
63+
auto c = (struct T { int x, y; }){0, 0}; // expected-error {{'struct T' is defined as an underspecified object initializer}}
64+
constexpr int (*fp)(struct X { int x; } val) = 0; // expected-warning {{declaration of 'struct X' will not be visible outside of this function}} \
65+
FIXME: declares `X` and `x` which are not ordinary identifiers
66+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// RUN: %clang_cc1 -fsyntax-only -verify=expected,c23 -std=c23 %s
2+
// RUN: %clang_cc1 -fsyntax-only -verify=expected,c17 -std=c17 %s
3+
4+
auto underspecified_struct = (struct S1 { int x, y; }){ 1, 2 }; // c23-error {{'struct S1' is defined as an underspecified object initializer}} \
5+
c17-error {{type specifier missing, defaults to 'int'; ISO C99 and later do not support implicit int}} \
6+
c17-error {{illegal storage class on file-scoped variable}}
7+
auto underspecified_union = (union U1 { int a; double b; }){ .a = 34 }; // c23-error {{'union U1' is defined as an underspecified object initializer}} \
8+
c17-error {{type specifier missing, defaults to 'int'; ISO C99 and later do not support implicit int}} \
9+
c17-error {{illegal storage class on file-scoped variable}}
10+
auto underspecified_enum = (enum E1 { FOO, BAR }){ BAR }; // c23-error {{'enum E1' is defined as an underspecified object initializer}} \
11+
c17-error {{type specifier missing, defaults to 'int'; ISO C99 and later do not support implicit int}} \
12+
c17-error {{illegal storage class on file-scoped variable}}

clang/www/c_status.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1180,7 +1180,7 @@ <h2 id="c2x">C23 implementation status</h2>
11801180
<tr>
11811181
<td>Underspecified object definitions</td>
11821182
<td><a href="https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3006.htm">N3006</a></td>
1183-
<td class="none" align="center">No</td>
1183+
<td class="unreleased" align="center">Clang 20</td>
11841184
</tr>
11851185
<tr>
11861186
<td>Type inference for object declarations</td>

0 commit comments

Comments
 (0)