Skip to content

Commit 7132c5d

Browse files
committed
N3006 base
1 parent b284a84 commit 7132c5d

File tree

6 files changed

+114
-4
lines changed

6 files changed

+114
-4
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,8 @@ C2y Feature Support
9898
C23 Feature Support
9999
^^^^^^^^^^^^^^^^^^^
100100

101+
- Clang now supports `N3006 <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3006.htm>`_ Underspecified object declarations.
102+
101103
Non-comprehensive list of changes in this release
102104
-------------------------------------------------
103105

clang/include/clang/Basic/DiagnosticSemaKinds.td

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

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

clang/lib/Sema/SemaExpr.cpp

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7101,10 +7101,38 @@ Sema::BuildCompoundLiteralExpr(SourceLocation LParenLoc, TypeSourceInfo *TInfo,
71017101
diagID))
71027102
return ExprError();
71037103
}
7104+
} else if (LangOpts.C23 &&
7105+
(literalType->isRecordType() || literalType->isEnumeralType())) {
7106+
// C23 6.2.1p7: Structure, union, and enumeration tags have scope that
7107+
// begins just after the appearance of the tag in a type specifier that
7108+
// declares the tag.
7109+
// [...]
7110+
// An ordinary identifier that has an underspecified definition has scope
7111+
// that starts when the definition is completed; if the same ordinary
7112+
// identifier declares another entity with a scope that encloses the current
7113+
// block, that declaration is hidden as soon as the inner declarator is
7114+
// completed*.)
7115+
// [...]
7116+
// *) That means, that the outer declaration is not visible for the
7117+
// initializer.
7118+
auto Range = SourceRange(LParenLoc, RParenLoc);
7119+
const auto *Tag = literalType->castAs<TagType>();
7120+
const auto &TagRange = Tag->getDecl()->getSourceRange();
7121+
7122+
// We should diagnose underspecified declaration, unless the identifier has
7123+
// been diagnosed as being a redefinition, since the tag is made anonymous.
7124+
if (Range.fullyContains(TagRange) && Tag->getDecl()->getIdentifier()) {
7125+
Diag(TagRange.getBegin(), diag::err_c23_underspecified_object_declaration)
7126+
<< (unsigned)Tag->getDecl()->getTagKind() << Tag->getDecl()->getName()
7127+
<< TagRange;
7128+
return ExprError();
7129+
}
71047130
} else if (!literalType->isDependentType() &&
7105-
RequireCompleteType(LParenLoc, literalType,
7106-
diag::err_typecheck_decl_incomplete_type,
7107-
SourceRange(LParenLoc, LiteralExpr->getSourceRange().getEnd())))
7131+
RequireCompleteType(
7132+
LParenLoc, literalType,
7133+
diag::err_typecheck_decl_incomplete_type,
7134+
SourceRange(LParenLoc,
7135+
LiteralExpr->getSourceRange().getEnd())))
71087136
return ExprError();
71097137

71107138
InitializedEntity Entity

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
@@ -797,7 +797,7 @@ <h2 id="c2x">C23 implementation status</h2>
797797
<tr>
798798
<td>Underspecified object definitions</td>
799799
<td><a href="https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3006.htm">N3006</a></td>
800-
<td class="none" align="center">No</td>
800+
<td class="unreleased" align="center">Clang 21</td>
801801
</tr>
802802
<tr>
803803
<td>Type inference for object declarations</td>

0 commit comments

Comments
 (0)