[clang] [clang][C23] N3006 Underspecified object declarations (PR #79845)
Guillot Tony via cfe-commits
cfe-commits at lists.llvm.org
Wed Jul 24 04:17:09 PDT 2024
https://github.com/to268 updated https://github.com/llvm/llvm-project/pull/79845
>From 5eca1b0b822e839b5834ea87f329819368caf563 Mon Sep 17 00:00:00 2001
From: Guillot Tony <tony.guillot at protonmail.com>
Date: Wed, 24 Jul 2024 13:11:48 +0200
Subject: [PATCH] Rebased N3006 feature after LLVM 19 release branch creation
---
clang/docs/ReleaseNotes.rst | 5 +-
.../clang/Basic/DiagnosticSemaKinds.td | 2 +
clang/lib/Sema/SemaExpr.cpp | 26 ++++++++
clang/test/C/C23/n3006.c | 66 +++++++++++++++++++
clang/test/Parser/c2x-underspecified-decls.c | 12 ++++
clang/www/c_status.html | 2 +-
6 files changed, 111 insertions(+), 2 deletions(-)
create mode 100644 clang/test/C/C23/n3006.c
create mode 100644 clang/test/Parser/c2x-underspecified-decls.c
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 88f4d09308e8e..e17713c8343db 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -93,6 +93,9 @@ C2y Feature Support
C23 Feature Support
^^^^^^^^^^^^^^^^^^^
+- Clang now diagnoses `N3006 Underspecified object declarations`
+ <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3006.htm>`_.
+
New Compiler Flags
------------------
@@ -117,7 +120,7 @@ Improvements to Clang's diagnostics
- Some template related diagnostics have been improved.
.. code-block:: c++
-
+
void foo() { template <typename> int i; } // error: templates can only be declared in namespace or class scope
struct S {
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 810abe4f23e31..597de7606cebf 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -7793,6 +7793,8 @@ def err_attribute_arm_mve_polymorphism : Error<
"'__clang_arm_mve_strict_polymorphism' attribute can only be applied to an MVE/NEON vector type">;
def err_attribute_webassembly_funcref : Error<
"'__funcref' attribute can only be applied to a function pointer type">;
+def err_c23_underspecified_object_declaration: Error<
+ "'%select{struct|<ERROR>|union|<ERROR>|enum}0 %1' is defined as an underspecified object initializer">;
def warn_setter_getter_impl_required : Warning<
"property %0 requires method %1 to be defined - "
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 9207bf7a41349..1d83e0fe43994 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -7002,6 +7002,32 @@ Sema::BuildCompoundLiteralExpr(SourceLocation LParenLoc, TypeSourceInfo *TInfo,
diagID))
return ExprError();
}
+ } else if (LangOpts.C23 &&
+ (literalType->isRecordType() || literalType->isEnumeralType())) {
+ // C23 6.2.1p7: Structure, union, and enumeration tags have scope that
+ // begins just after the appearance of the tag in a type specifier that
+ // declares the tag.
+ // [...]
+ // An ordinary identifier that has an underspecified definition has scope
+ // that starts when the definition is completed; if the same ordinary
+ // identifier declares another entity with a scope that encloses the current
+ // block, that declaration is hidden as soon as the inner declarator is
+ // completed*.)
+ // [...]
+ // *) That means, that the outer declaration is not visible for the
+ // initializer.
+ auto Range = SourceRange(LParenLoc, RParenLoc);
+ const auto *Tag = literalType->castAs<TagType>();
+ const auto &TagRange = Tag->getDecl()->getSourceRange();
+
+ // We should diagnose underspecified declaration, unless the identifier has
+ // been diagnosed as being a redefinition, since the tag is made anonymous.
+ if (Range.fullyContains(TagRange) && Tag->getDecl()->getIdentifier()) {
+ Diag(TagRange.getBegin(), diag::err_c23_underspecified_object_declaration)
+ << (unsigned)Tag->getDecl()->getTagKind() << Tag->getDecl()->getName()
+ << TagRange;
+ return ExprError();
+ }
} else if (!literalType->isDependentType() &&
RequireCompleteType(LParenLoc, literalType,
diag::err_typecheck_decl_incomplete_type,
diff --git a/clang/test/C/C23/n3006.c b/clang/test/C/C23/n3006.c
new file mode 100644
index 0000000000000..85b36f45289a9
--- /dev/null
+++ b/clang/test/C/C23/n3006.c
@@ -0,0 +1,66 @@
+// RUN: %clang_cc1 -std=c2x -verify %s
+
+/* WG14 N3006: Full
+ * Underspecified object declarations
+ */
+
+struct S1 { int x, y; }; // expected-note {{previous definition is here}}
+union U1 { int a; double b; }; // expected-note {{previous definition is here}}
+enum E1 { FOO, BAR }; // expected-note {{previous definition is here}}
+
+auto normal_struct = (struct S1){ 1, 2 };
+auto normal_struct2 = (struct S1) { .x = 1, .y = 2 };
+auto underspecified_struct = (struct S2 { int x, y; }){ 1, 2 }; // expected-error {{'struct S2' is defined as an underspecified object initializer}}
+auto underspecified_struct_redef = (struct S1 { char x, y; }){ 'A', 'B'}; // expected-error {{redefinition of 'S1'}}
+auto underspecified_empty_struct = (struct S3 { }){ }; // expected-error {{'struct S3' is defined as an underspecified object initializer}}
+
+auto normal_union_int = (union U1){ .a = 12 };
+auto normal_union_double = (union U1){ .b = 2.4 };
+auto underspecified_union = (union U2 { int a; double b; }){ .a = 34 }; // expected-error {{'union U2' is defined as an underspecified object initializer}}
+auto underspecified_union_redef = (union U1 { char a; double b; }){ .a = 'A' }; // expected-error {{redefinition of 'U1'}}
+auto underspecified_empty_union = (union U3 { }){ }; // expected-error {{'union U3' is defined as an underspecified object initializer}}
+
+auto normal_enum_foo = (enum E1){ FOO };
+auto normal_enum_bar = (enum E1){ BAR };
+auto underspecified_enum = (enum E2 { BAZ, QUX }){ BAZ }; // expected-error {{'enum E2' is defined as an underspecified object initializer}}
+auto underspecified_enum_redef = (enum E1 { ONE, TWO }){ ONE }; // expected-error {{redefinition of 'E1'}}
+auto underspecified_empty_enum = (enum E3 { }){ }; // expected-error {{'enum E3' is defined as an underspecified object initializer}} \
+ expected-error {{use of empty enum}}
+void constexpr_test() {
+ constexpr auto ce_struct = (struct S1){ 1, 2 };
+ constexpr auto ce_union = (union U1){ .a = 12 };
+ constexpr auto ce_enum = (enum E1){ FOO };
+}
+
+void trivial_test() {
+ constexpr int i = i; // expected-error {{constexpr variable 'i' must be initialized by a constant expression}} \
+ expected-note {{read of object outside its lifetime is not allowed in a constant expression}}
+ auto j = j; // expected-error {{variable 'j' declared with deduced type 'auto' cannot appear in its own initializer}}
+}
+
+void double_definition_test() {
+ const struct S { int x; } s; // expected-note {{previous definition is here}}
+ constexpr struct S s = {0}; // expected-error {{redefinition of 's'}}
+}
+
+void declaring_an_underspecified_defied_object_test() {
+ struct S { int x, y; };
+ constexpr int i = (struct T { int a, b; }){0, 1}.a; // expected-error {{'struct T' is defined as an underspecified object initializer}} \
+ FIXME: `constexpr variable 'i' must be initialized by a constant expression` shoud appear
+
+ struct T t = { 1, 2 }; // TODO: Should this be diagnosed as an invalid declaration?
+}
+
+void constexpr_complience_test() {
+ int x = (struct Foo { int x; }){ 0 }.x; // expected-error {{'struct Foo' is defined as an underspecified object initializer}}
+ constexpr int y = (struct Bar { int x; }){ 0 }.x; // expected-error {{'struct Bar' is defined as an underspecified object initializer}}
+}
+
+void special_test() {
+ constexpr typeof(struct s *) x = 0; // FIXME: declares `s` which is not an ordinary identifier
+ constexpr struct S { int a, b; } y = { 0 }; // FIXME: declares `S`, `a`, and `b`, none of which are ordinary identifiers
+ constexpr int a = 0, b = 0;
+ auto c = (struct T { int x, y; }){0, 0}; // expected-error {{'struct T' is defined as an underspecified object initializer}}
+ constexpr int (*fp)(struct X { int x; } val) = 0; // expected-warning {{declaration of 'struct X' will not be visible outside of this function}} \
+ FIXME: declares `X` and `x` which are not ordinary identifiers
+}
diff --git a/clang/test/Parser/c2x-underspecified-decls.c b/clang/test/Parser/c2x-underspecified-decls.c
new file mode 100644
index 0000000000000..5a7e935cda544
--- /dev/null
+++ b/clang/test/Parser/c2x-underspecified-decls.c
@@ -0,0 +1,12 @@
+// RUN: %clang_cc1 -fsyntax-only -verify=expected,c23 -std=c23 %s
+// RUN: %clang_cc1 -fsyntax-only -verify=expected,c17 -std=c17 %s
+
+auto underspecified_struct = (struct S1 { int x, y; }){ 1, 2 }; // c23-error {{'struct S1' is defined as an underspecified object initializer}} \
+ c17-error {{type specifier missing, defaults to 'int'; ISO C99 and later do not support implicit int}} \
+ c17-error {{illegal storage class on file-scoped variable}}
+auto underspecified_union = (union U1 { int a; double b; }){ .a = 34 }; // c23-error {{'union U1' is defined as an underspecified object initializer}} \
+ c17-error {{type specifier missing, defaults to 'int'; ISO C99 and later do not support implicit int}} \
+ c17-error {{illegal storage class on file-scoped variable}}
+auto underspecified_enum = (enum E1 { FOO, BAR }){ BAR }; // c23-error {{'enum E1' is defined as an underspecified object initializer}} \
+ c17-error {{type specifier missing, defaults to 'int'; ISO C99 and later do not support implicit int}} \
+ c17-error {{illegal storage class on file-scoped variable}}
diff --git a/clang/www/c_status.html b/clang/www/c_status.html
index 3ea70b0163c70..600e8b6d5b63c 100644
--- a/clang/www/c_status.html
+++ b/clang/www/c_status.html
@@ -1180,7 +1180,7 @@ <h2 id="c2x">C23 implementation status</h2>
<tr>
<td>Underspecified object definitions</td>
<td><a href="https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3006.htm">N3006</a></td>
- <td class="none" align="center">No</td>
+ <td class="unreleased" align="center">Clang 20</td>
</tr>
<tr>
<td>Type inference for object declarations</td>
More information about the cfe-commits
mailing list